""" Patch ownership Logic ===================== This provides the logic layer for calling the YouTube ownership.patch endpoint and processing the results. """ from googleapiclient.errors import HttpError from ytownership import config from ytownership.consts import youtube from ytownership.utils.misc import create_http_error from ytownership.utils.misc import rate_limited from ytownership.utils.misc import retry from ytownership.utils.response import Response OWNER_ORCHARD = 'theorchardmusic' OWNER_ORCHARD_CODE = 'J8vAyKuNSYBIN_9RIdxggQ' WW_FLAG = 'WW' TYPE_EXCLUDE = 'exclude' TYPE_INCLUDE = 'include' RATIO = 100 def patch_ownership(message): """Send patch owner object request to YoutubeContentAPI. Args: message (JSONMessageExt): message pulled from SQS. Returns: Response: result of patch operation. """ response_answer = Response() youtube_client = message.context.youtube_client payload = message.get_body() try: asset_id = get_asset_id(youtube_client, payload['isrc']) ownership = get_ownership(youtube_client, asset_id) update_ownership( youtube_client, ownership, asset_id, payload['territories']) response_answer.message = 'Successful patched.' except HttpError as e: response_answer.status = e.resp.status response_answer.errors = e.content response_answer.message = 'Error received.' message.logger.exception(e) return response_answer @rate_limited(max_per_second=config.API_RATE_LIMIT) def call_youtube_api(api_call_request): """Execute api_call_request. Execute given query to Youtube API. Args: api_call_request (Service request): Composed query to Youtube API. Returns: dict: result of request to Youtube API. """ return api_call_request.execute() @retry() def get_ownership(youtube_client, asset_id): """Seek for ownership by assetId. Args: youtube_client (ServerMapping): Youtube API wrapper object. asset_id (str): assetId of seeking Ownership. Returns: ownership (dict): object of ownership. """ query = youtube_client.ownership().get( assetId=asset_id, onBehalfOfContentOwner=OWNER_ORCHARD) ownership = call_youtube_api(query) return ownership @retry() def update_ownership(youtube_client, ownership, asset_id, territories): """Patching ownership object. Checking input data as territories list. Args: youtube_client (ServiceMapping) : API Wrapper object. ownership (dict): object that need be updated. asset_id (str) : asset ID of ownership object. territories (list) : list of territories for update. Returns: (str) : result of updating. """ # if we do not currently have any ownership on the asset if 'general' not in ownership: ownership['general'] = [{ 'ratio': RATIO, 'owner': OWNER_ORCHARD_CODE }] if len(territories) == 1 and territories[0] == WW_FLAG: ownership['general'][0]['territories'] = [] ownership['general'][0]['type'] = TYPE_EXCLUDE else: ownership['general'][0]['territories'] = territories ownership['general'][0]['type'] = TYPE_INCLUDE patch = { 'general': ownership['general'] } query = youtube_client.ownership().patch( assetId=asset_id, onBehalfOfContentOwner=OWNER_ORCHARD, body=patch ) ownership_updated = call_youtube_api(query) return ownership_updated @retry() def get_asset_id(youtube_client, isrc): """Get an asset ID from the Content ID API by a given isrc. Args: youtube_client (ServiceMapping) : API Wrapper object isrc (str): Specified ISRC to use to find the asset Returns: asset_id (str): Asset ID that matches the given isrc Raises: HttpError: raise if one matching asset was not found """ matching_assets = get_matching_assets( youtube_client, isrc, youtube.MINE ) or get_matching_assets( youtube_client, isrc, youtube.NONE ) matching_assets_length = len(matching_assets) # if only one Orchard owned sound recording found, return it if matching_assets_length == 1: return matching_assets[0].get('id') elif matching_assets_length > 1: # too many of the sound recordings found are Orchard owned raise create_http_error( 404, ('Two or more assets found are or were previously ' 'owned by The Orchard')) raise create_http_error( 404, ('None of the assets found are or were previously ' 'owned by The Orchard')) def get_matching_assets(youtube_client, isrc, ownership_restriction): """Get a list of matching assets from YouTube given an isrc. Args: youtube_client (ServiceMapping) : API Wrapper object isrc (str): Specified ISRCs, by which searching asset ownership_restriction (str): Indicates if API should return assets owned by the Orchard or by anyone Returns: (list): List of asset IDs that match the isrc, and are sound recordings, and have previous ownership if required """ asset_query = youtube_client.assetSearch().list( isrcs=isrc, onBehalfOfContentOwner=OWNER_ORCHARD, ownershipRestriction=ownership_restriction) asset_call = call_youtube_api(asset_query) assets = asset_call.get('items') if not assets: return [] matching_assets = [] for asset in assets: if asset.get('type') == 'sound_recording': asset_id = asset.get('id') # check that the Orchard currently or at one time owned the asset if ownership_restriction == youtube.MINE or \ has_previous_ownership(youtube_client, asset_id): matching_assets.append(asset) return matching_assets def has_previous_ownership(youtube_client, asset_id): """Check if the given asset has previously been owned by the Orchard. Args: youtube_client (ServiceMapping) : API Wrapper object asset_id (str): Asset ID of the matching asset Returns: (boolean): True if previously owned by the Orchard, otherwise False """ ownership_query = youtube_client.ownershipHistory().list( assetId=asset_id, onBehalfOfContentOwner=OWNER_ORCHARD) ownership_history_call = call_youtube_api(ownership_query) ownership_updates = ownership_history_call.get('items', []) for ownership_update in ownership_updates: origination = ownership_update.get('origination', {}) owner = origination.get('owner') if owner == OWNER_ORCHARD_CODE: return True return False