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

Code before:
        track_id: str = self._match_id(url)
        track_info: dict = self._download_json(f'https://api.idagio.com/v2.0/metadata/tracks/{track_id}',
                                               track_id).get('result')

        content_info: dict = self._download_json(f'https://api.idagio.com/v1.8/content/track/{track_id}?quality=0&format=2&client_type=web-4', track_id)

        work_name = traverse_obj(track_info, ('piece', 'workpart', 'work', 'title'))
        conductor = traverse_obj(track_info, ('recording', 'conductor', 'name'))
        artists = [] if conductor is None else [conductor]

        return {
            'id': str(traverse_obj(track_info, ('id',))),
            'title': work_name + ': ' + traverse_obj(track_info, ('piece', 'title')),
            'url': traverse_obj(content_info, ('url', )),
            'ext': 'mp3',
            'timestamp': traverse_obj(track_info, ('recording', 'created_at')),
            'location': traverse_obj(track_info, ('recording', 'location')),
            'duration': traverse_obj(track_info, ('duration',)),
            'track': work_name + ': ' + traverse_obj(track_info, ('piece', 'title')),
            'track_id': track_id,
            'artists': (artists + (traverse_obj(track_info, ('recording', 'ensembles', ..., 'name')) or [])
                        + (traverse_obj(track_info, ('recording', 'soloists', ..., 'name')) or [])),
            'composers': [traverse_obj(track_info, ('piece', 'workpart', 'work', 'composer', 'name'))],
            'genres': [traverse_obj(track_info, ('piece', 'workpart', 'work', 'genre', 'title')),
                       traverse_obj(track_info, ('piece', 'workpart', 'work', 'subgenre', 'title'))],
        }


class IdagioRecordingIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com/recordings/(?P<id>[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': 1554474370029,
            'modified_timestamp': 1554481570.0,
            'modified_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'))

Review comment (refactor):
The track title should just be the actual track title and not hardcode the album/recording title. If the user wants to include this, they should adjust the output template. Timestamps should be a second based unix timestamps.. All the `traverse_obj` calls can also be combined in to a single call like so:
```suggestion
        return {
            'ext': 'mp3',
            'id': track_id,
            'url': traverse_obj(content_info, ('url', {url_or_none})),
            **traverse_obj(track_info, ('result', {
                'title': ('piece', 'title', {str}),
                'timestamp': ('recording', 'created_at', {int_or_none(scale=1000)}),
                'location': ('recording', 'location', {str}),
                'duration': ('duration', {int_or_none}),
                'track': ('piece', 'title', {str}),
                'artists': ('recording', ('conductor', ('ensembles', ...), ('soloists', ...)), 'name', {str}, all),
                'composers': ('piece', 'workpart', 'work', 'composer', 'name', {str}, all),
                'genres': ('piece', 'workpart', 'work', ('genre', 'subgenre'), 'title', {str}, all),
            })),
        }
```
Similar changes should be applied to the other extractors

Code after:
            track_id, fatal=False, expected_status=406)
        if traverse_obj(track_info, 'error_code') == 'idagio.error.blocked.location':
            self.raise_geo_restricted()

        content_info = self._download_json(
            f'https://api.idagio.com/v1.8/content/track/{track_id}', track_id,
            query={
                'quality': '0',
                'format': '2',
                'client_type': 'web-4',
            })

        return {
            'ext': 'mp3',
            'vcodec': 'none',
            'id': track_id,
            'url': traverse_obj(content_info, ('url', {url_or_none})),
            **traverse_obj(track_info, ('result', {
                'title': ('piece', 'title', {str}),
                'timestamp': ('recording', 'created_at', {int_or_none(scale=1000)}),
                'location': ('recording', 'location', {str}),
                '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),