From f6c2a24af249a74d6a94580bb09ae366e7f4a584 Mon Sep 17 00:00:00 2001 From: Schnitzel5 Date: Tue, 26 Aug 2025 22:07:49 +0200 Subject: [PATCH] added app logger --- lib/l10n/app_en.arb | 2 + lib/l10n/generated/app_localizations.dart | 12 +++ lib/l10n/generated/app_localizations_ar.dart | 6 ++ lib/l10n/generated/app_localizations_as.dart | 6 ++ lib/l10n/generated/app_localizations_de.dart | 6 ++ lib/l10n/generated/app_localizations_en.dart | 6 ++ lib/l10n/generated/app_localizations_es.dart | 6 ++ lib/l10n/generated/app_localizations_fr.dart | 6 ++ lib/l10n/generated/app_localizations_hi.dart | 6 ++ lib/l10n/generated/app_localizations_id.dart | 6 ++ lib/l10n/generated/app_localizations_it.dart | 6 ++ lib/l10n/generated/app_localizations_pt.dart | 6 ++ lib/l10n/generated/app_localizations_ru.dart | 6 ++ lib/l10n/generated/app_localizations_th.dart | 6 ++ lib/l10n/generated/app_localizations_tr.dart | 6 ++ lib/l10n/generated/app_localizations_zh.dart | 6 ++ lib/main.dart | 3 + lib/modules/more/about/about_screen.dart | 29 +++++++ .../m3u8/m3u8_downloader.dart | 7 +- lib/utils/log/logger.dart | 77 +++++++++++++++++++ 20 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 lib/utils/log/logger.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index d48c55e9..c49e1d8c 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -143,6 +143,8 @@ "nsfw_sources_info": "This does not prevent unofficial or potentially incorrectly flagged extensions from surfacing NSFW (18+) content within the app", "version": "Version", "check_for_update": "Check for update", + "share_app_logs": "Share app logs", + "no_app_logs": "No log.txt available!", "n_days_ago": "{days} days ago", "today": "Today", "yesterday": "Yesterday", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 79209a5e..74b42a4d 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -933,6 +933,18 @@ abstract class AppLocalizations { /// **'Check for update'** String get check_for_update; + /// No description provided for @share_app_logs. + /// + /// In en, this message translates to: + /// **'Share app logs'** + String get share_app_logs; + + /// No description provided for @no_app_logs. + /// + /// In en, this message translates to: + /// **'No log.txt available!'** + String get no_app_logs; + /// No description provided for @n_days_ago. /// /// In en, this message translates to: diff --git a/lib/l10n/generated/app_localizations_ar.dart b/lib/l10n/generated/app_localizations_ar.dart index b57fe691..bebde3e5 100644 --- a/lib/l10n/generated/app_localizations_ar.dart +++ b/lib/l10n/generated/app_localizations_ar.dart @@ -434,6 +434,12 @@ class AppLocalizationsAr extends AppLocalizations { @override String get check_for_update => 'التحقق من التحديثات'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return 'منذ $days أيام'; diff --git a/lib/l10n/generated/app_localizations_as.dart b/lib/l10n/generated/app_localizations_as.dart index 86cf26b3..c5fbff38 100644 --- a/lib/l10n/generated/app_localizations_as.dart +++ b/lib/l10n/generated/app_localizations_as.dart @@ -436,6 +436,12 @@ class AppLocalizationsAs extends AppLocalizations { @override String get check_for_update => 'আপডেটৰ বাবে পৰীক্ষা কৰক'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return '$days দিনৰ আগতে'; diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index bfc37ffb..dc71f563 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -438,6 +438,12 @@ class AppLocalizationsDe extends AppLocalizations { @override String get check_for_update => 'Auf Aktualisierung prüfen'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return 'Vor $days Tagen'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 853a46c9..7b8f26a3 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -436,6 +436,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get check_for_update => 'Check for update'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return '$days days ago'; diff --git a/lib/l10n/generated/app_localizations_es.dart b/lib/l10n/generated/app_localizations_es.dart index e61a60bb..26d92e82 100644 --- a/lib/l10n/generated/app_localizations_es.dart +++ b/lib/l10n/generated/app_localizations_es.dart @@ -440,6 +440,12 @@ class AppLocalizationsEs extends AppLocalizations { @override String get check_for_update => 'Buscar actualizaciones'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return 'hace $days días'; diff --git a/lib/l10n/generated/app_localizations_fr.dart b/lib/l10n/generated/app_localizations_fr.dart index 14b8c9c5..5e70df1b 100644 --- a/lib/l10n/generated/app_localizations_fr.dart +++ b/lib/l10n/generated/app_localizations_fr.dart @@ -442,6 +442,12 @@ class AppLocalizationsFr extends AppLocalizations { @override String get check_for_update => 'Rechercher des mises à jour'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return 'Il y a $days jours'; diff --git a/lib/l10n/generated/app_localizations_hi.dart b/lib/l10n/generated/app_localizations_hi.dart index 9629d9b2..13e2fbd9 100644 --- a/lib/l10n/generated/app_localizations_hi.dart +++ b/lib/l10n/generated/app_localizations_hi.dart @@ -436,6 +436,12 @@ class AppLocalizationsHi extends AppLocalizations { @override String get check_for_update => 'अपडेट के लिए जांचें'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return '$days दिन पहले'; diff --git a/lib/l10n/generated/app_localizations_id.dart b/lib/l10n/generated/app_localizations_id.dart index 0da0b172..82dcec91 100644 --- a/lib/l10n/generated/app_localizations_id.dart +++ b/lib/l10n/generated/app_localizations_id.dart @@ -440,6 +440,12 @@ class AppLocalizationsId extends AppLocalizations { @override String get check_for_update => 'Periksa Pembaruan'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return '$days Hari yang Lalu'; diff --git a/lib/l10n/generated/app_localizations_it.dart b/lib/l10n/generated/app_localizations_it.dart index e281c0ba..6607c76d 100644 --- a/lib/l10n/generated/app_localizations_it.dart +++ b/lib/l10n/generated/app_localizations_it.dart @@ -440,6 +440,12 @@ class AppLocalizationsIt extends AppLocalizations { @override String get check_for_update => 'Controlla aggiornamenti'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return '$days giorni fa'; diff --git a/lib/l10n/generated/app_localizations_pt.dart b/lib/l10n/generated/app_localizations_pt.dart index 484f3c47..caa47153 100644 --- a/lib/l10n/generated/app_localizations_pt.dart +++ b/lib/l10n/generated/app_localizations_pt.dart @@ -440,6 +440,12 @@ class AppLocalizationsPt extends AppLocalizations { @override String get check_for_update => 'Verificar atualização'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return '$days dias atrás'; diff --git a/lib/l10n/generated/app_localizations_ru.dart b/lib/l10n/generated/app_localizations_ru.dart index d28b6178..f926133d 100644 --- a/lib/l10n/generated/app_localizations_ru.dart +++ b/lib/l10n/generated/app_localizations_ru.dart @@ -441,6 +441,12 @@ class AppLocalizationsRu extends AppLocalizations { @override String get check_for_update => 'Проверить обновления'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return '$days дней назад'; diff --git a/lib/l10n/generated/app_localizations_th.dart b/lib/l10n/generated/app_localizations_th.dart index 2bb852c7..9253d21a 100644 --- a/lib/l10n/generated/app_localizations_th.dart +++ b/lib/l10n/generated/app_localizations_th.dart @@ -436,6 +436,12 @@ class AppLocalizationsTh extends AppLocalizations { @override String get check_for_update => 'ตรวจสอบการอัพเดท'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return '$days วันที่แล้ว'; diff --git a/lib/l10n/generated/app_localizations_tr.dart b/lib/l10n/generated/app_localizations_tr.dart index 4f04a772..ee42bf30 100644 --- a/lib/l10n/generated/app_localizations_tr.dart +++ b/lib/l10n/generated/app_localizations_tr.dart @@ -436,6 +436,12 @@ class AppLocalizationsTr extends AppLocalizations { @override String get check_for_update => 'Güncelleme Kontrol Et'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return '$days gün önce'; diff --git a/lib/l10n/generated/app_localizations_zh.dart b/lib/l10n/generated/app_localizations_zh.dart index 0434771a..6669c85d 100644 --- a/lib/l10n/generated/app_localizations_zh.dart +++ b/lib/l10n/generated/app_localizations_zh.dart @@ -428,6 +428,12 @@ class AppLocalizationsZh extends AppLocalizations { @override String get check_for_update => '检查更新'; + @override + String get share_app_logs => 'Share app logs'; + + @override + String get no_app_logs => 'No log.txt available!'; + @override String n_days_ago(Object days) { return '$days天前'; diff --git a/lib/main.dart b/lib/main.dart index a83b1a58..f0573cea 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -30,6 +30,7 @@ import 'package:mangayomi/l10n/generated/app_localizations.dart'; import 'package:mangayomi/services/http/m_client.dart'; import 'package:mangayomi/src/rust/frb_generated.dart'; import 'package:mangayomi/utils/discord_rpc.dart'; +import 'package:mangayomi/utils/log/logger.dart'; import 'package:mangayomi/utils/url_protocol/api.dart'; import 'package:mangayomi/modules/more/settings/appearance/providers/theme_provider.dart'; import 'package:mangayomi/modules/library/providers/file_scanner.dart'; @@ -66,6 +67,7 @@ void main(List args) async { ); } } + await AppLogger.init(); isar = await StorageProvider().initDB(null, inspector: kDebugMode); await Hive.initFlutter(); Hive.registerAdapter(TrackSearchAdapter()); @@ -144,6 +146,7 @@ class _MyAppState extends ConsumerState { void dispose() { _linkSubscription?.cancel(); discordRpc?.destroy(); + AppLogger.dispose(); super.dispose(); } diff --git a/lib/modules/more/about/about_screen.dart b/lib/modules/more/about/about_screen.dart index c401dfd1..07973052 100644 --- a/lib/modules/more/about/about_screen.dart +++ b/lib/modules/more/about/about_screen.dart @@ -1,12 +1,19 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:mangayomi/eval/model/m_bridge.dart'; import 'package:mangayomi/main.dart'; import 'package:mangayomi/models/settings.dart'; import 'package:mangayomi/modules/more/about/providers/check_for_update.dart'; import 'package:mangayomi/modules/more/about/providers/get_package_info.dart'; import 'package:mangayomi/modules/widgets/progress_center.dart'; import 'package:mangayomi/providers/l10n_providers.dart'; +import 'package:mangayomi/providers/storage_provider.dart'; +import 'package:path/path.dart' as path; +import 'package:share_plus/share_plus.dart'; import 'package:url_launcher/url_launcher.dart'; class AboutScreen extends ConsumerWidget { @@ -73,6 +80,28 @@ class AboutScreen extends ConsumerWidget { }, title: Text(l10n.check_for_update), ), + ListTile( + onTap: () async { + final storage = StorageProvider(); + final directory = await storage.getDefaultDirectory(); + final file = File( + path.join(directory!.path, 'logs.txt'), + ); + if (await file.exists()) { + if (Platform.isLinux) { + await Clipboard.setData( + ClipboardData(text: file.path), + ); + } + Share.shareXFiles([ + XFile(file.path), + ], text: "log.txt"); + } else { + botToast(l10n.no_app_logs); + } + }, + title: Text(l10n.share_app_logs), + ), // ListTile( // onTap: () {}, // title: const Text("What's news"), diff --git a/lib/services/download_manager/m3u8/m3u8_downloader.dart b/lib/services/download_manager/m3u8/m3u8_downloader.dart index 297890bc..1ea79389 100644 --- a/lib/services/download_manager/m3u8/m3u8_downloader.dart +++ b/lib/services/download_manager/m3u8/m3u8_downloader.dart @@ -13,6 +13,7 @@ import 'package:mangayomi/services/download_manager/m3u8/models/download.dart'; import 'package:mangayomi/services/download_manager/m3u8/models/ts_info.dart'; import 'package:mangayomi/src/rust/frb_generated.dart'; import 'package:mangayomi/utils/extensions/string_extensions.dart'; +import 'package:mangayomi/utils/log/logger.dart'; import 'package:path/path.dart' as path; import 'package:encrypt/encrypt.dart' as encrypt; import 'package:convert/convert.dart'; @@ -49,6 +50,7 @@ class M3u8Downloader { if (kDebugMode) { log('[M3u8Downloader] $message'); } + AppLogger.log(message); } void close() { @@ -151,11 +153,11 @@ class M3u8Downloader { continue; } _log('Downloading subtitle file: ${element.label}'); - subtitleFile.createSync(recursive: true); if (element.file == null || element.file!.trim().isEmpty) { _log('Warning: No subtitle file: ${element.label}'); continue; } + subtitleFile.createSync(recursive: true); if (element.file!.startsWith("http")) { final response = await _withRetry( () => @@ -168,10 +170,13 @@ class M3u8Downloader { _log('Subtitle file downloaded: ${element.label}'); await subtitleFile.writeAsBytes(response.bodyBytes); } else { + _log('Subtitle file written: ${element.label}'); await subtitleFile.writeAsString(element.file!); } } } catch (e) { + AppLogger.log("Download failed", logLevel: LogLevel.error); + AppLogger.log(e.toString(), logLevel: LogLevel.error); throw M3u8DownloaderException('Download failed', e); } finally { close(); diff --git a/lib/utils/log/logger.dart b/lib/utils/log/logger.dart new file mode 100644 index 00000000..ad01e929 --- /dev/null +++ b/lib/utils/log/logger.dart @@ -0,0 +1,77 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:mangayomi/providers/storage_provider.dart'; +import 'package:path/path.dart' as path; + +class AppLogger { + static final _logQueue = StreamController(); + static late File _logFile; + static late IOSink _sink; + static bool _initialized = false; + + /// Initialize the logger + static Future init() async { + final storage = StorageProvider(); + final directory = await storage.getDefaultDirectory(); + _logFile = File(path.join(directory!.path, 'logs.txt')); + + if (await _logFile.exists() && await _logFile.length() > 100 * 1024) { + await _logFile.delete(); + } + + if (!await _logFile.exists()) { + await _logFile.create(recursive: true); + } + + _sink = _logFile.openWrite(mode: FileMode.append); + _initialized = true; + + _logQueue.stream.listen((log) { + _sink.writeln(log); + }); + + log('\n\nLogger initialized\n\n'); + } + + static void log(String message, {LogLevel logLevel = LogLevel.info}) { + if (!_initialized) return; + + final now = DateTime.now(); + final timestamp = + '${now.day.toString().padLeft(2, '0')}/${now.month.toString().padLeft(2, '0')}/${now.year.toString().padLeft(4, '0')} ' + '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}'; + + final logMessage = '[$timestamp][${logLevel.toString()}] $message'; + _logQueue.add(logMessage); + } + + static Future dispose() async { + if (!_initialized) return; + await _logQueue.close(); + await _sink.flush(); + await _sink.close(); + _initialized = false; + } +} + +enum LogLevel { + debug, + info, + warning, + error; + + @override + String toString() { + switch (this) { + case LogLevel.debug: + return 'DEBUG'; + case LogLevel.info: + return 'INFO'; + case LogLevel.warning: + return 'WARNING'; + case LogLevel.error: + return 'ERROR'; + } + } +}