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; }