#!/usr/bin/env node 'use strict' const spawn = require('child_process').spawn const fs = require('fs') const readline = require('readline') const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) function download (arg, handleCb) { let yt = spawn('youtube-dl', ['--no-playlist', '--playlist-end', 1, '-j', '-f', 'bestaudio/best', arg]) let output = '' yt.stdout.on('data', function (chunk) { output += chunk.toString('utf8') }) yt.on('close', function () { let data = JSON.parse(output) delete data.formats fetchVideo(data, handleCb) }) } function fetchVideo (data, cb) { console.log('audio codec:', data.acodec) if (data.acodec !== 'mp3' || data.vcodec !== 'none') { let tempName = __dirname + '/tmp.yt.' + data.id + '.mp3' let ffmpeg = spawn('ffmpeg', ['-hide_banner', '-i', data.url, '-codec:a', 'libmp3lame', '-q:a', 2, '-joint_stereo', 1, '-y', tempName]) ffmpeg.stdout.pipe(process.stderr) ffmpeg.stderr.pipe(process.stderr) data.filename = tempName console.log('Downloading ' + data.title + '...') ffmpeg.on('close', function () { outputVideo(data, cb) }) } else { console.log('Invalid format returned.') cb(null) } } function outputVideo (video, cb) { cb({ title: video.title, artist: video.uploader, url: video.webpage_url, art: video.thumbnail, source: video.filename }) } function parseTitle(data) { let tt = data.title // Remove []'s from the beginning tt = tt.replace(/^\[\w+\]\s?/i, '') // Remove "Official Video/Audio" tag tt = tt.replace(/\s?(?:[\(\)\[\]])?Official\s?[\w]+(?:[\(\)\[\]])?/i, '') // Remove "Audio" tag tt = tt.replace(/\s?(?:[\(\)\[\]])Audio?(?:[\(\)\[\]])/i, '') // Remove "lyrics" tag tt = tt.replace(/\s?(?:[\(\)\[\]])?lyrics?\s?(?:[\w]+)?(?:[\(\)\[\]])?\s?/i, '') // Artist / Title split let at = tt.split(' - ', 2) let artist let title if (at.length > 1) { artist = at[0] title = tt.substring(artist.length + 3) } else { artist = data.artist title = tt } data.title = title data.artist = artist return data } download(process.argv[2], function (s) { let clean = parseTitle(s) let filename = clean.artist + ' - ' + clean.title + '.mp3' console.log('Cleaning up..') console.log('Original Title: ' + s.title + '\n') console.log('Determined Title: ' + clean.title) console.log('Determined Artist: ' + clean.artist) console.log('Determined File Name: ' + filename) rl.question(`Title [${clean.title}] ? `, (answer) => { if (answer && answer.trim() !== '') { clean.title = answer } rl.question(`Artist [${clean.artist}] ? `, (answer) => { if (answer && answer.trim() !== '') { clean.artist = answer } rl.question(`File [${filename}] ? `, (answer) => { if (answer && answer.trim() !== '') { filename = answer } let fn = __dirname + '/' + filename fs.rename(s.source, fn, function (err) { if (err) { console.error(err) return rl.close() } let id3 = spawn('id3', ['-a', clean.artist, '-t', clean.title, fn]) id3.on('close', () => { console.log('Saved as ' + fn) rl.close() }) }) }) }) }) })