From 8fa0b98c66a4c26f12f9450e6dde77242c5dc008 Mon Sep 17 00:00:00 2001 From: Minteck Date: Sun, 27 Mar 2022 22:51:09 +0200 Subject: Commit --- index.js | 398 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 199 insertions(+), 199 deletions(-) (limited to 'index.js') diff --git a/index.js b/index.js index 9a4082c..4f6c96e 100644 --- a/index.js +++ b/index.js @@ -1,200 +1,200 @@ -/* - * MIT License - * - * Copyright (c) 2022- Minteck - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -'use strict'; - -(async () => { - function die(message, code) { - if (message) { console.log(message) } - if (code) { - process.exit(code); - } else { - process.exit(0); - } - } - - String.prototype.isBase64 = function () { - return Buffer.from(this, 'base64').toString('base64').split("=")[0].substring(0, Buffer.from(this, 'base64').toString('base64').split("=")[0].length - 1) === this.substring(0, this.length - 1); - } - - const exec = require('child_process').execFileSync; - const fs = require('fs'); - const axios = require('axios'); - - const Genius = require("genius-lyrics"); - const g = new Genius.Client(); - - if (process.argv[2] !== undefined) { - global.id = process.argv[2]; - if (!id.isBase64() || id.length !== 34) { - die("Invalid playlist ID"); - } - } else { - console.log("Please specify playlist ID"); - } - - if (process.argv[3] !== undefined) { - global.overrideAlbum = true; - global.overrideAlbumName = process.argv[3]; - } else { - global.overrideAlbum = false; - } - - console.log("Preparing folder structure..."); - if (!fs.existsSync("./_youtoo")) fs.mkdirSync("./_youtoo"); - if (!fs.existsSync("./_youtoo/Dumps")) fs.mkdirSync("./_youtoo/Dumps"); - if (!fs.existsSync("./_youtoo/Result")) fs.mkdirSync("./_youtoo/Result"); - if (!fs.existsSync("./_youtoo/Metadata")) fs.mkdirSync("./_youtoo/Metadata"); - if (!fs.existsSync("./_youtoo/AlbumArts")) fs.mkdirSync("./_youtoo/AlbumArts"); - - console.log("Gathering playlist data..."); - let playlist = JSON.parse(exec("yt-dlp", [ "--flat-playlist", "--dump-single-json", "https://www.youtube.com/playlist?list=" + id ]).toString()); - fs.writeFileSync("./_youtoo/Dumps/PlaylistDataRaw.json", JSON.stringify(playlist, null, 4)); - let songs = playlist.entries.map(i => i.id); - fs.writeFileSync("./_youtoo/Dumps/PlaylistDataList.json", JSON.stringify(songs, null, 4)); - console.log("Found " + songs.length + " songs"); - - for (let song of songs) { - if (!fs.existsSync("./_youtoo/Result/" + song + ".mp3")) { - console.log("[" + song + "] Gathering metadata..."); - exec("yt-dlp", [ "-o", "./_youtoo/_audio", "--skip-download", "--write-info-json", "https://www.youtube.com/watch?v=" + song ], { stdio: "inherit" }) - let metadata = JSON.parse(fs.readFileSync("./_youtoo/_audio.info.json").toString()); - fs.writeFileSync("./_youtoo/Dumps/MetadataRaw" + song + ".json", JSON.stringify(metadata, null, 4)); - - console.log("[" + song + "] Parsing metadata..."); - let partists = (metadata.track ?? metadata.alt_title ?? metadata.title).replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-(.*)/gm, "$1") !== (metadata.track ?? metadata.alt_title ?? metadata.title) ? (metadata.track ?? metadata.alt_title ?? metadata.title).replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-(.*)/gm, "$1") : ""; - let partists2 = (metadata.track ?? metadata.alt_title ?? metadata.title).replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$5") !== (metadata.track ?? metadata.alt_title ?? metadata.title) ? (metadata.track ?? metadata.alt_title ?? metadata.title).replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$5") : ""; - let artistsList = Array.from(new Set([...(metadata.artist ?? metadata.creator ?? metadata.uploader).replace(/(, |,|and |And |&)/gm, "|").split("|").map(i => i.trim()), ...partists.replace(/(, |,|and |And |&)/gm, "|").split("|").map(i => i.trim()), ...partists2.replace(/(, |,|and |And |&)/gm, "|").split("|").map(i => i.trim())])).filter(i => i.trim() !== ""); - let songTitle = (metadata.track ?? metadata.alt_title ?? metadata.title).replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-( |)(.*)(\[(.*)\]|)/gm, "$3").replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").replace(/(.*)\[(.*)\](.*)/gm, "$1").replace(/(.*)\((from|in|by)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").trim(); - console.log("[" + song + "] Gathering lyrics..."); - let lyricsComplete = false; - let lyrics = ""; - try { - let geniusSearchArtist = artistsList[0]; - let geniusSong = (await g.songs.search(geniusSearchArtist + " - " + songTitle))[0]; - let geniusTitle = geniusSong.title.replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-( |)(.*)(\[(.*)\]|)/gm, "$3").replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").replace(/(.*)\[(.*)\](.*)/gm, "$1").replace(/(.*)\((from|in|by)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").trim(); - console.log(geniusTitle.toLowerCase() + " <-> " + songTitle.toLowerCase(), geniusTitle.toLowerCase() === songTitle.toLowerCase()); - if (geniusTitle.toLowerCase() === songTitle.toLowerCase() && geniusSong.raw.lyrics_state === "complete") { - lyricsComplete = true; - lyrics = (await geniusSong.lyrics()).trim() - } - } catch (e) { - console.error(e); - lyrics = ""; - } - if (!lyricsComplete) { - console.log("[" + song + "] Incomplete/nonexistent lyrics, will try to fetch at every sync."); - } - let rmeta = { - video: metadata.title, - title: songTitle, - date: metadata.release_year ?? (metadata.upload_date.substring(0, 4) - 1 +1), - album: overrideAlbum ? overrideAlbumName : metadata.album ?? ((metadata.track ?? metadata.alt_title ?? metadata.title).replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-( |)(.*)(\[(.*)\]|)/gm, "$3").replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").replace(/(.*)\[(.*)\](.*)/gm, "$1").replace(/(.*)\((from|in|by)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").trim()), - potentialArtists: partists, - potentialArtists2: partists2, - artists: artistsList, - lyrics, - complete: lyricsComplete - } - fs.writeFileSync("./_youtoo/Metadata/" + song + ".json", JSON.stringify(rmeta, null, 4)); - console.log("[" + song + "] Downloading audio..."); - exec("yt-dlp", [ "-f", "bestaudio[ext=m4a]", "-x", "-o", "./_youtoo/_audio.m4a", "--audio-format", "m4a", "https://www.youtube.com/watch?v=" + song ], { stdio: "inherit" }); - console.log("[" + song + "] Downloading thumbnail..."); - fs.writeFileSync("./_youtoo/_audio.webp", (await axios.get(metadata.thumbnail, { responseType: "arraybuffer" })).data); - console.log("[" + song + "] Processing thumbnail..."); - exec("magick", [ "-define", "jpeg:size=1024x1024", "./_youtoo/_audio.webp", "-thumbnail", "512x512^", "-gravity", "center", "-extent", "512x512", "./_youtoo/_audio.jpg" ]); - console.log("[" + song + "] Processing audio..."); - let additionalArrayItems = [ "-metadata", "date=" + rmeta.date.toString() ] - if (overrideAlbum) { - additionalArrayItems = [ "-metadata", "album_artist=" + overrideAlbumName ]; - } - exec("ffmpeg", [ "-y", "-i", "./_youtoo/_audio.m4a", "-metadata", "artist=" + rmeta.artists.join(", ") + "", "-metadata", "title=" + rmeta.title, ...additionalArrayItems, "-metadata", "album=" + rmeta.album, "-metadata", "publisher=YouTube", "-metadata", "copyright=© " + rmeta.date.toString() + " " + rmeta.artists[0], "-metadata", "lyrics=" + lyrics, "-metadata", "encoded_by=" + require('./package.json').name + "/" + require('./package.json').version, "./_youtoo/_audio.1.mp3" ], { stdio: "inherit" }); - if (!overrideAlbum) { - exec("ffmpeg", [ "-y", "-i", "./_youtoo/_audio.1.mp3", "-i", "./_youtoo/_audio.jpg", "-map", "0:0", "-map", "1:0", "-c", "copy", "-id3v2_version", "3", "-metadata:s:v", "title=Album cover", "-metadata:s:v", "comment=Cover (front)", "./_youtoo/_audio.mp3" ], { stdio: "inherit" }); - fs.renameSync("./_youtoo/_audio.mp3", "./_youtoo/Result/" + song + ".mp3"); - } else { - fs.renameSync("./_youtoo/_audio.1.mp3", "./_youtoo/Result/" + song + ".mp3"); - } - fs.renameSync("./_youtoo/_audio.jpg", "./_youtoo/AlbumArts/" + song + ".jpg"); - console.log("[" + song + "] Cleaning up..."); - if (fs.existsSync("./_youtoo/_audio.1.mp3")) { fs.rmSync("./_youtoo/_audio.1.mp3"); } - if (fs.existsSync("./_youtoo/_audio.jpg")) { fs.rmSync("./_youtoo/_audio.jpg"); } - if (fs.existsSync("./_youtoo/_audio.webp")) { fs.rmSync("./_youtoo/_audio.webp"); } - if (fs.existsSync("./_youtoo/_audio.m4a")) { fs.rmSync("./_youtoo/_audio.m4a"); } - if (fs.existsSync("./_youtoo/_audio.info.json")) { fs.rmSync("./_youtoo/_audio.info.json"); } - } else { - console.log("[" + song + "] Song already in destination"); - } - } - - console.log("Deleting removed songs...") - for (let file of fs.readdirSync("./_youtoo/Result")) { - let song = file.substring(0, file.length - 4); - if (!songs.includes(song)) { - console.log("[" + song + "] Deleting..."); - fs.unlinkSync("./_youtoo/Result/" + file); - if (fs.existsSync("./_youtoo/Metadata/" + song + ".json")) fs.unlinkSync("./_youtoo/Metadata/" + song + ".json"); - } - } - - console.log("Cleaning up...") - if (fs.existsSync("./_youtoo/Dumps")) { fs.rmSync("./_youtoo/Dumps", { recursive: true }); } - - console.log("Fetching lyrics for incomplete songs...") - for (let file of fs.readdirSync("./_youtoo/Result")) { - let song = file.substring(0, file.length - 4); - if (fs.existsSync("./_youtoo/Metadata/" + song + ".json")) { - console.log("[" + song + "] Fetching metadata..."); - let metadata = JSON.parse(fs.readFileSync("./_youtoo/Metadata/" + song + ".json").toString()); - if (typeof metadata.complete !== "boolean" || !metadata.complete) { - console.log("[" + song + "] Gathering lyrics..."); - let lyricsComplete = false; - let lyrics = ""; - try { - let geniusSearchArtist = metadata.artists[0]; - let geniusSong = (await g.songs.search(geniusSearchArtist + " - " + metadata.title))[0]; - let geniusTitle = geniusSong.title.replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-( |)(.*)(\[(.*)\]|)/gm, "$3").replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").replace(/(.*)\[(.*)\](.*)/gm, "$1").replace(/(.*)\((from|in|by)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").trim(); - console.log(geniusTitle.toLowerCase() + " <-> " + metadata.title.toLowerCase(), geniusTitle.toLowerCase() === metadata.title.toLowerCase()); - if (geniusTitle.toLowerCase() === metadata.title.toLowerCase() && geniusSong.raw.lyrics_state === "complete") { - lyricsComplete = true; - lyrics = (await geniusSong.lyrics()).trim() - } - } catch (e) { - console.error(e); - lyrics = ""; - } - if (!lyricsComplete) { - console.log("[" + song + "] Incomplete/nonexistent lyrics, will try to fetch at every sync."); - } - metadata.complete = lyricsComplete; - metadata.lyrics = lyrics; - fs.writeFileSync("./_youtoo/Metadata/" + song + ".json", JSON.stringify(metadata, null, 4)); - console.log("[" + song + "] Processing audio..."); - exec("ffmpeg", [ "-y", "-i", "./_youtoo/Result/" + file, "-c", "copy", "-metadata", "lyrics=" + lyrics, "-metadata", "encoded_by=" + require('./package.json').name + "/" + require('./package.json').version, "./_youtoo/_audio.mp3" ], { stdio: "inherit" }); - fs.renameSync("./_youtoo/_audio.mp3", "./_youtoo/Result/" + song + ".mp3"); - } - } - } +/* + * MIT License + * + * Copyright (c) 2022- Minteck + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +'use strict'; + +(async () => { + function die(message, code) { + if (message) { console.log(message) } + if (code) { + process.exit(code); + } else { + process.exit(0); + } + } + + String.prototype.isBase64 = function () { + return Buffer.from(this, 'base64').toString('base64').split("=")[0].substring(0, Buffer.from(this, 'base64').toString('base64').split("=")[0].length - 1) === this.substring(0, this.length - 1); + } + + const exec = require('child_process').execFileSync; + const fs = require('fs'); + const axios = require('axios'); + + const Genius = require("genius-lyrics"); + const g = new Genius.Client(); + + if (process.argv[2] !== undefined) { + global.id = process.argv[2]; + if (!id.isBase64() || id.length !== 34) { + die("Invalid playlist ID"); + } + } else { + console.log("Please specify playlist ID"); + } + + if (process.argv[3] !== undefined) { + global.overrideAlbum = true; + global.overrideAlbumName = process.argv[3]; + } else { + global.overrideAlbum = false; + } + + console.log("Preparing folder structure..."); + if (!fs.existsSync("./_youtoo")) fs.mkdirSync("./_youtoo"); + if (!fs.existsSync("./_youtoo/Dumps")) fs.mkdirSync("./_youtoo/Dumps"); + if (!fs.existsSync("./_youtoo/Result")) fs.mkdirSync("./_youtoo/Result"); + if (!fs.existsSync("./_youtoo/Metadata")) fs.mkdirSync("./_youtoo/Metadata"); + if (!fs.existsSync("./_youtoo/AlbumArts")) fs.mkdirSync("./_youtoo/AlbumArts"); + + console.log("Gathering playlist data..."); + let playlist = JSON.parse(exec("yt-dlp", [ "--flat-playlist", "--dump-single-json", "https://www.youtube.com/playlist?list=" + id ]).toString()); + fs.writeFileSync("./_youtoo/Dumps/PlaylistDataRaw.json", JSON.stringify(playlist, null, 4)); + let songs = playlist.entries.map(i => i.id); + fs.writeFileSync("./_youtoo/Dumps/PlaylistDataList.json", JSON.stringify(songs, null, 4)); + console.log("Found " + songs.length + " songs"); + + for (let song of songs) { + if (!fs.existsSync("./_youtoo/Result/" + song + ".mp3")) { + console.log("[" + song + "] Gathering metadata..."); + exec("yt-dlp", [ "-o", "./_youtoo/_audio", "--skip-download", "--write-info-json", "https://www.youtube.com/watch?v=" + song ], { stdio: "inherit" }) + let metadata = JSON.parse(fs.readFileSync("./_youtoo/_audio.info.json").toString()); + fs.writeFileSync("./_youtoo/Dumps/MetadataRaw" + song + ".json", JSON.stringify(metadata, null, 4)); + + console.log("[" + song + "] Parsing metadata..."); + let partists = (metadata.track ?? metadata.alt_title ?? metadata.title).replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-(.*)/gm, "$1") !== (metadata.track ?? metadata.alt_title ?? metadata.title) ? (metadata.track ?? metadata.alt_title ?? metadata.title).replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-(.*)/gm, "$1") : ""; + let partists2 = (metadata.track ?? metadata.alt_title ?? metadata.title).replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$5") !== (metadata.track ?? metadata.alt_title ?? metadata.title) ? (metadata.track ?? metadata.alt_title ?? metadata.title).replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$5") : ""; + let artistsList = Array.from(new Set([...(metadata.artist ?? metadata.creator ?? metadata.uploader).replace(/(, |,|and |And |&)/gm, "|").split("|").map(i => i.trim()), ...partists.replace(/(, |,|and |And |&)/gm, "|").split("|").map(i => i.trim()), ...partists2.replace(/(, |,|and |And |&)/gm, "|").split("|").map(i => i.trim())])).filter(i => i.trim() !== ""); + let songTitle = (metadata.track ?? metadata.alt_title ?? metadata.title).replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-( |)(.*)(\[(.*)\]|)/gm, "$3").replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").replace(/(.*)\[(.*)\](.*)/gm, "$1").replace(/(.*)\((from|in|by)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").trim(); + console.log("[" + song + "] Gathering lyrics..."); + let lyricsComplete = false; + let lyrics = ""; + try { + let geniusSearchArtist = artistsList[0]; + let geniusSong = (await g.songs.search(geniusSearchArtist + " - " + songTitle))[0]; + let geniusTitle = geniusSong.title.replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-( |)(.*)(\[(.*)\]|)/gm, "$3").replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").replace(/(.*)\[(.*)\](.*)/gm, "$1").replace(/(.*)\((from|in|by)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").trim(); + console.log(geniusTitle.toLowerCase() + " <-> " + songTitle.toLowerCase(), geniusTitle.toLowerCase() === songTitle.toLowerCase()); + if (geniusTitle.toLowerCase() === songTitle.toLowerCase() && geniusSong.raw.lyrics_state === "complete") { + lyricsComplete = true; + lyrics = (await geniusSong.lyrics()).trim() + } + } catch (e) { + console.error(e); + lyrics = ""; + } + if (!lyricsComplete) { + console.log("[" + song + "] Incomplete/nonexistent lyrics, will try to fetch at every sync."); + } + let rmeta = { + video: metadata.title, + title: songTitle, + date: metadata.release_year ?? (metadata.upload_date.substring(0, 4) - 1 +1), + album: overrideAlbum ? overrideAlbumName : metadata.album ?? ((metadata.track ?? metadata.alt_title ?? metadata.title).replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-( |)(.*)(\[(.*)\]|)/gm, "$3").replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").replace(/(.*)\[(.*)\](.*)/gm, "$1").replace(/(.*)\((from|in|by)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").trim()), + potentialArtists: partists, + potentialArtists2: partists2, + artists: artistsList, + lyrics, + complete: lyricsComplete + } + fs.writeFileSync("./_youtoo/Metadata/" + song + ".json", JSON.stringify(rmeta, null, 4)); + console.log("[" + song + "] Downloading audio..."); + exec("yt-dlp", [ "-f", "bestaudio[ext=m4a]", "-x", "-o", "./_youtoo/_audio.m4a", "--audio-format", "m4a", "https://www.youtube.com/watch?v=" + song ], { stdio: "inherit" }); + console.log("[" + song + "] Downloading thumbnail..."); + fs.writeFileSync("./_youtoo/_audio.webp", (await axios.get(metadata.thumbnail, { responseType: "arraybuffer" })).data); + console.log("[" + song + "] Processing thumbnail..."); + exec("magick", [ "-define", "jpeg:size=1024x1024", "./_youtoo/_audio.webp", "-thumbnail", "512x512^", "-gravity", "center", "-extent", "512x512", "./_youtoo/_audio.jpg" ]); + console.log("[" + song + "] Processing audio..."); + let additionalArrayItems = [ "-metadata", "date=" + rmeta.date.toString() ] + if (overrideAlbum) { + additionalArrayItems = [ "-metadata", "album_artist=" + overrideAlbumName ]; + } + exec("ffmpeg", [ "-y", "-i", "./_youtoo/_audio.m4a", "-metadata", "artist=" + rmeta.artists.join(", ") + "", "-metadata", "title=" + rmeta.title, ...additionalArrayItems, "-metadata", "album=" + rmeta.album, "-metadata", "publisher=YouTube", "-metadata", "copyright=© " + rmeta.date.toString() + " " + rmeta.artists[0], "-metadata", "lyrics=" + lyrics, "-metadata", "encoded_by=" + require('./package.json').name + "/" + require('./package.json').version, "./_youtoo/_audio.1.mp3" ], { stdio: "inherit" }); + if (!overrideAlbum) { + exec("ffmpeg", [ "-y", "-i", "./_youtoo/_audio.1.mp3", "-i", "./_youtoo/_audio.jpg", "-map", "0:0", "-map", "1:0", "-c", "copy", "-id3v2_version", "3", "-metadata:s:v", "title=Album cover", "-metadata:s:v", "comment=Cover (front)", "./_youtoo/_audio.mp3" ], { stdio: "inherit" }); + fs.renameSync("./_youtoo/_audio.mp3", "./_youtoo/Result/" + song + ".mp3"); + } else { + fs.renameSync("./_youtoo/_audio.1.mp3", "./_youtoo/Result/" + song + ".mp3"); + } + fs.renameSync("./_youtoo/_audio.jpg", "./_youtoo/AlbumArts/" + song + ".jpg"); + console.log("[" + song + "] Cleaning up..."); + if (fs.existsSync("./_youtoo/_audio.1.mp3")) { fs.rmSync("./_youtoo/_audio.1.mp3"); } + if (fs.existsSync("./_youtoo/_audio.jpg")) { fs.rmSync("./_youtoo/_audio.jpg"); } + if (fs.existsSync("./_youtoo/_audio.webp")) { fs.rmSync("./_youtoo/_audio.webp"); } + if (fs.existsSync("./_youtoo/_audio.m4a")) { fs.rmSync("./_youtoo/_audio.m4a"); } + if (fs.existsSync("./_youtoo/_audio.info.json")) { fs.rmSync("./_youtoo/_audio.info.json"); } + } else { + console.log("[" + song + "] Song already in destination"); + } + } + + console.log("Deleting removed songs...") + for (let file of fs.readdirSync("./_youtoo/Result")) { + let song = file.substring(0, file.length - 4); + if (!songs.includes(song)) { + console.log("[" + song + "] Deleting..."); + fs.unlinkSync("./_youtoo/Result/" + file); + if (fs.existsSync("./_youtoo/Metadata/" + song + ".json")) fs.unlinkSync("./_youtoo/Metadata/" + song + ".json"); + } + } + + console.log("Cleaning up...") + if (fs.existsSync("./_youtoo/Dumps")) { fs.rmSync("./_youtoo/Dumps", { recursive: true }); } + + console.log("Fetching lyrics for incomplete songs...") + for (let file of fs.readdirSync("./_youtoo/Result")) { + let song = file.substring(0, file.length - 4); + if (fs.existsSync("./_youtoo/Metadata/" + song + ".json")) { + console.log("[" + song + "] Fetching metadata..."); + let metadata = JSON.parse(fs.readFileSync("./_youtoo/Metadata/" + song + ".json").toString()); + if (typeof metadata.complete !== "boolean" || !metadata.complete) { + console.log("[" + song + "] Gathering lyrics..."); + let lyricsComplete = false; + let lyrics = ""; + try { + let geniusSearchArtist = metadata.artists[0]; + let geniusSong = (await g.songs.search(geniusSearchArtist + " - " + metadata.title))[0]; + let geniusTitle = geniusSong.title.replace(/^([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)-( |)(.*)(\[(.*)\]|)/gm, "$3").replace(/(.*)\((feat|ft|with)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").replace(/(.*)\[(.*)\](.*)/gm, "$1").replace(/(.*)\((from|in|by)(\.|:| |)(| |)([:\"'a-zA-Z0-9 .\-*\_\/\\,&]*)\)(.*)/gmi, "$1").trim(); + console.log(geniusTitle.toLowerCase() + " <-> " + metadata.title.toLowerCase(), geniusTitle.toLowerCase() === metadata.title.toLowerCase()); + if (geniusTitle.toLowerCase() === metadata.title.toLowerCase() && geniusSong.raw.lyrics_state === "complete") { + lyricsComplete = true; + lyrics = (await geniusSong.lyrics()).trim() + } + } catch (e) { + console.error(e); + lyrics = ""; + } + if (!lyricsComplete) { + console.log("[" + song + "] Incomplete/nonexistent lyrics, will try to fetch at every sync."); + } + metadata.complete = lyricsComplete; + metadata.lyrics = lyrics; + fs.writeFileSync("./_youtoo/Metadata/" + song + ".json", JSON.stringify(metadata, null, 4)); + console.log("[" + song + "] Processing audio..."); + exec("ffmpeg", [ "-y", "-i", "./_youtoo/Result/" + file, "-c", "copy", "-metadata", "lyrics=" + lyrics, "-metadata", "encoded_by=" + require('./package.json').name + "/" + require('./package.json').version, "./_youtoo/_audio.mp3" ], { stdio: "inherit" }); + fs.renameSync("./_youtoo/_audio.mp3", "./_youtoo/Result/" + song + ".mp3"); + } + } + } })() \ No newline at end of file -- cgit