Repository: yt-dlp/yt-dlp

Issue:
Fix 91porn.com
### Checklist

- [X] I'm reporting a broken site
- [X] I've verified that I'm running yt-dlp version **2022.03.08.1**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- [X] I've checked that all provided URLs are alive and playable in a browser
- [X] I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/ytdl-org/youtube-dl#video-url-contains-an-ampersand-and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
- [X] I've searched the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues including closed ones. DO NOT post duplicates
- [X] I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
- [X] I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and I'm willing to share it if required

### Region

Indonesia

### Description

Fix 91porn.com since it cant extract title and the fix here https://github.com/ytdl-org/youtube-dl/pull/29876 isn't working. thx

### Verbose log

```shell
E:\scraper and downloader\91porn>youtube-dl -v http://91porn.com/view_video.php?viewkey=051a5a363acfd11e3e69
[debug] Command-line config: ['-v', 'http://91porn.com/view_video.php?viewkey=051a5a363acfd11e3e69']
[debug] User config "C:\Users\X\AppData\Roaming\yt-dlp\config": ['--continue', '--format-sort', 'vbr,abr', '--http-chunk-size', '1M', '--buffer-size', '16K', '--sub-lang', 'english', '--embed-metadata', '--abort-on-unavailable-fragment', '-o', '%(title)s [%(id)s].%(ext)s', '--ffmpeg-location', 'D:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe', '--no-lock']
[debug] Encodings: locale cp1252, fs utf-8, out utf-8, err utf-8, pref cp1252
[debug] ytdl-patched version 2022.03.08.1
[debug] ** The command you are running is not yt-dlp.
[debug] ** Please make bug reports at https://github.com/ytdl-patched/ytdl-patched/issues/new/choose instead.
[debug] Lazy loading extractors is disabled
[debug] Python version 3.9.4 (CPython 64bit) - Windows-10-10.0.19041-SP0
[debug] Checking exe version: "D:\Program Files\ffmpeg\bin\ffmpeg.exe" -bsfs
[debug] Checking exe version: "D:\Program Files\ffmpeg\bin\ffprobe" -bsfs
[debug] exe versions: ffmpeg 2022-03-28-git-5ee198f9aa-essentials_build-www.gyan.dev (setts), ffprobe 2022-03-28-git-5ee198f9aa-essentials_build-www.gyan.dev
[debug] Optional libraries: brotli, certifi, Cryptodome, mutagen, sqlite, websockets
[debug] Proxy map: {}
[debug] [91porn] Extracting URL: http://91porn.com/view_video.php?viewkey=051a5a363acfd11e3e69
[91porn] 051a5a363acfd11e3e69: Downloading webpage
[91porn] [debug] Fetching webpage from http://91porn.com/view_video.php?viewkey=051a5a363acfd11e3e69
ERROR: [91porn] 051a5a363acfd11e3e69: Unable to extract title; please report this issue on  https://github.com/yt-dlp/yt-dlp , filling out the appropriate issue template. Confirm you are on the latest version using  yt-dlp -U
  File "c:\python\python39\lib\site-packages\yt_dlp\extractor\common.py", line 670, in extract
    ie_result = self._real_extract(url)
  File "c:\python\python39\lib\site-packages\yt_dlp\extractor\porn91.py", line 38, in _real_extract
    title = self._search_regex(
  File "c:\python\python39\lib\site-packages\yt_dlp\extractor\common.py", line 1292, in _search_regex
    raise RegexNotFoundError('Unable to extract %s' % _name)


E:\scraper and downloader\91porn>pause
Press any key to continue . . .
```



Discussion:
> [debug] ** The command you are running is not yt-dlp.
[debug] ** Please make bug reports at https://github.com/ytdl-patched/ytdl-patched/issues/new/choose instead.

??
Time to fully change bug_reports_message in ytdl-patched... 
> > [debug] ** The command you are running is not yt-dlp.
> > [debug] ** Please make bug reports at https://github.com/ytdl-patched/ytdl-patched/issues/new/choose instead.
> 
> ??

it's same as yt-dlp except its patched version
ytdl-patched is NOT the same as yt-dlp. It is a fork, like what yt-dlp is to youtube-dl. For this site, they share the same code and so it's fine. But they are not interchangeable in general
I guess it is. So can you fix the title extraction error?
Somewhat hilariously, the page generates an obfuscated `<source>` element client-side, so the link is available just by unescaping the parameter of the obfuscator, as implemented in the linked PR.

The title can be extracted from the `<title>`:
```py
        title = self._html_search_regex(
            r'(?s)<title\b[^>]*>\s*(.+?)\s*(?:Chinese\s+homemade\s+video\s*)?</title', webpage, 'title')
```

I've tweaked the yt-dl PR a bit and will update it. The yt-dlp extractor seems to be identical to the upstream.
Ok, i will wait for the result :))
on which branch the patch is available??
@dirkf any good news??

Resolving patch:
diff --git a/yt_dlp/extractor/porn91.py b/yt_dlp/extractor/porn91.py
--- a/yt_dlp/extractor/porn91.py
+++ b/yt_dlp/extractor/porn91.py
@@ -1,26 +1,48 @@
+import urllib.parse
 from .common import InfoExtractor
 from ..utils import (
-    parse_duration,
+    determine_ext,
     int_or_none,
+    parse_duration,
+    remove_end,
+    unified_strdate,
     ExtractorError,
 )
 
 
 class Porn91IE(InfoExtractor):
     IE_NAME = '91porn'
