Example #1
1
 def setUp(self):
     super(VideoUploadTestCase, self).setUp()
     self.url = VideoUploadTestCase.get_url_for_course_key(self.course.id)
     self.test_token = "test_token"
     self.course.video_upload_pipeline = {
         "course_video_upload_token": self.test_token,
     }
     self.save_course()
     self.previous_uploads = [
         {
             "edx_video_id": "test1",
             "client_video_id": "test1.mp4",
             "duration": 42.0,
             "status": "transcode_active",
             "encoded_videos": [],
         },
         {
             "edx_video_id": "test2",
             "client_video_id": "test2.mp4",
             "duration": 128.0,
             "status": "file_complete",
             "encoded_videos": [],
         }
     ]
     for video in self.previous_uploads:
         create_video(video)
         modulestore().save_asset_metadata(
             AssetMetadata(
                 self.course.id.make_asset_key(VIDEO_ASSET_TYPE, video["edx_video_id"])
             ),
             self.user.id
         )
    def setUp(self):
        super(ScrapeVideoThumbnailsTestCase, self).setUp()
        course_ids = [unicode(self.course.id)]
        profiles = ['youtube']
        created = datetime.now(pytz.utc)
        previous_uploads = [
            {
                'edx_video_id': 'test1',
                'client_video_id': 'test1.mp4',
                'duration': 42.0,
                'status': 'upload',
                'courses': course_ids,
                'encoded_videos': [],
                'created': created
            },
            {
                'edx_video_id': 'test-youtube-video-1',
                'client_video_id': 'test-youtube-id.mp4',
                'duration': 128.0,
                'status': 'file_complete',
                'courses': course_ids,
                'created': created,
                'encoded_videos': [
                    {
                        'profile': 'youtube',
                        'url': '3_yD_cEKoCk',
                        'file_size': 1600,
                        'bitrate': 100,
                    }
                ],
            },
            {
                'edx_video_id': 'test-youtube-video-2',
                'client_video_id': 'test-youtube-id.mp4',
                'image': 'image2.jpg',
                'duration': 128.0,
                'status': 'file_complete',
                'courses': course_ids,
                'created': created,
                'encoded_videos': [
                    {
                        'profile': 'youtube',
                        'url': '3_yD_cEKoCk',
                        'file_size': 1600,
                        'bitrate': 100,
                    }
                ],
            },
        ]
        for profile in profiles:
            create_profile(profile)

        for video in previous_uploads:
            create_video(video)

        # Create video images.
        with make_image_file() as image_file:
            update_video_image(
                'test-youtube-video-2', unicode(self.course.id), image_file, 'image.jpg'
            )
Example #3
0
    def test_export_val_data(self):
        self.descriptor.edx_video_id = "test_edx_video_id"
        create_profile("mobile")
        create_video(
            {
                "edx_video_id": self.descriptor.edx_video_id,
                "client_video_id": "test_client_video_id",
                "duration": 111,
                "status": "dummy",
                "encoded_videos": [
                    {"profile": "mobile", "url": "http://example.com/video", "file_size": 222, "bitrate": 333}
                ],
            }
        )

        actual = self.descriptor.definition_to_xml(resource_fs=None)
        expected_str = """
            <video download_video="false" url_name="SampleProblem">
                <video_asset client_video_id="test_client_video_id" duration="111.0">
                    <encoded_video profile="mobile" url="http://example.com/video" file_size="222" bitrate="333"/>
                </video_asset>
            </video>
        """
        parser = etree.XMLParser(remove_blank_text=True)
        expected = etree.XML(expected_str, parser=parser)
        self.assertXmlEqual(expected, actual)
    def test_export_val_data(self):
        self.descriptor.edx_video_id = 'test_edx_video_id'
        create_profile('mobile')
        create_video({
            'edx_video_id': self.descriptor.edx_video_id,
            'client_video_id': 'test_client_video_id',
            'duration': 111,
            'status': 'dummy',
            'encoded_videos': [{
                'profile': 'mobile',
                'url': 'http://example.com/video',
                'file_size': 222,
                'bitrate': 333,
            }],
        })

        actual = self.descriptor.definition_to_xml(resource_fs=None)
        expected_str = """
            <video download_video="false" url_name="SampleProblem">
                <video_asset client_video_id="test_client_video_id" duration="111.0">
                    <encoded_video profile="mobile" url="http://example.com/video" file_size="222" bitrate="333"/>
                </video_asset>
            </video>
        """
        parser = etree.XMLParser(remove_blank_text=True)
        expected = etree.XML(expected_str, parser=parser)
        self.assertXmlEqual(expected, actual)
Example #5
0
 def setUp(self):
     super(VideoUploadTestCase, self).setUp()
     self.url = VideoUploadTestCase.get_url_for_course_key(self.course.id)
     self.test_token = "test_token"
     self.course.video_upload_pipeline = {
         "course_video_upload_token": self.test_token,
     }
     self.save_course()
     self.previous_uploads = [{
         "edx_video_id": "test1",
         "client_video_id": "test1.mp4",
         "duration": 42.0,
         "status": "transcode_active",
         "encoded_videos": [],
     }, {
         "edx_video_id": "test2",
         "client_video_id": "test2.mp4",
         "duration": 128.0,
         "status": "file_complete",
         "encoded_videos": [],
     }]
     for video in self.previous_uploads:
         create_video(video)
         modulestore().save_asset_metadata(
             AssetMetadata(
                 self.course.id.make_asset_key(VIDEO_ASSET_TYPE,
                                               video["edx_video_id"])),
             self.user.id)
    def test_export_val_data(self):
        self.descriptor.edx_video_id = 'test_edx_video_id'
        create_profile('mobile')
        create_video({
            'edx_video_id':
            self.descriptor.edx_video_id,
            'client_video_id':
            'test_client_video_id',
            'duration':
            111,
            'status':
            'dummy',
            'encoded_videos': [{
                'profile': 'mobile',
                'url': 'http://example.com/video',
                'file_size': 222,
                'bitrate': 333,
            }],
        })

        actual = self.descriptor.definition_to_xml(resource_fs=None)
        expected_str = """
            <video download_video="false" url_name="SampleProblem">
                <video_asset client_video_id="test_client_video_id" duration="111.0">
                    <encoded_video profile="mobile" url="http://example.com/video" file_size="222" bitrate="333"/>
                </video_asset>
            </video>
        """
        parser = etree.XMLParser(remove_blank_text=True)
        expected = etree.XML(expected_str, parser=parser)
        self.assertXmlEqual(expected, actual)
Example #7
0
    def setUp(self):
        super(TestUploadTranscripts, self).setUp()
        self.contents = {
            'good': SRT_TRANSCRIPT_CONTENT,
            'bad': u'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': [six.text_type(self.course.id)]
        })

        # Add clean up handler
        self.addCleanup(self.clean_temporary_transcripts)
Example #8
0
    def setUp(self):
        super(ScrapeVideoThumbnailsTestCase, self).setUp()
        course_ids = [unicode(self.course.id)]
        profiles = ['youtube']
        created = datetime.now(pytz.utc)
        previous_uploads = [
            {
                'edx_video_id': 'test1',
                'client_video_id': 'test1.mp4',
                'duration': 42.0,
                'status': 'upload',
                'courses': course_ids,
                'encoded_videos': [],
                'created': created
            },
            {
                'edx_video_id': 'test-youtube-video-1',
                'client_video_id': 'test-youtube-id.mp4',
                'duration': 128.0,
                'status': 'file_complete',
                'courses': course_ids,
                'created': created,
                'encoded_videos': [
                    {
                        'profile': 'youtube',
                        'url': '3_yD_cEKoCk',
                        'file_size': 1600,
                        'bitrate': 100,
                    }
                ],
            },
            {
                'edx_video_id': 'test-youtube-video-2',
                'client_video_id': 'test-youtube-id.mp4',
                'image': 'image2.jpg',
                'duration': 128.0,
                'status': 'file_complete',
                'courses': course_ids,
                'created': created,
                'encoded_videos': [
                    {
                        'profile': 'youtube',
                        'url': '3_yD_cEKoCk',
                        'file_size': 1600,
                        'bitrate': 100,
                    }
                ],
            },
        ]
        for profile in profiles:
            create_profile(profile)

        for video in previous_uploads:
            create_video(video)

        # Create video images.
        with make_image_file() as image_file:
            update_video_image(
                'test-youtube-video-2', unicode(self.course.id), image_file, 'image.jpg'
            )
Example #9
0
    def test_translation_delete_w_edx_video_id(self):
        """
        Verify that DELETE dispatch works as expected when video has edx_video_id
        """
        request_body = json.dumps({'lang': self.LANGUAGE_CODE_UK, 'edx_video_id': self.EDX_VIDEO_ID})
        api.create_video({
            'edx_video_id': self.EDX_VIDEO_ID,
            'status': 'upload',
            'client_video_id': 'awesome.mp4',
            'duration': 0,
            'encoded_videos': [],
            'courses': [unicode(self.course.id)]
        })
        api.create_video_transcript(
            video_id=self.EDX_VIDEO_ID,
            language_code=self.LANGUAGE_CODE_UK,
            file_format='srt',
            content=ContentFile(SRT_content)
        )

        # verify that a video transcript exists for expected data
        self.assertTrue(api.get_video_transcript_data(video_id=self.EDX_VIDEO_ID, language_code=self.LANGUAGE_CODE_UK))

        request = Request(self.REQUEST_META, body=request_body)
        self.item_descriptor.edx_video_id = self.EDX_VIDEO_ID
        response = self.item_descriptor.studio_transcript(request=request, dispatch='translation')
        self.assertEqual(response.status_code, 200)

        # verify that a video transcript dose not exist for expected data
        self.assertFalse(api.get_video_transcript_data(video_id=self.EDX_VIDEO_ID, language_code=self.LANGUAGE_CODE_UK))
Example #10
0
    def setUp(self):
        super(TestVideoAPITestCase, self).setUp()
        self.section = ItemFactory.create(
            parent=self.course, category="chapter", display_name=u"test factory section omega \u03a9"
        )
        self.sub_section = ItemFactory.create(
            parent=self.section, category="sequential", display_name=u"test subsection omega \u03a9"
        )

        self.unit = ItemFactory.create(
            parent=self.sub_section,
            category="vertical",
            metadata={"graded": True, "format": "Homework"},
            display_name=u"test unit omega \u03a9",
        )
        self.other_unit = ItemFactory.create(
            parent=self.sub_section,
            category="vertical",
            metadata={"graded": True, "format": "Homework"},
            display_name=u"test unit omega 2 \u03a9",
        )
        self.nameless_unit = ItemFactory.create(
            parent=self.sub_section,
            category="vertical",
            metadata={"graded": True, "format": "Homework"},
            display_name=None,
        )

        self.edx_video_id = "testing-123"
        self.video_url = "http://val.edx.org/val/video.mp4"
        self.video_url_high = "http://val.edx.org/val/video_high.mp4"
        self.youtube_url = "http://val.edx.org/val/youtube.mp4"
        self.html5_video_url = "http://video.edx.org/html5/video.mp4"

        api.create_profile("youtube")
        api.create_profile("mobile_high")
        api.create_profile("mobile_low")

        # create the video in VAL
        api.create_video(
            {
                "edx_video_id": self.edx_video_id,
                "status": "test",
                "client_video_id": u"test video omega \u03a9",
                "duration": 12,
                "courses": [unicode(self.course.id)],
                "encoded_videos": [
                    {"profile": "youtube", "url": "xyz123", "file_size": 0, "bitrate": 1500},
                    {"profile": "mobile_low", "url": self.video_url, "file_size": 12345, "bitrate": 250},
                    {"profile": "mobile_high", "url": self.video_url_high, "file_size": 99999, "bitrate": 250},
                ],
            }
        )

        # Set requested profiles
        MobileApiConfig(video_profiles="mobile_low,mobile_high,youtube").save()
Example #11
0
 def setUp(self):
     """
     Creation of Video object that will be used to test video update
     Creation of Profile objects that will be used to test video update
     """
     api.create_profile(constants.PROFILE_DESKTOP)
     api.create_profile(constants.PROFILE_MOBILE)
     video_data = dict(
         encoded_videos=[constants.ENCODED_VIDEO_DICT_FISH_MOBILE],
         **constants.VIDEO_DICT_FISH)
     api.create_video(video_data)
