Пример #1
0
def _get_index_videos(course):
    """
    Returns the information about each video upload required for the video list
    """
    course_id = unicode(course.id)
    attrs = ['edx_video_id', 'client_video_id', 'created', 'duration', 'status', 'courses']

    if VideoTranscriptEnabledFlag.feature_enabled(course.id):
        attrs += ['transcripts']

    def _get_values(video):
        """
        Get data for predefined video attributes.
        """
        values = {}
        for attr in attrs:
            if attr == 'courses':
                course = filter(lambda c: course_id in c, video['courses'])
                (__, values['course_video_image_url']), = course[0].items()
            else:
                values[attr] = video[attr]

        return values

    return [
        _get_values(video) for video in _get_videos(course)
    ]
Пример #2
0
def _get_index_videos(course):
    """
    Returns the information about each video upload required for the video list
    """
    course_id = unicode(course.id)
    attrs = ['edx_video_id', 'client_video_id', 'created', 'duration', 'status', 'courses']

    if VideoTranscriptEnabledFlag.feature_enabled(course.id):
        attrs += ['transcripts']

    def _get_values(video):
        """
        Get data for predefined video attributes.
        """
        values = {}
        for attr in attrs:
            if attr == 'courses':
                course = filter(lambda c: course_id in c, video['courses'])
                (__, values['course_video_image_url']), = course[0].items()
            else:
                values[attr] = video[attr]

        return values

    return [
        _get_values(video) for video in _get_videos(course)
    ]
Пример #3
0
def videos_index_html(course):
    """
    Returns an HTML page to display previous video uploads and allow new ones
    """
    is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(
        course.id)
    context = {
        'context_course':
        course,
        'image_upload_url':
        reverse_course_url('video_images_handler', unicode(course.id)),
        'video_handler_url':
        reverse_course_url('videos_handler', unicode(course.id)),
        'encodings_download_url':
        reverse_course_url('video_encodings_download', unicode(course.id)),
        'default_video_image_url':
        _get_default_video_image_url(),
        'previous_uploads':
        _get_index_videos(course),
        'concurrent_upload_limit':
        settings.VIDEO_UPLOAD_PIPELINE.get('CONCURRENT_UPLOAD_LIMIT', 0),
        'video_supported_file_formats':
        VIDEO_SUPPORTED_FILE_FORMATS.keys(),
        'video_upload_max_file_size':
        VIDEO_UPLOAD_MAX_FILE_SIZE_GB,
        'video_image_settings': {
            'video_image_upload_enabled':
            WAFFLE_SWITCHES.is_enabled(VIDEO_IMAGE_UPLOAD_ENABLED),
            'max_size':
            settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MAX_BYTES'],
            'min_size':
            settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MIN_BYTES'],
            'max_width':
            settings.VIDEO_IMAGE_MAX_WIDTH,
            'max_height':
            settings.VIDEO_IMAGE_MAX_HEIGHT,
            'supported_file_formats':
            settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS
        },
        'is_video_transcript_enabled':
        is_video_transcript_enabled,
        'video_transcript_settings':
        None,
        'active_transcript_preferences':
        None
    }

    if is_video_transcript_enabled:
        context['video_transcript_settings'] = {
            'transcript_preferences_handler_url':
            reverse_course_url('transcript_preferences_handler',
                               unicode(course.id)),
            'transcription_plans':
            get_3rd_party_transcription_plans(),
        }
        context['active_transcript_preferences'] = get_transcript_preferences(
            unicode(course.id))

    return render_to_response('videos_index.html', context)
def is_val_transcript_feature_enabled_for_course(course_id):
    """
    Get edx-val transcript feature flag

    Arguments:
        course_id(CourseKey): Course key identifying a course whose feature flag is being inspected.
    """
    return VideoTranscriptEnabledFlag.feature_enabled(course_id=course_id)
