mirror of https://github.com/calzoneman/sync.git
Merge pull request #385 from calzoneman/playback-improvements
Improvements to code handling Vimeo hack, Google Docs
This commit is contained in:
commit
0e8e0aef54
|
@ -114,6 +114,7 @@ Channel.prototype.initModules = function () {
|
||||||
"./opts" : "options",
|
"./opts" : "options",
|
||||||
"./library" : "library",
|
"./library" : "library",
|
||||||
"./playlist" : "playlist",
|
"./playlist" : "playlist",
|
||||||
|
"./mediarefresher": "mediarefresher",
|
||||||
"./voteskip" : "voteskip",
|
"./voteskip" : "voteskip",
|
||||||
"./poll" : "poll",
|
"./poll" : "poll",
|
||||||
"./kickban" : "kickban",
|
"./kickban" : "kickban",
|
||||||
|
@ -281,9 +282,11 @@ Channel.prototype.notifyModules = function (fn, args) {
|
||||||
Channel.prototype.joinUser = function (user, data) {
|
Channel.prototype.joinUser = function (user, data) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
self.activeLock.lock();
|
||||||
self.waitFlag(Flags.C_READY, function () {
|
self.waitFlag(Flags.C_READY, function () {
|
||||||
/* User closed the connection before the channel finished loading */
|
/* User closed the connection before the channel finished loading */
|
||||||
if (user.socket.disconnected) {
|
if (user.socket.disconnected) {
|
||||||
|
self.activeLock.release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +295,7 @@ Channel.prototype.joinUser = function (user, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Logger.errlog.log("user.refreshAccount failed at Channel.joinUser");
|
Logger.errlog.log("user.refreshAccount failed at Channel.joinUser");
|
||||||
Logger.errlog.log(err.stack);
|
Logger.errlog.log(err.stack);
|
||||||
|
self.activeLock.release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,6 +307,7 @@ Channel.prototype.joinUser = function (user, data) {
|
||||||
|
|
||||||
function afterAccount() {
|
function afterAccount() {
|
||||||
if (self.dead || user.socket.disconnected) {
|
if (self.dead || user.socket.disconnected) {
|
||||||
|
if (self.activeLock) self.activeLock.release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,11 +316,11 @@ Channel.prototype.joinUser = function (user, data) {
|
||||||
if (user.account.channelRank !== user.account.globalRank) {
|
if (user.account.channelRank !== user.account.globalRank) {
|
||||||
user.socket.emit("rank", user.account.effectiveRank);
|
user.socket.emit("rank", user.account.effectiveRank);
|
||||||
}
|
}
|
||||||
self.activeLock.lock();
|
|
||||||
self.acceptUser(user);
|
self.acceptUser(user);
|
||||||
} else {
|
} else {
|
||||||
user.account.channelRank = 0;
|
user.account.channelRank = 0;
|
||||||
user.account.effectiveRank = user.account.globalRank;
|
user.account.effectiveRank = user.account.globalRank;
|
||||||
|
self.activeLock.release();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ ChatModule.prototype.handleChatMsg = function (user, data) {
|
||||||
}
|
}
|
||||||
data.meta = meta;
|
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) {
|
if (result === ChannelModule.PASSTHROUGH) {
|
||||||
self.processChatMsg(user, data);
|
self.processChatMsg(user, data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ DrinkModule.prototype.onUserPostJoin = function (user) {
|
||||||
user.socket.emit("drinkCount", this.drinks);
|
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 msg = data.msg;
|
||||||
var perms = this.channel.modules.permissions;
|
var perms = this.channel.modules.permissions;
|
||||||
if (msg.match(/^\/d-?[0-9]*/) && perms.canCallDrink(user)) {
|
if (msg.match(/^\/d-?[0-9]*/) && perms.canCallDrink(user)) {
|
||||||
|
|
|
@ -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;
|
|
@ -54,7 +54,14 @@ ChannelModule.prototype = {
|
||||||
/**
|
/**
|
||||||
* Called when a chatMsg event is received
|
* 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);
|
cb(null, ChannelModule.PASSTHROUGH);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -977,12 +977,19 @@ PlaylistModule.prototype.startPlayback = function (time) {
|
||||||
var media = self.current.media;
|
var media = self.current.media;
|
||||||
media.reset();
|
media.reset();
|
||||||
|
|
||||||
var continuePlayback = function () {
|
|
||||||
if (self.leader != null) {
|
if (self.leader != null) {
|
||||||
media.paused = false;
|
media.paused = false;
|
||||||
media.currentTime = time || 0;
|
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);
|
self.sendChangeMedia(self.channel.users);
|
||||||
self.channel.notifyModules("onMediaChange", self.current.media);
|
}
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -997,8 +1004,14 @@ PlaylistModule.prototype.startPlayback = function (time) {
|
||||||
self._leadInterval = false;
|
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);
|
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 */
|
/* Only start the timer if the media item is not live, i.e. has a duration */
|
||||||
if (media.seconds > 0) {
|
if (media.seconds > 0) {
|
||||||
|
@ -1007,43 +1020,8 @@ PlaylistModule.prototype.startPlayback = function (time) {
|
||||||
self._leadLoop();
|
self._leadLoop();
|
||||||
}, 1000);
|
}, 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 (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();
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const UPDATE_INTERVAL = Config.get("playlist.update-interval");
|
const UPDATE_INTERVAL = Config.get("playlist.update-interval");
|
||||||
|
|
|
@ -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.
|
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 singleton = null;
|
||||||
var Config = require("./config");
|
var Config = require("./config");
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"author": "Calvin Montgomery",
|
"author": "Calvin Montgomery",
|
||||||
"name": "CyTube",
|
"name": "CyTube",
|
||||||
"description": "Online media synchronizer and chat",
|
"description": "Online media synchronizer and chat",
|
||||||
"version": "3.3.0",
|
"version": "3.3.1",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "http://github.com/calzoneman/sync"
|
"url": "http://github.com/calzoneman/sync"
|
||||||
},
|
},
|
||||||
|
|
|
@ -874,53 +874,11 @@ Callbacks = {
|
||||||
data.url = data.id;
|
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) {
|
if (NO_VIMEO && data.type === "vi" && data.meta.direct) {
|
||||||
data.type = "fi";
|
data = vimeoSimulator2014(data);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* RTMP player has been replaced with the general flash player */
|
||||||
if (data.type === "rt") {
|
if (data.type === "rt") {
|
||||||
data.url = data.id;
|
data.url = data.id;
|
||||||
data.type = "fi";
|
data.type = "fi";
|
||||||
|
|
|
@ -1002,7 +1002,15 @@ var GoogleDocsPlayer = function (data) {
|
||||||
self.videoLength = data.seconds;
|
self.videoLength = data.seconds;
|
||||||
self.paused = false;
|
self.paused = false;
|
||||||
var wmode = USEROPTS.wmode_transparent ? "transparent" : "opaque";
|
var wmode = USEROPTS.wmode_transparent ? "transparent" : "opaque";
|
||||||
|
|
||||||
var meta = data.meta;
|
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 = $("<object/>", meta.object)[0];
|
||||||
$(self.player).attr("data", meta.object.data);
|
$(self.player).attr("data", meta.object.data);
|
||||||
$(self.player).attr("width", VWIDTH)
|
$(self.player).attr("width", VWIDTH)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue