91 lines
3.1 KiB
Dart
91 lines
3.1 KiB
Dart
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<String> extensions;
|
|
|
|
ShellIntegration({String? exePath, List<String>? extensions})
|
|
: exePath = exePath ?? Platform.resolvedExecutable,
|
|
extensions = extensions ?? videoExtensions;
|
|
|
|
static Future<bool> _reg(List<String> args) async =>
|
|
(await Process.run('reg', args)).exitCode == 0;
|
|
|
|
Future<bool> isRegistered() => _reg(['query', '$_root\\.${extensions.first}\\shell\\JJMKVMaker.Play']);
|
|
|
|
/// 이전 이름(JJ CapCut)으로 등록된 메뉴
|
|
static const _legacyVerbs = ['JJCapCut.Play', 'JJCapCut.Subtitle'];
|
|
|
|
Future<bool> 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<void> unregister() async {
|
|
await _removeLegacy();
|
|
for (final ext in extensions) {
|
|
for (final verb in _verbs.keys) {
|
|
await _reg(['delete', '$_root\\.$ext\\shell\\$verb', '/f']);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _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<void> openExternally(String program, List<String> 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);
|
|
}
|
|
}
|