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

Code before:
        'expected_warnings': ['Failed to download extended metadata: HTTP Error 404: Not Found'],
    }]

    _API_URL_TMPL = None

    # the following few functions are ported from https://www.nhk.or.jp/radio/assets/js/timetable_detail_new.js

    def _format_actlist(self, act_list):
        role_groups = {}
        for act in act_list:
            role = act.get('role')
            if role not in role_groups:
                role_groups[role] = []
            role_groups[role].append(act)

        formatted_roles = []
        for role, acts in role_groups.items():
            for i, act in enumerate(acts):
                res = f'【{role}】' if i == 0 and role is not None else ''
                if title := act.get('title'):
                    res += f'{title}…'
                res += act.get('name')
                formatted_roles.append(res)
        return join_nonempty(*formatted_roles, delim='，')

    def _fetch_artists(self, by_artist):
        if not by_artist or len(by_artist) == 0:
            return None
        artists = []
        for artist in by_artist:
            res = ''

            name = artist.get('name')
            role = artist.get('role')
            part = artist.get('part')

            if role != '':
                res += f'{role}…'
            if part != '':
                res += f'（{part}）'
            res += name

            if res != '':
                artists.append(res)
        if len(artists) == 0:
            return None
        return '、'.join(artists)

    def _fetch_duration(self, duration):
        d = parse_duration(duration)
        if d is None:

Review comment (suggestion):
Typically `_fetch` in a method name implies a network request is being made. Let's use `_get` (or `_make` or `_parse`)
```suggestion
    def _get_artists(self, by_artist):
```

Code after:

        },
        'expected_warnings': ['Failed to download extended metadata: HTTP Error 404: Not Found'],
    }]

    _API_URL_TMPL = None

    # The `_format_*` and `_make_*` functions are ported from: https://www.nhk.or.jp/radio/assets/js/timetable_detail_new.js

    def _format_act_list(self, act_list):
        role_groups = {}
        for act in traverse_obj(act_list, (..., {dict})):
            role = act.get('role')
            if role not in role_groups:
                role_groups[role] = []
            role_groups[role].append(act)

        formatted_roles = []
        for role, acts in role_groups.items():
            for i, act in enumerate(acts):
                res = f'【{role}】' if i == 0 and role is not None else ''
                if title := act.get('title'):
                    res += f'{title}…'
                formatted_roles.append(join_nonempty(res, act.get('name'), delim=''))
        return join_nonempty(*formatted_roles, delim='，')

    def _make_artists(self, track, key):
        artists = []
        for artist in traverse_obj(track, (key, ..., {dict})):
            if res := join_nonempty(*traverse_obj(artist, ((
                ('role', filter, {'{}…'.format}),
                ('part', filter, {'（{}）'.format}),
                ('name', filter),
            ), {str})), delim=''):
                artists.append(res)

        return '、'.join(artists) or None

    def _make_duration(self, track, key):
        d = traverse_obj(track, (key, {parse_duration}))
        if d is None:
            return None
        hours, remainder = divmod(d, 3600)
        minutes, seconds = divmod(remainder, 60)
        res = '（'
        if hours > 0:
            res += f'{int(hours)}時間'
        if minutes > 0:
            res += f'{int(minutes)}分'
        res += f'{int(seconds):02}秒）'
        return res