Files
video/app/lib/main.dart
T
2026-10-25 13:26:40 +08:00

118 lines
3.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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,
);
}
}