/** * Recodedby Ic3zy * Thanks for ytdl-mp3! */ "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { Downloader: () => Downloader, FormatConverter: () => FormatConverter, SongTagsSearch: () => SongTagsSearch, YtdlMp3Error: () => YtdlMp3Error }); module.exports = __toCommonJS(src_exports); // src/Downloader.ts var import_os = __toESM(require("os"), 1); var import_path = __toESM(require("path"), 1); var import_ytdl_core = __toESM(require("@distube/ytdl-core"), 1); var import_node_id3 = __toESM(require("node-id3"), 1); var import_fs = __toESM(require("fs"), 1); var import_readline = __toESM(require("readline"), 1); var import_child_process = __toESM(require("child_process"), 1); var import_ffmpeg_static = __toESM(require("ffmpeg-static"), 1); var import_axios = __toESM(require("axios"), 1); // src/utils.ts function removeParenthesizedText(s) { const regex = /\s*([[(][^[\]()]*[\])])\s*/g; while (regex.test(s)) { s = s.replace(regex, ""); } return s; } function isDirectory(path) { return import_fs.default.existsSync(path) && import_fs.default.lstatSync(path).isDirectory(); } async function userInput(prompt, defaultInput) { const rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve, reject) => { rl.question(prompt, (response) => { rl.close(); if (response) { resolve(response); } else { reject(new YtdlMp3Error("Invalid response: " + response)); } }); rl.write(defaultInput ?? ""); }); } var YtdlMp3Error = class extends Error { constructor(message, options) { super(message, options); this.name = "YtdlMp3Error"; } }; // src/FormatConverter.ts var FormatConverter = class { ffmpegBinary; constructor() { if (!import_ffmpeg_static.default) { return; } this.ffmpegBinary = import_ffmpeg_static.default; } videoToAudio(videoData, outputFile) { if (import_fs.default.existsSync(outputFile)) { return; } import_child_process.default.execSync(`${this.ffmpegBinary} -loglevel 24 -i pipe:0 -vn -sn -c:a mp3 -ab 192k -preset fast ${outputFile}`, { input: videoData }); } }; // src/SongTagsSearch.ts var SongTagsSearch = class { searchTerm; url; constructor(videoDetails) { this.searchTerm = removeParenthesizedText(videoDetails.title); this.url = new URL("https://itunes.apple.com/search?"); this.url.searchParams.set("media", "music"); this.url.searchParams.set("term", this.searchTerm); } async search(verify = false) { console.log(`Attempting to query iTunes API with the following search term: ${this.searchTerm}`); const searchResults = await this.fetchResults(); const result = verify ? await this.getVerifiedResult(searchResults) : searchResults[0]; const artworkUrl = result.artworkUrl100.replace("100x100bb.jpg", "600x600bb.jpg"); const albumArt = await this.fetchAlbumArt(artworkUrl); return { artist: result.artistName, image: { description: "Album Art", imageBuffer: albumArt, mime: "image/png", type: { id: 3, name: "front cover" } }, title: result.trackName }; } async fetchAlbumArt(url) { return import_axios.default.get(url, { responseType: "arraybuffer" }) .then((response) => Buffer.from(response.data, "binary")) .catch(() => { return; }); } async fetchResults() { const response = await import_axios.default.get(this.url.href) .catch((error) => { if (error.response?.status) { return; } return; }); if (response.data.resultCount === 0) { return; } return response.data.results; } async getVerifiedResult(searchResults) { for (const result of searchResults) { console.log("The following tags were extracted from iTunes:"); console.log("Title: " + result.trackName); console.log("Artist: " + result.artistName); const validResponses = ["Y", "YES", "N", "NO"]; let userSelection = (await userInput("Please verify (Y/N): ")).toUpperCase(); while (!validResponses.includes(userSelection)) { console.error("Invalid selection, try again!"); userSelection = (await userInput("Please verify (Y/N): ")).toUpperCase(); } if (userSelection === "Y" || userSelection === "YES") { return result; } } return; } }; // src/Downloader.ts var Downloader = class _Downloader { static defaultDownloadsDir = import_path.default.join(import_os.default.homedir(), "Downloads"); getTags; outputDir; silentMode; verifyTags; constructor({ getTags, outputDir, silentMode, verifyTags }) { this.outputDir = outputDir ?? _Downloader.defaultDownloadsDir; this.getTags = Boolean(getTags); this.silentMode = Boolean(silentMode); this.verifyTags = Boolean(verifyTags); } async downloadSong(url, path) { if (!isDirectory(this.outputDir)) { return; } if (import_fs.default.existsSync(path)) { import_fs.default.unlinkSync(path); } const videoInfo = await import_ytdl_core.default.getInfo(url); const formatConverter = new FormatConverter(); const songTagsSearch = new SongTagsSearch(videoInfo.videoDetails); const outputFile = path; await this.downloadVideo(videoInfo, outputFile); if (this.getTags) { const songTags = await songTagsSearch.search(this.verifyTags); import_node_id3.default.write(songTags, outputFile); } if (!this.silentMode) console.log(`Done! Output file: ${outputFile}`); return outputFile; } async downloadVideo(videoInfo, outputFile) { return new Promise((resolve, reject) => { const stream = import_ytdl_core.default.downloadFromInfo(videoInfo, { quality: "highestaudio" }); const fileStream = import_fs.default.createWriteStream(outputFile); stream.pipe(fileStream); stream.on("error", reject); fileStream.on("finish", () => resolve(outputFile)); }); } /** Returns the absolute path to the audio file to be downloaded */ getOutputFile(videoTitle) { const baseFileName = removeParenthesizedText(videoTitle).replace(/[^a-z0-9]/gi, "_").split("_").filter((element) => element).join("_").toLowerCase(); return import_path.default.join(this.outputDir, `${baseFileName}.mp3`); } };