ytdls3/ytdl.js
2025-08-05 15:10:41 -07:00

108 lines
2.8 KiB
JavaScript

import {join} from "jsr:@std/path";
var decoder = new TextDecoder("utf8");
var infojsoncache = new Map();
async function getInfoJson(ytid) {
if (infojsoncache.has(ytid)) return infojsoncache.get(ytid);
try {
var text = await Deno.readTextFile(join(process.env.MEDIA_DIR, ytid, "info.json"));
var json = JSON.parse(text);
infojsoncache.set(ytid, json);
setTimeout(() => {
infojsoncache.delete(ytid);
}, 600000);
return json;
} catch (e) {
infojsoncache.set(ytid, null);
setTimeout(() => {
infojsoncache.delete(ytid);
}, 5000);
return null;
}
}
export async function ytdl_if_not_already(ytid) {
var info = await getInfoJson(ytid);
if (info) return info;
return await run_ytdlp_deduplicated(ytid);
}
var running_ytdl = new Map();
export async function run_ytdlp_deduplicated(ytid) {
var run = running_ytdl.get(ytid);
if (!run) {
run = run_ytdlp(ytid);
run.finally(() => {
running_ytdl.delete(ytid);
}).catch(()=>{});
running_ytdl.set(ytid, run);
}
return await run;
}
export async function run_ytdlp(ytid) {
var cwd = await Deno.makeTempDir();
console.debug("tmpdir", cwd);
var command = new Deno.Command("yt-dlp", {
args: [
"-f", "bestvideo*[vcodec!^=av01]+bestaudio/best",
"--no-mtime", "--no-playlist", "-J", "--no-simulate", ytid,
],
cwd,
stdout: "piped",
stderr: "piped",
stdin: "null"
});
var result = {ts: new Date()};
var output = await command.output();
if (!output.success) {
result.error = decoder.decode(output.stderr).trim();
}
else {
var stdout_text = decoder.decode(output.stdout).trim();
var jsons = stdout_text.split("\n");
if (jsons.length > 1) console.warn("more than one jsons outputted");
var json = JSON.parse(jsons[0]);
await Deno.writeTextFile(join(cwd, "more.json"), JSON.stringify(json));
result.title = json.title;
result.description = json.description;
result.thumbnail = json.thumbnail;
result.channel = json.channel;
result.duration = json.duration;
result.duration_string = json.duration_string;
result.view_count = json.view_count;
result.upload_date = json.upload_date;
result.filename = json.requested_downloads[0].filename;
if (!result.filename) {
var files = await Array.fromAsync(Deno.readDir(cwd));
files = files.map(file => file.name);
result.filename = files.find(name => /\.(?:webm|mp4|mkv)$/i.test(name));
}
let stat = await Deno.stat(join(cwd, result.filename));
result.size = stat.size;
}
await Deno.writeTextFile(join(cwd, "info.json"), JSON.stringify(result));
var newpath = join(process.env.MEDIA_DIR, ytid);
try {
await Deno.rename(cwd, newpath);
} catch (error) {
if (error.code === "ENOTEMPTY") {
await Deno.remove(newpath, {recursive: true});
await Deno.rename(cwd, newpath);
}
else {
throw error;
}
}
return result;
}