BLD-364: Merge "video sources" and "download video" fields.
This commit is contained in:
@@ -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
|
||||
the top. Include a label indicating the component affected.
|
||||
|
||||
Blades: Change the download video field to a dropdown that will allow students
|
||||
to download the first source listed in the alternate sources. BLD-364.
|
||||
|
||||
Blades: Change the track field to a dropdown that will allow students
|
||||
to download the transcript of the video without timecodes. BLD-368.
|
||||
|
||||
|
||||
@@ -40,12 +40,12 @@ def correct_video_settings(_step):
|
||||
|
||||
# advanced
|
||||
['Display Name', 'Video', False],
|
||||
['Download Video', '', False],
|
||||
['End Time', '00:00:00', False],
|
||||
['HTML5 Transcript', '', False],
|
||||
['Show Transcript', 'True', False],
|
||||
['Start Time', '00:00:00', False],
|
||||
['Transcript Download Allowed', 'False', False],
|
||||
['Video Download Allowed', 'False', False],
|
||||
['Video Sources', '', False],
|
||||
['Youtube ID', 'OEoXaMPEzfM', False],
|
||||
['Youtube ID for .75x speed', '', False],
|
||||
|
||||
@@ -106,7 +106,6 @@ function(BaseView, _, MetadataModel, AbstractEditor, VideoList) {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getValueFromEditor : function () {
|
||||
return this.$el.find('#' + this.uniqueId).val();
|
||||
},
|
||||
|
||||
@@ -187,6 +187,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
|
||||
show_captions="false"
|
||||
download_track="true"
|
||||
download_video="true"
|
||||
start_time="00:00:01"
|
||||
end_time="00:01:00">
|
||||
<source src="http://www.example.com/source.mp4"/>
|
||||
@@ -207,6 +208,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
'youtube_id_1_0': 'p2Q6BrNhdh8',
|
||||
'youtube_id_1_25': '1EeWXzPdhSA',
|
||||
'youtube_id_1_5': 'rABDYkeK0x8',
|
||||
'download_video': True,
|
||||
'show_captions': False,
|
||||
'start_time': datetime.timedelta(seconds=1),
|
||||
'end_time': datetime.timedelta(seconds=60),
|
||||
@@ -224,6 +226,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
show_captions="false"
|
||||
download_track="false"
|
||||
start_time="00:00:01"
|
||||
download_video="false"
|
||||
end_time="00:01:00">
|
||||
<source src="http://www.example.com/source.mp4"/>
|
||||
<track src="http://www.example.com/track"/>
|
||||
@@ -240,7 +243,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
'end_time': datetime.timedelta(seconds=60),
|
||||
'track': 'http://www.example.com/track',
|
||||
'download_track': False,
|
||||
'source': 'http://www.example.com/source.mp4',
|
||||
'download_video': False,
|
||||
'html5_sources': ['http://www.example.com/source.mp4'],
|
||||
'data': ''
|
||||
})
|
||||
@@ -269,7 +272,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
'end_time': datetime.timedelta(seconds=0.0),
|
||||
'track': '',
|
||||
'download_track': False,
|
||||
'source': 'http://www.example.com/source.mp4',
|
||||
'download_video': False,
|
||||
'html5_sources': ['http://www.example.com/source.mp4'],
|
||||
'data': ''
|
||||
})
|
||||
@@ -291,7 +294,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
'end_time': datetime.timedelta(seconds=0.0),
|
||||
'track': '',
|
||||
'download_track': False,
|
||||
'source': '',
|
||||
'download_video': False,
|
||||
'html5_sources': [],
|
||||
'data': ''
|
||||
})
|
||||
@@ -306,7 +309,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
<video display_name=""display_name""
|
||||
html5_sources="["source_1", "source_2"]"
|
||||
show_captions="false"
|
||||
source=""http://download_video""
|
||||
download_video="true"
|
||||
sub=""html5_subtitles""
|
||||
track=""http://download_track""
|
||||
download_track="true"
|
||||
@@ -327,7 +330,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
'end_time': datetime.timedelta(seconds=0.0),
|
||||
'track': 'http://download_track',
|
||||
'download_track': True,
|
||||
'source': 'http://download_video',
|
||||
'download_video': True,
|
||||
'html5_sources': ["source_1", "source_2"],
|
||||
'data': ''
|
||||
})
|
||||
@@ -350,7 +353,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
'end_time': datetime.timedelta(seconds=0.0),
|
||||
'track': '',
|
||||
'download_track': False,
|
||||
'source': '',
|
||||
'download_video': False,
|
||||
'html5_sources': [],
|
||||
'data': ''
|
||||
})
|
||||
@@ -364,6 +367,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
<video display_name="Test Video"
|
||||
youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
|
||||
show_captions="false"
|
||||
source="http://www.example.com/source.mp4"
|
||||
from="00:00:01"
|
||||
to="00:01:00">
|
||||
<source src="http://www.example.com/source.mp4"/>
|
||||
@@ -382,7 +386,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
|
||||
'track': 'http://www.example.com/track',
|
||||
'download_track': True,
|
||||
'html5_sources': ['http://www.example.com/source.mp4'],
|
||||
'data': ''
|
||||
'data': '',
|
||||
})
|
||||
|
||||
def test_old_video_data(self):
|
||||
@@ -473,10 +477,11 @@ class VideoExportTestCase(unittest.TestCase):
|
||||
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.download_video = True
|
||||
|
||||
xml = desc.definition_to_xml(None) # We don't use the `resource_fs` parameter
|
||||
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" download_track="true">
|
||||
<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_video="true" download_track="true">
|
||||
<source src="http://www.example.com/source.mp4"/>
|
||||
<source src="http://www.example.com/source.ogg"/>
|
||||
<track src="http://www.example.com/track"/>
|
||||
@@ -501,10 +506,11 @@ class VideoExportTestCase(unittest.TestCase):
|
||||
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.download_video = True
|
||||
|
||||
xml = desc.definition_to_xml(None) # We don't use the `resource_fs` parameter
|
||||
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" download_track="true">
|
||||
<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_video="true" download_track="true">
|
||||
<source src="http://www.example.com/source.mp4"/>
|
||||
<source src="http://www.example.com/source.ogg"/>
|
||||
<track src="http://www.example.com/track"/>
|
||||
|
||||
@@ -98,12 +98,20 @@ class VideoFields(object):
|
||||
)
|
||||
#front-end code of video player checks logical validity of (start_time, end_time) pair.
|
||||
|
||||
# `source` is deprecated field and should not be used in future.
|
||||
# `download_video` is used instead.
|
||||
source = String(
|
||||
help="The external URL to download the video. This appears as a link beneath the video.",
|
||||
help="The external URL to download the video.",
|
||||
display_name="Download Video",
|
||||
scope=Scope.settings,
|
||||
default=""
|
||||
)
|
||||
download_video = Boolean(
|
||||
help="Show a link beneath the video to allow students to download the video. Note: You must add at least one video source below.",
|
||||
display_name="Video Download Allowed",
|
||||
scope=Scope.settings,
|
||||
default=False
|
||||
)
|
||||
html5_sources = List(
|
||||
help="A list of filenames to be used with HTML5 video. The first supported filetype will be displayed.",
|
||||
display_name="Video Sources",
|
||||
@@ -181,7 +189,12 @@ class VideoModule(VideoFields, XModule):
|
||||
|
||||
get_ext = lambda filename: filename.rpartition('.')[-1]
|
||||
sources = {get_ext(src): src for src in self.html5_sources}
|
||||
sources['main'] = self.source
|
||||
|
||||
if self.download_video:
|
||||
if self.source:
|
||||
sources['main'] = self.source
|
||||
elif self.html5_sources:
|
||||
sources['main'] = self.html5_sources[0]
|
||||
|
||||
if self.download_track:
|
||||
if self.track:
|
||||
@@ -281,6 +294,14 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
|
||||
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.
|
||||
|
||||
`source` is deprecated field.
|
||||
a) If `source` exists and `source` is not `html5_sources`: show `source`
|
||||
field on front-end as not-editable but clearable. Dropdown is a new
|
||||
field `download_video` and it has value True.
|
||||
b) If `source` is cleared it is not shown anymore.
|
||||
c) If `source` exists and `source` in `html5_sources`, do not show `source`
|
||||
field. `download_video` field has value True.
|
||||
'''
|
||||
super(VideoDescriptor, self).__init__(*args, **kwargs)
|
||||
# For backwards compatibility -- if we've got XML data, parse
|
||||
@@ -290,21 +311,43 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
|
||||
self._field_data.set_many(self, field_data)
|
||||
del self.data
|
||||
|
||||
editable_fields = self.editable_metadata_fields
|
||||
|
||||
self.track_visible = False
|
||||
if self.track:
|
||||
self.track_visible = True
|
||||
download_track = self.editable_metadata_fields['download_track']
|
||||
download_track = editable_fields['download_track']
|
||||
if not download_track['explicitly_set']:
|
||||
self.download_track = True
|
||||
|
||||
self.source_visible = False
|
||||
if self.source:
|
||||
# If `source` field value exist in the `html5_sources` field values,
|
||||
# then delete `source` field value and use value from `html5_sources` field.
|
||||
if self.source in self.html5_sources:
|
||||
self.source = '' # Delete source field value.
|
||||
self.download_video = True
|
||||
else: # Otherwise, `source` field value will be used.
|
||||
self.source_visible = True
|
||||
download_video = editable_fields['download_video']
|
||||
if not download_video['explicitly_set']:
|
||||
self.download_video = 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')
|
||||
if hasattr(self, 'track_visible'):
|
||||
if self.track_visible:
|
||||
editable_fields['track']['non_editable'] = True
|
||||
else:
|
||||
editable_fields.pop('track')
|
||||
|
||||
if hasattr(self, 'source_visible'):
|
||||
if self.source_visible:
|
||||
editable_fields['source']['non_editable'] = True
|
||||
else:
|
||||
editable_fields.pop('source')
|
||||
|
||||
return editable_fields
|
||||
|
||||
@@ -359,6 +402,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
|
||||
'end_time': self.end_time,
|
||||
'sub': self.sub,
|
||||
'download_track': json.dumps(self.download_track),
|
||||
'download_video': json.dumps(self.download_video),
|
||||
}
|
||||
for key, value in attrs.items():
|
||||
# Mild workaround to ensure that tests pass -- if a field
|
||||
@@ -468,7 +512,6 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
|
||||
sources = xml.findall('source')
|
||||
if sources:
|
||||
field_data['html5_sources'] = [ele.get('src') for ele in sources]
|
||||
field_data['source'] = field_data['html5_sources'][0]
|
||||
|
||||
track = xml.find('track')
|
||||
if track is not None:
|
||||
|
||||
@@ -47,7 +47,7 @@ class TestVideoYouTube(TestVideo):
|
||||
METADATA = {}
|
||||
|
||||
def test_video_constructor(self):
|
||||
"""Make sure that all parameters extracted correclty from xml"""
|
||||
"""Make sure that all parameters extracted correctly from xml"""
|
||||
context = self.item_module.render('student_view').content
|
||||
|
||||
sources = {
|
||||
@@ -70,7 +70,7 @@ class TestVideoYouTube(TestVideo):
|
||||
'youtube_streams': _create_youtube_string(self.item_module),
|
||||
'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', False),
|
||||
'yt_test_timeout': 1500,
|
||||
'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/'
|
||||
'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/',
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
@@ -81,11 +81,11 @@ class TestVideoYouTube(TestVideo):
|
||||
|
||||
class TestVideoNonYouTube(TestVideo):
|
||||
"""Integration tests: web client + mongo."""
|
||||
|
||||
DATA = """
|
||||
<video show_captions="true"
|
||||
display_name="A Name"
|
||||
sub="a_sub_file.srt.sjson"
|
||||
download_video="true"
|
||||
start_time="01:00:03" end_time="01:00:10"
|
||||
>
|
||||
<source src="example.mp4"/>
|
||||
@@ -96,7 +96,6 @@ class TestVideoNonYouTube(TestVideo):
|
||||
'data': DATA
|
||||
}
|
||||
METADATA = {}
|
||||
|
||||
def test_video_constructor(self):
|
||||
"""Make sure that if the 'youtube' attribute is omitted in XML, then
|
||||
the template generates an empty string for the YouTube streams.
|
||||
@@ -123,7 +122,7 @@ class TestVideoNonYouTube(TestVideo):
|
||||
'youtube_streams': '1.00:OEoXaMPEzfM',
|
||||
'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
|
||||
'yt_test_timeout': 1500,
|
||||
'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/'
|
||||
'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/',
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
@@ -132,6 +131,386 @@ class TestVideoNonYouTube(TestVideo):
|
||||
)
|
||||
|
||||
|
||||
class TestGetHtmlMethod(BaseTestXmodule):
|
||||
'''
|
||||
Make sure that `get_html` works correctly.
|
||||
'''
|
||||
CATEGORY = "video"
|
||||
DATA = SOURCE_XML
|
||||
METADATA = {}
|
||||
|
||||
def setUp(self):
|
||||
self.setup_course();
|
||||
|
||||
def test_get_html_track(self):
|
||||
SOURCE_XML = """
|
||||
<video show_captions="true"
|
||||
display_name="A Name"
|
||||
sub="{sub}" download_track="{download_track}"
|
||||
start_time="01:00:03" end_time="01:00:10"
|
||||
>
|
||||
<source src="example.mp4"/>
|
||||
<source src="example.webm"/>
|
||||
{track}
|
||||
</video>
|
||||
"""
|
||||
|
||||
cases = [
|
||||
{
|
||||
'download_track': u'true',
|
||||
'track': u'<track src="http://www.example.com/track"/>',
|
||||
'sub': u'a_sub_file.srt.sjson',
|
||||
'expected_track_url': u'http://www.example.com/track',
|
||||
},
|
||||
{
|
||||
'download_track': u'true',
|
||||
'track': u'',
|
||||
'sub': u'a_sub_file.srt.sjson',
|
||||
'expected_track_url': u'a_sub_file.srt.sjson',
|
||||
},
|
||||
{
|
||||
'download_track': u'true',
|
||||
'track': u'',
|
||||
'sub': u'',
|
||||
'expected_track_url': None
|
||||
},
|
||||
{
|
||||
'download_track': u'false',
|
||||
'track': u'<track src="http://www.example.com/track"/>',
|
||||
'sub': u'a_sub_file.srt.sjson',
|
||||
'expected_track_url': None,
|
||||
},
|
||||
]
|
||||
|
||||
expected_context = {
|
||||
'data_dir': getattr(self, 'data_dir', None),
|
||||
'caption_asset_path': '/static/subs/',
|
||||
'show_captions': 'true',
|
||||
'display_name': u'A Name',
|
||||
'end': 3610.0,
|
||||
'id': None,
|
||||
'sources': {
|
||||
u'mp4': u'example.mp4',
|
||||
u'webm': u'example.webm'
|
||||
},
|
||||
'start': 3603.0,
|
||||
'sub': u'a_sub_file.srt.sjson',
|
||||
'track': '',
|
||||
'youtube_streams': '1.00:OEoXaMPEzfM',
|
||||
'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
|
||||
'yt_test_timeout': 1500,
|
||||
'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/',
|
||||
}
|
||||
|
||||
for data in cases:
|
||||
DATA = SOURCE_XML.format(
|
||||
download_track=data['download_track'],
|
||||
track=data['track'],
|
||||
sub=data['sub']
|
||||
)
|
||||
|
||||
self.initialize_module(data=DATA)
|
||||
track_url = self.item_descriptor.xmodule_runtime.handler_url(self.item_module, 'download_transcript')
|
||||
|
||||
expected_context.update({
|
||||
'track': track_url if data['expected_track_url'] == u'a_sub_file.srt.sjson' else data['expected_track_url'],
|
||||
'sub': data['sub'],
|
||||
'id': self.item_module.location.html_id(),
|
||||
})
|
||||
|
||||
context = self.item_module.render('student_view').content
|
||||
self.assertEqual(
|
||||
context,
|
||||
self.item_module.xmodule_runtime.render_template('video.html', expected_context)
|
||||
)
|
||||
|
||||
def test_get_html_source(self):
|
||||
SOURCE_XML = """
|
||||
<video show_captions="true"
|
||||
display_name="A Name"
|
||||
sub="a_sub_file.srt.sjson" source="{source}"
|
||||
download_video="{download_video}"
|
||||
start_time="01:00:03" end_time="01:00:10"
|
||||
>
|
||||
{sources}
|
||||
</video>
|
||||
"""
|
||||
cases = [
|
||||
# self.download_video == True
|
||||
{
|
||||
'download_video': 'true',
|
||||
'source': 'example_source.mp4',
|
||||
'sources': """
|
||||
<source src="example.mp4"/>
|
||||
<source src="example.webm"/>
|
||||
""",
|
||||
'result': {
|
||||
'main': u'example_source.mp4',
|
||||
u'mp4': u'example.mp4',
|
||||
u'webm': u'example.webm',
|
||||
},
|
||||
},
|
||||
{
|
||||
'download_video': 'true',
|
||||
'source': '',
|
||||
'sources': """
|
||||
<source src="example.mp4"/>
|
||||
<source src="example.webm"/>
|
||||
""",
|
||||
'result': {
|
||||
'main': u'example.mp4',
|
||||
u'mp4': u'example.mp4',
|
||||
u'webm': u'example.webm',
|
||||
},
|
||||
},
|
||||
{
|
||||
'download_video': 'true',
|
||||
'source': '',
|
||||
'sources': [],
|
||||
'result': {},
|
||||
},
|
||||
|
||||
# self.download_video == False
|
||||
{
|
||||
'download_video': 'false',
|
||||
'source': 'example_source.mp4',
|
||||
'sources': """
|
||||
<source src="example.mp4"/>
|
||||
<source src="example.webm"/>
|
||||
""",
|
||||
'result': {
|
||||
u'mp4': u'example.mp4',
|
||||
u'webm': u'example.webm',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
expected_context = {
|
||||
'data_dir': getattr(self, 'data_dir', None),
|
||||
'caption_asset_path': '/static/subs/',
|
||||
'show_captions': 'true',
|
||||
'display_name': u'A Name',
|
||||
'end': 3610.0,
|
||||
'id': None,
|
||||
'sources': None,
|
||||
'start': 3603.0,
|
||||
'sub': u'a_sub_file.srt.sjson',
|
||||
'track': None,
|
||||
'youtube_streams': '1.00:OEoXaMPEzfM',
|
||||
'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
|
||||
'yt_test_timeout': 1500,
|
||||
'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/',
|
||||
}
|
||||
|
||||
for data in cases:
|
||||
DATA = SOURCE_XML.format(
|
||||
download_video=data['download_video'],
|
||||
source=data['source'],
|
||||
sources=data['sources']
|
||||
)
|
||||
self.initialize_module(data=DATA)
|
||||
|
||||
expected_context.update({
|
||||
'sources': data['result'],
|
||||
'id': self.item_module.location.html_id(),
|
||||
})
|
||||
|
||||
context = self.item_module.render('student_view').content
|
||||
|
||||
self.assertEqual(
|
||||
context,
|
||||
self.item_module.xmodule_runtime.render_template('video.html', expected_context)
|
||||
)
|
||||
|
||||
|
||||
class TestVideoDescriptorInitialization(BaseTestXmodule):
|
||||
"""
|
||||
Make sure that module initialization works correctly.
|
||||
"""
|
||||
CATEGORY = "video"
|
||||
DATA = SOURCE_XML
|
||||
METADATA = {}
|
||||
|
||||
def setUp(self):
|
||||
self.setup_course();
|
||||
|
||||
def test_source_not_in_html5sources(self):
|
||||
metadata = {
|
||||
'source': 'http://example.org/video.mp4',
|
||||
'html5_sources': ['http://youtu.be/OEoXaMPEzfM.mp4'],
|
||||
}
|
||||
|
||||
self.initialize_module(metadata=metadata)
|
||||
fields = self.item_descriptor.editable_metadata_fields
|
||||
|
||||
self.assertIn('source', fields)
|
||||
self.assertEqual(self.item_module.source, 'http://example.org/video.mp4')
|
||||
self.assertTrue(self.item_module.download_video)
|
||||
self.assertTrue(self.item_module.source_visible)
|
||||
|
||||
def test_source_in_html5sources(self):
|
||||
metadata = {
|
||||
'source': 'http://example.org/video.mp4',
|
||||
'html5_sources': ['http://example.org/video.mp4'],
|
||||
}
|
||||
|
||||
self.initialize_module(metadata=metadata)
|
||||
fields = self.item_descriptor.editable_metadata_fields
|
||||
|
||||
self.assertNotIn('source', fields)
|
||||
self.assertTrue(self.item_module.download_video)
|
||||
self.assertFalse(self.item_module.source_visible)
|
||||
|
||||
@patch('xmodule.x_module.XModuleDescriptor.editable_metadata_fields', new_callable=PropertyMock)
|
||||
def test_download_video_is_explicitly_set(self, mock_editable_fields):
|
||||
mock_editable_fields.return_value = {
|
||||
'download_video': {
|
||||
'default_value': False,
|
||||
'explicitly_set': True,
|
||||
'display_name': 'Video Download Allowed',
|
||||
'help': 'Show a link beneath the video to allow students to download the video.',
|
||||
'type': 'Boolean',
|
||||
'value': False,
|
||||
'field_name': 'download_video',
|
||||
'options': [
|
||||
{'display_name': "True", "value": True},
|
||||
{'display_name': "False", "value": False}
|
||||
],
|
||||
},
|
||||
'html5_sources': {
|
||||
'default_value': [],
|
||||
'explicitly_set': False,
|
||||
'display_name': 'Video Sources',
|
||||
'help': 'A list of filenames to be used with HTML5 video.',
|
||||
'type': 'List',
|
||||
'value': [u'http://youtu.be/OEoXaMPEzfM.mp4'],
|
||||
'field_name': 'html5_sources',
|
||||
'options': [],
|
||||
},
|
||||
'source': {
|
||||
'default_value': '',
|
||||
'explicitly_set': False,
|
||||
'display_name': 'Download Video',
|
||||
'help': 'The external URL to download the video.',
|
||||
'type': 'Generic',
|
||||
'value': u'http://example.org/video.mp4',
|
||||
'field_name': 'source',
|
||||
'options': [],
|
||||
},
|
||||
'track': {
|
||||
'default_value': '',
|
||||
'explicitly_set': False,
|
||||
'display_name': 'Download Transcript',
|
||||
'help': 'The external URL to download the timed transcript track.',
|
||||
'type': 'Generic',
|
||||
'value': u'',
|
||||
'field_name': 'track',
|
||||
'options': [],
|
||||
},
|
||||
}
|
||||
metadata = {
|
||||
'track': '',
|
||||
'source': 'http://example.org/video.mp4',
|
||||
'html5_sources': ['http://youtu.be/OEoXaMPEzfM.mp4'],
|
||||
}
|
||||
|
||||
self.initialize_module(metadata=metadata)
|
||||
fields = self.item_descriptor.editable_metadata_fields
|
||||
|
||||
self.assertIn('source', fields)
|
||||
self.assertFalse(self.item_module.download_video)
|
||||
self.assertTrue(self.item_module.source_visible)
|
||||
|
||||
def test_source_is_empty(self):
|
||||
metadata = {
|
||||
'source': '',
|
||||
'html5_sources': ['http://youtu.be/OEoXaMPEzfM.mp4'],
|
||||
}
|
||||
|
||||
self.initialize_module(metadata=metadata)
|
||||
fields = self.item_descriptor.editable_metadata_fields
|
||||
|
||||
self.assertNotIn('source', fields)
|
||||
self.assertFalse(self.item_module.download_video)
|
||||
|
||||
def test_track_is_not_empty(self):
|
||||
metatdata = {
|
||||
'track': 'http://example.org/track',
|
||||
}
|
||||
|
||||
self.initialize_module(metadata=metatdata)
|
||||
fields = self.item_descriptor.editable_metadata_fields
|
||||
|
||||
self.assertIn('track', fields)
|
||||
self.assertEqual(self.item_module.track, 'http://example.org/track')
|
||||
self.assertTrue(self.item_module.download_track)
|
||||
self.assertTrue(self.item_module.track_visible)
|
||||
|
||||
@patch('xmodule.x_module.XModuleDescriptor.editable_metadata_fields', new_callable=PropertyMock)
|
||||
def test_download_track_is_explicitly_set(self, mock_editable_fields):
|
||||
mock_editable_fields.return_value = {
|
||||
'download_track': {
|
||||
'default_value': False,
|
||||
'explicitly_set': True,
|
||||
'display_name': 'Transcript Download Allowed',
|
||||
'help': 'Show a link beneath the video to allow students to download the transcript.',
|
||||
'type': 'Boolean',
|
||||
'value': False,
|
||||
'field_name': 'download_track',
|
||||
'options': [
|
||||
{'display_name': "True", "value": True},
|
||||
{'display_name': "False", "value": False}
|
||||
],
|
||||
},
|
||||
'track': {
|
||||
'default_value': '',
|
||||
'explicitly_set': False,
|
||||
'display_name': 'Download Transcript',
|
||||
'help': 'The external URL to download the timed transcript track.',
|
||||
'type': 'Generic',
|
||||
'value': u'http://example.org/track',
|
||||
'field_name': 'track',
|
||||
'options': [],
|
||||
},
|
||||
'source': {
|
||||
'default_value': '',
|
||||
'explicitly_set': False,
|
||||
'display_name': 'Download Video',
|
||||
'help': 'The external URL to download the video.',
|
||||
'type': 'Generic',
|
||||
'value': u'',
|
||||
'field_name': 'source',
|
||||
'options': [],
|
||||
},
|
||||
}
|
||||
metadata = {
|
||||
'source': '',
|
||||
'track': 'http://example.org/track',
|
||||
}
|
||||
|
||||
self.initialize_module(metadata=metadata)
|
||||
fields = self.item_descriptor.editable_metadata_fields
|
||||
|
||||
self.assertIn('track', fields)
|
||||
self.assertEqual(self.item_module.track, 'http://example.org/track')
|
||||
self.assertFalse(self.item_module.download_track)
|
||||
self.assertTrue(self.item_module.track_visible)
|
||||
|
||||
|
||||
def test_track_is_empty(self):
|
||||
metatdata = {
|
||||
'track': '',
|
||||
}
|
||||
|
||||
self.initialize_module(metadata=metatdata)
|
||||
fields = self.item_descriptor.editable_metadata_fields
|
||||
|
||||
self.assertNotIn('track', fields)
|
||||
self.assertEqual(self.item_module.track, '')
|
||||
self.assertFalse(self.item_module.download_track)
|
||||
self.assertFalse(self.item_module.track_visible)
|
||||
|
||||
|
||||
class TestVideoGetTranscriptsMethod(TestVideo):
|
||||
"""
|
||||
Make sure that `get_transcript` method works correctly
|
||||
@@ -177,9 +556,7 @@ class TestVideoGetTranscriptsMethod(TestVideo):
|
||||
text = item.get_transcript(subs_id)
|
||||
expected_text = "Hi, welcome to Edx.\nLet's start with what is on your screen right now."
|
||||
|
||||
self.assertEqual(
|
||||
text, expected_text
|
||||
)
|
||||
self.assertEqual(text, expected_text)
|
||||
|
||||
def test_not_found_error(self):
|
||||
self.item_module.render('student_view')
|
||||
@@ -192,9 +569,7 @@ class TestVideoGetTranscriptsMethod(TestVideo):
|
||||
self.item_module.render('student_view')
|
||||
item = self.item_descriptor.xmodule_runtime.xmodule_instance
|
||||
|
||||
good_sjson = _create_file(content="""
|
||||
bad content
|
||||
""")
|
||||
good_sjson = _create_file(content='bad content')
|
||||
|
||||
_upload_file(good_sjson, self.item_module.location)
|
||||
subs_id = _get_subs_id(good_sjson.name)
|
||||
@@ -226,179 +601,6 @@ class TestVideoGetTranscriptsMethod(TestVideo):
|
||||
item.get_transcript(subs_id)
|
||||
|
||||
|
||||
class TestGetHtmlMethod(BaseTestXmodule):
|
||||
"""
|
||||
Make sure that `get_html` works correctly.
|
||||
"""
|
||||
CATEGORY = "video"
|
||||
DATA = SOURCE_XML
|
||||
METADATA = {}
|
||||
|
||||
def setUp(self):
|
||||
self.setup_course();
|
||||
|
||||
def test_get_html_track(self):
|
||||
SOURCE_XML = """
|
||||
<video show_captions="true"
|
||||
display_name="A Name"
|
||||
sub="{sub}" download_track="{download_track}"
|
||||
start_time="01:00:03" end_time="01:00:10"
|
||||
>
|
||||
<source src="example.mp4"/>
|
||||
<source src="example.webm"/>
|
||||
{track}
|
||||
</video>
|
||||
"""
|
||||
|
||||
cases = [
|
||||
{
|
||||
'download_track': u'true',
|
||||
'track': u'<track src="http://www.example.com/track"/>',
|
||||
'sub': u'a_sub_file.srt.sjson',
|
||||
'expected_track_url': u'http://www.example.com/track',
|
||||
},
|
||||
{
|
||||
'download_track': u'true',
|
||||
'track': u'',
|
||||
'sub': u'a_sub_file.srt.sjson',
|
||||
'expected_track_url': u'a_sub_file.srt.sjson',
|
||||
},
|
||||
{
|
||||
'download_track': u'true',
|
||||
'track': u'',
|
||||
'sub': u'',
|
||||
'expected_track_url': None
|
||||
},
|
||||
{
|
||||
'download_track': u'false',
|
||||
'track': u'<track src="http://www.example.com/track"/>',
|
||||
'sub': u'a_sub_file.srt.sjson',
|
||||
'expected_track_url': None,
|
||||
}
|
||||
]
|
||||
|
||||
expected_context = {
|
||||
'data_dir': getattr(self, 'data_dir', None),
|
||||
'caption_asset_path': '/static/subs/',
|
||||
'show_captions': 'true',
|
||||
'display_name': u'A Name',
|
||||
'end': 3610.0,
|
||||
'id': None,
|
||||
'sources': {
|
||||
'main': u'example.mp4',
|
||||
u'mp4': u'example.mp4',
|
||||
u'webm': u'example.webm'
|
||||
},
|
||||
'start': 3603.0,
|
||||
'sub': u'a_sub_file.srt.sjson',
|
||||
'track': '',
|
||||
'youtube_streams': '1.00:OEoXaMPEzfM',
|
||||
'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
|
||||
'yt_test_timeout': 1500,
|
||||
'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/'
|
||||
}
|
||||
|
||||
for data in cases:
|
||||
DATA = SOURCE_XML.format(
|
||||
download_track=data['download_track'],
|
||||
track=data['track'],
|
||||
sub=data['sub'],
|
||||
)
|
||||
|
||||
self.initialize_module(data=DATA)
|
||||
track_url = self.item_descriptor.xmodule_runtime.handler_url(self.item_module, 'download_transcript')
|
||||
|
||||
expected_context.update({
|
||||
'track': track_url if data['expected_track_url'] == u'a_sub_file.srt.sjson' else data['expected_track_url'],
|
||||
'sub': data['sub'],
|
||||
'id': self.item_module.location.html_id(),
|
||||
})
|
||||
|
||||
context = self.item_module.render('student_view').content
|
||||
self.assertEqual(
|
||||
context,
|
||||
self.item_module.xmodule_runtime.render_template('video.html', expected_context)
|
||||
)
|
||||
|
||||
|
||||
class TestVideoDescriptorInitialization(BaseTestXmodule):
|
||||
"""
|
||||
Make sure that module initialization works correctly.
|
||||
"""
|
||||
CATEGORY = "video"
|
||||
DATA = SOURCE_XML
|
||||
METADATA = {}
|
||||
|
||||
def setUp(self):
|
||||
self.setup_course();
|
||||
|
||||
def test_track_is_not_empty(self):
|
||||
metatdata = {
|
||||
'track': 'http://example.org/track',
|
||||
}
|
||||
|
||||
self.initialize_module(metadata=metatdata)
|
||||
fields = self.item_descriptor.editable_metadata_fields
|
||||
|
||||
self.assertIn('track', fields)
|
||||
self.assertEqual(self.item_module.track, 'http://example.org/track')
|
||||
self.assertTrue(self.item_module.download_track)
|
||||
self.assertTrue(self.item_module.track_visible)
|
||||
|
||||
@patch('xmodule.x_module.XModuleDescriptor.editable_metadata_fields', new_callable=PropertyMock)
|
||||
def test_download_track_is_explicitly_set(self, mock_editable_fields):
|
||||
mock_editable_fields.return_value = {
|
||||
'download_track': {
|
||||
'default_value': False,
|
||||
'explicitly_set': True,
|
||||
'display_name': 'Transcript Download Allowed',
|
||||
'help': 'Show a link beneath the video to allow students to download the transcript.',
|
||||
'type': 'Boolean',
|
||||
'value': False,
|
||||
'field_name': 'download_track',
|
||||
'options': [
|
||||
{'display_name': "True", "value": True},
|
||||
{'display_name': "False", "value": False}
|
||||
]
|
||||
},
|
||||
'track': {
|
||||
'default_value': '',
|
||||
'explicitly_set': False,
|
||||
'display_name': 'Download Transcript',
|
||||
'help': 'The external URL to download the timed transcript track.',
|
||||
'type': 'Generic',
|
||||
'value': u'http://example.org/track',
|
||||
'field_name': 'track',
|
||||
'options': []
|
||||
},
|
||||
}
|
||||
metadata = {
|
||||
'track': 'http://example.org/track',
|
||||
}
|
||||
|
||||
self.initialize_module(metadata=metadata)
|
||||
fields = self.item_descriptor.editable_metadata_fields
|
||||
|
||||
self.assertIn('track', fields)
|
||||
self.assertEqual(self.item_module.track, 'http://example.org/track')
|
||||
self.assertFalse(self.item_module.download_track)
|
||||
self.assertTrue(self.item_module.track_visible)
|
||||
|
||||
|
||||
def test_track_is_empty(self):
|
||||
metatdata = {
|
||||
'track': '',
|
||||
}
|
||||
|
||||
self.initialize_module(metadata=metatdata)
|
||||
fields = self.item_descriptor.editable_metadata_fields
|
||||
|
||||
self.assertNotIn('track', fields)
|
||||
self.assertEqual(self.item_module.track, '')
|
||||
self.assertFalse(self.item_module.download_track)
|
||||
self.assertFalse(self.item_module.track_visible)
|
||||
|
||||
|
||||
def _clear_assets(location):
|
||||
store = contentstore()
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ SOURCE_XML = """
|
||||
display_name="A Name"
|
||||
youtube="0.75:jNCf2gIqpeE,1.0:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg"
|
||||
sub="a_sub_file.srt.sjson"
|
||||
download_video="true"
|
||||
start_time="01:00:03" end_time="01:00:10"
|
||||
>
|
||||
<source src="example.mp4"/>
|
||||
|
||||
Reference in New Issue
Block a user