Example #12
0
    def setUp(self):
        super(TestVideoOutline, self).setUp()
        self.user = UserFactory.create()
        self.course = CourseFactory.create(mobile_available=True)
        self.section = ItemFactory.create(
            parent_location=self.course.location, category="chapter", display_name=u"test factory section omega \u03a9"
        )
        self.sub_section = ItemFactory.create(
            parent_location=self.section.location, category="sequential", display_name=u"test subsection omega \u03a9"
        )

        self.unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={"graded": True, "format": "Homework"},
            display_name=u"test unit omega \u03a9",
        )
        self.other_unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={"graded": True, "format": "Homework"},
            display_name=u"test unit omega 2 \u03a9",
        )
        self.nameless_unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={"graded": True, "format": "Homework"},
            display_name=None,
        )

        self.edx_video_id = "testing-123"

        self.video_url = "http://val.edx.org/val/video.mp4"
        self.html5_video_url = "http://video.edx.org/html5/video.mp4"

        api.create_profile({"profile_name": "youtube", "extension": "mp4", "width": 1280, "height": 720})
        api.create_profile({"profile_name": "mobile_low", "extension": "mp4", "width": 640, "height": 480})

        # create the video in VAL
        api.create_video(
            {
                "edx_video_id": self.edx_video_id,
                "client_video_id": u"test video omega \u03a9",
                "duration": 12,
                "courses": [unicode(self.course.id)],
                "encoded_videos": [
                    {"profile": "youtube", "url": "xyz123", "file_size": 0, "bitrate": 1500},
                    {"profile": "mobile_low", "url": self.video_url, "file_size": 12345, "bitrate": 250},
                ],
            }
        )

        self.client.login(username=self.user.username, password="******")
Example #13
0
    def setUp(self):
        super(TestReplaceTranscripts, self).setUp()
        self.youtube_id = 'test_yt_id'

        # 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': [six.text_type(self.course.id)]
        })
    def setUp(self):
        super(TestReplaceTranscripts, self).setUp()
        self.youtube_id = 'test_yt_id'

        # 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)]
        })
Example #15
0
 def test_invalid_profile(self):
     """
     Tests inputting bad profile type
     """
     video_data = dict(
         encoded_videos=[
             dict(
                 profile=constants.PROFILE_DICT_MOBILE,
                 **constants.ENCODED_VIDEO_DICT_MOBILE
             )
         ],
         **constants.VIDEO_DICT_FISH
     )
     with self.assertRaises(ValidationError):
         api.create_video(video_data)
 def setup_val_video(self, associate_course_in_val=False):
     """
     Creates a video entry in VAL.
     Arguments:
         associate_course - If True, associates the test course with the video in VAL.
     """
     create_profile('mobile')
     create_video({
         'edx_video_id': self.TEST_EDX_VIDEO_ID,
         'client_video_id': 'test_client_video_id',
         'duration': self.TEST_DURATION,
         'status': 'dummy',
         'encoded_videos': [self.TEST_ENCODED_VIDEO],
         'courses': [self.video.location.course_key] if associate_course_in_val else [],
     })
     self.val_video = get_video_info(self.TEST_EDX_VIDEO_ID)  # pylint: disable=attribute-defined-outside-init
    def setUp(self):
        super(TestChooseTranscripts, self).setUp()

        # Create test transcript in contentstore
        self.chosen_html5_id = 'test_html5_subs'
        self.sjson_subs = Transcript.convert(SRT_TRANSCRIPT_CONTENT, Transcript.SRT, Transcript.SJSON)
        self.save_subs_to_store(json.loads(self.sjson_subs), self.chosen_html5_id)

        # 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)]
        })
Example #18
0
    def setUp(self):
        super(TestChooseTranscripts, self).setUp()

        # Create test transcript in contentstore
        self.chosen_html5_id = 'test_html5_subs'
        self.sjson_subs = Transcript.convert(SRT_TRANSCRIPT_CONTENT, Transcript.SRT, Transcript.SJSON)
        self.save_subs_to_store(json.loads(self.sjson_subs), self.chosen_html5_id)

        # 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': [six.text_type(self.course.id)]
        })
    def setUp(self):
        super(TestRenameTranscripts, self).setUp()

        # Create test transcript in contentstore and update item's sub.
        self.item.sub = 'test_video_subs'
        self.sjson_subs = Transcript.convert(SRT_TRANSCRIPT_CONTENT, Transcript.SRT, Transcript.SJSON)
        self.save_subs_to_store(json.loads(self.sjson_subs), self.item.sub)
        modulestore().update_item(self.item, self.user.id)

        # 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)]
        })
Example #20
0
    def setUp(self):
        super().setUp()

        # Create test transcript in contentstore and update item's sub.
        self.item.sub = 'test_video_subs'
        self.sjson_subs = Transcript.convert(SRT_TRANSCRIPT_CONTENT, Transcript.SRT, Transcript.SJSON)
        self.save_subs_to_store(json.loads(self.sjson_subs), self.item.sub)
        modulestore().update_item(self.item, self.user.id)

        # Setup a VEDA produced video and persist `edx_video_id` in VAL.
        create_video({
            'edx_video_id': '123-456-789',
            'status': 'upload',
            'client_video_id': 'Test Video',
            'duration': 0,
            'encoded_videos': [],
            'courses': [str(self.course.id)]
        })
Example #21
0
 def test_create_video(self):
     """
     Tests the creation of a video
     """
     video_data = dict(encoded_videos=[constants.ENCODED_VIDEO_DICT_FISH_MOBILE], **constants.VIDEO_DICT_FISH)
     result = api.create_video(video_data)
     videos = Video.objects.all()
     self.assertEqual(len(videos), 1)
     self.assertEqual("super-soaker", result)
    def setUp(self):
        super(TestCourseMixin, self).setUp()

        with modulestore().default_store(ModuleStoreEnum.Type.split):
            self.course = SampleCourseFactory.create(
                block_info_tree=TEST_COURSE)
        # And upload the course static asssets:
        asset_key = StaticContent.compute_location(self.course.id,
                                                   'sample_handout.txt')
        content = StaticContent(asset_key, "Fake asset", "application/text",
                                "test".encode('utf8'))
        contentstore().save(content)

        asset_key = StaticContent.compute_location(self.course.id, 'edx.svg')
        content = StaticContent(
            asset_key, "Fake image", "image/svg+xml", """
            <svg viewBox="0 0 403 403" version="1.1"
                xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
            >
                <title>edX</title>
                <desc>
                    the edX logo is comprised of a red letter e, grey d, and blue uppercase X, all slightly overlapping
                    each other.  The d is slightly transparent.
                </desc>
                <g transform="translate(0,100)">
                    <path id="e-path" stroke-width="1" stroke="none" fill="rgb(185, 0, 88)" fill-rule="evenodd"
                        d="M32.1,127 H141.9 A71,71.5 0 1,0 137.3,143 H103 A42,42 0 0,1 32.1,127 M32.1,102.5 H112 A42,42
                        0 0,0 32.1,102.5"/>
                    <path id="x-path" stroke-width="1" stroke="none" fill="rgb(0, 162, 228)" fill-rule="evenodd"
                        d="M228,1 H302 V31 H286 L315,67 L344,31 H328 V1 H401.5 V31 H385 L335.2,92.4 L387.5,156.8 H401.5
                        V187 H328 V156.8 H346.5 L315,117.4 L283,156.8 H302.0 V187 H228.5 V156.8 H243 L294.3,92.4
                        L244,30.5 H228 V1"/>
                    <path id="d-path" stroke-width="1" stroke="none" fill="rgb(55, 55, 60)" fill-rule="evenodd"
                        opacity="0.55" d="M198.5,1 L248,1 V156.5 H269.8 V187 H217.5 V174 A71.7,71.7 0 1,1 218,55.5
                        V30.5 H198.5 V1 M218,114 A41,41.5 0 1,1 136.1,114 A40.5,40.5 0 1,1 218,114"/>
                </g>
            </svg>
        """.strip().encode('utf8'))
        contentstore().save(content)
        # And the video data + transcript must also be stored in edx-val for the video export to work:
        edxval_api.create_video(VIDEO_B_VAL_DATA)
        edxval_api.create_video_transcript(**VIDEO_B_SRT_TRANSCRIPT_DATA)
Example #23
0
    def test_create_video(self):
        """
        Tests the creation of a video
        """

        video_data = dict(
            encoded_videos=[constants.ENCODED_VIDEO_DICT_FISH_MOBILE],
            **constants.VIDEO_DICT_FISH)
        result = api.create_video(video_data)
        videos = Video.objects.all()
        self.assertEqual(len(videos), 1)
        self.assertEqual("super-soaker", result)
    def setUp(self):
        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)]
        })

        # Add clean up handler
        self.addCleanup(self.clean_temporary_transcripts)
Example #25
0
    def create(self, request, *args, **kwargs):  # pylint: disable=unused-argument
        """
        Creates an HLS video object for a course and returns a serialized version of the
        newly-created video object
        """
        course_id = kwargs.get("course_id")
        file_name = request.data.get("filename")
        hls_url = request.data.get("hls_url")
        error = None
        if not file_name:
            error = u"Request does not contain file name"
        if not hls_url:
            error = u"Request does not contain HLS URL"
        elif not is_valid_hls_url(hls_url):
            error = u"Request does not contain a valid HLS URL"
        if error:
            return Response({ERROR_KEY: error},
                            status=status.HTTP_400_BAD_REQUEST)

        edx_video_id = unicode(uuid4())
        try:
            video_id = create_video({
                "edx_video_id":
                edx_video_id,
                "status":
                "file_complete",
                "client_video_id":
                file_name,
                "duration":
                0,
                "encoded_videos": [{
                    "profile": "hls",
                    "url": hls_url,
                    "bitrate": 0,
                    "file_size": 0
                }],
                "courses": [course_id]
            })
        except ValCannotCreateError as exc:
            return Response(
                {
                    ERROR_KEY:
                    u"Could not create video (exception: {})".format(str(exc))
                },
                status=status.HTTP_400_BAD_REQUEST)
        serialized_video = next(get_videos_for_ids([video_id]))
        return Response(json_serialize_video(serialized_video))
Example #26
0
    def setUp(self):
        super(TestVideoOutline, self).setUp()
        self.user = UserFactory.create()
        self.course = CourseFactory.create(mobile_available=True)
        section = ItemFactory.create(
            parent_location=self.course.location,
            category="chapter",
            display_name=u"test factory section omega \u03a9",
        )
        self.sub_section = ItemFactory.create(
            parent_location=section.location,
            category="sequential",
            display_name=u"test subsection omega \u03a9",
        )

        self.unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={'graded': True, 'format': 'Homework'},
            display_name=u"test unit omega \u03a9",
        )
        self.other_unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={'graded': True, 'format': 'Homework'},
            display_name=u"test unit omega 2 \u03a9",
        )

        self.edx_video_id = 'testing-123'

        self.video_url = 'http://val.edx.org/val/video.mp4'
        self.html5_video_url = 'http://video.edx.org/html5/video.mp4'

        api.create_profile({
            'profile_name': 'youtube',
            'extension': 'mp4',
            'width': 1280,
            'height': 720
        })
        api.create_profile({
            'profile_name': 'mobile_low',
            'extension': 'mp4',
            'width': 640,
            'height': 480
        })

        # create the video in VAL
        api.create_video({
            'edx_video_id': self.edx_video_id,
            'client_video_id': u"test video omega \u03a9",
            'duration': 12,
            'courses': [unicode(self.course.id)],
            'encoded_videos': [
                {
                    'profile': 'youtube',
                    'url': 'xyz123',
                    'file_size': 0,
                    'bitrate': 1500
                },
                {
                    'profile': 'mobile_low',
                    'url': self.video_url,
                    'file_size': 12345,
                    'bitrate': 250
                }
            ]})

        subid = uuid4().hex
        self.video = ItemFactory.create(
            parent_location=self.unit.location,
            category="video",
            edx_video_id=self.edx_video_id,
            display_name=u"test video omega \u03a9",
            sub=subid
        )

        transcripts_utils.save_subs_to_store({
            'start': [100, 200, 240, 390, 1000],
            'end': [200, 240, 380, 1000, 1500],
            'text': [
                'subs #1',
                'subs #2',
                'subs #3',
                'subs #4',
                'subs #5'
            ]},
            subid,
            self.course)

        self.client.login(username=self.user.username, password='******')
Example #27
0
    def setUp(self):
        super(TestVideoAPITestCase, self).setUp()
        self.section = ItemFactory.create(
            parent=self.course,
            category="chapter",
            display_name=u"test factory section omega \u03a9",
        )
        self.sub_section = ItemFactory.create(
            parent=self.section,
            category="sequential",
            display_name=u"test subsection omega \u03a9",
        )

        self.unit = ItemFactory.create(
            parent=self.sub_section,
            category="vertical",
            metadata={
                'graded': True,
                'format': 'Homework'
            },
            display_name=u"test unit omega \u03a9",
        )
        self.other_unit = ItemFactory.create(
            parent=self.sub_section,
            category="vertical",
            metadata={
                'graded': True,
                'format': 'Homework'
            },
            display_name=u"test unit omega 2 \u03a9",
        )
        self.nameless_unit = ItemFactory.create(
            parent=self.sub_section,
            category="vertical",
            metadata={
                'graded': True,
                'format': 'Homework'
            },
            display_name=None,
        )

        self.edx_video_id = 'testing-123'
        self.video_url = 'http://val.edx.org/val/video.mp4'
        self.video_url_high = 'http://val.edx.org/val/video_high.mp4'
        self.youtube_url = 'http://val.edx.org/val/youtube.mp4'
        self.html5_video_url = 'http://video.edx.org/html5/video.mp4'

        api.create_profile('youtube')
        api.create_profile('mobile_high')
        api.create_profile('mobile_low')

        # create the video in VAL
        api.create_video({
            'edx_video_id':
            self.edx_video_id,
            'status':
            'test',
            'client_video_id':
            u"test video omega \u03a9",
            'duration':
            12,
            'courses': [unicode(self.course.id)],
            'encoded_videos': [
                {
                    'profile': 'youtube',
                    'url': 'xyz123',
                    'file_size': 0,
                    'bitrate': 1500
                },
                {
                    'profile': 'mobile_low',
                    'url': self.video_url,
                    'file_size': 12345,
                    'bitrate': 250
                },
                {
                    'profile': 'mobile_high',
                    'url': self.video_url_high,
                    'file_size': 99999,
                    'bitrate': 250
                },
            ]
        })

        # Set requested profiles
        MobileApiConfig(video_profiles="mobile_low,mobile_high,youtube").save()
    def setUp(self):
        """ Common setup. """
        super(TestMigrateTranscripts, self).setUp()
        self.store = modulestore()
        self.course = CourseFactory.create()
        self.course_2 = CourseFactory.create()

        video = {
            'edx_video_id': 'test_edx_video_id',
            'client_video_id': 'test1.mp4',
            'duration': 42.0,
            'status': 'upload',
            'courses': [unicode(self.course.id)],
            'encoded_videos': [],
            'created': datetime.now(pytz.utc)
        }
        api.create_video(video)

        video_sample_xml = '''
            <video display_name="Test Video"
                   edx_video_id="test_edx_video_id"
                   youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
                   show_captions="false"
                   download_track="false"
                   start_time="00:00:01"
                   download_video="false"
                   end_time="00:01:00">
              <source src="http://www.example.com/source.mp4"/>
              <track src="http://www.example.com/track"/>
              <handout src="http://www.example.com/handout"/>
              <transcript language="ge" src="subs_grmtran1.srt" />
              <transcript language="hr" src="subs_croatian1.srt" />
            </video>
        '''

        video_sample_xml_2 = '''
            <video display_name="Test Video 2"
                   edx_video_id="test_edx_video_id_2"
                   youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
                   show_captions="false"
                   download_track="false"
                   start_time="00:00:01"
                   download_video="false"
                   end_time="00:01:00">
              <source src="http://www.example.com/source.mp4"/>
              <track src="http://www.example.com/track"/>
              <handout src="http://www.example.com/handout"/>
              <transcript language="ge" src="not_found.srt" />
            </video>
        '''
        self.video_descriptor = ItemFactory.create(
            parent_location=self.course.location, category='video',
            data={'data': video_sample_xml}
        )
        self.video_descriptor_2 = ItemFactory.create(
            parent_location=self.course_2.location, category='video',
            data={'data': video_sample_xml_2}
        )

        save_to_store(SRT_FILEDATA, 'subs_grmtran1.srt', 'text/srt', self.video_descriptor.location)
        save_to_store(CRO_SRT_FILEDATA, 'subs_croatian1.srt', 'text/srt', self.video_descriptor.location)
 def setUp(self):
     super(VideoUploadTestMixin, self).setUp()
     self.url = self.get_url_for_course_key(self.course.id)
     self.test_token = "test_token"
     self.course.video_upload_pipeline = {
         "course_video_upload_token": self.test_token,
     }
     self.save_course()
     self.profiles = [
         {
             "profile_name": "profile1",
             "extension": "mp4",
             "width": 640,
             "height": 480,
         },
         {
             "profile_name": "profile2",
             "extension": "mp4",
             "width": 1920,
             "height": 1080,
         },
     ]
     self.previous_uploads = [
         {
             "edx_video_id": "test1",
             "client_video_id": "test1.mp4",
             "duration": 42.0,
             "status": "upload",
             "encoded_videos": [],
         },
         {
             "edx_video_id": "test2",
             "client_video_id": "test2.mp4",
             "duration": 128.0,
             "status": "file_complete",
             "encoded_videos": [
                 {
                     "profile": "profile1",
                     "url": "http://example.com/profile1/test2.mp4",
                     "file_size": 1600,
                     "bitrate": 100,
                 },
                 {
                     "profile": "profile2",
                     "url": "http://example.com/profile2/test2.mov",
                     "file_size": 16000,
                     "bitrate": 1000,
                 },
             ],
         },
         {
             "edx_video_id": "non-ascii",
             "client_video_id": u"nón-ascii-näme.mp4",
             "duration": 256.0,
             "status": "transcode_active",
             "encoded_videos": [
                 {
                     "profile": "profile1",
                     "url": u"http://example.com/profile1/nón-ascii-näme.mp4",
                     "file_size": 3200,
                     "bitrate": 100,
                 },
             ]
         },
     ]
     for profile in self.profiles:
         create_profile(profile)
     for video in self.previous_uploads:
         create_video(video)
         modulestore().save_asset_metadata(
             AssetMetadata(
                 self.course.id.make_asset_key(VIDEO_ASSET_TYPE, video["edx_video_id"])
             ),
             self.user.id
         )
Example #30
0
    def test_mobile_api_video_profiles(self):
        """
        Tests VideoSummaryList with different MobileApiConfig video_profiles
        """
        self.login_and_enroll()
        edx_video_id = "testing_mobile_high"
        api.create_video({
            'edx_video_id':
            edx_video_id,
            'status':
            'test',
            'client_video_id':
            u"test video omega \u03a9",
            'duration':
            12,
            'courses': [unicode(self.course.id)],
            'encoded_videos': [
                {
                    'profile': 'youtube',
                    'url': self.youtube_url,
                    'file_size': 2222,
                    'bitrate': 4444
                },
                {
                    'profile': 'mobile_high',
                    'url': self.video_url_high,
                    'file_size': 111,
                    'bitrate': 333
                },
            ]
        })
        ItemFactory.create(
            parent=self.other_unit,
            category="video",
            display_name=u"testing mobile high video",
            edx_video_id=edx_video_id,
        )

        expected_output = {
            'all_sources': [],
            'category': u'video',
            'video_thumbnail_url': None,
            'language': u'en',
            'name': u'testing mobile high video',
            'video_url': self.video_url_high,
            'duration': 12.0,
            'transcripts': {
                'en':
                'http://testserver/api/mobile/v0.5/video_outlines/transcripts/{}/testing_mobile_high_video/en'
                .format(self.course.id)  # pylint: disable=line-too-long
            },
            'only_on_web': False,
            'encoded_videos': {
                u'mobile_high': {
                    'url': self.video_url_high,
                    'file_size': 111
                },
                u'youtube': {
                    'url': self.youtube_url,
                    'file_size': 2222
                }
            },
            'size': 111
        }

        # The transcript was not entered, so it should not be found!
        # This is the default behaviour at courses.edX.org, based on `FALLBACK_TO_ENGLISH_TRANSCRIPTS`
        transcripts_response = self.client.get(
            expected_output['transcripts']['en'])
        self.assertEqual(404, transcripts_response.status_code)

        with patch.dict(settings.FEATURES,
                        FALLBACK_TO_ENGLISH_TRANSCRIPTS=False):
            # Other platform installations may override this setting
            # This ensures that the server don't return empty English transcripts when there's none!
            self.assertFalse(
                self.api_response().data[0]['summary'].get('transcripts'))

        # Testing when video_profiles='mobile_low,mobile_high,youtube'
        course_outline = self.api_response().data
        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)

        # Testing when there is no mobile_low, and that mobile_high doesn't show
        MobileApiConfig(video_profiles="mobile_low,youtube").save()

        course_outline = self.api_response().data

        expected_output['encoded_videos'].pop('mobile_high')
        expected_output['video_url'] = self.youtube_url
        expected_output['size'] = 2222

        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)

        # Testing where youtube is the default video over mobile_high
        MobileApiConfig(video_profiles="youtube,mobile_high").save()

        course_outline = self.api_response().data

        expected_output['encoded_videos']['mobile_high'] = {
            'url': self.video_url_high,
            'file_size': 111
        }

        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)
Example #31
0
    def setUp(self):
        super(TestVideoAPITestCase, self).setUp()
        self.section = ItemFactory.create(
            parent_location=self.course.location,
            category="chapter",
            display_name=u"test factory section omega \u03a9",
        )
        self.sub_section = ItemFactory.create(
            parent_location=self.section.location,
            category="sequential",
            display_name=u"test subsection omega \u03a9",
        )

        self.unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={
                'graded': True,
                'format': 'Homework'
            },
            display_name=u"test unit omega \u03a9",
        )
        self.other_unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={
                'graded': True,
                'format': 'Homework'
            },
            display_name=u"test unit omega 2 \u03a9",
        )
        self.nameless_unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={
                'graded': True,
                'format': 'Homework'
            },
            display_name=None,
        )
        self.split_unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            display_name=u"split test vertical\u03a9",
        )

        self.split_test = ItemFactory.create(
            parent_location=self.split_unit.location,
            category="split_test",
            display_name=u"split test unit")

        self.edx_video_id = 'testing-123'

        self.video_url = 'http://val.edx.org/val/video.mp4'
        self.html5_video_url = 'http://video.edx.org/html5/video.mp4'

        api.create_profile({
            'profile_name': 'youtube',
            'extension': 'mp4',
            'width': 1280,
            'height': 720
        })
        api.create_profile({
            'profile_name': 'mobile_low',
            'extension': 'mp4',
            'width': 640,
            'height': 480
        })

        # create the video in VAL
        api.create_video({
            'edx_video_id':
            self.edx_video_id,
            'status':
            'test',
            'client_video_id':
            u"test video omega \u03a9",
            'duration':
            12,
            'courses': [unicode(self.course.id)],
            'encoded_videos': [{
                'profile': 'youtube',
                'url': 'xyz123',
                'file_size': 0,
                'bitrate': 1500
            }, {
                'profile': 'mobile_low',
                'url': self.video_url,
                'file_size': 12345,
                'bitrate': 250
            }]
        })
    def test_get_html_with_existing_edx_video_id(self):
        # create test profiles and their encodings
        encoded_videos = []
        for profile, extension in [("desktop_webm", "webm"),
                                   ("desktop_mp4", "mp4")]:
            create_profile(profile)
            encoded_videos.append(
                dict(
                    url=u"http://fake-video.edx.org/thundercats.{}".format(
                        extension),
                    file_size=9000,
                    bitrate=42,
                    profile=profile,
                ))

        result = create_video(
            dict(client_video_id="Thunder Cats",
                 duration=111,
                 edx_video_id="thundercats",
                 status='test',
                 encoded_videos=encoded_videos))
        self.assertEqual(result, "thundercats")

        SOURCE_XML = """
            <video show_captions="true"
            display_name="A Name"
            sub="a_sub_file.srt.sjson" source="{source}"
            download_video="{download_video}"
            start_time="01:00:03" end_time="01:00:10"
            edx_video_id="{edx_video_id}"
            >
                {sources}
            </video>
        """

        data = {
            'download_video': 'true',
            'source': 'example_source.mp4',
            'sources': """
                <source src="example.mp4"/>
                <source src="example.webm"/>
            """,
            'edx_video_id': "thundercats",
            'result': {
                'download_video_link':
                u'http://fake-video.edx.org/thundercats.mp4',
                # make sure the urls for the various encodings are included as part of the alternative sources.
                'sources':
                json.dumps([u'example.mp4', u'example.webm'] +
                           [video['url'] for video in encoded_videos]),
            }
        }

        # Video found for edx_video_id
        initial_context = {
            'branding_info':
            None,
            'cdn_eval':
            False,
            'cdn_exp_group':
            None,
            'data_dir':
            getattr(self, 'data_dir', None),
            'show_captions':
            'true',
            'handout':
            None,
            'display_name':
            u'A Name',
            'download_video_link':
            None,
            'end':
            3610.0,
            'id':
            None,
            'sources':
            '[]',
            'speed':
            'null',
            'general_speed':
            1.0,
            'start':
            3603.0,
            'saved_video_position':
            0.0,
            'sub':
            u'a_sub_file.srt.sjson',
            'track':
            None,
            'youtube_streams':
            '1.00:3_yD_cEKoCk',
            'autoplay':
            settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
            'yt_test_timeout':
            1500,
            'yt_api_url':
            'www.youtube.com/iframe_api',
            'yt_test_url':
            'gdata.youtube.com/feeds/api/videos/',
            'transcript_download_format':
            'srt',
            'transcript_download_formats_list': [{
                'display_name': 'SubRip (.srt) file',
                'value': 'srt'
            }, {
                'display_name': 'Text (.txt) file',
                'value': 'txt'
            }],
            'transcript_language':
            u'en',
            'transcript_languages':
            '{"en": "English"}',
        }

        DATA = SOURCE_XML.format(download_video=data['download_video'],
                                 source=data['source'],
                                 sources=data['sources'],
                                 edx_video_id=data['edx_video_id'])
        self.initialize_module(data=DATA)
        context = self.item_descriptor.render(STUDENT_VIEW).content

        expected_context = dict(initial_context)
        expected_context.update({
            'transcript_translation_url':
            self.item_descriptor.xmodule_runtime.handler_url(
                self.item_descriptor, 'transcript',
                'translation').rstrip('/?'),
            'transcript_available_translations_url':
            self.item_descriptor.xmodule_runtime.handler_url(
                self.item_descriptor, 'transcript',
                'available_translations').rstrip('/?'),
            'ajax_url':
            self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
            'id':
            self.item_descriptor.location.html_id(),
        })
        expected_context.update(data['result'])

        self.assertEqual(
            context,
            self.item_descriptor.xmodule_runtime.render_template(
                'video.html', expected_context))
Example #33
0
 def setUp(self):
     super(VideoUploadTestMixin, self).setUp()
     self.url = self.get_url_for_course_key(self.course.id)
     self.test_token = "test_token"
     self.course.video_upload_pipeline = {
         "course_video_upload_token": self.test_token,
     }
     self.save_course()
     self.profiles = ["profile1", "profile2"]
     self.previous_uploads = [
         {
             "edx_video_id": "test1",
             "client_video_id": "test1.mp4",
             "duration": 42.0,
             "status": "upload",
             "encoded_videos": [],
         },
         {
             "edx_video_id": "test2",
             "client_video_id": "test2.mp4",
             "duration": 128.0,
             "status": "file_complete",
             "encoded_videos": [
                 {
                     "profile": "profile1",
                     "url": "http://example.com/profile1/test2.mp4",
                     "file_size": 1600,
                     "bitrate": 100,
                 },
                 {
                     "profile": "profile2",
                     "url": "http://example.com/profile2/test2.mov",
                     "file_size": 16000,
                     "bitrate": 1000,
                 },
             ],
         },
         {
             "edx_video_id": "non-ascii",
             "client_video_id": u"nón-ascii-näme.mp4",
             "duration": 256.0,
             "status": "transcode_active",
             "encoded_videos": [
                 {
                     "profile": "profile1",
                     "url": u"http://example.com/profile1/nón-ascii-näme.mp4",
                     "file_size": 3200,
                     "bitrate": 100,
                 },
             ]
         },
     ]
     # Ensure every status string is tested
     self.previous_uploads += [
         {
             "edx_video_id": "status_test_{}".format(status),
             "client_video_id": "status_test.mp4",
             "duration": 3.14,
             "status": status,
             "encoded_videos": [],
         }
         for status in (
             StatusDisplayStrings._STATUS_MAP.keys() +  # pylint:disable=protected-access
             ["non_existent_status"]
         )
     ]
     for profile in self.profiles:
         create_profile(profile)
     for video in self.previous_uploads:
         create_video(video)
         modulestore().save_asset_metadata(
             AssetMetadata(
                 self.course.id.make_asset_key(VIDEO_ASSET_TYPE, video["edx_video_id"])
             ),
             self.user.id
         )
Example #34
0
    def setUp(self):
        super(TestVideoAPITestCase, self).setUp()
        self.section = ItemFactory.create(
            parent=self.course,
            category="chapter",
            display_name=u"test factory section omega \u03a9",
        )
        self.sub_section = ItemFactory.create(
            parent=self.section,
            category="sequential",
            display_name=u"test subsection omega \u03a9",
        )

        self.unit = ItemFactory.create(
            parent=self.sub_section,
            category="vertical",
            metadata={'graded': True, 'format': 'Homework'},
            display_name=u"test unit omega \u03a9",
        )
        self.other_unit = ItemFactory.create(
            parent=self.sub_section,
            category="vertical",
            metadata={'graded': True, 'format': 'Homework'},
            display_name=u"test unit omega 2 \u03a9",
        )
        self.nameless_unit = ItemFactory.create(
            parent=self.sub_section,
            category="vertical",
            metadata={'graded': True, 'format': 'Homework'},
            display_name=None,
        )

        self.edx_video_id = 'testing-123'
        self.video_url = 'http://val.edx.org/val/video.mp4'
        self.video_url_high = 'http://val.edx.org/val/video_high.mp4'
        self.youtube_url = 'http://val.edx.org/val/youtube.mp4'
        self.html5_video_url = 'http://video.edx.org/html5/video.mp4'

        api.create_profile('youtube')
        api.create_profile('mobile_high')
        api.create_profile('mobile_low')

        # create the video in VAL
        api.create_video({
            'edx_video_id': self.edx_video_id,
            'status': 'test',
            'client_video_id': u"test video omega \u03a9",
            'duration': 12,
            'courses': [unicode(self.course.id)],
            'encoded_videos': [
                {
                    'profile': 'youtube',
                    'url': 'xyz123',
                    'file_size': 0,
                    'bitrate': 1500
                },
                {
                    'profile': 'mobile_low',
                    'url': self.video_url,
                    'file_size': 12345,
                    'bitrate': 250
                },
                {
                    'profile': 'mobile_high',
                    'url': self.video_url_high,
                    'file_size': 99999,
                    'bitrate': 250
                },

            ]})

        # Set requested profiles
        MobileApiConfig(video_profiles="mobile_low,mobile_high,youtube").save()
Example #35
0
def videos_post(course, request):
    """
    Input (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "content_type": "video/mp4"
        }]
    }

    Returns (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "upload_url": "http://example.com/put_video"
        }]
    }

    The returned array corresponds exactly to the input array.
    """
    error = None
    data = request.json
    if 'files' not in data:
        error = "Request object is not JSON or does not contain 'files'"
    elif any(
            'file_name' not in file or 'content_type' not in file
            for file in data['files']
    ):
        error = "Request 'files' entry does not contain 'file_name' and 'content_type'"
    elif any(
            file['content_type'] not in VIDEO_SUPPORTED_FILE_FORMATS.values()
            for file in data['files']
    ):
        error = "Request 'files' entry contain unsupported content_type"

    if error:
        return JsonResponse({'error': error}, status=400)

    bucket = storage_service_bucket()
    req_files = data['files']
    resp_files = []

    for req_file in req_files:
        file_name = req_file['file_name']

        try:
            file_name.encode('ascii')
        except UnicodeEncodeError:
            error_msg = 'The file name for %s must contain only ASCII characters.' % file_name
            return JsonResponse({'error': error_msg}, status=400)

        edx_video_id = unicode(uuid4())
        key = storage_service_key(bucket, file_name=edx_video_id)

        metadata_list = [
            ('client_video_id', file_name),
            ('course_key', unicode(course.id)),
        ]

        # Only include `course_video_upload_token` if its set, as it won't be required if video uploads
        # are enabled by default.
        course_video_upload_token = course.video_upload_pipeline.get('course_video_upload_token')
        if course_video_upload_token:
            metadata_list.append(('course_video_upload_token', course_video_upload_token))

        is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
        if is_video_transcript_enabled:
            transcript_preferences = get_transcript_preferences(unicode(course.id))
            if transcript_preferences is not None:
                metadata_list.append(('transcript_preferences', json.dumps(transcript_preferences)))

        for metadata_name, value in metadata_list:
            key.set_metadata(metadata_name, value)
        upload_url = key.generate_url(
            KEY_EXPIRATION_IN_SECONDS,
            'PUT',
            headers={'Content-Type': req_file['content_type']}
        )

        # persist edx_video_id in VAL
        create_video({
            'edx_video_id': edx_video_id,
            'status': 'upload',
            'client_video_id': file_name,
            'duration': 0,
            'encoded_videos': [],
            'courses': [unicode(course.id)]
        })

        resp_files.append({'file_name': file_name, 'upload_url': upload_url, 'edx_video_id': edx_video_id})

    return JsonResponse({'files': resp_files}, status=200)
Example #36
0
    def setUp(self):
        super(TestVideoOutline, self).setUp()
        self.user = UserFactory.create()
        self.course = CourseFactory.create(mobile_available=True)
        self.section = ItemFactory.create(
            parent_location=self.course.location,
            category="chapter",
            display_name=u"test factory section omega \u03a9",
        )
        self.sub_section = ItemFactory.create(
            parent_location=self.section.location,
            category="sequential",
            display_name=u"test subsection omega \u03a9",
        )

        self.unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={'graded': True, 'format': 'Homework'},
            display_name=u"test unit omega \u03a9",
        )
        self.other_unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={'graded': True, 'format': 'Homework'},
            display_name=u"test unit omega 2 \u03a9",
        )
        self.nameless_unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={'graded': True, 'format': 'Homework'},
            display_name=None,
        )

        self.edx_video_id = 'testing-123'

        self.video_url = 'http://val.edx.org/val/video.mp4'
        self.html5_video_url = 'http://video.edx.org/html5/video.mp4'

        api.create_profile({
            'profile_name': 'youtube',
            'extension': 'mp4',
            'width': 1280,
            'height': 720
        })
        api.create_profile({
            'profile_name': 'mobile_low',
            'extension': 'mp4',
            'width': 640,
            'height': 480
        })

        # create the video in VAL
        api.create_video({
            'edx_video_id': self.edx_video_id,
            'client_video_id': u"test video omega \u03a9",
            'duration': 12,
            'courses': [unicode(self.course.id)],
            'encoded_videos': [
                {
                    'profile': 'youtube',
                    'url': 'xyz123',
                    'file_size': 0,
                    'bitrate': 1500
                },
                {
                    'profile': 'mobile_low',
                    'url': self.video_url,
                    'file_size': 12345,
                    'bitrate': 250
                }
            ]})

        self.client.login(username=self.user.username, password='******')
Example #37
0
def videos_post(course, request):
    """
    Input (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "content_type": "video/mp4"
        }]
    }

    Returns (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "upload_url": "http://example.com/put_video"
        }]
    }

    The returned array corresponds exactly to the input array.
    """
    error = None
    if "files" not in request.json:
        error = "Request object is not JSON or does not contain 'files'"
    elif any(
            "file_name" not in file or "content_type" not in file
            for file in request.json["files"]
    ):
        error = "Request 'files' entry does not contain 'file_name' and 'content_type'"

    if error:
        return JsonResponse({"error": error}, status=400)

    bucket = storage_service_bucket()
    course_video_upload_token = course.video_upload_pipeline["course_video_upload_token"]
    req_files = request.json["files"]
    resp_files = []

    for req_file in req_files:
        file_name = req_file["file_name"]

        edx_video_id = unicode(uuid4())
        key = storage_service_key(bucket, file_name=edx_video_id)
        for metadata_name, value in [
            ("course_video_upload_token", course_video_upload_token),
            ("client_video_id", file_name),
            ("course_key", unicode(course.id)),
            # Edraak: This is needed in the edraak video processor for youtube deployments
            ("course_title", urllib.quote_plus(course.display_name.encode("utf-8"))),
        ]:
            key.set_metadata(metadata_name, value)
        upload_url = key.generate_url(
            KEY_EXPIRATION_IN_SECONDS,
            "PUT",
            headers={"Content-Type": req_file["content_type"]}
        )

        # persist edx_video_id in VAL
        create_video({
            "edx_video_id": edx_video_id,
            "status": "upload",
            "client_video_id": file_name,
            "duration": 0,
            "encoded_videos": [],
            "courses": [course.id]
        })

        resp_files.append({"file_name": file_name, "upload_url": upload_url})

    return JsonResponse({"files": resp_files}, status=200)
