135 lines
3.3 KiB
JavaScript
135 lines
3.3 KiB
JavaScript
|
#!/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()
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
})
|