refactor: Convert BuiltIn Video Block's html from mako to django template (#37509)
Closes: https://github.com/openedx/public-engineering/issues/427
This commit is contained in:
@@ -3,8 +3,6 @@ Helpers for courseware tests.
|
||||
"""
|
||||
|
||||
|
||||
import ast
|
||||
import re
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from datetime import timedelta
|
||||
@@ -444,17 +442,27 @@ def get_expiration_banner_text(user, course, language='en'): # lint-amnesty, py
|
||||
return bannerText
|
||||
|
||||
|
||||
def get_context_dict_from_string(data):
|
||||
def get_context_from_dict(data):
|
||||
"""
|
||||
Retrieve dictionary from string.
|
||||
"""
|
||||
# Replace tuple and un-necessary info from inside string and get the dictionary.
|
||||
cleaned_data = data.split('((\'video.html\',')[1].replace("),\n {})", '').strip()
|
||||
# Omit user_id validation
|
||||
cleaned_data_without_user = re.sub(".*user_id.*\n?", '', cleaned_data)
|
||||
Retrieve validated dictionary from template's contextual data.
|
||||
|
||||
Args:
|
||||
data: The context dictionary to validate
|
||||
|
||||
Returns:
|
||||
dict: context dictionary
|
||||
"""
|
||||
# Make a copy to avoid modifying the original dict
|
||||
validated_data = data.copy()
|
||||
|
||||
# Omit user_id validation
|
||||
validated_data.pop('user_id', None)
|
||||
|
||||
# Handle metadata field - parse and sort to ensure consistent ordering
|
||||
if 'metadata' in validated_data and validated_data['metadata'] is not None:
|
||||
metadata_dict = json.loads(validated_data['metadata'])
|
||||
validated_data['metadata'] = OrderedDict(
|
||||
sorted(metadata_dict.items(), key=lambda t: t[0])
|
||||
)
|
||||
|
||||
validated_data = ast.literal_eval(cleaned_data_without_user)
|
||||
validated_data['metadata'] = OrderedDict(
|
||||
sorted(json.loads(validated_data['metadata']).items(), key=lambda t: t[0])
|
||||
)
|
||||
return validated_data
|
||||
|
||||
@@ -46,7 +46,7 @@ from xmodule.exceptions import NotFoundError
|
||||
from xmodule.modulestore.inheritance import own_metadata
|
||||
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE
|
||||
# noinspection PyUnresolvedReferences
|
||||
from xmodule.tests.helpers import override_descriptor_system # pylint: disable=unused-import
|
||||
from xmodule.tests.helpers import mock_render_template, override_descriptor_system # pylint: disable=unused-import
|
||||
from xmodule.tests.test_import import DummyModuleStoreRuntime
|
||||
from xmodule.tests.test_video import VideoBlockTestBase
|
||||
from xmodule.video_block import VideoBlock, bumper_utils, video_utils
|
||||
@@ -55,7 +55,7 @@ from xmodule.video_block.video_block import EXPORT_IMPORT_COURSE_DIR, EXPORT_IMP
|
||||
from xmodule.x_module import PUBLIC_VIEW, STUDENT_VIEW
|
||||
|
||||
from common.djangoapps.xblock_django.constants import ATTR_KEY_REQUEST_COUNTRY_CODE
|
||||
from lms.djangoapps.courseware.tests.helpers import get_context_dict_from_string
|
||||
from lms.djangoapps.courseware.tests.helpers import get_context_from_dict
|
||||
from openedx.core.djangoapps.video_config.toggles import PUBLIC_VIDEO_SHARE
|
||||
from openedx.core.djangoapps.video_config import sharing
|
||||
from openedx.core.djangoapps.video_pipeline.config.waffle import DEPRECATE_YOUTUBE
|
||||
@@ -82,9 +82,10 @@ TRANSCRIPT_FILE_SJSON_DATA = """{\n "start": [10],\n "end": [100],\n "text
|
||||
class TestVideoYouTube(TestVideo): # lint-amnesty, pylint: disable=missing-class-docstring, test-inherits-tests
|
||||
METADATA = {}
|
||||
|
||||
def test_video_constructor(self):
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_video_constructor(self, mock_render_django_template):
|
||||
"""Make sure that all parameters extracted correctly from xml"""
|
||||
context = self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
sources = ['example.mp4', 'example.webm']
|
||||
|
||||
expected_context = {
|
||||
@@ -145,9 +146,11 @@ class TestVideoYouTube(TestVideo): # lint-amnesty, pylint: disable=missing-clas
|
||||
'video_id': '',
|
||||
}
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_lms_template('video.html', expected_context))
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
# Validate and compare contexts
|
||||
assert get_context_from_dict(actual_context) == get_context_from_dict(expected_context)
|
||||
|
||||
|
||||
class TestVideoNonYouTube(TestVideo): # pylint: disable=test-inherits-tests
|
||||
@@ -168,11 +171,12 @@ class TestVideoNonYouTube(TestVideo): # pylint: disable=test-inherits-tests
|
||||
}
|
||||
METADATA = {}
|
||||
|
||||
def test_video_constructor(self):
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_video_constructor(self, mock_render_django_template):
|
||||
"""Make sure that if the 'youtube' attribute is omitted in XML, then
|
||||
the template generates an empty string for the YouTube streams.
|
||||
"""
|
||||
context = self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
sources = ['example.mp4', 'example.webm']
|
||||
|
||||
expected_context = {
|
||||
@@ -233,13 +237,17 @@ class TestVideoNonYouTube(TestVideo): # pylint: disable=test-inherits-tests
|
||||
'video_id': '',
|
||||
}
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
expected_result = get_context_dict_from_string(
|
||||
mako_service.render_lms_template('video.html', expected_context)
|
||||
)
|
||||
assert get_context_dict_from_string(context) == expected_result
|
||||
assert expected_result['download_video_link'] == 'example.mp4'
|
||||
assert expected_result['display_name'] == 'A Name'
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
# Validate and compare contexts
|
||||
validated_actual = get_context_from_dict(actual_context)
|
||||
validated_expected = get_context_from_dict(expected_context)
|
||||
assert validated_actual == validated_expected
|
||||
|
||||
# Verify specific fields
|
||||
assert validated_actual['download_video_link'] == 'example.mp4'
|
||||
assert validated_actual['display_name'] == 'A Name'
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -323,15 +331,19 @@ class TestVideoPublicAccess(BaseTestVideoXBlock):
|
||||
self.assertEqual(self.block.public_access, is_public_sharing_enabled)
|
||||
|
||||
@ddt.data(False, True)
|
||||
def test_context(self, is_public_sharing_enabled):
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_context(self, is_public_sharing_enabled, mock_render_django_template):
|
||||
with self.mock_feature_toggle():
|
||||
with patch.object(
|
||||
sharing,
|
||||
'is_public_sharing_enabled',
|
||||
return_value=is_public_sharing_enabled
|
||||
):
|
||||
content = self.block.student_view(None).content
|
||||
context = get_context_dict_from_string(content)
|
||||
self.block.student_view(None)
|
||||
|
||||
# Get the actual context that was passed to render_django_template
|
||||
context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
assert ('public_sharing_enabled' in context) == is_public_sharing_enabled
|
||||
assert ('public_video_url' in context) == is_public_sharing_enabled
|
||||
|
||||
@@ -391,7 +403,8 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
self.block, handler, suffix
|
||||
).rstrip('/?')
|
||||
|
||||
def test_get_html_track(self):
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_get_html_track(self, mock_render_django_template):
|
||||
# pylint: disable=invalid-name
|
||||
# lint-amnesty, pylint: disable=redefined-outer-name
|
||||
SOURCE_XML = """
|
||||
@@ -493,7 +506,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
self.initialize_block(data=DATA)
|
||||
track_url = self.get_handler_url('transcript', 'download')
|
||||
|
||||
context = self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
metadata.update({
|
||||
'transcriptLanguages': {"en": "English"} if not data['transcripts'] else {"uk": 'Українська'},
|
||||
'transcriptLanguage': 'en' if not data['transcripts'] or data.get('sub') else 'uk',
|
||||
@@ -516,11 +529,14 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'metadata': json.dumps(metadata)
|
||||
})
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_lms_template('video.html', expected_context))
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
def test_get_html_source(self):
|
||||
# Validate and compare contexts
|
||||
assert get_context_from_dict(actual_context) == get_context_from_dict(expected_context)
|
||||
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_get_html_source(self, mock_render_django_template):
|
||||
# lint-amnesty, pylint: disable=invalid-name, redefined-outer-name
|
||||
SOURCE_XML = """
|
||||
<video show_captions="true"
|
||||
@@ -619,7 +635,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
name=data['name'],
|
||||
)
|
||||
self.initialize_block(data=DATA)
|
||||
context = self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
|
||||
expected_context = dict(initial_context)
|
||||
expected_context['metadata'].update({
|
||||
@@ -638,11 +654,14 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_lms_template('video.html', expected_context))
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
def test_get_html_with_non_existent_edx_video_id(self):
|
||||
# Validate and compare contexts
|
||||
assert get_context_from_dict(actual_context) == get_context_from_dict(expected_context)
|
||||
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_get_html_with_non_existent_edx_video_id(self, mock_render_django_template):
|
||||
"""
|
||||
Tests the VideoBlock get_html where a edx_video_id is given but a video is not found
|
||||
"""
|
||||
@@ -684,9 +703,18 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
|
||||
# Referencing a non-existent VAL ID in courseware won't cause an error --
|
||||
# it'll just fall back to the values in the VideoBlock.
|
||||
assert 'example.mp4' in self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
|
||||
def test_get_html_with_mocked_edx_video_id(self):
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
# Verify it falls back to the sources defined in the VideoBlock XML
|
||||
assert actual_context['download_video_link'] == 'example.mp4'
|
||||
metadata_dict = json.loads(actual_context['metadata'])
|
||||
assert sorted(metadata_dict['sources']) == sorted(['example.mp4', 'example.webm'])
|
||||
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_get_html_with_mocked_edx_video_id(self, mock_render_django_template):
|
||||
# lint-amnesty, pylint: disable=invalid-name, redefined-outer-name
|
||||
SOURCE_XML = """
|
||||
<video show_captions="true"
|
||||
@@ -771,93 +799,28 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
}
|
||||
]
|
||||
}
|
||||
context = self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
expected_context = dict(initial_context)
|
||||
expected_context['metadata'].update({
|
||||
'transcriptTranslationUrl': self.get_handler_url('transcript', 'translation/__lang__'),
|
||||
'transcriptAvailableTranslationsUrl': self.get_handler_url('transcript', 'available_translations'),
|
||||
'publishCompletionUrl': self.get_handler_url('publish_completion', ''),
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'sources': data['result']['sources'],
|
||||
})
|
||||
expected_context.update({
|
||||
'id': self.block.location.html_id(),
|
||||
'block_id': str(self.block.location),
|
||||
'course_id': str(self.block.location.course_key),
|
||||
'download_video_link': data['result']['download_video_link'],
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
expected_context = dict(initial_context)
|
||||
expected_context['metadata'].update({
|
||||
'transcriptTranslationUrl': self.get_handler_url('transcript', 'translation/__lang__'),
|
||||
'transcriptAvailableTranslationsUrl': self.get_handler_url('transcript', 'available_translations'),
|
||||
'publishCompletionUrl': self.get_handler_url('publish_completion', ''),
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'sources': data['result']['sources'],
|
||||
})
|
||||
expected_context.update({
|
||||
'id': self.block.location.html_id(),
|
||||
'block_id': str(self.block.location),
|
||||
'course_id': str(self.block.location.course_key),
|
||||
'download_video_link': data['result']['download_video_link'],
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_lms_template('video.html', expected_context))
|
||||
|
||||
def test_get_html_with_existing_edx_video_id(self):
|
||||
"""
|
||||
Tests the `VideoBlock` `get_html` where `edx_video_id` is given and related video is found
|
||||
"""
|
||||
edx_video_id = 'thundercats'
|
||||
# create video with provided edx_video_id and return encoded_videos
|
||||
encoded_videos = self.encode_and_create_video(edx_video_id)
|
||||
# data to be used to retrieve video by edxval API
|
||||
data = {
|
||||
'download_video': 'true',
|
||||
'source': 'example_source.mp4',
|
||||
'sources': """
|
||||
<source src="example.mp4"/>
|
||||
<source src="example.webm"/>
|
||||
""",
|
||||
'edx_video_id': edx_video_id,
|
||||
'result': {
|
||||
'download_video_link': f'http://fake-video.edx.org/{edx_video_id}.mp4',
|
||||
'is_video_from_same_origin': True,
|
||||
'sources': ['http://fake-video.edx.org/example.mp4', 'http://fake-video.edx.org/example.webm'] +
|
||||
[video['url'] for video in encoded_videos],
|
||||
},
|
||||
}
|
||||
with override_settings(VIDEO_CDN_URL={'default': 'http://fake-video.edx.org'}):
|
||||
# context returned by get_html when provided with above data
|
||||
# expected_context, a dict to assert with context
|
||||
context, expected_context = self.helper_get_html_with_edx_video_id(data)
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_lms_template('video.html', expected_context))
|
||||
|
||||
def test_get_html_with_existing_unstripped_edx_video_id(self):
|
||||
"""
|
||||
Tests the `VideoBlock` `get_html` where `edx_video_id` with some unwanted tab(\t)
|
||||
is given and related video is found
|
||||
"""
|
||||
edx_video_id = 'thundercats'
|
||||
# create video with provided edx_video_id and return encoded_videos
|
||||
encoded_videos = self.encode_and_create_video(edx_video_id)
|
||||
# data to be used to retrieve video by edxval API
|
||||
# unstripped edx_video_id is provided here
|
||||
data = {
|
||||
'download_video': 'true',
|
||||
'source': 'example_source.mp4',
|
||||
'sources': """
|
||||
<source src="example.mp4"/>
|
||||
<source src="example.webm"/>
|
||||
""",
|
||||
'edx_video_id': f"{edx_video_id}\t",
|
||||
'result': {
|
||||
'download_video_link': f'http://fake-video.edx.org/{edx_video_id}.mp4',
|
||||
'is_video_from_same_origin': True,
|
||||
'sources': ['http://fake-video.edx.org/example.mp4', 'http://fake-video.edx.org/example.webm'] +
|
||||
[video['url'] for video in encoded_videos],
|
||||
},
|
||||
}
|
||||
with override_settings(VIDEO_CDN_URL={'default': 'http://fake-video.edx.org'}):
|
||||
# context returned by get_html when provided with above data
|
||||
# expected_context, a dict to assert with context
|
||||
context, expected_context = self.helper_get_html_with_edx_video_id(data)
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_lms_template('video.html', expected_context))
|
||||
# Validate and compare contexts
|
||||
assert get_context_from_dict(actual_context) == get_context_from_dict(expected_context)
|
||||
|
||||
def encode_and_create_video(self, edx_video_id):
|
||||
"""
|
||||
@@ -966,8 +929,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
return context, expected_context
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
@patch('xmodule.video_block.video_block.rewrite_video_url')
|
||||
def test_get_html_cdn_source(self, mocked_get_video):
|
||||
def test_get_html_cdn_source(self, mocked_get_video, mock_render_django_template):
|
||||
"""
|
||||
Test if sources got from CDN
|
||||
"""
|
||||
@@ -1056,7 +1020,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
user_service = self.block.runtime.service(self.block, 'user')
|
||||
user_location = user_service.get_current_user().opt_attrs[ATTR_KEY_REQUEST_COUNTRY_CODE]
|
||||
assert user_location == 'CN'
|
||||
context = self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
expected_context = dict(initial_context)
|
||||
expected_context['metadata'].update({
|
||||
'transcriptTranslationUrl': self.get_handler_url('transcript', 'translation/__lang__'),
|
||||
@@ -1073,12 +1037,15 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_lms_template('video.html', expected_context))
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
# Validate and compare contexts
|
||||
assert get_context_from_dict(actual_context) == get_context_from_dict(expected_context)
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def test_get_html_cdn_source_external_video(self):
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_get_html_cdn_source_external_video(self, mock_render_django_template):
|
||||
"""
|
||||
Test that video from an external source loads successfully.
|
||||
|
||||
@@ -1164,7 +1131,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'client_video_id': 'external video',
|
||||
'encoded_videos': {}
|
||||
}
|
||||
context = self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
expected_context = dict(initial_context)
|
||||
expected_context['metadata'].update({
|
||||
'transcriptTranslationUrl': self.get_handler_url('transcript', 'translation/__lang__'),
|
||||
@@ -1181,16 +1148,19 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_lms_template('video.html', expected_context))
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
# Validate and compare contexts (handles user_id and metadata ordering)
|
||||
assert get_context_from_dict(actual_context) == get_context_from_dict(expected_context)
|
||||
|
||||
@ddt.data(
|
||||
(True, ['youtube', 'desktop_webm', 'desktop_mp4', 'hls']),
|
||||
(False, ['youtube', 'desktop_webm', 'desktop_mp4'])
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_get_html_on_toggling_hls_feature(self, hls_feature_enabled, expected_val_profiles):
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_get_html_on_toggling_hls_feature(self, hls_feature_enabled, expected_val_profiles, _):
|
||||
"""
|
||||
Verify val profiles on toggling HLS Playback feature.
|
||||
"""
|
||||
@@ -1213,12 +1183,13 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
expected_val_profiles,
|
||||
)
|
||||
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
@patch(
|
||||
'openedx.core.djangoapps.video_config.services.VideoConfigService.is_hls_playback_enabled',
|
||||
Mock(return_value=True)
|
||||
)
|
||||
@patch('xmodule.video_block.video_block.edxval_api.get_urls_for_profiles')
|
||||
def test_get_html_hls(self, get_urls_for_profiles):
|
||||
def test_get_html_hls(self, get_urls_for_profiles, mock_render_django_template):
|
||||
"""
|
||||
Verify that hls profile functionality works as expected.
|
||||
|
||||
@@ -1235,14 +1206,23 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
}
|
||||
|
||||
self.initialize_block(data=video_xml)
|
||||
context = self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
|
||||
assert "'download_video_link': 'https://mp4.com/dm.mp4'" in context
|
||||
assert '"streams": "1.00:https://yt.com/?v=v0TFmdO4ZP0"' in context
|
||||
assert sorted(['https://webm.com/dw.webm', 'https://mp4.com/dm.mp4', 'https://hls.com/hls.m3u8']) ==\
|
||||
sorted(get_context_dict_from_string(context)['metadata']['sources'])
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
def test_get_html_hls_no_video_id(self):
|
||||
metadata_dict = json.loads(actual_context['metadata'])
|
||||
|
||||
assert actual_context['download_video_link'] == 'https://mp4.com/dm.mp4'
|
||||
assert metadata_dict['streams'] == '1.00:https://yt.com/?v=v0TFmdO4ZP0'
|
||||
assert sorted(metadata_dict['sources']) == sorted([
|
||||
'https://webm.com/dw.webm',
|
||||
'https://mp4.com/dm.mp4',
|
||||
'https://hls.com/hls.m3u8',
|
||||
])
|
||||
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_get_html_hls_no_video_id(self, mock_render_django_template):
|
||||
"""
|
||||
Verify that `download_video_link` is set to None for HLS videos if no video id
|
||||
"""
|
||||
@@ -1253,10 +1233,15 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
"""
|
||||
|
||||
self.initialize_block(data=video_xml)
|
||||
context = self.block.student_view(None).content
|
||||
assert "'download_video_link': None" in context
|
||||
self.block.student_view(None)
|
||||
|
||||
def test_get_html_non_hls_video_download(self):
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
assert actual_context['download_video_link'] is None
|
||||
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_get_html_non_hls_video_download(self, _):
|
||||
"""
|
||||
Verify that `download_video_link` is available if a non HLS videos is available
|
||||
"""
|
||||
@@ -1272,7 +1257,8 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
context = self.block.student_view(None).content
|
||||
assert "'download_video_link': 'http://example.com/example.mp4'" in context
|
||||
|
||||
def test_html_student_public_view(self):
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_html_student_public_view(self, _):
|
||||
"""
|
||||
Test the student and public views
|
||||
"""
|
||||
@@ -1288,8 +1274,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
context = self.block.render(PUBLIC_VIEW).content
|
||||
assert '"saveStateEnabled": false' in context
|
||||
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
@patch('xmodule.video_block.video_block.edxval_api.get_course_video_image_url')
|
||||
def test_poster_image(self, get_course_video_image_url):
|
||||
def test_poster_image(self, get_course_video_image_url, _):
|
||||
"""
|
||||
Verify that poster image functionality works as expected.
|
||||
"""
|
||||
@@ -1301,8 +1288,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
|
||||
assert '"poster": "/media/video-images/poster.png"' in context
|
||||
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
@patch('xmodule.video_block.video_block.edxval_api.get_course_video_image_url')
|
||||
def test_poster_image_without_edx_video_id(self, get_course_video_image_url):
|
||||
def test_poster_image_without_edx_video_id(self, get_course_video_image_url, _):
|
||||
"""
|
||||
Verify that poster image is set to None and there is no crash when no edx_video_id.
|
||||
"""
|
||||
@@ -1314,11 +1302,12 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
|
||||
assert "'poster': 'null'" in context
|
||||
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
@patch(
|
||||
'openedx.core.djangoapps.video_config.services.VideoConfigService.is_hls_playback_enabled',
|
||||
Mock(return_value=False)
|
||||
)
|
||||
def test_hls_primary_playback_on_toggling_hls_feature(self):
|
||||
def test_hls_primary_playback_on_toggling_hls_feature(self, _):
|
||||
"""
|
||||
Verify that `prioritize_hls` is set to `False` if `HLSPlaybackEnabledFlag` is disabled.
|
||||
"""
|
||||
@@ -1364,11 +1353,12 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'result': 'false'
|
||||
},
|
||||
)
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
@patch(
|
||||
'openedx.core.djangoapps.video_config.services.VideoConfigService.is_hls_playback_enabled',
|
||||
Mock(return_value=True)
|
||||
)
|
||||
def test_deprecate_youtube_course_waffle_flag(self, data):
|
||||
def test_deprecate_youtube_course_waffle_flag(self, data, mock_render_django_template):
|
||||
"""
|
||||
Tests various combinations of a `prioritize_hls` flag being set in waffle and overridden for a course.
|
||||
"""
|
||||
@@ -1381,8 +1371,10 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
with patch.object(WaffleFlagCourseOverrideModel, 'override_value', return_value=data['course_override']):
|
||||
with override_waffle_flag(DEPRECATE_YOUTUBE, active=data['waffle_enabled']):
|
||||
self.initialize_block(data=video_xml, metadata=metadata)
|
||||
context = self.block.student_view(None).content
|
||||
assert '"prioritizeHls": {}'.format(data['result']) in context
|
||||
self.block.student_view(None)
|
||||
context = mock_render_django_template.call_args.args[1]
|
||||
metadata_dict = json.loads(context['metadata'])
|
||||
assert metadata_dict['prioritizeHls'] == (data['result'] == 'true')
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -2353,10 +2345,13 @@ class TestVideoWithBumper(TestVideo): # pylint: disable=test-inherits-tests
|
||||
with override_settings(FEATURES=self.FEATURES):
|
||||
assert not bumper_utils.is_bumper_enabled(self.block)
|
||||
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
@patch('xmodule.video_block.bumper_utils.is_bumper_enabled')
|
||||
@patch('xmodule.video_block.bumper_utils.get_bumper_settings')
|
||||
@patch('edxval.api.get_urls_for_profiles')
|
||||
def test_bumper_metadata(self, get_url_for_profiles, get_bumper_settings, is_bumper_enabled):
|
||||
def test_bumper_metadata(
|
||||
self, get_url_for_profiles, get_bumper_settings, is_bumper_enabled, mock_render_django_template
|
||||
):
|
||||
"""
|
||||
Test content with rendered bumper metadata.
|
||||
"""
|
||||
@@ -2372,7 +2367,7 @@ class TestVideoWithBumper(TestVideo): # pylint: disable=test-inherits-tests
|
||||
|
||||
is_bumper_enabled.return_value = True
|
||||
|
||||
content = self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
sources = ['example.mp4', 'example.webm']
|
||||
|
||||
expected_context = {
|
||||
@@ -2452,9 +2447,11 @@ class TestVideoWithBumper(TestVideo): # pylint: disable=test-inherits-tests
|
||||
'video_id': '',
|
||||
}
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
expected_content = mako_service.render_lms_template('video.html', expected_context)
|
||||
assert get_context_dict_from_string(content) == get_context_dict_from_string(expected_content)
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
# Validate and compare contexts
|
||||
assert get_context_from_dict(actual_context) == get_context_from_dict(expected_context)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -2537,7 +2534,9 @@ class TestAutoAdvanceVideo(TestVideo): # lint-amnesty, pylint: disable=test-inh
|
||||
}
|
||||
return context
|
||||
|
||||
def assert_content_matches_expectations(self, autoadvanceenabled_must_be, autoadvance_must_be):
|
||||
def assert_content_matches_expectations(
|
||||
self, autoadvanceenabled_must_be, autoadvance_must_be, mock_render_django_template
|
||||
):
|
||||
"""
|
||||
Check (assert) that loading video.html produces content that corresponds
|
||||
to the passed context.
|
||||
@@ -2545,18 +2544,18 @@ class TestAutoAdvanceVideo(TestVideo): # lint-amnesty, pylint: disable=test-inh
|
||||
"""
|
||||
|
||||
with override_settings(FEATURES=self.FEATURES):
|
||||
content = self.block.student_view(None).content
|
||||
self.block.student_view(None)
|
||||
|
||||
expected_context = self.prepare_expected_context(
|
||||
autoadvanceenabled_flag=autoadvanceenabled_must_be,
|
||||
autoadvance_flag=autoadvance_must_be,
|
||||
)
|
||||
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
with override_settings(FEATURES=self.FEATURES):
|
||||
expected_content = mako_service.render_lms_template('video.html', expected_context)
|
||||
# Get the actual context that was passed to render_django_template
|
||||
actual_context = mock_render_django_template.call_args.args[1]
|
||||
|
||||
assert get_context_dict_from_string(content) == get_context_dict_from_string(expected_content)
|
||||
# Validate and compare contexts
|
||||
assert get_context_from_dict(actual_context) == get_context_from_dict(expected_context)
|
||||
|
||||
def change_course_setting_autoadvance(self, new_value):
|
||||
"""
|
||||
@@ -2578,7 +2577,8 @@ class TestAutoAdvanceVideo(TestVideo): # lint-amnesty, pylint: disable=test-inh
|
||||
(True, True),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_is_autoadvance_available_and_enabled(self, global_setting, course_setting):
|
||||
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
|
||||
def test_is_autoadvance_available_and_enabled(self, global_setting, course_setting, mock_render_django_template):
|
||||
"""
|
||||
Check that the autoadvance is not available when it is disabled via feature flag
|
||||
(ENABLE_AUTOADVANCE_VIDEOS set to False) or by the course setting.
|
||||
@@ -2590,7 +2590,9 @@ class TestAutoAdvanceVideo(TestVideo): # lint-amnesty, pylint: disable=test-inh
|
||||
"""
|
||||
self.FEATURES.update({"ENABLE_AUTOADVANCE_VIDEOS": global_setting})
|
||||
self.change_course_setting_autoadvance(new_value=course_setting)
|
||||
|
||||
self.assert_content_matches_expectations(
|
||||
autoadvanceenabled_must_be=(global_setting and course_setting),
|
||||
autoadvance_must_be=(global_setting and course_setting),
|
||||
mock_render_django_template=mock_render_django_template,
|
||||
)
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
<%page expression_filter="h"/>
|
||||
{% load i18n %}
|
||||
|
||||
<%!
|
||||
from django.utils.translation import gettext as _
|
||||
from openedx.core.djangolib.js_utils import (
|
||||
dump_js_escaped_json, js_escaped_string
|
||||
)
|
||||
%>
|
||||
% if display_name is not UNDEFINED and display_name is not None and not is_embed:
|
||||
<h3 class="hd hd-2">${display_name}</h3>
|
||||
% endif
|
||||
{% if display_name is not None and not is_embed %}
|
||||
<h3 class="hd hd-2">{{ display_name|escape }}</h3>
|
||||
{% endif %}
|
||||
|
||||
<div
|
||||
id="video_${id}"
|
||||
id="video_{{ id|escape }}"
|
||||
class="video closed"
|
||||
data-metadata='${metadata}'
|
||||
data-bumper-metadata='${bumper_metadata}'
|
||||
data-autoadvance-enabled="${autoadvance_enabled}"
|
||||
data-poster='${poster}'
|
||||
data-block-id='${block_id}'
|
||||
data-course-id='${course_id}'
|
||||
data-metadata="{{ metadata|escape }}"
|
||||
data-bumper-metadata='{{ bumper_metadata|escape }}'
|
||||
data-autoadvance-enabled="{{ autoadvance_enabled|escape }}"
|
||||
data-poster='{{ poster|escape }}'
|
||||
data-block-id='{{ block_id|escape }}'
|
||||
data-course-id='{{ course_id|escape }}'
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="focus_grabber first"></div>
|
||||
|
||||
<div class="tc-wrapper">
|
||||
<div class="video-wrapper">
|
||||
<span tabindex="0" class="spinner" aria-hidden="false" aria-label="${_('Loading video player')}"></span>
|
||||
<span tabindex="-1" class="btn-play fa fa-youtube-play fa-2x is-hidden" aria-hidden="true" aria-label="${_('Play video')}"></span>
|
||||
<span tabindex="0" class="spinner" aria-hidden="false"
|
||||
aria-label="{% trans 'Loading video player' as tmsg %}{{tmsg|force_escape}}"></span>
|
||||
<span tabindex="-1" class="btn-play fa fa-youtube-play fa-2x is-hidden" aria-hidden="true"
|
||||
aria-label="{% trans 'Play video' as tmsg %}{{tmsg|force_escape}}"></span>
|
||||
<div class="video-player-pre"></div>
|
||||
<div class="video-player">
|
||||
<div id="${id}"></div>
|
||||
<h4 class="hd hd-4 video-error is-hidden">${_('No playable video sources found.')}</h4>
|
||||
<div id="{{ id|escape }}"></div>
|
||||
<h4 class="hd hd-4 video-error is-hidden">{% trans 'No playable video sources found.' as tmsg %}{{tmsg|force_escape}}</h4>
|
||||
<h4 class="hd hd-4 video-hls-error is-hidden">
|
||||
${_('Your browser does not support this video format. Try using a different browser.')}
|
||||
{% trans 'Your browser does not support this video format. Try using a different browser.' as tmsg %}{{tmsg|force_escape}}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="video-player-post"></div>
|
||||
<div class="closed-captions"></div>
|
||||
<div class="video-controls is-hidden">
|
||||
<div>
|
||||
<div class="vcr"><div class="vidtime">0:00 / 0:00</div></div>
|
||||
<div class="vcr">
|
||||
<div class="vidtime">0:00 / 0:00</div>
|
||||
</div>
|
||||
<div class="secondary-controls"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,42 +47,44 @@ from openedx.core.djangolib.js_utils import (
|
||||
<div class="focus_grabber last"></div>
|
||||
|
||||
<div class="wrapper-video-bottom-section">
|
||||
% if ((download_video_link or track or handout or branding_info or public_sharing_enabled) and not hide_downloads):
|
||||
<h3 class="hd hd-4 downloads-heading sr" id="video-download-transcripts_${id}">${_('Downloads and transcripts')}</h3>
|
||||
<div class="wrapper-downloads" role="region" aria-labelledby="video-download-transcripts_${id}">
|
||||
% if download_video_link or public_sharing_enabled:
|
||||
{% if download_video_link or track or handout or public_sharing_enabled and not hide_downloads %}
|
||||
<h3 class="hd hd-4 downloads-heading sr"
|
||||
id="video-download-transcripts_{{ id|escape }}">{% trans 'Downloads and transcripts' as tmsg %}{{tmsg|force_escape}}</h3>
|
||||
<div class="wrapper-downloads" role="region" aria-labelledby="video-download-transcripts_{{ id|escape }}">
|
||||
{% if download_video_link or public_sharing_enabled %}
|
||||
<div class="wrapper-download-video">
|
||||
<h4 class="hd hd-5">${_('Video')}</h4>
|
||||
% if download_video_link:
|
||||
<span class="icon fa fa-download" aria-hidden="true"></span>
|
||||
<a class="btn-link video-sources video-download-button" href="${download_video_link}" target="${'_blank' if not is_video_from_same_origin else '_self'}">
|
||||
${_('Download video file')}
|
||||
</a>
|
||||
% endif
|
||||
% if download_video_link and public_sharing_enabled:
|
||||
<br>
|
||||
% endif
|
||||
% if sharing_sites_info:
|
||||
<div class="wrapper-social-share">
|
||||
<button class="social-toggle-btn btn">
|
||||
<h4 class="hd hd-5">{% trans 'Video' as tmsg %}{{tmsg|force_escape}}</h4>
|
||||
{% if download_video_link %}
|
||||
<span class="icon fa fa-download" aria-hidden="true"></span>
|
||||
<a class="btn-link video-sources video-download-button"
|
||||
href="{{ download_video_link|escape }}"
|
||||
target="{% if not is_video_from_same_origin %}_blank{% else %}_self{% endif %}">
|
||||
{% trans "Download video file" as tmsg %}{{ tmsg|force_escape }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if download_video_link and public_sharing_enabled %}
|
||||
<br>{% endif %}
|
||||
{% if sharing_sites_info %}
|
||||
<div class="wrapper-social-share">
|
||||
<button class="social-toggle-btn btn">
|
||||
<span class="icon fa fa-share-alt"></span>
|
||||
${_('Share this video')}
|
||||
{% trans 'Share this video' as tmsg %}{{tmsg|force_escape}}
|
||||
</button>
|
||||
<div hidden class="container-social-share">
|
||||
${_('Share this video')}
|
||||
{% trans "Share this video" as tmsg %}{{tmsg|force_escape}}
|
||||
<div class="btn-link close-btn">
|
||||
<span class="icon fa fa-close"></span>
|
||||
</div>
|
||||
|
||||
% for sharing_site_info in sharing_sites_info:
|
||||
{% for sharing_site_info in sharing_sites_info %}
|
||||
<a
|
||||
class="social-share-link"
|
||||
data-source="${sharing_site_info['name']}"
|
||||
href="${sharing_site_info['sharing_url']}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
data-source="{{ sharing_site_info.name|escape }}"
|
||||
href="{{ sharing_site_info.sharing_url|escape }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
% if (sharing_site_info['name'] == 'twitter'):
|
||||
{% if sharing_site_info.name == "twitter" %}
|
||||
<!--
|
||||
Twitter now uses the X brand icon, but Font Awesome does not include fa-x-twitter until 6.0
|
||||
Upgrading to 6.0 would require lots of leg work becuase all square icons have new name patterns
|
||||
@@ -95,56 +95,63 @@ from openedx.core.djangolib.js_utils import (
|
||||
<!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
|
||||
<path d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm297.1 84L257.3 234.6 379.4 396H283.8L209 298.1 123.3 396H75.8l111-126.9L69.7 116h98l67.7 89.5L313.6 116h47.5zM323.3 367.6L153.4 142.9H125.1L296.9 367.6h26.3z"/>
|
||||
</svg>
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="icon fa {{ sharing_site_info.fa_icon_name|escape }}"
|
||||
aria-hidden="true"></span>
|
||||
{% endif %}
|
||||
<span class="sr">
|
||||
{% filter force_escape %}{% blocktrans with site=sharing_site_info.name %}Share on {{ site }}{% endblocktrans %}{% endfilter %}
|
||||
</span>
|
||||
% else:
|
||||
<span class="icon fa ${sharing_site_info['fa_icon_name']}" aria-hidden="true"></span>
|
||||
% endif
|
||||
<span class="sr">${_("Share on {site}").format(site=sharing_site_info['name'])}</span>
|
||||
</a>
|
||||
% endfor
|
||||
<div class="public-video-url-container">
|
||||
<a href=${public_video_url} class="public-video-url-link">
|
||||
${public_video_url}
|
||||
</a>
|
||||
<div class="public-video-copy-btn" data-url=${public_video_url}>
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
<div class="public-video-url-container">
|
||||
<a href="{{ public_video_url|escape }}" class="public-video-url-link">
|
||||
{{ public_video_url|escape }}
|
||||
</a>
|
||||
<div
|
||||
class="public-video-copy-btn" data-url="{{ public_video_url|escape }}">
|
||||
<span class="icon fa fa-link"></span>
|
||||
<span>${_('Copy')}</span>
|
||||
<span>{% trans "Copy" as tmsg %}{{tmsg|force_escape}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
{% endif %}
|
||||
</div>
|
||||
% endif
|
||||
% if track:
|
||||
{% endif %}
|
||||
{% if track %}
|
||||
<div class="wrapper-download-transcripts">
|
||||
<h4 class="hd hd-5">${_('Transcripts')}</h4>
|
||||
% if transcript_download_format:
|
||||
<ul class="list-download-transcripts">
|
||||
% for item in transcript_download_formats_list:
|
||||
<li class="transcript-option">
|
||||
<h4 class="hd hd-5">{% trans 'Transcripts' as tmsg %}{{tmsg|force_escape}}</h4>
|
||||
{% if transcript_download_format %}
|
||||
<ul class="list-download-transcripts">
|
||||
{% for item in transcript_download_formats_list %}
|
||||
<li class="transcript-option">
|
||||
<span class="icon fa fa-download" aria-hidden="true"></span>
|
||||
<% dname = _("Download {file}").format(file=item['display_name']) %>
|
||||
<a class="btn btn-link" href="${track}" data-value="${item['value']}">${dname}</a>
|
||||
</li>
|
||||
% endfor
|
||||
</ul>
|
||||
% else:
|
||||
<a class="btn-link external-track" href="${track}">${_('Download transcript')}</a>
|
||||
% endif
|
||||
<a class="btn btn-link" href="{{ track|escape }}"
|
||||
data-value="{{ item.value|escape }}">
|
||||
{% filter force_escape %}{% blocktrans with file=item.display_name %}Download {{ file }}{% endblocktrans %}{% endfilter %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<a class="btn-link external-track" href="{{ track|escape }}">{% trans 'Download transcript' as tmsg %}{{tmsg|force_escape}}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
% endif
|
||||
% if handout:
|
||||
{% endif %}
|
||||
{% if handout %}
|
||||
<div class="wrapper-handouts">
|
||||
<h4 class="hd hd-5">${_('Handouts')}</h4>
|
||||
<a class="btn-link" href="${handout}">${_('Download Handout')}</a>
|
||||
<h4 class="hd hd-5">{% trans 'Handouts' as tmsg %}{{tmsg|force_escape}}</h4>
|
||||
<a class="btn-link" href="{{ handout|escape }}">{% trans 'Download Handout' as tmsg %}{{tmsg|force_escape}}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
% endif
|
||||
% if transcript_feedback_enabled and video_id:
|
||||
<div class="wrapper-transcript-feedback" data-video-id='${video_id}' data-user-id='${user_id}'>
|
||||
<h4 class="hd hd-5">${_('How is the transcript quality ?')}</h4>
|
||||
{% endif %}
|
||||
{% if transcript_feedback_enabled and video_id %}
|
||||
<div class="wrapper-transcript-feedback" data-video-id="{{ video_id|escape }}" data-user-id="{{ user_id|escape }}">
|
||||
<h4 class="hd hd-5">{% trans 'How is the transcript quality?' as tmsg %}{{tmsg|force_escape}}</h4>
|
||||
<div class="transcript-feedback-buttons">
|
||||
<div class="transcript-feedback-btn-wrapper">
|
||||
<button class="thumbs-up-btn">
|
||||
@@ -158,7 +165,7 @@ from openedx.core.djangolib.js_utils import (
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
{% endif %}
|
||||
<div class="google-disclaimer">
|
||||
<a href="https://translate.google.com/" target="_blank">
|
||||
<img
|
||||
@@ -172,7 +179,9 @@ from openedx.core.djangolib.js_utils import (
|
||||
</div>
|
||||
</div>
|
||||
|
||||
% if cdn_eval:
|
||||
{% if cdn_eval %}
|
||||
{{ cdn_exp_group|json_script:"cdnExpGroup" }}
|
||||
|
||||
<script>
|
||||
//TODO: refactor this js into a separate file.
|
||||
function sendPerformanceBeacon(id, expgroup, value, event_name) {
|
||||
@@ -181,16 +190,18 @@ from openedx.core.djangolib.js_utils import (
|
||||
}
|
||||
var cdnStartTime;
|
||||
var salt = Math.floor((1 + Math.random()) * 0x100000).toString(36);
|
||||
var id = "${id | n, js_escaped_string}";
|
||||
var id = "{{ id|escapejs }}";
|
||||
|
||||
function initializeCDNExperiment() {
|
||||
sendPerformanceBeacon(id + "_" + salt, ${cdn_exp_group | n, dump_js_escaped_json}, "", "load");
|
||||
var cdnData = JSON.parse(document.getElementById('cdnExpGroup').textContent);
|
||||
sendPerformanceBeacon(id + "_" + salt, cdnData, "", "load");
|
||||
cdnStartTime = Date.now();
|
||||
|
||||
$.each(['loadstart', 'abort', 'error', 'stalled', 'loadedmetadata',
|
||||
'loadeddata', 'canplay', 'canplaythrough', 'seeked'],
|
||||
function(index, eventName) {
|
||||
$("#video_" + id).bind("html5:" + eventName, null, function() {
|
||||
timeElapsed = Date.now() - cdnStartTime;
|
||||
sendPerformanceBeacon(id + "_" + salt, ${cdn_exp_group | n, dump_js_escaped_json}, timeElapsed, eventName);
|
||||
'loadeddata', 'canplay', 'canplaythrough', 'seeked'], function(index, eventName) {
|
||||
$("#video_" + id).bind("html5:" + eventName, null, function () {
|
||||
var timeElapsed = Date.now() - cdnStartTime;
|
||||
sendPerformanceBeacon(id + "_" + salt, cdnData, timeElapsed, eventName);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -199,4 +210,4 @@ from openedx.core.djangolib.js_utils import (
|
||||
initializeCDNExperiment();
|
||||
}
|
||||
</script>
|
||||
% endif
|
||||
{% endif %}
|
||||
|
||||
@@ -30,6 +30,7 @@ from xblock.core import XBlock
|
||||
from xblock.fields import ScopeIds
|
||||
from xblock.runtime import KvsFieldData
|
||||
from xblocks_contrib.video import VideoBlock as _ExtractedVideoBlock
|
||||
from xblock.utils.resources import ResourceLoader
|
||||
|
||||
from common.djangoapps.xblock_django.constants import ATTR_KEY_REQUEST_COUNTRY_CODE, ATTR_KEY_USER_ID
|
||||
from openedx.core.lib.cache_utils import request_cached
|
||||
@@ -37,7 +38,6 @@ from openedx.core.lib.license import LicenseMixin
|
||||
from xmodule.contentstore.content import StaticContent
|
||||
from xmodule.editing_block import EditingMixin
|
||||
from xmodule.exceptions import NotFoundError
|
||||
from xmodule.mako_block import MakoTemplateBlockBase
|
||||
from xmodule.modulestore.inheritance import InheritanceKeyValueStore, own_metadata
|
||||
from xmodule.raw_block import EmptyDataRawMixin
|
||||
from xmodule.util.builtin_assets import add_css_to_fragment, add_webpack_js_to_fragment
|
||||
@@ -95,6 +95,7 @@ except ImportError:
|
||||
edxval_api = None
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
loader = ResourceLoader("lms")
|
||||
|
||||
# Make '_' a no-op so we can scrape strings. Using lambda instead of
|
||||
# `django.utils.translation.ugettext_noop` because Django cannot be imported in this file
|
||||
@@ -497,7 +498,7 @@ class _BuiltInVideoBlock(
|
||||
if video_config_service:
|
||||
template_context.update(video_config_service.get_public_sharing_context(self, self.course_id))
|
||||
|
||||
return self.runtime.service(self, 'mako').render_lms_template('video.html', template_context)
|
||||
return loader.render_django_template("templates/video.html", template_context)
|
||||
|
||||
def is_transcript_feedback_enabled(self):
|
||||
"""
|
||||
@@ -848,7 +849,9 @@ class _BuiltInVideoBlock(
|
||||
"""
|
||||
Extend context by data for transcript basic tab.
|
||||
"""
|
||||
_context = MakoTemplateBlockBase.get_context(self)
|
||||
_context = {
|
||||
'editable_metadata_fields': self.editable_metadata_fields
|
||||
}
|
||||
_context.update({
|
||||
'tabs': self.tabs,
|
||||
'html_id': self.location.html_id(), # element_id
|
||||
|
||||
Reference in New Issue
Block a user