{"record":{"id":"24c7efed7d67b524","repo":"ytdl-org/youtube-dl","slug":"s-said-s-24c7ef","errorCode":null,"errorMessage":"%s said: %s","messagePattern":"%s said: %s","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/tvnow.py","lineNumber":213,"sourceCode":"\n    def _real_extract(self, url):\n        mobj = re.match(self._VALID_URL, url)\n        base_url = re.sub(r'(?:shows|serien)', '_', mobj.group('base_url'))\n        show, episode = mobj.group('show', 'episode')\n        return self.url_result(\n            # Rewrite new URLs to the old format and use extraction via old API\n            # at api.tvnow.de as a loophole for bypassing premium content checks\n            '%s/%s/%s' % (base_url, show, episode),\n            ie=TVNowIE.ie_key(), video_id=mobj.group('id'))\n\n\nclass TVNowNewBaseIE(InfoExtractor):\n    def _call_api(self, path, video_id, query={}):\n        result = self._download_json(\n            'https://apigw.tvnow.de/module/' + path, video_id, query=query)\n        error = result.get('error')\n        if error:\n            raise ExtractorError(\n                '%s said: %s' % (self.IE_NAME, error), expected=True)\n        return result\n\n\nr\"\"\"\nTODO: new apigw.tvnow.de based version of TVNowIE. Replace old TVNowIE with it\nwhen api.tvnow.de is shut down. This version can't bypass premium checks though.\nclass TVNowIE(TVNowNewBaseIE):\n    _VALID_URL = r'''(?x)\n                    https?://\n                        (?:www\\.)?tvnow\\.(?:de|at|ch)/\n                        (?:shows|serien)/[^/]+/\n                        (?:[^/]+/)+\n                        (?P<display_id>[^/?$&]+)-(?P<id>\\d+)\n                    '''\n\n    _TESTS = [{\n        # episode with annual navigation","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/tvnow.py#L195-L231","documentation":"Raised by TVNowNewBaseIE._call_api when the apigw.tvnow.de endpoint returns a JSON payload containing a non-empty 'error' key. It is a passthrough of the provider's own error text (e.g. 'not entitled', 'expired token'), tagged with the extractor name. Expected error: the message comes from the service, not from a parse failure.","triggerScenarios":"Any _download_json('https://apigw.tvnow.de/module/<path>', ...) whose response object has result['error'] set — e.g. requesting module/teaser or module/player with an invalid/expired item id, missing entitlement, or a stale session token in query.","commonSituations":"Using the newer tvnow.de URL scheme (routes handled by TVNowNewBaseIE subclasses), expired signed URLs, region restrictions reported as generic API errors, API contract changes after apigw rollout.","solutions":["Read the '%s said:' payload — the provider text (e.g. 'Forbidden', 'not found') identifies the real cause.","Verify the video id/URL opens on the tvnow website in the same region.","Pass --cookies from a logged-in browser if the error indicates missing entitlement.","Upgrade to yt-dlp for the maintained implementation of the apigw backend."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import requests\nr = requests.get('https://apigw.tvnow.de/module/' + path, params=query)\nif r.json().get('error'):\n    handle_provider_error(r.json()['error'])","typeGuard":"def tvnow_api_error(result: dict) -> bool:\n    return bool(result.get('error'))","tryCatchPattern":"try:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if 'said:' in str(e):\n        log_provider_message(str(e))  # text after 'said:' is the provider's own error\n    else:\n        raise","preventionTips":["Parse the provider text after 'said:' to classify entitlement vs. not-found vs. token expiry.","Cache module/ API responses during development to avoid tripping rate limits."],"tags":["api-error","extractor","tvnow","passthrough"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}