UNPKG

ariaa

Version:
142 lines 5.39 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); import { logger } from "#lib/structures"; import { path } from "@ffmpeg-installer/ffmpeg"; import { Presets, SingleBar } from "cli-progress"; import { blue, blueBright, greenBright, red, underline, yellowBright } from "colorette"; import ffmpeg from "fluent-ffmpeg"; import { rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import sanitize from "sanitize-filename"; import { request } from "undici"; import ytdl from "@distube/ytdl-core"; import { YouTube } from "youtube-sr"; import { getConfig, musicPath } from "./config.js"; import { searchSpotify } from "./spotify.js"; import { exec } from "child_process"; import { promisify } from "node:util"; const execAsync = promisify(exec); async function search(song, provider = "youtube") { if (provider === "youtube") { const results = await YouTube.search(song, { limit: 30, type: "video" }); return results.filter((r) => r.url && r.title && r.duration && r.views).sort((a, b) => b.views - a.views); } return (await searchSpotify(song, "track", 10)).tracks.items; } __name(search, "search"); function map(videos) { if ("title" in videos[0]) { return videos.map((video) => ({ name: video.title.concat(`${video.duration ? ` ${red(underline(video.durationFormatted))}` : ""}`), value: video.url })); } return videos.map((v) => ({ name: `${v.name} [${blue(v.artists.map((a) => a.name).join(", "))}] ${red(underline(parseReverse(v.duration_ms)))}`, value: v.id })); } __name(map, "map"); async function save(song, overrideformat, metadata) { ffmpeg.setFfmpegPath(path); const { bitrate, format } = getConfig(true); overrideformat ??= format; logger.debug(`Saving in ${underline(overrideformat)} format`); const bar = new SingleBar( { hideCursor: true, format: `Downloading | ${blueBright("{bar}")} {percentage}% | ETA: ${greenBright("{eta}")}s` }, Presets.shades_classic ); bar.start(song.duration * 2 / 1e3, 0); const start = Date.now(); const stream = ytdl(song.url, { quality: "highestaudio", highWaterMark: 1 << 25 }); const coverUrl = metadata?.album.images[0]?.url; let tmpImg = null; if (coverUrl) { const coverStream = await (await request(coverUrl)).body.arrayBuffer(); tmpImg = join(tmpdir(), `${(Math.random() + 1).toString(36)}.jpg`); await writeFile(tmpImg, new Uint8Array(coverStream)); } const tmpAudio = join(tmpdir(), `${(Math.random() + 1).toString(36)}.${overrideformat}`); await saveTmpAudio(stream, tmpAudio, bar, bitrate); let date; if (metadata?.album.release_date) { date = `${new Date(metadata.album.release_date).getUTCFullYear()}`; } const name = metadata ? filter(metadata.name) : filter(song.title); const file = ffmpeg(tmpAudio).outputOptions("-acodec", "libmp3lame", "-b:a", `${bitrate}k`, "-id3v2_version", "3").on("progress", (p) => { bar.update(Math.floor(parse(p.timemark.slice(3))) + song.duration / 1e3); }).on("end", () => { bar.update(song.duration * 2 / 1e3); bar.stop(); logger.debug("Download Complete"); logger.info(`Saved ${yellowBright(underline(name))} in ${(Date.now() - start) / 1e3}s`); }); if (metadata) { file.outputOptions(`-metadata`, `album=${metadata?.album.name}`).outputOptions(`-metadata`, `title=${filter(metadata?.name)}`).outputOptions(`-metadata`, `track=${metadata.track_number}`).outputOptions(`-metadata`, `TKEY=${metadata.key}`).outputOptions(`-metadata`, `TBPM=${metadata.tempo}`).outputOptions(`-metadata`, `genre=${metadata.genre}`).outputOptions(`-metadata`, `artist=${metadata?.artists.map((r) => r.name).join(", ")}`).outputOptions(`-metadata`, `album_artist=${metadata?.album.artists[0].name}`).outputOptions(`-metadata`, `date=${date}`); } if (tmpImg) { file.input(tmpImg).outputOptions( "-map", "0:0", "-map", "1:0", "-disposition:v:0", "attached_pic", "-id3v2_version", "3", "-metadata:s:v", 'title="Album cover"', "-metadata:s:v", 'comment="Cover (Front)"' ); } file.saveToFile(musicPath(sanitize(name), overrideformat)); file.on("end", async () => { if (tmpImg) await rm(tmpImg); await rm(tmpAudio); }); } __name(save, "save"); function parse(t) { const times = t.split(":"); if (times.length === 3) { return Number(times[0]) * 60 * 60 + Number(times[1]) * 60 + Number(times[2]); } return Number(times[0]) * 60 + Number(times[1]); } __name(parse, "parse"); function parseReverse(ms) { return new Date(ms).toISOString().slice(14, 19); } __name(parseReverse, "parseReverse"); function filter(s) { return s?.replaceAll(/\(.*\)|\[.*]/gm, "")?.trim(); } __name(filter, "filter"); function saveTmpAudio(audioStream, destination, bar, bitrate) { return new Promise((resolve) => { const ff = ffmpeg(audioStream).outputOptions("-acodec", "libmp3lame", "-b:a", `${bitrate}k`).saveToFile(destination); ff.on("progress", (p) => { bar.update(Math.floor(parse(p.timemark.slice(3)))); }); ff.on("end", resolve); }); } __name(saveTmpAudio, "saveTmpAudio"); export { filter, map, parse, parseReverse, save, saveTmpAudio, search }; //# sourceMappingURL=music.js.map