모니터링 카드에 실제로 실행할 것 · 이동 / 복사 (209)

관리자 지시: 카드의 옵션 칸은 저장된 원문이라 이동 카드는 "-avPog", 복사 카드는 "… --remove-source-files" (실제로는 빠짐) 로 거꾸로 읽혔다.
칸 옆에 "이동 · 실제 실행: rsync -avPog --remove-source-files" / "복사 · 실제 실행: rsync -avPog" 를 보이고,
복사 카드 칸에 원본 지우기 옵션이 있으면 쓰지 않는다고 알린다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix_mkv
RyuJongJin 2026-10-11 22:35:05 +09:00
parent f59e2f75a1
commit 37d7c0da31
5 changed files with 51 additions and 2 deletions

View File

@ -981,6 +981,7 @@
"복사 끝 ({0}): {1}개 → {2}": "Copy done ({0}): {1} → {2}", "복사 끝 ({0}): {1}개 → {2}": "Copy done ({0}): {1} → {2}",
"복사 모니터링": "Copy monitor", "복사 모니터링": "Copy monitor",
"복사 실패 ({0}): {1}": "Copy failed ({0}): {1}", "복사 실패 ({0}): {1}": "Copy failed ({0}): {1}",
"복사 작업이라 칸의 원본 지우기 옵션 (--remove-source-files · /MOV) 은 쓰지 않습니다. 옮기려면 Rsync 화면에서 [원본 파일 지우기] 를 켜세요": "This is a copy task, so source-removing options in the field (--remove-source-files · /MOV) are not used. To move, turn on [Remove source files] on the Rsync screen",
"복사(Ctrl+C)한 주소 자동 다운로드": "Auto-download copied (Ctrl+C) URLs", "복사(Ctrl+C)한 주소 자동 다운로드": "Auto-download copied (Ctrl+C) URLs",
"복사하는 중": "Copying", "복사하는 중": "Copying",
"복사할": "copy", "복사할": "copy",
@ -1174,6 +1175,7 @@
"실시간 동기화가 없습니다. [추가] 하거나 복사 · rsync 탭에서 [lsync 로 이동] 하세요.": "No live sync. [Add] one, or use [Move to lsync] in the copy · rsync tab.", "실시간 동기화가 없습니다. [추가] 하거나 복사 · rsync 탭에서 [lsync 로 이동] 하세요.": "No live sync. [Add] one, or use [Move to lsync] in the copy · rsync tab.",
"실시간 동기화를 지울까요?": "Delete this live sync?", "실시간 동기화를 지울까요?": "Delete this live sync?",
"실시간 동기화의 [최종 정리]": "[Final cleanup] in live sync", "실시간 동기화의 [최종 정리]": "[Final cleanup] in live sync",
"실제 실행: {0}": "Actually runs: {0}",
"실패": "Failed", "실패": "Failed",
"실패 · {0}": "Failed · {0}", "실패 · {0}": "Failed · {0}",
"실패: {0}": "Failed: {0}", "실패: {0}": "Failed: {0}",

View File

@ -981,6 +981,7 @@
"복사 끝 ({0}): {1}개 → {2}": "コピー完了 ({0}): {1} 件 → {2}", "복사 끝 ({0}): {1}개 → {2}": "コピー完了 ({0}): {1} 件 → {2}",
"복사 모니터링": "コピーのモニタリング", "복사 모니터링": "コピーのモニタリング",
"복사 실패 ({0}): {1}": "コピー失敗 ({0}): {1}", "복사 실패 ({0}): {1}": "コピー失敗 ({0}): {1}",
"복사 작업이라 칸의 원본 지우기 옵션 (--remove-source-files · /MOV) 은 쓰지 않습니다. 옮기려면 Rsync 화면에서 [원본 파일 지우기] 를 켜세요": "コピー作業のため、欄の元ファイル削除オプション (--remove-source-files · /MOV) は使いません。移動するには Rsync 画面で [元ファイルを削除] をオンにしてください",
"복사(Ctrl+C)한 주소 자동 다운로드": "コピー(Ctrl+C)した URL を自動ダウンロード", "복사(Ctrl+C)한 주소 자동 다운로드": "コピー(Ctrl+C)した URL を自動ダウンロード",
"복사하는 중": "コピー中", "복사하는 중": "コピー中",
"복사할": "コピーする", "복사할": "コピーする",
@ -1174,6 +1175,7 @@
"실시간 동기화가 없습니다. [추가] 하거나 복사 · rsync 탭에서 [lsync 로 이동] 하세요.": "リアルタイム同期はありません。[追加] するか、コピー · rsync タブで [lsync へ移動] してください。", "실시간 동기화가 없습니다. [추가] 하거나 복사 · rsync 탭에서 [lsync 로 이동] 하세요.": "リアルタイム同期はありません。[追加] するか、コピー · rsync タブで [lsync へ移動] してください。",
"실시간 동기화를 지울까요?": "このリアルタイム同期を削除しますか?", "실시간 동기화를 지울까요?": "このリアルタイム同期を削除しますか?",
"실시간 동기화의 [최종 정리]": "リアルタイム同期の [最終整理]", "실시간 동기화의 [최종 정리]": "リアルタイム同期の [最終整理]",
"실제 실행: {0}": "実際の実行: {0}",
"실패": "失敗", "실패": "失敗",
"실패 · {0}": "失敗 · {0}", "실패 · {0}": "失敗 · {0}",
"실패: {0}": "失敗: {0}", "실패: {0}": "失敗: {0}",

