""" Asset Search Logic ================= This provides the logic layer for calling the YouTube asset search endpoint and processing the results. """ from labelaudit.connectors import youtube_api OWNER_ORCHARD = 'theorchardmusic' OWNER_IODA = 'ioda' OWNER_RESTRICTION_MINE = 'mine' OWNER_RESTRICTION_NONE = 'none' ASSETS_LIST_MAX_IDS = 50 # Assets.list lets us pass a max of 50 ids per call def search_by_upc(upc, content_owner, restrict_mine, page_token=None): """Perform an AssetSearch:list call to the YouTube API for the given UPC. Args: upc (str): UPC to search for content_owner (str): Name of the YouTube content owner to search on behalf of. restrict_mine (bool): Whether or not to restrict search results to assets that belong to the given content owner. Return: dict: Dictionary containing the search results. Sample return value: { 'kind': 'youtubePartner#assetSnippetList', 'items': [{ 'kind': 'youtubePartner#assetSnippet', 'id': 'A820245425510328', 'type': 'sound_recording', 'title': 'Cigarette', 'isrc': 'GBJVC1500004' }], 'pageInfo': { 'totalResults': 1 } } """ restriction = OWNER_RESTRICTION_MINE if not restrict_mine: restriction = OWNER_RESTRICTION_NONE list_call = youtube_api.mapping.youtube_partner.assetSearch().list( metadataSearchFields='upc:{}'.format(upc), onBehalfOfContentOwner=content_owner, ownershipRestriction=restriction, type='sound_recording', pageToken=page_token) # A Result object doesn't seem necessary, since this function will be used # internally by other logic functions and the output is trivial to parse. return list_call.execute() def list_assets(asset_ids, content_owner): """Performs an Assets.list call to the YouTube API for the given assets. Args: asset_ids (list): YouTube asset IDs to fetchMetadata content_owner (str): Name of the YouTube content owner to search on behalf of Return: dict: Dictionary containing the asset list results. Sample: { 'items': [{ 'metadataMine': { 'customId': '889176217043_MXF551051976_18520096', 'album': 'Coleccion de Reflexiones', 'genre': ['Other'], 'label': 'MultiMusic Mexico', 'isrc': 'MXF551051976', 'upc': '889176217043', 'title': 'Mi Cristo Roto', 'artist': ['Augusto A. Pérez Gutiérrez'] } }] } """ list_call = youtube_api.mapping.youtube_partner.assets().list( id=','.join(asset_ids), fetchMetadata='mine', fields='items/metadataMine', onBehalfOfContentOwner=content_owner) return list_call.execute() def group_my_asset_ids(upc, content_owner): """Fetches paginated AssetSearch.list results and generates groups of asset IDs to be passed into Assets.list calls. This allows us to then filter the resulting assets based on the UPC in our content owner's metadata. Args: upc (str): UPC to search for content_owner (str): Name of the YouTube content owner to search on behalf of Return: generator: Generator that yields lists of 50 asset IDs at a time """ asset_ids = [] page_token = None should_search = True while should_search: search_results = search_by_upc(upc, content_owner, True, page_token) for search_result in search_results.get('items', []): asset_ids.append(search_result.get('id')) if len(asset_ids) == ASSETS_LIST_MAX_IDS: yield asset_ids asset_ids = [] page_token = search_results.get('nextPageToken') if not page_token: should_search = False # yield any leftover IDs now that there are no more search results if asset_ids: yield asset_ids def fetch_my_assets(upc, content_owner): """Fetches all assets for which the metadata provided by our content owner includes the UPC for our release. First we get the grouped asset IDs of all the assets that match the given UPC and are owned by the given content owner. We then need to filter those assets to remove any assets where we have ownership but the UPC actually matched on metadata provided by another content owner for the same asset. Yeah, it's kind of confusing, but that does happen. In those cases, we do not want to consider the asset a "my assets" match for this UPC. So we do an Assets.list call and specify fetchMetadata:mine to get the metadata that our content owner provided to YouTube. Then we filter the results to include only assets for which the UPC from our content owner matches the UPC that we searched for. Args: upc (str): UPC to search for content_owner (str): Name of the YouTube content owner to search on behalf of Return: generator: Generator that yields individual item entries from Assets.list (see list_assets for formatting of items). """ for asset_ids in group_my_asset_ids(upc, content_owner): assets_result = list_assets(asset_ids, content_owner) for asset in assets_result.get('items') or []: metadata = asset.get('metadataMine') if metadata.get('upc') == upc: yield asset def count_my_assets(upc, content_owner): """Gets a running count of all the "my assets" matches for the given UPC and content owner. For the audit doc stage, we will need the individual asset items from fetch_my_assets, but for the prep doc stage we only need the count of assets. Args: upc (str): UPC to search for content_owner (str): Name of the YouTube content owner to search on behalf of Return: int: Total count of filtered assets """ asset_count = 0 for asset in fetch_my_assets(upc, content_owner): asset_count += 1 return asset_count