{"record":{"id":"3f6933b1caf0ae54","repo":"ytdl-org/youtube-dl","slug":"not-a-video-or-an-audio","errorCode":null,"errorMessage":"not a video or an audio","messagePattern":"not a video or an audio","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/trilulilu.py","lineNumber":63,"sourceCode":"        },\n    }]\n\n    def _real_extract(self, url):\n        display_id = self._match_id(url)\n        media_info = self._download_json('http://m.trilulilu.ro/%s?format=json' % display_id, display_id)\n\n        age_limit = 0\n        errors = media_info.get('errors', {})\n        if errors.get('friends'):\n            raise ExtractorError('This video is private.', expected=True)\n        elif errors.get('geoblock'):\n            raise ExtractorError('This video is not available in your country.', expected=True)\n        elif errors.get('xxx_unlogged'):\n            age_limit = 18\n\n        media_class = media_info.get('class')\n        if media_class not in ('video', 'audio'):\n            raise ExtractorError('not a video or an audio')\n\n        user = media_info.get('user', {})\n\n        thumbnail = media_info.get('cover_url')\n        if thumbnail:\n            thumbnail.format(width='1600', height='1200')\n\n        # TODO: get correct ext for audio files\n        stream_type = media_info.get('stream_type')\n        formats = [{\n            'url': media_info['href'],\n            'ext': stream_type,\n        }]\n        if media_info.get('is_hd'):\n            formats.append({\n                'format_id': 'hd',\n                'url': media_info['hrefhd'],\n                'ext': stream_type,","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/trilulilu.py#L45-L81","documentation":"Raised by TriluliluExtractor when media_info['class'] is neither 'video' nor 'audio' — the id resolves to some other content type (e.g. an image gallery). Unlike the sibling errors, this one is NOT marked expected=True, so it surfaces as a generic extraction error.","triggerScenarios":"A trilulilu.ro URL pointing at an image or other non-A/V item: the JSON fetch succeeds, but 'class' is e.g. 'image', so the extractor refuses to build formats.","commonSituations":"Trilulilu was a general media host (photos + video); passing a photo page URL to a video downloader; URLs harvested without checking content type.","solutions":["Check the item type on the website; if it is an image, download it directly with curl/wget instead of youtube-dl","Ensure you are passing a media (video/audio) URL and not a profile or gallery page","Catch ExtractorError and skip non-A/V ids in crawlers","Update to yt-dlp where the site is either fixed or removed"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"info = json.load(urllib.request.urlopen('http://m.trilulilu.ro/%s?format=json' % display_id))\nif info.get('class') not in ('video', 'audio'):\n    handle_non_av(display_id, info.get('class'))","typeGuard":null,"tryCatchPattern":"try:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if 'not a video or an audio' in str(e):\n        download_as_image(url)  # fall back to direct HTTP for photos","preventionTips":["Check the media class via the JSON API before invoking the extractor","Do not feed gallery/profile URLs to a video downloader"],"tags":["extractor","content-type","not-expected"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}