refactor import by youtube url into plugin
This commit is contained in:
parent
cb6077bc87
commit
fa21bdc3d2
2 changed files with 130 additions and 87 deletions
155
lib/player.js
155
lib/player.js
|
|
@ -17,7 +17,6 @@ var safePath = require('./safe_path');
|
|||
var PassThrough = require('stream').PassThrough;
|
||||
var url = require('url');
|
||||
var superagent = require('superagent');
|
||||
var ytdl = require('ytdl');
|
||||
|
||||
module.exports = Player;
|
||||
|
||||
|
|
@ -26,16 +25,6 @@ groove.setLogging(groove.LOG_WARNING);
|
|||
var cpuCount = require('os').cpus().length;
|
||||
|
||||
|
||||
// sorted from worst to best
|
||||
var YTDL_AUDIO_ENCODINGS = [
|
||||
'mp3',
|
||||
'aac',
|
||||
'wma',
|
||||
'vorbis',
|
||||
'wav',
|
||||
'flac',
|
||||
];
|
||||
|
||||
var PLAYER_KEY_PREFIX = "Player.";
|
||||
var LIBRARY_KEY_PREFIX = "Library.";
|
||||
var LIBRARY_DIR_PREFIX = "LibraryDir.";
|
||||
|
|
@ -268,6 +257,8 @@ function Player(db, musicDirectory, instantBufferBytes) {
|
|||
this.expectHeaders = true;
|
||||
|
||||
this.playlistItemDeleteQueue = [];
|
||||
|
||||
this.importUrlFilters = [];
|
||||
}
|
||||
|
||||
Player.prototype.initialize = function(cb) {
|
||||
|
|
@ -797,88 +788,78 @@ Player.prototype.importUrl = function(urlString, cb) {
|
|||
cb = cb || logIfError;
|
||||
|
||||
var tmpDir = path.join(self.musicDirectory, '.tmp');
|
||||
var filterIndex = 0;
|
||||
|
||||
mkdirp(tmpDir, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var parsedUrl = url.parse(urlString);
|
||||
|
||||
// detect youtube downloads
|
||||
if ((parsedUrl.hostname === 'youtube.com' || parsedUrl.hostname === 'www.youtube.com') &&
|
||||
parsedUrl.pathname === '/watch')
|
||||
{
|
||||
var bestFormat = null;
|
||||
ytdl.getInfo(urlString, gotYouTubeInfo);
|
||||
} else {
|
||||
var remoteFilename = path.basename(parsedUrl.pathname);
|
||||
var decodedFilename;
|
||||
try {
|
||||
decodedFilename = decodeURI(remoteFilename);
|
||||
} catch (err) {
|
||||
decodedFilename = remoteFilename;
|
||||
}
|
||||
var req = superagent.get(urlString);
|
||||
handleDownload(req, decodedFilename);
|
||||
}
|
||||
|
||||
function gotYouTubeInfo(err, info) {
|
||||
if (err) return cb(err);
|
||||
for (var i = 0; i < info.formats.length; i += 1) {
|
||||
var format = info.formats[i];
|
||||
if (bestFormat == null || format.audioBitrate > bestFormat.audioBitrate ||
|
||||
(format.audioBitrate === bestFormat.audioBitrate &&
|
||||
YTDL_AUDIO_ENCODINGS.indexOf(format.audioEncoding) >
|
||||
YTDL_AUDIO_ENCODINGS.indexOf(bestFormat.audioEncoding)))
|
||||
{
|
||||
bestFormat = format;
|
||||
}
|
||||
}
|
||||
if (YTDL_AUDIO_ENCODINGS.indexOf(bestFormat.audioEncoding) === -1) {
|
||||
console.warn("YouTube Import: unrecognized audio format:", bestFormat.audioEncoding);
|
||||
}
|
||||
var req = ytdl(urlString, {filter: filter});
|
||||
handleDownload(req, info.title + '.' + bestFormat.container);
|
||||
|
||||
function filter(format) {
|
||||
return format.audioBitrate === bestFormat.audioBitrate &&
|
||||
format.audioEncoding === bestFormat.audioEncoding;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDownload(req, remoteFilename) {
|
||||
var ext = path.extname(remoteFilename);
|
||||
var destPath = path.join(tmpDir, uuid() + ext);
|
||||
var ws = fs.createWriteStream(destPath);
|
||||
|
||||
var calledCallback = false;
|
||||
req.pipe(ws);
|
||||
ws.on('close', function(){
|
||||
if (calledCallback) return;
|
||||
self.importFile(ws.path, remoteFilename, function(err, dbFile) {
|
||||
if (err) {
|
||||
cleanAndCb(err);
|
||||
} else {
|
||||
calledCallback = true;
|
||||
cb(null, dbFile);
|
||||
}
|
||||
});
|
||||
});
|
||||
ws.on('error', cleanAndCb);
|
||||
req.on('error', cleanAndCb);
|
||||
|
||||
function cleanAndCb(err) {
|
||||
fs.unlink(destPath, function(err) {
|
||||
if (err) {
|
||||
console.warn("Unable to clean up temp file:", err.stack);
|
||||
}
|
||||
});
|
||||
if (calledCallback) return;
|
||||
calledCallback = true;
|
||||
cb(err);
|
||||
}
|
||||
}
|
||||
tryImportFilter();
|
||||
});
|
||||
|
||||
function tryImportFilter() {
|
||||
var importPlugin = self.importUrlFilters[filterIndex];
|
||||
if (importPlugin) {
|
||||
importPlugin.importUrl(urlString, callNextFilter);
|
||||
} else {
|
||||
downloadRaw();
|
||||
}
|
||||
function callNextFilter(err, dlStream, filename) {
|
||||
if (err || !dlStream) {
|
||||
if (err) console.warn("import filter error, skipping:", err.stack);
|
||||
filterIndex += 1;
|
||||
tryImportFilter();
|
||||
return;
|
||||
}
|
||||
handleDownload(dlStream, filename);
|
||||
}
|
||||
}
|
||||
|
||||
function downloadRaw() {
|
||||
var parsedUrl = url.parse(urlString);
|
||||
var remoteFilename = path.basename(parsedUrl.pathname);
|
||||
var decodedFilename;
|
||||
try {
|
||||
decodedFilename = decodeURI(remoteFilename);
|
||||
} catch (err) {
|
||||
decodedFilename = remoteFilename;
|
||||
}
|
||||
var req = superagent.get(urlString);
|
||||
handleDownload(req, decodedFilename);
|
||||
}
|
||||
|
||||
function handleDownload(req, remoteFilename) {
|
||||
var ext = path.extname(remoteFilename);
|
||||
var destPath = path.join(tmpDir, uuid() + ext);
|
||||
var ws = fs.createWriteStream(destPath);
|
||||
|
||||
var calledCallback = false;
|
||||
req.pipe(ws);
|
||||
ws.on('close', function(){
|
||||
if (calledCallback) return;
|
||||
self.importFile(ws.path, remoteFilename, function(err, dbFile) {
|
||||
if (err) {
|
||||
cleanAndCb(err);
|
||||
} else {
|
||||
calledCallback = true;
|
||||
cb(null, dbFile);
|
||||
}
|
||||
});
|
||||
});
|
||||
ws.on('error', cleanAndCb);
|
||||
req.on('error', cleanAndCb);
|
||||
|
||||
function cleanAndCb(err) {
|
||||
fs.unlink(destPath, function(err) {
|
||||
if (err) {
|
||||
console.warn("Unable to clean up temp file:", err.stack);
|
||||
}
|
||||
});
|
||||
if (calledCallback) return;
|
||||
calledCallback = true;
|
||||
cb(err);
|
||||
}
|
||||
}
|
||||
|
||||
function logIfError(err) {
|
||||
if (err) {
|
||||
console.error("Unable to import by URL.", err.stack, "URL:", urlString);
|
||||
|
|
|
|||
62
lib/plugins/ytdl.js
Normal file
62
lib/plugins/ytdl.js
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
var ytdl = require('ytdl');
|
||||
var url = require('url');
|
||||
|
||||
module.exports = YtDlPlugin;
|
||||
|
||||
// sorted from worst to best
|
||||
var YTDL_AUDIO_ENCODINGS = [
|
||||
'mp3',
|
||||
'aac',
|
||||
'wma',
|
||||
'vorbis',
|
||||
'wav',
|
||||
'flac',
|
||||
];
|
||||
|
||||
function YtDlPlugin(gb) {
|
||||
gb.player.importUrlFilters.push(this);
|
||||
}
|
||||
|
||||
YtDlPlugin.prototype.importUrl = function(urlString, cb) {
|
||||
var parsedUrl = url.parse(urlString);
|
||||
|
||||
var isYouTube = (parsedUrl.pathname === '/watch' &&
|
||||
(parsedUrl.hostname === 'youtube.com' ||
|
||||
parsedUrl.hostname === 'www.youtube.com' ||
|
||||
parsedUrl.hostname === 'm.youtube.com')) ||
|
||||
parsedUrl.hostname === 'youtu.be' ||
|
||||
parsedUrl.hostname === 'www.youtu.be';
|
||||
|
||||
if (!isYouTube) {
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
var bestFormat = null;
|
||||
ytdl.getInfo(urlString, gotYouTubeInfo);
|
||||
|
||||
function gotYouTubeInfo(err, info) {
|
||||
if (err) return cb(err);
|
||||
for (var i = 0; i < info.formats.length; i += 1) {
|
||||
var format = info.formats[i];
|
||||
if (bestFormat == null || format.audioBitrate > bestFormat.audioBitrate ||
|
||||
(format.audioBitrate === bestFormat.audioBitrate &&
|
||||
YTDL_AUDIO_ENCODINGS.indexOf(format.audioEncoding) >
|
||||
YTDL_AUDIO_ENCODINGS.indexOf(bestFormat.audioEncoding)))
|
||||
{
|
||||
bestFormat = format;
|
||||
}
|
||||
}
|
||||
if (YTDL_AUDIO_ENCODINGS.indexOf(bestFormat.audioEncoding) === -1) {
|
||||
console.warn("YouTube Import: unrecognized audio format:", bestFormat.audioEncoding);
|
||||
}
|
||||
var req = ytdl(urlString, {filter: filter});
|
||||
var filename = info.title + '.' + bestFormat.container;
|
||||
cb(null, req, filename);
|
||||
|
||||
function filter(format) {
|
||||
return format.audioBitrate === bestFormat.audioBitrate &&
|
||||
format.audioEncoding === bestFormat.audioEncoding;
|
||||
}
|
||||
}
|
||||
};
|
||||
Loading…
Reference in a new issue