""" 3PlayMedia Transcription Client """ import json import logging import requests import sys from requests.packages.urllib3.exceptions import InsecurePlatformWarning from VEDA_OS01.models import TranscriptProcessMetadata, TranscriptProvider, TranscriptStatus from VEDA.utils import build_url, scrub_query_params requests.packages.urllib3.disable_warnings(InsecurePlatformWarning) LOGGER = logging.getLogger(__name__) class ThreePlayMediaError(Exception): """ An error that occurs during 3PlayMedia actions. """ pass class ThreePlayMediaLanguageNotFoundError(ThreePlayMediaError): """ An error when language is not found in available 3playMedia languages. """ pass class ThreePlayMediaPerformTranscriptionError(ThreePlayMediaError): """ An error occurred while adding media for transcription. """ pass class ThreePlayMediaUrlError(ThreePlayMediaError): """ Occurs when the media url is either inaccessible or of invalid content type. """ pass class ThreePlayMediaLanguagesRetrievalError(ThreePlayMediaError): """ An error Occurred while retrieving available 3PlayMedia languages. """ pass class ThreePlayMediaClient(object): def __init__( self, org, video, media_url, api_key, api_secret, callback_url, turnaround_level, three_play_api_base_url ): """ Initialize 3play media client """ self.org = org self.video = video self.media_url = media_url self.api_key = api_key self.api_secret = api_secret self.callback_url = callback_url self.turnaround_level = turnaround_level # default attributes self.base_url = three_play_api_base_url self.upload_media_file_url = u'files/' self.available_languages_url = u'caption_imports/available_languages/' self.allowed_content_type = u'video/mp4' def validate_media_url(self): """ Validates the media URL Raises: 3PlayMediaUrlError: on invalid media url or content type """ if not self.media_url: raise ThreePlayMediaUrlError('Invalid media URL "{media_url}".'.format(media_url=self.media_url)) response = requests.head(url=self.media_url) if not response.ok: raise ThreePlayMediaUrlError('The URL "{media_url}" is not Accessible.'.format(media_url=self.media_url)) elif response.headers['Content-Type'] != self.allowed_content_type: raise ThreePlayMediaUrlError( 'Media content-type should be "{allowed_type}". URL was "{media_url}", content-type was "{type}"'.format( # pylint: disable=line-too-long allowed_type=self.allowed_content_type, media_url=self.media_url, type=response.headers['Content-Type'], ) ) def get_available_languages(self): """ Gets all the 3Play Media supported languages """ available_languages_url = build_url(self.base_url, self.available_languages_url, apikey=self.api_key) response = requests.get( url=available_languages_url ) if not response.ok: raise ThreePlayMediaLanguagesRetrievalError( 'Error while retrieving available languages: url={url} -- {response} -- {status}'.format( url=scrub_query_params(available_languages_url, ['apikey']), response=response.text, status=response.status_code ) ) # A normal response should be a list containing 3Play Media supported languages and if we're getting a dict, # there must be an error: https://support.3playmedia.com/hc/en-us/articles/227729968-Captions-Imports-API available_languages = json.loads(response.text) if isinstance(available_languages, dict): raise ThreePlayMediaLanguagesRetrievalError( 'Expected 3Play Media Supported languages but got: {response}'.format(response=response.text) ) return available_languages def get_source_language_id(self, languages, source_language_code): """ Extracts language id for a language that matches `source_language_code` from the given 3Play Media languages. Arguments: languages(list): 3PlayMedia supported languages. source_language_code(unicode): A video source language code whose 3Play language id is required. """ for language in languages: if language['iso_639_1_code'] == source_language_code: return language['language_id'] def submit_media(self): """ Submits the media to perform transcription. Raises: ThreePlayMediaPerformTranscriptionError: error while transcription process """ self.validate_media_url() # Prepare requests payload payload = dict( # Mandatory attributes required for transcription link=self.media_url, apikey=self.api_key, api_secret_key=self.api_secret, turnaround_level=self.turnaround_level, callback_url=self.callback_url, ) available_languages = self.get_available_languages() source_language_id = self.get_source_language_id(available_languages, self.video.source_language) if source_language_id: payload['language_id'] = source_language_id upload_url = build_url(self.base_url, self.upload_media_file_url) response = requests.post(url=upload_url, json=payload) if not response.ok: raise ThreePlayMediaPerformTranscriptionError( 'Upload file request failed with: {response} -- {status}'.format( response=response.text, status=response.status_code ) ) # A normal response should be a text containing file id and if we're getting a deserializable dict, there # must be an error: http://support.3playmedia.com/hc/en-us/articles/227729828-Files-API-Methods if isinstance(json.loads(response.text), dict): raise ThreePlayMediaPerformTranscriptionError( 'Expected file id but got: {response}'.format(response=response.text) ) return response.text def generate_transcripts(self): """ Kicks off transcription process for default language. """ try: file_id = self.submit_media() # Track progress of transcription process TranscriptProcessMetadata.objects.create( video=self.video, process_id=file_id, lang_code=self.video.source_language, provider=TranscriptProvider.THREE_PLAY, status=TranscriptStatus.IN_PROGRESS, ) # Successfully kicked off transcription process for a video with the given language. LOGGER.info( '[3PlayMedia] Transcription process has been started for video=%s, source_language=%s.', self.video.studio_id, self.video.source_language, ) except ThreePlayMediaError: LOGGER.exception( '[3PlayMedia] Could not process transcripts for video=%s source_language=%s.', self.video.studio_id, self.video.source_language, ) except Exception: LOGGER.exception( '[3PlayMedia] Unexpected error while transcription for video=%s source_language=%s.', self.video.studio_id, self.video.source_language, ) raise