Commit 13ef23c3 by Alexander Kryklia

Merge pull request #2786 from edx/alex/fix_download_html5_subs

Fix donwload subs for non youtube videos and non-en language.
parents 70233a96 dd20e84c
...@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes, ...@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected. the top. Include a label indicating the component affected.
Blades: Fix download subs for non youtube videos and non-en language. BLD-897.
Blades: Fix issues related to videos that have separate YouTube IDs for the Blades: Fix issues related to videos that have separate YouTube IDs for the
different video speeds. BLD-915, BLD-901. different video speeds. BLD-915, BLD-901.
......
...@@ -369,31 +369,32 @@ Feature: CMS Transcripts ...@@ -369,31 +369,32 @@ Feature: CMS Transcripts
And I click transcript button "choose" number 2 And I click transcript button "choose" number 2
And I see value "test_transcripts|t_not_exist" in the field "Transcript (primary)" And I see value "test_transcripts|t_not_exist" in the field "Transcript (primary)"
# Flaky test fails occasionally in master. https://edx-wiki.atlassian.net/browse/BLD-927
#21 #21
Scenario: Work with 1 field only: Enter HTML5 source with transcripts - save - > change it to another one HTML5 source w/o transcripts - click on use existing - > change it to another one HTML5 source w/o transcripts - click on use existing #Scenario: Work with 1 field only: Enter HTML5 source with transcripts - save - > change it to another one HTML5 source w/o #transcripts - click on use existing - > change it to another one HTML5 source w/o transcripts - click on use existing
Given I have created a Video component with subtitles "t_not_exist" # Given I have created a Video component with subtitles "t_not_exist"
And I edit the component # And I edit the component
#
And I enter a "t_not_exist.mp4" source to field number 1 # And I enter a "t_not_exist.mp4" source to field number 1
Then I see status message "found" # Then I see status message "found"
And I see button "download_to_edit" # And I see button "download_to_edit"
And I see button "upload_new_timed_transcripts" # And I see button "upload_new_timed_transcripts"
And I see value "t_not_exist" in the field "Transcript (primary)" # And I see value "t_not_exist" in the field "Transcript (primary)"
#
And I save changes # And I save changes
And I edit the component # And I edit the component
#
And I enter a "video_name_2.mp4" source to field number 1 # And I enter a "video_name_2.mp4" source to field number 1
Then I see status message "use existing" # Then I see status message "use existing"
And I see button "use_existing" # And I see button "use_existing"
And I click transcript button "use_existing" # And I click transcript button "use_existing"
And I see value "video_name_2" in the field "Transcript (primary)" # And I see value "video_name_2" in the field "Transcript (primary)"
#
And I enter a "video_name_3.mp4" source to field number 1 # And I enter a "video_name_3.mp4" source to field number 1
Then I see status message "use existing" # Then I see status message "use existing"
And I see button "use_existing" # And I see button "use_existing"
And I click transcript button "use_existing" # And I click transcript button "use_existing"
And I see value "video_name_3" in the field "Transcript (primary)" # And I see value "video_name_3" in the field "Transcript (primary)"
#22 #22
Scenario: Work with 1 field only: Enter HTML5 source with transcripts - save -> change it to another one HTML5 source w/o transcripts - click on use existing -> change it to another one HTML5 source w/o transcripts - do not click on use existing -> change it to another one HTML5 source w/o transcripts - click on use existing Scenario: Work with 1 field only: Enter HTML5 source with transcripts - save -> change it to another one HTML5 source w/o transcripts - click on use existing -> change it to another one HTML5 source w/o transcripts - do not click on use existing -> change it to another one HTML5 source w/o transcripts - click on use existing
......
...@@ -487,3 +487,64 @@ class TestYoutubeTranscripts(unittest.TestCase): ...@@ -487,3 +487,64 @@ class TestYoutubeTranscripts(unittest.TestCase):
transcripts = transcripts_utils.get_transcripts_from_youtube(youtube_id, settings, translation) transcripts = transcripts_utils.get_transcripts_from_youtube(youtube_id, settings, translation)
self.assertEqual(transcripts, expected_transcripts) self.assertEqual(transcripts, expected_transcripts)
mock_get.assert_called_with('http://video.google.com/timedtext', params={'lang': 'en', 'v': 'good_youtube_id'}) mock_get.assert_called_with('http://video.google.com/timedtext', params={'lang': 'en', 'v': 'good_youtube_id'})
class TestTranscript(unittest.TestCase):
"""
Tests for Transcript class e.g. different transcript conversions.
"""
def setUp(self):
self.srt_transcript = textwrap.dedent("""\
0
00:00:10,500 --> 00:00:13,000
Elephant's Dream
1
00:00:15,000 --> 00:00:18,000
At the left we can see...
""")
self.sjson_transcript = textwrap.dedent("""\
{
"start": [
10500,
15000
],
"end": [
13000,
18000
],
"text": [
"Elephant's Dream",
"At the left we can see..."
]
}
""")
self.txt_transcript = u"Elephant's Dream\nAt the left we can see..."
def test_convert_srt_to_txt(self):
expected = self.txt_transcript
actual = transcripts_utils.Transcript.convert(self.srt_transcript, 'srt', 'txt')
self.assertEqual(actual, expected)
def test_convert_srt_to_srt(self):
expected = self.srt_transcript
actual = transcripts_utils.Transcript.convert(self.srt_transcript, 'srt', 'srt')
self.assertEqual(actual, expected)
def test_convert_sjson_to_txt(self):
expected = self.txt_transcript
actual = transcripts_utils.Transcript.convert(self.sjson_transcript, 'sjson', 'txt')
self.assertEqual(actual, expected)
def test_convert_sjson_to_srt(self):
expected = self.srt_transcript
actual = transcripts_utils.Transcript.convert(self.sjson_transcript, 'sjson', 'srt')
self.assertEqual(actual, expected)
def test_convert_srt_to_sjson(self):
with self.assertRaises(NotImplementedError):
transcripts_utils.Transcript.convert(self.srt_transcript, 'srt', 'sjson')
...@@ -233,8 +233,8 @@ class VideoDescriptorImportTestCase(unittest.TestCase): ...@@ -233,8 +233,8 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
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"/>
<track src="http://www.example.com/track"/> <track src="http://www.example.com/track"/>
<transcript language="ua" src="ukrainian_translation.srt" /> <transcript language="uk" src="ukrainian_translation.srt" />
<transcript language="ge" src="german_translation.srt" /> <transcript language="de" src="german_translation.srt" />
</video> </video>
''' '''
output = VideoDescriptor.from_xml(xml_data, module_system, Mock()) output = VideoDescriptor.from_xml(xml_data, module_system, Mock())
...@@ -251,7 +251,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase): ...@@ -251,7 +251,7 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'download_video': False, 'download_video': False,
'html5_sources': ['http://www.example.com/source.mp4'], 'html5_sources': ['http://www.example.com/source.mp4'],
'data': '', 'data': '',
'transcripts': {'ua': 'ukrainian_translation.srt', 'ge': 'german_translation.srt'}, 'transcripts': {'uk': 'ukrainian_translation.srt', 'de': 'german_translation.srt'},
}) })
def test_from_xml_missing_attributes(self): def test_from_xml_missing_attributes(self):
......
...@@ -9,6 +9,7 @@ import requests ...@@ -9,6 +9,7 @@ import requests
import logging import logging
from pysrt import SubRipTime, SubRipItem, SubRipFile from pysrt import SubRipTime, SubRipItem, SubRipFile
from lxml import etree from lxml import etree
from HTMLParser import HTMLParser
from xmodule.exceptions import NotFoundError from xmodule.exceptions import NotFoundError
from xmodule.contentstore.content import StaticContent from xmodule.contentstore.content import StaticContent
...@@ -77,7 +78,7 @@ def save_subs_to_store(subs, subs_id, item, language='en'): ...@@ -77,7 +78,7 @@ def save_subs_to_store(subs, subs_id, item, language='en'):
filedata = json.dumps(subs, indent=2) filedata = json.dumps(subs, indent=2)
mime_type = 'application/json' mime_type = 'application/json'
filename = subs_filename(subs_id, language) filename = subs_filename(subs_id, language)
content_location = asset_location(item.location, filename) content_location = Transcript.asset_location(item.location, filename)
content = StaticContent(content_location, filename, mime_type, filedata) content = StaticContent(content_location, filename, mime_type, filedata)
contentstore().save(content) contentstore().save(content)
return content_location return content_location
...@@ -193,7 +194,7 @@ def remove_subs_from_store(subs_id, item, lang='en'): ...@@ -193,7 +194,7 @@ def remove_subs_from_store(subs_id, item, lang='en'):
Remove from store, if transcripts content exists. Remove from store, if transcripts content exists.
""" """
try: try:
content = asset(item.location, subs_id, lang) content = Transcript.asset(item.location, subs_id, lang)
contentstore().delete(content.get_id()) contentstore().delete(content.get_id())
log.info("Removed subs %s from store", subs_id) log.info("Removed subs %s from store", subs_id)
except NotFoundError: except NotFoundError:
...@@ -412,30 +413,6 @@ def subs_filename(subs_id, lang='en'): ...@@ -412,30 +413,6 @@ def subs_filename(subs_id, lang='en'):
return '{0}_subs_{1}.srt.sjson'.format(lang, subs_id) return '{0}_subs_{1}.srt.sjson'.format(lang, subs_id)
def asset_location(location, filename):
"""
Return asset location.
`location` is module location.
"""
return StaticContent.compute_location(
location.org, location.course, filename
)
def asset(location, subs_id, lang='en', filename=None):
"""
Get asset from contentstore, asset location is built from subs_id and lang.
`location` is module location.
"""
return contentstore().find(
asset_location(
location,
subs_filename(subs_id, lang) if not filename else filename
)
)
def generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, lang): def generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, lang):
""" """
...@@ -444,7 +421,7 @@ def generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, lang): ...@@ -444,7 +421,7 @@ def generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, lang):
`item` is module object. `item` is module object.
""" """
try: try:
srt_transcripts = contentstore().find(asset_location(item.location, user_filename)) srt_transcript = contentstore().find(Transcript.asset_location(item.location, user_filename))
except NotFoundError as ex: except NotFoundError as ex:
raise TranscriptException("{}: Can't find uploaded transcripts: {}".format(ex.message, user_filename)) raise TranscriptException("{}: Can't find uploaded transcripts: {}".format(ex.message, user_filename))
...@@ -454,7 +431,7 @@ def generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, lang): ...@@ -454,7 +431,7 @@ def generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, lang):
generate_subs_from_source( generate_subs_from_source(
result_subs_dict, result_subs_dict,
os.path.splitext(user_filename)[1][1:], os.path.splitext(user_filename)[1][1:],
srt_transcripts.data.decode('utf8'), srt_transcript.data.decode('utf8'),
item, item,
lang lang
) )
...@@ -477,8 +454,72 @@ def get_or_create_sjson(item): ...@@ -477,8 +454,72 @@ def get_or_create_sjson(item):
user_subs_id = os.path.splitext(user_filename)[0] user_subs_id = os.path.splitext(user_filename)[0]
source_subs_id, result_subs_dict = user_subs_id, {1.0: user_subs_id} source_subs_id, result_subs_dict = user_subs_id, {1.0: user_subs_id}
try: try:
sjson_transcript = asset(item.location, source_subs_id, item.transcript_language).data sjson_transcript = Transcript.asset(item.location, source_subs_id, item.transcript_language).data
except (NotFoundError): # generating sjson from srt except (NotFoundError): # generating sjson from srt
generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, item.transcript_language) generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, item.transcript_language)
sjson_transcript = asset(item.location, source_subs_id, item.transcript_language).data sjson_transcript = Transcript.asset(item.location, source_subs_id, item.transcript_language).data
return sjson_transcript return sjson_transcript
class Transcript(object):
"""
Container for transcript methods.
"""
@staticmethod
def convert(content, input_format, output_format):
"""
Convert transcript `content` from `input_format` to `output_format`.
Accepted input formats: sjson, srt.
Accepted output format: srt, txt.
"""
assert input_format in ('srt', 'sjson')
assert output_format in ('txt', 'srt', 'sjson')
if input_format == output_format:
return content
if input_format == 'srt':
if output_format == 'txt':
text = SubRipFile.from_string(content.decode('utf8')).text
return HTMLParser().unescape(text)
elif output_format == 'sjson':
raise NotImplementedError
if input_format == 'sjson':
if output_format == 'txt':
text = json.loads(content)['text']
return HTMLParser().unescape("\n".join(text))
elif output_format == 'srt':
return generate_srt_from_sjson(json.loads(content), speed=1.0)
@staticmethod
def asset(location, subs_id, lang='en', filename=None):
"""
Get asset from contentstore, asset location is built from subs_id and lang.
`location` is module location.
"""
return contentstore().find(
Transcript.asset_location(
location,
subs_filename(subs_id, lang) if not filename else filename
)
)
@staticmethod
def asset_location(location, filename):
"""
Return asset location.
`location` is module location.
"""
return StaticContent.compute_location(
location.org, location.course, filename
)
...@@ -10,10 +10,10 @@ in-browser HTML5 video method (when in HTML5 mode). ...@@ -10,10 +10,10 @@ in-browser HTML5 video method (when in HTML5 mode).
in XML. in XML.
""" """
import os
import json import json
import logging import logging
from operator import itemgetter from operator import itemgetter
from HTMLParser import HTMLParser
from lxml import etree from lxml import etree
from pkg_resources import resource_string from pkg_resources import resource_string
...@@ -33,12 +33,11 @@ from xblock.core import XBlock ...@@ -33,12 +33,11 @@ from xblock.core import XBlock
from xblock.fields import Scope, String, Float, Boolean, List, Dict, ScopeIds from xblock.fields import Scope, String, Float, Boolean, List, Dict, ScopeIds
from xmodule.fields import RelativeTime from xmodule.fields import RelativeTime
from .transcripts_utils import ( from .transcripts_utils import (
generate_srt_from_sjson,
asset,
get_or_create_sjson, get_or_create_sjson,
TranscriptException, TranscriptException,
generate_sjson_for_all_speeds, generate_sjson_for_all_speeds,
youtube_speed_dict youtube_speed_dict,
Transcript,
) )
from .video_utils import create_youtube_string from .video_utils import create_youtube_string
...@@ -271,13 +270,13 @@ class VideoModule(VideoFields, XModule): ...@@ -271,13 +270,13 @@ class VideoModule(VideoFields, XModule):
track_url = self.runtime.handler_url(self, 'transcript').rstrip('/?') + '/download' track_url = self.runtime.handler_url(self, 'transcript').rstrip('/?') + '/download'
if not self.transcripts: if not self.transcripts:
transcript_language = 'en' transcript_language = u'en'
languages = {'en': 'English'} languages = {'en': 'English'}
else: else:
if self.transcript_language in self.transcripts: if self.transcript_language in self.transcripts:
transcript_language = self.transcript_language transcript_language = self.transcript_language
elif self.sub: elif self.sub:
transcript_language = 'en' transcript_language = u'en'
else: else:
transcript_language = sorted(self.transcripts.keys())[0] transcript_language = sorted(self.transcripts.keys())[0]
...@@ -323,30 +322,47 @@ class VideoModule(VideoFields, XModule): ...@@ -323,30 +322,47 @@ class VideoModule(VideoFields, XModule):
'transcript_available_translations_url': self.runtime.handler_url(self, 'transcript').rstrip('/?') + '/available_translations', 'transcript_available_translations_url': self.runtime.handler_url(self, 'transcript').rstrip('/?') + '/available_translations',
}) })
def get_transcript(self, format='srt'): def get_transcript(self, transcript_format='srt'):
""" """
Returns transcript in *.srt format. Returns transcript, filename and MIME type.
Raises: Raises:
- NotFoundError if cannot find transcript file in storage. - NotFoundError if cannot find transcript file in storage.
- ValueError if transcript file is empty or incorrect JSON. - ValueError if transcript file is empty or incorrect JSON.
- KeyError if transcript file has incorrect format. - KeyError if transcript file has incorrect format.
If language is 'en', self.sub should be correct subtitles name.
If language is 'en', but if self.sub is not defined, this means that we
should search for video name in order to get proper transcript (old style courses).
If language is not 'en', give back transcript in proper language and format.
""" """
lang = self.transcript_language lang = self.transcript_language
subs_id = self.sub if lang == 'en' else self.youtube_id_1_0
data = asset(self.location, subs_id, lang).data if lang == 'en':
if format == 'txt': if self.sub: # HTML5 case and (Youtube case for new style videos)
text = json.loads(data)['text'] transcript_name = self.sub
str_subs = HTMLParser().unescape("\n".join(text)) elif self.youtube_id_1_0: # old courses
mime_type = 'text/plain' transcript_name = self.youtube_id_1_0
else:
log.debug("No subtitles for 'en' language")
raise ValueError
data = Transcript.asset(self.location, transcript_name, lang).data
filename = '{}.{}'.format(transcript_name, transcript_format)
content = Transcript.convert(data, 'sjson', transcript_format)
else: else:
str_subs = generate_srt_from_sjson(json.loads(data), speed=1.0) data = Transcript.asset(self.location, None, None, self.transcripts[lang]).data
mime_type = 'application/x-subrip' filename = '{}.{}'.format(os.path.splitext(self.transcripts[lang])[0], transcript_format)
if not str_subs: content = Transcript.convert(data, 'srt', transcript_format)
log.debug('generate_srt_from_sjson produces no subtitles')
if not content:
log.debug('no subtitles produced in get_transcript')
raise ValueError raise ValueError
return str_subs, format, mime_type mime_type = 'text/plain' if transcript_format == 'txt' else 'application/x-subrip'
return content, filename, mime_type
@XBlock.handler @XBlock.handler
def transcript(self, request, dispatch): def transcript(self, request, dispatch):
...@@ -384,34 +400,31 @@ class VideoModule(VideoFields, XModule): ...@@ -384,34 +400,31 @@ class VideoModule(VideoFields, XModule):
elif dispatch == 'download': elif dispatch == 'download':
try: try:
subs, format, mime_type = self.get_transcript(format=self.transcript_download_format) transcript_content, transcript_filename, transcript_mime_type = self.get_transcript(self.transcript_download_format)
except (NotFoundError, ValueError, KeyError): except (NotFoundError, ValueError, KeyError):
log.debug("Video@download exception") log.debug("Video@download exception")
response = Response(status=404) response = Response(status=404)
else: else:
response = Response( response = Response(
subs, transcript_content,
headerlist=[ headerlist=[
('Content-Disposition', 'attachment; filename="{filename}.{format}"'.format( ('Content-Disposition', 'attachment; filename="{}"'.format(transcript_filename)),
filename=self.transcript_language,
format=format,
)),
] ]
) )
response.content_type = mime_type response.content_type = transcript_mime_type
elif dispatch == 'available_translations': elif dispatch == 'available_translations':
available_translations = [] available_translations = []
if self.sub: # check if sjson exists for 'en'. if self.sub: # check if sjson exists for 'en'.
try: try:
asset(self.location, self.sub, 'en') Transcript.asset(self.location, self.sub, 'en')
except NotFoundError: except NotFoundError:
pass pass
else: else:
available_translations = ['en'] available_translations = ['en']
for lang in self.transcripts: for lang in self.transcripts:
try: try:
asset(self.location, None, None, self.transcripts[lang]) Transcript.asset(self.location, None, None, self.transcripts[lang])
except NotFoundError: except NotFoundError:
continue continue
available_translations.append(lang) available_translations.append(lang)
...@@ -462,13 +475,13 @@ class VideoModule(VideoFields, XModule): ...@@ -462,13 +475,13 @@ class VideoModule(VideoFields, XModule):
if youtube_id: if youtube_id:
# Youtube case: # Youtube case:
if self.transcript_language == 'en': if self.transcript_language == 'en':
return asset(self.location, youtube_id).data return Transcript.asset(self.location, youtube_id).data
youtube_ids = youtube_speed_dict(self) youtube_ids = youtube_speed_dict(self)
assert youtube_id in youtube_ids assert youtube_id in youtube_ids
try: try:
sjson_transcript = asset(self.location, youtube_id, self.transcript_language).data sjson_transcript = Transcript.asset(self.location, youtube_id, self.transcript_language).data
except (NotFoundError): except (NotFoundError):
log.info("Can't find content in storage for %s transcript: generating.", youtube_id) log.info("Can't find content in storage for %s transcript: generating.", youtube_id)
generate_sjson_for_all_speeds( generate_sjson_for_all_speeds(
...@@ -477,19 +490,17 @@ class VideoModule(VideoFields, XModule): ...@@ -477,19 +490,17 @@ class VideoModule(VideoFields, XModule):
{speed: youtube_id for youtube_id, speed in youtube_ids.iteritems()}, {speed: youtube_id for youtube_id, speed in youtube_ids.iteritems()},
self.transcript_language self.transcript_language
) )
sjson_transcript = asset(self.location, youtube_id, self.transcript_language).data sjson_transcript = Transcript.asset(self.location, youtube_id, self.transcript_language).data
return sjson_transcript return sjson_transcript
else: else:
# HTML5 case # HTML5 case
if self.transcript_language == 'en': if self.transcript_language == 'en':
return asset(self.location, self.sub).data return Transcript.asset(self.location, self.sub).data
else: else:
return get_or_create_sjson(self) return get_or_create_sjson(self)
class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor): class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor):
"""Descriptor for `VideoModule`.""" """Descriptor for `VideoModule`."""
module_class = VideoModule module_class = VideoModule
......
...@@ -82,9 +82,9 @@ Feature: LMS Video component ...@@ -82,9 +82,9 @@ Feature: LMS Video component
# 10 # 10
Scenario: Language menu works correctly in Video component Scenario: Language menu works correctly in Video component
Given I am registered for the course "test_course" Given I am registered for the course "test_course"
And I have a "chinese_transcripts.srt" transcript file in assets And I have a "chinese_transcripts.srt" transcript file in assets
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
And it has a video in "Youtube" mode: And it has a video in "Youtube" mode:
| transcripts | sub | | transcripts | sub |
| {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | | {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM |
And I make sure captions are closed And I make sure captions are closed
...@@ -97,8 +97,8 @@ Feature: LMS Video component ...@@ -97,8 +97,8 @@ Feature: LMS Video component
# 11 # 11
Scenario: CC button works correctly w/o english transcript in HTML5 mode of Video component Scenario: CC button works correctly w/o english transcript in HTML5 mode of Video component
Given I am registered for the course "test_course" Given I am registered for the course "test_course"
And I have a "chinese_transcripts.srt" transcript file in assets And I have a "chinese_transcripts.srt" transcript file in assets
And it has a video in "HTML5" mode: And it has a video in "HTML5" mode:
| transcripts | | transcripts |
| {"zh": "chinese_transcripts.srt"} | | {"zh": "chinese_transcripts.srt"} |
And I make sure captions are opened And I make sure captions are opened
...@@ -181,11 +181,11 @@ Feature: LMS Video component ...@@ -181,11 +181,11 @@ Feature: LMS Video component
| track | download_track | | track | download_track |
| http://example.org/ | true | | http://example.org/ | true |
And I open the section with videos And I open the section with videos
And I can download transcript in "srt" format Then I can download transcript in "srt" format and has text "00:00:00,270"
And I select the transcript format "txt" And I select the transcript format "txt"
And I can download transcript in "txt" format Then I can download transcript in "txt" format and has text "Hi, welcome to Edx."
When I open video "B" When I open video "B"
Then I can download transcript in "txt" format Then I can download transcript in "txt" format and has text "Hi, welcome to Edx."
When I open video "C" When I open video "C"
Then menu "download_transcript" doesn't exist Then menu "download_transcript" doesn't exist
...@@ -203,3 +203,47 @@ Feature: LMS Video component ...@@ -203,3 +203,47 @@ Feature: LMS Video component
And I reload the page And I reload the page
Then I see "Hi, welcome to Edx." text in the captions Then I see "Hi, welcome to Edx." text in the captions
And I see duration "1:00" And I see duration "1:00"
# 21
Scenario: Download button works correctly for non-english transcript in Youtube mode of Video component
Given I am registered for the course "test_course"
And I have a "chinese_transcripts.srt" transcript file in assets
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
And it has a video in "Youtube" mode:
| transcripts | sub | download_track |
| {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | true |
And I select language with code "zh"
And I see "好 各位同学" text in the captions
Then I can download transcript in "srt" format and has text "好 各位同学"
# 22
Scenario: Download button works correctly for non-english transcript in HTML5 mode of Video component
Given I am registered for the course "test_course"
And I have a "chinese_transcripts.srt" transcript file in assets
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
And it has a video in "HTML5" mode:
| transcripts | sub | download_track |
| {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | true |
And I select language with code "zh"
And I see "好 各位同学" text in the captions
Then I can download transcript in "srt" format and has text "好 各位同学"
# 23
Scenario: Download button works correctly w/o english transcript in HTML5 mode of Video component
Given I am registered for the course "test_course"
And I have a "chinese_transcripts.srt" transcript file in assets
And it has a video in "HTML5" mode:
| transcripts | download_track |
| {"zh": "chinese_transcripts.srt"} | true |
And I see "好 各位同学" text in the captions
Then I can download transcript in "srt" format and has text "好 各位同学"
# 24
Scenario: Download button works correctly w/o english transcript in Youtube mode of Video component
Given I am registered for the course "test_course"
And I have a "chinese_transcripts.srt" transcript file in assets
And it has a video in "Youtube" mode:
| transcripts | download_track |
| {"zh": "chinese_transcripts.srt"} | true |
And I see "好 各位同学" text in the captions
Then I can download transcript in "srt" format and has text "好 各位同学"
\ No newline at end of file
...@@ -2,16 +2,17 @@ ...@@ -2,16 +2,17 @@
#pylint: disable=C0111 #pylint: disable=C0111
from lettuce import world, step from lettuce import world, step
import json
import os import os
import requests import json
import time import time
import requests
from common import i_am_registered_for_the_course, section_location, visit_scenario_item from common import i_am_registered_for_the_course, section_location, visit_scenario_item
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf import settings from django.conf import settings
from cache_toolbox.core import del_cached_content from cache_toolbox.core import del_cached_content
from xmodule.contentstore.content import StaticContent from xmodule.contentstore.content import StaticContent
from xmodule.contentstore.django import contentstore from xmodule.contentstore.django import contentstore
TEST_ROOT = settings.COMMON_TEST_DATA_ROOT TEST_ROOT = settings.COMMON_TEST_DATA_ROOT
LANGUAGES = settings.ALL_LANGUAGES LANGUAGES = settings.ALL_LANGUAGES
...@@ -54,7 +55,7 @@ class ReuqestHandlerWithSessionId(object): ...@@ -54,7 +55,7 @@ class ReuqestHandlerWithSessionId(object):
""" """
kwargs = dict() kwargs = dict()
session_id = [{i['name']:i['value']} for i in world.browser.cookies.all() if i['name']==u'sessionid'] session_id = [{i['name']:i['value']} for i in world.browser.cookies.all() if i['name'] == u'sessionid']
if session_id: if session_id:
kwargs.update({ kwargs.update({
'cookies': session_id[0] 'cookies': session_id[0]
...@@ -118,7 +119,7 @@ def add_video_to_course(course, player_mode, hashes, display_name='Video'): ...@@ -118,7 +119,7 @@ def add_video_to_course(course, player_mode, hashes, display_name='Video'):
}) })
if player_mode == 'youtube_html5': if player_mode == 'youtube_html5':
kwargs['metadata'].update({ kwargs['metadata'].update({
'html5_sources': HTML5_SOURCES 'html5_sources': HTML5_SOURCES,
}) })
if player_mode == 'youtube_html5_unsupported_video': if player_mode == 'youtube_html5_unsupported_video':
kwargs['metadata'].update({ kwargs['metadata'].update({
...@@ -166,7 +167,6 @@ def _change_video_speed(speed): ...@@ -166,7 +167,6 @@ def _change_video_speed(speed):
speed_css = 'li[data-speed="{0}"] a'.format(speed) speed_css = 'li[data-speed="{0}"] a'.format(speed)
world.css_click(speed_css) world.css_click(speed_css)
def _open_menu(menu): def _open_menu(menu):
world.browser.execute_script("$('{selector}').parent().addClass('open')".format( world.browser.execute_script("$('{selector}').parent().addClass('open')".format(
selector=VIDEO_MENUS[menu] selector=VIDEO_MENUS[menu]
...@@ -333,11 +333,11 @@ def set_captions_visibility_state(_step, captions_state): ...@@ -333,11 +333,11 @@ def set_captions_visibility_state(_step, captions_state):
def i_see_menu(_step, menu): def i_see_menu(_step, menu):
_open_menu(menu) _open_menu(menu)
menu_items = world.css_find(VIDEO_MENUS[menu] + ' li') menu_items = world.css_find(VIDEO_MENUS[menu] + ' li')
Video = world.scenario_dict['VIDEO'] video = world.scenario_dict['VIDEO']
transcripts = dict(Video.transcripts) transcripts = dict(video.transcripts)
if Video.sub: if video.sub:
transcripts.update({ transcripts.update({
'en': Video.sub 'en': video.sub
}) })
languages = {i[0]: i[1] for i in LANGUAGES} languages = {i[0]: i[1] for i in LANGUAGES}
...@@ -359,9 +359,8 @@ def select_language(_step, code): ...@@ -359,9 +359,8 @@ def select_language(_step, code):
selector = VIDEO_MENUS["language"] + ' li[data-lang-code={code}]'.format( selector = VIDEO_MENUS["language"] + ' li[data-lang-code={code}]'.format(
code=code code=code
) )
item = world.css_find(selector)
item.click() world.css_click(selector)
assert world.css_has_class(selector, 'active') assert world.css_has_class(selector, 'active')
assert len(world.css_find(VIDEO_MENUS["language"] + ' li.active')) == 1 assert len(world.css_find(VIDEO_MENUS["language"] + ' li.active')) == 1
...@@ -406,6 +405,7 @@ def upload_to_assets(_step, filename): ...@@ -406,6 +405,7 @@ def upload_to_assets(_step, filename):
def is_hidden_button(_step, button): def is_hidden_button(_step, button):
assert not world.css_visible(VIDEO_BUTTONS[button]) assert not world.css_visible(VIDEO_BUTTONS[button])
@step('menu "([^"]*)" doesn\'t exist$') @step('menu "([^"]*)" doesn\'t exist$')
def is_hidden_menu(_step, menu): def is_hidden_menu(_step, menu):
assert world.is_css_not_present(VIDEO_MENUS[menu]) assert world.is_css_not_present(VIDEO_MENUS[menu])
...@@ -435,27 +435,21 @@ def video_alignment(_step, transcript_visibility): ...@@ -435,27 +435,21 @@ def video_alignment(_step, transcript_visibility):
assert all([width, height]) assert all([width, height])
@step('I can download transcript in "([^"]*)" format$') @step('I can download transcript in "([^"]*)" format and has text "([^"]*)"$')
def i_can_download_transcript(_step, format): def i_can_download_transcript(_step, format, text):
button = world.css_find('.video-tracks .a11y-menu-button').first button = world.css_find('.video-tracks .a11y-menu-button').first
assert button.text.strip() == '.' + format assert button.text.strip() == '.' + format
formats = { formats = {
'srt': { 'srt': 'application/x-subrip',
'content': '0\n00:00:00,270', 'txt': 'text/plain',
'mime_type': 'application/x-subrip'
},
'txt': {
'content': 'Hi, welcome to Edx.',
'mime_type': 'text/plain'
},
} }
url = world.css_find(VIDEO_BUTTONS['download_transcript'])[0]['href'] url = world.css_find(VIDEO_BUTTONS['download_transcript'])[0]['href']
request = ReuqestHandlerWithSessionId() request = ReuqestHandlerWithSessionId()
assert request.get(url).is_success() assert request.get(url).is_success()
assert request.check_header('content-type', formats[format]['mime_type']) assert request.check_header('content-type', formats[format])
assert request.content.startswith(formats[format]['content']) assert (text.encode('utf-8') in request.content)
@step('I select the transcript format "([^"]*)"$') @step('I select the transcript format "([^"]*)"$')
......
...@@ -135,10 +135,69 @@ class TestVideo(BaseTestXmodule): ...@@ -135,10 +135,69 @@ class TestVideo(BaseTestXmodule):
def tearDown(self): def tearDown(self):
_clear_assets(self.item_descriptor.location) _clear_assets(self.item_descriptor.location)
class TestTranscriptAvailableTranslationsDispatch(TestVideo):
"""
Test video handler that provide available translations info.
class TestVideoTranscriptTranslation(TestVideo): Tests for `available_translations` dispatch.
""" """
Test video handlers that provide translation transcripts. non_en_file = _create_srt_file()
DATA = """
<video show_captions="true"
display_name="A Name"
>
<source src="example.mp4"/>
<source src="example.webm"/>
<transcript language="uk" src="{}"/>
</video>
""".format(os.path.split(non_en_file.name)[1])
MODEL_DATA = {
'data': DATA
}
def setUp(self):
super(TestTranscriptAvailableTranslationsDispatch, self).setUp()
self.item_descriptor.render('student_view')
self.item = self.item_descriptor.xmodule_runtime.xmodule_instance
self.subs = {"start": [10], "end": [100], "text": ["Hi, welcome to Edx."]}
def test_available_translation_en(self):
good_sjson = _create_file(json.dumps(self.subs))
_upload_sjson_file(good_sjson, self.item_descriptor.location)
self.item.sub = _get_subs_id(good_sjson.name)
request = Request.blank('/translation')
response = self.item.transcript(request=request, dispatch='available_translations')
self.assertEqual(json.loads(response.body), ['en'])
def test_available_translation_non_en(self):
_upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1])
request = Request.blank('/translation')
response = self.item.transcript(request=request, dispatch='available_translations')
self.assertEqual(json.loads(response.body), ['uk'])
def test_multiple_available_translations(self):
good_sjson = _create_file(json.dumps(self.subs))
# Upload english transcript.
_upload_sjson_file(good_sjson, self.item_descriptor.location)
# Upload non-english transcript.
_upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1])
self.item.sub = _get_subs_id(good_sjson.name)
request = Request.blank('/translation')
response = self.item.transcript(request=request, dispatch='available_translations')
self.assertEqual(json.loads(response.body), ['en', 'uk'])
class TestTranscriptDownloadDispatch(TestVideo):
"""
Test video handler that provide translation transcripts.
Tests for `download` dispatch.
""" """
non_en_file = _create_srt_file() non_en_file = _create_srt_file()
...@@ -157,11 +216,10 @@ class TestVideoTranscriptTranslation(TestVideo): ...@@ -157,11 +216,10 @@ class TestVideoTranscriptTranslation(TestVideo):
} }
def setUp(self): def setUp(self):
super(TestVideoTranscriptTranslation, self).setUp() super(TestTranscriptDownloadDispatch, self).setUp()
self.item_descriptor.render('student_view') self.item_descriptor.render('student_view')
self.item = self.item_descriptor.xmodule_runtime.xmodule_instance self.item = self.item_descriptor.xmodule_runtime.xmodule_instance
# Tests for `download` dispatch:
def test_language_is_not_supported(self): def test_language_is_not_supported(self):
request = Request.blank('/download?language=ru') request = Request.blank('/download?language=ru')
...@@ -173,7 +231,7 @@ class TestVideoTranscriptTranslation(TestVideo): ...@@ -173,7 +231,7 @@ class TestVideoTranscriptTranslation(TestVideo):
response = self.item.transcript(request=request, dispatch='download') response = self.item.transcript(request=request, dispatch='download')
self.assertEqual(response.status, '404 Not Found') self.assertEqual(response.status, '404 Not Found')
@patch('xmodule.video_module.VideoModule.get_transcript', return_value=('Subs!', 'srt', 'application/x-subrip')) @patch('xmodule.video_module.VideoModule.get_transcript', return_value=('Subs!', 'test_filename.srt', 'application/x-subrip'))
def test_download_srt_exist(self, __): def test_download_srt_exist(self, __):
request = Request.blank('/download?language=en') request = Request.blank('/download?language=en')
response = self.item.transcript(request=request, dispatch='download') response = self.item.transcript(request=request, dispatch='download')
...@@ -195,7 +253,32 @@ class TestVideoTranscriptTranslation(TestVideo): ...@@ -195,7 +253,32 @@ class TestVideoTranscriptTranslation(TestVideo):
with self.assertRaises(NotFoundError): with self.assertRaises(NotFoundError):
self.item.get_transcript() self.item.get_transcript()
# Tests for `translation` dispatch: class TestTranscriptTranslationDispatch(TestVideo):
"""
Test video handler that provide translation transcripts.
Tests for `translation` dispatch.
"""
non_en_file = _create_srt_file()
DATA = """
<video show_captions="true"
display_name="A Name"
>
<source src="example.mp4"/>
<source src="example.webm"/>
<transcript language="uk" src="{}"/>
</video>
""".format(os.path.split(non_en_file.name)[1])
MODEL_DATA = {
'data': DATA
}
def setUp(self):
super(TestTranscriptTranslationDispatch, self).setUp()
self.item_descriptor.render('student_view')
self.item = self.item_descriptor.xmodule_runtime.xmodule_instance
def test_translation_fails(self): def test_translation_fails(self):
# No language # No language
...@@ -295,30 +378,35 @@ class TestVideoTranscriptTranslation(TestVideo): ...@@ -295,30 +378,35 @@ class TestVideoTranscriptTranslation(TestVideo):
self.assertDictEqual(json.loads(response.body), subs) self.assertDictEqual(json.loads(response.body), subs)
class TestVideoTranscriptsDownload(TestVideo): class TestGetTranscript(TestVideo):
""" """
Make sure that `get_transcript` method works correctly Make sure that `get_transcript` method works correctly
""" """
non_en_file = _create_srt_file()
DATA = """ DATA = """
<video show_captions="true" <video show_captions="true"
display_name="A Name" display_name="A Name"
> >
<source src="example.mp4"/> <source src="example.mp4"/>
<source src="example.webm"/> <source src="example.webm"/>
<transcript language="uk" src="{}"/>
</video> </video>
""" """.format(os.path.split(non_en_file.name)[1])
MODEL_DATA = { MODEL_DATA = {
'data': DATA 'data': DATA
} }
METADATA = {} METADATA = {}
def setUp(self): def setUp(self):
super(TestVideoTranscriptsDownload, self).setUp() super(TestGetTranscript, self).setUp()
self.item_descriptor.render('student_view') self.item_descriptor.render('student_view')
self.item = self.item_descriptor.xmodule_runtime.xmodule_instance self.item = self.item_descriptor.xmodule_runtime.xmodule_instance
def test_good_srt_transcript(self): def test_good_transcript(self):
"""
Test for download 'en' sub with html5 video and self.sub has correct non-empty value.
"""
good_sjson = _create_file(content=textwrap.dedent("""\ good_sjson = _create_file(content=textwrap.dedent("""\
{ {
"start": [ "start": [
...@@ -338,7 +426,9 @@ class TestVideoTranscriptsDownload(TestVideo): ...@@ -338,7 +426,9 @@ class TestVideoTranscriptsDownload(TestVideo):
_upload_sjson_file(good_sjson, self.item.location) _upload_sjson_file(good_sjson, self.item.location)
self.item.sub = _get_subs_id(good_sjson.name) self.item.sub = _get_subs_id(good_sjson.name)
text, format, download = self.item.get_transcript()
text, filename, mime_type = self.item.get_transcript()
expected_text = textwrap.dedent("""\ expected_text = textwrap.dedent("""\
0 0
00:00:00,270 --> 00:00:02,720 00:00:00,270 --> 00:00:02,720
...@@ -351,6 +441,8 @@ class TestVideoTranscriptsDownload(TestVideo): ...@@ -351,6 +441,8 @@ class TestVideoTranscriptsDownload(TestVideo):
""") """)
self.assertEqual(text, expected_text) self.assertEqual(text, expected_text)
self.assertEqual(filename[:-4], self.item.sub)
self.assertEqual(mime_type, 'application/x-subrip')
def test_good_txt_transcript(self): def test_good_txt_transcript(self):
good_sjson = _create_file(content=textwrap.dedent("""\ good_sjson = _create_file(content=textwrap.dedent("""\
...@@ -372,17 +464,77 @@ class TestVideoTranscriptsDownload(TestVideo): ...@@ -372,17 +464,77 @@ class TestVideoTranscriptsDownload(TestVideo):
_upload_sjson_file(good_sjson, self.item.location) _upload_sjson_file(good_sjson, self.item.location)
self.item.sub = _get_subs_id(good_sjson.name) self.item.sub = _get_subs_id(good_sjson.name)
text, format, mime_type = self.item.get_transcript(format="txt") text, filename, mime_type = self.item.get_transcript("txt")
expected_text = textwrap.dedent("""\ expected_text = textwrap.dedent("""\
Hi, welcome to Edx. Hi, welcome to Edx.
Let's start with what is on your screen right now.""") Let's start with what is on your screen right now.""")
self.assertEqual(text, expected_text) self.assertEqual(text, expected_text)
self.assertEqual(filename, self.item.sub + '.txt')
self.assertEqual(mime_type, 'text/plain')
def test_en_with_empty_sub(self):
def test_not_found_error(self): # no self.sub, self.youttube_1_0 exist, but no file in assets
with self.assertRaises(NotFoundError): with self.assertRaises(NotFoundError):
self.item.get_transcript() self.item.get_transcript()
# no self.sub and no self.youtube_1_0
self.item.youtube_id_1_0 = None
with self.assertRaises(ValueError):
self.item.get_transcript()
# no self.sub but youtube_1_0 exists with file in assets
good_sjson = _create_file(content=textwrap.dedent("""\
{
"start": [
270,
2720
],
"end": [
2720,
5430
],
"text": [
"Hi, welcome to Edx.",
"Let&#39;s start with what is on your screen right now."
]
}
"""))
_upload_sjson_file(good_sjson, self.item.location)
self.item.youtube_id_1_0 = _get_subs_id(good_sjson.name)
text, filename, mime_type = self.item.get_transcript()
expected_text = textwrap.dedent("""\
0
00:00:00,270 --> 00:00:02,720
Hi, welcome to Edx.
1
00:00:02,720 --> 00:00:05,430
Let&#39;s start with what is on your screen right now.
""")
self.assertEqual(text, expected_text)
self.assertEqual(filename, self.item.youtube_id_1_0 + '.srt')
self.assertEqual(mime_type, 'application/x-subrip')
def test_non_en(self):
self.item.transcript_language = 'uk'
self.non_en_file.seek(0)
_upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1])
text, filename, mime_type = self.item.get_transcript()
expected_text = textwrap.dedent("""
0
00:00:00,12 --> 00:00:00,100
Привіт, edX вітає вас.
""")
self.assertEqual(text, expected_text)
self.assertEqual(filename, os.path.split(self.non_en_file.name)[1])
self.assertEqual(mime_type, 'application/x-subrip')
def test_value_error(self): def test_value_error(self):
good_sjson = _create_file(content='bad content') good_sjson = _create_file(content='bad content')
......
...@@ -42,7 +42,7 @@ class TestVideoYouTube(TestVideo): ...@@ -42,7 +42,7 @@ class TestVideoYouTube(TestVideo):
'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/', 'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/',
'transcript_download_format': 'srt', 'transcript_download_format': 'srt',
'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}], 'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}],
'transcript_language': 'en', 'transcript_language': u'en',
'transcript_languages': '{"en": "English", "uk": "Ukrainian"}', 'transcript_languages': '{"en": "English", "uk": "Ukrainian"}',
'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url( 'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url(
self.item_descriptor, 'transcript' self.item_descriptor, 'transcript'
...@@ -51,6 +51,7 @@ class TestVideoYouTube(TestVideo): ...@@ -51,6 +51,7 @@ class TestVideoYouTube(TestVideo):
self.item_descriptor, 'transcript' self.item_descriptor, 'transcript'
).rstrip('/?') + '/available_translations', ).rstrip('/?') + '/available_translations',
} }
self.assertEqual( self.assertEqual(
context, context,
self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context), self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context),
...@@ -106,7 +107,7 @@ class TestVideoNonYouTube(TestVideo): ...@@ -106,7 +107,7 @@ class TestVideoNonYouTube(TestVideo):
'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/', 'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/',
'transcript_download_format': 'srt', 'transcript_download_format': 'srt',
'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}], 'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}],
'transcript_language': 'en', 'transcript_language': u'en',
'transcript_languages': '{"en": "English"}', 'transcript_languages': '{"en": "English"}',
'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url( 'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url(
self.item_descriptor, 'transcript' self.item_descriptor, 'transcript'
...@@ -143,6 +144,7 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -143,6 +144,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
<source src="example.mp4"/> <source src="example.mp4"/>
<source src="example.webm"/> <source src="example.webm"/>
{track} {track}
{transcripts}
</video> </video>
""" """
...@@ -152,24 +154,35 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -152,24 +154,35 @@ class TestGetHtmlMethod(BaseTestXmodule):
'track': u'<track src="http://www.example.com/track"/>', 'track': u'<track src="http://www.example.com/track"/>',
'sub': u'a_sub_file.srt.sjson', 'sub': u'a_sub_file.srt.sjson',
'expected_track_url': u'http://www.example.com/track', 'expected_track_url': u'http://www.example.com/track',
'transcripts': '',
}, },
{ {
'download_track': u'true', 'download_track': u'true',
'track': u'', 'track': u'',
'sub': u'a_sub_file.srt.sjson', 'sub': u'a_sub_file.srt.sjson',
'expected_track_url': u'a_sub_file.srt.sjson', 'expected_track_url': u'a_sub_file.srt.sjson',
'transcripts': '',
}, },
{ {
'download_track': u'true', 'download_track': u'true',
'track': u'', 'track': u'',
'sub': u'', 'sub': u'',
'expected_track_url': None 'expected_track_url': None,
'transcripts': '',
}, },
{ {
'download_track': u'false', 'download_track': u'false',
'track': u'<track src="http://www.example.com/track"/>', 'track': u'<track src="http://www.example.com/track"/>',
'sub': u'a_sub_file.srt.sjson', 'sub': u'a_sub_file.srt.sjson',
'expected_track_url': None, 'expected_track_url': None,
'transcripts': '',
},
{
'download_track': u'true',
'track': u'',
'sub': u'',
'expected_track_url': u'a_sub_file.srt.sjson',
'transcripts': '<transcript language="uk" src="ukrainian.srt" />',
}, },
] ]
...@@ -201,7 +214,8 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -201,7 +214,8 @@ class TestGetHtmlMethod(BaseTestXmodule):
DATA = SOURCE_XML.format( DATA = SOURCE_XML.format(
download_track=data['download_track'], download_track=data['download_track'],
track=data['track'], track=data['track'],
sub=data['sub'] sub=data['sub'],
transcripts=data['transcripts'],
) )
self.initialize_module(data=DATA) self.initialize_module(data=DATA)
...@@ -213,8 +227,8 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -213,8 +227,8 @@ class TestGetHtmlMethod(BaseTestXmodule):
expected_context.update({ expected_context.update({
'transcript_download_format': None if self.item_descriptor.track and self.item_descriptor.download_track else 'srt', 'transcript_download_format': None if self.item_descriptor.track and self.item_descriptor.download_track else 'srt',
'transcript_languages': '{"en": "English"}', 'transcript_languages': '{"en": "English"}' if not data['transcripts'] else '{"uk": "Ukrainian"}',
'transcript_language': 'en', 'transcript_language': u'en' if not data['transcripts'] or data.get('sub') else u'uk',
'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url( 'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url(
self.item_descriptor, 'transcript' self.item_descriptor, 'transcript'
).rstrip('/?') + '/translation', ).rstrip('/?') + '/translation',
...@@ -312,7 +326,7 @@ class TestGetHtmlMethod(BaseTestXmodule): ...@@ -312,7 +326,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/', 'yt_test_url': 'https://gdata.youtube.com/feeds/api/videos/',
'transcript_download_format': 'srt', 'transcript_download_format': 'srt',
'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}], 'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}],
'transcript_language': 'en', 'transcript_language': u'en',
'transcript_languages': '{"en": "English"}', 'transcript_languages': '{"en": "English"}',
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment