ytdownloader/main.js

140 lines
5.0 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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("<YouTube video url> <?Artist name> <?Song name>")}\n`);
console.log("The arguments starting with <? are optional. If not specified the song won't have id3 tags written to it.");
process.exit();
}
if (process.argv.length !== 5) {
console.log(chalk.yellowBright("⚠ Not getting metadata for this song; expected arguments are not given!"));
}
const ffmpegPath = path.join(__dirname + "/ffmpeg");
if (fs.existsSync(ffmpegPath)) {
// On linux, you require the command-line version of ffmpeg.
if (process.platform !== "linux") ffmpeg.setFfmpegPath(ffmpegPath);
} else {
console.error(chalk.redBright("❌ The FFmpeg executable wasn't found. See " + terminalLink("ffmpeg's website", "https://www.ffmpeg.org") + " for downloads."))
process.exit(1);
}
log(chalk.yellow(" Getting information from YouTube..."));
ytdl.getInfo(process.argv[2], (err, info) => {
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();
}
});
});