JJ_MKVMaker/lib/app/settings/mkv_settings.dart

119 lines
5.5 KiB
Dart

import '../../core/encode_options.dart';
import 'records.dart';
import 'settings_group.dart';
/// 약속 (설정 A): 환경 설정 > MKV · 자막 만들기 (저장 위치 · 이동 버튼 · 인코딩 · AI 자막 · OpenSubtitles) - 자기 키 (이름 · 처음 값 · 읽기 · 쓰기) 를 스스로 갖는다.
/// 설정 하나를 더할 때는 이 파일만 고친다. 저장 파일의 키 이름은 예전 그대로 (평평한 키)
class MkvSettings implements SettingsGroup {
/// MKV·자막 저장 위치. null 이면 동영상이 있는 폴더 아래 jj_mkv
String? outputRoot;
/// MKV 세부 정보 오른쪽 아래의 이동 버튼들 (표시 이름 · 폴더). 세 번 누르기는 첫 번째 버튼의 폴더로.
/// 비어 있으면 [이동] 을 처음 누를 때 폴더를 골라 하나 만든다.
List<MoveTarget> moveTargets = [];
/// AI 자막: 매번 설정 창을 띄울지 (끄면 마지막 설정으로 바로 시작)
bool askAiOptions = true;
/// OpenSubtitles (인터넷 자막 검색) - 키는 필수, 아이디·비밀번호는 선택 (받기 횟수 늘어남)
String openSubtitlesKey = '';
String openSubtitlesUser = '';
String openSubtitlesPassword = '';
/// 동시에 만드는 MKV 수 (0 = 무제한)
int maxParallelJobs = 5;
/// 폰 세로 (좁은 화면) MKV 목록 크기: 'auto' 목록은 내용만큼 · 자세히 칸에 최소 높이 (처음 값) · 'small' · 'large' (관리자 판단 10/17)
String compactList = 'auto';
/// P0: MKV 를 만들 때 결과 MKV 가 이미 있으면 - 'ask' 묻기 (처음 값) · 'overwrite' 덮어쓰기 · 'skip' 건너뛰기 · 'rename' 다른 이름.
/// 덮어쓰기여도 다 만든 뒤에만 바꿔 넣는다 (도중에 취소 · 실패하면 예전 것 그대로)
String existing = 'ask';
/// 47: MKV 목록에서 세 번 누르면 'move' (확인 뒤 이동 폴더로) · 'none' (아무것도 안 함)
String tripleTapAction = 'move';
/// 마지막 인코딩 설정
EncodeSettings encode = const EncodeSettings();
/// 마지막 AI 자막 설정
String aiSource = 'und';
List<String> aiTargets = ['ko', 'en', 'ja'];
String aiWhisper = 'whisper-base';
/// 색 보정 값 (-100 ~ 100)
static int _adj(Object? v) => ((v as num?)?.round() ?? 0).clamp(-100, 100);
@override
void write(Map<String, Object?> j, {required bool plainSecrets}) {
j['mkvOutputRoot'] = outputRoot;
j['moveTargets'] = [for (final t in moveTargets) t.toJson()];
j['askAiOptions'] = askAiOptions;
// 비밀 값은 안전 저장소에 (54). 파일에는 저장해 두었는지만
j['openSubtitlesSaved'] = {
'key': openSubtitlesKey.isNotEmpty,
'user': openSubtitlesUser.isNotEmpty,
'password': openSubtitlesPassword.isNotEmpty,
};
if (plainSecrets) {
j['openSubtitlesKey'] = openSubtitlesKey;
j['openSubtitlesUser'] = openSubtitlesUser;
j['openSubtitlesPassword'] = openSubtitlesPassword;
}
j['maxParallelJobs'] = maxParallelJobs;
j['mkvCompactList'] = compactList;
j['mkvExisting'] = existing;
j['tripleTapAction'] = tripleTapAction;
j['encode'] = {
'codec': encode.codec.name,
'resolution': encode.resolution.name,
'quality': encode.quality.name,
'frame': encode.frame.name,
'fit': encode.fit.name,
'rotate': encode.rotate.name,
'brightness': encode.brightness,
'contrast': encode.contrast,
'saturation': encode.saturation,
'temperature': encode.temperature,
};
j['aiSource'] = aiSource;
j['aiTargets'] = aiTargets;
j['aiWhisper'] = aiWhisper;
}
@override
void read(Map<String, dynamic> j) {
final e = (j['encode'] as Map?) ?? const {};
outputRoot = j['mkvOutputRoot'] as String?;
moveTargets = [
for (final x in (j['moveTargets'] as List?) ?? const [])
if (x is Map && x['dir'] is String) MoveTarget.fromJson(x),
// 예전 판의 이동 폴더 하나 → "이동" 버튼으로
if (j['moveTargets'] == null && j['moveTargetDir'] is String) MoveTarget('이동', j['moveTargetDir'] as String),
];
askAiOptions = j['askAiOptions'] as bool? ?? true;
openSubtitlesKey = j['openSubtitlesKey'] as String? ?? '';
openSubtitlesUser = j['openSubtitlesUser'] as String? ?? '';
openSubtitlesPassword = j['openSubtitlesPassword'] as String? ?? '';
maxParallelJobs = (j['maxParallelJobs'] as num?)?.toInt() ?? 5;
compactList = const ['auto', 'small', 'large'].contains(j['mkvCompactList']) ? j['mkvCompactList'] as String : 'auto';
existing = const ['rename', 'overwrite', 'skip'].contains(j['mkvExisting']) ? j['mkvExisting'] as String : 'ask';
tripleTapAction = j['tripleTapAction'] == 'none' ? 'none' : 'move';
encode = EncodeSettings(
codec: pickEnum(VideoCodecChoice.values, e['codec'], VideoCodecChoice.copy),
resolution: pickEnum(ResolutionChoice.values, e['resolution'], ResolutionChoice.original),
quality: pickEnum(QualityChoice.values, e['quality'], QualityChoice.normal),
frame: pickEnum(FrameChoice.values, e['frame'], FrameChoice.original),
fit: pickEnum(FitChoice.values, e['fit'], FitChoice.fill),
rotate: pickEnum(RotateChoice.values, e['rotate'], RotateChoice.none),
brightness: _adj(e['brightness']),
contrast: _adj(e['contrast']),
saturation: _adj(e['saturation']),
temperature: _adj(e['temperature']),
);
aiSource = j['aiSource'] as String? ?? 'und';
aiTargets = (j['aiTargets'] as List?)?.cast<String>() ?? ['ko', 'en', 'ja'];
aiWhisper = j['aiWhisper'] as String? ?? 'whisper-base';
}
}