From 5cc09c004bd5edbbada9b041c08a720cadc4f4df Mon Sep 17 00:00:00 2001 From: Mozi <29089388+pzhlkj6612@users.noreply.github.com> Date: Tue, 20 Jun 2023 12:22:36 +0800 Subject: [PATCH] [extractor/zaiko] ZaikoETicket: Add extractor (#7347) Authored by: pzhlkj6612 --- yt_dlp/extractor/_extractors.py | 5 ++- yt_dlp/extractor/zaiko.py | 70 +++++++++++++++++++++++++-------- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/yt_dlp/extractor/_extractors.py b/yt_dlp/extractor/_extractors.py index 7e1fa4a0d..ff659a7a2 100644 --- a/yt_dlp/extractor/_extractors.py +++ b/yt_dlp/extractor/_extractors.py @@ -2448,7 +2448,10 @@ from .youporn import YouPornIE from .yourporn import YourPornIE from .yourupload import YourUploadIE -from .zaiko import ZaikoIE +from .zaiko import ( + ZaikoIE, + ZaikoETicketIE, +) from .zapiks import ZapiksIE from .zattoo import ( BBVTVIE, diff --git a/yt_dlp/extractor/zaiko.py b/yt_dlp/extractor/zaiko.py index 59fc64c5a..84cee4445 100644 --- a/yt_dlp/extractor/zaiko.py +++ b/yt_dlp/extractor/zaiko.py @@ -1,3 +1,5 @@ +import base64 + from .common import InfoExtractor from ..utils import ( ExtractorError, @@ -5,12 +7,33 @@ int_or_none, str_or_none, traverse_obj, + try_call, unescapeHTML, 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.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) + 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\d+)/stream(?:/\d+)+' _TESTS = [{ 'url': 'https://zaiko.io/event/324868/stream/20571/20571', @@ -30,24 +53,10 @@ class ZaikoIE(InfoExtractor): '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) + 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( @@ -90,3 +99,32 @@ def _real_extract(self, url): 'thumbnail': ('poster_url', {url_or_none}), })), } + + +class ZaikoETicketIE(ZaikoBaseIE): + _VALID_URL = r'https?://(?:www.)?zaiko\.io/account/eticket/(?P[\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', + })))