def create_video_transcript(video_id, language_code, file_format, content, provider=TranscriptProviderType.CUSTOM): """ Create a video transcript. Arguments: video_id(unicode): An Id identifying the Video data model object. language_code(unicode): A language code. file_format(unicode): Transcript file format. content(InMemoryUploadedFile): Transcript content. provider(unicode): Transcript provider (it will be 'custom' by default if not selected). """ transcript_serializer = TranscriptSerializer( data=dict(provider=provider, language_code=language_code, file_format=file_format), context=dict(video_id=video_id), ) if transcript_serializer.is_valid(): transcript_serializer.save(content=content) return transcript_serializer.data raise ValCannotCreateError(transcript_serializer.errors)
def create_profile(profile_name): """ Used to create Profile objects in the database A profile needs to exists before an EncodedVideo object can be created. Args: profile_name (str): ID of the profile Raises: ValCannotCreateError: Raised if the profile name is invalid or exists """ try: profile = Profile(profile_name=profile_name) profile.full_clean() profile.save() except ValidationError as err: raise ValCannotCreateError(err.message_dict)
def create_video(video_data): """ Called on to create Video objects in the database create_video is used to create Video objects whose children are EncodedVideo objects which are linked to Profile objects. This is an alternative to the HTTP requests so it can be used internally. The VideoSerializer is used to deserialize this object. If there are duplicate profile_names, the entire creation will be rejected. If the profile is not found in the database, the video will not be created. Args: video_data (dict): { url: api url to the video edx_video_id: ID of the video duration: Length of video in seconds client_video_id: client ID of video encoded_video: a list of EncodedVideo dicts url: url of the video file_size: size of the video in bytes profile: ID of the profile courses: Courses associated with this video image: poster image file name for a particular course } Raises: Raises ValCannotCreateError if the video cannot be created. Returns the successfully created Video object """ serializer = VideoSerializer(data=video_data) if serializer.is_valid(): serializer.save() return video_data.get("edx_video_id") else: raise ValCannotCreateError(serializer.errors)
def import_from_xml(xml, edx_video_id, course_id=None): """ Imports data from a video_asset element about the given video_id. If the edx_video_id already exists, then no changes are made. If an unknown profile is referenced by an encoded video, that encoding will be ignored. Arguments: xml (Element): An lxml video_asset element containing import data edx_video_id (str): val video id course_id (str): The ID of a course to associate the video with Raises: ValCannotCreateError: if there is an error importing the video """ if xml.tag != 'video_asset': raise ValCannotCreateError('Invalid XML') # if edx_video_id does not exist then create video transcripts only if not edx_video_id: return create_transcript_objects(xml) # If video with edx_video_id already exists, associate it with the given course_id. try: video = Video.objects.get(edx_video_id=edx_video_id) logger.info( "edx_video_id '%s' present in course '%s' not imported because it exists in VAL.", edx_video_id, course_id, ) if course_id: course_video, __ = CourseVideo.get_or_create_with_validation( video=video, course_id=course_id) image_file_name = xml.get('image', '').strip() if image_file_name: VideoImage.create_or_update(course_video, image_file_name) # import transcripts create_transcript_objects(xml) return except ValidationError as err: logger.exception(err.message) raise ValCannotCreateError(err.message_dict) except Video.DoesNotExist: pass # Video with edx_video_id did not exist, so create one from xml data. data = { 'edx_video_id': edx_video_id, 'client_video_id': xml.get('client_video_id'), 'duration': xml.get('duration'), 'status': 'imported', 'encoded_videos': [], 'courses': [{ course_id: xml.get('image') }] if course_id else [], } for encoded_video_el in xml.iterfind('encoded_video'): profile_name = encoded_video_el.get('profile') try: Profile.objects.get(profile_name=profile_name) except Profile.DoesNotExist: logger.info( "Imported edx_video_id '%s' contains unknown profile '%s'.", edx_video_id, profile_name) continue data['encoded_videos'].append({ 'profile': profile_name, 'url': encoded_video_el.get('url'), 'file_size': encoded_video_el.get('file_size'), 'bitrate': encoded_video_el.get('bitrate'), }) create_video(data) create_transcript_objects(xml)
def import_from_xml(xml, edx_video_id, resource_fs, static_dir, external_transcripts=None, course_id=None): """ Imports data from a video_asset element about the given video_id. If the edx_video_id already exists, then no changes are made. If an unknown profile is referenced by an encoded video, that encoding will be ignored. Arguments: xml (Element): An lxml video_asset element containing import data edx_video_id (str): val video id resource_fs (OSFS): Import file system. static_dir (str): The Directory to retrieve transcript file. external_transcripts : A dict containing the list of names of the external transcripts. course_id: course id Example: { 'en': ['The_Flash.srt', 'Harry_Potter.srt'], 'es': ['Green_Arrow.srt'] } course_id (str): The ID of a course to associate the video with Raises: ValCannotCreateError: if there is an error importing the video Returns: edx_video_id (str): val video id. """ if external_transcripts is None: external_transcripts = {} if xml.tag != 'video_asset': raise ValCannotCreateError('Invalid XML') # If video with edx_video_id already exists, associate it with the given course_id. try: if not edx_video_id: raise Video.DoesNotExist video = Video.objects.get(edx_video_id=edx_video_id) logger.info( "edx_video_id '%s' present in course '%s' not imported because it exists in VAL.", edx_video_id, course_id, ) # We don't want to link an existing video to course if its an external video. # External videos do not have any playback profiles associated, these are just to track video # transcripts for those video components who do not use edx hosted videos for playback. if course_id and video.status != EXTERNAL_VIDEO_STATUS: course_video, __ = CourseVideo.get_or_create_with_validation(video=video, course_id=course_id) image_file_name = xml.get('image', '').strip() if image_file_name: VideoImage.create_or_update(course_video, image_file_name) # Make sure transcripts are imported when video exists create_transcript_objects( xml, edx_video_id, resource_fs, static_dir, external_transcripts ) return edx_video_id except ValidationError as err: logger.exception(xml) raise ValCannotCreateError(err.message_dict) from err except Video.DoesNotExist: pass if edx_video_id: # Video with edx_video_id did not exist, so create one from xml data. data = { 'edx_video_id': edx_video_id, 'client_video_id': xml.get('client_video_id'), 'duration': xml.get('duration'), 'status': 'imported', 'encoded_videos': [], 'courses': [{course_id: xml.get('image')}] if course_id else [], } for encoded_video_el in xml.iterfind('encoded_video'): profile_name = encoded_video_el.get('profile') try: Profile.objects.get(profile_name=profile_name) except Profile.DoesNotExist: logger.info( "Imported edx_video_id '%s' contains unknown profile '%s'.", edx_video_id, profile_name ) continue data['encoded_videos'].append({ 'profile': profile_name, 'url': encoded_video_el.get('url'), 'file_size': encoded_video_el.get('file_size'), 'bitrate': encoded_video_el.get('bitrate'), }) if not data['encoded_videos']: # Video's status does not get included in video xml at the time of export. So, at this point, # we cannot tell from xml that whether a video had an external status. But if encoded videos # are not set, the chances are, the video was an external one, in which case, we will not link # it to the course(s). Even if the video wasn't an external one and it is having 0 encodes in # xml, it does not have a side effect if not linked to a course, since the video was already # non-playable. data['status'] = EXTERNAL_VIDEO_STATUS data['courses'] = [] # Create external video if no edx_video_id. edx_video_id = create_video(data) else: edx_video_id = create_external_video('External Video') create_transcript_objects(xml, edx_video_id, resource_fs, static_dir, external_transcripts) return edx_video_id