118 lines
3.8 KiB
Dart
118 lines
3.8 KiB
Dart
import 'dart:async';
|
||
import 'dart:ui' as ui;
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:media_kit/media_kit.dart';
|
||
|
||
import 'core/log/action_logger.dart';
|
||
import 'core/media/download_manager.dart';
|
||
import 'core/media/local_media_server.dart';
|
||
import 'core/storage/local_store.dart';
|
||
import 'core/storage/search_history_store.dart';
|
||
import 'core/storage/watch_dedup_store.dart';
|
||
import 'core/theme/app_theme.dart';
|
||
import 'core/config/app_config.dart';
|
||
import 'router.dart';
|
||
|
||
/// App 入口:本地存储 + MediaKit + MaterialApp.router
|
||
Future<void> main() async {
|
||
// 全局异常捕获:把 Dart 侧未捕获异常上报到操作日志(仅白名单用户启用)。
|
||
// 注意:原生层(mpv/内核)崩溃这里捕获不到,只能拿到 Dart/Framework 层。
|
||
runZonedGuarded(() async {
|
||
WidgetsFlutterBinding.ensureInitialized();
|
||
|
||
// Flutter Framework 错误(build/layout/paint 阶段抛出)
|
||
FlutterError.onError = (details) {
|
||
FlutterError.presentError(details);
|
||
ActionLogger.I.log(
|
||
'crash_flutter_error',
|
||
page: 'app',
|
||
detail: {
|
||
'exception': details.exceptionAsString(),
|
||
'library': details.library ?? '',
|
||
'stack': (details.stack ?? StackTrace.empty).toString().split('\n').take(20).join('\n'),
|
||
},
|
||
);
|
||
unawaited(ActionLogger.I.flush());
|
||
};
|
||
|
||
// Dart 异步/平台通道未捕获错误
|
||
ui.PlatformDispatcher.instance.onError = (error, stack) {
|
||
ActionLogger.I.log(
|
||
'crash_platform_error',
|
||
page: 'app',
|
||
detail: {
|
||
'exception': error.toString(),
|
||
'stack': stack.toString().split('\n').take(20).join('\n'),
|
||
},
|
||
);
|
||
unawaited(ActionLogger.I.flush());
|
||
return true;
|
||
};
|
||
|
||
// 版本信息(版本检查/请求头 X-App-Version 依赖,必须先初始化)
|
||
await AppConfig.initVersion();
|
||
// MediaKit(libmpv 底层)全局初始化:每个进程必须且仅调一次
|
||
MediaKit.ensureInitialized();
|
||
// 本地存储初始化(设备指纹在 splash 阶段生成)
|
||
await LocalStore.init();
|
||
// 搜索历史(本地,不上报服务端)
|
||
await SearchHistoryStore.init();
|
||
// 观看上报去重(持久化;不初始化则观看计费/游客免费额度链路失效)
|
||
await WatchDedupStore.init();
|
||
// 本地媒体服务:HLS 1G 分片缓存(回看免下载)+ 已下载视频离线播放
|
||
await LocalMediaServer.I.init();
|
||
|
||
runApp(ProviderScope(
|
||
overrides: const [],
|
||
child: const _AppInitializer(),
|
||
));
|
||
}, (error, stack) {
|
||
// Zone 内未捕获错误(一般与 PlatformDispatcher 重叠,双保险)
|
||
ActionLogger.I.log(
|
||
'crash_zone',
|
||
page: 'app',
|
||
detail: {
|
||
'exception': error.toString(),
|
||
'stack': stack.toString().split('\n').take(20).join('\n'),
|
||
},
|
||
);
|
||
unawaited(ActionLogger.I.flush());
|
||
});
|
||
}
|
||
|
||
/// 初始化需要 Provider 环境的单例(下载管理器扫描本地已下载列表)
|
||
class _AppInitializer extends ConsumerStatefulWidget {
|
||
const _AppInitializer();
|
||
|
||
@override
|
||
ConsumerState<_AppInitializer> createState() => _AppInitializerState();
|
||
}
|
||
|
||
class _AppInitializerState extends ConsumerState<_AppInitializer> {
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
ref.read(downloadManagerProvider.notifier).init();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) => const VideoApp();
|
||
}
|
||
|
||
class VideoApp extends ConsumerWidget {
|
||
const VideoApp({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context, WidgetRef ref) {
|
||
final router = ref.watch(routerProvider);
|
||
return MaterialApp.router(
|
||
title: '短视频',
|
||
debugShowCheckedModeBanner: false,
|
||
theme: AppTheme.dark,
|
||
routerConfig: router,
|
||
);
|
||
}
|
||
}
|