| | import { Innertube } from "youtubei.js"; |
| | import type { CaptionTrackData } from "youtubei.js/PlayerCaptionsTracklist"; |
| | import { HTTPException } from "hono/http-exception"; |
| |
|
| | function createTemporalDuration(milliseconds: number) { |
| | return new Temporal.Duration( |
| | undefined, |
| | undefined, |
| | undefined, |
| | undefined, |
| | undefined, |
| | undefined, |
| | undefined, |
| | milliseconds, |
| | ); |
| | } |
| |
|
| | const ESCAPE_SUBSTITUTIONS = { |
| | "&": "&", |
| | "<": "<", |
| | ">": ">", |
| | "\u200E": "‎", |
| | "\u200F": "‏", |
| | "\u00A0": " ", |
| | }; |
| |
|
| | export async function handleTranscripts( |
| | innertubeClient: Innertube, |
| | videoId: string, |
| | selectedCaption: CaptionTrackData, |
| | ) { |
| | const lines: string[] = ["WEBVTT"]; |
| |
|
| | const info = await innertubeClient.getInfo(videoId); |
| | const transcriptInfo = await (await info.getTranscript()).selectLanguage( |
| | selectedCaption.name.text || "", |
| | ); |
| | const rawTranscriptLines = transcriptInfo.transcript.content?.body |
| | ?.initial_segments; |
| |
|
| | if (rawTranscriptLines == undefined) throw new HTTPException(404); |
| |
|
| | rawTranscriptLines.forEach((line) => { |
| | const timestampFormatOptions = { |
| | style: "digital", |
| | minutesDisplay: "always", |
| | fractionalDigits: 3, |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | const start_ms = createTemporalDuration(Number(line.start_ms)).round({ |
| | largestUnit: "year", |
| | relativeTo: Temporal.PlainDateTime.from("2022-01-01"), |
| | |
| | }).toLocaleString("en-US", timestampFormatOptions); |
| |
|
| | const end_ms = createTemporalDuration(Number(line.end_ms)).round({ |
| | largestUnit: "year", |
| | relativeTo: Temporal.PlainDateTime.from("2022-01-01"), |
| | |
| | }).toLocaleString("en-US", timestampFormatOptions); |
| | const timestamp = `${start_ms} --> ${end_ms}`; |
| |
|
| | const text = (line.snippet?.text || "").replace( |
| | /[&<>\u200E\u200F\u00A0]/g, |
| | (match: string) => |
| | ESCAPE_SUBSTITUTIONS[ |
| | match as keyof typeof ESCAPE_SUBSTITUTIONS |
| | ], |
| | ); |
| |
|
| | lines.push(`${timestamp}\n${text}`); |
| | }); |
| |
|
| | return lines.join("\n\n"); |
| | } |
| |
|