Пример #5
0
def transcript_credentials_handler(request, course_key_string):
    """
    JSON view handler to update the transcript organization credentials.

    Arguments:
        request: WSGI request object
        course_key_string: A course identifier to extract the org.

    Returns:
        - A 200 response if credentials are valid and successfully updated in edx-video-pipeline.
        - A 404 response if transcript feature is not enabled for this course.
        - A 400 if credentials do not pass validations, hence not updated in edx-video-pipeline.
    """
    course_key = CourseKey.from_string(course_key_string)
    if not VideoTranscriptEnabledFlag.feature_enabled(course_key):
        return HttpResponseNotFound()

    provider = request.json.pop('provider')
    error_message, validated_credentials = validate_transcript_credentials(
        provider=provider, **request.json)
    if error_message:
        response = JsonResponse({'error': error_message}, status=400)
    else:
        # Send the validated credentials to edx-video-pipeline.
        credentials_payload = dict(validated_credentials,
                                   org=course_key.org,
                                   provider=provider)
        if waffle_flags()[SAVE_CREDENTIALS_IN_VAL].is_enabled(course_key):
            from edxval.api import create_or_update_transcript_credentials
            response = create_or_update_transcript_credentials(
                **credentials_payload)
            error_response, is_updated = response, not response.get(
                'error_type')
        else:
            error_response, is_updated = update_3rd_party_transcription_service_credentials(
                **credentials_payload)
        # Send appropriate response based on whether credentials were updated or not.
        if is_updated:
            # Cache credentials state in edx-val.
            update_transcript_credentials_state_for_org(org=course_key.org,
                                                        provider=provider,
                                                        exists=is_updated)
            response = JsonResponse(status=200)
        else:
            # Error response would contain error types and the following
            # error type is received from edx-video-pipeline whenever we've
            # got invalid credentials for a provider. Its kept this way because
            # edx-video-pipeline doesn't support i18n translations yet.
            error_type = error_response.get('error_type')
            if error_type == TranscriptionProviderErrorType.INVALID_CREDENTIALS:
                error_message = _('The information you entered is incorrect.')

            response = JsonResponse({'error': error_message}, status=400)

    return response
Пример #6
0
def transcript_upload_handler(request, course_key_string):
    """
    View to upload a transcript file.

    Arguments:
        request: A WSGI request object
        course_key_string: Course key identifying a course

    Transcript file, edx video id and transcript language are required.
    Transcript file should be in SRT(SubRip) format.

    Returns
        - A 400 if any of the validation fails
        - A 404 if the corresponding feature flag is disabled
        - A 200 if transcript has been uploaded successfully
    """
    # Check whether the feature is available for this course.
    course_key = CourseKey.from_string(course_key_string)
    if not VideoTranscriptEnabledFlag.feature_enabled(course_key):
        return HttpResponseNotFound()

    error = validate_transcript_upload_data(data=request.POST, files=request.FILES)
    if error:
        response = JsonResponse({'error': error}, status=400)
    else:
        edx_video_id = request.POST['edx_video_id']
        language_code = request.POST['language_code']
        new_language_code = request.POST['new_language_code']
        transcript_file = request.FILES['file']
        try:
            # Convert SRT transcript into an SJSON format
            # and upload it to S3.
            sjson_subs = Transcript.convert(
                content=transcript_file.read(),
                input_format=Transcript.SRT,
                output_format=Transcript.SJSON
            )
            create_or_update_video_transcript(
                video_id=edx_video_id,
                language_code=language_code,
                metadata={
                    'provider': TranscriptProvider.CUSTOM,
                    'file_format': Transcript.SJSON,
                    'language_code': new_language_code
                },
                file_data=ContentFile(sjson_subs),
            )
            response = JsonResponse(status=201)
        except (TranscriptsGenerationException, UnicodeDecodeError):
            response = JsonResponse(
                {'error': _(u'There is a problem with this transcript file. Try to upload a different file.')},
                status=400
            )

    return response
Пример #7
0
def videos_index_html(course, pagination_conf=None):
    """
    Returns an HTML page to display previous video uploads and allow new ones
    """
    is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
    previous_uploads, pagination_context = _get_index_videos(course, pagination_conf)
    context = {
        'context_course': course,
        'image_upload_url': reverse_course_url('video_images_handler', six.text_type(course.id)),
        'video_handler_url': reverse_course_url('videos_handler', six.text_type(course.id)),
        'encodings_download_url': reverse_course_url('video_encodings_download', six.text_type(course.id)),
        'default_video_image_url': _get_default_video_image_url(),
        'previous_uploads': previous_uploads,
        'concurrent_upload_limit': settings.VIDEO_UPLOAD_PIPELINE.get('CONCURRENT_UPLOAD_LIMIT', 0),
        'video_supported_file_formats': list(VIDEO_SUPPORTED_FILE_FORMATS.keys()),
        'video_upload_max_file_size': VIDEO_UPLOAD_MAX_FILE_SIZE_GB,
        'video_image_settings': {
            'video_image_upload_enabled': WAFFLE_SWITCHES.is_enabled(VIDEO_IMAGE_UPLOAD_ENABLED),
            'max_size': settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MAX_BYTES'],
            'min_size': settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MIN_BYTES'],
            'max_width': settings.VIDEO_IMAGE_MAX_WIDTH,
            'max_height': settings.VIDEO_IMAGE_MAX_HEIGHT,
            'supported_file_formats': settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS
        },
        'is_video_transcript_enabled': is_video_transcript_enabled,
        'active_transcript_preferences': None,
        'transcript_credentials': None,
        'transcript_available_languages': get_all_transcript_languages(),
        'video_transcript_settings': {
            'transcript_download_handler_url': reverse('transcript_download_handler'),
            'transcript_upload_handler_url': reverse('transcript_upload_handler'),
            'transcript_delete_handler_url': reverse_course_url('transcript_delete_handler', six.text_type(course.id)),
            'trancript_download_file_format': Transcript.SRT
        },
        'pagination_context': pagination_context
    }

    if is_video_transcript_enabled:
        context['video_transcript_settings'].update({
            'transcript_preferences_handler_url': reverse_course_url(
                'transcript_preferences_handler',
                six.text_type(course.id)
            ),
            'transcript_credentials_handler_url': reverse_course_url(
                'transcript_credentials_handler',
                six.text_type(course.id)
            ),
            'transcription_plans': get_3rd_party_transcription_plans(),
        })
        context['active_transcript_preferences'] = get_transcript_preferences(six.text_type(course.id))
        # Cached state for transcript providers' credentials (org-specific)
        context['transcript_credentials'] = get_transcript_credentials_state_for_org(course.id.org)

    return render_to_response('videos_index.html', context)
Пример #8
0
def videos_index_html(course):
    """
    Returns an HTML page to display previous video uploads and allow new ones
    """
    is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
    context = {
        'context_course': course,
        'image_upload_url': reverse_course_url('video_images_handler', unicode(course.id)),
        'video_handler_url': reverse_course_url('videos_handler', unicode(course.id)),
        'encodings_download_url': reverse_course_url('video_encodings_download', unicode(course.id)),
        'default_video_image_url': _get_default_video_image_url(),
        'previous_uploads': _get_index_videos(course),
        'concurrent_upload_limit': settings.VIDEO_UPLOAD_PIPELINE.get('CONCURRENT_UPLOAD_LIMIT', 0),
        'video_supported_file_formats': VIDEO_SUPPORTED_FILE_FORMATS.keys(),
        'video_upload_max_file_size': VIDEO_UPLOAD_MAX_FILE_SIZE_GB,
        'video_image_settings': {
            'video_image_upload_enabled': WAFFLE_SWITCHES.is_enabled(VIDEO_IMAGE_UPLOAD_ENABLED),
            'max_size': settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MAX_BYTES'],
            'min_size': settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MIN_BYTES'],
            'max_width': settings.VIDEO_IMAGE_MAX_WIDTH,
            'max_height': settings.VIDEO_IMAGE_MAX_HEIGHT,
            'supported_file_formats': settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS
        },
        'is_video_transcript_enabled': is_video_transcript_enabled,
        'active_transcript_preferences': None,
        'transcript_credentials': None,
        'transcript_available_languages': get_all_transcript_languages(),
        'video_transcript_settings': {
            'transcript_download_handler_url': reverse('transcript_download_handler'),
            'transcript_upload_handler_url': reverse('transcript_upload_handler'),
            'transcript_delete_handler_url': reverse_course_url('transcript_delete_handler', unicode(course.id)),
            'trancript_download_file_format': Transcript.SRT
        }
    }

    if is_video_transcript_enabled:
        context['video_transcript_settings'].update({
            'transcript_preferences_handler_url': reverse_course_url(
                'transcript_preferences_handler',
                unicode(course.id)
            ),
            'transcript_credentials_handler_url': reverse_course_url(
                'transcript_credentials_handler',
                unicode(course.id)
            ),
            'transcription_plans': get_3rd_party_transcription_plans(),
        })
        context['active_transcript_preferences'] = get_transcript_preferences(unicode(course.id))
        # Cached state for transcript providers' credentials (org-specific)
        context['transcript_credentials'] = get_transcript_credentials_state_for_org(course.id.org)

    return render_to_response('videos_index.html', context)
