Repository: yt-dlp/yt-dlp

Issue:
Booyah.com support 
### Checklist

- [X] I'm reporting a new site support request
- [X] I've verified that I'm running yt-dlp version **2022.07.18** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
- [X] I've checked that all provided URLs are playable in a browser with the same IP and same login details
- [X] I've checked that none of provided URLs [violate any copyrights](https://github.com/ytdl-org/youtube-dl#can-you-add-support-for-this-anime-video-site-or-site-which-shows-current-movies-for-free) or contain any [DRM](https://en.wikipedia.org/wiki/Digital_rights_management) to the best of my knowledge
- [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 am willing to share it if required

### Region

United states

### Example URLs

Single video: https://booyah.live/clips/13887261322952306617
Single video: https://booyah.live/clips/13620401526858573419?source=3
User: https://booyah.live/studio/10843280

### Provide a description that is worded well enough to be understood

yt-dlp is currently unable to extract video or audio from the website.

### Provide verbose output that clearly demonstrates the problem

- [X] Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
- [X] Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below

### Complete Verbose Output

```shell
[debug] Command-line config: ['-vU', 'https://booyah.live/clips/13887261322952306617']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version 2022.07.18 [135f05e] (win32_exe)
[debug] Python 3.8.10 (CPython 64bit) - Windows-10-10.0.19044-SP0
[debug] Checking exe version: ffmpeg -bsfs
[debug] Checking exe version: ffprobe -bsfs
[debug] exe versions: ffmpeg 2022-07-14-git-882aac99d2-essentials_build-www.gyan.dev (setts), ffprobe 2022-07-14-git-882aac99d2-essentials_build-www.gyan.dev
[debug] Optional libraries: Cryptodome-3.15.0, brotli-1.0.9, certifi-2022.06.15, mutagen-1.45.1, sqlite3-2.6.0, websockets-10.3
[debug] Proxy map: {}
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: 2022.07.18, Current version: 2022.07.18
yt-dlp is up to date (2022.07.18)
[debug] [generic] Extracting URL: https://booyah.live/clips/13887261322952306617
[generic] 13887261322952306617: Downloading webpage
WARNING: [generic] Falling back on generic information extractor.
[generic] 13887261322952306617: Extracting information
[debug] Looking for video embeds
ERROR: Unsupported URL: https://booyah.live/clips/13887261322952306617
Traceback (most recent call last):
  File "yt_dlp\YoutubeDL.py", line 1431, in wrapper
  File "yt_dlp\YoutubeDL.py", line 1506, in __extract_info
  File "yt_dlp\extractor\common.py", line 644, in extract
  File "yt_dlp\extractor\generic.py", line 4108, in _real_extract
yt_dlp.utils.UnsupportedError: Unsupported URL: https://booyah.live/clips/13887261322952306617
```



Discussion:
The video api json can be found in `https://booyah.live/api/v3/playbacks/<video_id>`. We can extract the video in `json['playback']['endpoint_list']`

Here's the sample of `endpoint_list`:
```
"endpoint_list": [
    {
      "stream_url": "https://google-gcp.cdn.booyah.live/Clip/10843280/10843280-6c109a02-719a-4059-844d-67d863c5987e.mp4",
      "download_url": "https://google-gcp.cdn.booyah.live/Clip/10843280/10843280-6c109a02-719a-4059-844d-67d863c5987e_watermark.mp4",
      "resolution": 640,
      "bitrate": 1544258
    },
    {
      "stream_url": "https://google-gcp.cdn.booyah.live/Clip/10843280/10843280-6c109a02-719a-4059-844d-67d863c5987e_480.mp4",
      "download_url": "https://google-gcp.cdn.booyah.live/Clip/10843280/10843280-6c109a02-719a-4059-844d-67d863c5987e_watermark.mp4",
      "resolution": 480,
      "bitrate": 1344000
    },
    {
      "stream_url": "https://google-gcp.cdn.booyah.live/Clip/10843280/10843280-6c109a02-719a-4059-844d-67d863c5987e_360.mp4",
      "download_url": "https://google-gcp.cdn.booyah.live/Clip/10843280/10843280-6c109a02-719a-4059-844d-67d863c5987e_watermark.mp4",
      "resolution": 360,
      "bitrate": 784000
    },
    {
      "stream_url": "https://google-gcp.cdn.booyah.live/Clip/10843280/10843280-6c109a02-719a-4059-844d-67d863c5987e_240.mp4",
      "download_url": "https://google-gcp.cdn.booyah.live/Clip/10843280/10843280-6c109a02-719a-4059-844d-67d863c5987e_watermark.mp4",
      "resolution": 240,
      "bitrate": 424000
    },
    {
      "stream_url": "https://google-gcp.cdn.booyah.live/Clip/10843280/10843280-6c109a02-719a-4059-844d-67d863c5987e_160.mp4",
      "download_url": "https://google-gcp.cdn.booyah.live/Clip/10843280/10843280-6c109a02-719a-4059-844d-67d863c5987e_watermark.mp4",
      "resolution": 160,
      "bitrate": 304000
    }
  ]
```
The video url is both `stream_url` and `download_url`. However, link in `download_url` get watermark.
However, this api url require `Booyah-Session-Key` in the header. At the moment, i don't know where this value came from. I guess it's from cookie.

The `Booyah-Session-Key` seems returned from this url "https://booyah.live/api/v3/auths/sessions". However, this url need request body (at least `""`). This url needs to send in POST Method. We can get `Booyah-Session-Key` either from header or cookie (with `session_key` key)

Resolving patch:
diff --git a/yt_dlp/extractor/_extractors.py b/yt_dlp/extractor/_extractors.py
--- a/yt_dlp/extractor/_extractors.py
+++ b/yt_dlp/extractor/_extractors.py
@@ -217,6 +217,7 @@
 from .bongacams import BongaCamsIE
 from .bostonglobe import BostonGlobeIE
 from .box import BoxIE
+from .booyah import BooyahClipsIE
 from .bpb import BpbIE
 from .br import (
     BRIE,
diff --git a/yt_dlp/extractor/booyah.py b/yt_dlp/extractor/booyah.py
new file mode 100644
--- /dev/null
+++ b/yt_dlp/extractor/booyah.py
@@ -0,0 +1,87 @@
+from .common import InfoExtractor
+from ..utils import int_or_none, str_or_none, traverse_obj
+
+
+class BooyahBaseIE(InfoExtractor):
+    _BOOYAH_SESSION_KEY = None
+
+    def _real_initialize(self):
+        BooyahBaseIE._BOOYAH_SESSION_KEY = self._request_webpage(
+            'https://booyah.live/api/v3/auths/sessions', None, data=b'').getheader('booyah-session-key')
+
+    def _get_comments(self, video_id):
+        comment_json = self._download_json(
+            f'https://booyah.live/api/v3/playbacks/{video_id}/comments/tops', video_id,
+            headers={'Booyah-Session-Key': self._BOOYAH_SESSION_KEY}, fatal=False) or {}
+
+        return [{
+            'id': comment.get('comment_id'),
+            'author': comment.get('from_nickname'),
+            'author_id': comment.get('from_uid'),
+            'author_thumbnail': comment.get('from_thumbnail'),
+            'text': comment.get('content'),
+            'timestamp': comment.get('create_time'),
+            'like_count': comment.get('like_cnt'),
+        } for comment in comment_json.get('comment_list') or ()]
+
+
+class BooyahClipsIE(BooyahBaseIE):
+    _VALID_URL = r'https?://booyah.live/clips/(?P<id>\d+)'
+    _TESTS = [{
+        'url': 'https://booyah.live/clips/13887261322952306617',
+        'info_dict': {
+            'id': '13887261322952306617',
+            'ext': 'mp4',
+            'view_count': int,
+            'duration': 30,
+            'channel_id': 90565760,
+            'like_count': int,
+            'title': 'Cayendo con estilo 😎',
+            'uploader': '♡LɪꜱGΛ​MER​',
+            'comment_count': int,
+            'uploader_id': '90565760',
+            'thumbnail': 'https://resmambet-a.akamaihd.net/mambet-storage/Clip/90565760/90565760-27204374-fba0-409d-9d7b-63a48b5c0e75.jpg',
+            'upload_date': '20220617',
+            'timestamp': 1655490556,
+            'modified_timestamp': 1655490556,
+            'modified_date': '20220617',
+        }
+    }]
+
+    def _real_extract(self, url):
+        video_id = self._match_id(url)
+        json_data = self._download_json(
+            f'https://booyah.live/api/v3/playbacks/{video_id}', video_id,
+            headers={'Booyah-Session-key': self._BOOYAH_SESSION_KEY})
+
+        formats = []
+        for video_data in json_data['playback']['endpoint_list']:
+            formats.extend(({
+                'url': video_data.get('stream_url'),
+                'ext': 'mp4',
+                'height': video_data.get('resolution'),
+            }, {
+                'url': video_data.get('download_url'),
+                'ext': 'mp4',
+                'format_note': 'Watermarked',
+                'height': video_data.get('resolution'),
+                'preference': -10,
+            }))
+        self._sort_formats(formats)
+
+        return {
+            'id': video_id,
+            'title': traverse_obj(json_data, ('playback', 'name')),
+            'thumbnail': traverse_obj(json_data, ('playback', 'thumbnail_url')),
+            'formats': formats,
+            'view_count': traverse_obj(json_data, ('playback', 'views')),
+            'like_count': traverse_obj(json_data, ('playback', 'likes')),
+            'duration': traverse_obj(json_data, ('playback', 'duration')),
+            'comment_count': traverse_obj(json_data, ('playback', 'comment_cnt')),
+            'channel_id': traverse_obj(json_data, ('playback', 'channel_id')),
+            'uploader': traverse_obj(json_data, ('user', 'nickname')),
+            'uploader_id': str_or_none(traverse_obj(json_data, ('user', 'uid'))),
+            'modified_timestamp': int_or_none(traverse_obj(json_data, ('playback', 'update_time_ms')), 1000),
+            'timestamp': int_or_none(traverse_obj(json_data, ('playback', 'create_time_ms')), 1000),
+            '__post_extractor': self.extract_comments(video_id, self._get_comments(video_id)),
+        }


Test patch: