]> jfr.im git - yt-dlp.git/blobdiff - yt_dlp/extractor/zaiko.py
[ie/orf:on] Improve extraction (#9677)
[yt-dlp.git] / yt_dlp / extractor / zaiko.py
index 59fc64c5a94beffcfb20b179f6dc8a23fb1ade1d..2b6221da21b54a4d341320d1e2d312b7593e7b84 100644 (file)
@@ -1,3 +1,5 @@
+import base64
+
 from .common import InfoExtractor
 from ..utils import (
     ExtractorError,
@@ -5,12 +7,34 @@
     int_or_none,
     str_or_none,
     traverse_obj,
+    try_call,
     unescapeHTML,
+    url_basename,
     url_or_none,
 )
 
 
-class ZaikoIE(InfoExtractor):
+class ZaikoBaseIE(InfoExtractor):
+    def _download_real_webpage(self, url, video_id):
+        webpage, urlh = self._download_webpage_handle(url, video_id)
+        final_url = urlh.url
+        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)
+        return webpage
+
+    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
+
+
+class ZaikoIE(ZaikoBaseIE):
     _VALID_URL = r'https?://(?:[\w-]+\.)?zaiko\.io/event/(?P<id>\d+)/stream(?:/\d+)+'
     _TESTS = [{
         'url': 'https://zaiko.io/event/324868/stream/20571/20571',
@@ -22,32 +46,20 @@ class ZaikoIE(InfoExtractor):
             'uploader_id': '454',
             'uploader': 'ZAIKO ZERO',
             'release_timestamp': 1583809200,
-            'thumbnail': r're:https://[a-z0-9]+.cloudfront.net/[a-z0-9_]+/[a-z0-9_]+',
+            'thumbnail': r're:^https://[\w.-]+/\w+/\w+',
+            'thumbnails': 'maxcount:2',
             'release_date': '20200310',
             'categories': ['Tech House'],
             'live_status': 'was_live',
         },
         'params': {'skip_download': 'm3u8'},
+        'skip': 'Your account does not have tickets to this event',
     }]
 
-    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)
+        webpage = self._download_real_webpage(url, video_id)
         stream_meta = self._parse_vue_element_attr('stream-page', webpage, video_id)
 
         player_page = self._download_webpage(
@@ -74,6 +86,12 @@ def _real_extract(self, url):
         if not formats:
             self.raise_no_formats(msg, expected=expected)
 
+        thumbnail_urls = [
+            traverse_obj(player_meta, ('initial_event_info', 'poster_url')),
+            self._og_search_thumbnail(self._download_webpage(
+                f'https://zaiko.io/event/{video_id}', video_id, 'Downloading event page', fatal=False) or ''),
+        ]
+
         return {
             'id': video_id,
             'formats': formats,
@@ -87,6 +105,35 @@ def _real_extract(self, url):
             }),
             **traverse_obj(player_meta, ('initial_event_info', {
                 'alt_title': ('title', {str}),
-                'thumbnail': ('poster_url', {url_or_none}),
             })),
+            'thumbnails': [{'url': url, 'id': url_basename(url)} for url in thumbnail_urls if url_or_none(url)]
         }
+
+
+class ZaikoETicketIE(ZaikoBaseIE):
+    _VALID_URL = r'https?://(?:www.)?zaiko\.io/account/eticket/(?P<id>[\w=-]{49})'
+    _TESTS = [{
+        'url': 'https://zaiko.io/account/eticket/TZjMwMzQ2Y2EzMXwyMDIzMDYwNzEyMTMyNXw1MDViOWU2Mw==',
+        'playlist_count': 1,
+        'info_dict': {
+            'id': 'f30346ca31-20230607121325-505b9e63',
+            'title': 'ZAIKO STREAMING TEST',
+            'thumbnail': 'https://media.zkocdn.net/pf_1/1_3wdyjcjyupseatkwid34u',
+        },
+        'skip': 'Only available with the ticketholding account',
+    }]
+
+    def _real_extract(self, url):
+        ticket_id = self._match_id(url)
+        ticket_id = try_call(
+            lambda: base64.urlsafe_b64decode(ticket_id[1:]).decode().replace('|', '-')) or ticket_id
+
+        webpage = self._download_real_webpage(url, ticket_id)
+        eticket = self._parse_vue_element_attr('eticket', webpage, ticket_id)
+
+        return self.playlist_result(
+            [self.url_result(stream, ZaikoIE) for stream in traverse_obj(eticket, ('streams', ..., 'url'))],
+            ticket_id, **traverse_obj(eticket, ('ticket-details', {
+                'title': 'event_name',
+                'thumbnail': 'event_img_url',
+            })))