-    _VALID_URL = r'(?:https?://)(?:www\.|)91porn\.com/.+?\?viewkey=(?P<id>[\w\d]+)'
+    _VALID_URL = r'(?:https?://)(?:www\.|)91porn\.com/view_video.php\?([^#]+&)?viewkey=(?P<id>\w+)'
 
-    _TEST = {
+    _TESTS = [{
         'url': 'http://91porn.com/view_video.php?viewkey=7e42283b4f5ab36da134',
-        'md5': '7fcdb5349354f40d41689bd0fa8db05a',
+        'md5': 'd869db281402e0ef4ddef3c38b866f86',
         'info_dict': {
             'id': '7e42283b4f5ab36da134',
             'title': '18岁大一漂亮学妹，水嫩性感，再爽一次！',
+            'description': 'md5:1ff241f579b07ae936a54e810ad2e891',
             'ext': 'mp4',
             'duration': 431,
+            'upload_date': '20150520',
+            'comment_count': int,
+            'view_count': int,
+            'age_limit': 18,
+        }
+    }, {
+        'url': 'https://91porn.com/view_video.php?viewkey=7ef0cf3d362c699ab91c',
+        'md5': 'f8fd50540468a6d795378cd778b40226',
+        'info_dict': {
+            'id': '7ef0cf3d362c699ab91c',
+            'title': '真实空乘，冲上云霄第二部',
+            'description': 'md5:618bf9652cafcc66cd277bd96789baea',
+            'ext': 'mp4',
+            'duration': 248,
+            'upload_date': '20221119',
+            'comment_count': int,
+            'view_count': int,
             'age_limit': 18,
         }
-    }
+    }]
 
     def _real_extract(self, url):
         video_id = self._match_id(url)
@@ -29,32 +51,45 @@ def _real_extract(self, url):
         webpage = self._download_webpage(
             'http://91porn.com/view_video.php?viewkey=%s' % video_id, video_id)
 
-        if '作为游客，你每天只可观看10个视频' in webpage:
-            raise ExtractorError('91 Porn says: Daily limit 10 videos exceeded', expected=True)
+        if '视频不存在,可能已经被删除或者被举报为不良内容!' in webpage:
+            raise ExtractorError('91 Porn says: Video does not exist', expected=True)
 
-        title = self._search_regex(
-            r'<div id="viewvideo-title">([^<]+)</div>', webpage, 'title')
-        title = title.replace('\n', '')
+        daily_limit = self._search_regex(
+            r'作为游客，你每天只可观看([\d]+)个视频', webpage, 'exceeded daily limit', default=None, fatal=False)
+        if daily_limit:
+            raise ExtractorError(f'91 Porn says: Daily limit {daily_limit} videos exceeded', expected=True)
 
         video_link_url = self._search_regex(
-            r'<textarea[^>]+id=["\']fm-video_link[^>]+>([^<]+)</textarea>',
-            webpage, 'video link')
-        videopage = self._download_webpage(video_link_url, video_id)
-
-        info_dict = self._parse_html5_media_entries(url, videopage, video_id)[0]
-
-        duration = parse_duration(self._search_regex(
-            r'时长:\s*</span>\s*(\d+:\d+)', webpage, 'duration', fatal=False))
+            r'document\.write\(\s*strencode2\s*\(\s*((?:"[^"]+")|(?:\'[^\']+\'))', webpage, 'video link')
+        video_link_url = self._search_regex(
+            r'src=["\']([^"\']+)["\']', urllib.parse.unquote(video_link_url), 'unquoted video link')
 
-        comment_count = int_or_none(self._search_regex(
-            r'留言:\s*</span>\s*(\d+)', webpage, 'comment count', fatal=False))
+        formats, subtitles = self._get_formats_and_subtitle(video_link_url, video_id)
 
-        info_dict.update({
+        return {
             'id': video_id,
-            'title': title,
-            'duration': duration,
-            'comment_count': comment_count,
-            'age_limit': self._rta_search(webpage),
-        })
+            'title': remove_end(self._html_extract_title(webpage).replace('\n', ''), 'Chinese homemade video').strip(),
+            'formats': formats,
+            'subtitles': subtitles,
+            'upload_date': unified_strdate(self._search_regex(
+                r'<span\s+class=["\']title-yakov["\']>(\d{4}-\d{2}-\d{2})</span>', webpage, 'upload_date', fatal=False)),
+            'description': self._html_search_regex(
+                r'<span\s+class=["\']more title["\']>\s*([^<]+)', webpage, 'description', fatal=False),
+            'duration': parse_duration(self._search_regex(
+                r'时长:\s*<span[^>]*>\s*(\d+(?::\d+){1,2})', webpage, 'duration', fatal=False)),
+            'comment_count': int_or_none(self._search_regex(
+                r'留言:\s*<span[^>]*>\s*(\d+)\s*</span>', webpage, 'comment count', fatal=False)),
+            'view_count': int_or_none(self._search_regex(
+                r'热度:\s*<span[^>]*>\s*(\d+)\s*</span>', webpage, 'view count', fatal=False)),
+            'age_limit': 18,
+        }
+
+    def _get_formats_and_subtitle(self, video_link_url, video_id):
+        ext = determine_ext(video_link_url)
+        if ext == 'm3u8':
+            formats, subtitles = self._extract_m3u8_formats_and_subtitles(video_link_url, video_id, ext='mp4')
+        else:
+            formats = [{'url': video_link_url, 'ext': ext}]
+            subtitles = {}
 
-        return info_dict
+        return formats, subtitles


Test patch: