Add workaround for GM sandbox and refactor userscript a bit
This commit is contained in:
parent
8d3b2e59df
commit
578d3fbb23
|
@ -7,16 +7,20 @@
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM_xmlhttpRequest
|
||||||
// @connect docs.google.com
|
// @connect docs.google.com
|
||||||
// @run-at document-end
|
// @run-at document-end
|
||||||
// @version 1.0.0
|
// @version 1.1.0
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
|
||||||
(function () {
|
try {
|
||||||
function debug(message) {
|
function debug(message) {
|
||||||
if (!unsafeWindow.enableCyTubeGoogleDriveUserscriptDebug) {
|
if (!unsafeWindow.enableCyTubeGoogleDriveUserscriptDebug) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafeWindow.console.log.apply(unsafeWindow.console, arguments);
|
try {
|
||||||
|
unsafeWindow.console.log(message);
|
||||||
|
} catch (error) {
|
||||||
|
unsafeWindow.console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ITAG_QMAP = {
|
var ITAG_QMAP = {
|
||||||
|
@ -53,36 +57,42 @@
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: url,
|
url: url,
|
||||||
onload: function (res) {
|
onload: function (res) {
|
||||||
var data = {};
|
try {
|
||||||
var error;
|
debug('Got response ' + res.responseText);
|
||||||
res.responseText.split('&').forEach(function (kv) {
|
var data = {};
|
||||||
var pair = kv.split('=');
|
var error;
|
||||||
data[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
|
res.responseText.split('&').forEach(function (kv) {
|
||||||
});
|
var pair = kv.split('=');
|
||||||
|
data[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
|
||||||
|
});
|
||||||
|
|
||||||
if (data.status === 'fail') {
|
if (data.status === 'fail') {
|
||||||
error = new Error('Google Docs request failed: ' +
|
error = new Error('Google Docs request failed: ' +
|
||||||
'metadata indicated status=fail');
|
'metadata indicated status=fail');
|
||||||
error.response = res.responseText;
|
error.response = res.responseText;
|
||||||
error.reason = 'RESPONSE_STATUS_FAIL';
|
error.reason = 'RESPONSE_STATUS_FAIL';
|
||||||
return cb(error);
|
return cb(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.fmt_stream_map) {
|
||||||
|
error = new Error('Google Docs request failed: ' +
|
||||||
|
'metadata lookup returned no valid links');
|
||||||
|
error.response = res.responseText;
|
||||||
|
error.reason = 'MISSING_LINKS';
|
||||||
|
return cb(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.links = {};
|
||||||
|
data.fmt_stream_map.split(',').forEach(function (item) {
|
||||||
|
var pair = item.split('|');
|
||||||
|
data.links[pair[0]] = pair[1];
|
||||||
|
});
|
||||||
|
data.videoMap = mapLinks(data.links);
|
||||||
|
|
||||||
|
cb(null, data);
|
||||||
|
} catch (error) {
|
||||||
|
unsafeWindow.console.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.fmt_stream_map) {
|
|
||||||
error = new Error('Google Docs request failed: ' +
|
|
||||||
'metadata lookup returned no valid links');
|
|
||||||
error.response = res.responseText;
|
|
||||||
error.reason = 'MISSING_LINKS';
|
|
||||||
return cb(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.links = {};
|
|
||||||
data.fmt_stream_map.split(',').forEach(function (item) {
|
|
||||||
var pair = item.split('|');
|
|
||||||
data.links[pair[0]] = pair[1];
|
|
||||||
});
|
|
||||||
|
|
||||||
cb(null, data);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onerror: function () {
|
onerror: function () {
|
||||||
|
@ -118,36 +128,83 @@
|
||||||
return videos;
|
return videos;
|
||||||
}
|
}
|
||||||
|
|
||||||
function GoogleDrivePlayer(data) {
|
/*
|
||||||
if (!(this instanceof GoogleDrivePlayer)) {
|
* Greasemonkey 2.0 has this wonderful sandbox that attempts
|
||||||
return new GoogleDrivePlayer(data);
|
* to prevent script developers from shooting themselves in
|
||||||
}
|
* the foot by removing the trigger from the gun, i.e. it's
|
||||||
|
* impossible to cross the boundary between the browser JS VM
|
||||||
|
* and the privileged sandbox that can run GM_xmlhttpRequest().
|
||||||
|
*
|
||||||
|
* So in this case, we have to resort to polling a special
|
||||||
|
* variable to see if getGoogleDriveMetadata needs to be called
|
||||||
|
* and deliver the result into another special variable that is
|
||||||
|
* being polled on the browser side.
|
||||||
|
*/
|
||||||
|
|
||||||
this.setMediaProperties(data);
|
/*
|
||||||
this.load(data);
|
* Browser side function -- sets gdUserscript.pollID to the
|
||||||
|
* ID of the Drive video to be queried and polls
|
||||||
|
* gdUserscript.pollResult for the result.
|
||||||
|
*/
|
||||||
|
function getGoogleDriveMetadata_GM(id, callback) {
|
||||||
|
debug('Setting GD poll ID to ' + id);
|
||||||
|
unsafeWindow.gdUserscript.pollID = id;
|
||||||
|
var tries = 0;
|
||||||
|
var i = setInterval(function () {
|
||||||
|
if (unsafeWindow.gdUserscript.pollResult) {
|
||||||
|
debug('Got result');
|
||||||
|
clearInterval(i);
|
||||||
|
var result = unsafeWindow.gdUserscript.pollResult;
|
||||||
|
unsafeWindow.gdUserscript.pollResult = null;
|
||||||
|
callback(result.error, result.result);
|
||||||
|
} else if (++tries > 100) {
|
||||||
|
// Took longer than 10 seconds, give up
|
||||||
|
clearInterval(i);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
GoogleDrivePlayer.prototype = Object.create(unsafeWindow.VideoJSPlayer.prototype);
|
/*
|
||||||
|
* Sandbox side function -- polls gdUserscript.pollID for
|
||||||
GoogleDrivePlayer.prototype.load = function (data) {
|
* the ID of a Drive video to be queried, looks up the
|
||||||
var self = this;
|
* metadata, and stores it in gdUserscript.pollResult
|
||||||
getVideoInfo(data.id, function (err, videoData) {
|
*/
|
||||||
if (err) {
|
function setupGDPoll() {
|
||||||
debug(err);
|
unsafeWindow.gdUserscript = cloneInto({}, unsafeWindow);
|
||||||
var alertBox = unsafeWindow.document.createElement('div');
|
var pollInterval = setInterval(function () {
|
||||||
alertBox.className = 'alert alert-danger';
|
if (unsafeWindow.gdUserscript.pollID) {
|
||||||
alertBox.textContent = err.message;
|
var id = unsafeWindow.gdUserscript.pollID;
|
||||||
document.getElementById('ytapiplayer').appendChild(alertBox);
|
unsafeWindow.gdUserscript.pollID = null;
|
||||||
return;
|
debug('Polled and got ' + id);
|
||||||
|
getVideoInfo(id, function (error, data) {
|
||||||
|
unsafeWindow.gdUserscript.pollResult = cloneInto({
|
||||||
|
error: error,
|
||||||
|
result: data
|
||||||
|
}, unsafeWindow);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
debug('Retrieved links: ' + JSON.stringify(videoData.links));
|
function isRunningTampermonkey() {
|
||||||
data.meta.direct = mapLinks(videoData.links);
|
try {
|
||||||
unsafeWindow.VideoJSPlayer.prototype.loadPlayer.call(self, data);
|
return GM_info.scriptHandler === 'Tampermonkey';
|
||||||
});
|
} catch (error) {
|
||||||
};
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRunningTampermonkey()) {
|
||||||
|
unsafeWindow.getGoogleDriveMetadata = getVideoInfo;
|
||||||
|
} else {
|
||||||
|
debug('Using non-TM polling workaround');
|
||||||
|
unsafeWindow.getGoogleDriveMetadata = exportFunction(
|
||||||
|
getGoogleDriveMetadata_GM, unsafeWindow);
|
||||||
|
setupGDPoll();
|
||||||
|
}
|
||||||
|
|
||||||
unsafeWindow.GoogleDrivePlayer = GoogleDrivePlayer;
|
|
||||||
unsafeWindow.console.log('Initialized userscript Google Drive player');
|
unsafeWindow.console.log('Initialized userscript Google Drive player');
|
||||||
unsafeWindow.hasDriveUserscript = true;
|
unsafeWindow.hasDriveUserscript = true;
|
||||||
})();
|
} catch (error) {
|
||||||
|
unsafeWindow.console.error(error);
|
||||||
|
}
|
||||||
|
|
|
@ -4,3 +4,19 @@ window.GoogleDrivePlayer = class GoogleDrivePlayer extends VideoJSPlayer
|
||||||
return new GoogleDrivePlayer(data)
|
return new GoogleDrivePlayer(data)
|
||||||
|
|
||||||
super(data)
|
super(data)
|
||||||
|
|
||||||
|
load: (data) ->
|
||||||
|
if typeof window.getGoogleDriveMetadata is 'function'
|
||||||
|
window.getGoogleDriveMetadata(data.id, (error, metadata) =>
|
||||||
|
if error
|
||||||
|
console.error(error)
|
||||||
|
alertBox = window.document.createElement('div')
|
||||||
|
alertBox.className = 'alert alert-danger'
|
||||||
|
alertBox.textContent = error.message
|
||||||
|
document.getElementById('ytapiplayer').appendChild(alertBox)
|
||||||
|
else
|
||||||
|
data.meta.direct = metadata.videoMap
|
||||||
|
super(data)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
super(data)
|
||||||
|
|
|
@ -33,7 +33,7 @@ window.loadMediaPlayer = (data) ->
|
||||||
else if data.type is 'gd'
|
else if data.type is 'gd'
|
||||||
try
|
try
|
||||||
if data.meta.html5hack or window.hasDriveUserscript
|
if data.meta.html5hack or window.hasDriveUserscript
|
||||||
window.PLAYER = new window.GoogleDrivePlayer(data)
|
window.PLAYER = new GoogleDrivePlayer(data)
|
||||||
else
|
else
|
||||||
window.PLAYER = new GoogleDriveYouTubePlayer(data)
|
window.PLAYER = new GoogleDriveYouTubePlayer(data)
|
||||||
catch e
|
catch e
|
||||||
|
|
|
@ -43,8 +43,7 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
|
||||||
if not (this instanceof VideoJSPlayer)
|
if not (this instanceof VideoJSPlayer)
|
||||||
return new VideoJSPlayer(data)
|
return new VideoJSPlayer(data)
|
||||||
|
|
||||||
@setMediaProperties(data)
|
@load(data)
|
||||||
@loadPlayer(data)
|
|
||||||
|
|
||||||
loadPlayer: (data) ->
|
loadPlayer: (data) ->
|
||||||
waitUntilDefined(window, 'videojs', =>
|
waitUntilDefined(window, 'videojs', =>
|
||||||
|
|
|
@ -503,8 +503,7 @@
|
||||||
if (!(this instanceof VideoJSPlayer)) {
|
if (!(this instanceof VideoJSPlayer)) {
|
||||||
return new VideoJSPlayer(data);
|
return new VideoJSPlayer(data);
|
||||||
}
|
}
|
||||||
this.setMediaProperties(data);
|
this.load(data);
|
||||||
this.loadPlayer(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoJSPlayer.prototype.loadPlayer = function(data) {
|
VideoJSPlayer.prototype.loadPlayer = function(data) {
|
||||||
|
@ -676,6 +675,28 @@
|
||||||
GoogleDrivePlayer.__super__.constructor.call(this, data);
|
GoogleDrivePlayer.__super__.constructor.call(this, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GoogleDrivePlayer.prototype.load = function(data) {
|
||||||
|
if (typeof window.getGoogleDriveMetadata === 'function') {
|
||||||
|
return window.getGoogleDriveMetadata(data.id, (function(_this) {
|
||||||
|
return function(error, metadata) {
|
||||||
|
var alertBox;
|
||||||
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
alertBox = window.document.createElement('div');
|
||||||
|
alertBox.className = 'alert alert-danger';
|
||||||
|
alertBox.textContent = error.message;
|
||||||
|
return document.getElementById('ytapiplayer').appendChild(alertBox);
|
||||||
|
} else {
|
||||||
|
data.meta.direct = metadata.videoMap;
|
||||||
|
return GoogleDrivePlayer.__super__.load.call(_this, data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
|
} else {
|
||||||
|
return GoogleDrivePlayer.__super__.load.call(this, data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return GoogleDrivePlayer;
|
return GoogleDrivePlayer;
|
||||||
|
|
||||||
})(VideoJSPlayer);
|
})(VideoJSPlayer);
|
||||||
|
@ -1359,7 +1380,7 @@
|
||||||
} else if (data.type === 'gd') {
|
} else if (data.type === 'gd') {
|
||||||
try {
|
try {
|
||||||
if (data.meta.html5hack || window.hasDriveUserscript) {
|
if (data.meta.html5hack || window.hasDriveUserscript) {
|
||||||
return window.PLAYER = new window.GoogleDrivePlayer(data);
|
return window.PLAYER = new GoogleDrivePlayer(data);
|
||||||
} else {
|
} else {
|
||||||
return window.PLAYER = new GoogleDriveYouTubePlayer(data);
|
return window.PLAYER = new GoogleDriveYouTubePlayer(data);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue