From c4add8f14211d884ea11900f19bf31fb39026e8a Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 24 May 2015 11:06:02 -0400 Subject: [PATCH] Preflight raw file requests to get better error messages --- lib/ffmpeg.js | 155 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 53 deletions(-) diff --git a/lib/ffmpeg.js b/lib/ffmpeg.js index 602c9cfe..e81a7345 100644 --- a/lib/ffmpeg.js +++ b/lib/ffmpeg.js @@ -1,6 +1,9 @@ var Logger = require("./logger"); var Config = require("./config"); var spawn = require("child_process").spawn; +var https = require("https"); +var http = require("http"); +var urlparse = require("url"); var USE_JSON = true; @@ -21,6 +24,47 @@ var audioOnlyContainers = { "mp3": true }; +function testUrl(url, cb, redirected) { + var data = urlparse.parse(url); + if (!/https?:/.test(data.protocol)) { + return cb("Video links must start with http:// or https://"); + } + + if (!data.hostname) { + return cb("Invalid link"); + } + + var transport = (data.protocol === "https:") ? https : http; + data.method = "HEAD"; + var req = transport.request(data, function (res) { + req.abort(); + + if (res.statusCode === 301 || res.statusCode === 302) { + if (redirected) { + return cb("Too many redirects. Please provide a direct link to the " + "file"); + } + return testUrl(res.headers['location'], cb, true); + } + + if (res.statusCode !== 200) { + return cb("HTTP " + res.statusCode + " " + res.statusMessage); + } + + if (!/^audio|^video/.test(res.headers['content-type'])) { + return cb("Server did not return an audio or video file"); + } + + cb(); + }); + + req.on("error", function (err) { + cb(err); + }); + + req.end(); +} + function readOldFormat(buf) { var lines = buf.split("\n"); var tmp = { tags: {} }; @@ -149,64 +193,69 @@ exports.query = function (filename, cb) { "or HTTPS"); } - exports.ffprobe(filename, function (err, data) { + testUrl(filename, function (err) { if (err) { - if (err.code && err.code === "ENOENT") { - return cb("Failed to execute `ffprobe`. Set ffmpeg.ffprobe-exec to " + - "the correct name of the executable in config.yaml. If " + - "you are using Debian or Ubuntu, it is probably avprobe."); - } else if (err.message) { - if (err.message.match(/protocol not found/i)) - return cb("Link uses a protocol unsupported by this server's ffmpeg"); + return cb(err); + } - var m = err.message.match(/(http error .*)/i); - if (m) return cb(m[1]); + exports.ffprobe(filename, function (err, data) { + if (err) { + if (err.code && err.code === "ENOENT") { + return cb("Failed to execute `ffprobe`. Set ffmpeg.ffprobe-exec " + + "to the correct name of the executable in config.yaml. " + + "If you are using Debian or Ubuntu, it is probably " + + "avprobe."); + } else if (err.message) { + if (err.message.match(/protocol not found/i)) + return cb("Link uses a protocol unsupported by this server's " + + "version of ffmpeg"); - Logger.errlog.log(err.stack || err); + Logger.errlog.log(err.stack || err); + return cb("Unable to query file data with ffmpeg"); + } else { + Logger.errlog.log(err.stack || err); + return cb("Unable to query file data with ffmpeg"); + } + } + + try { + data = reformatData(data); + } catch (e) { + Logger.errlog.log(e.stack || e); return cb("Unable to query file data with ffmpeg"); + } + + if (data.medium === "video") { + if (!acceptedCodecs.hasOwnProperty(data.type)) { + return cb("Unsupported video codec " + data.type); + } + + data = { + title: data.title || "Raw Video", + duration: data.duration, + bitrate: data.bitrate, + codec: data.type + }; + + cb(null, data); + } else if (data.medium === "audio") { + if (!acceptedAudioCodecs.hasOwnProperty(data.acodec)) { + return cb("Unsupported audio codec " + data.acodec); + } + + data = { + title: data.title || "Raw Audio", + duration: data.duration, + bitrate: data.bitrate, + codec: data.acodec + }; + + cb(null, data); } else { - Logger.errlog.log(err.stack || err); - return cb("Unable to query file data with ffmpeg"); + return cb("Parsed metadata did not contain a valid video or audio " + + "stream. Either the file is invalid or it has a format " + + "unsupported by this server's version of ffmpeg."); } - } - - try { - data = reformatData(data); - } catch (e) { - Logger.errlog.log(e.stack || e); - return cb("Unable to query file data with ffmpeg"); - } - - if (data.medium === "video") { - if (!acceptedCodecs.hasOwnProperty(data.type)) { - return cb("Unsupported video codec " + data.type); - } - - data = { - title: data.title || "Raw Video", - duration: data.duration, - bitrate: data.bitrate, - codec: data.type - }; - - cb(null, data); - } else if (data.medium === "audio") { - if (!acceptedAudioCodecs.hasOwnProperty(data.acodec)) { - return cb("Unsupported audio codec " + data.acodec); - } - - data = { - title: data.title || "Raw Audio", - duration: data.duration, - bitrate: data.bitrate, - codec: data.acodec - }; - - cb(null, data); - } else { - return cb("Parsed metadata did not contain a valid video or audio stream. " + - "Either the file is invalid or it has a format unsupported by " + - "this server's version of ffmpeg."); - } + }); }); };