Example #38
0
    def setUp(self):
        super(VideoUploadTestMixin, self).setUp()
        self.url = self.get_url_for_course_key(self.course.id)
        self.test_token = "test_token"
        self.course.video_upload_pipeline = {
            "course_video_upload_token": self.test_token,
        }
        self.save_course()

        # create another course for videos belonging to multiple courses
        self.course2 = CourseFactory.create()
        self.course2.video_upload_pipeline = {
            "course_video_upload_token": self.test_token,
        }
        self.course2.save()
        self.store.update_item(self.course2, self.user.id)

        # course ids for videos
        course_ids = [unicode(self.course.id), unicode(self.course2.id)]

        self.profiles = ["profile1", "profile2"]
        self.previous_uploads = [
            {
                "edx_video_id": "test1",
                "client_video_id": "test1.mp4",
                "duration": 42.0,
                "status": "upload",
                "courses": course_ids,
                "encoded_videos": [],
            },
            {
                "edx_video_id": "test2",
                "client_video_id": "test2.mp4",
                "duration": 128.0,
                "status": "file_complete",
                "courses": course_ids,
                "encoded_videos": [
                    {
                        "profile": "profile1",
                        "url": "http://example.com/profile1/test2.mp4",
                        "file_size": 1600,
                        "bitrate": 100,
                    },
                    {
                        "profile": "profile2",
                        "url": "http://example.com/profile2/test2.mov",
                        "file_size": 16000,
                        "bitrate": 1000,
                    },
                ],
            },
            {
                "edx_video_id": "non-ascii",
                "client_video_id": u"nón-ascii-näme.mp4",
                "duration": 256.0,
                "status": "transcode_active",
                "courses": course_ids,
                "encoded_videos": [
                    {
                        "profile": "profile1",
                        "url": u"http://example.com/profile1/nón-ascii-näme.mp4",
                        "file_size": 3200,
                        "bitrate": 100,
                    },
                ]
            },
        ]
        # Ensure every status string is tested
        self.previous_uploads += [
            {
                "edx_video_id": "status_test_{}".format(status),
                "client_video_id": "status_test.mp4",
                "duration": 3.14,
                "status": status,
                "courses": course_ids,
                "encoded_videos": [],
            }
            for status in (
                StatusDisplayStrings._STATUS_MAP.keys() +  # pylint:disable=protected-access
                ["non_existent_status"]
            )
        ]
        for profile in self.profiles:
            create_profile(profile)
        for video in self.previous_uploads:
            create_video(video)
Example #39
0
    def test_mobile_api_video_profiles(self):
        """
        Tests VideoSummaryList with different MobileApiConfig video_profiles
        """
        self.login_and_enroll()
        edx_video_id = "testing_mobile_high"
        api.create_video({
            'edx_video_id': edx_video_id,
            'status': 'test',
            'client_video_id': u"test video omega \u03a9",
            'duration': 12,
            'courses': [unicode(self.course.id)],
            'encoded_videos': [
                {
                    'profile': 'youtube',
                    'url': self.youtube_url,
                    'file_size': 2222,
                    'bitrate': 4444
                },
                {
                    'profile': 'mobile_high',
                    'url': self.video_url_high,
                    'file_size': 111,
                    'bitrate': 333
                },

            ]})
        ItemFactory.create(
            parent=self.other_unit,
            category="video",
            display_name=u"testing mobile high video",
            edx_video_id=edx_video_id,
        )

        expected_output = {
            'all_sources': [],
            'category': u'video',
            'video_thumbnail_url': None,
            'language': u'en',
            'name': u'testing mobile high video',
            'video_url': self.video_url_high,
            'duration': 12.0,
            'transcripts': {
                'en': 'http://testserver/api/mobile/v0.5/video_outlines/transcripts/{}/testing_mobile_high_video/en'.format(self.course.id)  # pylint: disable=line-too-long
            },
            'only_on_web': False,
            'encoded_videos': {
                u'mobile_high': {
                    'url': self.video_url_high,
                    'file_size': 111
                },
                u'youtube': {
                    'url': self.youtube_url,
                    'file_size': 2222
                }
            },
            'size': 111
        }

        # The transcript was not entered, so it should not be found!
        # This is the default behaviour at courses.edX.org, based on `FALLBACK_TO_ENGLISH_TRANSCRIPTS`
        transcripts_response = self.client.get(expected_output['transcripts']['en'])
        self.assertEqual(404, transcripts_response.status_code)

        with patch.dict(settings.FEATURES, FALLBACK_TO_ENGLISH_TRANSCRIPTS=False):
            # Other platform installations may override this setting
            # This ensures that the server don't return empty English transcripts when there's none!
            self.assertFalse(self.api_response().data[0]['summary'].get('transcripts'))

        # Testing when video_profiles='mobile_low,mobile_high,youtube'
        course_outline = self.api_response().data
        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)

        # Testing when there is no mobile_low, and that mobile_high doesn't show
        MobileApiConfig(video_profiles="mobile_low,youtube").save()

        course_outline = self.api_response().data

        expected_output['encoded_videos'].pop('mobile_high')
        expected_output['video_url'] = self.youtube_url
        expected_output['size'] = 2222

        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)

        # Testing where youtube is the default video over mobile_high
        MobileApiConfig(video_profiles="youtube,mobile_high").save()

        course_outline = self.api_response().data

        expected_output['encoded_videos']['mobile_high'] = {
            'url': self.video_url_high,
            'file_size': 111
        }

        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)
    def test_get_html_with_existing_edx_video_id(self):
        # create test profiles and their encodings
        encoded_videos = []
        for profile, extension in [("desktop_webm", "webm"), ("desktop_mp4", "mp4")]:
            create_profile(profile)
            encoded_videos.append(
                dict(
                    url=u"http://fake-video.edx.org/thundercats.{}".format(extension),
                    file_size=9000,
                    bitrate=42,
                    profile=profile,
                )
            )

        result = create_video(
            dict(
                client_video_id="Thunder Cats",
                duration=111,
                edx_video_id="thundercats",
                status='test',
                encoded_videos=encoded_videos
            )
        )
        self.assertEqual(result, "thundercats")

        SOURCE_XML = """
            <video show_captions="true"
            display_name="A Name"
            sub="a_sub_file.srt.sjson" source="{source}"
            download_video="{download_video}"
            start_time="01:00:03" end_time="01:00:10"
            edx_video_id="{edx_video_id}"
            >
                {sources}
            </video>
        """

        data = {
            'download_video': 'true',
            'source': 'example_source.mp4',
            'sources': """
                <source src="example.mp4"/>
                <source src="example.webm"/>
            """,
            'edx_video_id': "thundercats",
            'result': {
                'download_video_link': u'http://fake-video.edx.org/thundercats.mp4',
                # make sure the urls for the various encodings are included as part of the alternative sources.
                'sources': [u'example.mp4', u'example.webm'] +
                           [video['url'] for video in encoded_videos],
            }
        }

        # Video found for edx_video_id
        metadata = self.default_metadata_dict
        metadata['sources'] = ""
        initial_context = {
            'branding_info': None,
            'license': None,
            'bumper_metadata': 'null',
            'cdn_eval': False,
            'cdn_exp_group': None,
            'display_name': u'A Name',
            'download_video_link': u'example.mp4',
            'handout': None,
            'id': self.item_descriptor.location.html_id(),
            'track': None,
            'transcript_download_format': 'srt',
            'transcript_download_formats_list': [
                {'display_name': 'SubRip (.srt) file', 'value': 'srt'},
                {'display_name': 'Text (.txt) file', 'value': 'txt'}
            ],
            'poster': 'null',
            'metadata': metadata,
        }

        DATA = SOURCE_XML.format(
            download_video=data['download_video'],
            source=data['source'],
            sources=data['sources'],
            edx_video_id=data['edx_video_id']
        )
        self.initialize_module(data=DATA)
        context = self.item_descriptor.render(STUDENT_VIEW).content

        expected_context = dict(initial_context)
        expected_context['metadata'].update({
            'transcriptTranslationUrl': self.item_descriptor.xmodule_runtime.handler_url(
                self.item_descriptor, 'transcript', 'translation/__lang__'
            ).rstrip('/?'),
            'transcriptAvailableTranslationsUrl': self.item_descriptor.xmodule_runtime.handler_url(
                self.item_descriptor, 'transcript', 'available_translations'
            ).rstrip('/?'),
            'saveStateUrl': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
            'sources': data['result']['sources'],
        })
        expected_context.update({
            'id': self.item_descriptor.location.html_id(),
            'download_video_link': data['result']['download_video_link'],
            'metadata': json.dumps(expected_context['metadata'])
        })

        self.assertEqual(
            context,
            self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context)
        )
Example #41
0
    def test_mobile_api_config(self):
        """
        Tests VideoSummaryList with different MobileApiConfig video_profiles
        """
        self.login_and_enroll()
        edx_video_id = "testing_mobile_high"
        api.create_video({
            'edx_video_id': edx_video_id,
            'status': 'test',
            'client_video_id': u"test video omega \u03a9",
            'duration': 12,
            'courses': [unicode(self.course.id)],
            'encoded_videos': [
                {
                    'profile': 'youtube',
                    'url': self.youtube_url,
                    'file_size': 2222,
                    'bitrate': 4444
                },
                {
                    'profile': 'mobile_high',
                    'url': self.video_url_high,
                    'file_size': 111,
                    'bitrate': 333
                },

            ]})
        ItemFactory.create(
            parent=self.other_unit,
            category="video",
            display_name=u"testing mobile high video",
            edx_video_id=edx_video_id,
        )

        expected_output = {
            'category': u'video',
            'video_thumbnail_url': None,
            'language': u'en',
            'name': u'testing mobile high video',
            'video_url': self.video_url_high,
            'duration': 12.0,
            'transcripts': {
                'en': 'http://testserver/api/mobile/v0.5/video_outlines/transcripts/{}/testing_mobile_high_video/en'.format(self.course.id)  # pylint: disable=line-too-long
            },
            'only_on_web': False,
            'encoded_videos': {
                u'mobile_high': {
                    'url': self.video_url_high,
                    'file_size': 111
                },
                u'youtube': {
                    'url': self.youtube_url,
                    'file_size': 2222
                }
            },
            'size': 111
        }

        # Testing when video_profiles='mobile_low,mobile_high,youtube'
        course_outline = self.api_response().data
        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)

        # Testing when there is no mobile_low, and that mobile_high doesn't show
        MobileApiConfig(video_profiles="mobile_low,youtube").save()

        course_outline = self.api_response().data

        expected_output['encoded_videos'].pop('mobile_high')
        expected_output['video_url'] = self.youtube_url
        expected_output['size'] = 2222

        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)

        # Testing where youtube is the default video over mobile_high
        MobileApiConfig(video_profiles="youtube,mobile_high").save()

        course_outline = self.api_response().data

        expected_output['encoded_videos']['mobile_high'] = {
            'url': self.video_url_high,
            'file_size': 111
        }

        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)
Example #42
0
 def test_create_invalid_video(self, data):  # pylint: disable=W0621
     """
     Tests the creation of a video with invalid data
     """
     with self.assertRaises(ValCannotCreateError):
         api.create_video(data)
