Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
40d5f1e5
Commit
40d5f1e5
authored
Apr 04, 2014
by
Anton Stupak
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #3195 from edx/anton/force-flash
Force video player in flash mode.
parents
da470afc
d5555470
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
243 additions
and
53 deletions
+243
-53
cms/djangoapps/contentstore/features/transcripts.py
+2
-1
cms/djangoapps/contentstore/features/video.py
+4
-1
common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js
+83
-0
common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
+7
-4
common/lib/xmodule/xmodule/js/src/video/00_async_process.js
+1
-1
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
+45
-16
common/lib/xmodule/xmodule/js/src/video/03_video_player.js
+5
-5
common/lib/xmodule/xmodule/js/src/video/09_video_caption.js
+2
-1
common/lib/xmodule/xmodule/video_module/video_module.py
+0
-3
lms/djangoapps/courseware/features/video.feature
+31
-0
lms/djangoapps/courseware/features/video.py
+63
-21
No files found.
cms/djangoapps/contentstore/features/transcripts.py
View file @
40d5f1e5
...
@@ -201,7 +201,8 @@ def upload_file(_step, file_name):
...
@@ -201,7 +201,8 @@ def upload_file(_step, file_name):
@step
(
'I see "([^"]*)" text in the captions'
)
@step
(
'I see "([^"]*)" text in the captions'
)
def
check_text_in_the_captions
(
_step
,
text
):
def
check_text_in_the_captions
(
_step
,
text
):
world
.
wait_for
(
lambda
_
:
world
.
css_text
(
'.subtitles'
),
30
)
world
.
wait_for_present
(
'.video.is-captions-rendered'
)
world
.
wait_for
(
lambda
_
:
world
.
css_text
(
'.subtitles'
),
timeout
=
30
)
actual_text
=
world
.
css_text
(
'.subtitles'
)
actual_text
=
world
.
css_text
(
'.subtitles'
)
assert
(
text
in
actual_text
)
assert
(
text
in
actual_text
)
...
...
cms/djangoapps/contentstore/features/video.py
View file @
40d5f1e5
...
@@ -197,11 +197,15 @@ def find_caption_line_by_data_index(index):
...
@@ -197,11 +197,15 @@ def find_caption_line_by_data_index(index):
@step
(
'I focus on caption line with data-index "([^"]*)"$'
)
@step
(
'I focus on caption line with data-index "([^"]*)"$'
)
def
focus_on_caption_line
(
_step
,
index
):
def
focus_on_caption_line
(
_step
,
index
):
world
.
wait_for_present
(
'.video.is-captions-rendered'
)
world
.
wait_for
(
lambda
_
:
world
.
css_text
(
'.subtitles'
),
timeout
=
30
)
find_caption_line_by_data_index
(
int
(
index
.
strip
()))
.
_element
.
send_keys
(
Keys
.
TAB
)
find_caption_line_by_data_index
(
int
(
index
.
strip
()))
.
_element
.
send_keys
(
Keys
.
TAB
)
@step
(
'I press "enter" button on caption line with data-index "([^"]*)"$'
)
@step
(
'I press "enter" button on caption line with data-index "([^"]*)"$'
)
def
click_on_the_caption
(
_step
,
index
):
def
click_on_the_caption
(
_step
,
index
):
world
.
wait_for_present
(
'.video.is-captions-rendered'
)
world
.
wait_for
(
lambda
_
:
world
.
css_text
(
'.subtitles'
),
timeout
=
30
)
find_caption_line_by_data_index
(
int
(
index
.
strip
()))
.
_element
.
send_keys
(
Keys
.
ENTER
)
find_caption_line_by_data_index
(
int
(
index
.
strip
()))
.
_element
.
send_keys
(
Keys
.
ENTER
)
...
@@ -214,7 +218,6 @@ def caption_line_has_class(_step, index, className):
...
@@ -214,7 +218,6 @@ def caption_line_has_class(_step, index, className):
@step
(
'I see a range on slider$'
)
@step
(
'I see a range on slider$'
)
def
see_a_range_slider_with_proper_range
(
_step
):
def
see_a_range_slider_with_proper_range
(
_step
):
world
.
wait_for_visible
(
VIDEO_BUTTONS
[
'pause'
])
world
.
wait_for_visible
(
VIDEO_BUTTONS
[
'pause'
])
assert
world
.
css_visible
(
".slider-range"
)
assert
world
.
css_visible
(
".slider-range"
)
...
...
common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js
View file @
40d5f1e5
...
@@ -396,6 +396,89 @@ function (Initialize) {
...
@@ -396,6 +396,89 @@ function (Initialize) {
});
});
});
});
});
});
describe
(
'setPlayerMode'
,
function
()
{
beforeEach
(
function
()
{
state
=
{
currentPlayerMode
:
'flash'
,
};
});
it
(
'updates player mode'
,
function
()
{
var
setPlayerMode
=
Initialize
.
prototype
.
setPlayerMode
;
setPlayerMode
.
call
(
state
,
'html5'
);
expect
(
state
.
currentPlayerMode
).
toBe
(
'html5'
);
setPlayerMode
.
call
(
state
,
'flash'
);
expect
(
state
.
currentPlayerMode
).
toBe
(
'flash'
);
});
it
(
'sets default mode if passed is not supported'
,
function
()
{
var
setPlayerMode
=
Initialize
.
prototype
.
setPlayerMode
;
setPlayerMode
.
call
(
state
,
'77html77'
);
expect
(
state
.
currentPlayerMode
).
toBe
(
'html5'
);
});
});
describe
(
'getPlayerMode'
,
function
()
{
beforeEach
(
function
()
{
state
=
{
currentPlayerMode
:
'flash'
,
};
});
it
(
'returns current player mode'
,
function
()
{
var
getPlayerMode
=
Initialize
.
prototype
.
getPlayerMode
,
actual
=
getPlayerMode
.
call
(
state
);
expect
(
actual
).
toBe
(
state
.
currentPlayerMode
);
});
});
describe
(
'isFlashMode'
,
function
()
{
it
(
'returns `true` if player in `flash` mode'
,
function
()
{
var
state
=
{
getPlayerMode
:
jasmine
.
createSpy
().
andReturn
(
'flash'
),
},
isFlashMode
=
Initialize
.
prototype
.
isFlashMode
,
actual
=
isFlashMode
.
call
(
state
);
expect
(
actual
).
toBeTruthy
();
});
it
(
'returns `false` if player is not in `flash` mode'
,
function
()
{
var
state
=
{
getPlayerMode
:
jasmine
.
createSpy
().
andReturn
(
'html5'
),
},
isFlashMode
=
Initialize
.
prototype
.
isFlashMode
,
actual
=
isFlashMode
.
call
(
state
);
expect
(
actual
).
toBeFalsy
();
});
});
describe
(
'isHtml5Mode'
,
function
()
{
it
(
'returns `true` if player in `html5` mode'
,
function
()
{
var
state
=
{
getPlayerMode
:
jasmine
.
createSpy
().
andReturn
(
'html5'
),
},
isHtml5Mode
=
Initialize
.
prototype
.
isHtml5Mode
,
actual
=
isHtml5Mode
.
call
(
state
);
expect
(
actual
).
toBeTruthy
();
});
it
(
'returns `false` if player is not in `html5` mode'
,
function
()
{
var
state
=
{
getPlayerMode
:
jasmine
.
createSpy
().
andReturn
(
'flash'
),
},
isHtml5Mode
=
Initialize
.
prototype
.
isHtml5Mode
,
actual
=
isHtml5Mode
.
call
(
state
);
expect
(
actual
).
toBeFalsy
();
});
});
});
});
});
});
...
...
common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
View file @
40d5f1e5
...
@@ -1070,6 +1070,9 @@ function (VideoPlayer) {
...
@@ -1070,6 +1070,9 @@ function (VideoPlayer) {
beforeEach
(
function
()
{
beforeEach
(
function
()
{
state
=
{
state
=
{
youtubeId
:
jasmine
.
createSpy
().
andReturn
(
'videoId'
),
youtubeId
:
jasmine
.
createSpy
().
andReturn
(
'videoId'
),
isFlashMode
:
jasmine
.
createSpy
().
andReturn
(
false
),
isHtml5Mode
:
jasmine
.
createSpy
().
andReturn
(
true
),
setPlayerMode
:
jasmine
.
createSpy
(),
videoPlayer
:
{
videoPlayer
:
{
currentTime
:
60
,
currentTime
:
60
,
isPlaying
:
jasmine
.
createSpy
(),
isPlaying
:
jasmine
.
createSpy
(),
...
@@ -1083,7 +1086,8 @@ function (VideoPlayer) {
...
@@ -1083,7 +1086,8 @@ function (VideoPlayer) {
});
});
it
(
'in Flash mode and video is playing'
,
function
()
{
it
(
'in Flash mode and video is playing'
,
function
()
{
state
.
currentPlayerMode
=
'flash'
;
state
.
isFlashMode
.
andReturn
(
true
);
state
.
isHtml5Mode
.
andReturn
(
false
);
state
.
videoPlayer
.
isPlaying
.
andReturn
(
true
);
state
.
videoPlayer
.
isPlaying
.
andReturn
(
true
);
VideoPlayer
.
prototype
.
setPlaybackRate
.
call
(
state
,
'0.75'
);
VideoPlayer
.
prototype
.
setPlaybackRate
.
call
(
state
,
'0.75'
);
expect
(
state
.
videoPlayer
.
updatePlayTime
).
toHaveBeenCalledWith
(
60
);
expect
(
state
.
videoPlayer
.
updatePlayTime
).
toHaveBeenCalledWith
(
60
);
...
@@ -1092,7 +1096,8 @@ function (VideoPlayer) {
...
@@ -1092,7 +1096,8 @@ function (VideoPlayer) {
});
});
it
(
'in Flash mode and video not started'
,
function
()
{
it
(
'in Flash mode and video not started'
,
function
()
{
state
.
currentPlayerMode
=
'flash'
;
state
.
isFlashMode
.
andReturn
(
true
);
state
.
isHtml5Mode
.
andReturn
(
false
);
state
.
videoPlayer
.
isPlaying
.
andReturn
(
false
);
state
.
videoPlayer
.
isPlaying
.
andReturn
(
false
);
VideoPlayer
.
prototype
.
setPlaybackRate
.
call
(
state
,
'0.75'
);
VideoPlayer
.
prototype
.
setPlaybackRate
.
call
(
state
,
'0.75'
);
expect
(
state
.
videoPlayer
.
updatePlayTime
).
toHaveBeenCalledWith
(
60
);
expect
(
state
.
videoPlayer
.
updatePlayTime
).
toHaveBeenCalledWith
(
60
);
...
@@ -1101,13 +1106,11 @@ function (VideoPlayer) {
...
@@ -1101,13 +1106,11 @@ function (VideoPlayer) {
});
});
it
(
'in HTML5 mode'
,
function
()
{
it
(
'in HTML5 mode'
,
function
()
{
state
.
currentPlayerMode
=
'html5'
;
VideoPlayer
.
prototype
.
setPlaybackRate
.
call
(
state
,
'0.75'
);
VideoPlayer
.
prototype
.
setPlaybackRate
.
call
(
state
,
'0.75'
);
expect
(
state
.
videoPlayer
.
player
.
setPlaybackRate
).
toHaveBeenCalledWith
(
'0.75'
);
expect
(
state
.
videoPlayer
.
player
.
setPlaybackRate
).
toHaveBeenCalledWith
(
'0.75'
);
});
});
it
(
'Youtube video in FF, with new speed equal 1.0'
,
function
()
{
it
(
'Youtube video in FF, with new speed equal 1.0'
,
function
()
{
state
.
currentPlayerMode
=
'html5'
;
state
.
videoType
=
'youtube'
;
state
.
videoType
=
'youtube'
;
state
.
browserIsFirefox
=
true
;
state
.
browserIsFirefox
=
true
;
...
...
common/lib/xmodule/xmodule/js/src/video/00_async_process.js
View file @
40d5f1e5
...
@@ -10,7 +10,7 @@ function() {
...
@@ -10,7 +10,7 @@ function() {
* @param {array} list Array to process.
* @param {array} list Array to process.
* @param {function} process Calls this function on each item in the list.
* @param {function} process Calls this function on each item in the list.
* @return {array} Returns a Promise object to observe when all actions of a
* @return {array} Returns a Promise object to observe when all actions of a
certain type bound to the collection, queued or not, have finished.
*
certain type bound to the collection, queued or not, have finished.
*/
*/
var
AsyncProcess
=
{
var
AsyncProcess
=
{
array
:
function
(
list
,
process
)
{
array
:
function
(
list
,
process
)
{
...
...
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
View file @
40d5f1e5
...
@@ -63,13 +63,16 @@ function (VideoPlayer, VideoStorage) {
...
@@ -63,13 +63,16 @@ function (VideoPlayer, VideoStorage) {
fetchMetadata
:
fetchMetadata
,
fetchMetadata
:
fetchMetadata
,
getCurrentLanguage
:
getCurrentLanguage
,
getCurrentLanguage
:
getCurrentLanguage
,
getDuration
:
getDuration
,
getDuration
:
getDuration
,
getPlayerMode
:
getPlayerMode
,
getVideoMetadata
:
getVideoMetadata
,
getVideoMetadata
:
getVideoMetadata
,
initialize
:
initialize
,
initialize
:
initialize
,
isHtml5Mode
:
isHtml5Mode
,
isFlashMode
:
isFlashMode
,
isFlashMode
:
isFlashMode
,
parseSpeed
:
parseSpeed
,
parseSpeed
:
parseSpeed
,
parseVideoSources
:
parseVideoSources
,
parseVideoSources
:
parseVideoSources
,
parseYoutubeStreams
:
parseYoutubeStreams
,
parseYoutubeStreams
:
parseYoutubeStreams
,
saveState
:
saveState
,
saveState
:
saveState
,
setPlayerMode
:
setPlayerMode
,
setSpeed
:
setSpeed
,
setSpeed
:
setSpeed
,
trigger
:
trigger
,
trigger
:
trigger
,
youtubeId
:
youtubeId
youtubeId
:
youtubeId
...
@@ -250,18 +253,6 @@ function (VideoPlayer, VideoStorage) {
...
@@ -250,18 +253,6 @@ function (VideoPlayer, VideoStorage) {
}
}
}
}
// function _setPlayerMode(state)
// By default we will be forcing HTML5 player mode. Only in the case
// when, after initializtion, we will get one available playback rate,
// we will change to Flash player mode. There is a need to store this
// setting in cookies because otherwise we will have to change from
// HTML5 to Flash on every page load in a browser that doesn't fully
// support HTML5. When we have this setting in cookies, we can select
// the proper mode from the start (not having to change mode later on).
function
_setPlayerMode
(
state
)
{
state
.
currentPlayerMode
=
'html5'
;
}
// function _parseYouTubeIDs(state)
// function _parseYouTubeIDs(state)
// The function parse YouTube stream ID's.
// The function parse YouTube stream ID's.
// @return
// @return
...
@@ -339,8 +330,7 @@ function (VideoPlayer, VideoStorage) {
...
@@ -339,8 +330,7 @@ function (VideoPlayer, VideoStorage) {
function
_setConfigurations
(
state
)
{
function
_setConfigurations
(
state
)
{
_configureCaptions
(
state
);
_configureCaptions
(
state
);
_setPlayerMode
(
state
);
state
.
setPlayerMode
(
state
.
config
.
mode
);
// Possible value are: 'visible', 'hiding', and 'invisible'.
// Possible value are: 'visible', 'hiding', and 'invisible'.
state
.
controlState
=
'visible'
;
state
.
controlState
=
'visible'
;
state
.
controlHideTimeout
=
null
;
state
.
controlHideTimeout
=
null
;
...
@@ -520,7 +510,8 @@ function (VideoPlayer, VideoStorage) {
...
@@ -520,7 +510,8 @@ function (VideoPlayer, VideoStorage) {
element
:
element
,
element
:
element
,
fadeOutTimeout
:
1400
,
fadeOutTimeout
:
1400
,
captionsFreezeTime
:
10000
,
captionsFreezeTime
:
10000
,
availableQualities
:
[
'hd720'
,
'hd1080'
,
'highres'
]
availableQualities
:
[
'hd720'
,
'hd1080'
,
'highres'
],
mode
:
$
.
cookie
(
'edX_video_player_mode'
)
});
});
if
(
this
.
config
.
endTime
<
this
.
config
.
startTime
)
{
if
(
this
.
config
.
endTime
<
this
.
config
.
startTime
)
{
...
@@ -811,8 +802,46 @@ function (VideoPlayer, VideoStorage) {
...
@@ -811,8 +802,46 @@ function (VideoPlayer, VideoStorage) {
}
}
}
}
/**
* Sets player mode.
*
* @param {string} mode Mode to set for the video player if it is supported.
* Otherwise, `html5` is used by default.
*/
function
setPlayerMode
(
mode
)
{
var
supportedModes
=
[
'html5'
,
'flash'
];
mode
=
_
.
contains
(
supportedModes
,
mode
)
?
mode
:
'html5'
;
this
.
currentPlayerMode
=
mode
;
}
/**
* Returns current player mode.
*
* @return {string} Returns string that describes player mode
*/
function
getPlayerMode
()
{
return
this
.
currentPlayerMode
;
}
/**
* Checks if current player mode is Flash.
*
* @return {boolean} Returns `true` if current mode is `flash`, otherwise
* it returns `false`
*/
function
isFlashMode
()
{
function
isFlashMode
()
{
return
this
.
currentPlayerMode
===
'flash'
;
return
this
.
getPlayerMode
()
===
'flash'
;
}
/**
* Checks if current player mode is Html5.
*
* @return {boolean} Returns `true` if current mode is `html5`, otherwise
* it returns `false`
*/
function
isHtml5Mode
()
{
return
this
.
getPlayerMode
()
===
'html5'
;
}
}
function
getCurrentLanguage
()
{
function
getCurrentLanguage
()
{
...
...
common/lib/xmodule/xmodule/js/src/video/03_video_player.js
View file @
40d5f1e5
...
@@ -251,7 +251,7 @@ function (HTML5Video, Resizer) {
...
@@ -251,7 +251,7 @@ function (HTML5Video, Resizer) {
// Remove from the page current iFrame with HTML5 video.
// Remove from the page current iFrame with HTML5 video.
state
.
videoPlayer
.
player
.
destroy
();
state
.
videoPlayer
.
player
.
destroy
();
state
.
currentPlayerMode
=
'flash'
;
state
.
setPlayerMode
(
'flash'
)
;
console
.
log
(
'[Video info]: Changing YouTube player mode to "flash".'
);
console
.
log
(
'[Video info]: Changing YouTube player mode to "flash".'
);
...
@@ -334,7 +334,7 @@ function (HTML5Video, Resizer) {
...
@@ -334,7 +334,7 @@ function (HTML5Video, Resizer) {
methodName
,
youtubeId
;
methodName
,
youtubeId
;
if
(
if
(
this
.
currentPlayerMode
===
'html5'
&&
this
.
isHtml5Mode
()
&&
!
(
!
(
this
.
browserIsFirefox
&&
this
.
browserIsFirefox
&&
newSpeed
===
'1.0'
&&
newSpeed
===
'1.0'
&&
...
@@ -554,7 +554,7 @@ function (HTML5Video, Resizer) {
...
@@ -554,7 +554,7 @@ function (HTML5Video, Resizer) {
// For more information, please see the PR that introduced this change:
// For more information, please see the PR that introduced this change:
// https://github.com/edx/edx-platform/pull/2841
// https://github.com/edx/edx-platform/pull/2841
if
(
if
(
(
this
.
currentPlayerMode
===
'html5'
||
availablePlaybackRates
.
length
>
1
)
&&
(
this
.
isHtml5Mode
()
||
availablePlaybackRates
.
length
>
1
)
&&
this
.
videoType
===
'youtube'
this
.
videoType
===
'youtube'
)
{
)
{
if
(
availablePlaybackRates
.
length
===
1
&&
!
this
.
isTouch
)
{
if
(
availablePlaybackRates
.
length
===
1
&&
!
this
.
isTouch
)
{
...
@@ -568,7 +568,7 @@ function (HTML5Video, Resizer) {
...
@@ -568,7 +568,7 @@ function (HTML5Video, Resizer) {
_restartUsingFlash
(
this
);
_restartUsingFlash
(
this
);
}
else
if
(
availablePlaybackRates
.
length
>
1
)
{
}
else
if
(
availablePlaybackRates
.
length
>
1
)
{
this
.
currentPlayerMode
=
'html5'
;
this
.
setPlayerMode
(
'html5'
)
;
// We need to synchronize available frame rates with the ones
// We need to synchronize available frame rates with the ones
// that the user specified.
// that the user specified.
...
@@ -607,7 +607,7 @@ function (HTML5Video, Resizer) {
...
@@ -607,7 +607,7 @@ function (HTML5Video, Resizer) {
this
.
trigger
(
'videoSpeedControl.setSpeed'
,
this
.
speed
);
this
.
trigger
(
'videoSpeedControl.setSpeed'
,
this
.
speed
);
}
}
if
(
this
.
currentPlayerMode
===
'html5'
)
{
if
(
this
.
isHtml5Mode
()
)
{
this
.
videoPlayer
.
player
.
setPlaybackRate
(
this
.
speed
);
this
.
videoPlayer
.
player
.
setPlaybackRate
(
this
.
speed
);
}
}
...
...
common/lib/xmodule/xmodule/js/src/video/09_video_caption.js
View file @
40d5f1e5
...
@@ -234,6 +234,7 @@ function (Sjson, AsyncProcess) {
...
@@ -234,6 +234,7 @@ function (Sjson, AsyncProcess) {
};
};
}
}
state
.
el
.
removeClass
(
'is-captions-rendered'
);
// Fetch the captions file. If no file was specified, or if an error
// Fetch the captions file. If no file was specified, or if an error
// occurred, then we hide the captions panel, and the "CC" button
// occurred, then we hide the captions panel, and the "CC" button
this
.
fetchXHR
=
$
.
ajaxWithPrefix
({
this
.
fetchXHR
=
$
.
ajaxWithPrefix
({
...
@@ -447,9 +448,9 @@ function (Sjson, AsyncProcess) {
...
@@ -447,9 +448,9 @@ function (Sjson, AsyncProcess) {
// outline has to be drawn (tabbing) or not (mouse click).
// outline has to be drawn (tabbing) or not (mouse click).
self
.
isMouseFocus
=
false
;
self
.
isMouseFocus
=
false
;
self
.
rendered
=
true
;
self
.
rendered
=
true
;
self
.
state
.
el
.
addClass
(
'is-captions-rendered'
);
};
};
this
.
rendered
=
false
;
this
.
rendered
=
false
;
this
.
subtitlesEl
.
empty
();
this
.
subtitlesEl
.
empty
();
this
.
setSubtitlesHeight
();
this
.
setSubtitlesHeight
();
...
...
common/lib/xmodule/xmodule/video_module/video_module.py
View file @
40d5f1e5
...
@@ -10,7 +10,6 @@ in-browser HTML5 video method (when in HTML5 mode).
...
@@ -10,7 +10,6 @@ in-browser HTML5 video method (when in HTML5 mode).
- Navigational subtitles can be disabled altogether via an attribute
- Navigational subtitles can be disabled altogether via an attribute
in XML.
in XML.
"""
"""
import
os
import
json
import
json
import
logging
import
logging
from
operator
import
itemgetter
from
operator
import
itemgetter
...
@@ -36,8 +35,6 @@ from .video_utils import create_youtube_string
...
@@ -36,8 +35,6 @@ from .video_utils import create_youtube_string
from
.video_xfields
import
VideoFields
from
.video_xfields
import
VideoFields
from
.video_handlers
import
VideoStudentViewHandlers
,
VideoStudioViewHandlers
from
.video_handlers
import
VideoStudentViewHandlers
,
VideoStudioViewHandlers
from
xmodule.modulestore.inheritance
import
InheritanceKeyValueStore
from
xblock.runtime
import
KvsFieldData
from
urlparse
import
urlparse
from
urlparse
import
urlparse
def
get_ext
(
filename
):
def
get_ext
(
filename
):
...
...
lms/djangoapps/courseware/features/video.feature
View file @
40d5f1e5
...
@@ -310,3 +310,34 @@ Feature: LMS Video component
...
@@ -310,3 +310,34 @@ Feature: LMS Video component
When
I open video
"D"
When
I open video
"D"
Then
the video has rendered in
"HTML5"
mode
Then
the video has rendered in
"HTML5"
mode
And
the video does not show the captions
And
the video does not show the captions
# 27
Scenario
:
Transcripts are available on different speeds of Flash mode
Given
I am registered for the course
"test_course"
And
I have a
"subs_OEoXaMPEzfM.srt.sjson"
transcript file in assets
And
it has a video in
"Flash"
mode
Then
the video has rendered in
"Flash"
mode
And
I make sure captions are opened
And
I see
"Hi, welcome to Edx."
text in the captions
Then
I select the
"1.50"
speed
And
I see
"Hi, welcome to Edx."
text in the captions
Then
I select the
"0.75"
speed
And
I see
"Hi, welcome to Edx."
text in the captions
Then
I select the
"1.25"
speed
And
I see
"Hi, welcome to Edx."
text in the captions
# 28
Scenario
:
Elapsed time calculates correctly on different speeds of Flash mode
Given
I am registered for the course
"test_course"
And
I have a
"subs_OEoXaMPEzfM.srt.sjson"
transcript file in assets
And
it has a video in
"Flash"
mode
And
I make sure captions are opened
Then
I select the
"1.50"
speed
And
I click video button
"pause"
And
I click on caption line
"4"
, video module shows elapsed time
"7"
Then
I select the
"0.75"
speed
And
I click video button
"pause"
And
I click on caption line
"3"
, video module shows elapsed time
"9"
Then
I select the
"1.25"
speed
And
I click video button
"pause"
And
I click on caption line
"2"
, video module shows elapsed time
"4"
lms/djangoapps/courseware/features/video.py
View file @
40d5f1e5
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
# pylint: disable=C0111
# pylint: disable=C0111
from
lettuce
import
world
,
step
,
before
from
lettuce
import
world
,
step
,
before
,
after
import
json
import
json
import
os
import
os
import
time
import
time
...
@@ -26,6 +26,13 @@ HTML5_SOURCES = [
...
@@ -26,6 +26,13 @@ HTML5_SOURCES = [
'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.ogv'
,
'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.ogv'
,
]
]
FLASH_SOURCES
=
{
'youtube_id_1_0'
:
'OEoXaMPEzfM'
,
'youtube_id_0_75'
:
'JMD_ifUUfsU'
,
'youtube_id_1_25'
:
'AKqURZnYqpk'
,
'youtube_id_1_5'
:
'DYpADpL7jAY'
,
}
HTML5_SOURCES_INCORRECT
=
[
HTML5_SOURCES_INCORRECT
=
[
'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.mp99'
,
'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.mp99'
,
]
]
...
@@ -52,6 +59,11 @@ def setUp(scenario):
...
@@ -52,6 +59,11 @@ def setUp(scenario):
world
.
video_sequences
=
{}
world
.
video_sequences
=
{}
@after.each_scenario
def
tearDown
(
scenario
):
world
.
browser
.
cookies
.
delete
(
'edX_video_player_mode'
)
class
RequestHandlerWithSessionId
(
object
):
class
RequestHandlerWithSessionId
(
object
):
def
get
(
self
,
url
):
def
get
(
self
,
url
):
"""
"""
...
@@ -98,19 +110,6 @@ def get_metadata(parent_location, player_mode, data, display_name='Video'):
...
@@ -98,19 +110,6 @@ def get_metadata(parent_location, player_mode, data, display_name='Video'):
'metadata'
:
{},
'metadata'
:
{},
}
}
if
data
:
conversions
=
{
'transcripts'
:
json
.
loads
,
'download_track'
:
json
.
loads
,
'download_video'
:
json
.
loads
,
}
for
key
in
data
:
if
key
in
conversions
:
data
[
key
]
=
conversions
[
key
](
data
[
key
])
kwargs
[
'metadata'
]
.
update
(
data
)
if
player_mode
==
'html5'
:
if
player_mode
==
'html5'
:
kwargs
[
'metadata'
]
.
update
({
kwargs
[
'metadata'
]
.
update
({
'youtube_id_1_0'
:
''
,
'youtube_id_1_0'
:
''
,
...
@@ -136,6 +135,23 @@ def get_metadata(parent_location, player_mode, data, display_name='Video'):
...
@@ -136,6 +135,23 @@ def get_metadata(parent_location, player_mode, data, display_name='Video'):
'html5_sources'
:
HTML5_SOURCES_INCORRECT
'html5_sources'
:
HTML5_SOURCES_INCORRECT
})
})
if
player_mode
==
'flash'
:
kwargs
[
'metadata'
]
.
update
(
FLASH_SOURCES
)
world
.
browser
.
cookies
.
add
({
'edX_video_player_mode'
:
'flash'
})
if
data
:
conversions
=
{
'transcripts'
:
json
.
loads
,
'download_track'
:
json
.
loads
,
'download_video'
:
json
.
loads
,
}
for
key
in
data
:
if
key
in
conversions
:
data
[
key
]
=
conversions
[
key
](
data
[
key
])
kwargs
[
'metadata'
]
.
update
(
data
)
return
kwargs
return
kwargs
...
@@ -251,6 +267,14 @@ def duration():
...
@@ -251,6 +267,14 @@ def duration():
return
duration
return
duration
def
elapsed_time
():
"""
Elapsed time of the video, in seconds.
"""
elapsed_time
,
duration
=
video_time
()
return
elapsed_time
def
video_time
():
def
video_time
():
"""
"""
Return a tuple `(elapsed_time, duration)`, each in seconds.
Return a tuple `(elapsed_time, duration)`, each in seconds.
...
@@ -273,6 +297,11 @@ def parse_time_str(time_str):
...
@@ -273,6 +297,11 @@ def parse_time_str(time_str):
return
time_obj
.
tm_min
*
60
+
time_obj
.
tm_sec
return
time_obj
.
tm_min
*
60
+
time_obj
.
tm_sec
def
find_caption_line_by_data_index
(
index
):
SELECTOR
=
".subtitles > li[data-index='{index}']"
.
format
(
index
=
index
)
return
world
.
css_find
(
SELECTOR
)
.
first
@step
(
'youtube stub server (.*) YouTube API'
)
@step
(
'youtube stub server (.*) YouTube API'
)
def
configure_youtube_api
(
_step
,
action
):
def
configure_youtube_api
(
_step
,
action
):
action
=
action
.
strip
()
action
=
action
.
strip
()
...
@@ -349,7 +378,8 @@ def set_youtube_response_timeout(_step, time):
...
@@ -349,7 +378,8 @@ def set_youtube_response_timeout(_step, time):
def
video_is_rendered
(
_step
,
mode
):
def
video_is_rendered
(
_step
,
mode
):
modes
=
{
modes
=
{
'html5'
:
'video'
,
'html5'
:
'video'
,
'youtube'
:
'iframe'
'youtube'
:
'iframe'
,
'flash'
:
'iframe'
,
}
}
html_tag
=
modes
[
mode
.
lower
()]
html_tag
=
modes
[
mode
.
lower
()]
assert
world
.
css_find
(
'.video {0}'
.
format
(
html_tag
))
.
first
assert
world
.
css_find
(
'.video {0}'
.
format
(
html_tag
))
.
first
...
@@ -360,7 +390,8 @@ def video_is_rendered(_step, mode):
...
@@ -360,7 +390,8 @@ def video_is_rendered(_step, mode):
def
videos_are_rendered
(
_step
,
mode
):
def
videos_are_rendered
(
_step
,
mode
):
modes
=
{
modes
=
{
'html5'
:
'video'
,
'html5'
:
'video'
,
'youtube'
:
'iframe'
'youtube'
:
'iframe'
,
'flash'
:
'iframe'
,
}
}
html_tag
=
modes
[
mode
.
lower
()]
html_tag
=
modes
[
mode
.
lower
()]
...
@@ -423,6 +454,7 @@ def i_see_menu(_step, menu):
...
@@ -423,6 +454,7 @@ def i_see_menu(_step, menu):
@step
(
'I see "([^"]*)" text in the captions$'
)
@step
(
'I see "([^"]*)" text in the captions$'
)
def
check_text_in_the_captions
(
_step
,
text
):
def
check_text_in_the_captions
(
_step
,
text
):
world
.
wait_for_present
(
'.video.is-captions-rendered'
)
world
.
wait_for
(
lambda
_
:
world
.
css_text
(
'.subtitles'
))
world
.
wait_for
(
lambda
_
:
world
.
css_text
(
'.subtitles'
))
actual_text
=
world
.
css_text
(
'.subtitles'
)
actual_text
=
world
.
css_text
(
'.subtitles'
)
assert
(
text
in
actual_text
)
assert
(
text
in
actual_text
)
...
@@ -430,6 +462,7 @@ def check_text_in_the_captions(_step, text):
...
@@ -430,6 +462,7 @@ def check_text_in_the_captions(_step, text):
@step
(
'I see text in the captions:'
)
@step
(
'I see text in the captions:'
)
def
check_captions
(
_step
):
def
check_captions
(
_step
):
world
.
wait_for_present
(
'.video.is-captions-rendered'
)
for
index
,
video
in
enumerate
(
_step
.
hashes
):
for
index
,
video
in
enumerate
(
_step
.
hashes
):
assert
(
video
.
get
(
'text'
)
in
world
.
css_text
(
'.subtitles'
,
index
=
index
))
assert
(
video
.
get
(
'text'
)
in
world
.
css_text
(
'.subtitles'
,
index
=
index
))
...
@@ -439,12 +472,12 @@ def select_language(_step, code):
...
@@ -439,12 +472,12 @@ def select_language(_step, code):
# Make sure that all ajax requests that affects the language menu are finished.
# Make sure that all ajax requests that affects the language menu are finished.
# For example, request to get new translation etc.
# For example, request to get new translation etc.
world
.
wait_for_ajax_complete
()
world
.
wait_for_ajax_complete
()
selector
=
VIDEO_MENUS
[
"language"
]
+
' li[data-lang-code="{code}"]'
.
format
(
selector
=
VIDEO_MENUS
[
"language"
]
+
' li[data-lang-code="{code}"]'
.
format
(
code
=
code
code
=
code
)
)
world
.
css_find
(
VIDEO_BUTTONS
[
"CC"
])[
0
]
.
mouse_over
()
world
.
css_find
(
VIDEO_BUTTONS
[
"CC"
])[
0
]
.
mouse_over
()
world
.
wait_for_present
(
'.lang.open'
)
world
.
css_click
(
selector
)
world
.
css_click
(
selector
)
assert
world
.
css_has_class
(
selector
,
'active'
)
assert
world
.
css_has_class
(
selector
,
'active'
)
...
@@ -454,6 +487,7 @@ def select_language(_step, code):
...
@@ -454,6 +487,7 @@ def select_language(_step, code):
# For example, request to get new translation etc.
# For example, request to get new translation etc.
world
.
wait_for_ajax_complete
()
world
.
wait_for_ajax_complete
()
world
.
wait_for_visible
(
'.subtitles'
)
world
.
wait_for_visible
(
'.subtitles'
)
world
.
wait_for_present
(
'.video.is-captions-rendered'
)
@step
(
'I click video button "([^"]*)"$'
)
@step
(
'I click video button "([^"]*)"$'
)
...
@@ -472,10 +506,12 @@ def start_playing_video_from_n_seconds(_step, position):
...
@@ -472,10 +506,12 @@ def start_playing_video_from_n_seconds(_step, position):
@step
(
'I see duration "([^"]*)"$'
)
@step
(
'I see duration "([^"]*)"$'
)
def
i_see_duration
(
_step
,
position
):
def
i_see_duration
(
_step
,
position
):
world
.
wait_for
(
world
.
wait_for
(
func
=
lambda
_
:
duration
()
==
parse_time_str
(
position
)
,
func
=
lambda
_
:
duration
()
>
0
,
timeout
=
30
timeout
=
30
)
)
assert
duration
()
==
parse_time_str
(
position
)
@step
(
'I seek video to "([^"]*)" seconds$'
)
@step
(
'I seek video to "([^"]*)" seconds$'
)
def
seek_video_to_n_seconds
(
_step
,
seconds
):
def
seek_video_to_n_seconds
(
_step
,
seconds
):
...
@@ -507,14 +543,11 @@ def video_alignment(_step, transcript_visibility):
...
@@ -507,14 +543,11 @@ def video_alignment(_step, transcript_visibility):
set_window_dimensions
(
300
,
600
)
set_window_dimensions
(
300
,
600
)
real
,
expected
=
get_all_dimensions
()
real
,
expected
=
get_all_dimensions
()
width
=
round
(
100
*
real
[
'width'
]
/
expected
[
'width'
])
==
wrapper_width
width
=
round
(
100
*
real
[
'width'
]
/
expected
[
'width'
])
==
wrapper_width
set_window_dimensions
(
600
,
300
)
set_window_dimensions
(
600
,
300
)
real
,
expected
=
get_all_dimensions
()
real
,
expected
=
get_all_dimensions
()
height
=
abs
(
expected
[
'height'
]
-
real
[
'height'
])
<=
5
height
=
abs
(
expected
[
'height'
]
-
real
[
'height'
])
<=
5
# Restore initial window size
# Restore initial window size
set_window_dimensions
(
initial
[
'width'
],
initial
[
'height'
])
set_window_dimensions
(
initial
[
'width'
],
initial
[
'height'
])
...
@@ -569,3 +602,12 @@ def shows_captions(_step, show_captions):
...
@@ -569,3 +602,12 @@ def shows_captions(_step, show_captions):
assert
world
.
is_css_present
(
'div.video.closed'
)
assert
world
.
is_css_present
(
'div.video.closed'
)
else
:
else
:
assert
world
.
is_css_not_present
(
'div.video.closed'
)
assert
world
.
is_css_not_present
(
'div.video.closed'
)
@step
(
'I click on caption line "([^"]*)", video module shows elapsed time "([^"]*)"$'
)
def
click_on_the_caption
(
_step
,
index
,
expected_time
):
world
.
wait_for_present
(
'.video.is-captions-rendered'
)
find_caption_line_by_data_index
(
int
(
index
))
.
click
()
actual_time
=
elapsed_time
()
assert
int
(
expected_time
)
==
actual_time
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment