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

Code before:
        'params': {'extractor_args': {'nhkradirulive': {'area': ['fukuoka']}}},
        'info_dict': {
            'id': 'r2-fukuoka',
            'title': 're:^ＮＨＫネットラジオ第2 福岡.+$',
            'ext': 'm4a',
            'thumbnail': 'https://www.nhk.or.jp/common/img/media/r2-200x200.png',
            'live_status': 'is_live',
        },
    }, {
        # fm, area specified
        'url': 'https://www.nhk.or.jp/radio/player/?ch=fm',
        'params': {'extractor_args': {'nhkradirulive': {'area': ['sapporo']}}},
        'info_dict': {
            'id': 'fm-sapporo',
            'title': 're:^ＮＨＫネットラジオＦＭ 札幌.+$',
            'ext': 'm4a',
            'thumbnail': 'https://www.nhk.or.jp/common/img/media/fm-200x200.png',
            'live_status': 'is_live',
        }
    }]

    _NOA_STATION_IDS = {'r1': 'n1', 'r2': 'n2', 'fm': 'n3'}  # lookup table is easier than working it out

    def _real_extract(self, url):
        station = self._match_id(url)
        area = traverse_obj(self._configuration_arg('area'), 0) or "tokyo"

        config = self._download_xml('https://www.nhk.or.jp/radio/config/config_web.xml', None, 'Downloading area information')
        data = config.find(f'.//data//area[.="{area}"]/..')  # a <data> element with an <area> that matches the ^variable

        if data is None:
            raise ExtractorError('Invalid area - valid areas are: %s'
                                 % ', '.join([i.text for i in config.findall('.//data//area')]),
                                 expected=True)

        noa_info = self._download_json(f'https:{config.find(".//url_program_noa").text}'
                                       .replace("{area}", data.find('areakey').text), station)

        present_info = traverse_obj(noa_info, ('nowonair_list', self._NOA_STATION_IDS.get(station), 'present'))
        service = present_info.get('service')

        return {
            'title': join_nonempty(service.get('name'), traverse_obj(present_info, ('area', 'name')), delim=" "),
            'id': join_nonempty(station, area),
            'thumbnails': traverse_obj(service, ('images', ..., {
                                       'url': 'url',
                                       'width': ('width', {int_or_none}),
                                       'height': ('height', {int_or_none}),
                                       })),
            'formats': self._extract_m3u8_formats(data.find(f'{station}hls').text, station),
            'is_live': True,

Review comment (refactor):
you can use `_configuration_arg`'s default param instead of traversal
```suggestion
        area = self._configuration_arg('area', ['tokyo'])[0]
```
also, this needs to be documented in the "Extractor Arguments" section of the readme.

but how does the website handle this? does it determine which area to serve the user based on IP/location? there's nothing in the URL/query that we can use instead of extractor-arg?

Code after:
        'params': {'extractor_args': {'nhkradirulive': {'area': ['fukuoka']}}},
        'info_dict': {
            'id': 'r2-fukuoka',
            'title': 're:^ＮＨＫネットラジオ第2 福岡.+$',
            'ext': 'm4a',
            'thumbnail': 'https://www.nhk.or.jp/common/img/media/r2-200x200.png',
            'live_status': 'is_live',
        },
    }, {
        # fm, area specified
        'url': 'https://www.nhk.or.jp/radio/player/?ch=fm',
        'params': {'extractor_args': {'nhkradirulive': {'area': ['sapporo']}}},
        'info_dict': {
            'id': 'fm-sapporo',
            'title': 're:^ＮＨＫネットラジオＦＭ 札幌.+$',
            'ext': 'm4a',
            'thumbnail': 'https://www.nhk.or.jp/common/img/media/fm-200x200.png',
            'live_status': 'is_live',
        }
    }]

    _NOA_STATION_IDS = {'r1': 'n1', 'r2': 'n2', 'fm': 'n3'}

    def _real_extract(self, url):
        station = self._match_id(url)
        area = self._configuration_arg('area', ['tokyo'])[0]

        config = self._download_xml(
            'https://www.nhk.or.jp/radio/config/config_web.xml', station, 'Downloading area information')
        data = config.find(f'.//data//area[.="{area}"]/..')

        if not data:
            raise ExtractorError('Invalid area. Valid areas are: %s' % ', '.join(
                [i.text for i in config.findall('.//data//area')]), expected=True)

        noa_info = self._download_json(
            f'https:{config.find(".//url_program_noa").text}'.format(area=data.find('areakey').text),
            station, note=f'Downloading {area} station metadata')
        present_info = traverse_obj(noa_info, ('nowonair_list', self._NOA_STATION_IDS.get(station), 'present'))

        return {
            'title': ' '.join(traverse_obj(present_info, (('service', 'area',), 'name', {str}))),
            'id': join_nonempty(station, area),
            'thumbnails': traverse_obj(present_info, ('service', 'images', ..., {
                'url': 'url',
                'width': ('width', {int_or_none}),
                'height': ('height', {int_or_none}),
            })),
            'formats': self._extract_m3u8_formats(data.find(f'{station}hls').text, station),
            'is_live': True,
        }