JJ_MKVMaker/lib/app/settings/ai_image_settings.dart

151 lines
6.1 KiB
Dart

import '../../services/image_ai.dart';
import 'settings_group.dart';
/// 약속 (설정 A): 환경 설정 > AI 그림 · 해상도 올리기 (123 · 121) - 자기 키 (이름 · 처음 값 · 읽기 · 쓰기) 를 스스로 갖는다.
/// 설정 하나를 더할 때는 이 파일만 고친다. 저장 파일의 키 이름은 예전 그대로 (평평한 키)
class AiImageSettings implements SettingsGroup {
/// 그리는 엔진: 'local' 이 기기 안 (기본) · 아니면 [services] 의 id (사용자가 추가한 서버 · 서비스)
String engine = 'local';
/// 기기 안 처리 장치: 'auto' (잰 결과로 빠른 쪽) 또는 장치 열쇠 (예: 'engine-cuda:cuda0', 'engine-vulkan:cpu')
String device = 'auto';
/// 'auto' 가 잰 가장 빠른 장치와 그때의 장치 목록 (목록이 바뀌면 다시 잰다)
String autoDevice = '';
String autoDeviceFor = '';
/// 빠른 디코더 (TAESD) - 그림이 거의 같고 디코드가 10배쯤 빠르다 (받아 두었을 때)
bool taesd = true;
int width = 512;
int height = 512;
int steps = 4;
double cfg = 1;
int count = 1;
double strength = 0.6;
String negative = '';
/// 159: 마지막 프롬프트와 만든 그림 목록 (최근 것부터, 업데이트 · 다시 열어도 남게)
String prompt = '';
List<String> recent = [];
/// 저장 폴더 (비어 있으면 사진 폴더의 JJ_MKVMaker_AI)
String saveDir = '';
/// 사용자가 추가한 그림 서버 · 서비스 (API 키는 안전 저장소에)
List<AiService> services = [];
/// 라이선스 조건에 동의한 받는 파일 (149-④: 한 번 동의하면 다시 묻지 않음)
List<String> agreed = [];
/// 모델 'auto' (GPU 면 사진용, CPU 뿐이면 가볍고 빠른 만화용) · 'photo' · 'anime'
String upModel = 'auto';
/// 176: 해상도 올리기 메모리 (Android CPU 타일 크기) - 'low' 20% · 'normal' 30% (처음 값) · 'high' 45% (기기 메모리 중)
String upMemory = 'normal';
/// 156-2: 해상도 올리기에 걸린 시간 (처리 장치|모델 → 원본 100만 화소당 초) - "한 장 약 n초" 를 미리 알리려고
Map<String, double> upSecPerMp = {};
/// 배율 2 · 3 · 4
int upScale = 2;
/// 관리자 판단 (10/17): 보기 화면의 [품질 올리기] 단추 (그림 → 그림 약하게 + 해상도 올리기). 처음 값: 끔
bool qualityButton = false;
/// [품질 올리기] 다듬기 정도 (그림 → 그림 바꾸는 정도): 'weak' 0.15 · 'normal' 0.25 (처음 값) · 'strong' 0.4 (관리자 지시 - 판단은 설정으로)
String qualityStrength = 'normal';
/// 저장 형식 'same' (원본과 같게, 못 쓰는 형식은 PNG) · 'png' · 'jpg' · JPG 품질
String upFormat = 'same';
int upJpgQuality = 92;
/// 저장 위치 (비어 있으면 원본 옆 "이름_x2")
String upDir = '';
/// 저장한 뒤 'upscaled' 올린 것으로 계속 보기 · 'original' 원본으로
String upAfter = 'upscaled';
@override
void write(Map<String, Object?> j, {required bool plainSecrets}) {
j['aiEngine'] = engine;
j['aiDevice'] = device;
j['aiAutoDevice'] = autoDevice;
j['aiAutoDeviceFor'] = autoDeviceFor;
j['aiTaesd'] = taesd;
j['aiWidth'] = width;
j['aiHeight'] = height;
j['aiSteps'] = steps;
j['aiCfg'] = cfg;
j['aiCount'] = count;
j['aiStrength'] = strength;
j['aiNegative'] = negative;
j['aiPrompt'] = prompt;
j['aiRecent'] = recent;
j['aiSaveDir'] = saveDir;
j['aiServices'] = [
for (final x in services) {...x.toJson(), if (plainSecrets) 'apiKey': x.apiKey},
];
j['aiAgreed'] = agreed;
j['aiUpModel'] = upModel;
j['aiUpMemory'] = upMemory;
j['aiUpSecPerMp'] = upSecPerMp;
j['aiUpScale'] = upScale;
j['aiQualityButton'] = qualityButton;
j['aiQualityStrength'] = qualityStrength;
j['aiUpFormat'] = upFormat;
j['aiUpJpgQuality'] = upJpgQuality;
j['aiUpDir'] = upDir;
j['aiUpAfter'] = upAfter;
}
@override
void read(Map<String, dynamic> j) {
engine = j['aiEngine'] as String? ?? 'local';
device = j['aiDevice'] as String? ?? 'auto';
autoDevice = j['aiAutoDevice'] as String? ?? '';
autoDeviceFor = j['aiAutoDeviceFor'] as String? ?? '';
taesd = j['aiTaesd'] != false;
width = ((j['aiWidth'] as num?)?.toInt() ?? 512).clamp(64, 2048);
height = ((j['aiHeight'] as num?)?.toInt() ?? 512).clamp(64, 2048);
steps = ((j['aiSteps'] as num?)?.toInt() ?? 4).clamp(1, 150);
cfg = ((j['aiCfg'] as num?)?.toDouble() ?? 1).clamp(0, 30);
count = ((j['aiCount'] as num?)?.toInt() ?? 1).clamp(1, 100);
strength = ((j['aiStrength'] as num?)?.toDouble() ?? 0.6).clamp(0, 1);
negative = j['aiNegative'] as String? ?? '';
prompt = j['aiPrompt'] as String? ?? '';
recent = [for (final x in (j['aiRecent'] as List?) ?? const []) '$x'];
saveDir = j['aiSaveDir'] as String? ?? '';
agreed = [for (final x in (j['aiAgreed'] as List?) ?? const []) '$x'];
upModel = const ['photo', 'anime'].contains(j['aiUpModel']) ? j['aiUpModel'] as String : 'auto';
upMemory = const ['low', 'high'].contains(j['aiUpMemory']) ? j['aiUpMemory'] as String : 'normal';
upSecPerMp = {
if (j['aiUpSecPerMp'] case final Map m)
for (final e in m.entries)
if (e.value is num && (e.value as num) > 0) '${e.key}': (e.value as num).toDouble(),
};
// 1 = 크기는 그대로 선명하게만 (관리자 판단 10/17 - 사용자 질문에 답이 없어 넣되 처음 값은 2배 그대로)
upScale = const [1, 3, 4].contains(j['aiUpScale']) ? j['aiUpScale'] as int : 2;
qualityButton = j['aiQualityButton'] == true;
qualityStrength = const ['weak', 'normal', 'strong'].contains(j['aiQualityStrength']) ? j['aiQualityStrength'] as String : 'normal';
upFormat = const ['png', 'jpg'].contains(j['aiUpFormat']) ? j['aiUpFormat'] as String : 'same';
upJpgQuality = ((j['aiUpJpgQuality'] as num?)?.toInt() ?? 92).clamp(50, 100);
upDir = j['aiUpDir'] as String? ?? '';
upAfter = j['aiUpAfter'] == 'original' ? 'original' : 'upscaled';
services = [
for (final x in (j['aiServices'] as List?) ?? const [])
if (x is Map) AiService.fromJson(x),
];
}
}