119 lines
5.5 KiB
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';
|
|
}
|
|
}
|