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
7b2d67f2
Commit
7b2d67f2
authored
May 29, 2014
by
polesye
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
BLD-1049: Allow the video player to work with redirected links.
parent
7228d41a
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
215 additions
and
276 deletions
+215
-276
CHANGELOG.rst
+2
-0
cms/djangoapps/contentstore/features/transcripts.feature
+45
-3
cms/djangoapps/contentstore/features/transcripts.py
+5
-5
cms/static/js/spec/video/transcripts/utils_spec.js
+44
-22
cms/static/js/spec/video/transcripts/videolist_spec.js
+0
-0
cms/static/js/views/video/transcripts/metadata_videolist.js
+0
-0
cms/static/js/views/video/transcripts/utils.js
+29
-30
common/lib/xmodule/xmodule/js/fixtures/video_all.html
+1
-3
common/lib/xmodule/xmodule/js/fixtures/video_html5.html
+1
-3
common/lib/xmodule/xmodule/js/spec/video/general_spec.js
+0
-47
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
+13
-68
common/lib/xmodule/xmodule/js/src/video/02_html5_video.js
+45
-32
common/lib/xmodule/xmodule/js/src/video/03_video_player.js
+1
-1
common/lib/xmodule/xmodule/tests/test_video.py
+1
-13
common/lib/xmodule/xmodule/video_module/video_module.py
+6
-13
lms/djangoapps/courseware/tests/test_video_mongo.py
+19
-30
lms/templates/video.html
+3
-6
No files found.
CHANGELOG.rst
View file @
7b2d67f2
...
@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
...
@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near
in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected.
the top. Include a label indicating the component affected.
Blades: Fix bug with incorrect link format and redirection. BLD-1049
Blades: Fix bug with incorrect RelativeTime value after XML serialization. BLD-1060
Blades: Fix bug with incorrect RelativeTime value after XML serialization. BLD-1060
LMS: Update bulk email implementation to lessen load on the database
LMS: Update bulk email implementation to lessen load on the database
...
...
cms/djangoapps/contentstore/features/transcripts.feature
View file @
7b2d67f2
...
@@ -34,14 +34,22 @@ Feature: CMS Transcripts
...
@@ -34,14 +34,22 @@ Feature: CMS Transcripts
And
I expect inputs are enabled
And
I expect inputs are enabled
#User input URL with incorrect format
#User input URL with incorrect format
And I enter a "htt
:
//link.c"
source
to
field
number
1
And I enter a "htt
p
:
//link.c"
source
to
field
number
1
Then
I see error message
"url_format"
Then
I see error message
"url_format"
# Currently we are working with 1st field. It means, that if 1st field
# Currently we are working with 1st field. It means, that if 1st field
# contain incorrect value, 2nd and 3rd fields should be disabled until
# contain incorrect value, 2nd and 3rd fields should be disabled until
# 1st field will be filled by correct correct value
# 1st field will be filled by correct correct value
And
I expect 2, 3 inputs are disabled
And
I expect 2, 3 inputs are disabled
# We are not clearing fields here,
# Because we changing same field.
#User input URL with incorrect format
And I enter a "http
:
//goo.gl/pxxZrg"
source
to
field
number
1
And I enter a "http
:
//goo.gl/pxxZrg"
source
to
field
number
2
Then
I see error message
"links_duplication"
And
I expect 1, 3 inputs are disabled
And
I clear fields
And
I expect inputs are enabled
And I enter a "http
:
//youtu.be/t_not_exist"
source
to
field
number
1
And I enter a "http
:
//youtu.be/t_not_exist"
source
to
field
number
1
Then
I do not see error message
Then
I do not see error message
And
I expect inputs are enabled
And
I expect inputs are enabled
...
@@ -699,3 +707,37 @@ Feature: CMS Transcripts
...
@@ -699,3 +707,37 @@ Feature: CMS Transcripts
And
I edit the component
And
I edit the component
Then
I see status message
"found"
Then
I see status message
"found"
#37 Uploading subtitles with different file name than file
Scenario
:
Shortened link
:
File
name
and
name
of
subs
are
different
Given
I have created a Video component
And
I edit the component
And I enter a "http
:
//goo.gl/pxxZrg"
source
to
field
number
1
And
I see status message
"not found"
And
I upload the transcripts file
"uk_transcripts.srt"
Then
I see status message
"uploaded_successfully"
And
I see value
"pxxZrg"
in the field
"Default Timed Transcript"
And
I save changes
Then
when I view the video it does show the captions
And
I edit the component
Then
I see status message
"found"
#38 Uploading subtitles with different file name than file
Scenario
:
Relative link
:
File
name
and
name
of
subs
are
different
Given
I have created a Video component
And
I edit the component
And
I enter a
"/gizmo.webm"
source to field number 1
And
I see status message
"not found"
And
I upload the transcripts file
"uk_transcripts.srt"
Then
I see status message
"uploaded_successfully"
And
I see value
"gizmo"
in the field
"Default Timed Transcript"
And
I save changes
Then
when I view the video it does show the captions
And
I edit the component
Then
I see status message
"found"
cms/djangoapps/contentstore/features/transcripts.py
View file @
7b2d67f2
...
@@ -19,6 +19,7 @@ DELAY = 0.5
...
@@ -19,6 +19,7 @@ DELAY = 0.5
ERROR_MESSAGES
=
{
ERROR_MESSAGES
=
{
'url_format'
:
u'Incorrect url format.'
,
'url_format'
:
u'Incorrect url format.'
,
'file_type'
:
u'Link types should be unique.'
,
'file_type'
:
u'Link types should be unique.'
,
'links_duplication'
:
u'Links should be unique.'
,
}
}
STATUSES
=
{
STATUSES
=
{
...
@@ -118,8 +119,7 @@ def i_see_status_message(_step, status):
...
@@ -118,8 +119,7 @@ def i_see_status_message(_step, status):
assert
world
.
css_has_text
(
SELECTORS
[
'status_bar'
],
STATUSES
[
status
])
assert
world
.
css_has_text
(
SELECTORS
[
'status_bar'
],
STATUSES
[
status
])
DOWNLOAD_BUTTON
=
TRANSCRIPTS_BUTTONS
[
"download_to_edit"
][
0
]
DOWNLOAD_BUTTON
=
TRANSCRIPTS_BUTTONS
[
"download_to_edit"
][
0
]
if
world
.
is_css_present
(
DOWNLOAD_BUTTON
,
wait_time
=
1
)
\
if
world
.
is_css_present
(
DOWNLOAD_BUTTON
,
wait_time
=
1
)
and
not
world
.
css_find
(
DOWNLOAD_BUTTON
)[
0
]
.
has_class
(
'is-disabled'
):
and
not
world
.
css_find
(
DOWNLOAD_BUTTON
)[
0
]
.
has_class
(
'is-disabled'
):
assert
_transcripts_are_downloaded
()
assert
_transcripts_are_downloaded
()
...
@@ -210,7 +210,7 @@ def check_text_in_the_captions(_step, text):
...
@@ -210,7 +210,7 @@ def check_text_in_the_captions(_step, text):
@step
(
'I see value "([^"]*)" in the field "([^"]*)"$'
)
@step
(
'I see value "([^"]*)" in the field "([^"]*)"$'
)
def
check_transcripts_field
(
_step
,
values
,
field_name
):
def
check_transcripts_field
(
_step
,
values
,
field_name
):
world
.
select_editor_tab
(
'Advanced'
)
world
.
select_editor_tab
(
'Advanced'
)
tab
=
world
.
css_find
(
'#settings-tab'
)
.
first
;
tab
=
world
.
css_find
(
'#settings-tab'
)
.
first
field_id
=
'#'
+
tab
.
find_by_xpath
(
'.//label[text()="
%
s"]'
%
field_name
.
strip
())[
0
][
'for'
]
field_id
=
'#'
+
tab
.
find_by_xpath
(
'.//label[text()="
%
s"]'
%
field_name
.
strip
())[
0
][
'for'
]
values_list
=
[
i
.
strip
()
==
world
.
css_value
(
field_id
)
for
i
in
values
.
split
(
'|'
)]
values_list
=
[
i
.
strip
()
==
world
.
css_value
(
field_id
)
for
i
in
values
.
split
(
'|'
)]
assert
any
(
values_list
)
assert
any
(
values_list
)
...
@@ -229,7 +229,7 @@ def open_tab(_step, tab_name):
...
@@ -229,7 +229,7 @@ def open_tab(_step, tab_name):
@step
(
'I set value "([^"]*)" to the field "([^"]*)"$'
)
@step
(
'I set value "([^"]*)" to the field "([^"]*)"$'
)
def
set_value_transcripts_field
(
_step
,
value
,
field_name
):
def
set_value_transcripts_field
(
_step
,
value
,
field_name
):
tab
=
world
.
css_find
(
'#settings-tab'
)
.
first
;
tab
=
world
.
css_find
(
'#settings-tab'
)
.
first
XPATH
=
'.//label[text()="{name}"]'
.
format
(
name
=
field_name
)
XPATH
=
'.//label[text()="{name}"]'
.
format
(
name
=
field_name
)
SELECTOR
=
'#'
+
tab
.
find_by_xpath
(
XPATH
)[
0
][
'for'
]
SELECTOR
=
'#'
+
tab
.
find_by_xpath
(
XPATH
)[
0
][
'for'
]
element
=
world
.
css_find
(
SELECTOR
)
.
first
element
=
world
.
css_find
(
SELECTOR
)
.
first
...
@@ -241,7 +241,7 @@ def set_value_transcripts_field(_step, value, field_name):
...
@@ -241,7 +241,7 @@ def set_value_transcripts_field(_step, value, field_name):
world
.
browser
.
execute_script
(
SCRIPT
)
world
.
browser
.
execute_script
(
SCRIPT
)
assert
world
.
css_has_value
(
SELECTOR
,
value
)
assert
world
.
css_has_value
(
SELECTOR
,
value
)
else
:
else
:
assert
False
,
'Incorrect element type.'
;
assert
False
,
'Incorrect element type.'
world
.
wait_for_ajax_complete
()
world
.
wait_for_ajax_complete
()
...
...
cms/static/js/spec/video/transcripts/utils_spec.js
View file @
7b2d67f2
...
@@ -26,7 +26,7 @@ describe('Transcripts.Utils', function () {
...
@@ -26,7 +26,7 @@ describe('Transcripts.Utils', function () {
}
(
videoId
)),
}
(
videoId
)),
html5FileName
=
'file_name'
,
html5FileName
=
'file_name'
,
html5LinksList
=
(
function
(
videoName
)
{
html5LinksList
=
(
function
(
videoName
)
{
var
videoTypes
=
[
'mp4'
,
'webm'
],
var
videoTypes
=
[
'mp4'
,
'webm'
,
'm4v'
,
'ogv'
],
links
=
[
links
=
[
'http://somelink.com/%s.%s?param=1¶m=2#hash'
,
'http://somelink.com/%s.%s?param=1¶m=2#hash'
,
'http://somelink.com/%s.%s#hash'
,
'http://somelink.com/%s.%s#hash'
,
...
@@ -34,6 +34,7 @@ describe('Transcripts.Utils', function () {
...
@@ -34,6 +34,7 @@ describe('Transcripts.Utils', function () {
'http://somelink.com/%s.%s'
,
'http://somelink.com/%s.%s'
,
'ftp://somelink.com/%s.%s'
,
'ftp://somelink.com/%s.%s'
,
'https://somelink.com/%s.%s'
,
'https://somelink.com/%s.%s'
,
'https://somelink.com/sub/sub/%s.%s'
,
'http://cdn.somecdn.net/v/%s.%s'
,
'http://cdn.somecdn.net/v/%s.%s'
,
'somelink.com/%s.%s'
,
'somelink.com/%s.%s'
,
'%s.%s'
'%s.%s'
...
@@ -48,7 +49,25 @@ describe('Transcripts.Utils', function () {
...
@@ -48,7 +49,25 @@ describe('Transcripts.Utils', function () {
return
data
;
return
data
;
}
(
html5FileName
));
}
(
html5FileName
)),
otherLinkId
=
'other_link_id'
,
otherLinksList
=
(
function
(
linkId
)
{
var
links
=
[
'http://goo.gl/%s?param=1¶m=2#hash'
,
'http://goo.gl/%s?param=1¶m=2'
,
'http://goo.gl/%s#hash'
,
'http://goo.gl/%s'
,
'http://goo.gl/%s'
,
'ftp://goo.gl/%s'
,
'https://goo.gl/%s'
,
'%s'
];
return
$
.
map
(
links
,
function
(
link
)
{
return
_str
.
sprintf
(
link
,
linkId
);
});
}
(
otherLinkId
));
describe
(
'Method: getField'
,
function
(){
describe
(
'Method: getField'
,
function
(){
var
collection
,
var
collection
,
...
@@ -107,7 +126,6 @@ describe('Transcripts.Utils', function () {
...
@@ -107,7 +126,6 @@ describe('Transcripts.Utils', function () {
});
});
describe
(
'Wrong arguments '
,
function
()
{
describe
(
'Wrong arguments '
,
function
()
{
beforeEach
(
function
(){
beforeEach
(
function
(){
spyOn
(
console
,
'log'
);
spyOn
(
console
,
'log'
);
});
});
...
@@ -124,18 +142,9 @@ describe('Transcripts.Utils', function () {
...
@@ -124,18 +142,9 @@ describe('Transcripts.Utils', function () {
expect
(
result
).
toBeUndefined
();
expect
(
result
).
toBeUndefined
();
});
});
it
(
'videoId is wrong'
,
function
()
{
var
videoId
=
'wrong_id'
,
link
=
'http://youtu.be/'
+
videoId
,
result
=
Utils
.
parseYoutubeLink
(
link
);
expect
(
result
).
toBeUndefined
();
});
var
wrongUrls
=
[
var
wrongUrls
=
[
'http://youtu.bee/'
+
videoId
,
'http://youtu.be/'
,
'http://youtu.be/'
,
'
example.com
'
,
'
/static/example
'
,
'http://google.com/somevideo.mp4'
'http://google.com/somevideo.mp4'
];
];
...
@@ -163,10 +172,20 @@ describe('Transcripts.Utils', function () {
...
@@ -163,10 +172,20 @@ describe('Transcripts.Utils', function () {
});
});
});
});
});
});
$
.
each
(
otherLinksList
,
function
(
index
,
link
)
{
it
(
link
,
function
()
{
var
result
=
Utils
.
parseHTML5Link
(
link
);
expect
(
result
).
toEqual
({
video
:
otherLinkId
,
type
:
'other'
});
});
});
});
});
describe
(
'Wrong arguments '
,
function
()
{
describe
(
'Wrong arguments '
,
function
()
{
beforeEach
(
function
(){
beforeEach
(
function
(){
spyOn
(
console
,
'log'
);
spyOn
(
console
,
'log'
);
});
});
...
@@ -184,15 +203,11 @@ describe('Transcripts.Utils', function () {
...
@@ -184,15 +203,11 @@ describe('Transcripts.Utils', function () {
});
});
var
html5WrongUrls
=
[
var
html5WrongUrls
=
[
'http://youtu.bee/'
+
videoId
,
'http://youtu.be/'
,
'http://youtu.be/'
,
'example.com'
,
'http://example.com/.mp4'
,
'http://google.com/somevideo.mp1'
,
'http://example.com/video_name.'
,
'http://google.com/somevideomp4'
,
'http://example.com/'
,
'http://google.com/somevideo_mp4'
,
'http://example.com'
'http://google.com/somevideo:mp4'
,
'http://google.com/somevideo'
,
'http://google.com/somevideo.webm_'
];
];
$
.
each
(
html5WrongUrls
,
function
(
index
,
link
)
{
$
.
each
(
html5WrongUrls
,
function
(
index
,
link
)
{
...
@@ -248,6 +263,13 @@ describe('Transcripts.Utils', function () {
...
@@ -248,6 +263,13 @@ describe('Transcripts.Utils', function () {
});
});
describe
(
'Wrong arguments '
,
function
()
{
describe
(
'Wrong arguments '
,
function
()
{
it
(
'youtube videoId is wrong'
,
function
()
{
var
videoId
=
'wrong_id'
,
link
=
'http://youtu.be/'
+
videoId
,
result
=
Utils
.
parseLink
(
link
);
expect
(
result
).
toEqual
({
mode
:
'incorrect'
});
});
it
(
'no arguments'
,
function
()
{
it
(
'no arguments'
,
function
()
{
var
result
=
Utils
.
parseLink
();
var
result
=
Utils
.
parseLink
();
...
...
cms/static/js/spec/video/transcripts/videolist_spec.js
View file @
7b2d67f2
This diff is collapsed.
Click to expand it.
cms/static/js/views/video/transcripts/metadata_videolist.js
View file @
7b2d67f2
This diff is collapsed.
Click to expand it.
cms/static/js/views/video/transcripts/utils.js
View file @
7b2d67f2
...
@@ -13,7 +13,6 @@ return (function () {
...
@@ -13,7 +13,6 @@ return (function () {
*/
*/
Storage
.
set
=
function
(
data_id
,
data
)
{
Storage
.
set
=
function
(
data_id
,
data
)
{
Storage
[
data_id
]
=
data
;
Storage
[
data_id
]
=
data
;
return
this
;
return
this
;
};
};
...
@@ -24,11 +23,9 @@ return (function () {
...
@@ -24,11 +23,9 @@ return (function () {
* @return {Any} Stored data.
* @return {Any} Stored data.
*/
*/
Storage
.
get
=
function
(
data_id
)
{
Storage
.
get
=
function
(
data_id
)
{
return
Storage
[
data_id
];
return
Storage
[
data_id
];
};
};
/**
/**
* Deletes data from the Storage object by identifier.
* Deletes data from the Storage object by identifier.
* @function
* @function
...
@@ -36,7 +33,6 @@ return (function () {
...
@@ -36,7 +33,6 @@ return (function () {
* @return {Boolean} Boolean value that indicate if data is removed.
* @return {Boolean} Boolean value that indicate if data is removed.
*/
*/
Storage
.
remove
=
function
(
data_id
)
{
Storage
.
remove
=
function
(
data_id
)
{
return
(
delete
Storage
[
data_id
]);
return
(
delete
Storage
[
data_id
]);
};
};
...
@@ -83,11 +79,10 @@ return (function () {
...
@@ -83,11 +79,10 @@ return (function () {
*/
*/
var
_youtubeParser
=
(
function
()
{
var
_youtubeParser
=
(
function
()
{
var
cache
=
{},
var
cache
=
{},
regExp
=
/
(?:
http|https|
)(?:\:\/\/
|
)(?:
www.|
)(?:
youtu
\.
be
\/
|youtube
\.
com
(?:\/
embed
\/
|
\/
v
\/
|
\/
watch
\?
v=|
\/
ytscreeningroom
\?
v=|
\/
feeds
\/
api
\/
videos
\/
|
\/
user
\S
*
[^\w\-\s]
|
\S
*
[^\w\-\s]))([\w\-]
{11})[
a-z0-9;:@#?&%=+
\/\$
_.-
]
*
/i
;
regExp
=
/
(?:
http|https|
)(?:\:\/\/
|
)(?:
www.|
)(?:
youtu
\.
be
\/
|youtube
\.
com
(?:\/
embed
\/
|
\/
v
\/
|
\/
watch
\?
v=|
\/
ytscreeningroom
\?
v=|
\/
feeds
\/
api
\/
videos
\/
|
\/
user
\S
*
[^\w\-\s]
|
\S
*
[^\w\-\s]))([\w\-]
+
)
/i
;
return
function
(
url
)
{
return
function
(
url
)
{
if
(
typeof
url
!==
'string'
)
{
if
(
typeof
url
!==
'string'
)
{
return
void
(
0
);
return
void
(
0
);
}
}
...
@@ -96,9 +91,7 @@ return (function () {
...
@@ -96,9 +91,7 @@ return (function () {
}
}
var
match
=
url
.
match
(
regExp
);
var
match
=
url
.
match
(
regExp
);
cache
[
url
]
=
(
match
&&
match
[
1
].
length
===
11
)
?
cache
[
url
]
=
(
match
)
?
match
[
1
]
:
void
(
0
);
match
[
1
]
:
void
(
0
);
return
cache
[
url
];
return
cache
[
url
];
};
};
...
@@ -109,9 +102,9 @@ return (function () {
...
@@ -109,9 +102,9 @@ return (function () {
* @function
* @function
* @param {String} url Url that should be parsed.
* @param {String} url Url that should be parsed.
* @return {
* @return {
* o
bject: Object with information about the video
* O
bject: Object with information about the video
* (file name, video type),
* (file name, video type),
* u
ndefined: when url has incorrect format or argument is
* U
ndefined: when url has incorrect format or argument is
* non-string.
* non-string.
* }
* }
*/
*/
...
@@ -132,24 +125,26 @@ return (function () {
...
@@ -132,24 +125,26 @@ return (function () {
match
;
match
;
link
.
href
=
url
;
link
.
href
=
url
;
match
=
link
.
pathname
// The regular expression try catches file name and file extension.
.
split
(
'/'
)
// '[scheme://hostname/pathname/]filename.extension[?query#hash]'
.
pop
()
match
=
link
.
pathname
.
match
(
/
\/{1}([^\/]
+
)\.([^\/]
+
)
$/
);
.
match
(
/
(
.+
)\.(
mp
?
4v
?
|webm
)
$/
);
if
(
match
)
{
if
(
match
)
{
cache
[
url
]
=
{
cache
[
url
]
=
{
video
:
match
[
1
],
video
:
match
[
1
],
type
:
match
[
2
]
type
:
match
[
2
]
};
};
}
/*else {
}
else
{
// Links like http://goo.gl/pxxZrg
// The regular expression try catches file name.
// '[scheme://hostname/pathname/]filename[?query#hash]'
match
=
link
.
pathname
.
match
(
/
\/{1}([^\/\.]
+
)
$/
);
if
(
match
)
{
cache
[
url
]
=
{
cache
[
url
]
=
{
video: link.pathname
video
:
match
[
1
],
.split('/')
.pop(),
type
:
'other'
type
:
'other'
};
};
}*/
}
}
return
cache
[
url
];
return
cache
[
url
];
};
};
...
@@ -164,25 +159,31 @@ return (function () {
...
@@ -164,25 +159,31 @@ return (function () {
* {
* {
* mode: "youtube|html5|incorrect",
* mode: "youtube|html5|incorrect",
* video: "file_name|youtube_id",
* video: "file_name|youtube_id",
* type: "youtube|mp4|webm
"
* type: "youtube|mp4|webm|other
"
* },
* },
* undefined: when argument is non-string.
* undefined: when argument is non-string.
* }
* }
*/
*/
var
_linkParser
=
function
(
url
)
{
var
_linkParser
=
function
(
url
)
{
var
result
;
var
youtubeIdLength
=
11
,
result
;
if
(
typeof
url
!==
'string'
)
{
if
(
typeof
url
!==
'string'
)
{
return
void
(
0
);
return
void
(
0
);
}
}
if
(
_youtubeParser
(
url
))
{
if
(
_youtubeParser
(
url
))
{
if
(
_youtubeParser
(
url
).
length
===
youtubeIdLength
)
{
result
=
{
result
=
{
mode
:
'youtube'
,
mode
:
'youtube'
,
video
:
_youtubeParser
(
url
),
video
:
_youtubeParser
(
url
),
type
:
'youtube'
type
:
'youtube'
};
};
}
else
{
result
=
{
mode
:
'incorrect'
};
}
}
else
if
(
_videoLinkParser
(
url
))
{
}
else
if
(
_videoLinkParser
(
url
))
{
result
=
$
.
extend
({
mode
:
'html5'
},
_videoLinkParser
(
url
));
result
=
$
.
extend
({
mode
:
'html5'
},
_videoLinkParser
(
url
));
}
else
{
}
else
{
...
@@ -197,9 +198,8 @@ return (function () {
...
@@ -197,9 +198,8 @@ return (function () {
/**
/**
* Returns short-hand youtube url.
* Returns short-hand youtube url.
* @function
* @function
* @param {string} video_id Youtube Video Id that will be added to the
* @param {String} video_id Youtube Video Id that will be added to the link.
* link.
* @return {String} Short-hand Youtube url.
* @return {string} Short-hand Youtube url.
* @examples
* @examples
* _getYoutubeLink('OEoXaMPEzfM'); => 'http://youtu.be/OEoXaMPEzfM'
* _getYoutubeLink('OEoXaMPEzfM'); => 'http://youtu.be/OEoXaMPEzfM'
*/
*/
...
@@ -210,8 +210,8 @@ return (function () {
...
@@ -210,8 +210,8 @@ return (function () {
/**
/**
* Returns list of objects with information about the passed links.
* Returns list of objects with information about the passed links.
* @function
* @function
* @param {a
rray} links List of links that will be processed.
* @param {A
rray} links List of links that will be processed.
* @returns {a
rray} List of objects.
* @returns {A
rray} List of objects.
* @examples
* @examples
* var links = [
* var links = [
* 'http://youtu.be/OEoXaMPEzfM',
* 'http://youtu.be/OEoXaMPEzfM',
...
@@ -243,7 +243,6 @@ return (function () {
...
@@ -243,7 +243,6 @@ return (function () {
}
}
};
};
/**
/**
* Synchronizes 2 Backbone collections by 'field_name' property.
* Synchronizes 2 Backbone collections by 'field_name' property.
* @function
* @function
...
...
common/lib/xmodule/xmodule/js/fixtures/video_all.html
View file @
7b2d67f2
...
@@ -15,9 +15,7 @@
...
@@ -15,9 +15,7 @@
data-transcript-translation-url=
"/transcript/translation"
data-transcript-translation-url=
"/transcript/translation"
data-transcript-available-translations-url=
"/transcript/available_translations"
data-transcript-available-translations-url=
"/transcript/available_translations"
data-sub=
"Z5KLxerq05Y"
data-sub=
"Z5KLxerq05Y"
data-mp4-source=
"xmodule/include/fixtures/test.mp4"
data-sources=
'["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"]'
data-webm-source=
"xmodule/include/fixtures/test.webm"
data-ogg-source=
"xmodule/include/fixtures/test.ogv"
data-autoplay=
"False"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-timeout=
"1500"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-api-url=
"www.youtube.com/iframe_api"
...
...
common/lib/xmodule/xmodule/js/fixtures/video_html5.html
View file @
7b2d67f2
...
@@ -15,9 +15,7 @@
...
@@ -15,9 +15,7 @@
data-transcript-translation-url=
"/transcript/translation"
data-transcript-translation-url=
"/transcript/translation"
data-transcript-available-translations-url=
"/transcript/available_translations"
data-transcript-available-translations-url=
"/transcript/available_translations"
data-sub=
"Z5KLxerq05Y"
data-sub=
"Z5KLxerq05Y"
data-mp4-source=
"xmodule/include/fixtures/test.mp4"
data-sources=
'["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"]'
data-webm-source=
"xmodule/include/fixtures/test.webm"
data-ogg-source=
"xmodule/include/fixtures/test.ogv"
data-autoplay=
"False"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-timeout=
"1500"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-api-url=
"www.youtube.com/iframe_api"
...
...
common/lib/xmodule/xmodule/js/spec/video/general_spec.js
View file @
7b2d67f2
...
@@ -79,53 +79,6 @@
...
@@ -79,53 +79,6 @@
expect
(
state
.
videos
).
toBeUndefined
();
expect
(
state
.
videos
).
toBeUndefined
();
});
});
it
(
'parse Html5 sources'
,
function
()
{
var
html5Sources
=
{
mp4
:
null
,
webm
:
null
,
ogg
:
null
},
v
=
document
.
createElement
(
'video'
);
if
(
!!
(
v
.
canPlayType
&&
v
.
canPlayType
(
'video/webm; codecs="vp8, vorbis"'
).
replace
(
/no/
,
''
)
)
)
{
html5Sources
[
'webm'
]
=
'xmodule/include/fixtures/test.webm'
;
}
if
(
!!
(
v
.
canPlayType
&&
v
.
canPlayType
(
'video/mp4; codecs="avc1.42E01E, '
+
'mp4a.40.2"'
).
replace
(
/no/
,
''
)
)
)
{
html5Sources
[
'mp4'
]
=
'xmodule/include/fixtures/test.mp4'
;
}
if
(
!!
(
v
.
canPlayType
&&
v
.
canPlayType
(
'video/ogg; codecs="theora"'
).
replace
(
/no/
,
''
)
)
)
{
html5Sources
[
'ogg'
]
=
'xmodule/include/fixtures/test.ogv'
;
}
expect
(
state
.
html5Sources
).
toEqual
(
html5Sources
);
});
it
(
'parse available video speeds'
,
function
()
{
it
(
'parse available video speeds'
,
function
()
{
var
speeds
=
jasmine
.
stubbedHtml5Speeds
;
var
speeds
=
jasmine
.
stubbedHtml5Speeds
;
...
...
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
View file @
7b2d67f2
...
@@ -70,7 +70,6 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -70,7 +70,6 @@ function (VideoPlayer, VideoStorage, i18n) {
isFlashMode
:
isFlashMode
,
isFlashMode
:
isFlashMode
,
isYoutubeType
:
isYoutubeType
,
isYoutubeType
:
isYoutubeType
,
parseSpeed
:
parseSpeed
,
parseSpeed
:
parseSpeed
,
parseVideoSources
:
parseVideoSources
,
parseYoutubeStreams
:
parseYoutubeStreams
,
parseYoutubeStreams
:
parseYoutubeStreams
,
saveState
:
saveState
,
saveState
:
saveState
,
setPlayerMode
:
setPlayerMode
,
setPlayerMode
:
setPlayerMode
,
...
@@ -280,32 +279,17 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -280,32 +279,17 @@ function (VideoPlayer, VideoStorage, i18n) {
// The function prepare HTML5 video, parse HTML5
// The function prepare HTML5 video, parse HTML5
// video sources etc.
// video sources etc.
function
_prepareHTML5Video
(
state
)
{
function
_prepareHTML5Video
(
state
)
{
state
.
parseVideoSources
(
{
mp4
:
state
.
config
.
mp4Source
,
webm
:
state
.
config
.
webmSource
,
ogg
:
state
.
config
.
oggSource
}
);
state
.
speeds
=
[
'0.75'
,
'1.0'
,
'1.25'
,
'1.50'
];
state
.
speeds
=
[
'0.75'
,
'1.0'
,
'1.25'
,
'1.50'
];
// If none of the supported video formats can be played and there is no
// We must have at least one non-YouTube video source available.
// short-hand video links, than hide the spinner and show error message.
// Otherwise, return a negative.
if
(
!
state
.
config
.
sources
.
length
)
{
if
(
state
.
html5Sources
.
webm
===
null
&&
state
.
html5Sources
.
mp4
===
null
&&
state
.
html5Sources
.
ogg
===
null
)
{
// TODO: use 1 class to work with.
state
.
el
.
find
(
'.video-player div'
).
addClass
(
'hidden'
);
state
.
el
.
find
(
'.video-player h3'
).
removeClass
(
'hidden'
);
_hideWaitPlaceholder
(
state
);
_hideWaitPlaceholder
(
state
);
state
.
el
console
.
log
(
.
find
(
'.video-player div'
)
'[Video info]: Non-youtube video sources aren
\'
t available.'
.
addClass
(
'hidden'
)
);
.
end
()
.
find
(
'.video-player h3'
)
.
removeClass
(
'hidden'
);
return
false
;
return
false
;
}
}
...
@@ -642,48 +626,6 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -642,48 +626,6 @@ function (VideoPlayer, VideoStorage, i18n) {
return
_
.
isString
(
this
.
videos
[
'1.0'
]);
return
_
.
isString
(
this
.
videos
[
'1.0'
]);
}
}
// function parseVideoSources(, mp4Source, webmSource, oggSource)
//
// Take the HTML5 sources (URLs of videos), and make them available
// explictly for each type of video format (mp4, webm, ogg).
function
parseVideoSources
(
sources
)
{
var
_this
=
this
,
v
=
document
.
createElement
(
'video'
),
sourceCodecs
=
{
mp4
:
'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'
,
webm
:
'video/webm; codecs="vp8, vorbis"'
,
ogg
:
'video/ogg; codecs="theora"'
};
this
.
html5Sources
=
{
mp4
:
null
,
webm
:
null
,
ogg
:
null
};
$
.
each
(
sources
,
function
(
name
,
source
)
{
if
(
source
&&
source
.
length
)
{
if
(
Boolean
(
v
.
canPlayType
&&
v
.
canPlayType
(
sourceCodecs
[
name
]).
replace
(
/no/
,
''
)
)
)
{
_this
.
html5Sources
[
name
]
=
source
;
}
}
});
// None of the supported video formats can be played. Hide the spinner.
if
(
!
(
_
.
compact
(
_
.
values
(
this
.
html5Sources
))))
{
_hideWaitPlaceholder
(
state
);
console
.
log
(
'[Video info]: This browser cannot play .mp4, .ogg, or .webm '
+
'files'
);
}
}
// function fetchMetadata()
// function fetchMetadata()
//
//
// When dealing with YouTube videos, we must fetch meta data that has
// When dealing with YouTube videos, we must fetch meta data that has
...
@@ -763,7 +705,10 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -763,7 +705,10 @@ function (VideoPlayer, VideoStorage, i18n) {
}
}
successHandler
=
(
$
.
isFunction
(
callback
))
?
callback
:
null
;
successHandler
=
(
$
.
isFunction
(
callback
))
?
callback
:
null
;
xhr
=
$
.
ajax
({
xhr
=
$
.
ajax
({
url
:
document
.
location
.
protocol
+
'//'
+
this
.
config
.
ytTestUrl
+
url
+
'?v=2&alt=jsonc'
,
url
:
[
document
.
location
.
protocol
,
'//'
,
this
.
config
.
ytTestUrl
,
url
,
'?v=2&alt=jsonc'
].
join
(
''
),
dataType
:
'jsonp'
,
dataType
:
'jsonp'
,
timeout
:
this
.
config
.
ytTestTimeout
,
timeout
:
this
.
config
.
ytTestTimeout
,
success
:
successHandler
success
:
successHandler
...
...
common/lib/xmodule/xmodule/js/src/video/02_html5_video.js
View file @
7b2d67f2
...
@@ -94,6 +94,22 @@ function () {
...
@@ -94,6 +94,22 @@ function () {
return
this
.
logs
;
return
this
.
logs
;
};
};
Player
.
prototype
.
showErrorMessage
=
function
()
{
this
.
el
.
find
(
'.video-player div'
)
.
addClass
(
'hidden'
)
.
end
()
.
find
(
'.video-player h3'
)
.
removeClass
(
'hidden'
)
.
end
()
.
addClass
(
'is-initialized'
)
.
find
(
'.spinner'
)
.
attr
({
'aria-hidden'
:
'true'
,
'tabindex'
:
-
1
});
};
return
Player
;
return
Player
;
/*
/*
...
@@ -113,7 +129,7 @@ function () {
...
@@ -113,7 +129,7 @@ function () {
*
*
* config = {
* config = {
*
*
* videoSources:
{}, // An object
with properties being video
* videoSources:
[], // An array
with properties being video
* // sources. The property name is the
* // sources. The property name is the
* // video format of the source. Supported
* // video format of the source. Supported
* // video formats are: 'mp4', 'webm', and
* // video formats are: 'mp4', 'webm', and
...
@@ -134,7 +150,7 @@ function () {
...
@@ -134,7 +150,7 @@ function () {
*/
*/
function
Player
(
el
,
config
)
{
function
Player
(
el
,
config
)
{
var
isTouch
=
onTouchBasedDevice
()
||
''
,
var
isTouch
=
onTouchBasedDevice
()
||
''
,
source
Str
,
_this
,
errorMessag
e
;
source
List
,
_this
,
errorMessage
,
lastSourc
e
;
this
.
logs
=
[];
this
.
logs
=
[];
// Initially we assume that el is a DOM element. If jQuery selector
// Initially we assume that el is a DOM element. If jQuery selector
...
@@ -167,63 +183,50 @@ function () {
...
@@ -167,63 +183,50 @@ function () {
// We should have at least one video source. Otherwise there is no
// We should have at least one video source. Otherwise there is no
// point to continue.
// point to continue.
if
(
!
config
.
videoSources
)
{
if
(
!
config
.
videoSources
&&
!
config
.
videoSources
.
length
)
{
return
;
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.
// Will be used in inner functions to point to the current object.
_this
=
this
;
_this
=
this
;
// Create HTML markup for individual sources of the HTML5 <video>
// Create HTML markup for individual sources of the HTML5 <video>
// element.
// element.
$
.
each
(
sourceStr
,
function
(
videoType
,
videoSource
)
{
sourceList
=
$
.
map
(
config
.
videoSources
,
function
(
source
)
{
var
url
=
_this
.
config
.
videoSources
[
videoType
];
return
[
if
(
url
&&
url
.
length
)
{
'<source '
,
sourceStr
[
videoType
]
=
'src="'
,
source
,
'<source '
+
'src="'
+
url
+
// Following hack allows to open the same video twice
// Following hack allows to open the same video twice
// https://code.google.com/p/chromium/issues/detail?id=31014
// https://code.google.com/p/chromium/issues/detail?id=31014
// Check whether the url already has a '?' inside, and if so,
// Check whether the url already has a '?' inside, and if so,
// use '&' instead of '?' to prevent breaking the url's integrity.
// use '&' instead of '?' to prevent breaking the url's integrity.
(
url
.
indexOf
(
'?'
)
==
-
1
?
'?'
:
'&'
)
+
(
new
Date
()).
getTime
()
+
(
source
.
indexOf
(
'?'
)
===
-
1
?
'?'
:
'&'
),
'" '
+
'type="video/'
+
videoType
+
'" '
+
(
new
Date
()).
getTime
(),
'" />'
'/> '
;
].
join
(
''
);
}
});
});
// We should have at least one video source. Otherwise there is no
// point to continue.
if
(
sourceStr
.
mp4
===
' '
&&
sourceStr
.
webm
===
' '
&&
sourceStr
.
ogg
===
' '
)
{
return
;
}
// Create HTML markup for the <video> element, populating it with
// Create HTML markup for the <video> element, populating it with
// sources from previous step. Because of problems with creating
// sources from previous step. Because of problems with creating
// video element via jquery (http://bugs.jquery.com/ticket/9174) we
// video element via jquery (http://bugs.jquery.com/ticket/9174) we
// create it using native JS.
// create it using native JS.
this
.
video
=
document
.
createElement
(
'video'
);
this
.
video
=
document
.
createElement
(
'video'
);
errorMessage
=
gettext
(
'This browser cannot play .mp4, .ogg, or .webm files.'
)
+
gettext
(
'Try using a different browser, such as Google Chrome.'
);
errorMessage
=
[
this
.
video
.
innerHTML
=
_
.
values
(
sourceStr
).
join
(
''
)
+
errorMessage
;
gettext
(
'This browser cannot play .mp4, .ogg, or .webm files.'
),
gettext
(
'Try using a different browser, such as Google Chrome.'
)
].
join
(
''
);
this
.
video
.
innerHTML
=
sourceList
.
join
(
''
)
+
errorMessage
;
// Get the jQuery object, and set the player state to UNSTARTED.
// Get the jQuery object, and set the player state to UNSTARTED.
// The player state is used by other parts of the VideoPlayer to
// The player state is used by other parts of the VideoPlayer to
// determine what the video is currently doing.
// determine what the video is currently doing.
this
.
videoEl
=
$
(
this
.
video
);
this
.
videoEl
=
$
(
this
.
video
);
lastSource
=
this
.
videoEl
.
find
(
'source'
).
last
();
lastSource
.
on
(
'error'
,
this
.
showErrorMessage
.
bind
(
this
));
if
(
/iP
(
hone|od
)
/i
.
test
(
isTouch
[
0
]))
{
if
(
/iP
(
hone|od
)
/i
.
test
(
isTouch
[
0
]))
{
this
.
videoEl
.
prop
(
'controls'
,
true
);
this
.
videoEl
.
prop
(
'controls'
,
true
);
}
}
...
@@ -253,6 +256,7 @@ function () {
...
@@ -253,6 +256,7 @@ function () {
'durationchange'
,
'volumechange'
'durationchange'
,
'volumechange'
];
];
this
.
debug
=
false
;
$
.
each
(
events
,
function
(
index
,
eventName
)
{
$
.
each
(
events
,
function
(
index
,
eventName
)
{
_this
.
video
.
addEventListener
(
eventName
,
function
()
{
_this
.
video
.
addEventListener
(
eventName
,
function
()
{
_this
.
logs
.
push
({
_this
.
logs
.
push
({
...
@@ -260,6 +264,15 @@ function () {
...
@@ -260,6 +264,15 @@ function () {
'state'
:
_this
.
playerState
'state'
:
_this
.
playerState
});
});
if
(
_this
.
debug
)
{
console
.
log
(
'event name:'
,
eventName
,
'state:'
,
_this
.
playerState
,
'readyState:'
,
_this
.
video
.
readyState
,
'networkState:'
,
_this
.
video
.
networkState
);
}
el
.
trigger
(
'html5:'
+
eventName
,
arguments
);
el
.
trigger
(
'html5:'
+
eventName
,
arguments
);
});
});
});
});
...
...
common/lib/xmodule/xmodule/js/src/video/03_video_player.js
View file @
7b2d67f2
...
@@ -142,7 +142,7 @@ function (HTML5Video, Resizer) {
...
@@ -142,7 +142,7 @@ function (HTML5Video, Resizer) {
if
(
state
.
videoType
===
'html5'
)
{
if
(
state
.
videoType
===
'html5'
)
{
state
.
videoPlayer
.
player
=
new
HTML5Video
.
Player
(
state
.
el
,
{
state
.
videoPlayer
.
player
=
new
HTML5Video
.
Player
(
state
.
el
,
{
playerVars
:
state
.
videoPlayer
.
playerVars
,
playerVars
:
state
.
videoPlayer
.
playerVars
,
videoSources
:
state
.
html5S
ources
,
videoSources
:
state
.
config
.
s
ources
,
events
:
{
events
:
{
onReady
:
state
.
videoPlayer
.
onReady
,
onReady
:
state
.
videoPlayer
.
onReady
,
onStateChange
:
state
.
videoPlayer
.
onStateChange
onStateChange
:
state
.
videoPlayer
.
onStateChange
...
...
common/lib/xmodule/xmodule/tests/test_video.py
View file @
7b2d67f2
...
@@ -20,7 +20,7 @@ from mock import Mock
...
@@ -20,7 +20,7 @@ from mock import Mock
from
.
import
LogicTest
from
.
import
LogicTest
from
lxml
import
etree
from
lxml
import
etree
from
opaque_keys.edx.locations
import
Location
from
opaque_keys.edx.locations
import
Location
from
xmodule.video_module
import
VideoDescriptor
,
create_youtube_string
,
get_ext
from
xmodule.video_module
import
VideoDescriptor
,
create_youtube_string
from
.test_import
import
DummySystem
from
.test_import
import
DummySystem
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
...
@@ -107,18 +107,6 @@ class VideoModuleTest(LogicTest):
...
@@ -107,18 +107,6 @@ class VideoModuleTest(LogicTest):
'1.50'
:
''
}
'1.50'
:
''
}
)
)
def
test_get_ext
(
self
):
"""Test get the file's extension in a url without query string."""
filename_str
=
'http://www.example.com/path/video.mp4'
output
=
get_ext
(
filename_str
)
self
.
assertEqual
(
output
,
'mp4'
)
def
test_get_ext_with_query_string
(
self
):
"""Test get the file's extension in a url with query string."""
filename_str
=
'http://www.example.com/path/video.mp4?param1=1&p2=2'
output
=
get_ext
(
filename_str
)
self
.
assertEqual
(
output
,
'mp4'
)
class
VideoDescriptorTest
(
unittest
.
TestCase
):
class
VideoDescriptorTest
(
unittest
.
TestCase
):
"""Test for VideoDescriptor"""
"""Test for VideoDescriptor"""
...
...
common/lib/xmodule/xmodule/video_module/video_module.py
View file @
7b2d67f2
...
@@ -35,14 +35,6 @@ from .video_utils import create_youtube_string
...
@@ -35,14 +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
urlparse
import
urlparse
def
get_ext
(
filename
):
# Prevent incorrectly parsing urls like 'http://abc.com/path/video.mp4?xxxx'.
path
=
urlparse
(
filename
)
.
path
return
path
.
rpartition
(
'.'
)[
-
1
]
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
_
=
lambda
text
:
text
_
=
lambda
text
:
text
...
@@ -97,15 +89,15 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule):
...
@@ -97,15 +89,15 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule):
def
get_html
(
self
):
def
get_html
(
self
):
track_url
=
None
track_url
=
None
download_video_link
=
None
transcript_download_format
=
self
.
transcript_download_format
transcript_download_format
=
self
.
transcript_download_format
sources
=
filter
(
None
,
self
.
html5_sources
)
sources
=
{
get_ext
(
src
):
src
for
src
in
self
.
html5_sources
}
if
self
.
download_video
:
if
self
.
download_video
:
if
self
.
source
:
if
self
.
source
:
sources
[
'main'
]
=
self
.
source
download_video_link
=
self
.
source
elif
self
.
html5_sources
:
elif
self
.
html5_sources
:
sources
[
'main'
]
=
self
.
html5_sources
[
0
]
download_video_link
=
self
.
html5_sources
[
0
]
if
self
.
download_track
:
if
self
.
download_track
:
if
self
.
track
:
if
self
.
track
:
...
@@ -149,7 +141,8 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule):
...
@@ -149,7 +141,8 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule):
'handout'
:
self
.
handout
,
'handout'
:
self
.
handout
,
'id'
:
self
.
location
.
html_id
(),
'id'
:
self
.
location
.
html_id
(),
'show_captions'
:
json
.
dumps
(
self
.
show_captions
),
'show_captions'
:
json
.
dumps
(
self
.
show_captions
),
'sources'
:
sources
,
'download_video_link'
:
download_video_link
,
'sources'
:
json
.
dumps
(
sources
),
'speed'
:
json
.
dumps
(
self
.
speed
),
'speed'
:
json
.
dumps
(
self
.
speed
),
'general_speed'
:
self
.
global_speed
,
'general_speed'
:
self
.
global_speed
,
'saved_video_position'
:
self
.
saved_video_position
.
total_seconds
(),
'saved_video_position'
:
self
.
saved_video_position
.
total_seconds
(),
...
...
lms/djangoapps/courseware/tests/test_video_mongo.py
View file @
7b2d67f2
...
@@ -26,12 +26,7 @@ class TestVideoYouTube(TestVideo):
...
@@ -26,12 +26,7 @@ class TestVideoYouTube(TestVideo):
def
test_video_constructor
(
self
):
def
test_video_constructor
(
self
):
"""Make sure that all parameters extracted correctly from xml"""
"""Make sure that all parameters extracted correctly from xml"""
context
=
self
.
item_descriptor
.
render
(
'student_view'
)
.
content
context
=
self
.
item_descriptor
.
render
(
'student_view'
)
.
content
sources
=
json
.
dumps
([
u'example.mp4'
,
u'example.webm'
])
sources
=
{
'main'
:
u'example.mp4'
,
u'mp4'
:
u'example.mp4'
,
u'webm'
:
u'example.webm'
,
}
expected_context
=
{
expected_context
=
{
'ajax_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
ajax_url
+
'/save_user_state'
,
'ajax_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
ajax_url
+
'/save_user_state'
,
...
@@ -42,6 +37,7 @@ class TestVideoYouTube(TestVideo):
...
@@ -42,6 +37,7 @@ class TestVideoYouTube(TestVideo):
'id'
:
self
.
item_descriptor
.
location
.
html_id
(),
'id'
:
self
.
item_descriptor
.
location
.
html_id
(),
'show_captions'
:
'true'
,
'show_captions'
:
'true'
,
'handout'
:
None
,
'handout'
:
None
,
'download_video_link'
:
u'example.mp4'
,
'sources'
:
sources
,
'sources'
:
sources
,
'speed'
:
'null'
,
'speed'
:
'null'
,
'general_speed'
:
1.0
,
'general_speed'
:
1.0
,
...
@@ -93,13 +89,8 @@ class TestVideoNonYouTube(TestVideo):
...
@@ -93,13 +89,8 @@ class TestVideoNonYouTube(TestVideo):
"""Make sure that if the 'youtube' attribute is omitted in XML, then
"""Make sure that if the 'youtube' attribute is omitted in XML, then
the template generates an empty string for the YouTube streams.
the template generates an empty string for the YouTube streams.
"""
"""
sources
=
{
'main'
:
u'example.mp4'
,
u'mp4'
:
u'example.mp4'
,
u'webm'
:
u'example.webm'
,
}
context
=
self
.
item_descriptor
.
render
(
'student_view'
)
.
content
context
=
self
.
item_descriptor
.
render
(
'student_view'
)
.
content
sources
=
json
.
dumps
([
u'example.mp4'
,
u'example.webm'
])
expected_context
=
{
expected_context
=
{
'ajax_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
ajax_url
+
'/save_user_state'
,
'ajax_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
ajax_url
+
'/save_user_state'
,
...
@@ -107,6 +98,7 @@ class TestVideoNonYouTube(TestVideo):
...
@@ -107,6 +98,7 @@ class TestVideoNonYouTube(TestVideo):
'show_captions'
:
'true'
,
'show_captions'
:
'true'
,
'handout'
:
None
,
'handout'
:
None
,
'display_name'
:
u'A Name'
,
'display_name'
:
u'A Name'
,
'download_video_link'
:
u'example.mp4'
,
'end'
:
3610.0
,
'end'
:
3610.0
,
'id'
:
self
.
item_descriptor
.
location
.
html_id
(),
'id'
:
self
.
item_descriptor
.
location
.
html_id
(),
'sources'
:
sources
,
'sources'
:
sources
,
...
@@ -148,7 +140,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -148,7 +140,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
METADATA
=
{}
METADATA
=
{}
def
setUp
(
self
):
def
setUp
(
self
):
self
.
setup_course
()
;
self
.
setup_course
()
def
test_get_html_track
(
self
):
def
test_get_html_track
(
self
):
SOURCE_XML
=
"""
SOURCE_XML
=
"""
...
@@ -201,19 +193,17 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -201,19 +193,17 @@ class TestGetHtmlMethod(BaseTestXmodule):
'transcripts'
:
'<transcript language="uk" src="ukrainian.srt" />'
,
'transcripts'
:
'<transcript language="uk" src="ukrainian.srt" />'
,
},
},
]
]
sources
=
json
.
dumps
([
u'example.mp4'
,
u'example.webm'
])
expected_context
=
{
expected_context
=
{
'data_dir'
:
getattr
(
self
,
'data_dir'
,
None
),
'data_dir'
:
getattr
(
self
,
'data_dir'
,
None
),
'show_captions'
:
'true'
,
'show_captions'
:
'true'
,
'handout'
:
None
,
'handout'
:
None
,
'display_name'
:
u'A Name'
,
'display_name'
:
u'A Name'
,
'download_video_link'
:
u'example.mp4'
,
'end'
:
3610.0
,
'end'
:
3610.0
,
'id'
:
None
,
'id'
:
None
,
'sources'
:
{
'sources'
:
sources
,
'main'
:
u'example.mp4'
,
u'mp4'
:
u'example.mp4'
,
u'webm'
:
u'example.webm'
},
'start'
:
3603.0
,
'start'
:
3603.0
,
'saved_video_position'
:
0.0
,
'saved_video_position'
:
0.0
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'sub'
:
u'a_sub_file.srt.sjson'
,
...
@@ -284,9 +274,8 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -284,9 +274,8 @@ class TestGetHtmlMethod(BaseTestXmodule):
<source src="example.webm"/>
<source src="example.webm"/>
"""
,
"""
,
'result'
:
{
'result'
:
{
'main'
:
u'example_source.mp4'
,
'download_video_link'
:
u'example_source.mp4'
,
u'mp4'
:
u'example.mp4'
,
'sources'
:
json
.
dumps
([
u'example.mp4'
,
u'example.webm'
]),
u'webm'
:
u'example.webm'
,
},
},
},
},
{
{
...
@@ -297,9 +286,8 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -297,9 +286,8 @@ class TestGetHtmlMethod(BaseTestXmodule):
<source src="example.webm"/>
<source src="example.webm"/>
"""
,
"""
,
'result'
:
{
'result'
:
{
'main'
:
u'example.mp4'
,
'download_video_link'
:
u'example.mp4'
,
u'mp4'
:
u'example.mp4'
,
'sources'
:
json
.
dumps
([
u'example.mp4'
,
u'example.webm'
]),
u'webm'
:
u'example.webm'
,
},
},
},
},
{
{
...
@@ -318,20 +306,20 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -318,20 +306,20 @@ class TestGetHtmlMethod(BaseTestXmodule):
<source src="example.webm"/>
<source src="example.webm"/>
"""
,
"""
,
'result'
:
{
'result'
:
{
u'mp4'
:
u'example.mp4'
,
'sources'
:
json
.
dumps
([
u'example.mp4'
,
u'example.webm'
]),
u'webm'
:
u'example.webm'
,
},
},
},
},
]
]
expected
_context
=
{
initial
_context
=
{
'data_dir'
:
getattr
(
self
,
'data_dir'
,
None
),
'data_dir'
:
getattr
(
self
,
'data_dir'
,
None
),
'show_captions'
:
'true'
,
'show_captions'
:
'true'
,
'handout'
:
None
,
'handout'
:
None
,
'display_name'
:
u'A Name'
,
'display_name'
:
u'A Name'
,
'download_video_link'
:
None
,
'end'
:
3610.0
,
'end'
:
3610.0
,
'id'
:
None
,
'id'
:
None
,
'sources'
:
None
,
'sources'
:
'[]'
,
'speed'
:
'null'
,
'speed'
:
'null'
,
'general_speed'
:
1.0
,
'general_speed'
:
1.0
,
'start'
:
3603.0
,
'start'
:
3603.0
,
...
@@ -358,6 +346,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -358,6 +346,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
self
.
initialize_module
(
data
=
DATA
)
self
.
initialize_module
(
data
=
DATA
)
context
=
self
.
item_descriptor
.
render
(
'student_view'
)
.
content
context
=
self
.
item_descriptor
.
render
(
'student_view'
)
.
content
expected_context
=
dict
(
initial_context
)
expected_context
.
update
({
expected_context
.
update
({
'transcript_translation_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
handler_url
(
'transcript_translation_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
handler_url
(
self
.
item_descriptor
,
'transcript'
,
'translation'
self
.
item_descriptor
,
'transcript'
,
'translation'
...
@@ -366,9 +355,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -366,9 +355,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
self
.
item_descriptor
,
'transcript'
,
'available_translations'
self
.
item_descriptor
,
'transcript'
,
'available_translations'
)
.
rstrip
(
'/?'
),
)
.
rstrip
(
'/?'
),
'ajax_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
ajax_url
+
'/save_user_state'
,
'ajax_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
ajax_url
+
'/save_user_state'
,
'sources'
:
data
[
'result'
],
'id'
:
self
.
item_descriptor
.
location
.
html_id
(),
'id'
:
self
.
item_descriptor
.
location
.
html_id
(),
})
})
expected_context
.
update
(
data
[
'result'
])
self
.
assertEqual
(
self
.
assertEqual
(
context
,
context
,
...
@@ -385,7 +374,7 @@ class TestVideoDescriptorInitialization(BaseTestXmodule):
...
@@ -385,7 +374,7 @@ class TestVideoDescriptorInitialization(BaseTestXmodule):
METADATA
=
{}
METADATA
=
{}
def
setUp
(
self
):
def
setUp
(
self
):
self
.
setup_course
()
;
self
.
setup_course
()
def
test_source_not_in_html5sources
(
self
):
def
test_source_not_in_html5sources
(
self
):
metadata
=
{
metadata
=
{
...
...
lms/templates/video.html
View file @
7b2d67f2
...
@@ -13,10 +13,7 @@
...
@@ -13,10 +13,7 @@
${'
data-sub=
"{}"
'.
format
(
sub
)
if
sub
else
''}
${'
data-sub=
"{}"
'.
format
(
sub
)
if
sub
else
''}
${'
data-autoplay=
"{}"
'.
format
(
autoplay
)
if
autoplay
else
''}
${'
data-autoplay=
"{}"
'.
format
(
autoplay
)
if
autoplay
else
''}
${'
data-mp4-source=
"{}"
'.
format
(
sources
.
get
('
mp4
'))
if
sources
.
get
('
mp4
')
else
''}
data-sources=
'${sources}'
${'
data-webm-source=
"{}"
'.
format
(
sources
.
get
('
webm
'))
if
sources
.
get
('
webm
')
else
''}
${'
data-ogg-source=
"{}"
'.
format
(
sources
.
get
('
ogv
'))
if
sources
.
get
('
ogv
')
else
''}
data-save-state-url=
"${ajax_url}"
data-save-state-url=
"${ajax_url}"
data-caption-data-dir=
"${data_dir}"
data-caption-data-dir=
"${data_dir}"
data-show-captions=
"${show_captions}"
data-show-captions=
"${show_captions}"
...
@@ -106,9 +103,9 @@
...
@@ -106,9 +103,9 @@
<div
class=
"focus_grabber last"
></div>
<div
class=
"focus_grabber last"
></div>
<ul
class=
"wrapper-downloads"
>
<ul
class=
"wrapper-downloads"
>
% if
sources.get('main')
:
% if
download_video_link
:
<li
class=
"video-sources video-download-button"
>
<li
class=
"video-sources video-download-button"
>
${('
<a
href=
"%s"
>
' + _('Download video') + '
</a>
') %
sources.get('main')
}
${('
<a
href=
"%s"
>
' + _('Download video') + '
</a>
') %
download_video_link
}
</li>
</li>
% endif
% endif
% if track:
% if track:
...
...
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