View File

@ -981,6 +981,7 @@
"복사 끝 ({0}): {1}개 → {2}": "复制完成 ({0}): {1} 个 → {2}", "복사 끝 ({0}): {1}개 → {2}": "复制完成 ({0}): {1} 个 → {2}",
"복사 모니터링": "复制监控", "복사 모니터링": "复制监控",
"복사 실패 ({0}): {1}": "复制失败 ({0}): {1}", "복사 실패 ({0}): {1}": "复制失败 ({0}): {1}",
"복사 작업이라 칸의 원본 지우기 옵션 (--remove-source-files · /MOV) 은 쓰지 않습니다. 옮기려면 Rsync 화면에서 [원본 파일 지우기] 를 켜세요": "这是复制任务,栏中的删除源文件选项(--remove-source-files · /MOV)不会使用。要移动,请在 Rsync 界面打开 [删除源文件]",
"복사(Ctrl+C)한 주소 자동 다운로드": "自动下载复制(Ctrl+C)的地址", "복사(Ctrl+C)한 주소 자동 다운로드": "自动下载复制(Ctrl+C)的地址",
"복사하는 중": "正在复制", "복사하는 중": "正在复制",
"복사할": "复制", "복사할": "复制",
@ -1174,6 +1175,7 @@
"실시간 동기화가 없습니다. [추가] 하거나 복사 · rsync 탭에서 [lsync 로 이동] 하세요.": "没有实时同步。请 [添加],或在 复制 · rsync 标签中 [移到 lsync]。", "실시간 동기화가 없습니다. [추가] 하거나 복사 · rsync 탭에서 [lsync 로 이동] 하세요.": "没有实时同步。请 [添加],或在 复制 · rsync 标签中 [移到 lsync]。",
"실시간 동기화를 지울까요?": "删除此实时同步吗?", "실시간 동기화를 지울까요?": "删除此实时同步吗?",
"실시간 동기화의 [최종 정리]": "实时同步的 [最终整理]", "실시간 동기화의 [최종 정리]": "实时同步的 [最终整理]",
"실제 실행: {0}": "实际执行:{0}",
"실패": "失败", "실패": "失败",
"실패 · {0}": "失败 · {0}", "실패 · {0}": "失败 · {0}",
"실패: {0}": "失败: {0}", "실패: {0}": "失败: {0}",

View File

