{"record":{"id":"32d6e085d93691d5","repo":"ytdl-org/youtube-dl","slug":"s-said-s-32d6e0","errorCode":null,"errorMessage":"%s said: %s","messagePattern":"%s said: %s","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/tvplayer.py","lineNumber":73,"sourceCode":"        platform = try_get(\n            context, lambda x: x['platform']['key'], compat_str) or 'firefox'\n\n        try:\n            response = self._download_json(\n                'http://api.tvplayer.com/api/v2/stream/live',\n                display_id, 'Downloading JSON stream', headers={\n                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',\n                }, data=urlencode_postdata({\n                    'id': resource_id,\n                    'service': 1,\n                    'platform': platform,\n                    'validate': validate,\n                }))['tvplayer']['response']\n        except ExtractorError as e:\n            if isinstance(e.cause, compat_HTTPError):\n                response = self._parse_json(\n                    e.cause.read().decode(), resource_id)['tvplayer']['response']\n                raise ExtractorError(\n                    '%s said: %s' % (self.IE_NAME, response['error']), expected=True)\n            raise\n\n        formats = self._extract_m3u8_formats(response['stream'], display_id, 'mp4')\n        self._sort_formats(formats)\n\n        return {\n            'id': resource_id,\n            'display_id': display_id,\n            'title': self._live_title(title),\n            'formats': formats,\n            'is_live': True,\n        }\n","sourceCodeStart":55,"sourceCodeEnd":87,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/tvplayer.py#L55-L87","documentation":"Raised by TVPlayerIE when the stream request to tvplayer.com returns an HTTP error whose JSON body (['tvplayer']['response']['error']) carries an error string. The extractor catches the compat_HTTPError, parses the error payload, and rethrows it as an expected error. Typical payloads are 'bad stream request', geo or account errors.","triggerScenarios":"POSTing the form (id, service=1, platform, validate) to the stream endpoint and receiving an HTTP 4xx/5xx whose body decodes to a tvplayer response with a non-null 'error' key — e.g. 'Channel not available on your account', geo refusal, or invalid resource id.","commonSituations":"UK-only channels requested from outside the UK, free-tier accounts requesting premium channels, dead channel ids after the service rebranded, changed validate token.","solutions":["Match the error text: 'geo' variants need a UK IP; account variants need a TVPlayer subscription plus --username/--password.","Verify the channel still exists on tvplayer.com and copy a fresh URL.","Log in with credentials so the stream request carries your entitlements.","Update to yt-dlp; the TVPlayer service has since shut down/changed, so newer code or a different source is needed."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def tvplayer_error(response: dict) -> bool:\n    return bool((response.get('tvplayer', {}).get('response') or {}).get('error'))","tryCatchPattern":"try:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if 'said:' in str(e):\n        handle_tvplayer_message(str(e).split('said:')[-1].strip())\n    else:\n        raise","preventionTips":["Pass TVPlayer credentials for entitled channels; free accounts get account errors on premium channels.","Use a UK IP for UK-restricted channels."],"tags":["geo","extractor","tvplayer","api-error","account"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}