Example #43
0
def videos_post(course, request):
    """
    Input (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "content_type": "video/mp4"
        }]
    }

    Returns (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "upload_url": "http://example.com/put_video"
        }]
    }

    The returned array corresponds exactly to the input array.
    """
    error = None
    if "files" not in request.json:
        error = "Request object is not JSON or does not contain 'files'"
    elif any(
            "file_name" not in file or "content_type" not in file
            for file in request.json["files"]
    ):
        error = "Request 'files' entry does not contain 'file_name' and 'content_type'"
    elif any(
            file['content_type'] not in VIDEO_SUPPORTED_FILE_FORMATS.values()
            for file in request.json["files"]
    ):
        error = "Request 'files' entry contain unsupported content_type"

    if error:
        return JsonResponse({"error": error}, status=400)

    bucket = storage_service_bucket()
    course_video_upload_token = course.video_upload_pipeline["course_video_upload_token"]
    req_files = request.json["files"]
    resp_files = []

    for req_file in req_files:
        file_name = req_file["file_name"]

        try:
            file_name.encode('ascii')
        except UnicodeEncodeError:
            error_msg = 'The file name for %s must contain only ASCII characters.' % file_name
            return JsonResponse({'error': error_msg}, status=400)

        edx_video_id = unicode(uuid4())
        key = storage_service_key(bucket, file_name=edx_video_id)
        for metadata_name, value in [
                ("course_video_upload_token", course_video_upload_token),
                ("client_video_id", file_name),
                ("course_key", unicode(course.id)),
        ]:
            key.set_metadata(metadata_name, value)
        upload_url = key.generate_url(
            KEY_EXPIRATION_IN_SECONDS,
            "PUT",
            headers={"Content-Type": req_file["content_type"]}
        )

        # persist edx_video_id in VAL
        create_video({
            "edx_video_id": edx_video_id,
            "status": "upload",
            "client_video_id": file_name,
            "duration": 0,
            "encoded_videos": [],
            "courses": [course.id]
        })

        resp_files.append({"file_name": file_name, "upload_url": upload_url, "edx_video_id": edx_video_id})

    return JsonResponse({"files": resp_files}, status=200)
Example #44
0
def videos_post(course, request):
    """
    Input (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "content_type": "video/mp4"
        }]
    }

    Returns (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "upload_url": "http://example.com/put_video"
        }]
    }

    The returned array corresponds exactly to the input array.
    """
    error = None
    if "files" not in request.json:
        error = "Request object is not JSON or does not contain 'files'"
    elif any("file_name" not in file or "content_type" not in file
             for file in request.json["files"]):
        error = "Request 'files' entry does not contain 'file_name' and 'content_type'"
    elif any(file['content_type'] not in VIDEO_SUPPORTED_FILE_FORMATS.values()
             for file in request.json["files"]):
        error = "Request 'files' entry contain unsupported content_type"

    if error:
        return JsonResponse({"error": error}, status=400)

    bucket = storage_service_bucket()
    course_video_upload_token = course.video_upload_pipeline[
        "course_video_upload_token"]
    req_files = request.json["files"]
    resp_files = []

    for req_file in req_files:
        file_name = req_file["file_name"]

        try:
            file_name.encode('ascii')
        except UnicodeEncodeError:
            error_msg = 'The file name for %s must contain only ASCII characters.' % file_name
            return JsonResponse({'error': error_msg}, status=400)

        edx_video_id = unicode(uuid4())
        key = storage_service_key(bucket, file_name=edx_video_id)
        for metadata_name, value in [
            ("course_video_upload_token", course_video_upload_token),
            ("client_video_id", file_name),
            ("course_key", unicode(course.id)),
        ]:
            key.set_metadata(metadata_name, value)
        upload_url = key.generate_url(
            KEY_EXPIRATION_IN_SECONDS,
            "PUT",
            headers={"Content-Type": req_file["content_type"]})

        # persist edx_video_id in VAL
        create_video({
            "edx_video_id": edx_video_id,
            "status": "upload",
            "client_video_id": file_name,
            "duration": 0,
            "encoded_videos": [],
            "courses": [course.id]
        })

        resp_files.append({
            "file_name": file_name,
            "upload_url": upload_url,
            "edx_video_id": edx_video_id
        })

    return JsonResponse({"files": resp_files}, status=200)
Example #45
0
def videos_post(course, request):
    """
    Input (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "content_type": "video/mp4"
        }]
    }

    Returns (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "upload_url": "http://example.com/put_video"
        }]
    }

    The returned array corresponds exactly to the input array.
    """
    error = None
    if "files" not in request.json:
        error = "Request object is not JSON or does not contain 'files'"
    elif any("file_name" not in file or "content_type" not in file
             for file in request.json["files"]):
        error = "Request 'files' entry does not contain 'file_name' and 'content_type'"

    if error:
        return JsonResponse({"error": error}, status=400)

    bucket = storage_service_bucket()
    course_video_upload_token = course.video_upload_pipeline[
        "course_video_upload_token"]
    req_files = request.json["files"]
    resp_files = []

    for req_file in req_files:
        file_name = req_file["file_name"]

        edx_video_id = unicode(uuid4())
        key = storage_service_key(bucket, file_name=edx_video_id)
        for metadata_name, value in [
            ("course_video_upload_token", course_video_upload_token),
            ("client_video_id", file_name),
            ("course_key", unicode(course.id)),
                # Edraak: This is needed in the edraak video processor for youtube deployments
            ("course_title",
             urllib.quote_plus(course.display_name.encode("utf-8"))),
        ]:
            key.set_metadata(metadata_name, value)
        upload_url = key.generate_url(
            KEY_EXPIRATION_IN_SECONDS,
            "PUT",
            headers={"Content-Type": req_file["content_type"]})

        # persist edx_video_id in VAL
        create_video({
            "edx_video_id": edx_video_id,
            "status": "upload",
            "client_video_id": file_name,
            "duration": 0,
            "encoded_videos": [],
            "courses": [course.id]
        })

        resp_files.append({"file_name": file_name, "upload_url": upload_url})

    return JsonResponse({"files": resp_files}, status=200)
Example #46
0
def videos_post(course, request):
    """
    Input (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "content_type": "video/mp4"
        }]
    }

    Returns (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "upload_url": "http://example.com/put_video"
        }]
    }

    The returned array corresponds exactly to the input array.
    """
    error = None
    if "files" not in request.json:
        error = "Request object is not JSON or does not contain 'files'"
    elif any(
            "file_name" not in file or "content_type" not in file
            for file in request.json["files"]
    ):
        error = "Request 'files' entry does not contain 'file_name' and 'content_type'"

    if error:
        return JsonResponse({"error": error}, status=400)

    bucket = storage_service_bucket()
    course_video_upload_token = course.video_upload_pipeline["course_video_upload_token"]
    req_files = request.json["files"]
    resp_files = []

    for req_file in req_files:
        file_name = req_file["file_name"]

        edx_video_id = unicode(uuid4())
        key = storage_service_key(bucket, file_name=edx_video_id)
        for metadata_name, value in [
            ("course_video_upload_token", course_video_upload_token),
            ("client_video_id", file_name),
            ("course_key", unicode(course.id)),
        ]:
            key.set_metadata(metadata_name, value)
        upload_url = key.generate_url(
            KEY_EXPIRATION_IN_SECONDS,
            "PUT",
            headers={"Content-Type": req_file["content_type"]}
        )

        # persist edx_video_id as uploaded through this course
        video_meta_data = AssetMetadata(course.id.make_asset_key(VIDEO_ASSET_TYPE, edx_video_id))
        modulestore().save_asset_metadata(video_meta_data, request.user.id)

        # persist edx_video_id in VAL
        create_video({
            "edx_video_id": edx_video_id,
            "status": "upload",
            "client_video_id": file_name,
            "duration": 0,
            "encoded_videos": [],
        })

        resp_files.append({"file_name": file_name, "upload_url": upload_url})

    return JsonResponse({"files": resp_files}, status=200)
Example #47
0
    def setUp(self):
        super().setUp()

        block_info_tree = [
            BlockInfo('Section', 'chapter', {}, [
                BlockInfo('Subsection', 'sequential', {}, [
                    BlockInfo('Vertical', 'vertical', {}, [
                        BlockInfo(
                            'Clip', 'video', {
                                'edx_video_id': 'edxval1',
                                'start_time': timedelta(seconds=20),
                                'end_time': timedelta(seconds=60)
                            }, []),
                        BlockInfo('Video', 'video',
                                  {'edx_video_id': 'edxval2'}, []),
                        BlockInfo('Web', 'video', {
                            'edx_video_id': 'edxval3',
                            'only_on_web': True
                        }, []),
                        BlockInfo('HTML', 'html', {'data': 'Hello World'}, []),
                        BlockInfo('Problem1', 'problem', {}, []),
                        BlockInfo('Problem2', 'problem', {}, []),
                    ]),
                ]),
            ]),
        ]

        self.course_key = SampleCourseFactory.create(
            block_info_tree=block_info_tree).id
        self.course_usage_key = self.store.make_course_usage_key(
            self.course_key)
        self.block_structure = BlockStructureFactory.create_from_modulestore(
            self.course_usage_key, self.store)

        self.section_key = self.course_key.make_usage_key('chapter', 'Section')
        self.subsection_key = self.course_key.make_usage_key(
            'sequential', 'Subsection')
        self.vertical_key = self.course_key.make_usage_key(
            'vertical', 'Vertical')
        self.video_clip_key = self.course_key.make_usage_key('video', 'Clip')
        self.video_normal_key = self.course_key.make_usage_key(
            'video', 'Video')
        self.video_web_key = self.course_key.make_usage_key('video', 'Web')
        self.html_key = self.course_key.make_usage_key('html', 'HTML')

        # Set edxval data
        create_video({
            'edx_video_id': 'edxval1',
            'status': 'uploaded',
            'client_video_id': 'Clip Video',
            'duration': 200,
            'encoded_videos': [],
            'courses': [str(self.course_key)],
        })
        create_video({
            'edx_video_id': 'edxval2',
            'status': 'uploaded',
            'client_video_id': 'Normal Video',
            'duration': 30,
            'encoded_videos': [],
            'courses': [str(self.course_key)],
        })
        create_video({
            'edx_video_id': 'edxval3',
            'status': 'uploaded',
            'client_video_id': 'Web Video',
            'duration': 50,
            'encoded_videos': [],
            'courses': [str(self.course_key)],
        })
def videos_post(course, request):
    """
    Input (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "content_type": "video/mp4"
        }]
    }

    Returns (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "upload_url": "http://example.com/put_video"
        }]
    }

    The returned array corresponds exactly to the input array.
    """
    error = None
    data = request.json
    if 'files' not in data:
        error = "Request object is not JSON or does not contain 'files'"
    elif any(
            'file_name' not in file or 'content_type' not in file
            for file in data['files']
    ):
        error = "Request 'files' entry does not contain 'file_name' and 'content_type'"
    elif any(
            file['content_type'] not in VIDEO_SUPPORTED_FILE_FORMATS.values()
            for file in data['files']
    ):
        error = "Request 'files' entry contain unsupported content_type"

    if error:
        return JsonResponse({'error': error}, status=400)

    req_files = data['files']
    resp_files = []

    for req_file in req_files:
        file_name = req_file['file_name']

        try:
            file_name.encode('ascii')
        except UnicodeEncodeError:
            error_msg = 'The file name for %s must contain only ASCII characters.' % file_name
            return JsonResponse({'error': error_msg}, status=400)

        edx_video_id = unicode(uuid4())

        metadata_list = [
            ('client_video_id', file_name),
            ('course_key', unicode(course.id)),
        ]

        # Only include `course_video_upload_token` if its set, as it won't be required if video uploads
        # are enabled by default.
        course_video_upload_token = course.video_upload_pipeline.get('course_video_upload_token')
        if course_video_upload_token:
            metadata_list.append(('course_video_upload_token', course_video_upload_token))

        is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
        if is_video_transcript_enabled:
            transcript_preferences = get_transcript_preferences(unicode(course.id))
            if transcript_preferences is not None:
                metadata_list.append(('transcript_preferences', json.dumps(transcript_preferences)))

        upload_url = settings.VIDEO_UPLOAD_PREAUTH_URL+edx_video_id

        # persist edx_video_id in VAL
        create_video({
            'edx_video_id': edx_video_id,
            'status': 'upload',
            'client_video_id': file_name,
            'duration': 0,
            'encoded_videos': [],
            'courses': [unicode(course.id)]
        })

        resp_files.append({'file_name': file_name, 'upload_url': upload_url, 'edx_video_id': edx_video_id})

    return JsonResponse({'files': resp_files}, status=200)
