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

Code before:
                'ext': 'mp3',
                'url': f'https://loopstream01.apa.at/?channel={loop_station}&id={item_id}',
                '_old_archive_ids': [make_archive_id(old_ie, video_id)],
                'title': data.get('title'),
                'description': clean_html(data.get('subtitle')),
                'duration': try_call(lambda: (info['end'] - info['start']) / 1000),
                'timestamp': int_or_none(info.get('start'), scale=1000),
                'series': data.get('programTitle'),
            }

    def _real_extract(self, url):
        station, station2, show_date, show_id = self._match_valid_url(url).group('station', 'station2', 'date', 'show')
        api_station, _, _ = self.STATION_INFO[station or station2]
        data = self._download_json(
            f'http://audioapi.orf.at/{api_station}/api/json/current/broadcast/{show_id}/{show_date}', show_id)

        return self.playlist_result(
            self._entries(data, station or station2), show_id, data.get('title'), clean_html(data.get('subtitle')))


class ORFPodcastIE(InfoExtractor):
    IE_NAME = 'orf:podcast'

    _STATION_RE = 'noe|wie|bgl|ooe|stm|ktn|sbg|tir|vbg|oe3|oe1|tv'

    _VALID_URL = f'https?://sound.orf.at/podcast/(?P<station>{_STATION_RE})/(?P<show>[a-zA-Z0-9_-]+)/(?P<id>[a-zA-Z0-9_-]+)'

    _TESTS = [{
        'url': 'https://sound.orf.at/podcast/oe3/fruehstueck-bei-mir/nicolas-stockhammer-15102023',
        'md5': '526a5700e03d271a1505386a8721ab9b',
        'info_dict': {
            'id': 'nicolas-stockhammer-15102023',
            'ext': 'mp3',
            'title': 'Nicolas Stockhammer (15.10.2023)',
            'duration': 3396.0,
            'series': 'Frühstück bei mir',
        },
        'skip': 'ORF podcasts are only available for a limited time'
    }]

    def _real_extract(self, url):
        station, show, show_id = self._match_valid_url(url).group('station', 'show', 'id')
        data = self._download_json(
            f'https://audioapi.orf.at/radiothek/api/2.0/podcast/{station}/{show}/{show_id}', show_id)

        return {
            'id': show_id,
            'url': data.get('payload').get('enclosures')[0]['url'],
            'ext': 'mp3',
            'title': data.get('payload').get('title'),
            'description': clean_html(data.get('payload').get('description')),

Review comment (suggestion):
```suggestion
    _VALID_URL = rf'https?://sound\.orf\.at/podcast/(?P<station>{_STATION_RE})/(?P<show>[a-zA-Z0-9_-]+)/(?P<id>[a-zA-Z0-9_-]+)'
```

Code after:
            yield {
                'id': video_id,
                'ext': 'mp3',
                'url': f'https://loopstream01.apa.at/?channel={loop_station}&id={item_id}',
                '_old_archive_ids': [make_archive_id(old_ie, video_id)],
                'title': data.get('title'),
                'description': clean_html(data.get('subtitle')),
                'duration': try_call(lambda: (info['end'] - info['start']) / 1000),
                'timestamp': int_or_none(info.get('start'), scale=1000),
                'series': data.get('programTitle'),
            }

    def _real_extract(self, url):
        station, station2, show_date, show_id = self._match_valid_url(url).group('station', 'station2', 'date', 'show')
        api_station, _, _ = self.STATION_INFO[station or station2]
        data = self._download_json(
            f'http://audioapi.orf.at/{api_station}/api/json/current/broadcast/{show_id}/{show_date}', show_id)

        return self.playlist_result(
            self._entries(data, station or station2), show_id, data.get('title'), clean_html(data.get('subtitle')))


class ORFPodcastIE(InfoExtractor):
    IE_NAME = 'orf:podcast'
    _STATION_RE = '|'.join(map(re.escape, (
        'bgl', 'fm4', 'ktn', 'noe', 'oe1', 'oe3',
        'ooe', 'sbg', 'stm', 'tir', 'tv', 'vbg', 'wie')))
    _VALID_URL = rf'https?://sound\.orf\.at/podcast/(?P<station>{_STATION_RE})/(?P<show>[\w-]+)/(?P<id>[\w-]+)'
    _TESTS = [{
        'url': 'https://sound.orf.at/podcast/oe3/fruehstueck-bei-mir/nicolas-stockhammer-15102023',
        'md5': '526a5700e03d271a1505386a8721ab9b',
        'info_dict': {
            'id': 'nicolas-stockhammer-15102023',
            'ext': 'mp3',
            'title': 'Nicolas Stockhammer (15.10.2023)',
            'duration': 3396.0,
            'series': 'Frühstück bei mir',
        },
        'skip': 'ORF podcasts are only available for a limited time'
    }]

    def _real_extract(self, url):
        station, show, show_id = self._match_valid_url(url).group('station', 'show', 'id')
        data = self._download_json(
            f'https://audioapi.orf.at/radiothek/api/2.0/podcast/{station}/{show}/{show_id}', show_id)

        return {
            'id': show_id,
            'ext': 'mp3',
            'vcodec': 'none',
            **traverse_obj(data, ('payload', {