chapters-converter/src/index.ts

86 lines
2.0 KiB
TypeScript

import fs from "fs";
type Timestamp = {
timestamp: number;
title: string;
};
export function shift_timestamps(
timestamps: Timestamp[],
duration: number = 0
) {
return timestamps.map((ts) => ({
...ts,
timestamp: Math.max(0, ts.timestamp + duration),
}));
}
export function parse_audacity(content: string): Timestamp[] {
const lines = content.split("\n");
return lines
.filter((line) => line.length > 4)
.map((line) => line.split("\t"))
.map(([start_s, _, title]) => ({
timestamp: parseFloat(start_s),
title,
}));
}
function parseTimestamp(s: string): number {
return s
.split(":")
.map((e, i, all) => Math.pow(60, all.length - i - 1) * parseInt(e))
.reduce((a, b) => a + b);
}
function toTimestamp(n: number): string {
n = Math.floor(n);
let result: number[] = [];
while (n / 60 > 0) {
result.push(n % 60);
n = Math.floor(n / 60);
}
return result
.map((n, i) => (i >= 2 ? n.toString() : n.toString().padStart(2, "0")))
.reverse()
.join(":");
}
export function parse_youtube(content: string): Timestamp[] {
console.log(
content
.split("\n")
.filter((l) => l.length > 4)
.map((l) => l.match(/(([0-9]:)?[0-9]{1,2}:[0-9]{2})\s(.*)/))
);
return (
content
.split("\n")
.filter((l) => l.length > 4)
.map((l) => l.match(/(([0-9]:)?[0-9]{1,2}:[0-9]{2})\s(.*)/))
.filter((m) => m != null) as string[][]
).map(([_, time_s, __, title]) => ({
title: title.replace(/^\s*[-:]\s+/, ""),
timestamp: parseTimestamp(time_s),
}));
}
export function to_youtube(timestamps: Timestamp[]): string {
return timestamps
.map((ts) => `${toTimestamp(ts.timestamp)} ${ts.title}\n`)
.join("\n"); // yes, double newlines
}
export function to_json(timestamps: Timestamp[]): string {
return JSON.stringify({
version: "1.2",
chapters: timestamps.map((ts) => ({
startTime: ts.timestamp,
title: ts.title,
})),
});
}
export const parsers = { youtube: parse_youtube, audacity: parse_audacity };
export const encoders = { youtube: to_youtube, json: to_json };