Example #49
0
 def setUp(self):
     super(VideoUploadTestMixin, self).setUp()
     self.url = self.get_url_for_course_key(self.course.id)
     self.test_token = "test_token"
     self.course.video_upload_pipeline = {
         "course_video_upload_token": self.test_token,
     }
     self.save_course()
     self.profiles = ["profile1", "profile2"]
     self.previous_uploads = [
         {
             "edx_video_id": "test1",
             "client_video_id": "test1.mp4",
             "duration": 42.0,
             "status": "upload",
             "encoded_videos": [],
         },
         {
             "edx_video_id":
             "test2",
             "client_video_id":
             "test2.mp4",
             "duration":
             128.0,
             "status":
             "file_complete",
             "encoded_videos": [
                 {
                     "profile": "profile1",
                     "url": "http://example.com/profile1/test2.mp4",
                     "file_size": 1600,
                     "bitrate": 100,
                 },
                 {
                     "profile": "profile2",
                     "url": "http://example.com/profile2/test2.mov",
                     "file_size": 16000,
                     "bitrate": 1000,
                 },
             ],
         },
         {
             "edx_video_id":
             "non-ascii",
             "client_video_id":
             u"nón-ascii-näme.mp4",
             "duration":
             256.0,
             "status":
             "transcode_active",
             "encoded_videos": [
                 {
                     "profile": "profile1",
                     "url":
                     u"http://example.com/profile1/nón-ascii-näme.mp4",
                     "file_size": 3200,
                     "bitrate": 100,
                 },
             ]
         },
     ]
     # Ensure every status string is tested
     self.previous_uploads += [
         {
             "edx_video_id": "status_test_{}".format(status),
             "client_video_id": "status_test.mp4",
             "duration": 3.14,
             "status": status,
             "encoded_videos": [],
         } for status in (StatusDisplayStrings._STATUS_MAP.keys() +  # pylint:disable=protected-access
                          ["non_existent_status"])
     ]
     for profile in self.profiles:
         create_profile(profile)
     for video in self.previous_uploads:
         create_video(video)
         modulestore().save_asset_metadata(
             AssetMetadata(
                 self.course.id.make_asset_key(VIDEO_ASSET_TYPE,
                                               video["edx_video_id"])),
             self.user.id)
Example #50
0
def videos_post(course, request):
    """
    Input (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "content_type": "video/mp4"
        }]
    }

    Returns (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "upload_url": "http://example.com/put_video"
        }]
    }

    The returned array corresponds exactly to the input array.
    """
    error = None
    data = request.json
    if 'files' not in data:
        error = "Request object is not JSON or does not contain 'files'"
    elif any(
            'file_name' not in file or 'content_type' not in file
            for file in data['files']
    ):
        error = "Request 'files' entry does not contain 'file_name' and 'content_type'"
    elif any(
            file['content_type'] not in VIDEO_SUPPORTED_FILE_FORMATS.values()
            for file in data['files']
    ):
        error = "Request 'files' entry contain unsupported content_type"

    if error:
        return JsonResponse({'error': error}, status=400)

    bucket = storage_service_bucket()
    req_files = data['files']
    resp_files = []

    for req_file in req_files:
        file_name = req_file['file_name']

        try:
            file_name.encode('ascii')
        except UnicodeEncodeError:
            error_msg = u'The file name for %s must contain only ASCII characters.' % file_name
            return JsonResponse({'error': error_msg}, status=400)

        edx_video_id = unicode(uuid4())
        key = storage_service_key(bucket, file_name=edx_video_id)

        metadata_list = [
            ('client_video_id', file_name),
            ('course_key', unicode(course.id)),
        ]

        deprecate_youtube = waffle_flags()[DEPRECATE_YOUTUBE]
        course_video_upload_token = course.video_upload_pipeline.get('course_video_upload_token')

        # Only include `course_video_upload_token` if youtube has not been deprecated
        # for this course.
        if not deprecate_youtube.is_enabled(course.id) and course_video_upload_token:
            metadata_list.append(('course_video_upload_token', course_video_upload_token))

        is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
        if is_video_transcript_enabled:
            transcript_preferences = get_transcript_preferences(unicode(course.id))
            if transcript_preferences is not None:
                metadata_list.append(('transcript_preferences', json.dumps(transcript_preferences)))

        for metadata_name, value in metadata_list:
            key.set_metadata(metadata_name, value)
        upload_url = key.generate_url(
            KEY_EXPIRATION_IN_SECONDS,
            'PUT',
            headers={'Content-Type': req_file['content_type']}
        )

        # persist edx_video_id in VAL
        create_video({
            'edx_video_id': edx_video_id,
            'status': 'upload',
            'client_video_id': file_name,
            'duration': 0,
            'encoded_videos': [],
            'courses': [unicode(course.id)]
        })

        resp_files.append({'file_name': file_name, 'upload_url': upload_url, 'edx_video_id': edx_video_id})

    return JsonResponse({'files': resp_files}, status=200)
Example #51
0
 def setUp(self):
     super(VideoUploadTestMixin, self).setUp()
     self.url = self.get_url_for_course_key(self.course.id)
     self.test_token = "test_token"
     self.course.video_upload_pipeline = {
         "course_video_upload_token": self.test_token,
     }
     self.save_course()
     self.profiles = [
         {
             "profile_name": "profile1",
             "extension": "mp4",
             "width": 640,
             "height": 480,
         },
         {
             "profile_name": "profile2",
             "extension": "mp4",
             "width": 1920,
             "height": 1080,
         },
     ]
     self.previous_uploads = [
         {
             "edx_video_id": "test1",
             "client_video_id": "test1.mp4",
             "duration": 42.0,
             "status": "upload",
             "encoded_videos": [],
         },
         {
             "edx_video_id":
             "test2",
             "client_video_id":
             "test2.mp4",
             "duration":
             128.0,
             "status":
             "file_complete",
             "encoded_videos": [
                 {
                     "profile": "profile1",
                     "url": "http://example.com/profile1/test2.mp4",
                     "file_size": 1600,
                     "bitrate": 100,
                 },
                 {
                     "profile": "profile2",
                     "url": "http://example.com/profile2/test2.mov",
                     "file_size": 16000,
                     "bitrate": 1000,
                 },
             ],
         },
         {
             "edx_video_id":
             "non-ascii",
             "client_video_id":
             u"nón-ascii-näme.mp4",
             "duration":
             256.0,
             "status":
             "transcode_active",
             "encoded_videos": [
                 {
                     "profile": "profile1",
                     "url":
                     u"http://example.com/profile1/nón-ascii-näme.mp4",
                     "file_size": 3200,
                     "bitrate": 100,
                 },
             ]
         },
     ]
     for profile in self.profiles:
         create_profile(profile)
     for video in self.previous_uploads:
         create_video(video)
         modulestore().save_asset_metadata(
             AssetMetadata(
                 self.course.id.make_asset_key(VIDEO_ASSET_TYPE,
                                               video["edx_video_id"])),
             self.user.id)
Example #52
0
def videos_post(course, request):
    """
    Input (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "content_type": "video/mp4"
        }]
    }

    Returns (JSON):
    {
        "files": [{
            "file_name": "video.mp4",
            "upload_url": "http://example.com/put_video"
        }]
    }

    The returned array corresponds exactly to the input array.
    """
    error = None
    if "files" not in request.json:
        error = "Request object is not JSON or does not contain 'files'"
    elif any("file_name" not in file or "content_type" not in file
             for file in request.json["files"]):
        error = "Request 'files' entry does not contain 'file_name' and 'content_type'"

    if error:
        return JsonResponse({"error": error}, status=400)

    bucket = storage_service_bucket()
    course_video_upload_token = course.video_upload_pipeline[
        "course_video_upload_token"]
    req_files = request.json["files"]
    resp_files = []

    for req_file in req_files:
        file_name = req_file["file_name"]

        edx_video_id = unicode(uuid4())
        key = storage_service_key(bucket, file_name=edx_video_id)
        for metadata_name, value in [
            ("course_video_upload_token", course_video_upload_token),
            ("client_video_id", file_name),
            ("course_key", unicode(course.id)),
        ]:
            key.set_metadata(metadata_name, value)
        upload_url = key.generate_url(
            KEY_EXPIRATION_IN_SECONDS,
            "PUT",
            headers={"Content-Type": req_file["content_type"]})

        # persist edx_video_id as uploaded through this course
        video_meta_data = AssetMetadata(
            course.id.make_asset_key(VIDEO_ASSET_TYPE, edx_video_id))
        modulestore().save_asset_metadata(video_meta_data, request.user.id)

        # persist edx_video_id in VAL
        create_video({
            "edx_video_id": edx_video_id,
            "status": "upload",
            "client_video_id": file_name,
            "duration": 0,
            "encoded_videos": [],
        })

        resp_files.append({"file_name": file_name, "upload_url": upload_url})

    return JsonResponse({"files": resp_files}, status=200)
    def setUp(self):
        """ Common setup. """
        super(TestMigrateTranscripts, self).setUp()
        self.store = modulestore()
        self.course = CourseFactory.create()
        self.course_2 = CourseFactory.create()

        video = {
            'edx_video_id': 'test_edx_video_id',
            'client_video_id': 'test1.mp4',
            'duration': 42.0,
            'status': 'upload',
            'courses': [six.text_type(self.course.id)],
            'encoded_videos': [],
            'created': datetime.now(pytz.utc)
        }
        api.create_video(video)

        video_sample_xml = '''
            <video display_name="Test Video"
                   edx_video_id="test_edx_video_id"
                   youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
                   show_captions="false"
                   download_track="false"
                   start_time="1.0"
                   download_video="false"
                   end_time="60.0">
              <source src="http://www.example.com/source.mp4"/>
              <track src="http://www.example.com/track"/>
              <handout src="http://www.example.com/handout"/>
              <transcript language="ge" src="subs_grmtran1.srt" />
              <transcript language="hr" src="subs_croatian1.srt" />
            </video>
        '''

        video_sample_xml_2 = '''
            <video display_name="Test Video 2"
                   edx_video_id="test_edx_video_id_2"
                   youtube="1.0:p2Q6BrNhdh8,0.75:izygArpw-Qo,1.25:1EeWXzPdhSA,1.5:rABDYkeK0x8"
                   show_captions="false"
                   download_track="false"
                   start_time="1.0"
                   download_video="false"
                   end_time="60.0">
              <source src="http://www.example.com/source.mp4"/>
              <track src="http://www.example.com/track"/>
              <handout src="http://www.example.com/handout"/>
              <transcript language="ge" src="not_found.srt" />
            </video>
        '''
        self.video_descriptor = ItemFactory.create(
            parent_location=self.course.location, category='video',
            **VideoBlock.parse_video_xml(video_sample_xml)
        )
        self.video_descriptor_2 = ItemFactory.create(
            parent_location=self.course_2.location, category='video',
            **VideoBlock.parse_video_xml(video_sample_xml_2)
        )

        save_to_store(SRT_FILEDATA, 'subs_grmtran1.srt', 'text/srt', self.video_descriptor.location)
        save_to_store(CRO_SRT_FILEDATA, 'subs_croatian1.srt', 'text/srt', self.video_descriptor.location)
Example #54
0
    def test_mobile_api_config(self):
        """
        Tests VideoSummaryList with different MobileApiConfig video_profiles
        """
        self.login_and_enroll()
        edx_video_id = "testing_mobile_high"
        api.create_video({
            'edx_video_id':
            edx_video_id,
            'status':
            'test',
            'client_video_id':
            u"test video omega \u03a9",
            'duration':
            12,
            'courses': [unicode(self.course.id)],
            'encoded_videos': [
                {
                    'profile': 'youtube',
                    'url': self.youtube_url,
                    'file_size': 2222,
                    'bitrate': 4444
                },
                {
                    'profile': 'mobile_high',
                    'url': self.video_url_high,
                    'file_size': 111,
                    'bitrate': 333
                },
            ]
        })
        ItemFactory.create(
            parent=self.other_unit,
            category="video",
            display_name=u"testing mobile high video",
            edx_video_id=edx_video_id,
        )

        expected_output = {
            'category': u'video',
            'video_thumbnail_url': None,
            'language': u'en',
            'name': u'testing mobile high video',
            'video_url': self.video_url_high,
            'duration': 12.0,
            'transcripts': {
                'en':
                'http://testserver/api/mobile/v0.5/video_outlines/transcripts/{}/testing_mobile_high_video/en'
                .format(self.course.id)  # pylint: disable=line-too-long
            },
            'only_on_web': False,
            'encoded_videos': {
                u'mobile_high': {
                    'url': self.video_url_high,
                    'file_size': 111
                },
                u'youtube': {
                    'url': self.youtube_url,
                    'file_size': 2222
                }
            },
            'size': 111
        }

        # Testing when video_profiles='mobile_low,mobile_high,youtube'
        course_outline = self.api_response().data
        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)

        # Testing when there is no mobile_low, and that mobile_high doesn't show
        MobileApiConfig(video_profiles="mobile_low,youtube").save()

        course_outline = self.api_response().data

        expected_output['encoded_videos'].pop('mobile_high')
        expected_output['video_url'] = self.youtube_url
        expected_output['size'] = 2222

        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)

        # Testing where youtube is the default video over mobile_high
        MobileApiConfig(video_profiles="youtube,mobile_high").save()

        course_outline = self.api_response().data

        expected_output['encoded_videos']['mobile_high'] = {
            'url': self.video_url_high,
            'file_size': 111
        }

        course_outline[0]['summary'].pop("id")
        self.assertEqual(course_outline[0]['summary'], expected_output)
