From 7448429341320e0b3c6f88a7a365ee6990301bda Mon Sep 17 00:00:00 2001 From: calzoneman Date: Mon, 21 Mar 2016 23:28:21 -0700 Subject: [PATCH] Fix #566 Refactored the ffprobe stream-selection logic to handle rejected files better: * Streams tagged as a non-default disposition are not considered * If a file has any video stream, the audio stream will be ignored This should prevent videos from being misreported as invalid audio codecs, etc. --- package.json | 2 +- src/ffmpeg.js | 91 ++++++++++++++++++++++++++++++++++---------------- www/js/util.js | 1 + 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 18bc659e..61c5026e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Calvin Montgomery", "name": "CyTube", "description": "Online media synchronizer and chat", - "version": "3.14.3", + "version": "3.14.4", "repository": { "url": "http://github.com/calzoneman/sync" }, diff --git a/src/ffmpeg.js b/src/ffmpeg.js index e27d6418..6546a75e 100644 --- a/src/ffmpeg.js +++ b/src/ffmpeg.js @@ -113,6 +113,20 @@ function readOldFormat(buf) { return data; } +function isAlternateDisposition(stream) { + if (!stream.disposition) { + return false; + } + + for (var key in stream) { + if (key !== "default" && stream.disposition[key]) { + return true; + } + } + + return false; +} + function reformatData(data) { var reformatted = {}; @@ -127,24 +141,54 @@ function reformatData(data) { reformatted.title = data.format.tags ? data.format.tags.title : null; var container = data.format.format_name.split(",")[0]; - data.streams.forEach(function (stream) { - if (stream.codec_type === "video" && - acceptedCodecs.hasOwnProperty(container + "/" + stream.codec_name)) { - reformatted.vcodec = stream.codec_name; - if (!reformatted.title && stream.tags) { - reformatted.title = stream.tags.title; - } - } else if (stream.codec_type === "audio") { - reformatted.acodec = stream.codec_name; - } - }); + var isVideo = false; + var audio = null; + for (var i = 0; i < data.streams.length; i++) { + const stream = data.streams[i]; - if (reformatted.vcodec && !(audioOnlyContainers.hasOwnProperty(container))) { - reformatted.type = [container, reformatted.vcodec].join("/"); - reformatted.medium = "video"; - } else if (reformatted.acodec) { - reformatted.type = [container, reformatted.acodec].join("/"); - reformatted.medium = "audio"; + // Trash streams with alternate dispositions, e.g. `attached_pic` for + // embedded album art on MP3s (not a real video stream) + if (isAlternateDisposition(stream)) { + continue; + } + + if (stream.codec_type === "video" && + !audioOnlyContainers.hasOwnProperty(container)) { + isVideo = true; + if (acceptedCodecs.hasOwnProperty(container + "/" + stream.codec_name)) { + reformatted.vcodec = stream.codec_name; + reformatted.medium = "video"; + reformatted.type = [container, reformatted.vcodec].join("/"); + + if (stream.tags && stream.tags.title) { + reformatted.title = stream.tags.title; + } + + return reformatted; + } + } else if (stream.codec_type === "audio" && !audio && + acceptedAudioCodecs.hasOwnProperty(stream.codec_name)) { + audio = { + acodec: stream.codec_name, + medium: "audio" + }; + + if (stream.tags && stream.tags.title) { + audio.title = stream.tags.title; + } + } + } + + // Override to make sure video files with no valid video streams but some + // acceptable audio stream are rejected. + if (isVideo) { + return reformatted; + } + + if (audio) { + for (var key in audio) { + reformatted[key] = audio[key]; + } } return reformatted; @@ -273,10 +317,6 @@ exports.query = function (filename, cb) { } 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, @@ -286,10 +326,6 @@ exports.query = function (filename, cb) { 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, @@ -299,9 +335,8 @@ exports.query = function (filename, cb) { 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."); + return cb("File did not contain an acceptable codec. See " + + "https://git.io/va9g9 for details."); } }); }); diff --git a/www/js/util.js b/www/js/util.js index 942add40..2ef63c8b 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -2064,6 +2064,7 @@ function queueMessage(data, type) { } } var text = data.msg; + text = text.replace(/(https?:[^ ]+)/g, "$1"); if (typeof data.link === "string") { text += "
" + data.link + "";