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

Code before:
                video_id, 'mp4', 'm3u8_native', fatal=False)
        return formats

    def _raise_error(self, data):
        MESSAGES = {
            'ProgramRightsAreNotReady': 'Du kan dessverre ikke se eller høre programmet',
            'ProgramRightsHasExpired': 'Programmet har gått ut',
            'NoProgramRights': 'Ikke tilgjengelig',
            'ProgramIsGeoBlocked': 'NRK har ikke rettigheter til å vise dette programmet utenfor Norge',
        }
        message_type = data.get('messageType', '')
        # Can be ProgramIsGeoBlocked or ChannelIsGeoBlocked*
        if 'IsGeoBlocked' in message_type or try_get(data, lambda x: x['usageRights']['isGeoBlocked']) is True:
            self.raise_geo_restricted(
                msg=MESSAGES.get('ProgramIsGeoBlocked'),
                countries=self._GEO_COUNTRIES)
        message = data.get('endUserMessage') or MESSAGES.get(message_type, message_type)
        raise ExtractorError(f'{self.IE_NAME} said: {message}', expected=True)

    def _call_api(self, path, video_id, item=None, note=None, fatal=True, query=None, headers=None):
        return self._download_json(
            urljoin('https://psapi.nrk.no/', path),
            video_id, note or f'Downloading {item} JSON',
            fatal=fatal, query=query,
            # By default NRK returns an older version of the JSON API with broken streaming URLs. This makes it return a newer version.
            headers=headers or {'Accept': 'application/vnd.nrk.psapi+json; version=9; player=tv-player; device=player-core'})


class NRKIE(NRKBaseIE):
    _VALID_URL = r'''(?x)
                        (?:
                            nrk:|
                            https?://
                                (?:
                                    (?:www\.)?nrk\.no/video/(?:PS\*|[^_]+_)|
                                    v8[-.]psapi\.nrk\.no/mediaelement/
                                )
                            )
                            (?P<id>[^?\#&]+)
                        '''

    _TESTS = [{
        # video
        'url': 'http://www.nrk.no/video/PS*150533',
        'md5': '2b88a652ad2e275591e61cf550887eec',
        'info_dict': {
            'id': '150533',
            'ext': 'mp4',
            'title': 'Dompap og andre fugler i Piip-Show',
            'description': 'md5:d9261ba34c43b61c812cb6b0269a5c8f',
            'duration': 262,

Review comment (suggestion):
```suggestion
            fatal=fatal, query=query, headers={
                # Needed for working stream URLs, see https://github.com/yt-dlp/yt-dlp/issues/12192
                'Accept': 'application/vnd.nrk.psapi+json; version=9; player=tv-player; device=player-core',
            })
```

Code after:
        asset_url = re.sub(r'(?:bw_(?:low|high)=\d+|no_audio_only)&?', '', asset_url)
        formats = self._extract_m3u8_formats(
            asset_url, video_id, 'mp4', 'm3u8_native', fatal=False)
        if not formats and re.search(self._CDN_REPL_REGEX, asset_url):
            formats = self._extract_m3u8_formats(
                re.sub(self._CDN_REPL_REGEX, '://nrk-od-%02d.akamaized.net/no/' % random.randint(0, 99), asset_url),
                video_id, 'mp4', 'm3u8_native', fatal=False)
        return formats

    def _raise_error(self, data):
        MESSAGES = {
            'ProgramRightsAreNotReady': 'Du kan dessverre ikke se eller høre programmet',
            'ProgramRightsHasExpired': 'Programmet har gått ut',
            'NoProgramRights': 'Ikke tilgjengelig',
            'ProgramIsGeoBlocked': 'NRK har ikke rettigheter til å vise dette programmet utenfor Norge',
        }
        message_type = data.get('messageType', '')
        # Can be ProgramIsGeoBlocked or ChannelIsGeoBlocked*
        if 'IsGeoBlocked' in message_type or try_get(data, lambda x: x['usageRights']['isGeoBlocked']) is True:
            self.raise_geo_restricted(
                msg=MESSAGES.get('ProgramIsGeoBlocked'),
                countries=self._GEO_COUNTRIES)
        message = data.get('endUserMessage') or MESSAGES.get(message_type, message_type)
        raise ExtractorError(f'{self.IE_NAME} said: {message}', expected=True)

    def _call_api(self, path, video_id, item=None, note=None, fatal=True, query=None):
        return self._download_json(
            urljoin('https://psapi.nrk.no/', path),
            video_id, note or f'Downloading {item} JSON',
            fatal=fatal, query=query, headers={
                # Needed for working stream URLs, see https://github.com/yt-dlp/yt-dlp/issues/12192
                'Accept': 'application/vnd.nrk.psapi+json; version=9; player=tv-player; device=player-core',
            })


class NRKIE(NRKBaseIE):
    _VALID_URL = r'''(?x)
                        (?:
                            nrk:|
                            https?://
                                (?:
                                    (?:www\.)?nrk\.no/video/(?:PS\*|[^_]+_)|
                                    v8[-.]psapi\.nrk\.no/mediaelement/
                                )
                            )
                            (?P<id>[^?\#&]+)
                        '''

    _TESTS = [{
        # video
        'url': 'http://www.nrk.no/video/PS*150533',