mirror of https://github.com/calzoneman/sync.git
Merge pull request #323 from calzoneman/vimeoisadouchecopter
Workaround for Vimeo refusing to unblock my domain
This commit is contained in:
commit
97fd06f7a6
|
@ -45,6 +45,7 @@ var defaults = {
|
|||
"ytv3apikey" : "",
|
||||
"enable-ytv3" : false,
|
||||
"ytv2devkey" : "",
|
||||
"vimeo-workaround" : false,
|
||||
"stat-interval" : 3600000,
|
||||
"stat-max-age" : 86400000,
|
||||
"alias-purge-interval" : 3600000,
|
||||
|
|
|
@ -357,7 +357,7 @@ var Getters = {
|
|||
callback("API failure", null);
|
||||
return;
|
||||
} else if(status !== 200) {
|
||||
callback("YTv2 HTTP " + status, null);
|
||||
callback("Vimeo HTTP " + status, null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -702,6 +702,89 @@ var Getters = {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A function name like this deserves an explanation.
|
||||
*
|
||||
* Vimeo blocked my domain from using their embed API, citing "volume of abuse" as the
|
||||
* reason. After attempts to reach a compromise, Vimeo refused to unblock my domain.
|
||||
*
|
||||
* This function downloads the HTML for the embedded player and extracts direct links
|
||||
* to the h264 encoded MP4 video files (with a session hash required for downloading it).
|
||||
*
|
||||
* This rivals the Google Drive playback functionality as quite possibly the hackiest code
|
||||
* in CyTube. Thanks Vimeo, and may you never see a dime in advertising revenue from a
|
||||
* CyTube client again.
|
||||
*/
|
||||
function VimeoIsADoucheCopter(id, cb) {
|
||||
var failcount = 0;
|
||||
|
||||
var inner = function () {
|
||||
var options = {
|
||||
host: "player.vimeo.com",
|
||||
path: "/video/" + id,
|
||||
headers: {
|
||||
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:29.0) Gecko/20100101 Firefox/29.0",
|
||||
"Referrer": "player.vimeo.com"
|
||||
}
|
||||
};
|
||||
|
||||
var parse = function (data) {
|
||||
var i = data.indexOf("a={");
|
||||
var j = data.indexOf("};", i);
|
||||
var json = data.substring(i+2, j+1);
|
||||
try {
|
||||
json = JSON.parse(json);
|
||||
var codec = json.request.files.codecs[0];
|
||||
var files = json.request.files[codec];
|
||||
setImmediate(function () {
|
||||
cb(files);
|
||||
});
|
||||
} catch (e) {
|
||||
// Not sure why the response sometimes differs. Trying to weed out
|
||||
// search engines perhaps?
|
||||
// Maybe adding the User-Agent above will fix this, I dunno
|
||||
if (data.indexOf("crawler") !== -1) {
|
||||
Logger.syslog.log("Warning: VimeoIsADoucheCopter got crawler response");
|
||||
failcount++;
|
||||
if (failcount > 4) {
|
||||
Logger.errlog.log("VimeoIsADoucheCopter got bad response 5 times!"+
|
||||
" Giving up.");
|
||||
setImmediate(function () {
|
||||
cb({});
|
||||
});
|
||||
} else {
|
||||
setImmediate(function () {
|
||||
inner();
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
Logger.errlog.log("Vimeo workaround error: ");
|
||||
Logger.errlog.log(e);
|
||||
Logger.errlog.log(data);
|
||||
setImmediate(function () {
|
||||
cb({});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
http.get(options, function (res) {
|
||||
res.setEncoding("utf-8");
|
||||
var buffer = "";
|
||||
|
||||
res.on("data", function (data) {
|
||||
buffer += data;
|
||||
});
|
||||
|
||||
res.on("end", function () {
|
||||
parse(buffer);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
inner();
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Getters: Getters,
|
||||
getMedia: function (id, type, callback) {
|
||||
|
@ -710,5 +793,6 @@ module.exports = {
|
|||
} else {
|
||||
callback("Unknown media type '" + type + "'", null);
|
||||
}
|
||||
}
|
||||
},
|
||||
VimeoIsADoucheCopter: VimeoIsADoucheCopter
|
||||
};
|
||||
|
|
|
@ -68,6 +68,9 @@ Media.prototype.fullupdate = function() {
|
|||
if (this.params) {
|
||||
x.params = this.params;
|
||||
}
|
||||
if (this.direct) {
|
||||
x.direct = this.direct;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
|
@ -79,4 +82,9 @@ Media.prototype.timeupdate = function() {
|
|||
};
|
||||
}
|
||||
|
||||
Media.prototype.reset = function () {
|
||||
delete this.currentTime;
|
||||
delete this.direct;
|
||||
};
|
||||
|
||||
exports.Media = Media;
|
||||
|
|
|
@ -13,6 +13,8 @@ ULList = require("./ullist").ULList;
|
|||
var AsyncQueue = require("./asyncqueue");
|
||||
var Media = require("./media").Media;
|
||||
var AllPlaylists = {};
|
||||
var Server = require("./server");
|
||||
var VimeoIsADoucheCopter = require("./get-info").VimeoIsADoucheCopter;
|
||||
|
||||
function PlaylistItem(media, uid) {
|
||||
this.media = media;
|
||||
|
@ -212,6 +214,7 @@ Playlist.prototype.addMedia = function (data) {
|
|||
var m = new Media(data.id, data.title, data.seconds, data.type);
|
||||
m.object = data.object;
|
||||
m.params = data.params;
|
||||
m.direct = data.direct;
|
||||
var item = this.makeItem(m);
|
||||
item.queueby = data.queueby;
|
||||
item.temp = data.temp;
|
||||
|
@ -257,6 +260,7 @@ Playlist.prototype.next = function() {
|
|||
return;
|
||||
|
||||
var it = this.current;
|
||||
it.media.reset();
|
||||
|
||||
if (it.temp) {
|
||||
if (this.remove(it.uid)) {
|
||||
|
@ -290,6 +294,7 @@ Playlist.prototype.jump = function(uid) {
|
|||
return false;
|
||||
|
||||
var it = this.current;
|
||||
it.media.reset();
|
||||
|
||||
this.current = jmp;
|
||||
|
||||
|
@ -338,29 +343,41 @@ Playlist.prototype.lead = function(lead) {
|
|||
}
|
||||
|
||||
Playlist.prototype.startPlayback = function (time) {
|
||||
if(!this.current || !this.current.media)
|
||||
var self = this;
|
||||
if (!self.current || !self.current.media) {
|
||||
return false;
|
||||
if (!this.leading) {
|
||||
this.current.media.paused = false;
|
||||
this.current.media.currentTime = time || 0;
|
||||
this.on("changeMedia")(this.current.media);
|
||||
}
|
||||
|
||||
if (self.current.media.type === "vi" &&
|
||||
!self.current.media.direct &&
|
||||
Server.getServer().cfg["vimeo-workaround"]) {
|
||||
VimeoIsADoucheCopter(self.current.media.id, function (direct) {
|
||||
self.current.media.direct = direct;
|
||||
self.startPlayback(time);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.leading) {
|
||||
self.current.media.paused = false;
|
||||
self.current.media.currentTime = time || 0;
|
||||
self.on("changeMedia")(self.current.media);
|
||||
return;
|
||||
}
|
||||
|
||||
time = time || -3;
|
||||
this.current.media.paused = time < 0;
|
||||
this.current.media.currentTime = time;
|
||||
self.current.media.paused = time < 0;
|
||||
self.current.media.currentTime = time;
|
||||
|
||||
var pl = this;
|
||||
if(this._leadInterval) {
|
||||
clearInterval(this._leadInterval);
|
||||
this._leadInterval = false;
|
||||
if(self._leadInterval) {
|
||||
clearInterval(self._leadInterval);
|
||||
self._leadInterval = false;
|
||||
}
|
||||
this.on("changeMedia")(this.current.media);
|
||||
if(!isLive(this.current.media.type)) {
|
||||
this._lastUpdate = Date.now();
|
||||
this._leadInterval = setInterval(function() {
|
||||
pl._leadLoop();
|
||||
self.on("changeMedia")(self.current.media);
|
||||
if(!isLive(self.current.media.type)) {
|
||||
self._lastUpdate = Date.now();
|
||||
self._leadInterval = setInterval(function() {
|
||||
self._leadLoop();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1011,8 +1011,10 @@ Callbacks = {
|
|||
changeMedia: function(data) {
|
||||
if (PLAYER) {
|
||||
PLAYER.getVolume(function (v) {
|
||||
VOLUME = v;
|
||||
setOpt("volume", VOLUME);
|
||||
if (typeof v === "number") {
|
||||
VOLUME = v;
|
||||
setOpt("volume", VOLUME);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1032,7 +1034,32 @@ Callbacks = {
|
|||
$("#ytapiplayer_wrapper").remove();
|
||||
}
|
||||
|
||||
if(data.type != PLAYER.type) {
|
||||
/*
|
||||
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.direct && data.direct.sd) {
|
||||
// For browsers that don't support native h264 playback
|
||||
if (USEROPTS.no_h264) {
|
||||
data.type = "jw";
|
||||
} else {
|
||||
data.type = "rv";
|
||||
}
|
||||
// Right now only plays standard definition.
|
||||
// In the future, I may add a quality selector for mobile/standard/HD
|
||||
data.url = data.direct.sd.url;
|
||||
}
|
||||
|
||||
if (data.type != PLAYER.type) {
|
||||
loadMediaPlayer(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -97,6 +97,15 @@ function getOrDefault(k, def) {
|
|||
return v;
|
||||
}
|
||||
|
||||
function default_noh264() {
|
||||
var ua = navigator.userAgent + "";
|
||||
if (ua.match(/Firefox\/29.0|Chrome|Chromium/)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var USEROPTS = {
|
||||
theme : getOrDefault("theme", "default"),
|
||||
css : getOrDefault("css", ""),
|
||||
|
@ -120,12 +129,15 @@ var USEROPTS = {
|
|||
sort_afk : getOrDefault("sort_afk", false),
|
||||
default_quality : getOrDefault("default_quality", "#quality_auto"),
|
||||
boop : getOrDefault("boop", false),
|
||||
secure_connection : getOrDefault("secure_connection", false)
|
||||
secure_connection : getOrDefault("secure_connection", false),
|
||||
no_h264 : getOrDefault("no_h264", default_noh264())
|
||||
};
|
||||
|
||||
var VOLUME = getOrDefault("volume", 1);
|
||||
var VOLUME = parseFloat(getOrDefault("volume", 1));
|
||||
|
||||
var NO_WEBSOCKETS = USEROPTS.altsocket;
|
||||
// Change this to include your own domain if you enable Vimeo fallback
|
||||
var NO_VIMEO = Boolean(location.host.match("cytu.be"));
|
||||
|
||||
var Rank = {
|
||||
Guest: 0,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*}
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2013 Calvin Montgomery
|
||||
|
||||
|
@ -731,12 +731,17 @@ var RTMPPlayer = function (data) {
|
|||
var JWPlayer = function (data) {
|
||||
var self = this;
|
||||
self.videoId = data.id;
|
||||
if (data.url) {
|
||||
self.videoURL = data.url;
|
||||
} else {
|
||||
self.videoURL = data.id;
|
||||
}
|
||||
self.videoLength = data.seconds;
|
||||
self.init = function () {
|
||||
removeOld();
|
||||
|
||||
jwplayer("ytapiplayer").setup({
|
||||
file: self.videoId,
|
||||
file: self.videoURL,
|
||||
width: VWIDTH,
|
||||
height: VHEIGHT,
|
||||
autostart: true
|
||||
|
@ -990,6 +995,77 @@ var GoogleDocsPlayer = function (data) {
|
|||
self.init(data);
|
||||
};
|
||||
|
||||
function RawVideoPlayer(data) {
|
||||
var self = this;
|
||||
self.init = function (data) {
|
||||
self.videoId = data.id;
|
||||
self.videoURL = data.url;
|
||||
var video = $("<video/>")
|
||||
.attr("src", self.videoURL)
|
||||
.attr("controls", "controls")
|
||||
.attr("id", "#ytapiplayer")
|
||||
.attr("width", VWIDTH)
|
||||
.attr("height", VHEIGHT)
|
||||
.html("Your browser does not support HTML5 <code><video></code> tags :(");
|
||||
removeOld(video);
|
||||
self.player = video[0];
|
||||
self.setVolume(VOLUME);
|
||||
};
|
||||
|
||||
self.load = function (data) {
|
||||
self.init(data);
|
||||
};
|
||||
|
||||
self.pause = function () {
|
||||
if (self.player) {
|
||||
self.player.pause();
|
||||
}
|
||||
};
|
||||
|
||||
self.play = function () {
|
||||
if (self.player) {
|
||||
self.player.play();
|
||||
}
|
||||
};
|
||||
|
||||
self.isPaused = function (callback) {
|
||||
if (self.player) {
|
||||
callback(self.player.paused);
|
||||
}
|
||||
};
|
||||
|
||||
self.getTime = function (callback) {
|
||||
if (self.player) {
|
||||
callback(self.player.currentTime);
|
||||
}
|
||||
};
|
||||
|
||||
self.seek = function (time) {
|
||||
if (self.player) {
|
||||
self.player.currentTime = time;
|
||||
}
|
||||
};
|
||||
|
||||
self.getVolume = function (cb) {
|
||||
if (self.player) {
|
||||
if (self.player.muted) {
|
||||
cb(0);
|
||||
} else {
|
||||
cb(self.player.volume);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.setVolume = function (vol) {
|
||||
if (self.player) {
|
||||
self.player.volume = vol;
|
||||
}
|
||||
};
|
||||
|
||||
self.init(data);
|
||||
};
|
||||
|
||||
|
||||
function handleMediaUpdate(data) {
|
||||
// Don't update if the position is past the video length, but
|
||||
// make an exception when the video length is 0 seconds
|
||||
|
@ -1086,7 +1162,8 @@ var constructors = {
|
|||
"jw": JWPlayer,
|
||||
"im": ImgurPlayer,
|
||||
"cu": CustomPlayer,
|
||||
"gd": GoogleDocsPlayer
|
||||
"gd": GoogleDocsPlayer,
|
||||
"rv": RawVideoPlayer
|
||||
};
|
||||
|
||||
function loadMediaPlayer(data) {
|
||||
|
|
|
@ -662,6 +662,9 @@ function showOptionsMenu() {
|
|||
"issues on some systems");
|
||||
addOption(playback, "", pl_wmodewarn);
|
||||
|
||||
var pl_noh264 = addCheckbox(playback, "h.264", "Use JWPlayer for h.264 playback");
|
||||
pl_noh264.prop("checked", USEROPTS.no_h264);
|
||||
|
||||
var pl_hide = addCheckbox(playback, "Hide Video",
|
||||
"Remove the video player");
|
||||
pl_hide.prop("checked", USEROPTS.hidevid);
|
||||
|
@ -731,6 +734,7 @@ function showOptionsMenu() {
|
|||
USEROPTS.hidevid = pl_hide.prop("checked");
|
||||
USEROPTS.qbtn_hide = pl_hidebtn.prop("checked");
|
||||
USEROPTS.qbtn_idontlikechange = pl_oldbtn.prop("checked");
|
||||
USEROPTS.no_h264 = pl_noh264.prop("checked");
|
||||
USEROPTS.show_timestamps = chat_time.prop("checked");
|
||||
USEROPTS.sort_rank = chat_sort_rank.prop("checked");
|
||||
USEROPTS.sort_afk = chat_sort_afk.prop("checked");
|
||||
|
|
Loading…
Reference in New Issue