import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:path/path.dart' as p; import 'app/app_controller.dart'; import 'app/download_manager.dart'; import 'app/settings.dart'; import 'core/playlist.dart'; import 'platform/windows/single_instance.dart'; import 'services/platform_services.dart'; import 'ui/ai_dialog.dart'; import 'ui/downloads_page.dart'; import 'ui/home_page.dart'; import 'ui/player_page.dart'; import 'ui/setup_dialog.dart'; import 'ui/update_dialog.dart'; import 'ui/theme.dart'; Future main(List args) async { WidgetsFlutterBinding.ensureInitialized(); // 이미 실행 중이면 인수(탐색기에서 고른 파일)를 넘기고 끝낸다 final instance = SingleInstance(); if (!await instance.claim(LaunchRequest.parse(args), waitForExit: args.contains('--restart'))) { exit(0); } final services = PlatformServices.create(); final controller = AppController(services, settingsStore: SettingsStore()); await controller.init(); final downloads = DownloadManager( backends: services.createDownloadBackends?.call(() => controller.settings) ?? const [], settings: () => controller.settings, ); final navigatorKey = GlobalKey(); final messengerKey = GlobalKey(); /// 종료 요청 (창 X · 트레이 종료 · 종료 버튼): 다운로드 중이면 확인 Future confirmQuit() async { final ctx = navigatorKey.currentContext; if (ctx != null && !await confirmExit(ctx, downloads)) return false; await downloads.shutdown(); return true; } await services.shell.init( hotkey: controller.settings.showHotkey, minimizeToTray: () => controller.settings.minimizeToTray, onCloseRequested: confirmQuit, onDownloadsRequested: () => navigatorKey.currentState ?.push(MaterialPageRoute(builder: (_) => DownloadsPage(d: downloads))), ); // 클립보드로 추가된 다운로드 알림 · 트레이 글 downloads.onAdded.listen((t) => messengerKey.currentState ?.showSnackBar(SnackBar(content: Text('다운로드 추가: ${t.source}')))); downloads.addListener(() { final n = downloads.downloadingCount; services.shell.setTooltip(n > 0 ? 'JJ_MKVMaker - 다운로드 $n개' : 'JJ_MKVMaker'); }); unawaited(downloads.startClipboardWatch()); // 탐색기 메뉴 · 두 번째 실행에서 온 요청 처리 instance.listen((req) async { await services.shell.show(); final ctx = navigatorKey.currentContext; if (ctx == null || req.files.isEmpty) return; if (!ctx.mounted) return; switch (req.action) { case LaunchAction.play: await playFiles(ctx, controller, req.files); case LaunchAction.subtitle: await controller.addVideos(req.files.where(isVideoFile).toList()); final targets = controller.videos .where((v) => req.files.any((f) => p.equals(f, v.path))) .toList(); if (targets.isNotEmpty && ctx.mounted) { controller.select(targets.first); await showAiDialog(ctx, controller, targets.first, targets: targets); } case LaunchAction.add: await controller.addVideos(req.files.where(isVideoFile).toList()); } }); controller.confirmQuit = confirmQuit; // 처음 화면이 뜬 뒤: 필수 프로그램 점검 (없으면 물어보고 설치) → 새 버전 확인 (하루 한 번) WidgetsBinding.instance.addPostFrameCallback((_) async { final ctx = navigatorKey.currentContext; if (ctx == null) return; await checkRequiredTools(ctx, services.shell); final ctx2 = navigatorKey.currentContext; if (ctx2 != null && ctx2.mounted) await checkForUpdate(ctx2, controller); }); runApp(JjCapCutApp( controller: controller, downloads: downloads, navigatorKey: navigatorKey, messengerKey: messengerKey, onExit: () async { if (await confirmQuit()) await services.shell.quit(); }, )); } class JjCapCutApp extends StatelessWidget { final AppController controller; final DownloadManager? downloads; final GlobalKey? navigatorKey; final GlobalKey? messengerKey; final VoidCallback? onExit; const JjCapCutApp({ super.key, required this.controller, this.downloads, this.navigatorKey, this.messengerKey, this.onExit, }); @override Widget build(BuildContext context) => MaterialApp( title: 'JJ_MKVMaker', debugShowCheckedModeBanner: false, theme: buildTheme(), navigatorKey: navigatorKey, scaffoldMessengerKey: messengerKey, home: HomePage(c: controller, downloads: downloads, onExit: onExit), ); }