JJ_MKVMaker/lib/main.dart

158 lines
5.6 KiB
Dart

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/bookmarks_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/browser_page.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<void> main(List<String> 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 bookmarks = BookmarksController();
await bookmarks.load();
final downloads = DownloadManager(
backends: services.createDownloadBackends?.call(() => controller.settings) ?? const [],
settings: () => controller.settings,
);
final navigatorKey = GlobalKey<NavigatorState>();
final messengerKey = GlobalKey<ScaffoldMessengerState>();
/// 종료 요청 (창 X · 트레이 종료 · 종료 버튼): 다운로드 중이면 확인
Future<bool> 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<void>(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');
});
// 다 받은 동영상 → 편집 목록 (설정에서 끌 수 있음)
downloads.onFinished.listen((t) async {
if (!controller.settings.addFinishedDownloads) return;
final files = DownloadManager.videoFilesOf(t);
if (files.isNotEmpty && await controller.addDownloaded(files) > 0) {
messengerKey.currentState?.showSnackBar(SnackBar(content: Text('편집 목록에 추가: ${t.title}')));
}
});
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;
// 시작 화면을 "웹 브라우저" 로 정했으면 브라우저를 연다 (MKV 화면은 그 아래에 있음)
if (controller.settings.startScreen == 'browser') {
navigatorKey.currentState?.push(MaterialPageRoute<void>(
builder: (_) => BrowserPage(c: controller, downloads: downloads, bookmarks: bookmarks)));
}
await checkRequiredTools(ctx, services.shell);
final ctx2 = navigatorKey.currentContext;
if (ctx2 != null && ctx2.mounted) await checkForUpdate(ctx2, controller);
});
runApp(JjCapCutApp(
controller: controller,
downloads: downloads,
bookmarks: bookmarks,
navigatorKey: navigatorKey,
messengerKey: messengerKey,
onExit: () async {
if (await confirmQuit()) await services.shell.quit();
},
));
}
class JjCapCutApp extends StatelessWidget {
final AppController controller;
final DownloadManager? downloads;
final BookmarksController? bookmarks;
final GlobalKey<NavigatorState>? navigatorKey;
final GlobalKey<ScaffoldMessengerState>? messengerKey;
final VoidCallback? onExit;
const JjCapCutApp({
super.key,
required this.controller,
this.downloads,
this.bookmarks,
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, bookmarks: bookmarks),
);
}