refactor: remove catcher_2 and use custom zoned based error handling

This commit is contained in:
Kingkor Roy Tirtho 2024-06-09 22:52:34 +06:00
parent d115e57058
commit f9087b63d5
23 changed files with 148 additions and 122 deletions

View File

@ -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<NavigatorState>();
final shellRouteNavigatorKey = GlobalKey<NavigatorState>();
final routerProvider = Provider((ref) {
return GoRouter(

View File

@ -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);
}
}

View File

@ -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<void> main(List<String> 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>(
SourceMatch.boxName,
path: hiveCacheDir,
);
await Hive.openLazyBox(
SkipSegment.boxName,
path: hiveCacheDir,
);
await PersistedStateNotifier.initializeBoxes(
path: hiveCacheDir,
);
await Hive.openLazyBox<SourceMatch>(
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;

View File

@ -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,

View File

@ -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<WebSocketChannel?> {
});
},
onError: (error) {
Catcher2.reportCheckedError(
error,
StackTrace.current,
);
AppLogger.reportError(error, StackTrace.current);
},
);
@ -113,7 +110,7 @@ class ConnectNotifier extends AsyncNotifier<WebSocketChannel?> {
return channel;
} catch (e, stack) {
Catcher2.reportCheckedError(e, stack);
AppLogger.reportError(e, stack);
rethrow;
}
}

View File

@ -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());
}
},

View File

@ -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;
}
}

View File

@ -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 {};
}
});

View File

@ -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<List<PipedInstance>>(
return await pipedClient.instanceList();
} catch (e, stack) {
Catcher2.reportCheckedError(e, stack);
AppLogger.reportError(e, stack);
return <PipedInstance>[];
}
},

View File

@ -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);
}
});
}

View File

@ -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<List<SkipSegment>> getAndCacheSkipSegments(String id) async {
return List.castFrom<dynamic, SkipSegment>(segments);
} catch (e, stack) {
await SkipSegment.box.put(id, []);
Catcher2.reportCheckedError(e, stack);
AppLogger.reportError(e, stack);
return List.castFrom<dynamic, SkipSegment>([]);
}
}

View File

@ -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<ScrobblerState?> {
trackNumber: track.trackNumber,
);
} catch (e, stackTrace) {
Catcher2.reportCheckedError(e, stackTrace);
AppLogger.reportError(e, stackTrace);
}
});
}

View File

@ -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();
}
}

View File

@ -145,7 +145,7 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?>
return lyrics;
} catch (e, stackTrace) {
Catcher2.reportCheckedError(e, stackTrace);
AppLogger.reportError(e, stackTrace);
rethrow;
}
}

View File

@ -24,7 +24,7 @@ final generatePlaylistProvider = FutureProvider.autoDispose
?.cast<String, num>(),
)
.catchError((e, stackTrace) {
Catcher2.reportCheckedError(e, stackTrace);
AppLogger.reportError(e, stackTrace);
return Recommendations();
});

View File

@ -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';

View File

@ -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);
});
}

View File

@ -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) {

View File

@ -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 &&

View File

@ -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>(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<dynamic>;
reportError(
isolateError.first.toString(),
isolateError.last,
);
}).sendPort,
);
}
return runZonedGuarded<R>(
body,
(error, stackTrace) {
reportError(error, stackTrace);
},
);
}
static void reportError(
dynamic error, [
StackTrace? stackTrace,
message = "",
]) {
log.e(message, error: error, stackTrace: stackTrace);
}
}

View File

@ -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() ??
<SongLink>[];
} catch (e, stackTrace) {
Catcher2.reportCheckedError(e, stackTrace);
AppLogger.reportError(e, stackTrace);
return <SongLink>[];
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}