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
4a36fa89
Commit
4a36fa89
authored
Dec 26, 2013
by
polesye
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
BLD-368: Turn "download transcript" into a dropdown.
parent
2536224b
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
171 additions
and
37 deletions
+171
-37
CHANGELOG.rst
+3
-0
cms/djangoapps/contentstore/features/component_settings_editor_helpers.py
+1
-1
cms/djangoapps/contentstore/features/video-editor.py
+1
-1
cms/static/js/views/metadata.js
+13
-0
cms/static/sass/views/_unit.scss
+6
-0
common/lib/xmodule/xmodule/tests/test_video.py
+17
-5
common/lib/xmodule/xmodule/video_module.py
+99
-4
lms/djangoapps/courseware/tests/__init__.py
+30
-22
lms/djangoapps/courseware/tests/test_video_mongo.py
+0
-0
lms/djangoapps/courseware/tests/test_video_xml.py
+1
-4
No files found.
CHANGELOG.rst
View file @
4a36fa89
...
@@ -5,6 +5,9 @@ These are notable changes in edx-platform. This is a rolling list of changes,
...
@@ -5,6 +5,9 @@ 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: Change the track field to a dropdown that will allow students
to download the transcript of the video without timecodes. BLD-368.
Blades: Video player start-end time range is now shown even before Play is
Blades: Video player start-end time range is now shown even before Play is
clicked. Video player VCR time shows correct non-zero total time for YouTube
clicked. Video player VCR time shows correct non-zero total time for YouTube
videos even before Play is clicked. BLD-529.
videos even before Play is clicked. BLD-529.
...
...
cms/djangoapps/contentstore/features/component_settings_editor_helpers.py
View file @
4a36fa89
...
@@ -140,7 +140,7 @@ def verify_setting_entry(setting, display_name, value, explicitly_set):
...
@@ -140,7 +140,7 @@ def verify_setting_entry(setting, display_name, value, explicitly_set):
for the problem, rather than derived from the defaults. This is verified
for the problem, rather than derived from the defaults. This is verified
by the existence of a "Clear" button next to the field value.
by the existence of a "Clear" button next to the field value.
"""
"""
assert_equal
(
display_name
,
setting
.
find_by_css
(
'.setting-label'
)[
0
]
.
html
)
assert_equal
(
display_name
,
setting
.
find_by_css
(
'.setting-label'
)[
0
]
.
html
.
strip
()
)
# Check if the web object is a list type
# Check if the web object is a list type
# If so, we use a slightly different mechanism for determining its value
# If so, we use a slightly different mechanism for determining its value
...
...
cms/djangoapps/contentstore/features/video-editor.py
View file @
4a36fa89
...
@@ -40,12 +40,12 @@ def correct_video_settings(_step):
...
@@ -40,12 +40,12 @@ def correct_video_settings(_step):
# advanced
# advanced
[
'Display Name'
,
'Video'
,
False
],
[
'Display Name'
,
'Video'
,
False
],
[
'Download Transcript'
,
''
,
False
],
[
'Download Video'
,
''
,
False
],
[
'Download Video'
,
''
,
False
],
[
'End Time'
,
'00:00:00'
,
False
],
[
'End Time'
,
'00:00:00'
,
False
],
[
'HTML5 Transcript'
,
''
,
False
],
[
'HTML5 Transcript'
,
''
,
False
],
[
'Show Transcript'
,
'True'
,
False
],
[
'Show Transcript'
,
'True'
,
False
],
[
'Start Time'
,
'00:00:00'
,
False
],
[
'Start Time'
,
'00:00:00'
,
False
],
[
'Transcript Download Allowed'
,
'False'
,
False
],
[
'Video Sources'
,
''
,
False
],
[
'Video Sources'
,
''
,
False
],
[
'Youtube ID'
,
'OEoXaMPEzfM'
,
False
],
[
'Youtube ID'
,
'OEoXaMPEzfM'
,
False
],
[
'Youtube ID for .75x speed'
,
''
,
False
],
[
'Youtube ID for .75x speed'
,
''
,
False
],
...
...
cms/static/js/views/metadata.js
View file @
4a36fa89
...
@@ -94,6 +94,19 @@ function(BaseView, _, MetadataModel, AbstractEditor, VideoList) {
...
@@ -94,6 +94,19 @@ function(BaseView, _, MetadataModel, AbstractEditor, VideoList) {
templateName
:
"metadata-string-entry"
,
templateName
:
"metadata-string-entry"
,
render
:
function
()
{
AbstractEditor
.
prototype
.
render
.
apply
(
this
);
// If the model has property `non editable` equals `true`,
// the field is disabled, but user is able to clear it.
if
(
this
.
model
.
get
(
'non_editable'
))
{
this
.
$el
.
find
(
'#'
+
this
.
uniqueId
)
.
prop
(
'readonly'
,
true
)
.
addClass
(
'is-disabled'
);
}
},
getValueFromEditor
:
function
()
{
getValueFromEditor
:
function
()
{
return
this
.
$el
.
find
(
'#'
+
this
.
uniqueId
).
val
();
return
this
.
$el
.
find
(
'#'
+
this
.
uniqueId
).
val
();
},
},
...
...
cms/static/sass/views/_unit.scss
View file @
4a36fa89
...
@@ -708,6 +708,12 @@ body.course.unit,.view-unit {
...
@@ -708,6 +708,12 @@ body.course.unit,.view-unit {
text-overflow
:
ellipsis
;
text-overflow
:
ellipsis
;
}
}
//Allows users to copy full value of disabled inputs.
input
.is-disabled
{
text-overflow
:
clip
;
opacity
:
.5
;
}
input
[
type
=
"number"
]
{
input
[
type
=
"number"
]
{
width
:
38
.5%
;
width
:
38
.5%
;
...
...
common/lib/xmodule/xmodule/tests/test_video.py
View file @
4a36fa89
...
@@ -25,7 +25,6 @@ from .test_import import DummySystem
...
@@ -25,7 +25,6 @@ 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
from
textwrap
import
dedent
from
xmodule.tests
import
get_test_descriptor_system
from
xmodule.tests
import
get_test_descriptor_system
...
@@ -187,6 +186,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -187,6 +186,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
<video display_name="Test Video"
<video display_name="Test Video"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
show_captions="false"
show_captions="false"
download_track="true"
start_time="00:00:01"
start_time="00:00:01"
end_time="00:01:00">
end_time="00:01:00">
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
...
@@ -211,6 +211,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -211,6 +211,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'track'
:
'http://www.example.com/track'
,
'track'
:
'http://www.example.com/track'
,
'download_track'
:
True
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
],
'data'
:
''
'data'
:
''
})
})
...
@@ -221,6 +222,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -221,6 +222,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
<video display_name="Test Video"
<video display_name="Test Video"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
show_captions="false"
show_captions="false"
download_track="false"
start_time="00:00:01"
start_time="00:00:01"
end_time="00:01:00">
end_time="00:01:00">
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
...
@@ -237,6 +239,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -237,6 +239,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'track'
:
'http://www.example.com/track'
,
'track'
:
'http://www.example.com/track'
,
'download_track'
:
False
,
'source'
:
'http://www.example.com/source.mp4'
,
'source'
:
'http://www.example.com/source.mp4'
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
'data'
:
''
...
@@ -253,7 +256,6 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -253,7 +256,6 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
youtube="1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA"
youtube="1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA"
show_captions="true">
show_captions="true">
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
<track src="http://www.example.com/track"/>
</video>
</video>
'''
'''
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
())
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
())
...
@@ -265,7 +267,8 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -265,7 +267,8 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'show_captions'
:
True
,
'show_captions'
:
True
,
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'track'
:
'http://www.example.com/track'
,
'track'
:
''
,
'download_track'
:
False
,
'source'
:
'http://www.example.com/source.mp4'
,
'source'
:
'http://www.example.com/source.mp4'
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
'data'
:
''
...
@@ -287,6 +290,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -287,6 +290,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'track'
:
''
,
'track'
:
''
,
'download_track'
:
False
,
'source'
:
''
,
'source'
:
''
,
'html5_sources'
:
[],
'html5_sources'
:
[],
'data'
:
''
'data'
:
''
...
@@ -305,6 +309,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -305,6 +309,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
source=""http://download_video""
source=""http://download_video""
sub=""html5_subtitles""
sub=""html5_subtitles""
track=""http://download_track""
track=""http://download_track""
download_track="true"
youtube_id_0_75=""OEoXaMPEzf65""
youtube_id_0_75=""OEoXaMPEzf65""
youtube_id_1_25=""OEoXaMPEzf125""
youtube_id_1_25=""OEoXaMPEzf125""
youtube_id_1_5=""OEoXaMPEzf15""
youtube_id_1_5=""OEoXaMPEzf15""
...
@@ -321,6 +326,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -321,6 +326,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'track'
:
'http://download_track'
,
'track'
:
'http://download_track'
,
'download_track'
:
True
,
'source'
:
'http://download_video'
,
'source'
:
'http://download_video'
,
'html5_sources'
:
[
"source_1"
,
"source_2"
],
'html5_sources'
:
[
"source_1"
,
"source_2"
],
'data'
:
''
'data'
:
''
...
@@ -343,6 +349,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -343,6 +349,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'track'
:
''
,
'track'
:
''
,
'download_track'
:
False
,
'source'
:
''
,
'source'
:
''
,
'html5_sources'
:
[],
'html5_sources'
:
[],
'data'
:
''
'data'
:
''
...
@@ -373,6 +380,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -373,6 +380,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'track'
:
'http://www.example.com/track'
,
'track'
:
'http://www.example.com/track'
,
'download_track'
:
True
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
'data'
:
''
})
})
...
@@ -402,6 +410,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -402,6 +410,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'track'
:
'http://www.example.com/track'
,
'track'
:
'http://www.example.com/track'
,
'download_track'
:
True
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
'data'
:
''
})
})
...
@@ -431,6 +440,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
...
@@ -431,6 +440,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'start_time'
:
datetime
.
timedelta
(
seconds
=
1
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
60
),
'track'
:
'http://www.example.com/track'
,
'track'
:
'http://www.example.com/track'
,
'download_track'
:
True
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
'data'
:
''
})
})
...
@@ -461,11 +471,12 @@ class VideoExportTestCase(unittest.TestCase):
...
@@ -461,11 +471,12 @@ class VideoExportTestCase(unittest.TestCase):
desc
.
start_time
=
datetime
.
timedelta
(
seconds
=
1.0
)
desc
.
start_time
=
datetime
.
timedelta
(
seconds
=
1.0
)
desc
.
end_time
=
datetime
.
timedelta
(
seconds
=
60
)
desc
.
end_time
=
datetime
.
timedelta
(
seconds
=
60
)
desc
.
track
=
'http://www.example.com/track'
desc
.
track
=
'http://www.example.com/track'
desc
.
download_track
=
True
desc
.
html5_sources
=
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
]
desc
.
html5_sources
=
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
]
xml
=
desc
.
definition_to_xml
(
None
)
# We don't use the `resource_fs` parameter
xml
=
desc
.
definition_to_xml
(
None
)
# We don't use the `resource_fs` parameter
expected
=
etree
.
fromstring
(
'''
\
expected
=
etree
.
fromstring
(
'''
\
<video url_name="SampleProblem1" start_time="0:00:01" youtube="0.75:izygArpw-Qo,1.00:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8" show_captions="false" end_time="0:01:00">
<video url_name="SampleProblem1" start_time="0:00:01" youtube="0.75:izygArpw-Qo,1.00:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8" show_captions="false" end_time="0:01:00"
download_track="true"
>
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.ogg"/>
<source src="http://www.example.com/source.ogg"/>
<track src="http://www.example.com/track"/>
<track src="http://www.example.com/track"/>
...
@@ -488,11 +499,12 @@ class VideoExportTestCase(unittest.TestCase):
...
@@ -488,11 +499,12 @@ class VideoExportTestCase(unittest.TestCase):
desc
.
start_time
=
datetime
.
timedelta
(
seconds
=
5.0
)
desc
.
start_time
=
datetime
.
timedelta
(
seconds
=
5.0
)
desc
.
end_time
=
datetime
.
timedelta
(
seconds
=
0.0
)
desc
.
end_time
=
datetime
.
timedelta
(
seconds
=
0.0
)
desc
.
track
=
'http://www.example.com/track'
desc
.
track
=
'http://www.example.com/track'
desc
.
download_track
=
True
desc
.
html5_sources
=
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
]
desc
.
html5_sources
=
[
'http://www.example.com/source.mp4'
,
'http://www.example.com/source.ogg'
]
xml
=
desc
.
definition_to_xml
(
None
)
# We don't use the `resource_fs` parameter
xml
=
desc
.
definition_to_xml
(
None
)
# We don't use the `resource_fs` parameter
expected
=
etree
.
fromstring
(
'''
\
expected
=
etree
.
fromstring
(
'''
\
<video url_name="SampleProblem1" start_time="0:00:05" youtube="0.75:izygArpw-Qo,1.00:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8" show_captions="false">
<video url_name="SampleProblem1" start_time="0:00:05" youtube="0.75:izygArpw-Qo,1.00:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8" show_captions="false"
download_track="true"
>
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.mp4"/>
<source src="http://www.example.com/source.ogg"/>
<source src="http://www.example.com/source.ogg"/>
<track src="http://www.example.com/track"/>
<track src="http://www.example.com/track"/>
...
...
common/lib/xmodule/xmodule/video_module.py
View file @
4a36fa89
...
@@ -13,18 +13,24 @@ in XML.
...
@@ -13,18 +13,24 @@ in XML.
import
json
import
json
import
logging
import
logging
from
HTMLParser
import
HTMLParser
from
lxml
import
etree
from
lxml
import
etree
from
pkg_resources
import
resource_string
from
pkg_resources
import
resource_string
import
datetime
import
datetime
import
copy
import
copy
from
webob
import
Response
from
django.http
import
Http404
from
django.http
import
Http404
from
django.conf
import
settings
from
django.conf
import
settings
from
xmodule.x_module
import
XModule
from
xmodule.x_module
import
XModule
,
module_attr
from
xmodule.editing_module
import
TabsEditingDescriptor
from
xmodule.editing_module
import
TabsEditingDescriptor
from
xmodule.raw_module
import
EmptyDataRawDescriptor
from
xmodule.raw_module
import
EmptyDataRawDescriptor
from
xmodule.xml_module
import
is_pointer_tag
,
name_to_pathname
,
deserialize_field
from
xmodule.xml_module
import
is_pointer_tag
,
name_to_pathname
,
deserialize_field
from
xmodule.contentstore.django
import
contentstore
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.exceptions
import
NotFoundError
from
xblock.core
import
XBlock
from
xblock.fields
import
Scope
,
String
,
Boolean
,
List
,
Integer
,
ScopeIds
from
xblock.fields
import
Scope
,
String
,
Boolean
,
List
,
Integer
,
ScopeIds
from
xmodule.fields
import
RelativeTime
from
xmodule.fields
import
RelativeTime
...
@@ -103,11 +109,19 @@ class VideoFields(object):
...
@@ -103,11 +109,19 @@ class VideoFields(object):
display_name
=
"Video Sources"
,
display_name
=
"Video Sources"
,
scope
=
Scope
.
settings
,
scope
=
Scope
.
settings
,
)
)
# `track` is deprecated field and should not be used in future.
# `download_track` is used instead.
track
=
String
(
track
=
String
(
help
=
"The external URL to download the timed transcript track.
This appears as a link beneath the video.
"
,
help
=
"The external URL to download the timed transcript track."
,
display_name
=
"Download Transcript"
,
display_name
=
"Download Transcript"
,
scope
=
Scope
.
settings
,
scope
=
Scope
.
settings
,
default
=
""
default
=
''
)
download_track
=
Boolean
(
help
=
"Show a link beneath the video to allow students to download the transcript. Note: You must add a link to the HTML5 Transcript field above."
,
display_name
=
"Transcript Download Allowed"
,
scope
=
Scope
.
settings
,
default
=
False
)
)
sub
=
String
(
sub
=
String
(
help
=
"The name of the timed transcript track (for non-Youtube videos)."
,
help
=
"The name of the timed transcript track (for non-Youtube videos)."
,
...
@@ -162,18 +176,25 @@ class VideoModule(VideoFields, XModule):
...
@@ -162,18 +176,25 @@ class VideoModule(VideoFields, XModule):
raise
Http404
()
raise
Http404
()
def
get_html
(
self
):
def
get_html
(
self
):
track_url
=
None
caption_asset_path
=
"/static/subs/"
caption_asset_path
=
"/static/subs/"
get_ext
=
lambda
filename
:
filename
.
rpartition
(
'.'
)[
-
1
]
get_ext
=
lambda
filename
:
filename
.
rpartition
(
'.'
)[
-
1
]
sources
=
{
get_ext
(
src
):
src
for
src
in
self
.
html5_sources
}
sources
=
{
get_ext
(
src
):
src
for
src
in
self
.
html5_sources
}
sources
[
'main'
]
=
self
.
source
sources
[
'main'
]
=
self
.
source
if
self
.
download_track
:
if
self
.
track
:
track_url
=
self
.
track
elif
self
.
sub
:
track_url
=
self
.
runtime
.
handler_url
(
self
,
'download_transcript'
)
return
self
.
system
.
render_template
(
'video.html'
,
{
return
self
.
system
.
render_template
(
'video.html'
,
{
'youtube_streams'
:
_create_youtube_string
(
self
),
'youtube_streams'
:
_create_youtube_string
(
self
),
'id'
:
self
.
location
.
html_id
(),
'id'
:
self
.
location
.
html_id
(),
'sub'
:
self
.
sub
,
'sub'
:
self
.
sub
,
'sources'
:
sources
,
'sources'
:
sources
,
'track'
:
self
.
track
,
'track'
:
track_url
,
'display_name'
:
self
.
display_name_with_default
,
'display_name'
:
self
.
display_name_with_default
,
# This won't work when we move to data that
# This won't work when we move to data that
# isn't on the filesystem
# isn't on the filesystem
...
@@ -189,10 +210,58 @@ class VideoModule(VideoFields, XModule):
...
@@ -189,10 +210,58 @@ class VideoModule(VideoFields, XModule):
'yt_test_url'
:
settings
.
YOUTUBE_TEST_URL
'yt_test_url'
:
settings
.
YOUTUBE_TEST_URL
})
})
def
get_transcript
(
self
,
subs_id
):
'''
Returns transcript without timecodes.
Args:
`subs_id`: str, subtitles id
Raises:
- NotFoundError if cannot find transcript file in storage.
- ValueError if transcript file is incorrect JSON.
- KeyError if transcript file has incorrect format.
'''
filename
=
'subs_{0}.srt.sjson'
.
format
(
subs_id
)
content_location
=
StaticContent
.
compute_location
(
self
.
location
.
org
,
self
.
location
.
course
,
filename
)
data
=
contentstore
()
.
find
(
content_location
)
.
data
text
=
json
.
loads
(
data
)[
'text'
]
return
HTMLParser
()
.
unescape
(
"
\n
"
.
join
(
text
))
@XBlock.handler
def
download_transcript
(
self
,
__
,
___
):
"""
This is called to get transcript file without timecodes to student.
"""
try
:
subs
=
self
.
get_transcript
(
self
.
sub
)
except
(
NotFoundError
):
log
.
debug
(
"Can't find content in storage for
%
s transcript"
,
self
.
sub
)
return
Response
(
status
=
404
)
except
(
ValueError
,
KeyError
):
log
.
debug
(
"Invalid transcript JSON."
)
return
Response
(
status
=
400
)
response
=
Response
(
subs
,
headerlist
=
[
(
'Content-Disposition'
,
'attachment; filename="{0}.txt"'
.
format
(
self
.
sub
)),
])
response
.
content_type
=
"text/plain; charset=utf-8"
return
response
class
VideoDescriptor
(
VideoFields
,
TabsEditingDescriptor
,
EmptyDataRawDescriptor
):
class
VideoDescriptor
(
VideoFields
,
TabsEditingDescriptor
,
EmptyDataRawDescriptor
):
"""Descriptor for `VideoModule`."""
"""Descriptor for `VideoModule`."""
module_class
=
VideoModule
module_class
=
VideoModule
download_transcript
=
module_attr
(
'download_transcript'
)
tabs
=
[
tabs
=
[
{
{
...
@@ -207,6 +276,12 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -207,6 +276,12 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
]
]
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
'''
`track` is deprecated field.
If `track` field exists show `track` field on front-end as not-editable
but clearable. Dropdown `download_track` is a new field and it has value
True.
'''
super
(
VideoDescriptor
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
VideoDescriptor
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
# For backwards compatibility -- if we've got XML data, parse
# For backwards compatibility -- if we've got XML data, parse
# it out and set the metadata fields
# it out and set the metadata fields
...
@@ -215,6 +290,24 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -215,6 +290,24 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
self
.
_field_data
.
set_many
(
self
,
field_data
)
self
.
_field_data
.
set_many
(
self
,
field_data
)
del
self
.
data
del
self
.
data
self
.
track_visible
=
False
if
self
.
track
:
self
.
track_visible
=
True
download_track
=
self
.
editable_metadata_fields
[
'download_track'
]
if
not
download_track
[
'explicitly_set'
]:
self
.
download_track
=
True
@property
def
editable_metadata_fields
(
self
):
editable_fields
=
super
(
VideoDescriptor
,
self
)
.
editable_metadata_fields
if
self
.
track_visible
:
editable_fields
[
'track'
][
'non_editable'
]
=
True
else
:
editable_fields
.
pop
(
'track'
)
return
editable_fields
@classmethod
@classmethod
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
def
from_xml
(
cls
,
xml_data
,
system
,
id_generator
):
"""
"""
...
@@ -265,6 +358,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -265,6 +358,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
'start_time'
:
self
.
start_time
,
'start_time'
:
self
.
start_time
,
'end_time'
:
self
.
end_time
,
'end_time'
:
self
.
end_time
,
'sub'
:
self
.
sub
,
'sub'
:
self
.
sub
,
'download_track'
:
json
.
dumps
(
self
.
download_track
),
}
}
for
key
,
value
in
attrs
.
items
():
for
key
,
value
in
attrs
.
items
():
# Mild workaround to ensure that tests pass -- if a field
# Mild workaround to ensure that tests pass -- if a field
...
@@ -282,6 +376,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
...
@@ -282,6 +376,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
ele
=
etree
.
Element
(
'track'
)
ele
=
etree
.
Element
(
'track'
)
ele
.
set
(
'src'
,
self
.
track
)
ele
.
set
(
'src'
,
self
.
track
)
xml
.
append
(
ele
)
xml
.
append
(
ele
)
return
xml
return
xml
def
get_context
(
self
):
def
get_context
(
self
):
...
...
lms/djangoapps/courseware/tests/__init__.py
View file @
4a36fa89
...
@@ -15,7 +15,6 @@ from edxmako.shortcuts import render_to_string
...
@@ -15,7 +15,6 @@ from edxmako.shortcuts import render_to_string
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
courseware.tests.modulestore_config
import
TEST_DATA_MIXED_MODULESTORE
from
courseware.tests.modulestore_config
import
TEST_DATA_MIXED_MODULESTORE
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xblock.fields
import
Scope
from
xmodule.tests
import
get_test_system
,
get_test_descriptor_system
from
xmodule.tests
import
get_test_system
,
get_test_descriptor_system
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
...
@@ -35,7 +34,7 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -35,7 +34,7 @@ class BaseTestXmodule(ModuleStoreTestCase):
Any xmodule should overwrite only next parameters for test:
Any xmodule should overwrite only next parameters for test:
1. CATEGORY
1. CATEGORY
2. DATA
2. DATA
or METADATA
3. MODEL_DATA
3. MODEL_DATA
4. COURSE_DATA and USER_COUNT if needed
4. COURSE_DATA and USER_COUNT if needed
...
@@ -48,6 +47,10 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -48,6 +47,10 @@ class BaseTestXmodule(ModuleStoreTestCase):
# Data from YAML common/lib/xmodule/xmodule/templates/NAME/default.yaml
# Data from YAML common/lib/xmodule/xmodule/templates/NAME/default.yaml
CATEGORY
=
"vertical"
CATEGORY
=
"vertical"
DATA
=
''
DATA
=
''
# METADATA must be overwritten for every instance that uses it. Otherwise,
# if we'll change it in the tests, it will be changed for all other instances
# of parent class.
METADATA
=
{}
MODEL_DATA
=
{
'data'
:
'<some_module></some_module>'
}
MODEL_DATA
=
{
'data'
:
'<some_module></some_module>'
}
def
new_module_runtime
(
self
):
def
new_module_runtime
(
self
):
...
@@ -71,8 +74,27 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -71,8 +74,27 @@ class BaseTestXmodule(ModuleStoreTestCase):
runtime
.
get_block
=
modulestore
()
.
get_item
runtime
.
get_block
=
modulestore
()
.
get_item
return
runtime
return
runtime
def
setUp
(
self
):
def
initialize_module
(
self
,
**
kwargs
):
kwargs
.
update
({
'parent_location'
:
self
.
section
.
location
,
'category'
:
self
.
CATEGORY
})
self
.
item_descriptor
=
ItemFactory
.
create
(
**
kwargs
)
self
.
runtime
=
self
.
new_descriptor_runtime
()
field_data
=
{}
field_data
.
update
(
self
.
MODEL_DATA
)
student_data
=
DictFieldData
(
field_data
)
self
.
item_descriptor
.
_field_data
=
LmsFieldData
(
self
.
item_descriptor
.
_field_data
,
student_data
)
self
.
item_descriptor
.
xmodule_runtime
=
self
.
new_module_runtime
()
self
.
item_module
=
self
.
item_descriptor
self
.
item_url
=
Location
(
self
.
item_module
.
location
)
.
url
()
def
setup_course
(
self
):
self
.
course
=
CourseFactory
.
create
(
data
=
self
.
COURSE_DATA
)
self
.
course
=
CourseFactory
.
create
(
data
=
self
.
COURSE_DATA
)
# Turn off cache.
# Turn off cache.
...
@@ -83,7 +105,7 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -83,7 +105,7 @@ class BaseTestXmodule(ModuleStoreTestCase):
parent_location
=
self
.
course
.
location
,
parent_location
=
self
.
course
.
location
,
category
=
"sequential"
,
category
=
"sequential"
,
)
)
section
=
ItemFactory
.
create
(
se
lf
.
se
ction
=
ItemFactory
.
create
(
parent_location
=
chapter
.
location
,
parent_location
=
chapter
.
location
,
category
=
"sequential"
category
=
"sequential"
)
)
...
@@ -97,24 +119,6 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -97,24 +119,6 @@ class BaseTestXmodule(ModuleStoreTestCase):
for
user
in
self
.
users
:
for
user
in
self
.
users
:
CourseEnrollmentFactory
.
create
(
user
=
user
,
course_id
=
self
.
course
.
id
)
CourseEnrollmentFactory
.
create
(
user
=
user
,
course_id
=
self
.
course
.
id
)
self
.
item_descriptor
=
ItemFactory
.
create
(
parent_location
=
section
.
location
,
category
=
self
.
CATEGORY
,
data
=
self
.
DATA
)
self
.
runtime
=
self
.
new_descriptor_runtime
()
field_data
=
{}
field_data
.
update
(
self
.
MODEL_DATA
)
student_data
=
DictFieldData
(
field_data
)
self
.
item_descriptor
.
_field_data
=
LmsFieldData
(
self
.
item_descriptor
.
_field_data
,
student_data
)
self
.
item_descriptor
.
xmodule_runtime
=
self
.
new_module_runtime
()
self
.
item_module
=
self
.
item_descriptor
self
.
item_url
=
Location
(
self
.
item_module
.
location
)
.
url
()
# login all users for acces to Xmodule
# login all users for acces to Xmodule
self
.
clients
=
{
user
.
username
:
Client
()
for
user
in
self
.
users
}
self
.
clients
=
{
user
.
username
:
Client
()
for
user
in
self
.
users
}
self
.
login_statuses
=
[
self
.
login_statuses
=
[
...
@@ -125,6 +129,10 @@ class BaseTestXmodule(ModuleStoreTestCase):
...
@@ -125,6 +129,10 @@ class BaseTestXmodule(ModuleStoreTestCase):
self
.
assertTrue
(
all
(
self
.
login_statuses
))
self
.
assertTrue
(
all
(
self
.
login_statuses
))
def
setUp
(
self
):
self
.
setup_course
();
self
.
initialize_module
(
metadata
=
self
.
METADATA
,
data
=
self
.
DATA
)
def
get_url
(
self
,
dispatch
):
def
get_url
(
self
,
dispatch
):
"""Return item url with dispatch."""
"""Return item url with dispatch."""
return
reverse
(
return
reverse
(
...
...
lms/djangoapps/courseware/tests/test_video_mongo.py
View file @
4a36fa89
This diff is collapsed.
Click to expand it.
lms/djangoapps/courseware/tests/test_video_xml.py
View file @
4a36fa89
...
@@ -35,7 +35,6 @@ SOURCE_XML = """
...
@@ -35,7 +35,6 @@ SOURCE_XML = """
>
>
<source src="example.mp4"/>
<source src="example.mp4"/>
<source src="example.webm"/>
<source src="example.webm"/>
<source src="example.ogv"/>
</video>
</video>
"""
"""
...
@@ -68,12 +67,10 @@ class VideoModuleUnitTest(unittest.TestCase):
...
@@ -68,12 +67,10 @@ class VideoModuleUnitTest(unittest.TestCase):
def
test_video_get_html
(
self
):
def
test_video_get_html
(
self
):
"""Make sure that all parameters extracted correclty from xml"""
"""Make sure that all parameters extracted correclty from xml"""
module
=
VideoFactory
.
create
()
module
=
VideoFactory
.
create
()
sources
=
{
sources
=
{
'main'
:
'example.mp4'
,
'main'
:
'example.mp4'
,
'mp4'
:
'example.mp4'
,
'mp4'
:
'example.mp4'
,
'webm'
:
'example.webm'
,
'webm'
:
'example.webm'
,
'ogv'
:
'example.ogv'
}
}
expected_context
=
{
expected_context
=
{
...
@@ -87,7 +84,7 @@ class VideoModuleUnitTest(unittest.TestCase):
...
@@ -87,7 +84,7 @@ class VideoModuleUnitTest(unittest.TestCase):
'show_captions'
:
'true'
,
'show_captions'
:
'true'
,
'sources'
:
sources
,
'sources'
:
sources
,
'youtube_streams'
:
_create_youtube_string
(
module
),
'youtube_streams'
:
_create_youtube_string
(
module
),
'track'
:
''
,
'track'
:
None
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
False
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
False
),
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
'yt_test_url'
:
'https://gdata.youtube.com/feeds/api/videos/'
'yt_test_url'
:
'https://gdata.youtube.com/feeds/api/videos/'
...
...
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