@ -19,11 +19,20 @@ import 'copy_sync_settings.dart' show addLiveSyncPairDialog, editLiveSyncPairDia
import 'path_label.dart'; import 'path_label.dart';
import 'rsync_setup.dart'; import 'rsync_setup.dart';
import 'schedule_editor.dart'; import 'schedule_editor.dart';
import '../core/sync_preview.dart' show withoutSourceRemovingOptions; import '../core/sync_preview.dart' show withoutSourceRemovingOptions, optionsRemoveSource;
import 'sync_preview_view.dart' show confirmDeletingRun; import 'sync_preview_view.dart' show confirmDeletingRun;
import 'theme.dart'; import 'theme.dart';
/// 모니터링 (파일 탐색기 > 모니터링): 복사 · rsync 와 lsync (실시간 동기화) 를 따로 보여 준다. /// 모니터링 (파일 탐색기 > 모니터링): 복사 · rsync 와 lsync (실시간 동기화) 를 따로 보여 준다.
/// 178 · 209: 실제로 실행할 방법 · 옵션 (원본 지우기 옵션은 빼고, 이동일 때만 그 방법의 원본 지우기를 붙인다)
String effectiveRunText(String method, String options, {required bool move}) {
final name = method == 'builtin' ? tr('현재 방식') : method;
if (method == 'builtin') return name;
final opts = withoutSourceRemovingOptions(options);
final extra = move ? (method == 'robocopy' ? ' /MOVE' : method == 'rsync' ? ' --remove-source-files' : '') : '';
return '$name $opts$extra'.trim();
}
class MonitorPage extends StatelessWidget { class MonitorPage extends StatelessWidget {
final AppController c; final AppController c;
final int initialTab; final int initialTab;
@ -376,7 +385,7 @@ class _CopyCardState extends State<_CopyCard> {
content: Text(trf('칸에 보이는 옵션: {0}\n저장된 옵션: {1}\n\n고친 옵션을 저장하고 실행할까요?', [ content: Text(trf('칸에 보이는 옵션: {0}\n저장된 옵션: {1}\n\n고친 옵션을 저장하고 실행할까요?', [
'${_method == 'builtin' ? tr('현재 방식') : _method} ${_options.text.trim()}', '${_method == 'builtin' ? tr('현재 방식') : _method} ${_options.text.trim()}',
// 178: 실제로 쓰는 옵션 (원본 지우기는 이동일 때만) // 178: 실제로 쓰는 옵션 (원본 지우기는 이동일 때만)
'${t.method == 'builtin' ? tr('현재 방식') : t.method} ${withoutSourceRemovingOptions(t.options)}${t.move ? (t.method == 'robocopy' ? ' /MOVE' : t.method == 'rsync' ? ' --remove-source-files' : '') : ''}', effectiveRunText(t.method, t.options, move: t.move),
])), ])),
actions: [ actions: [
TextButton(onPressed: () => Navigator.pop(ctx), child: Text(tr('취소'))), TextButton(onPressed: () => Navigator.pop(ctx), child: Text(tr('취소'))),
@ -540,6 +549,19 @@ class _CopyCardState extends State<_CopyCard> {
onChanged: (_) => setState(() {}), onChanged: (_) => setState(() {}),
), ),
), ),
// 209: 실제로 실행할 것 - 이동 / 복사를 분명히 (칸의 원본 지우기 옵션은 이동일 때만 쓰인다)
SizedBox(
width: 460,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [
Text(
'${t.move ? tr('이동') : tr('복사')} · ${trf('실제 실행: {0}', [effectiveRunText(_method, _options.text.trim(), move: t.move)])}',
style: TextStyle(fontSize: 12, color: t.move ? Colors.orangeAccent : JjColors.textDim),
),
if (!t.move && optionsRemoveSource(_options.text))
Text(tr('복사 작업이라 칸의 원본 지우기 옵션 (--remove-source-files · /MOV) 은 쓰지 않습니다. 옮기려면 Rsync 화면에서 [원본 파일 지우기] 를 켜세요'),
style: const TextStyle(fontSize: 12, color: Colors.orangeAccent)),
]),
),
SizedBox( SizedBox(
width: 130, width: 130,
child: TextField( child: TextField(

View File

@ -230,6 +230,27 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
testWidgets('209: 모니터링 카드는 실제로 실행할 것을 이동 / 복사와 함께 보인다 (복사 카드 칸의 원본 지우기 옵션은 쓰지 않는다고 알림)', (tester) async {
expect(effectiveRunText('rsync', '-avPog', move: true), 'rsync -avPog --remove-source-files');
expect(effectiveRunText('rsync', '-avPog --remove-source-files', move: false), 'rsync -avPog');
expect(effectiveRunText('robocopy', '/E /MOV', move: true), 'robocopy /E /MOVE');
final a = p.join(tmp.path, 'ca'), b = p.join(tmp.path, 'cb');
Directory(a).createSync(recursive: true);
Directory(b).createSync(recursive: true);
c.settings.copyTasks = [
CopyTask(id: '1', sources: [a], dest: b, contents: true, method: 'rsync', options: '-avPog --remove-source-files'),
CopyTask(id: '2', sources: [b], dest: a, contents: true, move: true, method: 'rsync', options: '-avPog'),
];
tester.view.physicalSize = const Size(1500, 1200);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
await tester.pumpWidget(MaterialApp(home: MonitorPage(c: c)));
await tester.pump();
expect(find.text('복사 · 실제 실행: rsync -avPog'), findsOneWidget);
expect(find.textContaining('복사 작업이라 칸의 원본 지우기 옵션'), findsOneWidget);
expect(find.text('이동 · 실제 실행: rsync -avPog --remove-source-files'), findsOneWidget);
});
testWidgets('112 · 114: 모니터링 작업 - 짧은 경로, 저장하지 않은 고침이 있으면 [실행] 할 때 묻는다 (저장하면 그 옵션 · 지우기면 미리 보기)', testWidgets('112 · 114: 모니터링 작업 - 짧은 경로, 저장하지 않은 고침이 있으면 [실행] 할 때 묻는다 (저장하면 그 옵션 · 지우기면 미리 보기)',
(tester) async { (tester) async {
final src = p.join(tmp.path, 'Download', 'movies'), dst = p.join(tmp.path, 'backup', 'movies'); final src = p.join(tmp.path, 'Download', 'movies'), dst = p.join(tmp.path, 'backup', 'movies');