import 'records.dart'; import 'settings_group.dart'; /// 약속 (설정 A): 환경 설정 > 복사 · 이동 (파일 탐색기) · rsync · 실시간 동기화 (Rsync 화면) - 자기 키 (이름 · 처음 값 · 읽기 · 쓰기) 를 스스로 갖는다. /// 설정 하나를 더할 때는 이 파일만 고친다. 저장 파일의 키 이름은 예전 그대로 (평평한 키) class CopySettings implements SettingsGroup { /// 파일 탐색기의 복사 · 이동 방법: 'builtin' 현재 방식 (기본) · 'robocopy' (Windows). 파일만 고를 때 / 폴더가 들어 있을 때. /// rsync 는 Rsync 화면에서 따로 (파일 탐색기에서는 쓰지 않음) String methodFile = 'builtin'; /// 48: 복사 · 이동할 때 받는 폴더에 같은 이름이 있으면 'ask' (확인 창에서 고름, 처음 값) · 'rename' · 'overwrite' · 'skip' String conflict = 'ask'; /// 229: 앱이 직접 하는 복사 · 이동 (현재 방식 · WebDAV 의 안의 것 맞추기) 에서 받는 쪽이 더 새것이면 건너뛴다 (rsync -u 와 같음). /// 처음 값 켜짐 - 예전 원본으로 새 파일을 덮어쓰고 (이동이면 원본까지 지워) 새 내용을 잃었다 (관리자 판단) bool skipNewer = true; String methodFolder = 'builtin'; String rsyncOptions = '-avPog'; String robocopyOptions = '/E /COPY:DAT /DCOPY:T /R:2 /W:2'; /// 여러 개를 고르면: 'each' 항목마다 따로 실행 (기본) · 'once' 한 번에 (rsync) String runMode = 'each'; /// 속도 제한 KB/s (0 = 제한 없음). 모든 방법에 (rsync --bwlimit, 현재 방식은 앱이 조절, robocopy 는 /IPG 로 비슷하게) int bandwidthKBps = 0; /// rsync 가져오기: 'download' 앱에 들어 있는 것 (기본, Windows 는 없으면 내려받기) · 'custom' 직접 지정한 실행 파일 ([rsyncPath]) String rsyncSource = 'download'; String rsyncPath = ''; /// 실시간 동기화 (lsyncd 처럼) List liveSyncPairs = []; /// 모니터링 (Rsync 화면 가운데 [모니터링]): 실행한 rsync 를 기억해 다시 실행 · 옵션 고치기 · lsync 로 옮기기 List tasks = []; /// Rsync 화면에서 마지막으로 연 폴더 (왼쪽 · 오른쪽) List rsyncPaths = []; /// 92 (Windows): 실시간 동기화가 멈췄을 때 Windows 알림 bool syncStopToast = true; /// 실시간 동기화 확인 간격 (초). Windows 는 바뀌면 바로, 그 밖은 이 간격으로 살핀다. int liveSyncIntervalSec = 30; /// 앱을 다시 켤 때 실시간 동기화: 'auto' 바로 시작 / 'ask' 골라서 시작 / 'off' 시작 안 함 (모니터링에서 시작) String liveSyncOnStart = 'auto'; /// 파일 탐색기의 복사 방법 (rsync 는 Rsync 화면으로 옮겨 예전 값 rsync 도 현재 방식으로) static String _method(Object? v) => v == 'robocopy' ? 'robocopy' : 'builtin'; @override void write(Map j, {required bool plainSecrets}) { j['copyMethodFile'] = methodFile; j['copyConflict'] = conflict; j['copySkipNewer'] = skipNewer; j['copyMethodFolder'] = methodFolder; j['rsyncOptions'] = rsyncOptions; j['robocopyOptions'] = robocopyOptions; j['copyRunMode'] = runMode; j['copyBandwidthKBps'] = bandwidthKBps; j['rsyncSource'] = rsyncSource; j['rsyncPath'] = rsyncPath; j['liveSyncPairs'] = [for (final x in liveSyncPairs) x.toJson()]; j['liveSyncIntervalSec'] = liveSyncIntervalSec; j['liveSyncOnStart'] = liveSyncOnStart; j['copyTasks'] = [for (final x in tasks) x.toJson()]; j['rsyncPaths'] = rsyncPaths; j['syncStopToast'] = syncStopToast; // 193: robocopy 가 링크 · junction 을 따라가지 않게 (/XJ) 바뀐 것을 알린 판 (바뀐 기본값 알림 'robocopyLinks') j['robocopyLinksV1'] = true; } @override void read(Map j) { methodFile = _method(j['copyMethodFile']); conflict = const ['rename', 'overwrite', 'skip'].contains(j['copyConflict']) ? j['copyConflict'] as String : 'ask'; skipNewer = j['copySkipNewer'] != false; methodFolder = _method(j['copyMethodFolder']); rsyncPaths = [for (final x in (j['rsyncPaths'] as List?) ?? const []) '$x']; syncStopToast = j['syncStopToast'] != false; rsyncOptions = j['rsyncOptions'] as String? ?? '-avPog'; robocopyOptions = j['robocopyOptions'] as String? ?? '/E /COPY:DAT /DCOPY:T /R:2 /W:2'; runMode = j['copyRunMode'] == 'once' ? 'once' : 'each'; bandwidthKBps = ((j['copyBandwidthKBps'] as num?)?.toInt() ?? 0).clamp(0, 10000000); rsyncSource = j['rsyncSource'] == 'custom' ? 'custom' : 'download'; rsyncPath = j['rsyncPath'] as String? ?? ''; liveSyncPairs = [ for (final x in (j['liveSyncPairs'] as List?) ?? const []) if (x is Map && x['source'] is String && x['target'] is String) LiveSyncPair.fromJson(x), ]; liveSyncIntervalSec = ((j['liveSyncIntervalSec'] as num?)?.toInt() ?? 30).clamp(5, 3600); liveSyncOnStart = const ['auto', 'ask', 'off'].contains(j['liveSyncOnStart']) ? j['liveSyncOnStart'] as String : 'auto'; tasks = [ for (final x in (j['copyTasks'] as List?) ?? const []) if (x is Map && x['dest'] is String) CopyTask.fromJson(x), ]; } }