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
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
623 additions
and
596 deletions
+623
-596
CHANGELOG.rst
+2
-0
cms/djangoapps/contentstore/features/transcripts.feature
+45
-3
cms/djangoapps/contentstore/features/transcripts.py
+9
-9
cms/static/js/spec/video/transcripts/utils_spec.js
+44
-22
cms/static/js/spec/video/transcripts/videolist_spec.js
+148
-62
cms/static/js/views/video/transcripts/metadata_videolist.js
+140
-138
cms/static/js/views/video/transcripts/utils.js
+140
-141
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
+49
-36
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
+20
-31
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
=
{
...
@@ -43,7 +44,7 @@ TRANSCRIPTS_BUTTONS = {
...
@@ -43,7 +44,7 @@ TRANSCRIPTS_BUTTONS = {
'import'
:
(
'.setting-import'
,
'Import YouTube Transcript'
),
'import'
:
(
'.setting-import'
,
'Import YouTube Transcript'
),
'download_to_edit'
:
(
'.setting-download'
,
'Download Transcript for Editing'
),
'download_to_edit'
:
(
'.setting-download'
,
'Download Transcript for Editing'
),
'disabled_download_to_edit'
:
(
'.setting-download.is-disabled'
,
'Download Transcript for Editing'
),
'disabled_download_to_edit'
:
(
'.setting-download.is-disabled'
,
'Download Transcript for Editing'
),
'upload_new_timed_transcripts'
:
(
'.setting-upload'
,
'Upload New Transcript'
),
'upload_new_timed_transcripts'
:
(
'.setting-upload'
,
'Upload New Transcript'
),
'replace'
:
(
'.setting-replace'
,
'Yes, replace the edX transcript with the YouTube transcript'
),
'replace'
:
(
'.setting-replace'
,
'Yes, replace the edX transcript with the YouTube transcript'
),
'choose'
:
(
'.setting-choose'
,
'Timed Transcript from {}'
),
'choose'
:
(
'.setting-choose'
,
'Timed Transcript from {}'
),
'use_existing'
:
(
'.setting-use-existing'
,
'Use Current Transcript'
),
'use_existing'
:
(
'.setting-use-existing'
,
'Use Current Transcript'
),
...
@@ -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,19 +229,19 @@ def open_tab(_step, tab_name):
...
@@ -229,19 +229,19 @@ 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
if
element
[
'type'
]
==
'text'
:
if
element
[
'type'
]
==
'text'
:
SCRIPT
=
'$("{selector}").val("{value}").change()'
.
format
(
SCRIPT
=
'$("{selector}").val("{value}").change()'
.
format
(
selector
=
SELECTOR
,
selector
=
SELECTOR
,
value
=
value
value
=
value
)
)
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
define
(
define
(
[
[
"jquery"
,
"underscore"
,
'jquery'
,
'underscore'
,
"js/views/video/transcripts/utils"
,
"js/views/video/transcripts/metadata_videolist"
,
'js/views/video/transcripts/utils'
,
"js/views/metadata"
,
"js/models/metadata"
,
"js/views/abstract_editor"
,
'js/views/video/transcripts/metadata_videolist'
,
'js/models/metadata'
,
"sinon"
,
"xmodule"
,
"jasmine-jquery"
'js/views/abstract_editor'
,
'sinon'
,
'xmodule'
,
'jasmine-jquery'
],
],
function
(
$
,
_
,
Utils
,
VideoList
,
MetadataView
,
MetadataModel
,
AbstractEditor
,
sinon
)
{
function
(
$
,
_
,
Utils
,
VideoList
,
MetadataModel
,
AbstractEditor
,
sinon
)
{
'use strict'
;
describe
(
'CMS.Views.Metadata.VideoList'
,
function
()
{
describe
(
'CMS.Views.Metadata.VideoList'
,
function
()
{
var
videoListEntryTemplate
=
readFixtures
(
var
videoListEntryTemplate
=
readFixtures
(
'video/transcripts/metadata-videolist-entry.underscore'
'video/transcripts/metadata-videolist-entry.underscore'
...
@@ -14,19 +16,19 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -14,19 +16,19 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
component_locator
=
'component_locator'
,
component_locator
=
'component_locator'
,
videoList
=
[
videoList
=
[
{
{
mode
:
"youtube"
,
mode
:
'youtube'
,
type
:
"youtube"
,
type
:
'youtube'
,
video
:
"12345678901"
video
:
'12345678901'
},
},
{
{
mode
:
"html5"
,
mode
:
'html5'
,
type
:
"mp4"
,
type
:
'mp4'
,
video
:
"video"
video
:
'video'
},
},
{
{
mode
:
"html5"
,
mode
:
'html5'
,
type
:
"webm"
,
type
:
'webm'
,
video
:
"video"
video
:
'video'
}
}
],
],
modelStub
=
{
modelStub
=
{
...
@@ -54,7 +56,7 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -54,7 +56,7 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
sinonXhr
=
sinon
.
fakeServer
.
create
();
sinonXhr
=
sinon
.
fakeServer
.
create
();
sinonXhr
.
respondWith
([
sinonXhr
.
respondWith
([
200
,
200
,
{
"Content-Type"
:
"application/json"
},
{
'Content-Type'
:
'application/json'
},
response
response
]);
]);
...
@@ -65,15 +67,15 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -65,15 +67,15 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
'data-locator'
:
component_locator
'data-locator'
:
component_locator
}),
}),
model
=
new
MetadataModel
(
modelStub
),
model
=
new
MetadataModel
(
modelStub
),
videoList
,
$el
;
$el
;
setFixtures
(
tpl
);
setFixtures
(
tpl
);
appendSetFixtures
(
appendSetFixtures
(
$
(
"<script>"
,
$
(
'<script>'
,
{
{
id
:
"metadata-videolist-entry"
,
id
:
'metadata-videolist-entry'
,
type
:
"text/template"
type
:
'text/template'
}
}
).
text
(
videoListEntryTemplate
)
).
text
(
videoListEntryTemplate
)
);
);
...
@@ -147,7 +149,7 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -147,7 +149,7 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
}
}
return
flag
;
return
flag
;
},
"Ajax Timeout"
,
750
);
},
'Ajax Timeout'
,
750
);
runs
(
expectFunc
);
runs
(
expectFunc
);
};
};
...
@@ -189,21 +191,21 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -189,21 +191,21 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
it
(
'is rendered with opened extra videos bar'
,
function
()
{
it
(
'is rendered with opened extra videos bar'
,
function
()
{
var
videoListLength
=
[
var
videoListLength
=
[
{
{
mode
:
"youtube"
,
mode
:
'youtube'
,
type
:
"youtube"
,
type
:
'youtube'
,
video
:
"12345678901"
video
:
'12345678901'
},
},
{
{
mode
:
"html5"
,
mode
:
'html5'
,
type
:
"mp4"
,
type
:
'mp4'
,
video
:
"video"
video
:
'video'
}
}
],
],
videoListHtml5mode
=
[
videoListHtml5mode
=
[
{
{
mode
:
"html5"
,
mode
:
'html5'
,
type
:
"mp4"
,
type
:
'mp4'
,
video
:
"video"
video
:
'video'
}
}
];
];
...
@@ -240,9 +242,9 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -240,9 +242,9 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
it
(
'is rendered without opened extra videos bar'
,
function
()
{
it
(
'is rendered without opened extra videos bar'
,
function
()
{
var
videoList
=
[
var
videoList
=
[
{
{
mode
:
"youtube"
,
mode
:
'youtube'
,
type
:
"youtube"
,
type
:
'youtube'
,
video
:
"12345678901"
video
:
'12345678901'
}
}
];
];
...
@@ -263,6 +265,59 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -263,6 +265,59 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
});
});
describe
(
'isUniqOtherVideos'
,
function
()
{
it
(
'Unique data - return true'
,
function
()
{
var
data
=
videoList
.
concat
([{
mode
:
'html5'
,
type
:
'other'
,
video
:
'pxxZrg'
}]);
waitsForResponse
(
function
()
{
var
result
=
view
.
isUniqOtherVideos
(
data
);
expect
(
result
).
toBe
(
true
);
});
});
it
(
'Not Unique data - return false'
,
function
()
{
var
data
=
[
{
mode
:
'html5'
,
type
:
'mp4'
,
video
:
'video'
},
{
mode
:
'html5'
,
type
:
'mp4'
,
video
:
'video'
},
{
mode
:
'html5'
,
type
:
'other'
,
video
:
'pxxZrg'
},
{
mode
:
'html5'
,
type
:
'other'
,
video
:
'pxxZrg'
},
{
mode
:
'youtube'
,
type
:
'youtube'
,
video
:
'12345678901'
}
];
waitsForResponse
(
function
()
{
var
result
=
view
.
isUniqOtherVideos
(
data
);
expect
(
result
).
toBe
(
false
);
});
});
});
describe
(
'isUniqVideoTypes'
,
function
()
{
describe
(
'isUniqVideoTypes'
,
function
()
{
it
(
'Unique data - return true'
,
function
()
{
it
(
'Unique data - return true'
,
function
()
{
...
@@ -279,19 +334,24 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -279,19 +334,24 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
it
(
'Not Unique data - return false'
,
function
()
{
it
(
'Not Unique data - return false'
,
function
()
{
var
data
=
[
var
data
=
[
{
{
mode
:
"html5"
,
mode
:
'html5'
,
type
:
"mp4"
,
type
:
'mp4'
,
video
:
"video"
video
:
'video'
},
},
{
{
mode
:
"html5"
,
mode
:
'html5'
,
type
:
"mp4"
,
type
:
'mp4'
,
video
:
"video"
video
:
'video'
},
},
{
{
mode
:
"youtube"
,
mode
:
'html5'
,
type
:
"youtube"
,
type
:
'other'
,
video
:
"12345678901"
video
:
'pxxZrg'
},
{
mode
:
'youtube'
,
type
:
'youtube'
,
video
:
'12345678901'
}
}
];
];
...
@@ -304,23 +364,27 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -304,23 +364,27 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
});
});
describe
(
'checkIsUniqVideoTypes'
,
function
()
{
describe
(
'checkIsUniqVideoTypes'
,
function
()
{
it
(
'Error is shown'
,
function
()
{
it
(
'Error is shown'
,
function
()
{
var
data
=
[
var
data
=
[
{
{
mode
:
"html5"
,
mode
:
'html5'
,
type
:
"mp4"
,
type
:
'mp4'
,
video
:
"video"
video
:
'video'
},
},
{
{
mode
:
"html5"
,
mode
:
'html5'
,
type
:
"mp4"
,
type
:
'mp4'
,
video
:
"video"
video
:
'video'
},
},
{
{
mode
:
"youtube"
,
mode
:
'html5'
,
type
:
"youtube"
,
type
:
'other'
,
video
:
"12345678901"
video
:
'pxxZrg'
},
{
mode
:
'youtube'
,
type
:
'youtube'
,
video
:
'12345678901'
}
}
];
];
...
@@ -350,7 +414,7 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -350,7 +414,7 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
spyOn
(
view
,
'checkIsUniqVideoTypes'
).
andReturn
(
true
);
spyOn
(
view
,
'checkIsUniqVideoTypes'
).
andReturn
(
true
);
});
});
it
(
'Error message
are
shown'
,
function
()
{
it
(
'Error message
is
shown'
,
function
()
{
waitsForResponse
(
function
()
{
waitsForResponse
(
function
()
{
var
data
=
{
mode
:
'incorrect'
},
var
data
=
{
mode
:
'incorrect'
},
result
=
view
.
checkValidity
(
data
,
true
);
result
=
view
.
checkValidity
(
data
,
true
);
...
@@ -361,7 +425,7 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -361,7 +425,7 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
});
});
});
});
it
(
'Error message
are
shown when flag is not passed'
,
function
()
{
it
(
'Error message
is
shown when flag is not passed'
,
function
()
{
waitsForResponse
(
function
()
{
waitsForResponse
(
function
()
{
var
data
=
{
mode
:
'incorrect'
},
var
data
=
{
mode
:
'incorrect'
},
result
=
view
.
checkValidity
(
data
);
result
=
view
.
checkValidity
(
data
);
...
@@ -435,6 +499,11 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -435,6 +499,11 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
mode
:
'html5'
,
mode
:
'html5'
,
type
:
'mp4'
,
type
:
'mp4'
,
video
:
'video'
video
:
'video'
},
{
mode
:
'html5'
,
type
:
'other'
,
video
:
'pxxZrg'
}
}
];
];
...
@@ -442,6 +511,7 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -442,6 +511,7 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
view
.
setValueInEditor
([
view
.
setValueInEditor
([
'http://youtu.be/12345678901'
,
'http://youtu.be/12345678901'
,
'video.mp4'
,
'video.mp4'
,
'http://goo.gl/pxxZrg'
,
'video'
'video'
]);
]);
expect
(
view
).
assertIsCorrectVideoList
(
value
);
expect
(
view
).
assertIsCorrectVideoList
(
value
);
...
@@ -540,13 +610,17 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -540,13 +610,17 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
expect
(
messenger
.
hideError
).
not
.
toHaveBeenCalled
();
expect
(
messenger
.
hideError
).
not
.
toHaveBeenCalled
();
expect
(
view
.
updateModel
).
not
.
toHaveBeenCalled
();
expect
(
view
.
updateModel
).
not
.
toHaveBeenCalled
();
expect
(
view
.
closeExtraVideosBar
).
not
.
toHaveBeenCalled
();
expect
(
view
.
closeExtraVideosBar
).
not
.
toHaveBeenCalled
();
expect
(
$
.
fn
.
prop
).
toHaveBeenCalledWith
(
'disabled'
,
true
);
expect
(
$
.
fn
.
prop
).
toHaveBeenCalledWith
(
expect
(
$
.
fn
.
addClass
).
toHaveBeenCalledWith
(
'is-disabled'
);
'disabled'
,
true
);
expect
(
$
.
fn
.
addClass
).
toHaveBeenCalledWith
(
'is-disabled'
);
});
});
}
}
);
);
it
(
'Main field has invalid value - extra Videos Bar
should be
closed'
,
it
(
'Main field has invalid value - extra Videos Bar
is
closed'
,
function
()
{
function
()
{
$
.
fn
.
hasClass
.
andReturn
(
true
);
$
.
fn
.
hasClass
.
andReturn
(
true
);
view
.
checkValidity
.
andReturn
(
false
);
view
.
checkValidity
.
andReturn
(
false
);
...
@@ -556,8 +630,12 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -556,8 +630,12 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
expect
(
messenger
.
hideError
).
not
.
toHaveBeenCalled
();
expect
(
messenger
.
hideError
).
not
.
toHaveBeenCalled
();
expect
(
view
.
updateModel
).
not
.
toHaveBeenCalled
();
expect
(
view
.
updateModel
).
not
.
toHaveBeenCalled
();
expect
(
view
.
closeExtraVideosBar
).
toHaveBeenCalled
();
expect
(
view
.
closeExtraVideosBar
).
toHaveBeenCalled
();
expect
(
$
.
fn
.
prop
).
toHaveBeenCalledWith
(
'disabled'
,
true
);
expect
(
$
.
fn
.
prop
).
toHaveBeenCalledWith
(
expect
(
$
.
fn
.
addClass
).
toHaveBeenCalledWith
(
'is-disabled'
);
'disabled'
,
true
);
expect
(
$
.
fn
.
addClass
).
toHaveBeenCalledWith
(
'is-disabled'
);
});
});
}
}
);
);
...
@@ -572,8 +650,12 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -572,8 +650,12 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
expect
(
messenger
.
hideError
).
not
.
toHaveBeenCalled
();
expect
(
messenger
.
hideError
).
not
.
toHaveBeenCalled
();
expect
(
view
.
updateModel
).
toHaveBeenCalled
();
expect
(
view
.
updateModel
).
toHaveBeenCalled
();
expect
(
view
.
closeExtraVideosBar
).
not
.
toHaveBeenCalled
();
expect
(
view
.
closeExtraVideosBar
).
not
.
toHaveBeenCalled
();
expect
(
$
.
fn
.
prop
).
toHaveBeenCalledWith
(
'disabled'
,
false
);
expect
(
$
.
fn
.
prop
).
toHaveBeenCalledWith
(
expect
(
$
.
fn
.
removeClass
).
toHaveBeenCalledWith
(
'is-disabled'
);
'disabled'
,
false
);
expect
(
$
.
fn
.
removeClass
).
toHaveBeenCalledWith
(
'is-disabled'
);
});
});
}
}
);
);
...
@@ -588,8 +670,12 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
...
@@ -588,8 +670,12 @@ function ($, _, Utils, VideoList, MetadataView, MetadataModel, AbstractEditor, s
expect
(
messenger
.
hideError
).
toHaveBeenCalled
();
expect
(
messenger
.
hideError
).
toHaveBeenCalled
();
expect
(
view
.
updateModel
).
not
.
toHaveBeenCalled
();
expect
(
view
.
updateModel
).
not
.
toHaveBeenCalled
();
expect
(
view
.
closeExtraVideosBar
).
not
.
toHaveBeenCalled
();
expect
(
view
.
closeExtraVideosBar
).
not
.
toHaveBeenCalled
();
expect
(
$
.
fn
.
prop
).
toHaveBeenCalledWith
(
'disabled'
,
false
);
expect
(
$
.
fn
.
prop
).
toHaveBeenCalledWith
(
expect
(
$
.
fn
.
removeClass
).
toHaveBeenCalledWith
(
'is-disabled'
);
'disabled'
,
false
);
expect
(
$
.
fn
.
removeClass
).
toHaveBeenCalledWith
(
'is-disabled'
);
});
});
}
}
);
);
...
...
cms/static/js/views/video/transcripts/metadata_videolist.js
View file @
7b2d67f2
define
(
define
(
[
[
"jquery"
,
"backbone"
,
"underscore"
,
"js/views/abstract_editor"
,
'jquery'
,
'backbone'
,
'underscore'
,
'js/views/abstract_editor'
,
"js/views/video/transcripts/utils"
,
"js/views/video/transcripts/message_manager"
,
'js/views/video/transcripts/utils'
,
"js/views/metadata"
'js/views/video/transcripts/message_manager'
],
],
function
(
$
,
Backbone
,
_
,
AbstractEditor
,
Utils
,
MessageManager
,
MetadataView
)
{
function
(
$
,
Backbone
,
_
,
AbstractEditor
,
Utils
,
MessageManager
)
{
'use strict'
;
var
VideoList
=
AbstractEditor
.
extend
({
var
VideoList
=
AbstractEditor
.
extend
({
// Time that we wait since the last time user typed.
// Time that we wait since the last time user typed.
inputDelay
:
300
,
inputDelay
:
300
,
...
@@ -27,10 +28,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -27,10 +28,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
initialize
:
function
()
{
initialize
:
function
()
{
// Initialize MessageManager that is responsible for
// Initialize MessageManager that is responsible for
// status messages and errors.
// status messages and errors.
var
Messenger
=
this
.
options
.
MessageManager
||
MessageManager
;
this
.
messenger
=
new
Messenger
({
var
messenger
=
this
.
options
.
MessageManager
||
MessageManager
;
this
.
messenger
=
new
messenger
({
el
:
this
.
$el
,
el
:
this
.
$el
,
parent
:
this
parent
:
this
});
});
...
@@ -46,7 +46,8 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -46,7 +46,8 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
_
.
debounce
(
_
.
bind
(
this
.
inputHandler
,
this
),
this
.
inputDelay
)
_
.
debounce
(
_
.
bind
(
this
.
inputHandler
,
this
),
this
.
inputDelay
)
);
);
this
.
component_locator
=
this
.
$el
.
closest
(
'[data-locator]'
).
data
(
'locator'
);
this
.
component_locator
=
this
.
$el
.
closest
(
'[data-locator]'
)
.
data
(
'locator'
);
},
},
render
:
function
()
{
render
:
function
()
{
...
@@ -55,11 +56,14 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -55,11 +56,14 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
.
apply
(
this
,
arguments
);
.
apply
(
this
,
arguments
);
var
self
=
this
,
var
self
=
this
,
component_locator
=
this
.
$el
.
closest
(
'[data-locator]'
).
data
(
'locator'
),
component_locator
=
this
.
$el
.
closest
(
'[data-locator]'
)
.
data
(
'locator'
),
videoList
=
this
.
getVideoObjectsList
(),
videoList
=
this
.
getVideoObjectsList
(),
showServerError
=
function
(
response
)
{
showServerError
=
function
(
response
)
{
var
errorMessage
=
response
.
status
||
'Error: Connection with server failed.'
;
var
errorMessage
=
response
.
status
||
'Error: Connection with server failed.'
;
self
.
messenger
self
.
messenger
.
render
(
'not_found'
)
.
render
(
'not_found'
)
.
showError
(
.
showError
(
...
@@ -105,13 +109,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -105,13 +109,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
},
},
/**
/**
* @function
* Returns the values currently displayed in the editor/view.
*
* @return {Array} List of non-empty values.
* Returns the values currently displayed in the editor/view.
*/
*
* @returns {array} List of non-empty values.
*
*/
getValueFromEditor
:
function
()
{
getValueFromEditor
:
function
()
{
return
_
.
map
(
return
_
.
map
(
this
.
$el
.
find
(
'.input'
),
this
.
$el
.
find
(
'.input'
),
...
@@ -122,29 +122,25 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -122,29 +122,25 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
},
},
/**
/**
* @function
* Returns list of objects with information about the values currently
*
* displayed in the editor/view.
* Returns list of objects with information about the values currently
* @return {Array} List of objects.
* displayed in the editor/view.
* @examples
*
* this.getValueFromEditor(); // =>
* @returns {array} List of objects.
* [
*
* 'http://youtu.be/OEoXaMPEzfM',
* @examples
* 'video_name.mp4',
* this.getValueFromEditor(); // =>
* 'video_name.webm'
* [
* ]
* 'http://youtu.be/OEoXaMPEzfM',
*
* 'video_name.mp4',
* this.getVideoObjectsList(); // =>
* 'video_name.webm'
* [
* ]
* {mode: `youtube`, type: `youtube`, ...},
*
* {mode: `html5`, type: `mp4`, ...},
* this.getVideoObjectsList(); // =>
* {mode: `html5`, type: `webm`, ...}
* [
* ]
* {mode: `youtube`, type: `youtube`, ...},
*
* {mode: `html5`, type: `mp4`, ...},
*/
* {mode: `html5`, type: `webm`, ...}
* ]
*
*/
getVideoObjectsList
:
function
()
{
getVideoObjectsList
:
function
()
{
var
links
=
this
.
getValueFromEditor
();
var
links
=
this
.
getValueFromEditor
();
...
@@ -152,16 +148,11 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -152,16 +148,11 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
},
},
/**
/**
* @function
* Sets the values currently displayed in the editor/view.
*
* @param {Array} value List of values.
* Sets the values currently displayed in the editor/view.
*/
*
* @params {array} value List of values.
*
*/
setValueInEditor
:
function
(
value
)
{
setValueInEditor
:
function
(
value
)
{
var
parseLink
=
Utils
.
parseLink
,
var
list
=
this
.
$el
.
find
(
'.input'
),
list
=
this
.
$el
.
find
(
'.input'
),
val
=
value
.
filter
(
_
.
identity
),
val
=
value
.
filter
(
_
.
identity
),
placeholders
=
this
.
getPlaceholders
(
val
);
placeholders
=
this
.
getPlaceholders
(
val
);
...
@@ -174,19 +165,15 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -174,19 +165,15 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
/**
/**
* @function
* Returns the placeholders for the values currently displayed in the
*
* editor/view.
* Returns the placeholders for the values currently displayed in the
* @return {Array} List of placeholders.
* editor/view.
*/
*
* @returns {array} List of placeholders.
*
*/
getPlaceholders
:
function
(
value
)
{
getPlaceholders
:
function
(
value
)
{
var
parseLink
=
Utils
.
parseLink
,
var
parseLink
=
Utils
.
parseLink
,
placeholders
=
_
.
clone
(
this
.
placeholders
);
placeholders
=
_
.
clone
(
this
.
placeholders
);
// Returned list should have the same size as a count of editors
/views
.
// Returned list should have the same size as a count of editors.
return
_
.
map
(
return
_
.
map
(
this
.
$el
.
find
(
'.input'
),
this
.
$el
.
find
(
'.input'
),
function
(
element
,
index
)
{
function
(
element
,
index
)
{
...
@@ -214,13 +201,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -214,13 +201,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
},
},
/**
/**
* @function
* Opens video sources box.
*
* @param {Object} event Event object.
* Opens video sources box.
*/
*
* @params {object} event Event object.
*
*/
openExtraVideosBar
:
function
(
event
)
{
openExtraVideosBar
:
function
(
event
)
{
if
(
event
&&
event
.
preventDefault
)
{
if
(
event
&&
event
.
preventDefault
)
{
event
.
preventDefault
();
event
.
preventDefault
();
...
@@ -230,13 +213,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -230,13 +213,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
},
},
/**
/**
* @function
* Closes video sources box.
*
* @param {Object} event Event object.
* Closes video sources box.
*/
*
* @params {object} event Event object.
*
*/
closeExtraVideosBar
:
function
(
event
)
{
closeExtraVideosBar
:
function
(
event
)
{
if
(
event
&&
event
.
preventDefault
)
{
if
(
event
&&
event
.
preventDefault
)
{
event
.
preventDefault
();
event
.
preventDefault
();
...
@@ -246,13 +225,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -246,13 +225,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
},
},
/**
/**
* @function
* Toggles video sources box.
*
* @param {Object} event Event object.
* Toggles video sources box.
*/
*
* @params {object} event Event object.
*
*/
toggleExtraVideosBar
:
function
(
event
)
{
toggleExtraVideosBar
:
function
(
event
)
{
if
(
event
&&
event
.
preventDefault
)
{
if
(
event
&&
event
.
preventDefault
)
{
event
.
preventDefault
();
event
.
preventDefault
();
...
@@ -266,13 +241,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -266,13 +241,9 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
},
},
/**
/**
* @function
* Handle `input` event.
*
* @param {Object} event Event object.
* Handle `input` event.
*/
*
* @params {object} event Event object.
*
*/
inputHandler
:
function
(
event
)
{
inputHandler
:
function
(
event
)
{
if
(
event
&&
event
.
preventDefault
)
{
if
(
event
&&
event
.
preventDefault
)
{
event
.
preventDefault
();
event
.
preventDefault
();
...
@@ -329,77 +300,108 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
...
@@ -329,77 +300,108 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager, MetadataView) {
},
},
/**
/**
* @function
* Checks the values currently displayed in the editor/view have unique
*
* types (mp4 | webm | youtube).
* Checks the values currently displayed in the editor/view have unique
* @param {Object} videoList List of objects with information about the
* types (mp4 | webm | youtube).
* @return {Boolean} Boolean value that indicate if video types are
*
* unique.
* @param {object} videoList List of objects with information about the
*/
* values currently displayed in the editor/view
*
* @returns {boolean} Boolean value that indicate if video types are unique.
*
*/
isUniqVideoTypes
:
function
(
videoList
)
{
isUniqVideoTypes
:
function
(
videoList
)
{
// Extract a list of "type" property values.
// Extract a list of "type" property values.
var
arr
=
_
.
pluck
(
videoList
,
'type'
),
// => ex: ['youtube', 'mp4', 'mp4']
// => ex: ['webm', 'mp4', 'mp4']
// Produces a duplicate-free version of the array.
var
arr
=
_
.
pluck
(
videoList
,
'type'
).
filter
(
function
(
item
)
{
uniqArr
=
_
.
uniq
(
arr
);
// => ex: ['youtube', 'mp4']
return
item
!==
'other'
;
}),
// Produces a duplicate-free version of the array.
uniqArr
=
_
.
uniq
(
arr
);
// => ex: ['webm', 'mp4']
return
arr
.
length
===
uniqArr
.
length
;
return
arr
.
length
===
uniqArr
.
length
;
},
},
/**
/**
* @function
* Checks that links without file format are unique.
*
* @param {Object} videoList List of objects with information about the
* Shows error message if the values currently displayed in the
* @return {Boolean} Boolean value that indicate if video types are
* editor/view have duplicate types.
* unique.
*
*/
* @param {object} list List of objects with information about the
isUniqOtherVideos
:
function
(
videoList
)
{
* values currently displayed in the editor/view
// Returns list of video objects with "type" equal "other" or
*
// "youtube".
* @returns {boolean} Boolean value that indicate if video types are unique.
var
otherLinksList
=
videoList
.
filter
(
function
(
item
)
{
*
return
item
.
type
===
'other'
;
*/
}),
checkIsUniqVideoTypes
:
function
(
list
)
{
// Extract a list of "video" property values.
var
videoList
=
list
||
this
.
getVideoObjectsList
(),
namesList
=
_
.
pluck
(
otherLinksList
,
'video'
),
isUnique
=
true
;
// Produces a duplicate-free version of the array.
uniqNamesList
=
_
.
uniq
(
namesList
);
return
namesList
.
length
===
uniqNamesList
.
length
;
},
if
(
!
this
.
isUniqVideoTypes
(
videoList
))
{
/**
this
.
messenger
* Validates video list using provided validator.
.
showError
(
'Link types should be unique.'
,
true
);
* @param {Function} validator Function that validate provided list.
* @param {Object} list List of objects with information about the
* values currently displayed in the editor.
* @param {String} message Error message.
* @return {Boolean}
*/
checkIsValid
:
function
(
validator
,
list
,
message
)
{
var
videoList
=
list
||
this
.
getVideoObjectsList
(),
isValid
=
true
;
isUnique
=
false
;
if
(
!
validator
(
videoList
))
{
this
.
messenger
.
showError
(
message
,
true
);
isValid
=
false
;
}
}
return
is
Unique
;
return
is
Valid
;
},
},
/**
/**
* @function
* Validates if video types are unique.
*
* @param {Object} list List of objects with information about the
* Checks if the values currently displayed in the editor/view have
* values currently displayed in the editor.
* valid values and show error messages.
* @return {Boolean}
*
*/
* @param {object} data Objects with information about the value
checkIsUniqVideoTypes
:
function
(
list
)
{
* currently displayed in the editor/view
return
this
.
checkIsValid
(
*
this
.
isUniqVideoTypes
,
list
,
'Link types should be unique.'
* @param {boolean} showErrorModeMessage Disable mode validation
);
*
},
* @returns {boolean} Boolean value that indicate if value is valid.
*
/**
*/
* Validates if other videos ids are unique.
* editor/view have duplicate types.
* @param {Object} list List of objects with information about the
* values currently displayed in the editor.
* @return {Boolean}
*/
checkIsUniqOtherVideos
:
function
(
list
)
{
return
this
.
checkIsValid
(
this
.
isUniqOtherVideos
,
list
,
'Links should be unique.'
);
},
/**
* Checks if the values currently displayed in the editor/view have
* valid values and show error messages.
* @param {Object} data Objects with information about the value
* currently displayed in the editor/view
* @param {Boolean} showErrorModeMessage Disable mode validation
* @return {Boolean} Boolean value that indicate if value is valid.
*/
checkValidity
:
function
(
data
,
showErrorModeMessage
)
{
checkValidity
:
function
(
data
,
showErrorModeMessage
)
{
var
self
=
this
,
var
videoList
=
this
.
getVideoObjectsList
(),
videoList
=
this
.
getVideoObjectsList
();
isUniqTypes
=
this
.
checkIsUniqVideoTypes
.
bind
(
this
),
isUniqOtherVideos
=
this
.
checkIsUniqOtherVideos
.
bind
(
this
);
if
(
!
this
.
checkIsUniqVideoType
s
(
videoList
))
{
if
(
!
isUniqTypes
(
videoList
)
||
!
isUniqOtherVideo
s
(
videoList
))
{
return
false
;
return
false
;
}
}
if
(
data
.
mode
===
'incorrect'
&&
showErrorModeMessage
)
{
if
(
data
.
mode
===
'incorrect'
&&
showErrorModeMessage
)
{
this
.
messenger
this
.
messenger
.
showError
(
'Incorrect url format.'
,
true
);
.
showError
(
'Incorrect url format.'
,
true
);
return
false
;
return
false
;
}
}
...
...
cms/static/js/views/video/transcripts/utils.js
View file @
7b2d67f2
...
@@ -4,53 +4,49 @@ return (function () {
...
@@ -4,53 +4,49 @@ return (function () {
var
Storage
=
{};
var
Storage
=
{};
/**
/**
* Adds some data to the Storage object. If data with existent `data_id`
* Adds some data to the Storage object. If data with existent `data_id`
* is added, nothing happens.
* is added, nothing happens.
* @function
* @function
* @param {String} data_id Unique identifier for the data.
* @param {String} data_id Unique identifier for the data.
* @param {Any} data Data that should be stored.
* @param {Any} data Data that should be stored.
* @return {Object} Object itself for chaining.
* @return {Object} Object itself for chaining.
*/
*/
Storage
.
set
=
function
(
data_id
,
data
)
{
Storage
.
set
=
function
(
data_id
,
data
)
{
Storage
[
data_id
]
=
data
;
Storage
[
data_id
]
=
data
;
return
this
;
return
this
;
};
};
/**
/**
* Return data from the Storage object by identifier.
* Return data from the Storage object by identifier.
* @function
* @function
* @param {String} data_id Unique identifier of the data.
* @param {String} data_id Unique identifier of the data.
* @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
* @param {String} data_id Unique identifier of the data.
* @param {String} data_id Unique identifier of the data.
* @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
]);
};
};
/**
/**
* Returns model from collection by 'field_name' property.
* Returns model from collection by 'field_name' property.
* @function
* @function
* @param {Object} collection The model (CMS.Models.Metadata) information
* @param {Object} collection The model (CMS.Models.Metadata) information
* about metadata setting editors.
* about metadata setting editors.
* @param {String} field_name Name of field that should be found.
* @param {String} field_name Name of field that should be found.
* @return {
* @return {
* Object: When model exist.
* Object: When model exist.
* Undefined: When model doesn't exist.
* Undefined: When model doesn't exist.
* }
* }
*/
*/
var
_getField
=
function
(
collection
,
field_name
)
{
var
_getField
=
function
(
collection
,
field_name
)
{
var
model
;
var
model
;
...
@@ -64,30 +60,29 @@ return (function () {
...
@@ -64,30 +60,29 @@ return (function () {
};
};
/**
/**
* Parses Youtube link and return video id.
* Parses Youtube link and return video id.
* @function
* @function
* These are the types of URLs supported:
* These are the types of URLs supported:
* http://www.youtube.com/watch?v=OEoXaMPEzfM&feature=feedrec_grec_index
* http://www.youtube.com/watch?v=OEoXaMPEzfM&feature=feedrec_grec_index
* http://www.youtube.com/user/IngridMichaelsonVEVO#p/a/u/1/OEoXaMPEzfM
* http://www.youtube.com/user/IngridMichaelsonVEVO#p/a/u/1/OEoXaMPEzfM
* http://www.youtube.com/v/OEoXaMPEzfM?fs=1&hl=en_US&rel=0
* http://www.youtube.com/v/OEoXaMPEzfM?fs=1&hl=en_US&rel=0
* http://www.youtube.com/watch?v=OEoXaMPEzfM#t=0m10s
* http://www.youtube.com/watch?v=OEoXaMPEzfM#t=0m10s
* http://www.youtube.com/embed/OEoXaMPEzfM?rel=0
* http://www.youtube.com/embed/OEoXaMPEzfM?rel=0
* http://www.youtube.com/watch?v=OEoXaMPEzfM
* http://www.youtube.com/watch?v=OEoXaMPEzfM
* http://youtu.be/OEoXaMPEzfM
* http://youtu.be/OEoXaMPEzfM
* @param {String} url Url that should be parsed.
* @param {String} url Url that should be parsed.
* @return {
* @return {
* String: Video Id.
* String: Video Id.
* Undefined: When url has incorrect format or argument is
* Undefined: When url has incorrect format or argument is
* non-string, video id's length is not equal 11.
* non-string, video id's length is not equal 11.
* }
* }
*/
*/
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,25 +91,23 @@ return (function () {
...
@@ -96,25 +91,23 @@ 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
];
};
};
}());
}());
/**
/**
* Parses links with html5 video sources in mp4 or webm formats.
* Parses links with html5 video sources in mp4 or webm formats.
* @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.
* }
* }
*/
*/
var
_videoLinkParser
=
(
function
()
{
var
_videoLinkParser
=
(
function
()
{
var
cache
=
{};
var
cache
=
{};
...
@@ -132,57 +125,65 @@ return (function () {
...
@@ -132,57 +125,65 @@ 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
{
cache[url] = {
// Links like http://goo.gl/pxxZrg
video: link.pathname
// The regular expression try catches file name.
.split('/')
// '[scheme://hostname/pathname/]filename[?query#hash]'
.pop(),
match
=
link
.
pathname
.
match
(
/
\/{1}([^\/\.]
+
)
$/
);
type: 'other'
if
(
match
)
{
};
cache
[
url
]
=
{
}*/
video
:
match
[
1
],
type
:
'other'
};
}
}
return
cache
[
url
];
return
cache
[
url
];
};
};
}());
}());
/**
/**
* Facade function that parses html5 and youtube links.
* Facade function that parses html5 and youtube links.
* @function
* @function
* @param {String} url Url that should be parsed.
* @param {String} url Url that should be parsed.
* @return {
* @return {
* object: Object with information about the video:
* object: Object with information about the video:
* {
* {
* 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
))
{
result
=
{
if
(
_youtubeParser
(
url
).
length
===
youtubeIdLength
)
{
mode
:
'youtube'
,
result
=
{
video
:
_youtubeParser
(
url
),
mode
:
'youtube'
,
type
:
'youtube'
video
:
_youtubeParser
(
url
),
};
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
{
...
@@ -195,37 +196,36 @@ return (function () {
...
@@ -195,37 +196,36 @@ 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'
*/
*/
var
_getYoutubeLink
=
function
(
video_id
)
{
var
_getYoutubeLink
=
function
(
video_id
)
{
return
'http://youtu.be/'
+
video_id
;
return
'http://youtu.be/'
+
video_id
;
};
};
/**
/**
* 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',
* 'video_name.mp4',
* 'video_name.mp4',
* 'video_name.webm'
* 'video_name.webm'
* ]
* ]
*
*
* _getVideoList(links); // =>
* _getVideoList(links); // =>
* [
* [
* {mode: `youtube`, type: `youtube`, ...},
* {mode: `youtube`, type: `youtube`, ...},
* {mode: `html5`, type: `mp4`, ...},
* {mode: `html5`, type: `mp4`, ...},
* {mode: `html5`, type: `webm`, ...}
* {mode: `html5`, type: `webm`, ...}
* ]
* ]
*/
*/
var
_getVideoList
=
function
(
links
)
{
var
_getVideoList
=
function
(
links
)
{
if
(
$
.
isArray
(
links
))
{
if
(
$
.
isArray
(
links
))
{
var
arr
=
[],
var
arr
=
[],
...
@@ -243,14 +243,13 @@ return (function () {
...
@@ -243,14 +243,13 @@ return (function () {
}
}
};
};
/**
/**
* Synchronizes 2 Backbone collections by 'field_name' property.
* Synchronizes 2 Backbone collections by 'field_name' property.
* @function
* @function
* @param {Object} fromCollection Collection with which synchronization will
* @param {Object} fromCollection Collection with which synchronization will
* happens.
* happens.
* @param {Object} toCollection Collection which will synchronized.
* @param {Object} toCollection Collection which will synchronized.
*/
*/
var
_syncCollections
=
function
(
fromCollection
,
toCollection
)
{
var
_syncCollections
=
function
(
fromCollection
,
toCollection
)
{
fromCollection
.
each
(
function
(
m
)
{
fromCollection
.
each
(
function
(
m
)
{
var
model
=
toCollection
.
findWhere
({
var
model
=
toCollection
.
findWhere
({
...
@@ -264,18 +263,18 @@ return (function () {
...
@@ -264,18 +263,18 @@ return (function () {
};
};
/**
/**
* Sends Ajax requests in appropriate format.
* Sends Ajax requests in appropriate format.
* @function
* @function
* @param {String} action Action that will be invoked on server.
* @param {String} action Action that will be invoked on server.
* @param {String} component_locator the locator of component.
* @param {String} component_locator the locator of component.
* @param {Array} videoList List of object with information about inserted
* @param {Array} videoList List of object with information about inserted
* urls.
* urls.
* @param {Object} extraParams Extra parameters that can be send to the
* @param {Object} extraParams Extra parameters that can be send to the
* server.
* server.
* @return {Object} XMLHttpRequest object. Using this object, we can
* @return {Object} XMLHttpRequest object. Using this object, we can
* attach callbacks to AJAX request events (for example on 'done',
* attach callbacks to AJAX request events (for example on 'done',
* 'fail', etc.).
* 'fail', etc.).
*/
*/
var
_command
=
(
function
()
{
var
_command
=
(
function
()
{
// We will store the XMLHttpRequest object that $.ajax() function
// We will store the XMLHttpRequest object that $.ajax() function
// returns, to abort an ongoing AJAX request (if necessary) upon
// returns, to abort an ongoing AJAX request (if necessary) upon
...
...
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 '
+
// Following hack allows to open the same video twice
'src="'
+
url
+
// https://code.google.com/p/chromium/issues/detail?id=31014
// Following hack allows to open the same video twice
// Check whether the url already has a '?' inside, and if so,
// https://code.google.com/p/chromium/issues/detail?id=31014
// use '&' instead of '?' to prevent breaking the url's integrity.
// Check whether the url already has a '?' inside, and if so,
(
source
.
indexOf
(
'?'
)
===
-
1
?
'?'
:
'&'
),
// use '&' instead of '?' to prevent breaking the url's integrity.
(
new
Date
()).
getTime
(),
'" />'
(
url
.
indexOf
(
'?'
)
==
-
1
?
'?'
:
'&'
)
+
(
new
Date
()).
getTime
()
+
].
join
(
''
);
'" '
+
'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
;
}
// 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
,
...
@@ -56,7 +52,7 @@ class TestVideoYouTube(TestVideo):
...
@@ -56,7 +52,7 @@ class TestVideoYouTube(TestVideo):
'transcript_download_format'
:
'srt'
,
'transcript_download_format'
:
'srt'
,
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_language'
:
u'en'
,
'transcript_language'
:
u'en'
,
'transcript_languages'
:
json
.
dumps
(
OrderedDict
({
"en"
:
"English"
,
"uk"
:
u"Українська"
})),
'transcript_languages'
:
json
.
dumps
(
OrderedDict
({
"en"
:
"English"
,
"uk"
:
u"Українська"
})),
'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'
)
.
rstrip
(
'/?'
),
)
.
rstrip
(
'/?'
),
...
@@ -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