From a3e898c66dc8b93f23902c1b54d8e421660e6343 Mon Sep 17 00:00:00 2001 From: RyuJongJin <266630912+RyuJongJin@users.noreply.github.com> Date: Mon, 12 Oct 2026 08:34:13 +0900 Subject: [PATCH] =?UTF-8?q?WIP=20184-2=20=EB=B3=B4=EC=B6=A9:=20generic=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C=EA=B8=B0=20(=EC=A7=81=EC=A0=91=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=C2=B7=20.unknown=5Fvideo)=20=EC=9D=B4=EA=B3=A0=20?= =?UTF-8?q?=ED=95=A9=EC=B9=98=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=EB=A7=8C=20=EC=9D=BC=EB=B0=98=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=A1=9C=20-=20=EC=82=AC=EC=9D=B4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C=EA=B8=B0=EC=9D=98=20=ED=9B=84=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=EB=8A=94=20=EA=B7=B8=EB=8C=80=EB=A1=9C=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20(=EA=B2=80=ED=86=A0=20=EB=8C=80=EA=B8=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- lib/platform/windows/ytdlp_backend.dart | 8 +++++++- test/ytdlp_plain_file_test.dart | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/platform/windows/ytdlp_backend.dart b/lib/platform/windows/ytdlp_backend.dart index 3c2f7ac..b3a66e0 100644 --- a/lib/platform/windows/ytdlp_backend.dart +++ b/lib/platform/windows/ytdlp_backend.dart @@ -177,7 +177,11 @@ class YtDlpBackend implements DownloadBackend { // 영상 · 음성을 따로 받아도 하나의 진행률 · 크기로 보이게 합친다 final totals = YtDlpTotals(); var files = 0; + // 184-2: 사이트 추출기가 아닌 "generic" (직접 파일 등) 으로 받았는지 · 영상 · 음성을 합쳤는지 + var generic = false, merged = false; void onLine(String line) { + if (line.startsWith('[generic]')) generic = true; + if (line.startsWith('[Merger]')) merged = true; final prog = parseYtDlpProgress(line); if (prog != null) { totals.update(prog); @@ -246,7 +250,9 @@ class YtDlpBackend implements DownloadBackend { } // 184-2: 동영상이 아닌 직접 파일 (확장자로 미리 알 수 없던 주소) 을 다 받았는데 후처리에서 실패 - // 받은 파일을 원래 이름으로 남기고 "완료" (예전에는 다 받고도 "실패 · 받지 못했습니다" 와 "… [id].unknown_video") - if (isNonVideoPostprocessError(err) && await _keepAsPlainFile(t)) { + // 사이트 추출기 (youtube 등) 나 합치기 실패는 진짜 동영상이 망가진 것일 수 있어 실패 그대로 (이유를 보인다) + final plain = !merged && (generic || t.files.any((f) => f.endsWith('.unknown_video'))); + if (plain && isNonVideoPostprocessError(err) && await _keepAsPlainFile(t)) { changed(); return; } diff --git a/test/ytdlp_plain_file_test.dart b/test/ytdlp_plain_file_test.dart index 4362452..9a1caba 100644 --- a/test/ytdlp_plain_file_test.dart +++ b/test/ytdlp_plain_file_test.dart @@ -32,6 +32,7 @@ a = sys.argv[1:] d = a[a.index('-P') + 1] f = os.path.join(d, 'download [download].unknown_video') open(f, 'w').write('PK') +print('[generic] Extracting URL: ' + a[-1], flush=True) print('[download] Destination: ' + f, flush=True) sys.stderr.write('ERROR: Postprocessing: Error opening input files: Invalid data found when processing input\n') sys.exit(1) @@ -49,6 +50,30 @@ sys.exit(1) expect(File(p.join(out.path, 'download [download].unknown_video')).existsSync(), isFalse); }); + test('사이트 추출기 (youtube 등) 의 후처리 실패는 바꾸지 않는다 - 망가진 동영상을 "완료" 로 두지 않음', () async { + final dir = Directory.systemTemp.createTempSync('jj_plain_'); + addTearDown(() => dir.deleteSync(recursive: true)); + final script = File(p.join(dir.path, 'fake_ytdlp.py')) + ..writeAsStringSync(r''' +import os, sys +a = sys.argv[1:] +d = a[a.index('-P') + 1] +f = os.path.join(d, 'clip [abc].f137.mp4') +open(f, 'w').write('V') +print('[youtube] Extracting URL: ' + a[-1], flush=True) +print('[download] Destination: ' + f, flush=True) +sys.stderr.write('ERROR: Postprocessing: Error opening input files: Invalid data found when processing input\n') +sys.exit(1) +'''); + final backend = YtDlpBackend(ytdlp: 'python', prefixArgs: [script.path], deno: ''); + final t = DownloadTask( + id: '1', kind: DownloadKind.video, source: 'https://www.youtube.com/watch?v=abc&file=a.zip', dir: dir.path); + await backend.start(t, () {}); + expect(t.state, DownloadState.failed); + expect(t.error, contains('Postprocessing')); + expect(File(p.join(dir.path, 'clip [abc].f137.mp4')).existsSync(), isTrue, reason: '이름도 바꾸지 않음'); + }); + test('받은 파일이 없으면 그대로 실패', () async { final dir = Directory.systemTemp.createTempSync('jj_plain_'); addTearDown(() => dir.deleteSync(recursive: true));