UNPKG

@yemreak/yt-dlp

Version:

Downloading videos and subtitles using yt-dlp, with utilities for extracting text from subtitles

130 lines 4.88 kB
import fs from "fs"; import os from "os"; import { downloadLatestRelease, execYtDlp, extractTextFromJson3Subtitle, extractTextFromSrtSubtitle, extractTextFromVttSubtitle, parseFilenamesFromOutput, } from "./helpers.js"; import { downloadFile } from "./utils.js"; export class YtDlp { config; filename = os.platform() === "win32" ? "yt-dlp.exe" : "yt-dlp"; binaryPath; constructor(config) { this.config = config; this.binaryPath = `${config.workdir}/${this.filename}`; fs.mkdirSync(config.workdir, { recursive: true }); } /** * Downloads the latest release of yt-dlp from GitHub */ async downloadLatestReleaseIfNotExists() { if (fs.existsSync(this.binaryPath)) return; await downloadLatestRelease(this.config.workdir); } /** * Returns multiple {@link MediaInfo} objects if contains multiple videos */ async retrieveMediaInfoList(url) { const { stdout } = await this.exec({ dumpJson: true, url }); return stdout .trim() .split("\n") .map(line => JSON.parse(line)); } async exec(optionals) { return await execYtDlp({ binaryPath: this.binaryPath, cookies: this.config.cookies, ...optionals, }); } async downloadAudio(params) { return await this.download({ format: "ba", ...params }); } async download(params) { const { url, ...optionals } = params; const pattern = "%(id)s.%(ext)s"; const symPath = `${this.config.workdir}/${pattern}`; const { stdout, stderr } = await this.exec({ url, outputPath: symPath, ...optionals, }); if (stderr && stderr.includes("ERROR")) throw new Error(stderr); const mediaPaths = parseFilenamesFromOutput(stdout); return mediaPaths; } async retrieveMediaInfoFromSource(source) { if (typeof source === "string") return await this.retrieveMediaInfoList(source); return source; } /** * Downloads the subtitle file of a video */ async downloadSubtitle(params) { const { info, lang } = params; const { stdout } = await this.exec({ url: info.original_url, subtitle: { auto: true, lang }, outputPath: `${this.config.workdir}/${info.id}.%(lang)s.%(ext)s`, }); const subtitlePath = parseFilenamesFromOutput(stdout)[0].replace(".vtt", ".srt"); return subtitlePath; } /** * Downloads the subtitle file of a video without using yt-dlp */ async downloadSubtitleWithoutYtDlp(params) { const { info, lang = "en" } = params; const keys = Object.keys(info.subtitles); if (keys.length === 0) { const { stdout } = await this.exec({ url: info.original_url, subtitle: { lang, auto: true }, outputPath: `${this.config.workdir}/${info.id}`, }); const subtitlePath = parseFilenamesFromOutput(stdout)[0].replace(".vtt", ".srt"); return subtitlePath; } const key = keys.find(key => key.includes(lang)); if (!key) throw new Error(`No subtitles found for ${lang}`); const subtitle = info.subtitles[key]?.find(sub => sub.ext === "json3"); if (subtitle) { const subtitlePath = `${this.config.workdir}/${info.id}.${subtitle.ext}`; if (fs.existsSync(subtitlePath)) return subtitlePath; await downloadFile(subtitle.url, subtitlePath); return subtitlePath; } } /** * Extracts the text from a `json3`, `vtt` or `srt` subtitle file */ extractTextFromSubtitles(subtitlePath) { const subtitleFile = fs.readFileSync(subtitlePath, "utf-8"); if (subtitlePath.endsWith(".json3")) { const subtitleData = JSON.parse(subtitleFile); return extractTextFromJson3Subtitle(subtitleData); } else if (subtitlePath.endsWith(".vtt")) { return extractTextFromVttSubtitle(subtitleFile); } else if (subtitlePath.endsWith(".srt")) { return extractTextFromSrtSubtitle(subtitleFile); } throw new Error(`Unsupported subtitle format: "${subtitlePath}"`); } /** * Downloads and extracts the text from a subtitle file * - If `source` is a video URL, retrieves the {@link MediaInfo} from the video URL */ async downloadSubtitleText(params) { const subtitlePath = await this.downloadSubtitle(params); if (!subtitlePath) return; const subtitleText = this.extractTextFromSubtitles(subtitlePath); return subtitleText; } } //# sourceMappingURL=index.js.map