Repository: yt-dlp/yt-dlp

Issue:
Support for https://zaiko.io/
### DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE

- [X] I understand that I will be **blocked** if I *intentionally* remove or skip any mandatory\* field

### Checklist

- [X] I'm reporting a new site support request
- [X] I've verified that I'm running yt-dlp version **2023.03.04** ([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/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-website-primarily-used-for-piracy) or contain any [DRM](https://en.wikipedia.org/wiki/Digital_rights_management) to the best of my knowledge
- [X] I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and 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

Worldwide (Japanese)

### Example URLs

Video: https://zaiko.io/event/324868/stream/20571/20571

Free Shop Page: https://streaming.zaiko.io/buy/1mvO:5lN:0f92a?item_data=1mvO%3A5lN%3A0f92a


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

I'd like to suggest this website zaiko.io to be supported. This website mainly provides paid contents. In order to watch the URL video, you will need an account and get the free ticket on the second link. 
As far as I know, they use self-developed players. 

### 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] If using API, add `'verbose': True` to `YoutubeDL` params instead
- [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://zaiko.io/event/324868/stream/20571/20571']
[debug] Encodings: locale cp932, fs utf-8, pref cp932, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version stable@2023.03.04 [392389b7d] (win_exe)
[debug] Python 3.8.10 (CPython AMD64 64bit) - Windows-10-10.0.19044-SP0 (OpenSSL 1.1.1k  25 Mar 2021)
[debug] exe versions: ffmpeg 5.1.2-essentials_build-www.gyan.dev (setts), ffprobe 5.1.2-essentials_build-www.gyan.dev
[debug] Optional libraries: Cryptodome-3.17, brotli-1.0.9, certifi-2022.12.07, mutagen-1.46.0, sqlite3-2.6.0, websockets-10.4
[debug] Proxy map: {}
[debug] Loaded 1786 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Available version: stable@2023.03.04, Current version: stable@2023.03.04
Current Build Hash: 5590c57bd0433ed239a2deaaf92e2ad6f37fe50f53664c821575cafe106a9421
yt-dlp is up to date (stable@2023.03.04)
[generic] Extracting URL: https://zaiko.io/event/324868/stream/20571/20571
[generic] 20571: Downloading webpage
[redirect] Following redirect to https://zaiko.io/login
[generic] Extracting URL: https://zaiko.io/login
[generic] login: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] login: Extracting information
[debug] Looking for embeds
ERROR: Unsupported URL: https://zaiko.io/login
Traceback (most recent call last):
  File "yt_dlp\YoutubeDL.py", line 1518, in wrapper
  File "yt_dlp\YoutubeDL.py", line 1594, in __extract_info
  File "yt_dlp\extractor\common.py", line 694, in extract
  File "yt_dlp\extractor\generic.py", line 2510, in _real_extract
yt_dlp.utils.UnsupportedError: Unsupported URL: https://zaiko.io/login
```



Discussion:
This issue is potentially a duplicate of one of the following issues:
  - #7157 (70%)


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
@@ -2440,6 +2440,7 @@
 from .youporn import YouPornIE
 from .yourporn import YourPornIE
 from .yourupload import YourUploadIE
+from .zaiko import ZaikoIE
 from .zapiks import ZapiksIE
 from .zattoo import (
     BBVTVIE,
diff --git a/yt_dlp/extractor/zaiko.py b/yt_dlp/extractor/zaiko.py
new file mode 100644
--- /dev/null
+++ b/yt_dlp/extractor/zaiko.py
@@ -0,0 +1,92 @@
+from .common import InfoExtractor
+from ..utils import (
+    ExtractorError,
+    extract_attributes,
+    int_or_none,
+    str_or_none,
+    traverse_obj,
+    unescapeHTML,
+    url_or_none,
+)
+
+
+class ZaikoIE(InfoExtractor):
+    _VALID_URL = r'https?://(?:[\w-]+\.)?zaiko\.io/event/(?P<id>\d+)/stream(?:/\d+)+'
+    _TESTS = [{
+        'url': 'https://zaiko.io/event/324868/stream/20571/20571',
+        'info_dict': {
+            'id': '324868',
+            'ext': 'mp4',
+            'title': 'ZAIKO STREAMING TEST',
+            'alt_title': '[VOD] ZAIKO STREAMING TEST_20210603(Do Not Delete)',
+            'uploader_id': '454',
+            'uploader': 'ZAIKO ZERO',
+            'release_timestamp': 1583809200,
+            'thumbnail': r're:https://[a-z0-9]+.cloudfront.net/[a-z0-9_]+/[a-z0-9_]+',
+            'release_date': '20200310',
+            'categories': ['Tech House'],
+            'live_status': 'was_live',
+        },
+        'params': {'skip_download': 'm3u8'},
+    }]
+
+    def _parse_vue_element_attr(self, name, string, video_id):
+        page_elem = self._search_regex(rf'(<{name}[^>]+>)', string, name)
+        attrs = {}
+        for key, value in extract_attributes(page_elem).items():
+            if key.startswith(':'):
+                attrs[key[1:]] = self._parse_json(
+                    value, video_id, transform_source=unescapeHTML, fatal=False)
+        return attrs
+
+    def _real_extract(self, url):
+        video_id = self._match_id(url)
+
+        webpage, urlh = self._download_webpage_handle(url, video_id)
+        final_url = urlh.geturl()
+        if 'zaiko.io/login' in final_url:
+            self.raise_login_required()
+        elif '/_buy/' in final_url:
+            raise ExtractorError('Your account does not have tickets to this event', expected=True)
+        stream_meta = self._parse_vue_element_attr('stream-page', webpage, video_id)
+
+        player_page = self._download_webpage(
+            stream_meta['stream-access']['video_source'], video_id,
+            'Downloading player page', headers={'referer': 'https://zaiko.io/'})
+        player_meta = self._parse_vue_element_attr('player', player_page, video_id)
+        status = traverse_obj(player_meta, ('initial_event_info', 'status', {str}))
+        live_status, msg, expected = {
+            'vod': ('was_live', 'No VOD stream URL was found', False),
+            'archiving': ('post_live', 'Event VOD is still being processed', True),
+            'deleting': ('post_live', 'This event has ended', True),
+            'deleted': ('post_live', 'This event has ended', True),
+            'error': ('post_live', 'This event has ended', True),
+            'disconnected': ('post_live', 'Stream has been disconnected', True),
+            'live_to_disconnected': ('post_live', 'Stream has been disconnected', True),
+            'live': ('is_live', 'No livestream URL found was found', False),
+            'waiting': ('is_upcoming', 'Live event has not yet started', True),
+            'cancelled': ('not_live', 'Event has been cancelled', True),
+        }.get(status) or ('not_live', f'Unknown event status "{status}"', False)
+
+        stream_url = traverse_obj(player_meta, ('initial_event_info', 'endpoint', {url_or_none}))
+        formats = self._extract_m3u8_formats(
+            stream_url, video_id, live=True, fatal=False) if stream_url else []
+        if not formats:
+            self.raise_no_formats(msg, expected=expected)
+
+        return {
+            'id': video_id,
+            'formats': formats,
+            'live_status': live_status,
+            **traverse_obj(stream_meta, {
+                'title': ('event', 'name', {str}),
+                'uploader': ('profile', 'name', {str}),
+                'uploader_id': ('profile', 'id', {str_or_none}),
+                'release_timestamp': ('stream', 'start', 'timestamp', {int_or_none}),
+                'categories': ('event', 'genres', ..., {lambda x: x or None}),
+            }),
+            **traverse_obj(player_meta, ('initial_event_info', {
+                'alt_title': ('title', {str}),
+                'thumbnail': ('poster_url', {url_or_none}),
+            })),
+        }


Test patch: