"""GraphQL utils for DDEX SoundRecording metadata enrichment.""" from gql.transport.exceptions import TransportQueryError, TransportServerError from ... import config from ...common.connectors import graphql_gateway from ...common.exceptions import exceptions TRACKS_QUERY = """ query Track( $isrc: String!) { isrc(isrc: $isrc) { tracks { tracks { tuid trackName labelSoundRecording { label { name } } product { subgenre { genre { name } } } participations { participant { name uuid } participated_as } } } } } """ OSR_SEARCH_BY_ISRC_QUERY = """ query OrchardSoundRecording($isrc: String!) { orchardSoundRecordingSearchByIsrc(term: $isrc) { tracks { items { active isrc primaryTrack tuid } } } } """ def _build_headers(application_name): return { 'apollographql-client-name': application_name, 'apollographql-client-version': '1', 'Orchard-Identity-Id': config.TRACKS_IDENTITY_ID, 'Orchard-Profile-Id': config.TRACKS_PROFILE_ID, 'Orchard-Profile-Type': config.TRACKS_PROFILE_TYPE, 'Cache-Control': 'no-cache' } def _execute_tracks_query(isrc, application_name): """Execute gql tracks query. Args: isrc (str): ISRC of the primary track application_name (str): Name of the application making the request Returns: list: dicts of tracks """ params = {'isrc': isrc} return graphql_gateway.make_request( _build_headers(application_name), TRACKS_QUERY, params ) def _execute_osr_search_by_isrc_query(osr_isrc, application_name): """Execute the OSR search-by-ISRC query. Args: osr_isrc (str): ISRC of the OrchardSoundRecording application_name (str): Name of the application making the request Returns: dict: raw GQL response data """ params = {'isrc': osr_isrc} return graphql_gateway.make_request( _build_headers(application_name), OSR_SEARCH_BY_ISRC_QUERY, params ) def get_primary_track_isrc_and_tuid(osr_isrc, application_name): """Resolve the primary track's ISRC and TUID from an OSR ISRC via GQL. Searches for the OSR by ISRC, then returns the ISRC and TUID of the primary track (falling back to the first active track if none is marked primary). Args: osr_isrc (str): ISRC of the OrchardSoundRecording application_name (str): Name of the application making the request Returns: tuple(str, str) or None: (track_isrc, track_tuid) of the primary track, or None if not resolved """ try: data = _execute_osr_search_by_isrc_query(osr_isrc, application_name) except TransportServerError as e: if e.code in (504, 503, 502): raise exceptions.RetryableException( f'Unexpected response code from GraphQL Gateway HTTP:{e.code}' ) raise except TransportQueryError as e: raise exceptions.RetryableException(str(e)) osr_raw = data.get('orchardSoundRecordingSearchByIsrc') osr = (osr_raw[0] if osr_raw else {}) if isinstance(osr_raw, list) else (osr_raw or {}) tracks = osr.get('tracks') or {} items = tracks.get('items') or [] primary = next((item for item in items if item.get('primaryTrack')), None) active_items = [item for item in items if item.get('active')] chosen = primary or (active_items[0] if active_items else None) if not chosen: return None return chosen.get('isrc'), chosen.get('tuid') def get_track(isrc, primary_tuid, application_name): """Get metadata for the primary track by ISRC, identified by TUID. Queries all tracks sharing the given ISRC, then: 1. Returns the track whose TUID matches ``primary_tuid`` if it has participations. 2. Falls back to the first track with participations if the primary track has none. 3. Returns the primary track (or first track) if no track has participations. Args: isrc (str): ISRC of the primary track primary_tuid (str): TUID of the primary track application_name (str): Name of the application making the request. This value is forwarded to the GraphQL gateway in the ``apollographql-client-name`` header for client identification. Returns: track: track data, or None if no tracks found """ try: data = _execute_tracks_query(isrc, application_name) except TransportServerError as e: if e.code in (504, 503, 502): message_error = f'Unexpected response code from GraphQL Gateway HTTP:{e.code}' # noqa:E501 raise exceptions.RetryableException(message_error) else: raise e except TransportQueryError as e: raise exceptions.RetryableException(str(e)) isrc_data = data.get('isrc') or {} tracks_data = isrc_data.get('tracks') or {} tracks = tracks_data.get('tracks') or [] if not tracks: return None primary = next((t for t in tracks if t.get('tuid') == primary_tuid), None) if primary and primary.get('participations'): return primary fallback = next((t for t in tracks if t.get('participations')), None) return fallback or primary or tracks[0]