mirror of https://github.com/calzoneman/sync.git
Merge remote-tracking branch 'origin' into ip-session-age
This commit is contained in:
commit
af6e958c49
13
NEWS.md
13
NEWS.md
|
@ -1,3 +1,16 @@
|
||||||
|
2016-08-23
|
||||||
|
==========
|
||||||
|
|
||||||
|
A few weeks ago, the previous Google Drive player stopped working. This is
|
||||||
|
nothing new; Google Drive has consistently broken a few times a year ever since
|
||||||
|
support for it was added. However, it's becoming increasingly difficult and
|
||||||
|
complicated to provide good support for Google Drive, so I've made the decision
|
||||||
|
to phase out the native player and require a userscript for it, in order to
|
||||||
|
bypass CORS and allow each browser to request the video stream itself.
|
||||||
|
|
||||||
|
See [the updated documentation](docs/gdrive-userscript-serveradmins.md) for
|
||||||
|
details on how to enable this for your users.
|
||||||
|
|
||||||
2016-04-27
|
2016-04-27
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ var order = [
|
||||||
'youtube.coffee',
|
'youtube.coffee',
|
||||||
'dailymotion.coffee',
|
'dailymotion.coffee',
|
||||||
'videojs.coffee',
|
'videojs.coffee',
|
||||||
|
'gdrive-player.coffee',
|
||||||
'raw-file.coffee',
|
'raw-file.coffee',
|
||||||
'soundcloud.coffee',
|
'soundcloud.coffee',
|
||||||
'embed.coffee',
|
'embed.coffee',
|
||||||
|
@ -19,6 +20,7 @@ var order = [
|
||||||
'ustream.coffee',
|
'ustream.coffee',
|
||||||
'imgur.coffee',
|
'imgur.coffee',
|
||||||
'gdrive-youtube.coffee',
|
'gdrive-youtube.coffee',
|
||||||
|
'hls.coffee',
|
||||||
'update.coffee'
|
'update.coffee'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Google Drive Userscript Setup
|
||||||
|
|
||||||
|
In response to increasing difficulty and complexity of maintaining Google Drive
|
||||||
|
support, the native player is being phased out in favor of requiring a
|
||||||
|
userscript to allow each client to fetch the video stream links for themselves.
|
||||||
|
Users will be prompted with a link to `/google_drive_userscript`, which explains
|
||||||
|
the situation and instructs how to install the userscript.
|
||||||
|
|
||||||
|
As a server admin, you must generate the userscript from the template by using
|
||||||
|
the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run generate-userscript <site name> <url> [<url>...]
|
||||||
|
```
|
||||||
|
|
||||||
|
The first argument is the site name as it will appear in the userscript title.
|
||||||
|
The remaining arguments are the URL patterns on which the script will run. For
|
||||||
|
example, for cytu.be I use:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run generate-userscript CyTube http://cytu.be/r/* https://cytu.be/r/*
|
||||||
|
```
|
||||||
|
|
||||||
|
This will generate `www/js/cytube-google-drive.user.js`.
|
|
@ -0,0 +1,210 @@
|
||||||
|
// ==UserScript==
|
||||||
|
// @name Google Drive Video Player for {SITENAME}
|
||||||
|
// @namespace gdcytube
|
||||||
|
// @description Play Google Drive videos on {SITENAME}
|
||||||
|
// {INCLUDE_BLOCK}
|
||||||
|
// @grant unsafeWindow
|
||||||
|
// @grant GM_xmlhttpRequest
|
||||||
|
// @connect docs.google.com
|
||||||
|
// @run-at document-end
|
||||||
|
// @version 1.1.0
|
||||||
|
// ==/UserScript==
|
||||||
|
|
||||||
|
try {
|
||||||
|
function debug(message) {
|
||||||
|
if (!unsafeWindow.enableCyTubeGoogleDriveUserscriptDebug) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
unsafeWindow.console.log(message);
|
||||||
|
} catch (error) {
|
||||||
|
unsafeWindow.console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ITAG_QMAP = {
|
||||||
|
37: 1080,
|
||||||
|
46: 1080,
|
||||||
|
22: 720,
|
||||||
|
45: 720,
|
||||||
|
59: 480,
|
||||||
|
44: 480,
|
||||||
|
35: 480,
|
||||||
|
18: 360,
|
||||||
|
43: 360,
|
||||||
|
34: 360
|
||||||
|
};
|
||||||
|
|
||||||
|
var ITAG_CMAP = {
|
||||||
|
43: 'video/webm',
|
||||||
|
44: 'video/webm',
|
||||||
|
45: 'video/webm',
|
||||||
|
46: 'video/webm',
|
||||||
|
18: 'video/mp4',
|
||||||
|
22: 'video/mp4',
|
||||||
|
37: 'video/mp4',
|
||||||
|
59: 'video/mp4',
|
||||||
|
35: 'video/flv',
|
||||||
|
34: 'video/flv'
|
||||||
|
};
|
||||||
|
|
||||||
|
function getVideoInfo(id, cb) {
|
||||||
|
var url = 'https://docs.google.com/file/d/' + id + '/get_video_info';
|
||||||
|
debug('Fetching ' + url);
|
||||||
|
|
||||||
|
GM_xmlhttpRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: url,
|
||||||
|
onload: function (res) {
|
||||||
|
try {
|
||||||
|
debug('Got response ' + res.responseText);
|
||||||
|
var data = {};
|
||||||
|
var error;
|
||||||
|
res.responseText.split('&').forEach(function (kv) {
|
||||||
|
var pair = kv.split('=');
|
||||||
|
data[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.status === 'fail') {
|
||||||
|
error = new Error('Google Docs request failed: ' +
|
||||||
|
'metadata indicated status=fail');
|
||||||
|
error.response = res.responseText;
|
||||||
|
error.reason = 'RESPONSE_STATUS_FAIL';
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onerror: function () {
|
||||||
|
var error = new Error('Google Docs request failed: ' +
|
||||||
|
'metadata lookup HTTP request failed');
|
||||||
|
error.reason = 'HTTP_ONERROR';
|
||||||
|
return cb(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapLinks(links) {
|
||||||
|
var videos = {
|
||||||
|
1080: [],
|
||||||
|
720: [],
|
||||||
|
480: [],
|
||||||
|
360: []
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(links).forEach(function (itag) {
|
||||||
|
itag = parseInt(itag, 10);
|
||||||
|
if (!ITAG_QMAP.hasOwnProperty(itag)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
videos[ITAG_QMAP[itag]].push({
|
||||||
|
itag: itag,
|
||||||
|
contentType: ITAG_CMAP[itag],
|
||||||
|
link: links[itag]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return videos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Greasemonkey 2.0 has this wonderful sandbox that attempts
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sandbox side function -- polls gdUserscript.pollID for
|
||||||
|
* the ID of a Drive video to be queried, looks up the
|
||||||
|
* metadata, and stores it in gdUserscript.pollResult
|
||||||
|
*/
|
||||||
|
function setupGDPoll() {
|
||||||
|
unsafeWindow.gdUserscript = cloneInto({}, unsafeWindow);
|
||||||
|
var pollInterval = setInterval(function () {
|
||||||
|
if (unsafeWindow.gdUserscript.pollID) {
|
||||||
|
var id = unsafeWindow.gdUserscript.pollID;
|
||||||
|
unsafeWindow.gdUserscript.pollID = null;
|
||||||
|
debug('Polled and got ' + id);
|
||||||
|
getVideoInfo(id, function (error, data) {
|
||||||
|
unsafeWindow.gdUserscript.pollResult = cloneInto({
|
||||||
|
error: error,
|
||||||
|
result: data
|
||||||
|
}, unsafeWindow);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRunningTampermonkey() {
|
||||||
|
try {
|
||||||
|
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.console.log('Initialized userscript Google Drive player');
|
||||||
|
unsafeWindow.hasDriveUserscript = true;
|
||||||
|
} catch (error) {
|
||||||
|
unsafeWindow.console.error(error);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var sitename = process.argv[2];
|
||||||
|
var includes = process.argv.slice(3).map(function (include) {
|
||||||
|
return '// @include ' + include;
|
||||||
|
}).join('\n');
|
||||||
|
|
||||||
|
var lines = String(fs.readFileSync(
|
||||||
|
path.resolve(__dirname, 'cytube-google-drive.user.js'))).split('\n');
|
||||||
|
lines.forEach(function (line) {
|
||||||
|
if (line.match(/\{INCLUDE_BLOCK\}/)) {
|
||||||
|
console.log(includes);
|
||||||
|
} else if (line.match(/\{SITENAME\}/)) {
|
||||||
|
console.log(line.replace(/\{SITENAME\}/, sitename));
|
||||||
|
} else {
|
||||||
|
console.log(line);
|
||||||
|
}
|
||||||
|
});
|
|
@ -47,7 +47,8 @@
|
||||||
"build-player": "$npm_node_execpath build-player.js",
|
"build-player": "$npm_node_execpath build-player.js",
|
||||||
"build-server": "babel -D --source-maps --loose es6.destructuring,es6.forOf --out-dir lib/ src/",
|
"build-server": "babel -D --source-maps --loose es6.destructuring,es6.forOf --out-dir lib/ src/",
|
||||||
"postinstall": "./postinstall.sh",
|
"postinstall": "./postinstall.sh",
|
||||||
"server-dev": "babel -D --watch --source-maps --loose es6.destructuring,es6.forOf --out-dir lib/ src/"
|
"server-dev": "babel -D --watch --source-maps --loose es6.destructuring,es6.forOf --out-dir lib/ src/",
|
||||||
|
"generate-userscript": "$npm_node_execpath gdrive-userscript/generate-userscript $@ > www/js/cytube-google-drive.user.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"coffee-script": "^1.9.2"
|
"coffee-script": "^1.9.2"
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
window.GoogleDrivePlayer = class GoogleDrivePlayer extends VideoJSPlayer
|
||||||
|
constructor: (data) ->
|
||||||
|
if not (this instanceof GoogleDrivePlayer)
|
||||||
|
return new GoogleDrivePlayer(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)
|
|
@ -7,6 +7,7 @@ window.GoogleDriveYouTubePlayer = class GoogleDriveYouTubePlayer extends Player
|
||||||
@init(data)
|
@init(data)
|
||||||
|
|
||||||
init: (data) ->
|
init: (data) ->
|
||||||
|
window.promptToInstallDriveUserscript()
|
||||||
embed = $('<embed />').attr(
|
embed = $('<embed />').attr(
|
||||||
type: 'application/x-shockwave-flash'
|
type: 'application/x-shockwave-flash'
|
||||||
src: "https://www.youtube.com/get_player?docid=#{data.id}&ps=docs\
|
src: "https://www.youtube.com/get_player?docid=#{data.id}&ps=docs\
|
||||||
|
@ -102,3 +103,30 @@ window.GoogleDriveYouTubePlayer = class GoogleDriveYouTubePlayer extends Player
|
||||||
cb(@yt.getVolume() / 100)
|
cb(@yt.getVolume() / 100)
|
||||||
else
|
else
|
||||||
cb(VOLUME)
|
cb(VOLUME)
|
||||||
|
|
||||||
|
window.promptToInstallDriveUserscript = ->
|
||||||
|
if document.getElementById('prompt-install-drive-userscript')
|
||||||
|
return
|
||||||
|
alertBox = document.createElement('div')
|
||||||
|
alertBox.id = 'prompt-install-drive-userscript'
|
||||||
|
alertBox.className = 'alert alert-info'
|
||||||
|
alertBox.innerHTML = """
|
||||||
|
Due to continual breaking changes making it increasingly difficult to
|
||||||
|
maintain Google Drive support, you can now install a userscript that
|
||||||
|
simplifies the code and has better compatibility. In the future, the
|
||||||
|
old player will be removed."""
|
||||||
|
alertBox.appendChild(document.createElement('br'))
|
||||||
|
infoLink = document.createElement('a')
|
||||||
|
infoLink.className = 'btn btn-info'
|
||||||
|
infoLink.href = '/google_drive_userscript'
|
||||||
|
infoLink.textContent = 'Click here for details'
|
||||||
|
infoLink.target = '_blank'
|
||||||
|
alertBox.appendChild(infoLink)
|
||||||
|
|
||||||
|
closeButton = document.createElement('button')
|
||||||
|
closeButton.className = 'close pull-right'
|
||||||
|
closeButton.innerHTML = '×'
|
||||||
|
closeButton.onclick = ->
|
||||||
|
alertBox.parentNode.removeChild(alertBox)
|
||||||
|
alertBox.insertBefore(closeButton, alertBox.firstChild)
|
||||||
|
document.getElementById('videowrap').appendChild(alertBox)
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
window.HLSPlayer = class HLSPlayer extends VideoJSPlayer
|
||||||
|
constructor: (data) ->
|
||||||
|
if not (this instanceof HLSPlayer)
|
||||||
|
return new HLSPlayer(data)
|
||||||
|
|
||||||
|
@setupMeta(data)
|
||||||
|
super(data)
|
||||||
|
|
||||||
|
load: (data) ->
|
||||||
|
@setupMeta(data)
|
||||||
|
super(data)
|
||||||
|
|
||||||
|
setupMeta: (data) ->
|
||||||
|
data.meta.direct =
|
||||||
|
# Quality is required for data.meta.direct processing but doesn't
|
||||||
|
# matter here because it's dictated by the stream. Arbitrarily
|
||||||
|
# choose 480.
|
||||||
|
480: [
|
||||||
|
{
|
||||||
|
link: data.id
|
||||||
|
contentType: 'application/x-mpegURL'
|
||||||
|
}
|
||||||
|
]
|
|
@ -2,7 +2,7 @@ TYPE_MAP =
|
||||||
yt: YouTubePlayer
|
yt: YouTubePlayer
|
||||||
vi: VimeoPlayer
|
vi: VimeoPlayer
|
||||||
dm: DailymotionPlayer
|
dm: DailymotionPlayer
|
||||||
gd: GoogleDriveYouTubePlayer
|
gd: GoogleDrivePlayer
|
||||||
gp: VideoJSPlayer
|
gp: VideoJSPlayer
|
||||||
fi: FilePlayer
|
fi: FilePlayer
|
||||||
jw: FilePlayer
|
jw: FilePlayer
|
||||||
|
@ -15,6 +15,7 @@ TYPE_MAP =
|
||||||
us: UstreamPlayer
|
us: UstreamPlayer
|
||||||
im: ImgurPlayer
|
im: ImgurPlayer
|
||||||
vm: VideoJSPlayer
|
vm: VideoJSPlayer
|
||||||
|
hl: HLSPlayer
|
||||||
sb: VideoJSPlayer
|
sb: VideoJSPlayer
|
||||||
|
|
||||||
window.loadMediaPlayer = (data) ->
|
window.loadMediaPlayer = (data) ->
|
||||||
|
@ -29,6 +30,14 @@ window.loadMediaPlayer = (data) ->
|
||||||
window.PLAYER = new VideoJSPlayer(data)
|
window.PLAYER = new VideoJSPlayer(data)
|
||||||
catch e
|
catch e
|
||||||
console.error e
|
console.error e
|
||||||
|
else if data.type is 'gd'
|
||||||
|
try
|
||||||
|
if data.meta.html5hack or window.hasDriveUserscript
|
||||||
|
window.PLAYER = new GoogleDrivePlayer(data)
|
||||||
|
else
|
||||||
|
window.PLAYER = new GoogleDriveYouTubePlayer(data)
|
||||||
|
catch e
|
||||||
|
console.error e
|
||||||
else if data.type of TYPE_MAP
|
else if data.type of TYPE_MAP
|
||||||
try
|
try
|
||||||
window.PLAYER = TYPE_MAP[data.type](data)
|
window.PLAYER = TYPE_MAP[data.type](data)
|
||||||
|
|
|
@ -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', =>
|
||||||
|
@ -53,14 +52,15 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
|
||||||
.attr(width: '100%', height: '100%')
|
.attr(width: '100%', height: '100%')
|
||||||
removeOld(video)
|
removeOld(video)
|
||||||
|
|
||||||
sources = sortSources(data.meta.direct)
|
@sources = sortSources(data.meta.direct)
|
||||||
if sources.length == 0
|
if @sources.length == 0
|
||||||
console.error('VideoJSPlayer::constructor(): data.meta.direct
|
console.error('VideoJSPlayer::constructor(): data.meta.direct
|
||||||
has no sources!')
|
has no sources!')
|
||||||
@mediaType = null
|
@mediaType = null
|
||||||
return
|
return
|
||||||
|
|
||||||
sources.forEach((source) ->
|
@sourceIdx = 0
|
||||||
|
@sources.forEach((source) ->
|
||||||
$('<source/>').attr(
|
$('<source/>').attr(
|
||||||
src: source.src
|
src: source.src
|
||||||
type: source.type
|
type: source.type
|
||||||
|
@ -84,6 +84,18 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
|
||||||
|
|
||||||
@player = videojs(video[0], autoplay: true, controls: true)
|
@player = videojs(video[0], autoplay: true, controls: true)
|
||||||
@player.ready(=>
|
@player.ready(=>
|
||||||
|
@player.on('error', =>
|
||||||
|
err = @player.error()
|
||||||
|
if err and err.code == 4
|
||||||
|
console.error('Caught error, trying next source')
|
||||||
|
@sourceIdx++
|
||||||
|
if @sourceIdx < @sources.length
|
||||||
|
@player.src(@sources[@sourceIdx])
|
||||||
|
else
|
||||||
|
console.error('Out of sources, video will not play')
|
||||||
|
if @mediaType is 'gd' and not window.hasDriveUserscript
|
||||||
|
window.promptToInstallDriveUserscript()
|
||||||
|
)
|
||||||
@setVolume(VOLUME)
|
@setVolume(VOLUME)
|
||||||
@player.on('ended', ->
|
@player.on('ended', ->
|
||||||
if CLIENT.leader
|
if CLIENT.leader
|
||||||
|
@ -114,12 +126,13 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
|
||||||
# not to run until the ready() function returns.
|
# not to run until the ready() function returns.
|
||||||
setTimeout(->
|
setTimeout(->
|
||||||
$('#ytapiplayer .vjs-subtitles-button .vjs-menu-item').each((i, elem) ->
|
$('#ytapiplayer .vjs-subtitles-button .vjs-menu-item').each((i, elem) ->
|
||||||
if elem.textContent == localStorage.lastSubtitle
|
textNode = elem.childNodes[0]
|
||||||
|
if textNode.textContent == localStorage.lastSubtitle
|
||||||
elem.click()
|
elem.click()
|
||||||
|
|
||||||
elem.onclick = ->
|
elem.onclick = ->
|
||||||
if elem.attributes['aria-selected'].value == 'true'
|
if elem.attributes['aria-checked'].value == 'true'
|
||||||
localStorage.lastSubtitle = elem.textContent
|
localStorage.lastSubtitle = textNode.textContent
|
||||||
)
|
)
|
||||||
, 1)
|
, 1)
|
||||||
)
|
)
|
||||||
|
|
|
@ -120,6 +120,9 @@ var defaults = {
|
||||||
"service-socket": {
|
"service-socket": {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
socket: "service.sock"
|
socket: "service.sock"
|
||||||
|
},
|
||||||
|
"google-drive": {
|
||||||
|
"html5-hack-enabled": false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ var YouTube = require("cytube-mediaquery/lib/provider/youtube");
|
||||||
var Vimeo = require("cytube-mediaquery/lib/provider/vimeo");
|
var Vimeo = require("cytube-mediaquery/lib/provider/vimeo");
|
||||||
var Vidme = require("cytube-mediaquery/lib/provider/vidme");
|
var Vidme = require("cytube-mediaquery/lib/provider/vidme");
|
||||||
var Streamable = require("cytube-mediaquery/lib/provider/streamable");
|
var Streamable = require("cytube-mediaquery/lib/provider/streamable");
|
||||||
|
var GoogleDrive = require("cytube-mediaquery/lib/provider/googledrive");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Preference map of quality => youtube formats.
|
* Preference map of quality => youtube formats.
|
||||||
|
@ -458,6 +459,13 @@ var Getters = {
|
||||||
callback(false, media);
|
callback(false, media);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* HLS stream */
|
||||||
|
hl: function (id, callback) {
|
||||||
|
var title = "Livestream";
|
||||||
|
var media = new Media(id, title, "--:--", "hl");
|
||||||
|
callback(false, media);
|
||||||
|
},
|
||||||
|
|
||||||
/* imgur.com albums */
|
/* imgur.com albums */
|
||||||
im: function (id, callback) {
|
im: function (id, callback) {
|
||||||
/**
|
/**
|
||||||
|
@ -493,6 +501,7 @@ var Getters = {
|
||||||
|
|
||||||
/* google docs */
|
/* google docs */
|
||||||
gd: function (id, callback) {
|
gd: function (id, callback) {
|
||||||
|
GoogleDrive.setHTML5HackEnabled(Config.get("google-drive.html5-hack-enabled"));
|
||||||
var data = {
|
var data = {
|
||||||
type: "googledrive",
|
type: "googledrive",
|
||||||
kind: "single",
|
kind: "single",
|
||||||
|
|
|
@ -38,7 +38,8 @@ Media.prototype = {
|
||||||
bitrate: this.meta.bitrate,
|
bitrate: this.meta.bitrate,
|
||||||
scuri: this.meta.scuri,
|
scuri: this.meta.scuri,
|
||||||
embed: this.meta.embed,
|
embed: this.meta.embed,
|
||||||
gdrive_subtitles: this.meta.gdrive_subtitles
|
gdrive_subtitles: this.meta.gdrive_subtitles,
|
||||||
|
html5hack: this.meta.html5hack
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -242,6 +242,8 @@
|
||||||
return id;
|
return id;
|
||||||
case "hb":
|
case "hb":
|
||||||
return "http://hitbox.tv/" + id;
|
return "http://hitbox.tv/" + id;
|
||||||
|
case "hl":
|
||||||
|
return id;
|
||||||
case "sb":
|
case "sb":
|
||||||
return "https://streamable.com/" + id;
|
return "https://streamable.com/" + id;
|
||||||
default:
|
default:
|
||||||
|
@ -259,6 +261,7 @@
|
||||||
case "im":
|
case "im":
|
||||||
case "jw":
|
case "jw":
|
||||||
case "hb":
|
case "hb":
|
||||||
|
case "hl":
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -294,8 +297,6 @@
|
||||||
var accumulator = "";
|
var accumulator = "";
|
||||||
|
|
||||||
parts = parts.map(function (segment, i) {
|
parts = parts.map(function (segment, i) {
|
||||||
if (i < 2) return segment;
|
|
||||||
|
|
||||||
var part = iphash(accumulator + segment + i, 3);
|
var part = iphash(accumulator + segment + i, 3);
|
||||||
accumulator += segment;
|
accumulator += segment;
|
||||||
return part;
|
return part;
|
||||||
|
@ -311,8 +312,6 @@
|
||||||
var accumulator = "";
|
var accumulator = "";
|
||||||
|
|
||||||
parts = parts.map(function (segment, i) {
|
parts = parts.map(function (segment, i) {
|
||||||
if (i < 2) return segment;
|
|
||||||
|
|
||||||
var part = iphash(accumulator + segment + i, 4);
|
var part = iphash(accumulator + segment + i, 4);
|
||||||
accumulator += segment;
|
accumulator += segment;
|
||||||
return part;
|
return part;
|
||||||
|
|
|
@ -36,6 +36,9 @@ function getBaseUrl(res) {
|
||||||
* Renders and serves a pug template
|
* Renders and serves a pug template
|
||||||
*/
|
*/
|
||||||
function sendPug(res, view, locals) {
|
function sendPug(res, view, locals) {
|
||||||
|
if (!locals) {
|
||||||
|
locals = {};
|
||||||
|
}
|
||||||
locals.loggedIn = locals.loggedIn || !!res.user;
|
locals.loggedIn = locals.loggedIn || !!res.user;
|
||||||
locals.loginName = locals.loginName || res.user ? res.user.name : false;
|
locals.loginName = locals.loginName || res.user ? res.user.name : false;
|
||||||
locals.superadmin = locals.superadmin || res.user ? res.user.global_rank >= 255 : false;
|
locals.superadmin = locals.superadmin || res.user ? res.user.global_rank >= 255 : false;
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { sendPug } from '../pug';
|
||||||
|
|
||||||
|
export default function initialize(app) {
|
||||||
|
app.get('/google_drive_userscript', (req, res) => {
|
||||||
|
return sendPug(res, 'google_drive_userscript')
|
||||||
|
});
|
||||||
|
}
|
|
@ -178,6 +178,7 @@ module.exports = {
|
||||||
require('./account').init(app);
|
require('./account').init(app);
|
||||||
require('./acp').init(app);
|
require('./acp').init(app);
|
||||||
require('../google2vtt').attach(app);
|
require('../google2vtt').attach(app);
|
||||||
|
require('./routes/google_drive_userscript')(app);
|
||||||
app.use(serveStatic(path.join(__dirname, '..', '..', 'www'), {
|
app.use(serveStatic(path.join(__dirname, '..', '..', 'www'), {
|
||||||
maxAge: webConfig.getCacheTTL()
|
maxAge: webConfig.getCacheTTL()
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -249,3 +249,4 @@ html(lang="en")
|
||||||
script(defer, src="/js/sc.js")
|
script(defer, src="/js/sc.js")
|
||||||
script(defer, src="/js/froogaloop.min.js")
|
script(defer, src="/js/froogaloop.min.js")
|
||||||
script(defer, src="/js/video.js")
|
script(defer, src="/js/video.js")
|
||||||
|
script(defer, src="/js/videojs-contrib-hls.min.js")
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
doctype html
|
||||||
|
html(lang="en")
|
||||||
|
head
|
||||||
|
include head
|
||||||
|
+head()
|
||||||
|
body
|
||||||
|
#wrap
|
||||||
|
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
|
||||||
|
include nav
|
||||||
|
+navheader()
|
||||||
|
#nav-collapsible.collapse.navbar-collapse
|
||||||
|
ul.nav.navbar-nav
|
||||||
|
+navdefaultlinks("/google_drive_userscript")
|
||||||
|
+navloginlogout("/google_drive_userscript")
|
||||||
|
section#mainpage
|
||||||
|
.container
|
||||||
|
.col-md-8.col-md-offset-2
|
||||||
|
h1 Google Drive Userscript
|
||||||
|
h2 Why?
|
||||||
|
p.
|
||||||
|
Since Google Drive support was launched in early 2014, it has broken
|
||||||
|
at least 4-5 times, requiring increasing effort to get it working again
|
||||||
|
and disrupting many channels. This is because there is no official API
|
||||||
|
for it like there is for YouTube videos, which means support for it
|
||||||
|
relies on undocumented tricks. In August 2016, the decision was made
|
||||||
|
to phase out the native support for Google Drive and instead require
|
||||||
|
users to install a userscript, which allows to bypass certain browser
|
||||||
|
restrictions and make the code easier, simpler, and less prone to failure
|
||||||
|
(it could still break due to future Google Drive changes, but is less
|
||||||
|
likely to be difficult to fix).
|
||||||
|
h2 How It Works
|
||||||
|
p.
|
||||||
|
The userscript is a short script that you can install using a browser
|
||||||
|
extension such as Greasemonkey or Tampermonkey that runs on the page
|
||||||
|
and provides additional functionality needed to play Google Drive
|
||||||
|
videos.
|
||||||
|
h2 Installation
|
||||||
|
ul
|
||||||
|
li
|
||||||
|
strong Chrome
|
||||||
|
| —Install <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo" target="_blank">Tampermonkey</a>.
|
||||||
|
li
|
||||||
|
strong Firefox
|
||||||
|
| —Install <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Tampermonkey</a>
|
||||||
|
| or <a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/" target="_blank">Greasemonkey</a>.
|
||||||
|
li
|
||||||
|
strong Other Browsers
|
||||||
|
| —Install the appropriate userscript plugin for your browser.
|
||||||
|
| Tampermonkey supports many browsers besides Chrome.
|
||||||
|
p.
|
||||||
|
Once you have installed the userscript manager addon for your browser,
|
||||||
|
you can <a href="/js/cytube-google-drive.user.js?v=1.1" target="_blank">
|
||||||
|
install the userscript</a>. If this link 404s, it means the administrator
|
||||||
|
of this server hasn't generated it yet.
|
||||||
|
p.
|
||||||
|
You can find a guide with screenshots of the installation process
|
||||||
|
<a href="https://github.com/calzoneman/sync/wiki/Google-Drive-Userscript-Installation-Guide" target="_blank">on GitHub</a>.
|
||||||
|
|
||||||
|
include footer
|
||||||
|
+footer()
|
||||||
|
script(type="text/javascript").
|
||||||
|
function showEmail(btn, email, key) {
|
||||||
|
email = unescape(email);
|
||||||
|
key = unescape(key);
|
||||||
|
var dest = new Array(email.length);
|
||||||
|
for (var i = 0; i < email.length; i++) {
|
||||||
|
dest[i] = String.fromCharCode(email.charCodeAt(i) ^ key.charCodeAt(i % key.length));
|
||||||
|
}
|
||||||
|
email = dest.join("");
|
||||||
|
$("<a/>").attr("href", "mailto:" + email)
|
||||||
|
.text(email)
|
||||||
|
.insertBefore(btn);
|
||||||
|
$(btn).remove();
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -216,3 +216,5 @@ function eraseCookie(name) {
|
||||||
|
|
||||||
/* to be implemented in callbacks.js */
|
/* to be implemented in callbacks.js */
|
||||||
function setupCallbacks() { }
|
function setupCallbacks() { }
|
||||||
|
|
||||||
|
window.enableCyTubeGoogleDriveUserscriptDebug = getOrDefault("cytube_drive_debug", false);
|
||||||
|
|
152
www/js/player.js
152
www/js/player.js
|
@ -1,5 +1,5 @@
|
||||||
(function() {
|
(function() {
|
||||||
var CUSTOM_EMBED_WARNING, CustomEmbedPlayer, DEFAULT_ERROR, DailymotionPlayer, EmbedPlayer, FilePlayer, GoogleDriveYouTubePlayer, HITBOX_ERROR, HitboxPlayer, ImgurPlayer, LivestreamPlayer, Player, RTMPPlayer, SoundCloudPlayer, TYPE_MAP, TwitchPlayer, USTREAM_ERROR, UstreamPlayer, VideoJSPlayer, VimeoPlayer, YouTubePlayer, codecToMimeType, genParam, sortSources,
|
var CUSTOM_EMBED_WARNING, CustomEmbedPlayer, DEFAULT_ERROR, DailymotionPlayer, EmbedPlayer, FilePlayer, GoogleDrivePlayer, GoogleDriveYouTubePlayer, HITBOX_ERROR, HLSPlayer, HitboxPlayer, ImgurPlayer, LivestreamPlayer, Player, RTMPPlayer, SoundCloudPlayer, TYPE_MAP, TwitchPlayer, USTREAM_ERROR, UstreamPlayer, VideoJSPlayer, VimeoPlayer, YouTubePlayer, codecToMimeType, genParam, sortSources,
|
||||||
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||||
hasProp = {}.hasOwnProperty;
|
hasProp = {}.hasOwnProperty;
|
||||||
|
|
||||||
|
@ -503,26 +503,26 @@
|
||||||
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) {
|
||||||
return waitUntilDefined(window, 'videojs', (function(_this) {
|
return waitUntilDefined(window, 'videojs', (function(_this) {
|
||||||
return function() {
|
return function() {
|
||||||
var sources, video;
|
var video;
|
||||||
video = $('<video/>').addClass('video-js vjs-default-skin embed-responsive-item').attr({
|
video = $('<video/>').addClass('video-js vjs-default-skin embed-responsive-item').attr({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%'
|
height: '100%'
|
||||||
});
|
});
|
||||||
removeOld(video);
|
removeOld(video);
|
||||||
sources = sortSources(data.meta.direct);
|
_this.sources = sortSources(data.meta.direct);
|
||||||
if (sources.length === 0) {
|
if (_this.sources.length === 0) {
|
||||||
console.error('VideoJSPlayer::constructor(): data.meta.direct has no sources!');
|
console.error('VideoJSPlayer::constructor(): data.meta.direct has no sources!');
|
||||||
_this.mediaType = null;
|
_this.mediaType = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sources.forEach(function(source) {
|
_this.sourceIdx = 0;
|
||||||
|
_this.sources.forEach(function(source) {
|
||||||
return $('<source/>').attr({
|
return $('<source/>').attr({
|
||||||
src: source.src,
|
src: source.src,
|
||||||
type: source.type,
|
type: source.type,
|
||||||
|
@ -549,6 +549,22 @@
|
||||||
controls: true
|
controls: true
|
||||||
});
|
});
|
||||||
return _this.player.ready(function() {
|
return _this.player.ready(function() {
|
||||||
|
_this.player.on('error', function() {
|
||||||
|
var err;
|
||||||
|
err = _this.player.error();
|
||||||
|
if (err && err.code === 4) {
|
||||||
|
console.error('Caught error, trying next source');
|
||||||
|
_this.sourceIdx++;
|
||||||
|
if (_this.sourceIdx < _this.sources.length) {
|
||||||
|
return _this.player.src(_this.sources[_this.sourceIdx]);
|
||||||
|
} else {
|
||||||
|
console.error('Out of sources, video will not play');
|
||||||
|
if (_this.mediaType === 'gd' && !window.hasDriveUserscript) {
|
||||||
|
return window.promptToInstallDriveUserscript();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
_this.setVolume(VOLUME);
|
_this.setVolume(VOLUME);
|
||||||
_this.player.on('ended', function() {
|
_this.player.on('ended', function() {
|
||||||
if (CLIENT.leader) {
|
if (CLIENT.leader) {
|
||||||
|
@ -572,12 +588,14 @@
|
||||||
});
|
});
|
||||||
return setTimeout(function() {
|
return setTimeout(function() {
|
||||||
return $('#ytapiplayer .vjs-subtitles-button .vjs-menu-item').each(function(i, elem) {
|
return $('#ytapiplayer .vjs-subtitles-button .vjs-menu-item').each(function(i, elem) {
|
||||||
if (elem.textContent === localStorage.lastSubtitle) {
|
var textNode;
|
||||||
|
textNode = elem.childNodes[0];
|
||||||
|
if (textNode.textContent === localStorage.lastSubtitle) {
|
||||||
elem.click();
|
elem.click();
|
||||||
}
|
}
|
||||||
return elem.onclick = function() {
|
return elem.onclick = function() {
|
||||||
if (elem.attributes['aria-selected'].value === 'true') {
|
if (elem.attributes['aria-checked'].value === 'true') {
|
||||||
return localStorage.lastSubtitle = elem.textContent;
|
return localStorage.lastSubtitle = textNode.textContent;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -650,6 +668,42 @@
|
||||||
|
|
||||||
})(Player);
|
})(Player);
|
||||||
|
|
||||||
|
window.GoogleDrivePlayer = GoogleDrivePlayer = (function(superClass) {
|
||||||
|
extend(GoogleDrivePlayer, superClass);
|
||||||
|
|
||||||
|
function GoogleDrivePlayer(data) {
|
||||||
|
if (!(this instanceof GoogleDrivePlayer)) {
|
||||||
|
return new GoogleDrivePlayer(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;
|
||||||
|
|
||||||
|
})(VideoJSPlayer);
|
||||||
|
|
||||||
codecToMimeType = function(codec) {
|
codecToMimeType = function(codec) {
|
||||||
switch (codec) {
|
switch (codec) {
|
||||||
case 'mov/h264':
|
case 'mov/h264':
|
||||||
|
@ -1128,6 +1182,7 @@
|
||||||
|
|
||||||
GoogleDriveYouTubePlayer.prototype.init = function(data) {
|
GoogleDriveYouTubePlayer.prototype.init = function(data) {
|
||||||
var embed;
|
var embed;
|
||||||
|
window.promptToInstallDriveUserscript();
|
||||||
embed = $('<embed />').attr({
|
embed = $('<embed />').attr({
|
||||||
type: 'application/x-shockwave-flash',
|
type: 'application/x-shockwave-flash',
|
||||||
src: "https://www.youtube.com/get_player?docid=" + data.id + "&ps=docs&partnerid=30&enablejsapi=1&cc_load_policy=1&auth_timeout=86400000000",
|
src: "https://www.youtube.com/get_player?docid=" + data.id + "&ps=docs&partnerid=30&enablejsapi=1&cc_load_policy=1&auth_timeout=86400000000",
|
||||||
|
@ -1257,11 +1312,68 @@
|
||||||
|
|
||||||
})(Player);
|
})(Player);
|
||||||
|
|
||||||
|
window.promptToInstallDriveUserscript = function() {
|
||||||
|
var alertBox, closeButton, infoLink;
|
||||||
|
if (document.getElementById('prompt-install-drive-userscript')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
alertBox = document.createElement('div');
|
||||||
|
alertBox.id = 'prompt-install-drive-userscript';
|
||||||
|
alertBox.className = 'alert alert-info';
|
||||||
|
alertBox.innerHTML = "Due to continual breaking changes making it increasingly difficult to\nmaintain Google Drive support, you can now install a userscript that\nsimplifies the code and has better compatibility. In the future, the\nold player will be removed.";
|
||||||
|
alertBox.appendChild(document.createElement('br'));
|
||||||
|
infoLink = document.createElement('a');
|
||||||
|
infoLink.className = 'btn btn-info';
|
||||||
|
infoLink.href = '/google_drive_userscript';
|
||||||
|
infoLink.textContent = 'Click here for details';
|
||||||
|
infoLink.target = '_blank';
|
||||||
|
alertBox.appendChild(infoLink);
|
||||||
|
closeButton = document.createElement('button');
|
||||||
|
closeButton.className = 'close pull-right';
|
||||||
|
closeButton.innerHTML = '×';
|
||||||
|
closeButton.onclick = function() {
|
||||||
|
return alertBox.parentNode.removeChild(alertBox);
|
||||||
|
};
|
||||||
|
alertBox.insertBefore(closeButton, alertBox.firstChild);
|
||||||
|
return document.getElementById('videowrap').appendChild(alertBox);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.HLSPlayer = HLSPlayer = (function(superClass) {
|
||||||
|
extend(HLSPlayer, superClass);
|
||||||
|
|
||||||
|
function HLSPlayer(data) {
|
||||||
|
if (!(this instanceof HLSPlayer)) {
|
||||||
|
return new HLSPlayer(data);
|
||||||
|
}
|
||||||
|
this.setupMeta(data);
|
||||||
|
HLSPlayer.__super__.constructor.call(this, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
HLSPlayer.prototype.load = function(data) {
|
||||||
|
this.setupMeta(data);
|
||||||
|
return HLSPlayer.__super__.load.call(this, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
HLSPlayer.prototype.setupMeta = function(data) {
|
||||||
|
return data.meta.direct = {
|
||||||
|
480: [
|
||||||
|
{
|
||||||
|
link: data.id,
|
||||||
|
contentType: 'application/x-mpegURL'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return HLSPlayer;
|
||||||
|
|
||||||
|
})(VideoJSPlayer);
|
||||||
|
|
||||||
TYPE_MAP = {
|
TYPE_MAP = {
|
||||||
yt: YouTubePlayer,
|
yt: YouTubePlayer,
|
||||||
vi: VimeoPlayer,
|
vi: VimeoPlayer,
|
||||||
dm: DailymotionPlayer,
|
dm: DailymotionPlayer,
|
||||||
gd: GoogleDriveYouTubePlayer,
|
gd: GoogleDrivePlayer,
|
||||||
gp: VideoJSPlayer,
|
gp: VideoJSPlayer,
|
||||||
fi: FilePlayer,
|
fi: FilePlayer,
|
||||||
jw: FilePlayer,
|
jw: FilePlayer,
|
||||||
|
@ -1274,11 +1386,12 @@
|
||||||
us: UstreamPlayer,
|
us: UstreamPlayer,
|
||||||
im: ImgurPlayer,
|
im: ImgurPlayer,
|
||||||
vm: VideoJSPlayer,
|
vm: VideoJSPlayer,
|
||||||
|
hl: HLSPlayer,
|
||||||
sb: VideoJSPlayer
|
sb: VideoJSPlayer
|
||||||
};
|
};
|
||||||
|
|
||||||
window.loadMediaPlayer = function(data) {
|
window.loadMediaPlayer = function(data) {
|
||||||
var e, error, error1, error2, error3;
|
var e, error, error1, error2, error3, error4;
|
||||||
try {
|
try {
|
||||||
if (window.PLAYER) {
|
if (window.PLAYER) {
|
||||||
window.PLAYER.destroy();
|
window.PLAYER.destroy();
|
||||||
|
@ -1294,11 +1407,22 @@
|
||||||
e = error2;
|
e = error2;
|
||||||
return console.error(e);
|
return console.error(e);
|
||||||
}
|
}
|
||||||
|
} else if (data.type === 'gd') {
|
||||||
|
try {
|
||||||
|
if (data.meta.html5hack || window.hasDriveUserscript) {
|
||||||
|
return window.PLAYER = new GoogleDrivePlayer(data);
|
||||||
|
} else {
|
||||||
|
return window.PLAYER = new GoogleDriveYouTubePlayer(data);
|
||||||
|
}
|
||||||
|
} catch (error3) {
|
||||||
|
e = error3;
|
||||||
|
return console.error(e);
|
||||||
|
}
|
||||||
} else if (data.type in TYPE_MAP) {
|
} else if (data.type in TYPE_MAP) {
|
||||||
try {
|
try {
|
||||||
return window.PLAYER = TYPE_MAP[data.type](data);
|
return window.PLAYER = TYPE_MAP[data.type](data);
|
||||||
} catch (error3) {
|
} catch (error4) {
|
||||||
e = error3;
|
e = error4;
|
||||||
return console.error(e);
|
return console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,8 @@ function formatURL(data) {
|
||||||
return data.id;
|
return data.id;
|
||||||
case "hb":
|
case "hb":
|
||||||
return "http://hitbox.tv/" + data.id;
|
return "http://hitbox.tv/" + data.id;
|
||||||
|
case "hl":
|
||||||
|
return data.id;
|
||||||
case "sb":
|
case "sb":
|
||||||
return "https://streamable.com/" + data.id;
|
return "https://streamable.com/" + data.id;
|
||||||
default:
|
default:
|
||||||
|
@ -1375,6 +1377,13 @@ function parseMediaLink(url) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((m = url.match(/(.*\.m3u8)/))) {
|
||||||
|
return {
|
||||||
|
id: m[1],
|
||||||
|
type: "hl"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if((m = url.match(/streamable\.com\/([\w-]+)/))) {
|
if((m = url.match(/streamable\.com\/([\w-]+)/))) {
|
||||||
return {
|
return {
|
||||||
id: m[1],
|
id: m[1],
|
||||||
|
|
6516
www/js/video.js
6516
www/js/video.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue