Start working on VideoJS for Google Drive

This commit is contained in:
calzoneman 2015-05-15 00:03:05 -05:00
parent daf2463a6a
commit fe9ebfa6b1
5 changed files with 293 additions and 94 deletions

View File

@ -7,7 +7,7 @@ var CustomEmbedFilter = require("./customembed").filter;
var Server = require("./server");
var Config = require("./config");
var ffmpeg = require("./ffmpeg");
require("cytube-mediaquery"); // Initialize sourcemaps
var mediaquery = require("cytube-mediaquery");
var YouTube = require("cytube-mediaquery/lib/provider/youtube");
/*
@ -62,6 +62,17 @@ var urlRetrieve = function (transport, options, callback) {
req.end();
};
var mediaTypeMap = {
"youtube": "yt",
"googledrive": "gd",
"google+": "gp"
};
function convertMedia(media) {
return new Media(media.id, media.title, media.duration, mediaTypeMap[media.type],
media.meta);
}
var Getters = {
/* youtube.com */
yt: function (id, callback) {
@ -506,96 +517,16 @@ var Getters = {
/* google docs */
gd: function (id, callback) {
/* WARNING: hacks inbound */
var options = {
host: "docs.google.com",
path: "/file/d/" + id + "/get_video_info?sle=true",
port: 443
var data = {
type: "googledrive",
kind: "single",
id: id
};
urlRetrieve(https, options, function (status, res) {
switch (status) {
case 200:
break; /* Request is OK, skip to handling data */
case 400:
return callback("Invalid request", null);
case 403:
return callback("Private video", null);
case 404:
return callback("Video not found", null);
case 500:
case 503:
return callback("Service unavailable", null);
default:
return callback("HTTP " + status, null);
}
try {
var data = {};
res.split("&").forEach(function (urlparam) {
var pair = urlparam.split("=").map(decodeURIComponent).map(
function (s) { return s.replace(/\+/g, ' '); });
data[pair[0]] = pair[1];
});
if (data.hasOwnProperty("reason")) {
var reason = data.reason;
if (reason.indexOf("Unable to play this video at this time.") === 0) {
reason = "There is currently a bug with Google Drive which prevents playback " +
"of videos 1 hour long or longer.";
} else if (reason.indexOf(
"You must be signed in to access this video") >= 0) {
reason = "This video is not shared properly";
}
return callback(reason);
}
if (!data.hasOwnProperty("title")) {
return callback("Returned HTML is missing the video title. Are you " +
"sure the video is done processing?");
}
if (!data.hasOwnProperty("length_seconds")) {
return callback("Returned HTML is missing the video duration. Are you " +
"sure the video is done processing?");
}
var title = data.title;
var seconds = parseInt(data.length_seconds);
var videos = {};
data.fmt_stream_map.split(",").forEach(function (stream) {
var parts = stream.split("|");
videos[parts[0]] = parts[1];
});
var direct = {};
for (var key in GOOGLE_PREFERENCE) {
for (var i = 0; i < GOOGLE_PREFERENCE[key].length; i++) {
var format = GOOGLE_PREFERENCE[key][i];
if (format in videos) {
direct[key] = {
url: videos[format],
contentType: CONTENT_TYPES[format]
};
break;
}
}
}
if (Object.keys(direct).length === 0) {
return callback("No valid links could be extracted", null);
}
callback(null, new Media(id, title, seconds, "gd", { gpdirect: direct }));
} catch (e) {
return callback("Failed to parse Google Docs output", null);
}
mediaquery.lookup(data).then(function (video) {
callback(null, convertMedia(video));
}).catch(function (err) {
callback(err.message || err);
});
},

View File

@ -2,6 +2,7 @@ TYPE_MAP =
yt: YouTubePlayer
vi: VimeoPlayer
dm: DailymotionPlayer
gd: VideoJSPlayer
window.loadMediaPlayer = (data) ->
if data.type of TYPE_MAP

View File

@ -1,6 +1,121 @@
class VideoJSPlayer extends Player
sortSources = (sources) ->
if not sources
console.error('sortSources() called with null source list')
return []
qualities = ['1080', '720', '480', '360', '240']
pref = String(USEROPTS.default_quality)
idx = qualities.indexOf(pref)
if idx < 0
pref = '480'
qualityOrder = qualities.slice(idx).concat(qualities.slice(0, idx))
sourceOrder = []
flvOrder = []
for quality in qualityOrder
if quality of sources
flv = []
nonflv = []
sources[quality].forEach((source) ->
if source.contentType == 'flv'
flv.push(source)
else
nonflv.push(source)
)
sourceOrder = sourceOrder.concat(nonflv)
flvOrder = flvOrder.concat(flv)
return sourceOrder.concat(flvOrder).map((source) ->
type: "video/#{source.contentType}"
src: source.link
)
window.VideoJSPlayer = class VideoJSPlayer extends Player
constructor: (data) ->
if not (this instanceof VideoJSPlayer)
return new VideoJSPlayer(data)
@setMediaProperties(data)
waitUntilDefined(window, 'videojs', =>
video = $('<video/>')
.addClass('video-js vjs-default-skin embed-responsive-item')
.attr(width: '100%', height: '100%')
removeOld(video)
sources = sortSources(data.meta.direct)
if sources.length == 0
# Temporary fix for race condition caused by channel playlist
# sending a changeMedia in onPostUserJoin before mediarefresher
# has refreshed it.
# TODO: Actually fix this on the server side rather than this
# hack.
@mediaType = null
return
sources.forEach((source) ->
$('<source/>').attr('src', source.src)
.attr('type', source.type)
.appendTo(video)
)
@player = videojs(video[0], autoplay: true, controls: true)
@player.ready(=>
@player.on('ended', ->
if CLIENT.leader
socket.emit('playNext')
)
@player.on('pause', =>
@paused = true
if CLIENT.leader
sendVideoUpdate()
)
@player.on('play', =>
@paused = false
if CLIENT.leader
sendVideoUpdate()
)
)
)
load: (data) ->
video = $('<video/>')
.addClass('video-js vjs-default-skin embed-responsive-item')
@setMediaProperties(data)
if @player
@player.src(sortSources(data.meta.direct))
else
console.log('VideoJSPlayer::load() called but @player is undefined')
play: ->
@paused = false
if @player and @player.readyState() > 0
@player.play()
pause: ->
@paused = true
if @player and @player.readyState() > 0
@player.pause()
seekTo: (time) ->
if @player and @player.readyState() > 0
@player.currentTime(time)
setVolume: (volume) ->
if @player and @player.readyState() > 0
@player.volume(volume)
getTime: (cb) ->
if @player and @player.readyState() > 0
cb(@player.currentTime())
else
cb(0)
getVolume: (cb) ->
if @player and @player.readyState() > 0
if @player.muted()
cb(0)
else
cb(@player.volume())
else
cb(VOLUME)

View File

@ -4,6 +4,7 @@ html(lang="en")
include head
mixin head()
link(href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css", rel="stylesheet")
link(rel="stylesheet", href="https://vjs.zencdn.net/4.12/video-js.css")
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
@ -228,3 +229,4 @@ html(lang="en")
script(defer, src="/js/sc.js")
script(defer, src="/js/froogaloop.min.js")
script(defer, src="/js/swf.js")
script(defer, src="https://vjs.zencdn.net/4.12/video.js")

View File

@ -1,5 +1,5 @@
(function() {
var DailymotionPlayer, Player, TYPE_MAP, VimeoPlayer, YouTubePlayer,
var DailymotionPlayer, Player, TYPE_MAP, VideoJSPlayer, VimeoPlayer, YouTubePlayer, 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; },
hasProp = {}.hasOwnProperty;
@ -444,10 +444,160 @@
})(Player);
sortSources = function(sources) {
var flv, flvOrder, i, idx, len, nonflv, pref, qualities, quality, qualityOrder, sourceOrder;
if (!sources) {
console.error('sortSources() called with null source list');
return [];
}
qualities = ['1080', '720', '480', '360', '240'];
pref = String(USEROPTS.default_quality);
idx = qualities.indexOf(pref);
if (idx < 0) {
pref = '480';
}
qualityOrder = qualities.slice(idx).concat(qualities.slice(0, idx));
sourceOrder = [];
flvOrder = [];
for (i = 0, len = qualityOrder.length; i < len; i++) {
quality = qualityOrder[i];
if (quality in sources) {
flv = [];
nonflv = [];
sources[quality].forEach(function(source) {
if (source.contentType === 'flv') {
return flv.push(source);
} else {
return nonflv.push(source);
}
});
sourceOrder = sourceOrder.concat(nonflv);
flvOrder = flvOrder.concat(flv);
}
}
return sourceOrder.concat(flvOrder).map(function(source) {
return {
type: "video/" + source.contentType,
src: source.link
};
});
};
window.VideoJSPlayer = VideoJSPlayer = (function(superClass) {
extend(VideoJSPlayer, superClass);
function VideoJSPlayer(data) {
if (!(this instanceof VideoJSPlayer)) {
return new VideoJSPlayer(data);
}
this.setMediaProperties(data);
waitUntilDefined(window, 'videojs', (function(_this) {
return function() {
var sources, video;
video = $('<video/>').addClass('video-js vjs-default-skin embed-responsive-item').attr({
width: '100%',
height: '100%'
});
removeOld(video);
sources = sortSources(data.meta.direct);
if (sources.length === 0) {
_this.mediaType = null;
return;
}
sources.forEach(function(source) {
return $('<source/>').attr('src', source.src).attr('type', source.type).appendTo(video);
});
_this.player = videojs(video[0], {
autoplay: true,
controls: true
});
return _this.player.ready(function() {
_this.player.on('ended', function() {
if (CLIENT.leader) {
return socket.emit('playNext');
}
});
_this.player.on('pause', function() {
_this.paused = true;
if (CLIENT.leader) {
return sendVideoUpdate();
}
});
return _this.player.on('play', function() {
_this.paused = false;
if (CLIENT.leader) {
return sendVideoUpdate();
}
});
});
};
})(this));
}
VideoJSPlayer.prototype.load = function(data) {
this.setMediaProperties(data);
if (this.player) {
return this.player.src(sortSources(data.meta.direct));
} else {
return console.log('VideoJSPlayer::load() called but @player is undefined');
}
};
VideoJSPlayer.prototype.play = function() {
this.paused = false;
if (this.player && this.player.readyState() > 0) {
return this.player.play();
}
};
VideoJSPlayer.prototype.pause = function() {
this.paused = true;
if (this.player && this.player.readyState() > 0) {
return this.player.pause();
}
};
VideoJSPlayer.prototype.seekTo = function(time) {
if (this.player && this.player.readyState() > 0) {
return this.player.currentTime(time);
}
};
VideoJSPlayer.prototype.setVolume = function(volume) {
if (this.player && this.player.readyState() > 0) {
return this.player.volume(volume);
}
};
VideoJSPlayer.prototype.getTime = function(cb) {
if (this.player && this.player.readyState() > 0) {
return cb(this.player.currentTime());
} else {
return cb(0);
}
};
VideoJSPlayer.prototype.getVolume = function(cb) {
if (this.player && this.player.readyState() > 0) {
if (this.player.muted()) {
return cb(0);
} else {
return cb(this.player.volume());
}
} else {
return cb(VOLUME);
}
};
return VideoJSPlayer;
})(Player);
TYPE_MAP = {
yt: YouTubePlayer,
vi: VimeoPlayer,
dm: DailymotionPlayer
dm: DailymotionPlayer,
gd: VideoJSPlayer
};
window.loadMediaPlayer = function(data) {