const readline = require("readline"); const ytdl = require("ytdl-core"); const ffmpeg = require("fluent-ffmpeg"); const id3 = require('node-id3'); const axios = require('axios'); const request = require("request").defaults({ encoding: null }); const chalk = require('chalk'); const cp = require('child_process'); const fs = require('fs'); const path = require('path'); const terminalLink = require('terminal-link'); const hasflag = require('has-flag'); const gradient = require('gradient-string'); const figlet = require('figlet'); /** * log function for logging in one shell line. * @param {any} something the thing that should be logged. */ const log = (something) => { readline.clearLine(process.stdout, 0); readline.cursorTo(process.stdout, 0); process.stdout.write(something); } /** * convertTime converts a string to a number of seconds. * @param {string} timeString like '00:00:46.27' */ const convertTime = (timeString) => { const timeParts = timeString.split(':'); let seconds = Number(timeParts[2]); seconds = seconds + (Number(timeParts[1]) * 60) + (Number(timeParts[0]) * 60 * 60); return seconds; }; if (process.argv.find(v => v === "help" || v === "-h" || v === "--help")) { console.log(gradient.rainbow.multiline(figlet.textSync("ytdownloader", {horizontalLayout: 'fitted'}))); console.log(chalk.underline("\nUsage:")); console.log(`node main.js ${chalk.italic(" ")}\n`); console.log("The arguments starting with { let stream = ytdl(process.argv[2], { quality: "highestaudio" //filter: 'audioonly', }); const filePath = path.join(__dirname, `/music/${info.title}.mp3`); log(chalk.yellow("🏁 Starting download...")); ffmpeg(stream) .audioBitrate(128) .save(filePath) .on("progress", p => { const progress = convertTime(p.timemark); log( chalk.blue( `⬇️ ${Math.floor( (progress / Number(info.length_seconds)) * 100 )}% downloaded. (${p.targetSize}kB)` ) ); }) .on('error', err => { console.error(chalk.redBright('❌ Found an error: ' + err.message)); process.exit(1); }) .on("end", () => { if (process.argv.length === 5) { // Retreiving info from itunes api and writing tags to downloaded file. const artist = process.argv[3]; const song = process.argv[4]; log(chalk.yellow("🎵 Calling iTunes api...")); axios.get(`https://itunes.apple.com/search?term=${artist} ${song}&entity=song`).then(res => { request.get(res.data.results[0].artworkUrl100.replace('100x100', '3000x3000'), function(err, _res, body) { axios.get(`https://itunes.apple.com/search?term=${artist} ${res.data.results[0].collectionName}&entity=album`).then(album => { log(chalk.green("✅ Retreived information!")); var tags = { title: res.data.results[0].trackName, artist: res.data.results[0].artistName, performerInfo: album.data.results[0].artistName, album: res.data.results[0].collectionName, copyright: album.data.results[0].copyright, year: new Date(res.data.results[0].releaseDate).getFullYear(), image: { mime: "jpeg", type: { id: 3, name: "front cover" }, imageBuffer: body }, fileType: "mp3", genre: res.data.results[0].primaryGenreName }; log(chalk.yellow("✍🏻 Writing tags to mp3 file...")); id3.update(tags, filePath, () => { if (fs.existsSync(filePath) && process.platform === "darwin" && !hasflag("no-itunes")) { log(chalk.yellow("🎵 Adding song to Apple Music library...")); cp.execSync(`cp "${filePath}" "/Users/job/Music/Music/Media/Automatically\ Add\ to\ Music.localized"`); } log(chalk.green(`✅ Successfully downloaded ${info.title}.mp3!\n`)); process.exit(); }); }); }); }); } else { log(chalk.green(`✅ Successfully downloaded ${info.title}.mp3!`)); process.exit(); } }); });