Пример #9
0
def _get_videos(course):
    """
    Retrieves the list of videos from VAL corresponding to this course.
    """
    is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
    videos = list(get_videos_for_course(unicode(course.id), VideoSortField.created, SortDirection.desc))

    # convert VAL's status to studio's Video Upload feature status.
    for video in videos:
        video["status"] = convert_video_status(video)

        if is_video_transcript_enabled:
            video['transcripts'] = get_available_transcript_languages(video_id=video['edx_video_id'])

    return videos
Пример #10
0
def _get_videos(course):
    """
    Retrieves the list of videos from VAL corresponding to this course.
    """
    is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
    videos = list(get_videos_for_course(unicode(course.id), VideoSortField.created, SortDirection.desc))

    # convert VAL's status to studio's Video Upload feature status.
    for video in videos:
        video["status"] = convert_video_status(video)

        if is_video_transcript_enabled:
            video['transcripts'] = get_available_transcript_languages(video_id=video['edx_video_id'])

    return videos
Пример #11
0
def transcript_download_handler(request, course_key_string):
    """
    JSON view handler to download a transcript.

    Arguments:
        request: WSGI request object
        course_key_string: course key

    Returns:
        - A 200 response with SRT transcript file attached.
        - A 400 if there is a validation error.
        - A 404 if there is no such transcript or feature flag is disabled.
    """
    course_key = CourseKey.from_string(course_key_string)
    if not VideoTranscriptEnabledFlag.feature_enabled(course_key):
        return HttpResponseNotFound()

    missing = [attr for attr in ['edx_video_id', 'language_code'] if attr not in request.GET]
    if missing:
        return JsonResponse(
            {'error': _(u'The following parameters are required: {missing}.').format(missing=', '.join(missing))},
            status=400
        )

    edx_video_id = request.GET['edx_video_id']
    language_code = request.GET['language_code']
    transcript = get_video_transcript_data(video_id=edx_video_id, language_code=language_code)
    if transcript:
        name_and_extension = os.path.splitext(transcript['file_name'])
        basename, file_format = name_and_extension[0], name_and_extension[1][1:]
        transcript_filename = '{base_name}.{ext}'.format(base_name=basename.encode('utf8'), ext=Transcript.SRT)
        transcript_content = Transcript.convert(
            content=transcript['content'],
            input_format=file_format,
            output_format=Transcript.SRT
        )
        # Construct an HTTP response
        response = HttpResponse(transcript_content, content_type=Transcript.mime_types[Transcript.SRT])
        response['Content-Disposition'] = 'attachment; filename="{filename}"'.format(filename=transcript_filename)
    else:
        response = HttpResponseNotFound()

    return response
Пример #12
0
def transcript_credentials_handler(request, course_key_string):
    """
    JSON view handler to update the transcript organization credentials.

    Arguments:
        request: WSGI request object
        course_key_string: A course identifier to extract the org.

    Returns:
        - A 200 response if credentials are valid and successfully updated in edx-video-pipeline.
        - A 404 response if transcript feature is not enabled for this course.
        - A 400 if credentials do not pass validations, hence not updated in edx-video-pipeline.
    """
    course_key = CourseKey.from_string(course_key_string)
    if not VideoTranscriptEnabledFlag.feature_enabled(course_key):
        return HttpResponseNotFound()

    provider = request.json.pop('provider')
    error_message, validated_credentials = validate_transcript_credentials(provider=provider, **request.json)
    if error_message:
        response = JsonResponse({'error': error_message}, status=400)
    else:
        # Send the validated credentials to edx-video-pipeline.
        credentials_payload = dict(validated_credentials, org=course_key.org, provider=provider)
        error_response, is_updated = update_3rd_party_transcription_service_credentials(**credentials_payload)
        # Send appropriate response based on whether credentials were updated or not.
        if is_updated:
            # Cache credentials state in edx-val.
            update_transcript_credentials_state_for_org(org=course_key.org, provider=provider, exists=is_updated)
            response = JsonResponse(status=200)
        else:
            # Error response would contain error types and the following
            # error type is received from edx-video-pipeline whenever we've
            # got invalid credentials for a provider. Its kept this way because
            # edx-video-pipeline doesn't support i18n translations yet.
            error_type = error_response.get('error_type')
            if error_type == TranscriptionProviderErrorType.INVALID_CREDENTIALS:
                error_message = _('The information you entered is incorrect.')

            response = JsonResponse({'error': error_message}, status=400)

    return response
