Finalized HTML5 YouTube player inbrowser speed changes.
This commit is contained in:
@@ -25,7 +25,7 @@ class @VideoAlpha
|
||||
"1.0": sub
|
||||
"1.25": sub
|
||||
"1.5": sub
|
||||
@setSpeed($.cookie('video_speed'))
|
||||
@setSpeed $.cookie('video_speed')
|
||||
$("#video_#{@id}").data('video', this).addClass('video-load-complete')
|
||||
if @show_captions is true
|
||||
@hide_captions = $.cookie('hide_captions') == 'true'
|
||||
@@ -47,7 +47,7 @@ class @VideoAlpha
|
||||
youtubeId: (speed)->
|
||||
@videos[speed || @speed]
|
||||
|
||||
VideoAlpha::parseVideos = (videos) ->
|
||||
parseVideos: (videos)->
|
||||
return false if (typeof videos isnt "string") or (videos.length is 0)
|
||||
@videos = {}
|
||||
_this = this
|
||||
@@ -58,7 +58,7 @@ class @VideoAlpha
|
||||
_this.videos[speed] = video[1]
|
||||
true
|
||||
|
||||
VideoAlpha::parseVideoSources = (mp4Source, webmSource, oggSource) ->
|
||||
parseVideoSources: (mp4Source, webmSource, oggSource)->
|
||||
@html5Sources =
|
||||
mp4: null
|
||||
webm: null
|
||||
@@ -69,9 +69,9 @@ class @VideoAlpha
|
||||
|
||||
parseSpeed: ->
|
||||
@speeds = ($.map @videos, (url, speed) -> speed).sort()
|
||||
@setSpeed($.cookie('video_speed'))
|
||||
@setSpeed $.cookie('video_speed')
|
||||
|
||||
VideoAlpha::setSpeed = (newSpeed) ->
|
||||
setSpeed: (newSpeed)->
|
||||
if @speeds.indexOf(newSpeed) isnt -1
|
||||
@speed = newSpeed
|
||||
$.cookie "video_speed", "" + newSpeed,
|
||||
@@ -91,7 +91,7 @@ class @VideoAlpha
|
||||
getDuration: ->
|
||||
@metadata[@youtubeId()].duration
|
||||
|
||||
VideoAlpha::log = (eventName) ->
|
||||
log: (eventName)->
|
||||
logInfo =
|
||||
id: @id
|
||||
code: @youtubeId()
|
||||
|
||||
@@ -1,195 +1,16 @@
|
||||
this.HTML5Video = (function () {
|
||||
var HTML5Video = {};
|
||||
var HTML5Video;
|
||||
|
||||
HTML5Video = {};
|
||||
|
||||
HTML5Video.Player = (function () {
|
||||
|
||||
/*
|
||||
* Constructor function for HTML5 Video player.
|
||||
*
|
||||
* @el - A DOM element where the HTML5 player will be inserted (as returned by jQuery(selector) function),
|
||||
* or a selector string which will be used to select an element. This is a required parameter.
|
||||
*
|
||||
* @config - An object whose properties will be used as configuration options for the HTML5 video
|
||||
* player. This is an optional parameter. In the case if this parameter is missing, or some of the config
|
||||
* object's properties are missing, defaults will be used. The available options (and their defaults) are as
|
||||
* follows:
|
||||
*
|
||||
* config = {
|
||||
*
|
||||
* 'videoSources': {}, // An object of with properties being video sources. The property name is the
|
||||
* // video format of the source. Supported video formats are: 'mp4', 'webm', and
|
||||
* // 'ogg'. By default videoSources property is null. This means that the
|
||||
* // player will initialize, and not play anything. If you do not provide a
|
||||
* // 'videoSource' option, you can later call loadVideoBySource() method to load
|
||||
* // a video and start playing it.
|
||||
*
|
||||
* 'playerVars': { // Object's properties identify player parameters. *
|
||||
* 'start': null, // Possible values: positive integer. Position from which to start playing the
|
||||
* // video. Measured in seconds. If value is null, or 'start' property is not
|
||||
* // specified, the video will start playing from the beginning.
|
||||
*
|
||||
* 'end': null // Possible values: positive integer. Position when to stop playing the
|
||||
* // video. Measured in seconds. If value is null, or 'end' property is not
|
||||
* // specified, the video will end playing at the end.
|
||||
*
|
||||
* },
|
||||
*
|
||||
* 'events': { // Object's properties identify the events that the API fires, and the
|
||||
* // functions (event listeners) that the API will call when those events occur.
|
||||
* // If value is null, or property is not specified, then no callback will be
|
||||
* // called for that event.
|
||||
*
|
||||
* 'onReady': null,
|
||||
* 'onStateChange': null
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
function Player(el, config) {
|
||||
var sourceStr, _this;
|
||||
|
||||
if (typeof el === 'string') {
|
||||
this.el = $(el);
|
||||
} else if (el instanceof jQuery) {
|
||||
this.el = el;
|
||||
} else {
|
||||
// Error. Parameter el does not have a recognized type.
|
||||
|
||||
// TODO: Make sure that nothing breaks if one of the methods available via this object's prototype
|
||||
// is called after we return.
|
||||
|
||||
return;
|
||||
Player.prototype.callStateChangeCallback = function () {
|
||||
if ($.isFunction(this.config.events.onStateChange) === true) {
|
||||
this.config.events.onStateChange({
|
||||
'data': this.playerState
|
||||
});
|
||||
}
|
||||
|
||||
if ($.isPlainObject(config) === true) {
|
||||
this.config = config;
|
||||
} else {
|
||||
// Error. Parameter config does not have a recognized type.
|
||||
|
||||
// TODO: Make sure that nothing breaks if one of the methods available via this object's prototype
|
||||
// is called after we return.
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.start = 0;
|
||||
this.end = null;
|
||||
if (config.hasOwnProperty('playerVars') === true) {
|
||||
this.start = parseFloat(config.playerVars.start);
|
||||
if ((isFinite(this.start) !== true) || (this.start < 0)) {
|
||||
this.start = 0;
|
||||
}
|
||||
|
||||
this.end = parseFloat(config.playerVars.end);
|
||||
if ((isFinite(this.end) !== true) || (this.end < this.start)) {
|
||||
this.end = null;
|
||||
}
|
||||
}
|
||||
|
||||
sourceStr = {
|
||||
'mp4': ' ',
|
||||
'webm': ' ',
|
||||
'ogg': ' '
|
||||
};
|
||||
|
||||
_this = this;
|
||||
$.each(sourceStr, function (videoType, videoSource) {
|
||||
if (
|
||||
(_this.config.videoSources.hasOwnProperty(videoType) === true) &&
|
||||
(typeof _this.config.videoSources[videoType] === 'string') &&
|
||||
(_this.config.videoSources[videoType].length > 0)
|
||||
) {
|
||||
sourceStr[videoType] =
|
||||
'<source ' +
|
||||
'src="' + _this.config.videoSources[videoType] + '" ' +
|
||||
'type="video/' + videoType + '" ' +
|
||||
'/> ';
|
||||
}
|
||||
});
|
||||
|
||||
this.playerState = HTML5Video.PlayerState.UNSTARTED;
|
||||
|
||||
this.videoEl = $(
|
||||
'<video style="width: 100%;">' +
|
||||
sourceStr.mp4 +
|
||||
sourceStr.webm +
|
||||
sourceStr.ogg +
|
||||
'</video>'
|
||||
);
|
||||
|
||||
this.video = this.videoEl[0];
|
||||
|
||||
this.videoEl.on('click', function (event) {
|
||||
if (_this.playerState === HTML5Video.PlayerState.PAUSED) {
|
||||
_this.video.play();
|
||||
_this.playerState = HTML5Video.PlayerState.PLAYING;
|
||||
|
||||
if ($.isFunction(_this.config.events.onStateChange) === true) {
|
||||
_this.config.events.onStateChange({
|
||||
'data': _this.playerState
|
||||
});
|
||||
}
|
||||
} else if (_this.playerState === HTML5Video.PlayerState.PLAYING) {
|
||||
_this.video.pause();
|
||||
_this.playerState = HTML5Video.PlayerState.PAUSED;
|
||||
|
||||
if ($.isFunction(_this.config.events.onStateChange) === true) {
|
||||
_this.config.events.onStateChange({
|
||||
'data': _this.playerState
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.video.addEventListener('canplay', function () {
|
||||
_this.playerState = HTML5Video.PlayerState.PAUSED;
|
||||
|
||||
if (_this.start > _this.video.duration) {
|
||||
_this.start = 0;
|
||||
}
|
||||
if ((_this.end === null) || (_this.end > _this.video.duration)) {
|
||||
_this.end = _this.video.duration;
|
||||
}
|
||||
_this.video.currentTime = _this.start;
|
||||
|
||||
if ($.isFunction(_this.config.events.onReady) === true) {
|
||||
_this.config.events.onReady({});
|
||||
}
|
||||
}, false);
|
||||
this.video.addEventListener('play', function () {
|
||||
_this.playerState = HTML5Video.PlayerState.PLAYING;
|
||||
|
||||
if ($.isFunction(_this.config.events.onStateChange) === true) {
|
||||
_this.config.events.onStateChange({
|
||||
'data': _this.playerState
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
this.video.addEventListener('pause', function () {
|
||||
_this.playerState = HTML5Video.PlayerState.PAUSED;
|
||||
|
||||
if ($.isFunction(_this.config.events.onStateChange) === true) {
|
||||
_this.config.events.onStateChange({
|
||||
'data': _this.playerState
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
this.video.addEventListener('ended', function () {
|
||||
_this.playerState = HTML5Video.PlayerState.ENDED;
|
||||
|
||||
if ($.isFunction(_this.config.events.onStateChange) === true) {
|
||||
_this.config.events.onStateChange({
|
||||
'data': _this.playerState
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
this.video.addEventListener('timeupdate', function (data) {
|
||||
if (_this.end < _this.video.currentTime) {
|
||||
_this.video.pause();
|
||||
}
|
||||
}, false);
|
||||
|
||||
this.videoEl.appendTo(this.el.find('.video-player div'));
|
||||
}
|
||||
};
|
||||
|
||||
Player.prototype.pauseVideo = function () {
|
||||
this.video.pause();
|
||||
@@ -230,7 +51,7 @@ this.HTML5Video = (function () {
|
||||
return this.video.duration;
|
||||
};
|
||||
|
||||
Player.prototype.setSpeed = function (value) {
|
||||
Player.prototype.setPlaybackRate = function (value) {
|
||||
var newSpeed;
|
||||
|
||||
newSpeed = parseFloat(value);
|
||||
@@ -245,6 +66,205 @@ this.HTML5Video = (function () {
|
||||
};
|
||||
|
||||
return Player;
|
||||
|
||||
/*
|
||||
* Constructor function for HTML5 Video player.
|
||||
*
|
||||
* @el - A DOM element where the HTML5 player will be inserted (as returned by jQuery(selector) function),
|
||||
* or a selector string which will be used to select an element. This is a required parameter.
|
||||
*
|
||||
* @config - An object whose properties will be used as configuration options for the HTML5 video
|
||||
* player. This is an optional parameter. In the case if this parameter is missing, or some of the config
|
||||
* object's properties are missing, defaults will be used. The available options (and their defaults) are as
|
||||
* follows:
|
||||
*
|
||||
* config = {
|
||||
*
|
||||
* 'videoSources': {}, // An object with properties being video sources. The property name is the
|
||||
* // video format of the source. Supported video formats are: 'mp4', 'webm', and
|
||||
* // 'ogg'.
|
||||
*
|
||||
* 'playerVars': { // Object's properties identify player parameters.
|
||||
* 'start': 0, // Possible values: positive integer. Position from which to start playing the
|
||||
* // video. Measured in seconds. If value is non-numeric, or 'start' property is
|
||||
* // not specified, the video will start playing from the beginning.
|
||||
*
|
||||
* 'end': null // Possible values: positive integer. Position when to stop playing the
|
||||
* // video. Measured in seconds. If value is null, or 'end' property is not
|
||||
* // specified, the video will end playing at the end.
|
||||
*
|
||||
* },
|
||||
*
|
||||
* 'events': { // Object's properties identify the events that the API fires, and the
|
||||
* // functions (event listeners) that the API will call when those events occur.
|
||||
* // If value is null, or property is not specified, then no callback will be
|
||||
* // called for that event.
|
||||
*
|
||||
* 'onReady': null,
|
||||
* 'onStateChange': null
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
function Player(el, config) {
|
||||
var sourceStr, _this;
|
||||
|
||||
// If el is string, we assume it is an ID of a DOM element. Get the element, and check that the ID
|
||||
// really belongs to an element. If we didn't get a DOM element, return. At this stage, nothing will
|
||||
// break because other parts of the video player are waiting for 'onReady' callback to be called.
|
||||
if (typeof el === 'string') {
|
||||
this.el = $(el);
|
||||
|
||||
if (this.el.length === 0) {
|
||||
return;
|
||||
}
|
||||
} else if (el instanceof jQuery) {
|
||||
this.el = el;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// A simple test to see that the 'config' is a normal object.
|
||||
if ($.isPlainObject(config) === true) {
|
||||
this.config = config;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// We should have at least one video source. Otherwise there is no point to continue.
|
||||
if (config.hasOwnProperty('videoSources') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// From the start, all sources are empty. We will populate this object below.
|
||||
sourceStr = {
|
||||
'mp4': ' ',
|
||||
'webm': ' ',
|
||||
'ogg': ' '
|
||||
};
|
||||
|
||||
// Will be used in inner functions to point to the current object.
|
||||
_this = this;
|
||||
|
||||
// Create HTML markup for individual sources of the HTML5 <video> element.
|
||||
$.each(sourceStr, function (videoType, videoSource) {
|
||||
if (
|
||||
(_this.config.videoSources.hasOwnProperty(videoType) === true) &&
|
||||
(typeof _this.config.videoSources[videoType] === 'string') &&
|
||||
(_this.config.videoSources[videoType].length > 0)
|
||||
) {
|
||||
sourceStr[videoType] =
|
||||
'<source ' +
|
||||
'src="' + _this.config.videoSources[videoType] + '" ' +
|
||||
'type="video/' + videoType + '" ' +
|
||||
'/> ';
|
||||
}
|
||||
});
|
||||
|
||||
// We should have at least one video source. Otherwise there is no point to continue.
|
||||
if ((sourceStr.mp4 === ' ') && (sourceStr.webm === ' ') && (sourceStr.ogg === ' ')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the starting and ending time for the video.
|
||||
this.start = 0;
|
||||
this.end = null;
|
||||
if (config.hasOwnProperty('playerVars') === true) {
|
||||
this.start = parseFloat(config.playerVars.start);
|
||||
if ((isFinite(this.start) !== true) || (this.start < 0)) {
|
||||
this.start = 0;
|
||||
}
|
||||
|
||||
this.end = parseFloat(config.playerVars.end);
|
||||
if ((isFinite(this.end) !== true) || (this.end < this.start)) {
|
||||
this.end = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Create HTML markup for the <video> element, populating it with sources from previous step.
|
||||
this.videoEl = $(
|
||||
'<video style="width: 100%;">' +
|
||||
sourceStr.mp4 +
|
||||
sourceStr.webm +
|
||||
sourceStr.ogg +
|
||||
'</video>'
|
||||
);
|
||||
|
||||
// Get the DOM element (to access the HTML5 video API), and set the player state to UNSTARTED.
|
||||
// The player state is used by other parts of the VideoPlayer to detrermine what the video is
|
||||
// currently doing.
|
||||
this.video = this.videoEl[0];
|
||||
this.playerState = HTML5Video.PlayerState.UNSTARTED;
|
||||
|
||||
// Attach a 'click' event on the <video> element. It will cause the video to pause/play.
|
||||
this.videoEl.on('click', function (event) {
|
||||
if (_this.playerState === HTML5Video.PlayerState.PAUSED) {
|
||||
_this.video.play();
|
||||
_this.playerState = HTML5Video.PlayerState.PLAYING;
|
||||
_this.callStateChangeCallback();
|
||||
} else if (_this.playerState === HTML5Video.PlayerState.PLAYING) {
|
||||
_this.video.pause();
|
||||
_this.playerState = HTML5Video.PlayerState.PAUSED;
|
||||
_this.callStateChangeCallback();
|
||||
}
|
||||
});
|
||||
|
||||
// When the <video> tag has been processed by the browser, and it is ready for playback,
|
||||
// notify other parts of the VideoPlayer, and initially pause the video.
|
||||
//
|
||||
// Also, at this time we can get the real duration of the video. Update the starting end ending
|
||||
// points of the video. Note that first time, the video will start playing at the specified start time,
|
||||
// and end playing at the specified end time. After it was paused, or when a seek operation happeded,
|
||||
// the starting time and ending time will reset to the beginning and the end of the video respectively.
|
||||
this.video.addEventListener('canplay', function () {
|
||||
_this.playerState = HTML5Video.PlayerState.PAUSED;
|
||||
|
||||
if (_this.start > _this.video.duration) {
|
||||
_this.start = 0;
|
||||
}
|
||||
if ((_this.end === null) || (_this.end > _this.video.duration)) {
|
||||
_this.end = _this.video.duration;
|
||||
}
|
||||
_this.video.currentTime = _this.start;
|
||||
|
||||
if ($.isFunction(_this.config.events.onReady) === true) {
|
||||
_this.config.events.onReady({});
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Register the 'play' event.
|
||||
this.video.addEventListener('play', function () {
|
||||
_this.playerState = HTML5Video.PlayerState.PLAYING;
|
||||
_this.callStateChangeCallback();
|
||||
}, false);
|
||||
|
||||
// Register the 'pause' event.
|
||||
this.video.addEventListener('pause', function () {
|
||||
_this.playerState = HTML5Video.PlayerState.PAUSED;
|
||||
_this.callStateChangeCallback();
|
||||
}, false);
|
||||
|
||||
// Register the 'ended' event.
|
||||
this.video.addEventListener('ended', function () {
|
||||
_this.playerState = HTML5Video.PlayerState.ENDED;
|
||||
_this.callStateChangeCallback();
|
||||
}, false);
|
||||
|
||||
// Register the 'timeupdate' event. This is the place where we control when the video ends.
|
||||
// If an ending time was specified, then after the video plays through to this spot, pauses, we
|
||||
// must make sure to update the ending time to the end of the video. This way, the user can watch
|
||||
// any parts of it afterwards.
|
||||
this.video.addEventListener('timeupdate', function (data) {
|
||||
if (_this.end < _this.video.currentTime) {
|
||||
// When we call video.pause(), a 'pause' event will be formed, and we will catch it
|
||||
// in another handler (see above).
|
||||
_this.video.pause();
|
||||
_this.end = _this.video.duration;
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Place the <video> element on the page.
|
||||
this.videoEl.appendTo(this.el.find('.video-player div'));
|
||||
}
|
||||
}());
|
||||
|
||||
HTML5Video.PlayerState = {
|
||||
|
||||
@@ -94,19 +94,16 @@ class @VideoPlayerAlpha extends SubviewAlpha
|
||||
when @PlayerState.UNSTARTED
|
||||
if @video.videoType is "youtube"
|
||||
availableSpeeds = @player.getAvailablePlaybackRates()
|
||||
console.log @video.videos
|
||||
if availableSpeeds.length > 1
|
||||
baseSpeedSubs = @video.videos["1.0"]
|
||||
$.each @video.videos, (index, value) ->
|
||||
delete _this.video.videos[index]
|
||||
|
||||
@video.speeds = []
|
||||
$.each availableSpeeds, (index, value) ->
|
||||
_this.video.videos[value.toFixed(2).replace(/\.00$/, ".0")] = baseSpeedSubs
|
||||
|
||||
@speedControl.reRender()
|
||||
|
||||
console.log "UNSTARTED. available speeds = "
|
||||
console.log availableSpeeds
|
||||
_this.video.speeds.push value.toFixed(2).replace(/\.00$/, ".0")
|
||||
@speedControl.reRender @video.speeds
|
||||
@video.videoType = 'html5'
|
||||
@onUnstarted()
|
||||
when @PlayerState.PLAYING
|
||||
@onPlay()
|
||||
@@ -162,12 +159,12 @@ class @VideoPlayerAlpha extends SubviewAlpha
|
||||
if @video.videoType is 'youtube'
|
||||
@currentTime = Time.convert(@currentTime, parseFloat(@currentSpeed()), newSpeed)
|
||||
newSpeed = parseFloat(newSpeed).toFixed(2).replace /\.00$/, '.0'
|
||||
@video.setSpeed(newSpeed)
|
||||
@video.setSpeed newSpeed
|
||||
if @video.videoType is 'youtube'
|
||||
if @video.show_captions is true
|
||||
@caption.currentSpeed = newSpeed
|
||||
if @video.videoType is 'html5'
|
||||
@player.setSpeed(newSpeed)
|
||||
@player.setPlaybackRate newSpeed
|
||||
else if @video.videoType is 'youtube'
|
||||
if @isPlaying()
|
||||
@player.loadVideoById(@video.youtubeId(), @currentTime)
|
||||
|
||||
@@ -24,20 +24,14 @@ class @VideoSpeedControlAlpha extends SubviewAlpha
|
||||
<ol class="video_speeds"></ol>
|
||||
</div>
|
||||
"""
|
||||
|
||||
$.each @speeds, (index, speed) =>
|
||||
link = $('<a>').attr(href: "#").html("#{speed}x")
|
||||
@$('.video_speeds').prepend($('<li>').attr('data-speed', speed).html(link))
|
||||
@setSpeed(@currentSpeed)
|
||||
@setSpeed @currentSpeed
|
||||
|
||||
reRender: (newSpeeds) ->
|
||||
@$('.video_speeds').empty()
|
||||
|
||||
@speeds newSpeeds
|
||||
|
||||
console.log "Changing speeds"
|
||||
console.log @speeds
|
||||
|
||||
@speeds = newSpeeds
|
||||
$.each @speeds, (index, speed) =>
|
||||
link = $('<a>').attr(href: "#").html("#{speed}x")
|
||||
@$('.video_speeds').prepend($('<li>').attr('data-speed', speed).html(link))
|
||||
|
||||
Reference in New Issue
Block a user