fix(ytdlp): keep valid downloaded outputs

This commit is contained in:
hicocos committed 2026-07-14 22:52:38 +08:00
1 parent 03bb21c951
commit b9d988b379
3 files changed
+91 -23

No files matched your search

+15 -8
View File
@@ -11032,15 +11032,22 @@ function safeRmDir(dir) {
} catch {
}
}
function isYtDlpSidecarOrTemporaryFile(fileName) {
const lower = fileName.toLowerCase();
return lower.endsWith(".part") || lower.endsWith(".ytdl") || lower.endsWith(".tmp") || lower.endsWith(".info.json") || lower.endsWith(".live_chat.json") || lower.endsWith(".description") || lower.endsWith(".annotations.xml");
}
function selectPrimaryOutputFile(taskDir) {
const entries = fs10.readdirSync(taskDir, { withFileTypes: true });
const files = entries.filter((e) => e.isFile()).map((e) => ({
name: e.name,
fullPath: path15.join(taskDir, e.name)
})).filter((f) => !f.name.endsWith(".part") && !f.name.endsWith(".ytdl") && !f.name.endsWith(".json") && !f.name.endsWith(".tmp")).map((f) => ({
...f,
size: fs10.existsSync(f.fullPath) ? fs10.statSync(f.fullPath).size : 0
})).filter((f) => f.size > 0).sort((a, b) => b.size - a.size);
const collectFiles = (dir) => {
const entries = fs10.readdirSync(dir, { withFileTypes: true });
return entries.flatMap((entry) => {
const fullPath = path15.join(dir, entry.name);
if (entry.isDirectory()) return collectFiles(fullPath);
if (!entry.isFile() || isYtDlpSidecarOrTemporaryFile(entry.name)) return [];
const size = fs10.existsSync(fullPath) ? fs10.statSync(fullPath).size : 0;
return size > 0 ? [{ name: entry.name, fullPath, size }] : [];
});
};
const files = collectFiles(taskDir).sort((a, b) => b.size - a.size);
if (files.length === 0) return null;
return { filePath: files[0].fullPath, fileName: files[0].name, size: files[0].size };
}
+23 -15
View File
@@ -70,22 +70,30 @@ function safeRmDir(dir: string) {
}
}
function selectPrimaryOutputFile(taskDir: string): { filePath: string; fileName: string; size: number } | null {
const entries = fs.readdirSync(taskDir, { withFileTypes: true });
const files = entries
.filter(e => e.isFile())
.map(e => ({
name: e.name,
fullPath: path.join(taskDir, e.name),
}))
.filter(f => !f.name.endsWith('.part') && !f.name.endsWith('.ytdl') && !f.name.endsWith('.json') && !f.name.endsWith('.tmp'))
.map(f => ({
...f,
size: fs.existsSync(f.fullPath) ? fs.statSync(f.fullPath).size : 0
}))
.filter(f => f.size > 0)
.sort((a, b) => b.size - a.size);
function isYtDlpSidecarOrTemporaryFile(fileName: string): boolean {
const lower = fileName.toLowerCase();
return lower.endsWith('.part')
|| lower.endsWith('.ytdl')
|| lower.endsWith('.tmp')
|| lower.endsWith('.info.json')
|| lower.endsWith('.live_chat.json')
|| lower.endsWith('.description')
|| lower.endsWith('.annotations.xml');
}
export function selectPrimaryOutputFile(taskDir: string): { filePath: string; fileName: string; size: number } | null {
const collectFiles = (dir: string): Array<{ name: string; fullPath: string; size: number }> => {
const entries = fs.readdirSync(dir, { withFileTypes: true });
return entries.flatMap(entry => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) return collectFiles(fullPath);
if (!entry.isFile() || isYtDlpSidecarOrTemporaryFile(entry.name)) return [];
const size = fs.existsSync(fullPath) ? fs.statSync(fullPath).size : 0;
return size > 0 ? [{ name: entry.name, fullPath, size }] : [];
});
};
const files = collectFiles(taskDir).sort((a, b) => b.size - a.size);
if (files.length === 0) return null;
return { filePath: files[0].fullPath, fileName: files[0].name, size: files[0].size };
}
@@ -0,0 +1,53 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { selectPrimaryOutputFile } from './ytDlpDownload.js';
function makeTaskDir(): string {
return fs.mkdtempSync(path.join(os.tmpdir(), 'tg-vault-ytdlp-select-'));
}
function writeFile(filePath: string, content: string) {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, content);
}
function testSelectsRealOutputFromNestedDirectories() {
const dir = makeTaskDir();
writeFile(path.join(dir, 'video.info.json'), '{"metadata":true}');
writeFile(path.join(dir, 'tmp.part'), 'partial');
writeFile(path.join(dir, 'nested', 'small-thumb.jpg'), 'x');
writeFile(path.join(dir, 'nested', 'video.mp4'), 'real-media-output');
const selected = selectPrimaryOutputFile(dir);
assert.equal(selected?.fileName, 'video.mp4');
assert.equal(selected?.filePath, path.join(dir, 'nested', 'video.mp4'));
assert.equal(selected?.size, Buffer.byteLength('real-media-output'));
}
function testDoesNotDiscardJsonWhenItIsTheOnlyRealOutput() {
const dir = makeTaskDir();
writeFile(path.join(dir, 'result.json'), '{"download":"payload"}');
const selected = selectPrimaryOutputFile(dir);
assert.equal(selected?.fileName, 'result.json');
assert.equal(selected?.filePath, path.join(dir, 'result.json'));
}
function testReturnsNullWhenOnlyTemporarySidecarsExist() {
const dir = makeTaskDir();
writeFile(path.join(dir, 'video.part'), 'partial');
writeFile(path.join(dir, 'video.ytdl'), 'state');
writeFile(path.join(dir, 'video.info.json'), '{"metadata":true}');
writeFile(path.join(dir, 'video.tmp'), 'tmp');
assert.equal(selectPrimaryOutputFile(dir), null);
}
testSelectsRealOutputFromNestedDirectories();
testDoesNotDiscardJsonWhenItIsTheOnlyRealOutput();
testReturnsNullWhenOnlyTemporarySidecarsExist();
console.log('yt-dlp output selection ok');