diff --git a/config.template.yaml b/config.template.yaml index 049cbf8b..1daea6ee 100644 --- a/config.template.yaml +++ b/config.template.yaml @@ -179,6 +179,6 @@ channel-blacklist: [] # If you have ffmpeg installed, you can query metadata from raw files, allowing # server-synched raw file playback. This requires the following: # * ffmpeg must be installed on the server -# * you must install the fluent-ffmpeg module (npm install fluent-ffmpeg) +# * you must install the fluent-ffmpeg module (npm install fluent-ffmpeg@~1.7.2) ffmpeg: enabled: false diff --git a/lib/channel/playlist.js b/lib/channel/playlist.js index fadf0e48..bca2486a 100644 --- a/lib/channel/playlist.js +++ b/lib/channel/playlist.js @@ -885,7 +885,8 @@ PlaylistModule.prototype._addItem = function (media, data, user, cb) { } /* Warn about possibly unsupported formats */ - if (media.type === "fi" && media.meta.codec.indexOf("/") !== -1 && + if (media.type === "fi" && media.meta.codec && + media.meta.codec.indexOf("/") !== -1 && media.meta.codec !== "mov/h264" && media.meta.codec !== "flv/h264") { user.socket.emit("queueWarn", { @@ -1280,8 +1281,21 @@ PlaylistModule.prototype.handleQueuePlaylist = function (user, data) { } } + /* Ancient playlists don't have full data */ + if (pl.length > 0 && !pl[0].hasOwnProperty("title")) { + pl.forEach(function (item) { + self.handleQueue(user, { + id: item.id, + type: item.type, + pos: data.pos, + temp: temp + }); + }); + return; + } + pl.forEach(function (item) { - var m = new Media(item.id, item.title, item.seconds, item.type); + var m = new Media(item.id, item.title, item.seconds, item.type, item.meta); self._addItem(m, qdata, user); }); self.channel.activeLock.release(); diff --git a/lib/database.js b/lib/database.js index 505f54d1..bb4e24b5 100644 --- a/lib/database.js +++ b/lib/database.js @@ -405,7 +405,11 @@ module.exports.saveUserPlaylist = function (pl, username, plname, callback) { id: pl[i].media.id, title: pl[i].media.title, seconds: pl[i].media.seconds, - type: pl[i].media.type + type: pl[i].media.type, + meta: { + codec: pl[i].media.meta.codec, + bitrate: pl[i].media.meta.bitrate + } }; time += pl[i].media.seconds; tmp.push(e); diff --git a/lib/ffmpeg.js b/lib/ffmpeg.js index bf4d2329..3176f204 100644 --- a/lib/ffmpeg.js +++ b/lib/ffmpeg.js @@ -1,12 +1,12 @@ var Logger = require("./logger"); var Config = require("./config"); -var Metadata; +var ffprobe; var enabled = false; function init() { if (Config.get("ffmpeg.enabled")) { try { - Metadata = require("fluent-ffmpeg").Metadata; + ffprobe = require("fluent-ffmpeg").ffprobe; Logger.syslog.log("Enabling raw file support with fluent-ffmpeg"); enabled = true; } catch (e) { @@ -34,7 +34,7 @@ var audioOnlyContainers = { }; exports.query = function (filename, cb) { - if (!Metadata) { + if (!ffprobe) { init(); } @@ -47,14 +47,18 @@ exports.query = function (filename, cb) { "or HTTPS"); } - new Metadata(filename, function (meta, err) { + ffprobe(filename, function (err, meta) { if (err) { - return cb(err); + return cb("Unable to query file data with ffmpeg"); + } + + meta = parse(meta); + if (meta == null) { + return cb("Unknown error"); } if (isVideo(meta)) { - var video = meta.video; - var codec = video.container + "/" + video.codec; + var codec = meta.container + "/" + meta.vcodec; if (!(codec in acceptedCodecs)) { return cb("Unsupported video codec " + codec); @@ -62,15 +66,14 @@ exports.query = function (filename, cb) { var data = { title: meta.title || "Raw Video", - duration: Math.ceil(meta.durationsec), - bitrate: video.bitrate, + duration: Math.ceil(meta.seconds), + bitrate: meta.bitrate, codec: codec }; cb(null, data); } else if (isAudio(meta)) { - var audio = meta.audio; - var codec = audio.codec; + var codec = meta.acodec; if (!(codec in acceptedAudioCodecs)) { return cb("Unsupported audio codec " + codec); @@ -78,8 +81,8 @@ exports.query = function (filename, cb) { var data = { title: meta.title || "Raw Audio", - duration: Math.ceil(meta.durationsec), - bitrate: audio.bitrate, + duration: Math.ceil(meta.seconds), + bitrate: meta.bitrate, codec: codec }; @@ -96,13 +99,36 @@ exports.query = function (filename, cb) { }; function isVideo(meta) { - return meta.video && - meta.video.bitrate > 0 && - meta.video.container && - meta.video.codec && - !(meta.video.container in audioOnlyContainers); + return meta.vcodec && !(meta.container in audioOnlyContainers); } function isAudio(meta) { - return meta.audio && meta.audio.bitrate > 0 && meta.audio.codec; + return meta.acodec; +} + +function parse(meta) { + if (meta == null) { + return null; + } + + if (!meta.format) { + return null; + } + + var data = {}; + meta.streams.forEach(function (s) { + if (s.codec_type === "video") { + data.vcodec = s.codec_name; + } else if (s.codec_type === "audio") { + data.acodec = s.codec_name; + } + }); + + data.container = meta.format.format_name.split(",")[0]; + data.bitrate = parseInt(meta.format.bit_rate) / 1000; + if (meta.format.tags) { + data.title = meta.format.tags.title; + } + data.seconds = Math.ceil(parseFloat(meta.format.duration)); + return data; } diff --git a/lib/server.js b/lib/server.js index be530ba2..eb033473 100644 --- a/lib/server.js +++ b/lib/server.js @@ -9,7 +9,7 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -const VERSION = "3.2.0"; +const VERSION = "3.2.1"; var singleton = null; var Config = require("./config"); diff --git a/package.json b/package.json index 0af9bf93..79d9ea5d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Calvin Montgomery", "name": "CyTube", "description": "Online media synchronizer and chat", - "version": "3.2.0", + "version": "3.2.1", "repository": { "url": "http://github.com/calzoneman/sync" }, diff --git a/www/js/ui.js b/www/js/ui.js index 43e388b0..f8b6d84a 100644 --- a/www/js/ui.js +++ b/www/js/ui.js @@ -398,7 +398,7 @@ $("#mediaurl").keyup(function(ev) { } var url = $("#mediaurl").val().split("?")[0]; - if (url.match(/^https?:\/\/(.*)?\.(flv|mp4|og[gv]|webm|mp3)$/)) { + if (url.match(/^https?:\/\/(.*)?\.(flv|mp4|og[gv]|webm|mp3|mov)$/)) { var title = $("#addfromurl-title"); if (title.length === 0) { title = $("
") diff --git a/www/js/util.js b/www/js/util.js index 49f75a28..e0bc3a31 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -1289,7 +1289,7 @@ function parseMediaLink(url) { /* Raw file */ var tmp = url.split("?")[0]; if (tmp.match(/^https?:\/\//)) { - if (tmp.match(/\.(mp4|flv|webm|og[gv]|mp3)$/)) { + if (tmp.match(/\.(mp4|flv|webm|og[gv]|mp3|mov)$/)) { return { id: url, type: "fi" @@ -1298,7 +1298,7 @@ function parseMediaLink(url) { Callbacks.queueFail({ link: url, msg: "The file you are attempting to queue does not match the supported " + - "file extensions mp4, flv, webm, ogg, ogv, mp3." + "file extensions mp4, flv, webm, ogg, ogv, mp3, mov." }); throw new Error("ERROR_QUEUE_UNSUPPORTED_EXTENSION"); }