"""Generate a SoundRecording resource for TikTok (v38).""" from typing import Any, Optional from soundrecording_utils.constants.ddex import constants from soundrecording_utils.ddex.utils.control_chars import remove_control_chars from soundrecording_utils.ddex.utils.rules import get_fingerprint_rules_by_track_id, get_territory_claims_by_rules from soundrecording_utils.metadata.types import Asset, OrchardSoundRecording, Track from .e import E from .sound_recording_details_by_territory import generate_sound_recording_details_by_territory def _format_track_duration_iso_8601(track_metadata: Track) -> str: """Format a Track's duration.""" return f'PT{track_metadata.duration_minutes}M{track_metadata.duration_seconds}S' def _generate_language_of_performance(track_metadata: Track): """Generate the language of performance node, if required.""" if track_metadata.meta_language_code == 'N/A': return E('IsInstrumental', 'true') if track_metadata.language and track_metadata.language.iso_639_1_code: return E( 'LanguageOfPerformance', track_metadata.language.iso_639_1_code.lower(), ) return None def _generate_subtitle(track_metadata: Track) -> Optional[Any]: """Generate a subtitle field if the Track has a version field.""" if track_metadata.version: return E('SubTitle', track_metadata.version) return None def generate_reference_title(track_metadata: Track): """Generate a ReferenceTitle from Track metadata.""" return E( 'ReferenceTitle', E('TitleText', remove_control_chars(track_metadata.name)), _generate_subtitle(track_metadata), ) def _generate_sound_recording_details_by_territory( sound_recording_metadata: OrchardSoundRecording, primary_track_metadata: Track, upload_asset: Optional[Asset], service: str, takedown: bool = False, ): """Generate SoundRecordingDetailByTerritory node for a sound recording.""" # Extract and format rules on each level for each track if not takedown if not takedown: track_and_rules_by_tuid = get_fingerprint_rules_by_track_id( sound_recording_metadata.track_connection.tracks ) # Extract territory claims for each track tracks_with_claims = get_territory_claims_by_rules(track_and_rules_by_tuid, service) # Find if any territory claims / block otherwise handle takedown claimed_or_blocked_tracks = False for _, v in tracks_with_claims.items(): if len(v['claimed_rules']) or len(v['blocked_rules']): claimed_or_blocked_tracks = True break if claimed_or_blocked_tracks: sr_details = [] territories_to_use = [] for _, v in tracks_with_claims.items(): if len(v['claimed_rules']): territories_to_use += [r['territory'] for r in v['claimed_rules']] if len(v['blocked_rules']): territories_to_use += [r['territory'] for r in v['blocked_rules']] # de-dup and sort territories territories_to_use = list(set(territories_to_use)) territories_to_use.sort() if territories_to_use: sr_details.append( generate_sound_recording_details_by_territory( territories_to_use, primary_track_metadata, constants.DEFAULT_RIGHTS_SHARE_PERCENTAGE, upload_asset, ) ) return sr_details # Handle 'takedown' return [ generate_sound_recording_details_by_territory( [constants.DEFAULT_TERRITORY_CODE], primary_track_metadata, constants.TAKEDOWN_RIGHTS_SHARE_PERCENTAGE, upload_asset, ) ] def generate_sound_recording_resource( resource_reference: str, sound_recording_metadata: OrchardSoundRecording, primary_track_metadata: Track, upload_asset: Optional[Asset], service: str, takedown: bool = False, ): """Generate a SoundRecording resource node.""" return E( 'SoundRecording', E('SoundRecordingType', constants.DEFAULT_SOUND_RECORDING_TYPE), E('SoundRecordingId', E('ISRC', sound_recording_metadata.isrc)), E('ResourceReference', resource_reference), generate_reference_title(primary_track_metadata), _generate_language_of_performance(primary_track_metadata), E('Duration', _format_track_duration_iso_8601(primary_track_metadata)), *_generate_sound_recording_details_by_territory( sound_recording_metadata, primary_track_metadata, upload_asset, service, takedown=takedown, ), )