Пример #13
0
def transcript_preferences_handler(request, course_key_string):
    """
    JSON view handler to post the transcript preferences.

    Arguments:
        request: WSGI request object
        course_key_string: string for course key

    Returns: valid json response or 400 with error message
    """
    course_key = CourseKey.from_string(course_key_string)
    is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(
        course_key)
    if not is_video_transcript_enabled:
        return HttpResponseNotFound()
    if request.method == 'POST':
        data = request.json
        provider = data.get('provider')
        error, preferences = validate_transcript_preferences(
            provider=provider,
            cielo24_fidelity=data.get('cielo24_fidelity', ''),
            cielo24_turnaround=data.get('cielo24_turnaround', ''),
            three_play_turnaround=data.get('three_play_turnaround', ''),
            video_source_language=data.get('video_source_language'),
            preferred_languages=list(
                map(str, data.get('preferred_languages', []))))
        if error:
            response = JsonResponse({'error': error}, status=400)
        else:
            preferences.update({'provider': provider})
            transcript_preferences = create_or_update_transcript_preferences(
                course_key_string, **preferences)
            response = JsonResponse(
                {'transcript_preferences': transcript_preferences}, status=200)

        return response
    elif request.method == 'DELETE':
        remove_transcript_preferences(course_key_string)
        return JsonResponse()
Пример #14
0
def transcript_preferences_handler(request, course_key_string):
    """
    JSON view handler to post the transcript preferences.

    Arguments:
        request: WSGI request object
        course_key_string: string for course key

    Returns: valid json response or 400 with error message
    """
    course_key = CourseKey.from_string(course_key_string)
    is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course_key)
    if not is_video_transcript_enabled:
        return HttpResponseNotFound()

    if request.method == 'POST':
        data = request.json
        provider = data.get('provider')
        error, preferences = validate_transcript_preferences(
            provider=provider,
            cielo24_fidelity=data.get('cielo24_fidelity', ''),
            cielo24_turnaround=data.get('cielo24_turnaround', ''),
            three_play_turnaround=data.get('three_play_turnaround', ''),
            video_source_language=data.get('video_source_language'),
            preferred_languages=data.get('preferred_languages', [])
        )
        if error:
            response = JsonResponse({'error': error}, status=400)
        else:
            preferences.update({'provider': provider})
            transcript_preferences = create_or_update_transcript_preferences(course_key_string, **preferences)
            response = JsonResponse({'transcript_preferences': transcript_preferences}, status=200)

        return response
    elif request.method == 'DELETE':
        remove_transcript_preferences(course_key_string)
        return JsonResponse()
Пример #15
0
def transcript_delete_handler(request, course_key_string, edx_video_id, language_code):
    """
    View to delete a transcript file.

    Arguments:
        request: A WSGI request object
        course_key_string: Course key identifying a course.
        edx_video_id: edX video identifier whose transcript need to be deleted.
        language_code: transcript's language code.

    Returns
        - A 404 if the corresponding feature flag is disabled or user does not have required permisions
        - A 200 if transcript is deleted without any error(s)
    """
    # Check whether the feature is available for this course.
    course_key = CourseKey.from_string(course_key_string)
    video_transcripts_enabled = VideoTranscriptEnabledFlag.feature_enabled(course_key)
    # User needs to have studio write access for this course.
    if not video_transcripts_enabled or not has_studio_write_access(request.user, course_key):
        return HttpResponseNotFound()

    delete_video_transcript(video_id=edx_video_id, language_code=language_code)

    return JsonResponse(status=200)
Пример #16
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)

    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)
Пример #17
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)
Пример #18
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)