import 'dart:io'; import '../../core/subtitle_detector.dart'; /// 탐색기 오른쪽 클릭 메뉴 등록 (현재 사용자만, 관리자 권한 불필요) /// /// JJ_MKVMaker 로 재생 → jj_mkvmaker.exe --play "파일" /// JJ_MKVMaker 로 자막 만들기 → jj_mkvmaker.exe --subtitle "파일" /// /// Windows 11 에서는 "더 많은 옵션 표시" 안에 나타난다. class ShellIntegration { static const _root = r'HKCU\Software\Classes\SystemFileAssociations'; static const _verbs = { 'JJMKVMaker.Play': ('JJ_MKVMaker 로 재생', '--play'), 'JJMKVMaker.Subtitle': ('JJ_MKVMaker 로 자막 만들기', '--subtitle'), }; final String exePath; final List extensions; ShellIntegration({String? exePath, List? extensions}) : exePath = exePath ?? Platform.resolvedExecutable, extensions = extensions ?? videoExtensions; static Future _reg(List args) async => (await Process.run('reg', args)).exitCode == 0; Future isRegistered() => _reg(['query', '$_root\\.${extensions.first}\\shell\\JJMKVMaker.Play']); /// 이전 이름(JJ CapCut)으로 등록된 메뉴 static const _legacyVerbs = ['JJCapCut.Play', 'JJCapCut.Subtitle']; Future register() async { await _removeLegacy(); var ok = true; for (final ext in extensions) { for (final e in _verbs.entries) { final key = '$_root\\.$ext\\shell\\${e.key}'; final (label, flag) = e.value; ok &= await _reg(['add', key, '/ve', '/d', label, '/f']); ok &= await _reg(['add', key, '/v', 'Icon', '/d', '"$exePath",0', '/f']); // 파일을 많이 골라도 메뉴가 보이도록 ok &= await _reg(['add', key, '/v', 'MultiSelectModel', '/d', 'Player', '/f']); ok &= await _reg(['add', '$key\\command', '/ve', '/d', '"$exePath" $flag "%1"', '/f']); } } return ok; } Future unregister() async { await _removeLegacy(); for (final ext in extensions) { for (final verb in _verbs.keys) { await _reg(['delete', '$_root\\.$ext\\shell\\$verb', '/f']); } } } Future _removeLegacy() async { for (final ext in extensions) { for (final verb in _legacyVerbs) { await _reg(['delete', '$_root\\.$ext\\shell\\$verb', '/f']); } } } } /// 외부 플레이어 찾기 String? findVlc() { for (final base in [ Platform.environment['ProgramFiles'], Platform.environment['ProgramFiles(x86)'], Platform.environment['ProgramW6432'], ]) { if (base == null) continue; final f = '$base\\VideoLAN\\VLC\\vlc.exe'; if (File(f).existsSync()) return f; } return null; } /// 외부 프로그램으로 열기. [program] 이 'system' 이면 Windows 기본 연결 프로그램 (첫 파일만). Future openExternally(String program, List files) async { if (files.isEmpty) return; if (program == 'system') { await Process.start('explorer.exe', [files.first], mode: ProcessStartMode.detached); } else { await Process.start(program, files, mode: ProcessStartMode.detached); } }