Create VX1 .pls for bpev.me/blog/vx1 sessions
0
create_pls.ts
1import titleCase from "https://deno.land/x/case@2.1.1/titleCase.ts";
2
3const [dir, postDir] = Deno.args;
4
5const contents = [];
6
7for await (const file of Deno.readDir(dir)) {
8 if (file.name[0] === ".") continue;
9 contents.push(file);
10}
11
12const filenames = contents
13 .map((dir) => dir.name)
14 .sort()
15 .reverse();
16
17const entriesAsync = filenames.map(formatEntry);
18const entries = await Promise.all(entriesAsync);
19
20const plsText = `[playlist]
21${entries.join("\n")}
22NumberOfEntries=${entries.length}
23Version=2
24`;
25
26console.log(plsText);
27
28async function formatEntry(fileName, index) {
29 const num = index + 1;
30 const [nameOrDate, memoName] = fileName.split("_");
31 const title = memoName
32 ? titleCase(memoName.split(".")[0]) + ` (${nameOrDate})`
33 : titleCase(nameOrDate.split(".")[0]);
34
35 return "" +
36 `File${num}=https://static.bpev.me/blog/${postDir}/version/${fileName}\n` +
37 `Title${num}=${title}\n` +
38 `Length${num}=${await getDurationSeconds(fileName)}\n`;
39}
40
41async function getDurationSeconds(fileName) {
42 const p = Deno.run({
43 cmd: [ "/Users/ben/apps/ffmpeg", "-i", fileName ],
44 stderr: "piped",
45 });
46 await p.status();
47 const output = new TextDecoder().decode(await p.stderrOutput());
48 const time = output.match(/Duration: ([^.]*)/)[1];
49 const [hours, minutes, seconds] = time.split(":");
50 return (parseInt(hours) * 3600) + (parseInt(minutes) * 60) + parseInt(seconds);
51}