Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/idagio.py (Python)

Code before:
            })),
        }


class IdagioRecordingIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com/recordings/(?P<id>[0-9]+)($|\?(?!trackId=[0-9]+))'
    _TESTS = [{
        'url': 'https://app.idagio.com/recordings/30576934',
        'info_dict': {
            'id': '30576934',
            'title': 'Variations on an Original Theme op. 36',
            'composers': ['Edward Elgar'],
            'artists': ['Vasily Petrenko', 'Royal Liverpool Philharmonic Orchestra'],
            'genres': ['Orchestral', 'Other Orchestral Music'],
            'timestamp': 1554474370,
            'modified_timestamp': 1554474370,
            'modified_date': '20190405',
            'upload_date': '20190405',
        },
        'playlist_count': 15,
    }]

    def _real_extract(self, url):
        recording_id: str = self._match_id(url)
        recording_info: dict = self._download_json(f'https://api.idagio.com/v2.0/metadata/recordings/{recording_id}',
                                                   recording_id).get('result')

        track_ids: list[int] = traverse_obj(recording_info, ('tracks', ..., 'id'))

        return {
            '_type': 'multi_video',
            'ext': 'mp3',
            'id': recording_id,
            'entries': [self.url_result(f'https://app.idagio.com/recordings/{recording_id}?trackId={track_id}',
                                        ie='IdagioTrack', video_id=track_id, track_number=i)
                        for i, track_id in enumerate(track_ids, start=1)],
            **traverse_obj(recording_info, {
                'title': ('work', 'title', {str}),
                'timestamp': ('created_at', {int_or_none(scale=1000)}),
                'modified_timestamp': ('created_at', {int_or_none(scale=1000)}),
                'location': ('location', {str}),
                'artists': (('conductor', ('ensembles', ...), ('soloists', ...)), 'name', {str}, all),
                'composers': ('work', 'composer', 'name', {str}, all),
                'genres': ('work', ('genre', 'subgenre'), 'title', {str}, all),
                'tags': ('tags', ..., {str}),
            }),
        }


class IdagioAlbumIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com/albums/(?P<id>[a-zA-Z0-9\-]+)'

Review comment (suggestion):
If the recording info is mandatory, we can make this fatal
```suggestion
        recording_id = self._match_id(url)
        recording_info = self._download_json(
            f'https://api.idagio.com/v2.0/metadata/recordings/{recording_id}',
            recording_id)['result']
```

Code after:
                'duration': ('duration', {int_or_none}),
                'track': ('piece', 'title', {str}),
                'artists': ('recording', ('conductor', ('ensembles', ...), ('soloists', ...)), 'name', {str}, filter),
                'composers': ('piece', 'workpart', 'work', 'composer', 'name', {str}, filter, all, filter),
                'genres': ('piece', 'workpart', 'work', ('genre', 'subgenre'), 'title', {str}, filter),
            })),
        }


class IdagioPlaylistBaseIE(InfoExtractor):
    """Subclasses must set _API_URL_TMPL and define _parse_playlist_metadata"""
    _PLAYLIST_ID_KEY = 'id'  # vs. 'display_id'

    def _entries(self, playlist_info):
        for track_data in traverse_obj(playlist_info, ('tracks', lambda _, v: v['id'] and v['recording']['id'])):
            track_id = track_data['id']
            recording_id = track_data['recording']['id']
            yield self.url_result(
                f'https://app.idagio.com/recordings/{recording_id}?trackId={track_id}',
                ie=IdagioTrackIE, video_id=track_id)

    def _real_extract(self, url):
        playlist_id = self._match_id(url)
        playlist_info = self._download_json(
            self._API_URL_TMPL.format(playlist_id), playlist_id)['result']

        return {
            '_type': 'playlist',
            self._PLAYLIST_ID_KEY: playlist_id,
            'entries': self._entries(playlist_info),
            **self._parse_playlist_metadata(playlist_info),
        }


class IdagioRecordingIE(IdagioPlaylistBaseIE):
    _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com/recordings/(?P<id>\d+)(?![^#]*[&?]trackId=\d+)'
    _TESTS = [{
        'url': 'https://app.idagio.com/recordings/30576934',
        'info_dict': {
            'id': '30576934',
            'title': 'Variations on an Original Theme op. 36',
            'composers': ['Edward Elgar'],
            'artists': ['Vasily Petrenko', 'Royal Liverpool Philharmonic Orchestra'],
            'genres': ['Orchestral', 'Other Orchestral Music'],
            'timestamp': 1554474370,
            'modified_timestamp': 1554474370,
            'modified_date': '20190405',
            'upload_date': '20190405',
        },
        'playlist_count': 15,
    }]