Commit 33031b76 by Valera Rozuvan

Enable correct total time display in VCR for YouTube videos.

Before this, the VCR showed total time as 00:00:00 for YouTube
videos just after page loaded. The correct total time is shown only
after the user clicks Play button.

BLD-529.
parent 591470df
......@@ -86,7 +86,7 @@ Feature: CMS.Video Component
# 11
Scenario: When start end end times are specified, a range on slider is shown
Given I have created a Video component
Given I have created a Video component with subtitles
And Make sure captions are closed
And I edit the component
And I open tab "Advanced"
......
......@@ -24,7 +24,7 @@ class StubYouTubeServiceTest(unittest.TestCase):
self.url + 'test_youtube/OEoXaMPEzfM?v=2&alt=jsonc&callback=callback_func'
)
self.assertEqual('callback_func({"message": "I\'m youtube."})', response.content)
self.assertEqual('callback_func({"data": {"duration": 60, "message": "I\'m youtube.", "id": "OEoXaMPEzfM"}})', response.content)
def test_transcript_url_equal(self):
response = requests.get(
......
......@@ -6,6 +6,8 @@ from .http import StubHttpRequestHandler, StubHttpService
import json
import time
import requests
from urlparse import urlparse
from collections import OrderedDict
class StubYouTubeHandler(StubHttpRequestHandler):
......@@ -54,14 +56,17 @@ class StubYouTubeHandler(StubHttpRequestHandler):
self.send_response(404)
elif 'test_youtube' in self.path:
self._send_video_response("I'm youtube.")
params = urlparse(self.path)
youtube_id = params.path.split('/').pop()
self._send_video_response(youtube_id, "I'm youtube.")
else:
self.send_response(
404, content="Unused url", headers={'Content-type': 'text/plain'}
)
def _send_video_response(self, message):
def _send_video_response(self, youtube_id, message):
"""
Send message back to the client for video player requests.
Requires sending back callback id.
......@@ -71,7 +76,14 @@ class StubYouTubeHandler(StubHttpRequestHandler):
# Construct the response content
callback = self.get_params['callback'][0]
response = callback + '({})'.format(json.dumps({'message': message}))
data = OrderedDict({
'data': OrderedDict({
'id': youtube_id,
'message': message,
'duration': 60,
})
})
response = callback + '({})'.format(json.dumps(data))
self.send_response(200, content=response, headers={'Content-type': 'text/html'})
self.log_message("Youtube: sent response {}".format(message))
......
# Stub Youtube API
window.YT =
Player: ->
getDuration: ->
60
PlayerState:
UNSTARTED: -1
ENDED: 0
......@@ -128,6 +130,7 @@ jasmine.stubYoutubePlayer = ->
obj = jasmine.createSpyObj 'YT.Player', ['cueVideoById', 'getVideoEmbedCode',
'getCurrentTime', 'getPlayerState', 'getVolume', 'setVolume', 'loadVideoById',
'playVideo', 'pauseVideo', 'seekTo', 'getDuration', 'getAvailablePlaybackRates', 'setPlaybackRate']
obj['getDuration'] = jasmine.createSpy('getDuration').andReturn 60
obj['getAvailablePlaybackRates'] = jasmine.createSpy('getAvailablePlaybackRates').andReturn [0.75, 1.0, 1.25, 1.5]
obj
......
......@@ -64,7 +64,8 @@
window.YT = {
Player: function () {
return {
getPlaybackQuality: function () {}
getPlaybackQuality: function () {},
getDuration: function () { return 60; }
};
},
PlayerState: this.oldYT.PlayerState,
......
......@@ -163,14 +163,16 @@
jasmine.stubRequests();
window.YT = {
Player: function () { },
Player: function () {
return { getDuration: function () { return 60; } };
},
PlayerState: this.oldYT.PlayerState,
ready: function (callback) {
callback();
}
};
spyOn(window.YT, 'Player');
spyOn(window.YT, 'Player').andCallThrough();
});
afterEach(function () {
......
......@@ -117,14 +117,16 @@
jasmine.stubRequests();
window.YT = {
Player: function () { },
Player: function () {
return { getDuration: function () { return 60; } };
},
PlayerState: oldYT.PlayerState,
ready: function (callback) {
callback();
}
};
spyOn(window.YT, 'Player');
spyOn(window.YT, 'Player').andCallThrough();
initializeYouTube();
......@@ -873,6 +875,16 @@
});
});
describe('getDuration', function () {
beforeEach(function () {
});
it('getDuration is called as a fall-back', function () {
});
});
describe('playback rate', function () {
beforeEach(function () {
initialize();
......
......@@ -26,7 +26,9 @@
beforeEach(function() {
window.YT = {
Player: function () { },
Player: function () {
return { getDuration: function () { return 60; } };
},
PlayerState: oldYT.PlayerState,
ready: function(f){f();}
};
......
......@@ -476,8 +476,7 @@ function (VideoPlayer) {
this.config.endTime = parseInt(this.config.endTime, 10);
if (
!isFinite(this.config.endTime) ||
this.config.endTime < this.config.startTime ||
this.config.endTime === 0
this.config.endTime <= this.config.startTime
) {
this.config.endTime = null;
}
......
......@@ -86,7 +86,7 @@ function (HTML5Video, Resizer) {
// At the start, the initial value of the variable
// `seekToStartTimeOldSpeed` should always differ from the value
// returned by the duration function.
// of `state.speed` variable.
state.videoPlayer.seekToStartTimeOldSpeed = 'void';
state.videoPlayer.playerVars = {
......@@ -166,6 +166,44 @@ function (HTML5Video, Resizer) {
videoHeight = player.attr('height') || player.height();
_resize(state, videoWidth, videoHeight);
// We wait for metdata to arrive, before we request the update
// of the VCR video time. Metadata contains duration of the
// video. We wait for 2 seconds, and then abandon our attempts
// to update the VCR video time using metadata.
(function () {
var checkInterval = window.setInterval(
checkForMetadata, 50
),
numberOfChecks = 0;
return;
function checkForMetadata() {
if (state.metadata && state.metadata[state.youtubeId()]) {
console.log('[_initialize]: (youtube) .duration');
// After initialization, update the VCR with total time.
// At this point only the metadata duration is available (not
// very precise), but it is better than having 00:00:00 for
// total time.
state.trigger(
'videoControl.updateVcrVidTime',
{
time: 0,
duration: state.videoPlayer.duration()
}
);
window.clearInterval(checkInterval);
} else {
numberOfChecks += 1;
if (numberOfChecks === 40) {
window.clearInterval(checkInterval);
}
}
}
}());
});
}
......@@ -652,20 +690,12 @@ function (HTML5Video, Resizer) {
}
}
// Rebuild the slider start-end range (if it doesn't take up the
// whole slider). Remember that endTime === null means the end time
// is set to the end of video by default.
if (!(
this.videoPlayer.startTime === 0 &&
this.videoPlayer.endTime === null
)) {
this.trigger(
'videoProgressSlider.updateStartEndTimeRegion',
{
duration: duration
}
);
}
this.trigger(
'videoProgressSlider.updateStartEndTimeRegion',
{
duration: duration
}
);
// If this is not a duration change (if it is, we continue playing
// from current time), then we need to seek the video to the start
......@@ -737,8 +767,28 @@ function (HTML5Video, Resizer) {
function duration() {
var dur = this.videoPlayer.player.getDuration();
if (!isFinite(dur)) {
dur = this.getDuration();
// For YouTube videos, before the video starts playing, the API
// function player.getDuration() will return 0. This means that the VCR
// will show total time as 0 when the page just loads (before the user
// clicks the Play button).
//
// We can do betterin a case when dur is 0 (or less than 0). We can ask
// the getDuration() function for total time, which will query the
// metadata for a duration.
//
// Be careful! Often the metadata duration is not very precise. It
// might differ by one or two seconds against the actual time as will
// be reported later on by the player.getDuration() API function.
if (!isFinite(dur) || dur <= 0) {
if (this.videoType === 'youtube') {
dur = this.getDuration();
}
}
// Just in case the metadata is garbled, or something went wrong, we
// have a final check.
if (!isFinite(dur) || dur <= 0) {
dur = 0;
}
return Math.floor(dur);
......
......@@ -99,6 +99,9 @@ function () {
.find('.ui-slider-range.ui-widget-header.ui-slider-range-min');
}
// Rebuild the slider start-end range (if it doesn't take up the
// whole slider). Remember that endTime === null means the end time
// is set to the end of video by default.
function updateStartEndTimeRegion(params) {
var left, width, start, end, duration, rangeParams;
......@@ -112,11 +115,14 @@ function () {
start = this.videoPlayer.startTime;
// If end is set to null, then we set it to the end of the video. We
// know that start is not a the beginning, therefore we must build a
// range.
// If end is set to null, then we set it to the end of the video.
end = this.videoPlayer.endTime || duration;
// Don't build a range if it takes up the whole slider.
if (start === 0 && end === duration) {
return;
}
// Because JavaScript has weird rounding rules when a series of
// mathematical operations are performed in a single statement, we will
// split everything up into smaller statements.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment