옮기기: 시작하면 바로 목록에서 빼고, 옮기지 못하면 원래 자리 · 표시 · 선택으로 되돌리기
- 다른 드라이브 · NAS 로는 복사라 큰 동영상이 1초 넘게 목록에 남아 보였다 (1GB 시험 파일 M: → C: 1,079ms, 같은 드라이브 22ms). - 데이터 버그 고침 (moveFileInto): 다른 드라이브로 복사한 뒤 원본을 지우지 못하면 (열려 있음 등) 대상 폴더에 사본이 남아 두 벌이 됐다. 이제 방금 만든 사본을 지우고 오류를 낸다 (원본은 그대로). 같은 함수를 쓰는 app_controller 안의 다른 두 곳 (옆 자막 · jj_mkv 결과물 함께 옮기기) 도 시험으로 확인했다: 자막 · 결과물을 열어 둔 채 옮기면 동영상만 옮겨지고, 자막 · 결과물은 원래 자리에 하나만 남는다 (고치기 전 코드로는 이 시험이 실패하는 것도 확인). - move_test: 옮기는 중 목록에 없음 → 실패하면 복원 · 대상에 사본 없음, 함께 옮기는 파일의 실패. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>묶음4
parent
bd0b75ce7b
commit
abeb3d8999
|
|
@ -525,6 +525,15 @@ class AppController extends ChangeNotifier {
|
|||
errors.add(trf('{0}: 이미 이동 폴더에 있습니다', [v.fileName]));
|
||||
continue;
|
||||
}
|
||||
// 옮기기 시작하면 바로 목록에서 뺀다 (다른 드라이브 · NAS 로는 복사라 큰 동영상은 몇 초 넘게 남아 보였다).
|
||||
// 옮기지 못하면 원래 자리 · 표시 · 선택으로 되돌린다.
|
||||
final at = videos.indexOf(v);
|
||||
final wasChecked = checked.contains(v);
|
||||
final wasSelected = selected == v;
|
||||
videos.remove(v);
|
||||
checked.remove(v);
|
||||
if (wasSelected) selected = videos.elementAtOrNull(at) ?? videos.lastOrNull;
|
||||
notifyListeners();
|
||||
try {
|
||||
final side = {
|
||||
for (final s in v.subtitles)
|
||||
|
|
@ -551,11 +560,15 @@ class AppController extends ChangeNotifier {
|
|||
if (results > 0) trf(' (결과물 {0}개 함께)', [results]),
|
||||
].join(),
|
||||
]));
|
||||
videos.remove(v);
|
||||
checked.remove(v);
|
||||
moved++;
|
||||
} catch (e) {
|
||||
errors.add('${v.fileName}: $e');
|
||||
if (File(v.path).existsSync() && !videos.contains(v)) {
|
||||
videos.insert(at.clamp(0, videos.length), v);
|
||||
if (wasChecked) checked.add(v);
|
||||
if (wasSelected) selected = v;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selected != null && !videos.contains(selected)) selected = videos.firstOrNull;
|
||||
|
|
@ -594,8 +607,16 @@ class AppController extends ChangeNotifier {
|
|||
try {
|
||||
await File(from).rename(to);
|
||||
} on FileSystemException {
|
||||
try {
|
||||
await File(from).copy(to);
|
||||
await File(from).delete();
|
||||
} catch (_) {
|
||||
// 원본을 지우지 못하면 (열려 있음 등) 옮긴 것이 아니다 - 방금 만든 사본을 지워 두 벌이 남지 않게 (원본은 그대로)
|
||||
try {
|
||||
if (File(from).existsSync()) await File(to).delete();
|
||||
} catch (_) {}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,61 @@ void main() {
|
|||
expect(c.selected, busy);
|
||||
});
|
||||
|
||||
test('옮기기 시작하면 바로 목록에서 빠지고, 옮기지 못하면 원래 자리 · 표시 · 선택으로 돌아온다', () async {
|
||||
final c = _controller();
|
||||
final dest = p.join(dir.path, 'done');
|
||||
final a = VideoItem(make('a.mp4').path);
|
||||
final b = VideoItem(make('b.mp4').path);
|
||||
final d = VideoItem(make('d.mp4').path);
|
||||
c.videos.addAll([a, b, d]);
|
||||
c.checked.add(b);
|
||||
c.selected = b;
|
||||
// 처음 알릴 때 (옮기는 중) 이미 목록에 없다
|
||||
List<String>? whileMoving;
|
||||
void first() => whileMoving ??= c.videos.map((v) => v.fileName).toList();
|
||||
c.addListener(first);
|
||||
// Windows: 열려 있는 파일은 옮길 수 없다
|
||||
final h = File(b.path).openSync(mode: FileMode.append);
|
||||
try {
|
||||
final (moved, errors) = await c.moveVideos([b], dest);
|
||||
expect(moved, 0);
|
||||
expect(errors.single, contains('b.mp4'));
|
||||
} finally {
|
||||
h.closeSync();
|
||||
c.removeListener(first);
|
||||
}
|
||||
expect(whileMoving, ['a.mp4', 'd.mp4']);
|
||||
expect(c.videos, [a, b, d]);
|
||||
expect(c.checked, {b});
|
||||
expect(c.selected, b);
|
||||
expect(File(b.path).existsSync(), isTrue);
|
||||
expect(File(p.join(dest, 'b.mp4')).existsSync(), isFalse, reason: '옮기다 만 사본을 남기지 않는다');
|
||||
}, skip: !Platform.isWindows);
|
||||
|
||||
test('함께 옮기는 자막 · 결과물을 옮기지 못하면 원본은 그대로 두고 대상에 사본을 남기지 않는다', () async {
|
||||
final c = _controller();
|
||||
final dest = p.join(dir.path, 'done');
|
||||
final a = VideoItem(make('a.mp4').path)
|
||||
..subtitles.add(SubtitleEntry.external(path: make('a.ko.srt').path, language: undetermined));
|
||||
make(p.join('jj_mkv', 'a.mkv'));
|
||||
c.videos.add(a);
|
||||
// Windows: 열려 있는 파일은 옮길 수 없다 (옆 자막 · jj_mkv 결과물)
|
||||
final h1 = File(p.join(dir.path, 'a.ko.srt')).openSync(mode: FileMode.append);
|
||||
final h2 = File(p.join(dir.path, 'jj_mkv', 'a.mkv')).openSync(mode: FileMode.append);
|
||||
try {
|
||||
final (moved, _) = await c.moveVideos([a], dest);
|
||||
expect(moved, 1, reason: '동영상은 옮긴다');
|
||||
} finally {
|
||||
h1.closeSync();
|
||||
h2.closeSync();
|
||||
}
|
||||
expect(File(p.join(dest, 'a.mp4')).existsSync(), isTrue);
|
||||
expect(File(p.join(dir.path, 'a.ko.srt')).existsSync(), isTrue);
|
||||
expect(File(p.join(dest, 'a.ko.srt')).existsSync(), isFalse, reason: '자막 사본이 두 벌로 남지 않는다');
|
||||
expect(File(p.join(dir.path, 'jj_mkv', 'a.mkv')).existsSync(), isTrue);
|
||||
expect(File(p.join(dest, 'jj_mkv', 'a.mkv')).existsSync(), isFalse, reason: '결과물 사본이 두 벌로 남지 않는다');
|
||||
}, skip: !Platform.isWindows);
|
||||
|
||||
test('목록에 다시 넣으면 (앱을 다시 켜도) 전에 만든 자막 (jj_mkv 의 파일명_ko.srt 등) 이 다시 붙는다', () async {
|
||||
final c = _controller();
|
||||
final video = make('강의.mp4').path;
|
||||
|
|
|
|||
Loading…
Reference in New Issue