diff --git a/lib/channel/mediarefresher.js b/lib/channel/mediarefresher.js index a56766d3..3cddc24e 100644 --- a/lib/channel/mediarefresher.js +++ b/lib/channel/mediarefresher.js @@ -21,6 +21,10 @@ MediaRefresherModule.prototype.onPreMediaChange = function (data, cb) { return this.initGoogleDocs(data, function () { cb(null, ChannelModule.PASSTHROUGH); }); + case "gp": + return this.initGooglePlus(data, function () { + cb(null, ChannelModule.PASSTHROUGH); + }); case "vi": return this.initVimeo(data, function () { cb(null, ChannelModule.PASSTHROUGH); @@ -116,4 +120,52 @@ MediaRefresherModule.prototype.refreshGoogleDocs = function (media, cb) { }); }; +MediaRefresherModule.prototype.initGooglePlus = function (media, cb) { + var self = this; + + if (self.dead || self.channel.dead) { + return; + } + + self.channel.activeLock.lock(); + InfoGetter.getMedia(media.id, "gp", function (err, data) { + if (self.dead || self.channel.dead) { + return; + } + + switch (err) { + case "HTTP 302": + case "Video not found": + case "Private video": + self.channel.logger.log("[mediarefresher] Google+ refresh failed: " + + err); + self.channel.activeLock.release(); + if (cb) cb(); + return; + default: + if (err) { + self.channel.logger.log("[mediarefresher] Google+ refresh failed: " + + err); + Logger.errlog.log("Google+ refresh failed for ID " + media.id + + ": " + err); + self.channel.activeLock.release(); + if (cb) cb(); + return; + } + } + + if (media !== self._media) { + self.channel.activeLock.release(); + if (cb) cb(); + return; + } + + self.channel.logger.log("[mediarefresher] Refreshed Google+ video with ID " + + media.id); + media.meta = data.meta; + self.channel.activeLock.release(); + if (cb) cb(); + }); +}; + module.exports = MediaRefresherModule; diff --git a/lib/database/channels.js b/lib/database/channels.js index 13b7c80a..dd68f083 100644 --- a/lib/database/channels.js +++ b/lib/database/channels.js @@ -426,8 +426,7 @@ module.exports = { var meta = JSON.stringify({ bitrate: media.meta.bitrate, - codec: media.meta.codec, - gpdirect: media.meta.gpdirect + codec: media.meta.codec }); db.query("INSERT INTO `channel_libraries` " + diff --git a/lib/get-info.js b/lib/get-info.js index e1ef5993..82a6e1e1 100644 --- a/lib/get-info.js +++ b/lib/get-info.js @@ -703,71 +703,52 @@ var Getters = { return callback("HTTP " + status, null); } - var m = res.match(/main\((.*?)\);<\/script>/); - if (m) { - try { - var data = m[1]; - data = data.substring(data.indexOf(",") + 1); - data = data.replace(/'(.*?)'([:\,\}\]])/g, "\"$1\"$2"); - /* Fixes an issue with certain characters represented as \xkk */ - data = data.replace(/\\x(\d*)/g, function (sub, s1) { - return String.fromCharCode(parseInt(s1, 16)); - }); - data = "[" + data + "]"; - var js = JSON.parse(data); + var m = res.match(/(\[\s*\[[^\[].*?\]\s*\])/); + try { + var propertyList = JSON.parse(m[1]); + var data = {}; + propertyList.forEach(function (kv) { + data[kv[0]] = kv[1]; + }); - if (js[1].videoplay.status === "QUOTA_DENIED") { - return callback("Google Docs error: Video has exceeded quota", null); - } + var title = data.title; + var seconds = parseInt(data.length_seconds); - var title = js[0].title; - var seconds = js[1].videodetails.duration / 1000; - var meta = {}; - var fv = js[1].videoplay.flashVars; - var fvstr = ""; - for (var k in fv) { - if (k === "autoplay") - fv[k] = "1"; - fvstr += "&" + k + "=" + encodeURIComponent(fv[k]); - } - fvstr = fvstr.substring(1); + var videos = {}; + data.fmt_stream_map.split(",").forEach(function (stream) { + var parts = stream.split("|"); + videos[parts[0]] = parts[1]; + }); - var url = js[1].videoplay.swfUrl + "&enablejsapi=1"; - meta.object = { - type: "application/x-shockwave-flash", - allowscriptaccess: "always", - allowfullscreen: "true", - wmode: "opaque", - data: url - }; + /* + * Preference map of quality => youtube formats. + * see https://en.wikipedia.org/wiki/Youtube#Quality_and_codecs + */ + const preference = { + "hd1080": [46, 37], + "hd720": [22, 45], + "large": [44, 35], + "medium": [43, 18], + "small": [5] + }; - meta.params = [ - { - name: "allowFullScreen", - value: "true" - }, - { - name: "allowScriptAccess", - value: "always" - }, - { - name: "wmode", - value: "opaque" - }, - { - name: "flashvars", - value: fvstr + var direct = {}; + + for (var key in preference) { + for (var i = 0; i < preference[key].length; i++) { + var format = preference[key][i]; + + if (format in videos) { + direct[key] = videos[format]; + break; } - ]; - - var med = new Media(id, title, seconds, "gd", meta); - - callback(false, med); - } catch (e) { - callback("Parsing of Google Docs output failed", null); + } } - } else { - callback(res, null); + + callback(null, new Media(id, title, seconds, "gd", { gpdirect: direct })); + } catch (e) { + console.error(e); + return callback("Failed to parse Google Docs output", null); } }); }, diff --git a/lib/media.js b/lib/media.js index 4da128ad..442481c1 100644 --- a/lib/media.js +++ b/lib/media.js @@ -32,8 +32,6 @@ Media.prototype = { duration: this.duration, type: this.type, meta: { - object: this.meta.object, - params: this.meta.params, direct: this.meta.direct, gpdirect: this.meta.gpdirect, restricted: this.meta.restricted, diff --git a/www/js/callbacks.js b/www/js/callbacks.js index 2c644a50..98e11467 100644 --- a/www/js/callbacks.js +++ b/www/js/callbacks.js @@ -878,7 +878,10 @@ Callbacks = { data = vimeoSimulator2014(data); } - if (data.type === "gp") { + /* + * Google Docs now uses the same simulator as Google+ + */ + if (data.type === "gp" || data.type === "gd") { data = googlePlusSimulator2014(data); } diff --git a/www/js/util.js b/www/js/util.js index cd352a20..324c0b15 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -2737,11 +2737,6 @@ function googlePlusSimulator2014(data) { /* Google+ Simulator uses the raw file player */ data.type = "fi"; - /* For browsers that don't support native h264 playback */ - if (USEROPTS.no_h264) { - data.forceFlash = true; - } - if (!data.meta.gpdirect) { data.url = ""; return data;