Merge pull request #385 from calzoneman/playback-improvements

Improvements to code handling Vimeo hack, Google Docs
This commit is contained in:
Calvin Montgomery 2014-07-10 19:55:31 -07:00
commit 0e8e0aef54
11 changed files with 230 additions and 118 deletions

View File

@ -114,6 +114,7 @@ Channel.prototype.initModules = function () {
"./opts" : "options",
"./library" : "library",
"./playlist" : "playlist",
"./mediarefresher": "mediarefresher",
"./voteskip" : "voteskip",
"./poll" : "poll",
"./kickban" : "kickban",
@ -281,9 +282,11 @@ Channel.prototype.notifyModules = function (fn, args) {
Channel.prototype.joinUser = function (user, data) {
var self = this;
self.activeLock.lock();
self.waitFlag(Flags.C_READY, function () {
/* User closed the connection before the channel finished loading */
if (user.socket.disconnected) {
self.activeLock.release();
return;
}
@ -292,6 +295,7 @@ Channel.prototype.joinUser = function (user, data) {
if (err) {
Logger.errlog.log("user.refreshAccount failed at Channel.joinUser");
Logger.errlog.log(err.stack);
self.activeLock.release();
return;
}
@ -303,6 +307,7 @@ Channel.prototype.joinUser = function (user, data) {
function afterAccount() {
if (self.dead || user.socket.disconnected) {
if (self.activeLock) self.activeLock.release();
return;
}
@ -311,11 +316,11 @@ Channel.prototype.joinUser = function (user, data) {
if (user.account.channelRank !== user.account.globalRank) {
user.socket.emit("rank", user.account.effectiveRank);
}
self.activeLock.lock();
self.acceptUser(user);
} else {
user.account.channelRank = 0;
user.account.effectiveRank = user.account.globalRank;
self.activeLock.release();
}
});
}

View File

@ -131,7 +131,7 @@ ChatModule.prototype.handleChatMsg = function (user, data) {
}
data.meta = meta;
this.channel.checkModules("onUserChat", [user, data], function (err, result) {
this.channel.checkModules("onUserPreChat", [user, data], function (err, result) {
if (result === ChannelModule.PASSTHROUGH) {
self.processChatMsg(user, data);
}

View File

@ -11,7 +11,7 @@ DrinkModule.prototype.onUserPostJoin = function (user) {
user.socket.emit("drinkCount", this.drinks);
};
DrinkModule.prototype.onUserChat = function (user, data, cb) {
DrinkModule.prototype.onUserPreChat = function (user, data, cb) {
var msg = data.msg;
var perms = this.channel.modules.permissions;
if (msg.match(/^\/d-?[0-9]*/) && perms.canCallDrink(user)) {

View File

@ -0,0 +1,106 @@
var ChannelModule = require("./module");
var Config = require("../config");
var InfoGetter = require("../get-info");
var Logger = require("../logger");
function MediaRefresherModule(channel) {
ChannelModule.apply(this, arguments);
this._interval = false;
this._media = null;
}
MediaRefresherModule.prototype = Object.create(ChannelModule.prototype);
MediaRefresherModule.prototype.onPreMediaChange = function (data, cb) {
if (this._interval) clearInterval(this._interval);
this._media = data;
switch (data.type) {
case "gd":
return this.initGoogleDocs(data, function () {
cb(null, ChannelModule.PASSTHROUGH);
});
case "vi":
return this.initVimeo(data, function () {
cb(null, ChannelModule.PASSTHROUGH);
});
default:
return cb(null, ChannelModule.PASSTHROUGH);
}
};
MediaRefresherModule.prototype.initGoogleDocs = function (data, cb) {
var self = this;
self.refreshGoogleDocs(data, cb);
/*
* Refresh every 55 minutes.
* The expiration is 1 hour, but refresh 5 minutes early to be safe
*/
self._interval = setInterval(function () {
self.refreshGoogleDocs(data);
}, 55 * 60 * 1000);
};
MediaRefresherModule.prototype.initVimeo = function (data, cb) {
if (!Config.get("vimeo-workaround")) {
if (cb) cb();
return;
}
var self = this;
self.channel.activeLock.lock();
InfoGetter.vimeoWorkaround(data.id, function (hack) {
if (self._media === data) {
self.channel.logger.log("[mediarefresher] Refreshed vimeo video with ID " +
data.id);
data.meta.direct = hack;
}
self.channel.activeLock.release();
if (cb) cb();
});
};
MediaRefresherModule.prototype.refreshGoogleDocs = function (media, cb) {
var self = this;
if (self.dead || self.channel.dead) {
return;
}
self.channel.activeLock.lock();
InfoGetter.getMedia(media.id, "gd", function (err, data) {
switch (err) {
case "HTTP 302":
case "Video not found":
case "Private video":
self.channel.activeLock.release();
if (cb) cb();
return;
default:
if (err) {
Logger.errlog.log("Google Docs 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 Docs video with ID " +
media.id);
media.meta = data.meta;
self.channel.activeLock.release();
if (cb) cb();
});
};
module.exports = MediaRefresherModule;

View File

@ -54,7 +54,14 @@ ChannelModule.prototype = {
/**
* Called when a chatMsg event is received
*/
onUserChat: function (user, data, cb) {
onUserPreChat: function (user, data, cb) {
cb(null, ChannelModule.PASSTHROUGH);
},
/**
* Called before a new video begins playing
*/
onPreMediaChange: function (data, cb) {
cb(null, ChannelModule.PASSTHROUGH);
},

View File

@ -977,73 +977,51 @@ PlaylistModule.prototype.startPlayback = function (time) {
var media = self.current.media;
media.reset();
var continuePlayback = function () {
if (self.leader != null) {
media.paused = false;
media.currentTime = time || 0;
self.sendChangeMedia(self.channel.users);
self.channel.notifyModules("onMediaChange", self.current.media);
return;
}
/* Lead-in time of 3 seconds to allow clients to buffer */
time = time || -3;
media.paused = time < 0;
media.currentTime = time;
/* Module was already leading, stop the previous timer */
if (self._leadInterval) {
clearInterval(self._leadInterval);
self._leadInterval = false;
}
self.sendChangeMedia(self.channel.users);
self.channel.notifyModules("onMediaChange", self.current.media);
/* Only start the timer if the media item is not live, i.e. has a duration */
if (media.seconds > 0) {
self._lastUpdate = Date.now();
self._leadInterval = setInterval(function() {
self._leadLoop();
}, 1000);
}
/* Google Docs autorefresh */
if (self._gdRefreshTimer) {
clearInterval(self._gdRefreshTimer);
self._gdRefreshTimer = false;
}
if (media.type === "gd") {
self._gdRefreshTimer = setInterval(self.refreshGoogleDocs.bind(self), 3600000);
if (media.meta.expiration && media.meta.expiration < Date.now() + 3600000) {
setTimeout(self.refreshGoogleDocs.bind(self), media.meta.expiration - Date.now());
if (self.leader != null) {
media.paused = false;
media.currentTime = time || 0;
self.channel.checkModules("onPreMediaChange", [self.current.media],
function () {
/*
* onPreMediaChange doesn't care about the callback result.
* Its purpose is to allow modification of playback data before
* users are sent a changeMedia
*/
self.sendChangeMedia(self.channel.users);
}
}
};
if (media.type === "vi" && !media.meta.direct && Config.get("vimeo-workaround")) {
self.channel.activeLock.lock();
vimeoWorkaround(media.id, function (direct) {
self.channel.activeLock.release();
if (self.current && self.current.media === media) {
self.current.media.meta.direct = direct;
continuePlayback();
}
});
);
return;
} else if (media.type === "gd" && isExpired(media) && !media.meta.failed) {
self.channel.activeLock.lock();
self.refreshGoogleDocs(function () {
self.channel.activeLock.release();
if (self.current && self.current.media === media) {
continuePlayback();
}
});
return;
} else {
continuePlayback();
}
/* Lead-in time of 3 seconds to allow clients to buffer */
time = time || -3;
media.paused = time < 0;
media.currentTime = time;
/* Module was already leading, stop the previous timer */
if (self._leadInterval) {
clearInterval(self._leadInterval);
self._leadInterval = false;
}
self.channel.checkModules("onPreMediaChange", [self.current.media],
function () {
/*
* onPreMediaChange currently doesn't care about the callback result.
* Its purpose is to allow modification of playback data before
* users are sent a changeMedia
*/
self.sendChangeMedia(self.channel.users);
/* Only start the timer if the media item is not live, i.e. has a duration */
if (media.seconds > 0) {
self._lastUpdate = Date.now();
self._leadInterval = setInterval(function() {
self._leadLoop();
}, 1000);
}
}
);
}
const UPDATE_INTERVAL = Config.get("playlist.update-interval");

View File

@ -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.3.0";
const VERSION = "3.3.1";
var singleton = null;
var Config = require("./config");

View File

@ -2,7 +2,7 @@
"author": "Calvin Montgomery",
"name": "CyTube",
"description": "Online media synchronizer and chat",
"version": "3.3.0",
"version": "3.3.1",
"repository": {
"url": "http://github.com/calzoneman/sync"
},

View File

@ -850,16 +850,16 @@ Callbacks = {
});
}
if(CHANNEL.opts.allow_voteskip)
if (CHANNEL.opts.allow_voteskip)
$("#voteskip").attr("disabled", false);
$("#currenttitle").text("Currently Playing: " + data.title);
if(data.type != "sc" && PLAYER.type == "sc")
if (data.type != "sc" && PLAYER.type == "sc")
// [](/goddamnitmango)
fixSoundcloudShit();
if(data.type != "jw" && PLAYER.type == "jw") {
if (data.type != "jw" && PLAYER.type == "jw") {
// Is it so hard to not mess up my DOM?
$("<div/>").attr("id", "ytapiplayer")
.insertBefore($("#ytapiplayer_wrapper"));
@ -874,60 +874,18 @@ Callbacks = {
data.url = data.id;
}
/*
VIMEO SIMULATOR 2014
Vimeo decided to block my domain. After repeated emails, they refused to
unblock it. Rather than give in to their demands, there is a serverside
option which extracts direct links to the h264 encoded MP4 video files.
These files can be loaded in a custom player to allow Vimeo playback without
triggering their dumb API domain block.
It's a little bit hacky, but my only other option is to keep buying new
domains every time one gets blocked. No thanks to Vimeo, who were of no help
and unwilling to compromise on the issue.
*/
if (NO_VIMEO && data.type === "vi" && data.meta.direct) {
data.type = "fi";
// For browsers that don't support native h264 playback
if (USEROPTS.no_h264) {
data.forceFlash = true;
}
/* Convert youtube-style quality key to vimeo workaround quality */
var q = {
small: "mobile",
medium: "sd",
large: "sd",
hd720: "hd",
hd1080:"hd",
highres: "hd"
}[USEROPTS.default_quality] || "sd";
var fallback = {
hd: "sd",
sd: "mobile",
mobile: false
};
while (!(q in data.meta.direct) && q != false) {
q = fallback[q];
}
if (!q) {
q = "sd";
}
data.url = data.meta.direct[q].url;
data = vimeoSimulator2014(data);
}
/* RTMP player has been replaced with the general flash player */
if (data.type === "rt") {
data.url = data.id;
data.type = "fi";
data.forceFlash = true;
}
if(data.type != PLAYER.type) {
if (data.type != PLAYER.type) {
loadMediaPlayer(data);
}

View File

@ -1002,7 +1002,15 @@ var GoogleDocsPlayer = function (data) {
self.videoLength = data.seconds;
self.paused = false;
var wmode = USEROPTS.wmode_transparent ? "transparent" : "opaque";
var meta = data.meta;
if (!meta || !meta.object || !meta.params) {
// Reset videoId so that a changeMedia with the appropriate data
// will properly reset the player
self.videoId = "";
return;
}
self.player = $("<object/>", meta.object)[0];
$(self.player).attr("data", meta.object.data);
$(self.player).attr("width", VWIDTH)

View File

@ -2674,3 +2674,53 @@ function formatScriptAccessPrefs() {
});
});
}
/*
VIMEO SIMULATOR 2014
Vimeo decided to block my domain. After repeated emails, they refused to
unblock it. Rather than give in to their demands, there is a serverside
option which extracts direct links to the h264 encoded MP4 video files.
These files can be loaded in a custom player to allow Vimeo playback without
triggering their dumb API domain block.
It's a little bit hacky, but my only other option is to keep buying new
domains every time one gets blocked. No thanks to Vimeo, who were of no help
and unwilling to compromise on the issue.
*/
function vimeoSimulator2014(data) {
/* Vimeo 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;
}
/* Convert youtube-style quality key to vimeo workaround quality */
var q = {
small: "mobile",
medium: "sd",
large: "sd",
hd720: "hd",
hd1080:"hd",
highres: "hd"
}[USEROPTS.default_quality] || "sd";
var fallback = {
hd: "sd",
sd: "mobile",
mobile: false
};
/* Pick highest quality less than or equal to user's preference from the options */
while (!(q in data.meta.direct) && q != false) {
q = fallback[q];
}
if (!q) {
q = "sd";
}
data.url = data.meta.direct[q].url;
return data;
}