diff --git a/lib/collections/routes.dart b/lib/collections/routes.dart index e1cc5fb6..b9e06c61 100644 --- a/lib/collections/routes.dart +++ b/lib/collections/routes.dart @@ -1,4 +1,3 @@ -import 'package:catcher_2/catcher_2.dart'; import 'package:flutter/foundation.dart' hide Category; import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; @@ -46,7 +45,7 @@ import 'package:spotube/pages/root/root_app.dart'; import 'package:spotube/pages/settings/settings.dart'; import 'package:spotube/pages/mobile_login/mobile_login.dart'; -final rootNavigatorKey = Catcher2.navigatorKey; +final rootNavigatorKey = GlobalKey(); final shellRouteNavigatorKey = GlobalKey(); final routerProvider = Provider((ref) { return GoRouter( diff --git a/lib/hooks/configurators/use_endless_playback.dart b/lib/hooks/configurators/use_endless_playback.dart index 98f38165..97eb3f48 100644 --- a/lib/hooks/configurators/use_endless_playback.dart +++ b/lib/hooks/configurators/use_endless_playback.dart @@ -1,4 +1,4 @@ -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotify/spotify.dart'; @@ -62,7 +62,7 @@ void useEndlessPlayback(WidgetRef ref) { }), ); } catch (e, stack) { - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); } } diff --git a/lib/main.dart b/lib/main.dart index 30526bc6..75d2ada5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ -import 'package:catcher_2/catcher_2.dart'; +import 'dart:async'; + import 'package:dart_discord_rpc/dart_discord_rpc.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -19,7 +20,6 @@ import 'package:spotube/hooks/configurators/use_disable_battery_optimizations.da import 'package:spotube/hooks/configurators/use_get_storage_perms.dart'; import 'package:spotube/provider/tray_manager/tray_manager.dart'; import 'package:spotube/l10n/l10n.dart'; -import 'package:spotube/models/logger.dart'; import 'package:spotube/models/skip_segment.dart'; import 'package:spotube/models/source_match.dart'; import 'package:spotube/provider/connect/clients.dart'; @@ -30,6 +30,7 @@ import 'package:spotube/provider/user_preferences/user_preferences_provider.dart import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/cli/cli.dart'; import 'package:spotube/services/kv_store/kv_store.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/wm_tools/wm_tools.dart'; import 'package:spotube/themes/theme.dart'; import 'package:spotube/utils/persisted_state_notifier.dart'; @@ -44,98 +45,73 @@ import 'package:window_manager/window_manager.dart'; Future main(List rawArgs) async { final arguments = await startCLI(rawArgs); + AppLogger.initialize(arguments["verbose"]); - final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); + AppLogger.runZoned(() async { + final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); - await registerWindowsScheme("spotify"); + await registerWindowsScheme("spotify"); - tz.initializeTimeZones(); + tz.initializeTimeZones(); - FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); - MediaKit.ensureInitialized(); + MediaKit.ensureInitialized(); - // force High Refresh Rate on some Android devices (like One Plus) - if (kIsAndroid) { - await FlutterDisplayMode.setHighRefreshRate(); - } + // force High Refresh Rate on some Android devices (like One Plus) + if (kIsAndroid) { + await FlutterDisplayMode.setHighRefreshRate(); + } - if (kIsDesktop) { - await windowManager.setPreventClose(true); - } + if (kIsDesktop) { + await windowManager.setPreventClose(true); + } - await SystemTheme.accentColor.load(); + await SystemTheme.accentColor.load(); - if (!kIsWeb) { - MetadataGod.initialize(); - } + if (!kIsWeb) { + MetadataGod.initialize(); + } - if (kIsWindows || kIsLinux) { - DiscordRPC.initialize(); - } + if (kIsWindows || kIsLinux) { + DiscordRPC.initialize(); + } - await KVStoreService.initialize(); + await KVStoreService.initialize(); - final hiveCacheDir = - kIsWeb ? null : (await getApplicationSupportDirectory()).path; + final hiveCacheDir = + kIsWeb ? null : (await getApplicationSupportDirectory()).path; - Hive.init(hiveCacheDir); + Hive.init(hiveCacheDir); - Hive.registerAdapter(SkipSegmentAdapter()); + Hive.registerAdapter(SkipSegmentAdapter()); - Hive.registerAdapter(SourceMatchAdapter()); - Hive.registerAdapter(SourceTypeAdapter()); + Hive.registerAdapter(SourceMatchAdapter()); + Hive.registerAdapter(SourceTypeAdapter()); - // Cache versioning entities with Adapter - SourceMatch.version = 'v1'; - SkipSegment.version = 'v1'; + // Cache versioning entities with Adapter + SourceMatch.version = 'v1'; + SkipSegment.version = 'v1'; - await Hive.openLazyBox( - SourceMatch.boxName, - path: hiveCacheDir, - ); - await Hive.openLazyBox( - SkipSegment.boxName, - path: hiveCacheDir, - ); - await PersistedStateNotifier.initializeBoxes( - path: hiveCacheDir, - ); + await Hive.openLazyBox( + SourceMatch.boxName, + path: hiveCacheDir, + ); + await Hive.openLazyBox( + SkipSegment.boxName, + path: hiveCacheDir, + ); + await PersistedStateNotifier.initializeBoxes( + path: hiveCacheDir, + ); - if (kIsDesktop) { - await localNotifier.setup(appName: "Spotube"); - await WindowManagerTools.initialize(); - } + if (kIsDesktop) { + await localNotifier.setup(appName: "Spotube"); + await WindowManagerTools.initialize(); + } - Catcher2( - enableLogger: arguments["verbose"], - debugConfig: Catcher2Options( - SilentReportMode(), - [ - ConsoleHandler( - enableDeviceParameters: false, - enableApplicationParameters: false, - ), - if (!kIsWeb) FileHandler(await getLogsPath(), printLogs: false), - ], - ), - releaseConfig: Catcher2Options( - SilentReportMode(), - [ - if (arguments["verbose"] ?? false) ConsoleHandler(), - if (!kIsWeb) - FileHandler( - await getLogsPath(), - printLogs: false, - ), - ], - ), - runAppFunction: () { - runApp( - const ProviderScope(child: Spotube()), - ); - }, - ); + runApp(const ProviderScope(child: Spotube())); + }); } class Spotube extends HookConsumerWidget { @@ -166,6 +142,7 @@ class Spotube extends HookConsumerWidget { useEffect(() { FlutterNativeSplash.remove(); + return () { /// For enabling hot reload for audio player if (!kDebugMode) return; diff --git a/lib/provider/authentication_provider.dart b/lib/provider/authentication_provider.dart index 95ce6240..52c7f281 100644 --- a/lib/provider/authentication_provider.dart +++ b/lib/provider/authentication_provider.dart @@ -73,10 +73,10 @@ class AuthenticationCredentials { ), ); } catch (e) { - if (rootNavigatorKey?.currentContext != null) { + if (rootNavigatorKey.currentContext != null) { showPromptDialog( - context: rootNavigatorKey!.currentContext!, - title: rootNavigatorKey!.currentContext!.l10n + context: rootNavigatorKey.currentContext!, + title: rootNavigatorKey.currentContext!.l10n .error("Authentication Failure"), message: e.toString(), cancelText: null, diff --git a/lib/provider/connect/connect.dart b/lib/provider/connect/connect.dart index 6360c750..feb9fbd2 100644 --- a/lib/provider/connect/connect.dart +++ b/lib/provider/connect/connect.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/models/connect/connect.dart'; @@ -99,10 +99,7 @@ class ConnectNotifier extends AsyncNotifier { }); }, onError: (error) { - Catcher2.reportCheckedError( - error, - StackTrace.current, - ); + AppLogger.reportError(error, StackTrace.current); }, ); @@ -113,7 +110,7 @@ class ConnectNotifier extends AsyncNotifier { return channel; } catch (e, stack) { - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); rethrow; } } diff --git a/lib/provider/connect/server.dart b/lib/provider/connect/server.dart index 9c4e6466..aeaaf149 100644 --- a/lib/provider/connect/server.dart +++ b/lib/provider/connect/server.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:shelf/shelf.dart'; import 'package:shelf/shelf_io.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -215,7 +215,7 @@ final connectServerProvider = FutureProvider((ref) async { ref.read(volumeProvider.notifier).setVolume(event.data); }); } catch (e, stackTrace) { - Catcher2.reportCheckedError(e, stackTrace); + AppLogger.reportError(e, stackTrace); channel.sink.add(WebSocketErrorEvent(e.toString()).toJson()); } }, diff --git a/lib/provider/download_manager_provider.dart b/lib/provider/download_manager_provider.dart index c964f982..0e80d729 100644 --- a/lib/provider/download_manager_provider.dart +++ b/lib/provider/download_manager_provider.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; @@ -130,7 +130,7 @@ class DownloadManagerProvider extends ChangeNotifier { return Uint8List.fromList(bytes); } catch (e, stackTrace) { - Catcher2.reportCheckedError(e, stackTrace); + AppLogger.reportError(e, stackTrace); return null; } } @@ -216,7 +216,7 @@ class DownloadManagerProvider extends ChangeNotifier { ); } } catch (e) { - Catcher2.reportCheckedError(e, StackTrace.current); + AppLogger.reportError(e, StackTrace.current); continue; } } diff --git a/lib/provider/local_tracks/local_tracks_provider.dart b/lib/provider/local_tracks/local_tracks_provider.dart index 867774bd..6d2da59c 100644 --- a/lib/provider/local_tracks/local_tracks_provider.dart +++ b/lib/provider/local_tracks/local_tracks_provider.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:metadata_god/metadata_god.dart'; @@ -56,7 +56,7 @@ final localTracksProvider = try { entities.addAll(Directory(location).listSync(recursive: true)); } catch (e, stack) { - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); } } @@ -92,7 +92,7 @@ final localTracksProvider = if (e is FfiException) { return {"file": file}; } - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); return {}; } }, @@ -119,7 +119,7 @@ final localTracksProvider = } return tracks; } catch (e, stack) { - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); return {}; } }); diff --git a/lib/provider/piped_instances_provider.dart b/lib/provider/piped_instances_provider.dart index d571f730..3c5d5f04 100644 --- a/lib/provider/piped_instances_provider.dart +++ b/lib/provider/piped_instances_provider.dart @@ -1,4 +1,4 @@ -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:piped_client/piped_client.dart'; import 'package:spotube/services/sourced_track/sources/piped.dart'; @@ -10,7 +10,7 @@ final pipedInstancesFutureProvider = FutureProvider>( return await pipedClient.instanceList(); } catch (e, stack) { - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); return []; } }, diff --git a/lib/provider/proxy_playlist/player_listeners.dart b/lib/provider/proxy_playlist/player_listeners.dart index 3c36491c..2c1423a5 100644 --- a/lib/provider/proxy_playlist/player_listeners.dart +++ b/lib/provider/proxy_playlist/player_listeners.dart @@ -2,7 +2,7 @@ import 'dart:async'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:palette_generator/palette_generator.dart'; import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/extensions/image.dart'; @@ -86,7 +86,7 @@ extension ProxyPlaylistListeners on ProxyPlaylistNotifier { history.addTrack(playlist.activeTrack!); lastScrobbled = uid; } catch (e, stack) { - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); } }); } diff --git a/lib/provider/proxy_playlist/skip_segments.dart b/lib/provider/proxy_playlist/skip_segments.dart index 7f3d1e9a..12d066ac 100644 --- a/lib/provider/proxy_playlist/skip_segments.dart +++ b/lib/provider/proxy_playlist/skip_segments.dart @@ -1,4 +1,4 @@ -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:dio/dio.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/models/skip_segment.dart'; @@ -71,7 +71,7 @@ Future> getAndCacheSkipSegments(String id) async { return List.castFrom(segments); } catch (e, stack) { await SkipSegment.box.put(id, []); - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); return List.castFrom([]); } } diff --git a/lib/provider/scrobbler_provider.dart b/lib/provider/scrobbler_provider.dart index 9ad2a58b..ab111ea4 100644 --- a/lib/provider/scrobbler_provider.dart +++ b/lib/provider/scrobbler_provider.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:scrobblenaut/scrobblenaut.dart'; import 'package:spotify/spotify.dart'; @@ -52,7 +52,7 @@ class ScrobblerNotifier extends PersistedStateNotifier { trackNumber: track.trackNumber, ); } catch (e, stackTrace) { - Catcher2.reportCheckedError(e, stackTrace); + AppLogger.reportError(e, stackTrace); } }); } diff --git a/lib/provider/server/server.dart b/lib/provider/server/server.dart index 009cc534..b6a7dfe9 100644 --- a/lib/provider/server/server.dart +++ b/lib/provider/server/server.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'dart:math'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:dio/dio.dart' hide Response; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -108,7 +108,7 @@ class PlaybackServer { headers: res.headers.map, ); } catch (e, stack) { - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); return Response.internalServerError(); } } diff --git a/lib/provider/spotify/lyrics/synced.dart b/lib/provider/spotify/lyrics/synced.dart index 04a2ddca..ef83a1a1 100644 --- a/lib/provider/spotify/lyrics/synced.dart +++ b/lib/provider/spotify/lyrics/synced.dart @@ -145,7 +145,7 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier return lyrics; } catch (e, stackTrace) { - Catcher2.reportCheckedError(e, stackTrace); + AppLogger.reportError(e, stackTrace); rethrow; } } diff --git a/lib/provider/spotify/playlist/generate.dart b/lib/provider/spotify/playlist/generate.dart index 15447b54..2e1196dd 100644 --- a/lib/provider/spotify/playlist/generate.dart +++ b/lib/provider/spotify/playlist/generate.dart @@ -24,7 +24,7 @@ final generatePlaylistProvider = FutureProvider.autoDispose ?.cast(), ) .catchError((e, stackTrace) { - Catcher2.reportCheckedError(e, stackTrace); + AppLogger.reportError(e, stackTrace); return Recommendations(); }); diff --git a/lib/provider/spotify/spotify.dart b/lib/provider/spotify/spotify.dart index ac83ba72..e592e93b 100644 --- a/lib/provider/spotify/spotify.dart +++ b/lib/provider/spotify/spotify.dart @@ -2,7 +2,7 @@ library spotify; import 'dart:async'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; diff --git a/lib/services/audio_player/audio_player.dart b/lib/services/audio_player/audio_player.dart index 8d3e0bfb..8b391a07 100644 --- a/lib/services/audio_player/audio_player.dart +++ b/lib/services/audio_player/audio_player.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:flutter/foundation.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/models/local_track.dart'; @@ -57,7 +57,7 @@ abstract class AudioPlayerInterface { ), ) { _mkPlayer.stream.error.listen((event) { - Catcher2.reportCheckedError(event, StackTrace.current); + AppLogger.reportError(event, StackTrace.current); }); } diff --git a/lib/services/audio_player/custom_player.dart b/lib/services/audio_player/custom_player.dart index e32a0d14..f0dc8f13 100644 --- a/lib/services/audio_player/custom_player.dart +++ b/lib/services/audio_player/custom_player.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:media_kit/media_kit.dart'; import 'package:flutter_broadcasts/flutter_broadcasts.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -48,7 +48,7 @@ class CustomPlayer extends Player { } }), stream.error.listen((event) { - Catcher2.reportCheckedError('[MediaKitError] \n$event', null); + AppLogger.reportError('[MediaKitError] \n$event', StackTrace.current); }), ]; PackageInfo.fromPlatform().then((packageInfo) { diff --git a/lib/services/download_manager/download_manager.dart b/lib/services/download_manager/download_manager.dart index 54d35b02..afbee88c 100644 --- a/lib/services/download_manager/download_manager.dart +++ b/lib/services/download_manager/download_manager.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:collection'; import 'dart:io'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; @@ -148,7 +148,7 @@ class DownloadManager { } } } catch (e, stackTrace) { - Catcher2.reportCheckedError(e, stackTrace); + AppLogger.reportError(e, stackTrace); var task = getDownload(url)!; if (task.status.value != DownloadStatus.canceled && diff --git a/lib/services/logger/logger.dart b/lib/services/logger/logger.dart new file mode 100644 index 00000000..1a9f3771 --- /dev/null +++ b/lib/services/logger/logger.dart @@ -0,0 +1,53 @@ +import 'dart:async'; +import 'dart:isolate'; + +import 'package:flutter/foundation.dart'; +import 'package:logger/logger.dart'; + +class AppLogger { + static late final Logger log; + + static initialize(bool verbose) { + log = Logger( + level: kDebugMode || (verbose && kReleaseMode) ? Level.all : Level.info, + ); + } + + static R? runZoned(R Function() body) { + FlutterError.onError = (details) { + reportError(details.exception, details.stack ?? StackTrace.current); + }; + + PlatformDispatcher.instance.onError = (error, stackTrace) { + reportError(error, stackTrace); + return true; + }; + + if (!kIsWeb) { + Isolate.current.addErrorListener( + RawReceivePort((pair) async { + final isolateError = pair as List; + reportError( + isolateError.first.toString(), + isolateError.last, + ); + }).sendPort, + ); + } + + return runZonedGuarded( + body, + (error, stackTrace) { + reportError(error, stackTrace); + }, + ); + } + + static void reportError( + dynamic error, [ + StackTrace? stackTrace, + message = "", + ]) { + log.e(message, error: error, stackTrace: stackTrace); + } +} diff --git a/lib/services/song_link/song_link.dart b/lib/services/song_link/song_link.dart index b02f60cb..e3cffa52 100644 --- a/lib/services/song_link/song_link.dart +++ b/lib/services/song_link/song_link.dart @@ -2,7 +2,7 @@ library song_link; import 'dart:convert'; -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:dio/dio.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:html/parser.dart'; @@ -47,7 +47,7 @@ abstract class SongLinkService { return songLinks?.map((link) => SongLink.fromJson(link)).toList() ?? []; } catch (e, stackTrace) { - Catcher2.reportCheckedError(e, stackTrace); + AppLogger.reportError(e, stackTrace); return []; } } diff --git a/lib/services/sourced_track/sources/youtube.dart b/lib/services/sourced_track/sources/youtube.dart index af61a882..b144d701 100644 --- a/lib/services/sourced_track/sources/youtube.dart +++ b/lib/services/sourced_track/sources/youtube.dart @@ -1,9 +1,9 @@ -import 'package:catcher_2/core/catcher_2.dart'; import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/models/source_match.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/song_link/song_link.dart'; import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/services/sourced_track/exceptions.dart'; @@ -236,7 +236,7 @@ class YoutubeSourcedTrack extends SourcedTrack { ]; } on VideoUnplayableException catch (e, stack) { // Ignore this error and continue with the search - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); } } diff --git a/lib/utils/duration.dart b/lib/utils/duration.dart index a2bb4d16..1869cea1 100644 --- a/lib/utils/duration.dart +++ b/lib/utils/duration.dart @@ -1,4 +1,4 @@ -import 'package:catcher_2/catcher_2.dart'; +import 'package:spotube/services/logger/logger.dart'; /// Parses duration string formatted by Duration.toString() to [Duration]. /// The string should be of form hours:minutes:seconds.microseconds @@ -51,7 +51,7 @@ Duration? tryParseDuration(String input) { try { return parseDuration(input); } catch (e, stack) { - Catcher2.reportCheckedError(e, stack); + AppLogger.reportError(e, stack); return null; } }