Example #55
0
    def test_mobile_api_config(self):
        """
        Tests VideoSummaryList with different MobileApiConfig video_profiles
        """
        self.login_and_enroll()
        edx_video_id = "testing_mobile_high"
        api.create_video(
            {
                "edx_video_id": edx_video_id,
                "status": "test",
                "client_video_id": u"test video omega \u03a9",
                "duration": 12,
                "courses": [unicode(self.course.id)],
                "encoded_videos": [
                    {"profile": "youtube", "url": self.youtube_url, "file_size": 2222, "bitrate": 4444},
                    {"profile": "mobile_high", "url": self.video_url_high, "file_size": 111, "bitrate": 333},
                ],
            }
        )
        ItemFactory.create(
            parent=self.other_unit,
            category="video",
            display_name=u"testing mobile high video",
            edx_video_id=edx_video_id,
        )

        expected_output = {
            "category": u"video",
            "video_thumbnail_url": None,
            "language": u"en",
            "name": u"testing mobile high video",
            "video_url": self.video_url_high,
            "duration": 12.0,
            "transcripts": {
                "en": "http://testserver/api/mobile/v0.5/video_outlines/transcripts/{}/testing_mobile_high_video/en".format(
                    self.course.id
                )  # pylint: disable=line-too-long
            },
            "only_on_web": False,
            "encoded_videos": {
                u"mobile_high": {"url": self.video_url_high, "file_size": 111},
                u"youtube": {"url": self.youtube_url, "file_size": 2222},
            },
            "size": 111,
        }

        # Testing when video_profiles='mobile_low,mobile_high,youtube'
        course_outline = self.api_response().data
        course_outline[0]["summary"].pop("id")
        self.assertEqual(course_outline[0]["summary"], expected_output)

        # Testing when there is no mobile_low, and that mobile_high doesn't show
        MobileApiConfig(video_profiles="mobile_low,youtube").save()

        course_outline = self.api_response().data

        expected_output["encoded_videos"].pop("mobile_high")
        expected_output["video_url"] = self.youtube_url
        expected_output["size"] = 2222

        course_outline[0]["summary"].pop("id")
        self.assertEqual(course_outline[0]["summary"], expected_output)

        # Testing where youtube is the default video over mobile_high
        MobileApiConfig(video_profiles="youtube,mobile_high").save()

        course_outline = self.api_response().data

        expected_output["encoded_videos"]["mobile_high"] = {"url": self.video_url_high, "file_size": 111}

        course_outline[0]["summary"].pop("id")
        self.assertEqual(course_outline[0]["summary"], expected_output)
Example #56
0
    def setUp(self):
        super(TestVideoOutline, self).setUp()
        self.user = UserFactory.create()
        self.course = CourseFactory.create(mobile_available=True)
        section = ItemFactory.create(
            parent_location=self.course.location,
            category="chapter",
            display_name=u"test factory section omega \u03a9",
        )
        self.sub_section = ItemFactory.create(
            parent_location=section.location,
            category="sequential",
            display_name=u"test subsection omega \u03a9",
        )

        self.unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={'graded': True, 'format': 'Homework'},
            display_name=u"test unit omega \u03a9",
        )
        self.other_unit = ItemFactory.create(
            parent_location=self.sub_section.location,
            category="vertical",
            metadata={'graded': True, 'format': 'Homework'},
            display_name=u"test unit omega 2 \u03a9",
        )

        self.edx_video_id = 'testing-123'

        self.video_url = 'http://val.edx.org/val/video.mp4'
        self.html5_video_url = 'http://video.edx.org/html5/video.mp4'

        api.create_profile({
            'profile_name': 'youtube',
            'extension': 'mp4',
            'width': 1280,
            'height': 720
            })
        api.create_profile({
            'profile_name': 'mobile_low',
            'extension': 'mp4',
            'width': 640,
            'height': 480
            })

        val_video = api.create_video({
            'edx_video_id': self.edx_video_id,
            'client_video_id': u"test video omega \u03a9",
            'duration': 12,
            'courses': [unicode(self.course.id)],
            'encoded_videos': [
                {
                    'profile': 'youtube',
                    'url': 'xyz123',
                    'file_size': 0,
                    'bitrate': 1500
                },
                {
                    'profile': 'mobile_low',
                    'url': self.video_url,
                    'file_size': 12345,
                    'bitrate': 250
                }
            ]})

        subid = uuid4().hex
        self.video = ItemFactory.create(
            parent_location=self.unit.location,
            category="video",
            edx_video_id=self.edx_video_id,
            display_name=u"test video omega \u03a9",
            sub=subid
        )

        result_location = transcripts_utils.save_subs_to_store({
            'start': [100, 200, 240, 390, 1000],
            'end': [200, 240, 380, 1000, 1500],
            'text': [
                'subs #1',
                'subs #2',
                'subs #3',
                'subs #4',
                'subs #5'
            ]},
            subid,
            self.course)

        self.client.login(username=self.user.username, password='******')
Example #57
0
 def test_create_invalid_video(self, data):  # pylint: disable=W0621
     """
     Tests the creation of a video with invalid data
     """
     with self.assertRaises(ValCannotCreateError):
         api.create_video(data)
Example #58
0
    def setUp(self):
        super(VideoUploadTestBase, self).setUp()
        self.url = self.get_url_for_course_key(self.course.id)
        self.test_token = "test_token"
        self.course.video_upload_pipeline = {
            "course_video_upload_token": self.test_token,
        }
        self.save_course()

        # create another course for videos belonging to multiple courses
        self.course2 = CourseFactory.create()
        self.course2.video_upload_pipeline = {
            "course_video_upload_token": self.test_token,
        }
        self.course2.save()
        self.store.update_item(self.course2, self.user.id)

        # course ids for videos
        course_ids = [unicode(self.course.id), unicode(self.course2.id)]
        created = datetime.now(pytz.utc)

        self.profiles = ["profile1", "profile2"]
        self.previous_uploads = [
            {
                "edx_video_id": "test1",
                "client_video_id": "test1.mp4",
                "duration": 42.0,
                "status": "upload",
                "courses": course_ids,
                "encoded_videos": [],
                "created": created
            },
            {
                "edx_video_id":
                "test2",
                "client_video_id":
                "test2.mp4",
                "duration":
                128.0,
                "status":
                "file_complete",
                "courses":
                course_ids,
                "created":
                created,
                "encoded_videos": [
                    {
                        "profile": "profile1",
                        "url": "http://example.com/profile1/test2.mp4",
                        "file_size": 1600,
                        "bitrate": 100,
                    },
                    {
                        "profile": "profile2",
                        "url": "http://example.com/profile2/test2.mov",
                        "file_size": 16000,
                        "bitrate": 1000,
                    },
                ],
            },
            {
                "edx_video_id":
                "non-ascii",
                "client_video_id":
                u"nón-ascii-näme.mp4",
                "duration":
                256.0,
                "status":
                "transcode_active",
                "courses":
                course_ids,
                "created":
                created,
                "encoded_videos": [
                    {
                        "profile": "profile1",
                        "url":
                        u"http://example.com/profile1/nón-ascii-näme.mp4",
                        "file_size": 3200,
                        "bitrate": 100,
                    },
                ]
            },
        ]
        # Ensure every status string is tested
        self.previous_uploads += [
            {
                "edx_video_id": "status_test_{}".format(status),
                "client_video_id": "status_test.mp4",
                "duration": 3.14,
                "status": status,
                "courses": course_ids,
                "created": created,
                "encoded_videos": [],
            } for status in (StatusDisplayStrings._STATUS_MAP.keys() +  # pylint:disable=protected-access
                             ["non_existent_status"])
        ]
        for profile in self.profiles:
            create_profile(profile)
        for video in self.previous_uploads:
            create_video(video)
Example #59
0
    def editor_saved(self, user, old_metadata, old_content):
        """
        Used to update video values during `self`:save method from CMS.
        old_metadata: dict, values of fields of `self` with scope=settings which were explicitly set by user.
        old_content, same as `old_metadata` but for scope=content.
        Due to nature of code flow in item.py::_save_item, before current function is called,
        fields of `self` instance have been already updated, but not yet saved.
        To obtain values, which were changed by user input,
        one should compare own_metadata(self) and old_medatada.
        Video player has two tabs, and due to nature of sync between tabs,
        metadata from Basic tab is always sent when video player is edited and saved first time, for example:
        {'youtube_id_1_0': u'3_yD_cEKoCk', 'display_name': u'Video', 'sub': u'3_yD_cEKoCk', 'html5_sources': []},
        that's why these fields will always present in old_metadata after first save. This should be fixed.
        At consequent save requests html5_sources are always sent too, disregard of their change by user.
        That means that html5_sources are always in list of fields that were changed (`metadata` param in save_item).
        This should be fixed too.
        """
        metadata_was_changed_by_user = old_metadata != own_metadata(self)

        if edxval_api and int(self.duration) > 0:
            video_url = ""
            if len(self.html5_sources):
                video_url = self.html5_sources[0]
            elif self.youtube_id_1_0:
                video_url = "https://www.youtube.com/watch?v=" + str(self.youtube_id_1_0)

            if video_url:
                
                if not self.edx_video_id:
                    now = datetime.datetime.now()
                    hash_object = hashlib.sha256(str(now))
                    hex_dig = hash_object.hexdigest()
                    self.edx_video_id = hex_dig
                    self.save()

                payload = {
                    "url": video_url,
                    "edx_video_id": self.edx_video_id,
                    "duration": self.duration,
                    "status": "live",
                    "encoded_videos": [{
                        "url": video_url,
                        "file_size": 1,
                        "bitrate": 1,
                        "profile": "mobile_high"
                    }]
                }

                # TODO: Change this try catch
                try:
                    edxval_api.get_video_info(self.edx_video_id)
                except:
                    edxval_api.create_video(payload)
                    
                # with this
                # edxval_api.create_video(payload)
                # when edxval app is updated

        # There is an edge case when old_metadata and own_metadata are same and we are importing transcript from youtube
        # then there is a syncing issue where html5_subs are not syncing with youtube sub, We can make sync better by
        # checking if transcript is present for the video and if any html5_ids transcript is not present then trigger
        # the manage_video_subtitles_save to create the missing transcript with particular html5_id.
        if not metadata_was_changed_by_user and self.sub and hasattr(self, 'html5_sources'):
            html5_ids = get_html5_ids(self.html5_sources)
            for subs_id in html5_ids:
                try:
                    Transcript.asset(self.location, subs_id)
                except NotFoundError:
                    # If a transcript does not not exist with particular html5_id then there is no need to check other
                    # html5_ids because we have to create a new transcript with this missing html5_id by turning on
                    # metadata_was_changed_by_user flag.
                    metadata_was_changed_by_user = True
                    break

        if metadata_was_changed_by_user:
            self.edx_video_id = self.edx_video_id and self.edx_video_id.strip()

            # We want to override `youtube_id_1_0` with val youtube profile in the first place when someone adds/edits
            # an `edx_video_id` or its underlying YT val profile. Without this, override will only happen when a user
            # saves the video second time. This is because of the syncing of basic and advanced video settings which
            # also syncs val youtube id from basic tab's `Video Url` to advanced tab's `Youtube ID`.
            if self.edx_video_id and edxval_api:
                val_youtube_id = edxval_api.get_url_for_profile(self.edx_video_id, 'youtube')
                if val_youtube_id and self.youtube_id_1_0 != val_youtube_id:
                    self.youtube_id_1_0 = val_youtube_id

            manage_video_subtitles_save(
                self,
                user,
                old_metadata if old_metadata else None,
                generate_translation=True
            )