diff --git a/cms/djangoapps/contentstore/views/tests/test_transcripts.py b/cms/djangoapps/contentstore/views/tests/test_transcripts.py
index d9fffdb51528..05c1bebe45ad 100644
--- a/cms/djangoapps/contentstore/views/tests/test_transcripts.py
+++ b/cms/djangoapps/contentstore/views/tests/test_transcripts.py
@@ -1,9 +1,10 @@
"""Tests for items views."""
import copy
+from codecs import BOM_UTF8
import ddt
import json
-import os
+from mock import patch, Mock
import tempfile
import textwrap
from uuid import uuid4
@@ -11,7 +12,7 @@
from django.conf import settings
from django.core.urlresolvers import reverse
from django.test.utils import override_settings
-from mock import patch, Mock
+from edxval.api import create_video
from opaque_keys.edx.keys import UsageKey
from contentstore.tests.utils import CourseTestCase, mock_requests_get
@@ -20,11 +21,25 @@
from xmodule.contentstore.django import contentstore
from xmodule.exceptions import NotFoundError
from xmodule.modulestore.django import modulestore
-from xmodule.video_module import transcripts_utils
+from xmodule.video_module.transcripts_utils import (
+ get_video_transcript_content,
+ remove_subs_from_store,
+ Transcript,
+)
TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE)
TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'] = 'test_xcontent_%s' % uuid4().hex
+SRT_TRANSCRIPT_CONTENT = """
+1
+00:00:10,500 --> 00:00:13,000
+Elephant's Dream
+
+2
+00:00:15,000 --> 00:00:18,000
+At the left we can see...
+"""
+
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class BaseTranscripts(CourseTestCase):
@@ -96,120 +111,175 @@ def get_youtube_ids(self):
}
+@ddt.ddt
class TestUploadTranscripts(BaseTranscripts):
"""
- Tests for '/transcripts/upload' url.
+ Tests for '/transcripts/upload' endpoint.
"""
def setUp(self):
- """Create initial data."""
super(TestUploadTranscripts, self).setUp()
+ self.contents = {
+ 'good': SRT_TRANSCRIPT_CONTENT,
+ 'bad': 'Some BAD data',
+ }
+ # Create temporary transcript files
+ self.good_srt_file = self.create_transcript_file(content=self.contents['good'], suffix='.srt')
+ self.bad_data_srt_file = self.create_transcript_file(content=self.contents['bad'], suffix='.srt')
+ self.bad_name_srt_file = self.create_transcript_file(content=self.contents['good'], suffix='.bad')
+ self.bom_srt_file = self.create_transcript_file(content=self.contents['good'], suffix='.srt', include_bom=True)
+
+ # Setup a VEDA produced video and persist `edx_video_id` in VAL.
+ create_video({
+ 'edx_video_id': u'123-456-789',
+ 'status': 'upload',
+ 'client_video_id': u'Test Video',
+ 'duration': 0,
+ 'encoded_videos': [],
+ 'courses': [unicode(self.course.id)]
+ })
- self.good_srt_file = tempfile.NamedTemporaryFile(suffix='.srt')
- self.good_srt_file.write(textwrap.dedent("""
- 1
- 00:00:10,500 --> 00:00:13,000
- Elephant's Dream
-
- 2
- 00:00:15,000 --> 00:00:18,000
- At the left we can see...
- """))
- self.good_srt_file.seek(0)
-
- self.bad_data_srt_file = tempfile.NamedTemporaryFile(suffix='.srt')
- self.bad_data_srt_file.write('Some BAD data')
- self.bad_data_srt_file.seek(0)
-
- self.bad_name_srt_file = tempfile.NamedTemporaryFile(suffix='.BAD')
- self.bad_name_srt_file.write(textwrap.dedent("""
- 1
- 00:00:10,500 --> 00:00:13,000
- Elephant's Dream
-
- 2
- 00:00:15,000 --> 00:00:18,000
- At the left we can see...
- """))
- self.bad_name_srt_file.seek(0)
-
- self.ufeff_srt_file = tempfile.NamedTemporaryFile(suffix='.srt')
-
- def test_success_video_module_source_subs_uploading(self):
- self.item.data = textwrap.dedent("""
-
- """)
- modulestore().update_item(self.item, self.user.id)
+ # Add clean up handler
+ self.addCleanup(self.clean_temporary_transcripts)
- link = reverse('upload_transcripts')
- filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0]
- resp = self.client.post(link, {
- 'locator': self.video_usage_key,
- 'transcript-file': self.good_srt_file,
- 'video_list': json.dumps([{
- 'type': 'html5',
- 'video': filename,
- 'mode': 'mp4',
- }])
- })
- self.assertEqual(resp.status_code, 200)
- self.assertEqual(json.loads(resp.content).get('status'), 'Success')
+ def create_transcript_file(self, content, suffix, include_bom=False):
+ """
+ Setup a transcript file with suffix and content.
+ """
+ transcript_file = tempfile.NamedTemporaryFile(suffix=suffix)
+ wrapped_content = textwrap.dedent(content)
+ if include_bom:
+ wrapped_content = wrapped_content.encode('utf-8-sig')
+ # Verify that ufeff(BOM) character is in content.
+ self.assertIn(BOM_UTF8, wrapped_content)
- item = modulestore().get_item(self.video_usage_key)
- self.assertEqual(item.sub, filename)
+ transcript_file.write(wrapped_content)
+ transcript_file.seek(0)
- content_location = StaticContent.compute_location(
- self.course.id, 'subs_{0}.srt.sjson'.format(filename))
- self.assertTrue(contentstore().find(content_location))
+ return transcript_file
- def test_fail_data_without_id(self):
- link = reverse('upload_transcripts')
- resp = self.client.post(link, {'transcript-file': self.good_srt_file})
- self.assertEqual(resp.status_code, 400)
- self.assertEqual(json.loads(resp.content).get('status'), 'POST data without "locator" form data.')
+ def clean_temporary_transcripts(self):
+ """
+ Close transcript files gracefully.
+ """
+ self.good_srt_file.close()
+ self.bad_data_srt_file.close()
+ self.bad_name_srt_file.close()
+ self.bom_srt_file.close()
- def test_fail_data_without_file(self):
- link = reverse('upload_transcripts')
- resp = self.client.post(link, {'locator': self.video_usage_key})
- self.assertEqual(resp.status_code, 400)
- self.assertEqual(json.loads(resp.content).get('status'), 'POST data without "file" form data.')
+ def upload_transcript(self, locator, transcript_file):
+ """
+ Uploads a transcript for a video
+ """
+ payload = {}
+ if locator:
+ payload.update({'locator': locator})
- def test_fail_data_with_bad_locator(self):
- # Test for raising `InvalidLocationError` exception.
- link = reverse('upload_transcripts')
- filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0]
- resp = self.client.post(link, {
- 'locator': 'BAD_LOCATOR',
- 'transcript-file': self.good_srt_file,
- 'video_list': json.dumps([{
- 'type': 'html5',
- 'video': filename,
- 'mode': 'mp4',
- }])
- })
- self.assertEqual(resp.status_code, 400)
- self.assertEqual(json.loads(resp.content).get('status'), "Can't find item by locator.")
+ if transcript_file:
+ payload.update({'transcript-file': transcript_file})
- # Test for raising `ItemNotFoundError` exception.
- link = reverse('upload_transcripts')
- filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0]
- resp = self.client.post(link, {
- 'locator': '{0}_{1}'.format(self.video_usage_key, 'BAD_LOCATOR'),
- 'transcript-file': self.good_srt_file,
- 'video_list': json.dumps([{
- 'type': 'html5',
- 'video': filename,
- 'mode': 'mp4',
- }])
- })
- self.assertEqual(resp.status_code, 400)
- self.assertEqual(json.loads(resp.content).get('status'), "Can't find item by locator.")
+ upload_url = reverse('upload_transcripts')
+ response = self.client.post(upload_url, payload)
- def test_fail_for_non_video_module(self):
- # non_video module: setup
+ return response
+
+ def assert_transcript_upload_response(self, response, expected_status_code, expected_message):
+ response_content = json.loads(response.content)
+ self.assertEqual(response.status_code, expected_status_code)
+ self.assertEqual(response_content['status'], expected_message)
+
+ @ddt.data(
+ (u'123-456-789', False),
+ (u'', False),
+ (u'123-456-789', True)
+ )
+ @ddt.unpack
+ def test_transcript_upload_success(self, edx_video_id, include_bom):
+ """
+ Tests transcript file upload to video component works as
+ expected in case of following:
+
+ 1. External video component
+ 2. VEDA produced video component
+ 3. Transcript content containing BOM character
+ """
+ # In case of an external video component, the `edx_video_id` must be empty
+ # and VEDA produced video component will have `edx_video_id` set to VAL video ID.
+ self.item.edx_video_id = edx_video_id
+ modulestore().update_item(self.item, self.user.id)
+
+ # Upload a transcript
+ transcript_file = self.bom_srt_file if include_bom else self.good_srt_file
+ response = self.upload_transcript(self.video_usage_key, transcript_file)
+
+ # Verify the response
+ self.assert_transcript_upload_response(response, expected_status_code=200, expected_message='Success')
+
+ # Verify the `edx_video_id` on the video component
+ json_response = json.loads(response.content)
+ expected_edx_video_id = edx_video_id if edx_video_id else json_response['edx_video_id']
+ video = modulestore().get_item(self.video_usage_key)
+ self.assertEqual(video.edx_video_id, expected_edx_video_id)
+
+ # Verify transcript content
+ actual_transcript = get_video_transcript_content(video.edx_video_id, language_code=u'en')
+ actual_sjson_content = json.loads(actual_transcript['content'])
+ expected_sjson_content = json.loads(Transcript.convert(
+ self.contents['good'],
+ input_format=Transcript.SRT,
+ output_format=Transcript.SJSON
+ ))
+ self.assertDictEqual(actual_sjson_content, expected_sjson_content)
+
+ def test_transcript_upload_without_locator(self):
+ """
+ Test that transcript upload validation fails if the video locator is missing
+ """
+ response = self.upload_transcript(locator=None, transcript_file=self.good_srt_file)
+ self.assert_transcript_upload_response(
+ response,
+ expected_status_code=400,
+ expected_message=u'Video locator is required.'
+ )
+
+ def test_transcript_upload_without_file(self):
+ """
+ Test that transcript upload validation fails if transcript file is missing
+ """
+ response = self.upload_transcript(locator=self.video_usage_key, transcript_file=None)
+ self.assert_transcript_upload_response(
+ response,
+ expected_status_code=400,
+ expected_message=u'A transcript file is required.'
+ )
+
+ def test_transcript_upload_bad_format(self):
+ """
+ Test that transcript upload validation fails if transcript format is not SRT
+ """
+ response = self.upload_transcript(locator=self.video_usage_key, transcript_file=self.bad_name_srt_file)
+ self.assert_transcript_upload_response(
+ response,
+ expected_status_code=400,
+ expected_message=u'This transcript file type is not supported.'
+ )
+
+ def test_transcript_upload_bad_content(self):
+ """
+ Test that transcript upload validation fails in case of bad transcript content.
+ """
+ # Request to upload transcript for the video
+ response = self.upload_transcript(locator=self.video_usage_key, transcript_file=self.bad_data_srt_file)
+ self.assert_transcript_upload_response(
+ response,
+ expected_status_code=400,
+ expected_message=u'There is a problem with this transcript file. Try to upload a different file.'
+ )
+
+ def test_transcript_upload_unknown_category(self):
+ """
+ Test that transcript upload validation fails if item's category is other than video.
+ """
+ # non_video module setup - i.e. an item whose category is not 'video'.
data = {
'parent_locator': unicode(self.course.location),
'category': 'non_video',
@@ -221,145 +291,25 @@ def test_fail_for_non_video_module(self):
item.data = ''
modulestore().update_item(item, self.user.id)
- # non_video module: testing
-
- link = reverse('upload_transcripts')
- filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0]
- resp = self.client.post(link, {
- 'locator': unicode(usage_key),
- 'transcript-file': self.good_srt_file,
- 'video_list': json.dumps([{
- 'type': 'html5',
- 'video': filename,
- 'mode': 'mp4',
- }])
- })
- self.assertEqual(resp.status_code, 400)
- self.assertEqual(json.loads(resp.content).get('status'), 'Transcripts are supported only for "video" modules.')
-
- def test_fail_bad_xml(self):
- self.item.data = '<<'
- modulestore().update_item(self.item, self.user.id)
-
- link = reverse('upload_transcripts')
- filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0]
- resp = self.client.post(link, {
- 'locator': unicode(self.video_usage_key),
- 'transcript-file': self.good_srt_file,
- 'video_list': json.dumps([{
- 'type': 'html5',
- 'video': filename,
- 'mode': 'mp4',
- }])
- })
-
- self.assertEqual(resp.status_code, 400)
- # incorrect xml produces incorrect item category error
- self.assertEqual(json.loads(resp.content).get('status'), 'Transcripts are supported only for "video" modules.')
-
- def test_fail_bad_data_srt_file(self):
- link = reverse('upload_transcripts')
- filename = os.path.splitext(os.path.basename(self.bad_data_srt_file.name))[0]
- resp = self.client.post(link, {
- 'locator': unicode(self.video_usage_key),
- 'transcript-file': self.bad_data_srt_file,
- 'video_list': json.dumps([{
- 'type': 'html5',
- 'video': filename,
- 'mode': 'mp4',
- }])
- })
- self.assertEqual(resp.status_code, 400)
- self.assertEqual(json.loads(resp.content).get('status'), 'Something wrong with SubRip transcripts file during parsing.')
-
- def test_fail_bad_name_srt_file(self):
- link = reverse('upload_transcripts')
- filename = os.path.splitext(os.path.basename(self.bad_name_srt_file.name))[0]
- resp = self.client.post(link, {
- 'locator': unicode(self.video_usage_key),
- 'transcript-file': self.bad_name_srt_file,
- 'video_list': json.dumps([{
- 'type': 'html5',
- 'video': filename,
- 'mode': 'mp4',
- }])
- })
- self.assertEqual(resp.status_code, 400)
- self.assertEqual(json.loads(resp.content).get('status'), 'We support only SubRip (*.srt) transcripts format.')
-
- def test_undefined_file_extension(self):
- srt_file = tempfile.NamedTemporaryFile(suffix='')
- srt_file.write(textwrap.dedent("""
- 1
- 00:00:10,500 --> 00:00:13,000
- Elephant's Dream
-
- 2
- 00:00:15,000 --> 00:00:18,000
- At the left we can see...
- """))
- srt_file.seek(0)
-
- link = reverse('upload_transcripts')
- filename = os.path.splitext(os.path.basename(srt_file.name))[0]
- resp = self.client.post(link, {
- 'locator': self.video_usage_key,
- 'transcript-file': srt_file,
- 'video_list': json.dumps([{
- 'type': 'html5',
- 'video': filename,
- 'mode': 'mp4',
- }])
- })
- self.assertEqual(resp.status_code, 400)
- self.assertEqual(json.loads(resp.content).get('status'), 'Undefined file extension.')
+ # Request to upload transcript for the item
+ response = self.upload_transcript(locator=usage_key, transcript_file=self.good_srt_file)
+ self.assert_transcript_upload_response(
+ response,
+ expected_status_code=400,
+ expected_message=u'Transcripts are supported only for "video" module.'
+ )
- def test_subs_uploading_with_byte_order_mark(self):
+ def test_transcript_upload_non_existent_item(self):
"""
- Test uploading subs containing BOM(Byte Order Mark), e.g. U+FEFF
+ Test that transcript upload validation fails in case of invalid item's locator.
"""
- filedata = textwrap.dedent("""
- 1
- 00:00:10,500 --> 00:00:13,000
- Test ufeff characters
-
- 2
- 00:00:15,000 --> 00:00:18,000
- At the left we can see...
- """).encode('utf-8-sig')
-
- # Verify that ufeff character is in filedata.
- self.assertIn("ufeff", filedata)
- self.ufeff_srt_file.write(filedata)
- self.ufeff_srt_file.seek(0)
-
- link = reverse('upload_transcripts')
- filename = os.path.splitext(os.path.basename(self.ufeff_srt_file.name))[0]
- resp = self.client.post(link, {
- 'locator': self.video_usage_key,
- 'transcript-file': self.ufeff_srt_file,
- 'video_list': json.dumps([{
- 'type': 'html5',
- 'video': filename,
- 'mode': 'mp4',
- }])
- })
- self.assertEqual(resp.status_code, 200)
-
- content_location = StaticContent.compute_location(
- self.course.id, 'subs_{0}.srt.sjson'.format(filename))
- self.assertTrue(contentstore().find(content_location))
-
- subs_text = json.loads(contentstore().find(content_location).data).get('text')
- self.assertIn("Test ufeff characters", subs_text)
-
- def tearDown(self):
- super(TestUploadTranscripts, self).tearDown()
-
- self.good_srt_file.close()
- self.bad_data_srt_file.close()
- self.bad_name_srt_file.close()
- self.ufeff_srt_file.close()
+ # Request to upload transcript for the item
+ response = self.upload_transcript(locator='non_existent_locator', transcript_file=self.good_srt_file)
+ self.assert_transcript_upload_response(
+ response,
+ expected_status_code=400,
+ expected_message=u'Cannot find item by locator.'
+ )
class TestDownloadTranscripts(BaseTranscripts):
@@ -416,7 +366,7 @@ def test_success_download_nonyoutube(self):
'0\n00:00:00,100 --> 00:00:00,200\nsubs #1\n\n1\n00:00:00,200 --> '
'00:00:00,240\nsubs #2\n\n2\n00:00:00,240 --> 00:00:00,380\nsubs #3\n\n'
)
- transcripts_utils.remove_subs_from_store(subs_id, self.item)
+ remove_subs_from_store(subs_id, self.item)
def test_fail_data_without_file(self):
link = reverse('download_transcripts')
@@ -643,7 +593,7 @@ def test_success_download_nonyoutube(self):
}
)
- transcripts_utils.remove_subs_from_store(subs_id, self.item)
+ remove_subs_from_store(subs_id, self.item)
def test_check_youtube(self):
self.item.data = ''
diff --git a/cms/djangoapps/contentstore/views/transcripts_ajax.py b/cms/djangoapps/contentstore/views/transcripts_ajax.py
index da2cc4670fe0..880b48bd0f9b 100644
--- a/cms/djangoapps/contentstore/views/transcripts_ajax.py
+++ b/cms/djangoapps/contentstore/views/transcripts_ajax.py
@@ -14,12 +14,13 @@
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
+from django.core.files.base import ContentFile
from django.http import Http404, HttpResponse
from django.utils.translation import ugettext as _
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import UsageKey
from six import text_type
-
+from edxval.api import create_or_update_video_transcript, create_external_video
from student.auth import has_course_author_access
from util.json_request import JsonResponse
from xmodule.contentstore.content import StaticContent
@@ -28,6 +29,7 @@
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.video_module.transcripts_utils import (
+ clean_video_id,
copy_or_rename_transcript,
download_youtube_subs,
GetTranscriptsFromYouTubeException,
@@ -38,6 +40,7 @@
remove_subs_from_store,
Transcript,
TranscriptsRequestValidationException,
+ TranscriptsGenerationException,
youtube_video_transcript_name,
get_transcript,
get_transcript_from_val,
@@ -46,6 +49,8 @@
is_val_transcript_feature_enabled_for_course
)
+from cms.djangoapps.contentstore.views.videos import TranscriptProvider
+
__all__ = [
'upload_transcripts',
'download_transcripts',
@@ -70,6 +75,43 @@ def error_response(response, message, status_code=400):
return JsonResponse(response, status_code)
+def validate_transcript_upload_data(request):
+ """
+ Validates video transcript file.
+
+ Arguments:
+ request: A WSGI request's data part.
+
+ Returns:
+ Tuple containing an error and validated data
+ If there is a validation error then, validated data will be empty.
+ """
+ error, validated_data = None, {}
+ data, files = request.POST, request.FILES
+ if not data.get('locator'):
+ error = _(u'Video locator is required.')
+ elif 'transcript-file' not in files:
+ error = _(u'A transcript file is required.')
+ elif os.path.splitext(files['transcript-file'].name)[1][1:] != Transcript.SRT:
+ error = _(u'This transcript file type is not supported.')
+
+ if not error:
+ try:
+ item = _get_item(request, data)
+ if item.category != 'video':
+ error = _(u'Transcripts are supported only for "video" module.')
+ else:
+ validated_data.update({
+ 'video': item,
+ 'edx_video_id': clean_video_id(item.edx_video_id),
+ 'transcript_file': files['transcript-file']
+ })
+ except (InvalidKeyError, ItemNotFoundError):
+ error = _(u'Cannot find item by locator.')
+
+ return error, validated_data
+
+
@login_required
def upload_transcripts(request):
"""
@@ -80,67 +122,47 @@ def upload_transcripts(request):
status: 'Success' and HTTP 200 or 'Error' and HTTP 400.
subs: Value of uploaded and saved html5 sub field in video item.
"""
- response = {
- 'status': 'Unknown server error',
- 'subs': '',
- }
-
- locator = request.POST.get('locator')
- if not locator:
- return error_response(response, 'POST data without "locator" form data.')
-
- try:
- item = _get_item(request, request.POST)
- except (InvalidKeyError, ItemNotFoundError):
- return error_response(response, "Can't find item by locator.")
-
- if 'transcript-file' not in request.FILES:
- return error_response(response, 'POST data without "file" form data.')
-
- video_list = request.POST.get('video_list')
- if not video_list:
- return error_response(response, 'POST data without video names.')
-
- try:
- video_list = json.loads(video_list)
- except ValueError:
- return error_response(response, 'Invalid video_list JSON.')
-
- # Used utf-8-sig encoding type instead of utf-8 to remove BOM(Byte Order Mark), e.g. U+FEFF
- source_subs_filedata = request.FILES['transcript-file'].read().decode('utf-8-sig')
- source_subs_filename = request.FILES['transcript-file'].name
-
- if '.' not in source_subs_filename:
- return error_response(response, "Undefined file extension.")
-
- basename = os.path.basename(source_subs_filename)
- source_subs_name = os.path.splitext(basename)[0]
- source_subs_ext = os.path.splitext(basename)[1][1:]
-
- if item.category != 'video':
- return error_response(response, 'Transcripts are supported only for "video" modules.')
+ error, validated_data = validate_transcript_upload_data(request)
+ if error:
+ response = JsonResponse({'status': error}, status=400)
+ else:
+ video = validated_data['video']
+ edx_video_id = validated_data['edx_video_id']
+ transcript_file = validated_data['transcript_file']
+ # check if we need to create an external VAL video to associate the transcript
+ # and save its ID on the video component.
+ if not edx_video_id:
+ edx_video_id = create_external_video(display_name=u'external video')
+ video.edx_video_id = edx_video_id
+ video.save_with_metadata(request.user)
- # Allow upload only if any video link is presented
- if video_list:
- sub_attr = source_subs_name
try:
- # Generate and save for 1.0 speed, will create subs_sub_attr.srt.sjson subtitles file in storage.
- generate_subs_from_source({1: sub_attr}, source_subs_ext, source_subs_filedata, item)
-
- for video_dict in video_list:
- video_name = video_dict['video']
- # We are creating transcripts for every video source, if in future some of video sources would be deleted.
- # Updates item.sub with `video_name` on success.
- copy_or_rename_transcript(video_name, sub_attr, item, user=request.user)
-
- response['subs'] = item.sub
- response['status'] = 'Success'
- except Exception as ex:
- return error_response(response, text_type(ex))
- else:
- return error_response(response, 'Empty video sources.')
+ # Convert 'srt' transcript into the 'sjson' and upload it to
+ # configured transcript storage. For example, S3.
+ sjson_subs = Transcript.convert(
+ content=transcript_file.read(),
+ input_format=Transcript.SRT,
+ output_format=Transcript.SJSON
+ )
+ create_or_update_video_transcript(
+ video_id=edx_video_id,
+ language_code=u'en',
+ metadata={
+ 'provider': TranscriptProvider.CUSTOM,
+ 'file_format': Transcript.SJSON,
+ 'language_code': u'en'
+ },
+ file_data=ContentFile(sjson_subs),
+ )
+ response = JsonResponse({'edx_video_id': edx_video_id, 'status': 'Success'}, status=200)
+
+ except (TranscriptsGenerationException, UnicodeDecodeError):
+
+ response = JsonResponse({
+ 'status': _(u'There is a problem with this transcript file. Try to upload a different file.')
+ }, status=400)
- return JsonResponse(response)
+ return response
@login_required
diff --git a/cms/envs/bok_choy.py b/cms/envs/bok_choy.py
index cd681ed13b09..f4ea6652277a 100644
--- a/cms/envs/bok_choy.py
+++ b/cms/envs/bok_choy.py
@@ -145,6 +145,16 @@
'course_author': 'http://edx.readthedocs.io/projects/edx-partner-course-staff',
}
+########################## VIDEO TRANSCRIPTS STORAGE ############################
+VIDEO_TRANSCRIPTS_SETTINGS = dict(
+ VIDEO_TRANSCRIPTS_MAX_BYTES=3 * 1024 * 1024, # 3 MB
+ STORAGE_KWARGS=dict(
+ location=MEDIA_ROOT,
+ base_url=MEDIA_URL,
+ ),
+ DIRECTORY_PREFIX='video-transcripts/',
+)
+
#####################################################################
# Lastly, see if the developer has any local overrides.
try:
diff --git a/cms/static/js/spec/video/transcripts/file_uploader_spec.js b/cms/static/js/spec/video/transcripts/file_uploader_spec.js
index 0b7091241375..ad62e6ff750e 100644
--- a/cms/static/js/spec/video/transcripts/file_uploader_spec.js
+++ b/cms/static/js/spec/video/transcripts/file_uploader_spec.js
@@ -1,10 +1,10 @@
define(
[
- 'jquery', 'underscore',
+ 'jquery', 'underscore', 'backbone',
'js/views/video/transcripts/utils', 'js/views/video/transcripts/file_uploader',
'xmodule', 'jquery.form'
],
-function($, _, Utils, FileUploader) {
+function($, _, Backbone, Utils, FileUploader) {
'use strict';
describe('Transcripts.FileUploader', function() {
@@ -34,10 +34,6 @@ function($, _, Utils, FileUploader) {
'MessageManager',
['render', 'showError', 'hideError']
),
- videoListObject = jasmine.createSpyObj(
- 'MetadataView.VideoList',
- ['render', 'getVideoObjectsList']
- ),
$container = $('.transcripts-status');
$container
@@ -49,7 +45,6 @@ function($, _, Utils, FileUploader) {
view = new FileUploader({
el: $container,
messenger: messenger,
- videoListObject: videoListObject,
component_locator: 'component_locator'
});
});
@@ -196,17 +191,17 @@ function($, _, Utils, FileUploader) {
status: 200,
responseText: JSON.stringify({
status: 'Success',
- subs: 'test'
+ edx_video_id: 'test_video_id'
})
};
- spyOn(Utils.Storage, 'set');
+ spyOn(Backbone, 'trigger');
view.xhrCompleteHandler(xhr);
expect(view.$progress).toHaveClass('is-invisible');
expect(view.options.messenger.render.calls.mostRecent().args[0])
.toEqual('uploaded');
- expect(Utils.Storage.set)
- .toHaveBeenCalledWith('sub', 'test');
+ expect(Backbone.trigger)
+ .toHaveBeenCalledWith('transcripts:basicTabUpdateEdxVideoId', 'test_video_id');
});
var assertAjaxError = function(xhr) {
diff --git a/cms/static/js/spec/video/transcripts/message_manager_spec.js b/cms/static/js/spec/video/transcripts/message_manager_spec.js
index ec07e8a3da9a..cad64b8f28d5 100644
--- a/cms/static/js/spec/video/transcripts/message_manager_spec.js
+++ b/cms/static/js/spec/video/transcripts/message_manager_spec.js
@@ -61,8 +61,7 @@ function($, _, Utils, MessageManager, FileUploader, sinon) {
expect(fileUploader.initialize).toHaveBeenCalledWith({
el: view.$el,
messenger: view,
- component_locator: view.component_locator,
- videoListObject: view.options.parent
+ component_locator: view.component_locator
});
});
diff --git a/cms/static/js/views/video/transcripts/editor.js b/cms/static/js/views/video/transcripts/editor.js
index e949fd34290d..8fe6659a18a1 100644
--- a/cms/static/js/views/video/transcripts/editor.js
+++ b/cms/static/js/views/video/transcripts/editor.js
@@ -23,6 +23,9 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
el: this.$el,
collection: this.collection
});
+
+ // Listen to edx_video_id update
+ this.listenTo(Backbone, 'transcripts:basicTabUpdateEdxVideoId', this.handleUpdateEdxVideoId);
},
/**
@@ -241,6 +244,11 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
// Synchronize other fields that has the same `field_name` property.
Utils.syncCollections(this.collection, metadataCollection);
+ },
+
+ handleUpdateEdxVideoId: function(edxVideoId) {
+ var edxVideoIdField = Utils.getField(this.collection, 'edx_video_id');
+ edxVideoIdField.setValue(edxVideoId);
}
});
diff --git a/cms/static/js/views/video/transcripts/file_uploader.js b/cms/static/js/views/video/transcripts/file_uploader.js
index 7cfdc493ecb3..872578c96492 100644
--- a/cms/static/js/views/video/transcripts/file_uploader.js
+++ b/cms/static/js/views/video/transcripts/file_uploader.js
@@ -29,8 +29,7 @@ function($, Backbone, _, Utils) {
render: function() {
var tpl = $(this.uploadTpl).text(),
- tplContainer = this.$el.find('.transcripts-file-uploader'),
- videoList = this.options.videoListObject.getVideoObjectsList();
+ tplContainer = this.$el.find('.transcripts-file-uploader');
if (tplContainer.length) {
if (!tpl) {
@@ -42,8 +41,7 @@ function($, Backbone, _, Utils) {
tplContainer.html(this.template({
ext: this.validFileExtensions,
- component_locator: this.options.component_locator,
- video_list: videoList
+ component_locator: this.options.component_locator
}));
this.$form = this.$el.find('.file-chooser');
@@ -186,14 +184,14 @@ function($, Backbone, _, Utils) {
xhrCompleteHandler: function(xhr) {
var resp = JSON.parse(xhr.responseText),
err = resp.status || gettext('Error: Uploading failed.'),
- sub = resp.subs;
+ edxVideoId = resp.edx_video_id;
this.$progress
.addClass(this.invisibleClass);
if (xhr.status === 200) {
this.options.messenger.render('uploaded', resp);
- Utils.Storage.set('sub', sub);
+ Backbone.trigger('transcripts:basicTabUpdateEdxVideoId', edxVideoId);
} else {
this.options.messenger.showError(err);
}
diff --git a/cms/static/js/views/video/transcripts/message_manager.js b/cms/static/js/views/video/transcripts/message_manager.js
index 8ea93c99cccf..df4b983582c7 100644
--- a/cms/static/js/views/video/transcripts/message_manager.js
+++ b/cms/static/js/views/video/transcripts/message_manager.js
@@ -40,8 +40,7 @@ function($, Backbone, _, Utils, FileUploader, gettext) {
this.fileUploader = new FileUploader({
el: this.$el,
messenger: this,
- component_locator: this.component_locator,
- videoListObject: this.options.parent
+ component_locator: this.component_locator
});
},
diff --git a/cms/templates/js/video/transcripts/file-upload.underscore b/cms/templates/js/video/transcripts/file-upload.underscore
index 8edc816f9c02..925b846c0180 100644
--- a/cms/templates/js/video/transcripts/file-upload.underscore
+++ b/cms/templates/js/video/transcripts/file-upload.underscore
@@ -6,5 +6,4 @@
-
diff --git a/common/lib/xmodule/xmodule/video_module/transcripts_utils.py b/common/lib/xmodule/xmodule/video_module/transcripts_utils.py
index 53d5ebbb9b3d..fd6bc793c870 100644
--- a/common/lib/xmodule/xmodule/video_module/transcripts_utils.py
+++ b/common/lib/xmodule/xmodule/video_module/transcripts_utils.py
@@ -637,7 +637,8 @@ def convert(content, input_format, output_format):
# With error handling (set to 'ERROR_RAISE'), we will be getting
# the exception if something went wrong in parsing the transcript.
srt_subs = SubRipFile.from_string(
- content.decode('utf8'),
+ # Skip byte order mark(BOM) character
+ content.decode('utf-8-sig'),
error_handling=SubRipFile.ERROR_RAISE
)
except Error as ex: # Base exception from pysrt
diff --git a/common/test/acceptance/tests/video/test_studio_video_transcript.py b/common/test/acceptance/tests/video/test_studio_video_transcript.py
index 2f294f0efb48..6ba31c333bd9 100644
--- a/common/test/acceptance/tests/video/test_studio_video_transcript.py
+++ b/common/test/acceptance/tests/video/test_studio_video_transcript.py
@@ -502,40 +502,45 @@ def test_html5_with_transcripts(self):
def test_two_html5_sources_w_transcripts(self):
"""
Scenario: Enter 2 HTML5 sources with transcripts, they are not the same, choose
- Given I have created a Video component with subtitles "t_not_exist"
+ Given I have created a Video component and subtitles "t_not_exist" and "t__eq_exist"
+ are present in contentstore.
- And I enter a "uk_transcripts.mp4" source to field number 1
- Then I see status message "No Timed Transcript"
+ And I enter a "t_not_exist.mp4" source to field number 1
+ Then I see status message "Timed Transcript Found"
`download_to_edit` and `upload_new_timed_transcripts` buttons are shown
- And I upload the transcripts file "uk_transcripts.srt"
- Then I see status message "Timed Transcript Uploaded Successfully"
- And I see value "uk_transcripts" in the field "Default Timed Transcript"
- And I enter a "t_not_exist.webm" source to field number 2
+ And I enter a "t__eq_exist.webm" source to field number 2
Then I see status message "Timed Transcript Conflict"
- `Timed Transcript from uk_transcripts.mp4` and `Timed Transcript from t_not_exist.webm` buttons are shown
+ `Timed Transcript from t_not_exist.mp4` and `Timed Transcript from t__eq_exist.webm` buttons are shown
And I click transcript button "Timed Transcript from t_not_exist.webm"
And I see value "uk_transcripts|t_not_exist" in the field "Default Timed Transcript"
"""
- self._create_video_component(subtitles=True, subtitle_id='t_not_exist')
+ # Setup a course, navigate to the unit containing
+ # video component and edit the video component.
+ self.assets.append('subs_t_not_exist.srt.sjson')
+ self.assets.append('subs_t__eq_exist.srt.sjson')
+ self.navigate_to_course_unit()
self.edit_component()
- self.video.set_url_field('uk_transcripts.mp4', 1)
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
+ self.video.set_url_field('t_not_exist.mp4', 1)
+ self.assertEqual(self.video.message('status'), 'Timed Transcript Found')
self.assertTrue(self.video.is_transcript_button_visible('download_to_edit'))
self.assertTrue(self.video.is_transcript_button_visible('upload_new_timed_transcripts'))
- self.video.upload_transcript('uk_transcripts.srt')
- self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
- self.open_advanced_tab()
- self.assertTrue(self.video.verify_field_value('Default Timed Transcript', 'uk_transcripts'))
- self.open_basic_tab()
- self.video.set_url_field('t_not_exist.webm', 2)
+ self.video.set_url_field('t__eq_exist.webm', 2)
self.assertEqual(self.video.message('status'), 'Timed Transcript Conflict')
- self.assertTrue(
- self.video.is_transcript_button_visible('choose', button_text='Timed Transcript from uk_transcripts.mp4'))
- self.assertTrue(self.video.is_transcript_button_visible('choose', index=1,
- button_text='Timed Transcript from t_not_exist.webm'))
+
+ self.assertTrue(self.video.is_transcript_button_visible(
+ 'choose',
+ button_text='Timed Transcript from t__eq_exist.webm'
+ ))
+ self.assertTrue(self.video.is_transcript_button_visible(
+ 'choose',
+ index=1,
+ button_text='Timed Transcript from t_not_exist.mp4'
+ ))
+
+ # TODO: Incomplete – working on `choose_transcript` handler will complete this test.
def test_one_field_only(self):
"""
@@ -640,31 +645,35 @@ def test_two_fields_only(self):
def test_upload_subtitles(self):
"""
- Scenario: File name and name of subs are different (Uploading subtitles with different file name than file)
+ Scenario: Transcript upload for a video who has Video ID set on it.
Given I have created a Video component
- And I enter a "video_name_1.mp4" source to field number 1
+ I enter a "video_name_1.mp4" source to field number 1
+ And set "Video ID" to "video_001"
And I see status message "No Timed Transcript"
And I upload the transcripts file "uk_transcripts.srt"
Then I see status message "Timed Transcript Uploaded Successfully"
- And I see value "video_name_1" in the field "Default Timed Transcript"
And I save changes
Then when I view the video it does show the captions
And I edit the component
Then I see status message "Timed Transcript Found"
"""
self._create_video_component()
+
self.edit_component()
+ self.video.set_field_value('Video ID', 'video_001')
+ self.save_unit_settings()
+ self.edit_component()
self.video.set_url_field('video_name_1.mp4', 1)
self.assertEqual(self.video.message('status'), 'No Timed Transcript')
self.video.upload_transcript('uk_transcripts.srt')
self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
- self.open_advanced_tab()
- self.assertTrue(self.video.verify_field_value('Default Timed Transcript', 'video_name_1'))
self.save_unit_settings()
self.video.is_captions_visible()
+
self.edit_component()
+ self.video.verify_field_value('Video ID', 'video_001')
self.assertEqual(self.video.message('status'), 'Timed Transcript Found')
def test_video_wo_subtitles(self):
@@ -684,37 +693,6 @@ def test_video_wo_subtitles(self):
self.video.set_url_field('video_name_1.mp4', 1)
self.assertEqual(self.video.message('status'), 'No Timed Transcript')
- def test_subtitles_copy(self):
- """
- Scenario: Subtitles are copied for every html5 video source
- Given I have created a Video component
-
- After I enter a "video_name_1.mp4" source to field number 1 Then I see status message "No Timed Transcript"
-
- After I enter a "video_name_2.webm" source to field number 2 Then I see status message "No Timed Transcript"
- After uploading transcript "uk_transcripts.srt" I should see message "Timed Transcript Uploaded Successfully"
- And I see value "video_name_2" in the field "Default Timed Transcript"
- When I clear field number 1 Then I see status message "Timed Transcript Found"
- And I see value "video_name_2" in the field "Default Timed Transcript"
- """
- self._create_video_component()
- self.edit_component()
-
- self.video.set_url_field('video_name_1.mp4', 1)
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
-
- self.video.set_url_field('video_name_2.webm', 2)
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
- self.video.upload_transcript('uk_transcripts.srt')
- self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
- self.open_advanced_tab()
- self.assertTrue(self.video.verify_field_value('Default Timed Transcript', 'video_name_2'))
- self.open_basic_tab()
- self.video.clear_field(1)
- self.assertEqual(self.video.message('status'), 'Timed Transcript Found')
- self.open_advanced_tab()
- self.assertTrue(self.video.verify_field_value('Default Timed Transcript', 'video_name_2'))
-
def test_upload_button_w_youtube(self):
"""
Scenario: Upload button for single youtube id
@@ -736,6 +714,7 @@ def test_upload_button_w_youtube(self):
self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
self.save_unit_settings()
self.assertTrue(self.video.is_captions_visible())
+
self.edit_component()
self.assertEqual(self.video.message('status'), 'Timed Transcript Found')
@@ -751,8 +730,7 @@ def test_upload_button_w_html5_ids(self):
And I see button "upload_new_timed_transcripts"
After I upload the transcripts file "uk_transcripts.srt"I see message "Timed Transcript Uploaded Successfully"
When I clear field number 1 Then I see status message "Timed Transcript Found"
- And I see value "video_name_1" in the field "Default Timed Transcript"
- After saving the changes video captions should be visible
+ After saving the changes video captions are visible
When I edit the component Then I see status message "Timed Transcript Found"
"""
self._create_video_component()
@@ -767,12 +745,12 @@ def test_upload_button_w_html5_ids(self):
self.assertTrue(self.video.is_transcript_button_visible('upload_new_timed_transcripts'))
self.video.upload_transcript('uk_transcripts.srt')
self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
+ # Removing a source from "Video URL" field will make an ajax call to `check_transcripts`.
self.video.clear_field(1)
self.assertEqual(self.video.message('status'), 'Timed Transcript Found')
- self.open_advanced_tab()
- self.assertTrue(self.video.verify_field_value('Default Timed Transcript', 'video_name_1'))
self.save_unit_settings()
self.assertTrue(self.video.is_captions_visible())
+
self.edit_component()
self.assertEqual(self.video.message('status'), 'Timed Transcript Found')
@@ -846,148 +824,13 @@ def test_module_metadata_save(self):
self.assertEqual(self.video.message('status'), 'Timed Transcript Found')
self.assertTrue(self.video.verify_field_value('Default Timed Transcript', 'video_name_1'))
- def test_clearing_transcripts_wo_save(self):
- """
- Scenario: After clearing Transcripts field in the Advanced tab "not found" message should be visible w/o saving
- Given I have created a Video component
-
- After I enter a "t_not_exist.mp4" source to field number 1 I should see status message "No Timed Transcript"
- After uploading transcripts "chinese_transcripts.srt" I see message "Timed Transcript Uploaded Successfully"
- Open tab "Advanced" and set value "" to the field "Default Timed Transcript"
- When I open tab "Basic" I see status message "No Timed Transcript"
- After saving the changes video captions should not be visible
- When I edit the component I should see status message "No Timed Transcript"
- And I see value "" in the field "Default Timed Transcript"
- """
- self._create_video_component()
- self.edit_component()
-
- self.video.set_url_field('t_not_exist.mp4', 1)
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
- self.video.upload_transcript('chinese_transcripts.srt')
- self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
- self.open_advanced_tab()
- self.video.set_field_value('Default Timed Transcript', '')
- self.open_basic_tab()
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
- self.save_unit_settings()
- self.assertFalse(self.video.is_captions_visible())
- self.edit_component()
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
- self.assertTrue(self.video.verify_field_value('Default Timed Transcript', ''))
-
- def test_clearing_transcripts_w_save(self):
- """
- Scenario: After clearing Transcripts field in the Advanced tab "not found" message should be visible with saving
- Given I have created a Video component
-
- After I enter a "t_not_exist.mp4" source to field number 1 I see status message "No Timed Transcript"
- After uploading the transcripts "chinese_transcripts.srt" I see message "Timed Transcript Uploaded Successfully"
- After saving changes I see "好 各位同学" text in the captions
- And I edit the component
- Open tab "Advanced" I set value "" to the field "Default Timed Transcript"
- When I open tab "Basic" I see status message "No Timed Transcript"
- After saving the changes video captions should not be visible
- After I edit the component I should see status message "No Timed Transcript"
- And I see value "" in the field "Default Timed Transcript"
- """
- self._create_video_component()
- self.edit_component()
-
- self.video.set_url_field('t_not_exist.mp4', 1)
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
- self.video.upload_transcript('chinese_transcripts.srt')
- self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
- self.save_unit_settings()
- unicode_text = "好 各位同学".decode('utf-8')
- self.assertIn(unicode_text, self.video.captions_text)
- self.edit_component()
- self.open_advanced_tab()
- self.video.set_field_value('Default Timed Transcript', '')
- self.open_basic_tab()
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
- self.save_unit_settings()
- self.assertFalse(self.video.is_captions_visible())
- self.edit_component()
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
- self.assertTrue(self.video.verify_field_value('Default Timed Transcript', ''))
-
- def test_video_w_existing_subtitles(self):
- """
- Scenario: Video with existing subs - Advanced tab - change to another one subs -
- Basic tab - Found message - Save - see correct subs
- Given I have created a Video component with subtitles "t_not_exist"
-
- After I enter a "video_name_1.mp4" source to field number 1 I see status message "No Timed Transcript"
- After uploading the transcripts "chinese_transcripts.srt" I see message "Timed Transcript Uploaded Successfully"
- After saving the changes video captions should be visible
- And I see "好 各位同学" text in the captions
- And I edit the component
- Open tab "Advanced" And set value "t_not_exist" to the field "Default Timed Transcript"
- When I open tab "Basic" I should see status message "Timed Transcript Found"
- After saving the changes video captions should be visible
- And I see "LILA FISHER: Hi, welcome to Edx." text in the captions
- """
- self._create_video_component(subtitles=True, subtitle_id='t_not_exist')
- self.edit_component()
-
- self.video.set_url_field('video_name_1.mp4', 1)
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
- self.video.upload_transcript('chinese_transcripts.srt')
- self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
- self.save_unit_settings()
- self.assertTrue(self.video.is_captions_visible())
- unicode_text = "好 各位同学".decode('utf-8')
- self.assertIn(unicode_text, self.video.captions_text)
- self.edit_component()
- self.open_advanced_tab()
- self.video.set_field_value('Default Timed Transcript', 't_not_exist')
- self.open_basic_tab()
- self.assertEqual(self.video.message('status'), 'Timed Transcript Found')
- self.save_unit_settings()
- self.assertTrue(self.video.is_captions_visible())
- self.assertIn('LILA FISHER: Hi, welcome to Edx.', self.video.captions_text)
-
- def test_reverting_transcripts(self):
- """
- Scenario: After reverting Transcripts field in the Advanced tab "not found" message should be visible
- Given I have created a Video component
-
- After I enter a "t_not_exist.mp4" source to field number 1 Then I see status message "No Timed Transcript"
- After uploading transcripts "chinese_transcripts.srt" I see message "Timed Transcript Uploaded Successfully"
- After saving the changes I should see "好 各位同学" text in the captions
- After I edit the component I open tab "Advanced"
- And I revert the transcript field "Default Timed Transcript"
- After saving the changes video captions should not be visible
- After I edit the component I should see status message "No Timed Transcript"
- """
- self._create_video_component()
- self.edit_component()
-
- self.video.set_url_field('t_not_exist.mp4', 1)
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
- self.video.upload_transcript('chinese_transcripts.srt')
- self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
- self.save_unit_settings()
- unicode_text = "好 各位同学".decode('utf-8')
- self.assertIn(unicode_text, self.video.captions_text)
- self.edit_component()
- self.open_advanced_tab()
- self.video.revert_field('Default Timed Transcript')
- self.save_unit_settings()
- self.assertFalse(self.video.is_captions_visible())
- self.edit_component()
- self.assertEqual(self.video.message('status'), 'No Timed Transcript')
-
def test_upload_subtitles_w_different_names2(self):
"""
- Scenario: File name and name of subs are different -- Uploading subtitles for file with periods
- in it should properly set the transcript name and keep the periods
+ Scenario: Uploading subtitles for file with periods in it does not effect the uploaded transcript in anyway
Given I have created a Video component
After I enter a "video_name_1.1.2.mp4" source to field number 1, I see status message "No Timed Transcript"
After I upload the transcripts file "uk_transcripts.srt" I see message "Timed Transcript Uploaded Successfully"
- And I see value "video_name_1.1.2" in the field "Default Timed Transcript"
After saving the changes video captions should be visible
After I edit the component I should see status message "Timed Transcript Found"
"""
@@ -998,21 +841,19 @@ def test_upload_subtitles_w_different_names2(self):
self.assertEqual(self.video.message('status'), 'No Timed Transcript')
self.video.upload_transcript('uk_transcripts.srt')
self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
- self.open_advanced_tab()
- self.assertTrue(self.video.verify_field_value('Default Timed Transcript', 'video_name_1.1.2'))
self.save_unit_settings()
self.assertTrue(self.video.is_captions_visible())
+
self.edit_component()
self.assertEqual(self.video.message('status'), 'Timed Transcript Found')
def test_upload_subtitles_w_different_names3(self):
"""
- Scenario: Shortened link: File name and name of subs are different
- Given I have created a Video component
+ Scenario: Shortened link: Shortened link to the source does not effect the uploaded
+ transcript, given I have created a Video component
After I enter a "http://goo.gl/pxxZrg" source to field number 1 Then I see status message "No Timed Transcript"
After I upload the transcripts file "uk_transcripts.srt" I see message "Timed Transcript Uploaded Successfully"
- And I see value "pxxZrg" in the field "Default Timed Transcript"
After saving the changes video captions should be visible
After I edit the component I should see status message "Timed Transcript Found"
"""
@@ -1023,17 +864,16 @@ def test_upload_subtitles_w_different_names3(self):
self.assertEqual(self.video.message('status'), 'No Timed Transcript')
self.video.upload_transcript('uk_transcripts.srt')
self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
- self.open_advanced_tab()
- self.assertTrue(self.video.verify_field_value('Default Timed Transcript', 'pxxZrg'))
self.save_unit_settings()
self.assertTrue(self.video.is_captions_visible())
+
self.edit_component()
self.assertEqual(self.video.message('status'), 'Timed Transcript Found')
def test_upload_subtitles_w_different_names4(self):
"""
- Scenario: Relative link: File name and name of subs are different
- Given I have created a Video component
+ Scenario: Relative link: Relative link to the source does not effect the uploaded
+ transcript, given I have created a Video component
After i enter a "/gizmo.webm" source to field number 1 Then I see status message "No Timed Transcript"
After I upload the transcripts file "uk_transcripts.srt" I see message "Timed Transcript Uploaded Successfully"
@@ -1048,9 +888,8 @@ def test_upload_subtitles_w_different_names4(self):
self.assertEqual(self.video.message('status'), 'No Timed Transcript')
self.video.upload_transcript('uk_transcripts.srt')
self.assertEqual(self.video.message('status'), 'Timed Transcript Uploaded Successfully')
- self.open_advanced_tab()
- self.assertTrue(self.video.verify_field_value('Default Timed Transcript', 'gizmo'))
self.save_unit_settings()
self.assertTrue(self.video.is_captions_visible())
+
self.edit_component()
self.assertEqual(self.video.message('status'), 'Timed Transcript Found')