logo

oasis-root

Compiled tree of Oasis Linux based on own branch at <https://hacktivis.me/git/oasis/> git clone https://anongit.hacktivis.me/git/oasis-root.git

zaiko.py (6302B)


  1. import base64
  2. from .common import InfoExtractor
  3. from ..utils import (
  4. ExtractorError,
  5. extract_attributes,
  6. int_or_none,
  7. str_or_none,
  8. traverse_obj,
  9. try_call,
  10. unescapeHTML,
  11. url_basename,
  12. url_or_none,
  13. )
  14. class ZaikoBaseIE(InfoExtractor):
  15. def _download_real_webpage(self, url, video_id):
  16. webpage, urlh = self._download_webpage_handle(url, video_id)
  17. final_url = urlh.url
  18. if 'zaiko.io/login' in final_url:
  19. self.raise_login_required()
  20. elif '/_buy/' in final_url:
  21. raise ExtractorError('Your account does not have tickets to this event', expected=True)
  22. return webpage
  23. def _parse_vue_element_attr(self, name, string, video_id):
  24. page_elem = self._search_regex(rf'(<{name}[^>]+>)', string, name)
  25. attrs = {}
  26. for key, value in extract_attributes(page_elem).items():
  27. if key.startswith(':'):
  28. attrs[key[1:]] = self._parse_json(
  29. value, video_id, transform_source=unescapeHTML, fatal=False)
  30. return attrs
  31. class ZaikoIE(ZaikoBaseIE):
  32. _VALID_URL = r'https?://(?:[\w-]+\.)?zaiko\.io/event/(?P<id>\d+)/stream(?:/\d+)+'
  33. _TESTS = [{
  34. 'url': 'https://zaiko.io/event/324868/stream/20571/20571',
  35. 'info_dict': {
  36. 'id': '324868',
  37. 'ext': 'mp4',
  38. 'title': 'ZAIKO STREAMING TEST',
  39. 'alt_title': '[VOD] ZAIKO STREAMING TEST_20210603(Do Not Delete)',
  40. 'uploader_id': '454',
  41. 'uploader': 'ZAIKO ZERO',
  42. 'release_timestamp': 1583809200,
  43. 'thumbnail': r're:^https://[\w.-]+/\w+/\w+',
  44. 'thumbnails': 'maxcount:2',
  45. 'release_date': '20200310',
  46. 'categories': ['Tech House'],
  47. 'live_status': 'was_live',
  48. },
  49. 'params': {'skip_download': 'm3u8'},
  50. 'skip': 'Your account does not have tickets to this event',
  51. }]
  52. def _real_extract(self, url):
  53. video_id = self._match_id(url)
  54. webpage = self._download_real_webpage(url, video_id)
  55. stream_meta = self._parse_vue_element_attr('stream-page', webpage, video_id)
  56. player_page = self._download_webpage(
  57. stream_meta['stream-access']['video_source'], video_id,
  58. 'Downloading player page', headers={'referer': 'https://zaiko.io/'})
  59. player_meta = self._parse_vue_element_attr('player', player_page, video_id)
  60. initial_event_info = traverse_obj(player_meta, ('initial_event_info', {dict})) or {}
  61. status = traverse_obj(initial_event_info, ('status', {str}))
  62. live_status, msg, expected = {
  63. 'vod': ('was_live', 'No VOD stream URL was found', False),
  64. 'archiving': ('post_live', 'Event VOD is still being processed', True),
  65. 'deleting': ('post_live', 'This event has ended', True),
  66. 'deleted': ('post_live', 'This event has ended', True),
  67. 'error': ('post_live', 'This event has ended', True),
  68. 'disconnected': ('post_live', 'Stream has been disconnected', True),
  69. 'live_to_disconnected': ('post_live', 'Stream has been disconnected', True),
  70. 'live': ('is_live', 'No livestream URL found was found', False),
  71. 'waiting': ('is_upcoming', 'Live event has not yet started', True),
  72. 'cancelled': ('not_live', 'Event has been cancelled', True),
  73. }.get(status) or ('not_live', f'Unknown event status "{status}"', False)
  74. if traverse_obj(initial_event_info, ('is_jwt_protected', {bool})):
  75. stream_url = self._download_json(
  76. initial_event_info['jwt_token_url'], video_id, 'Downloading JWT-protected stream URL',
  77. 'Failed to download JWT-protected stream URL')['playback_url']
  78. else:
  79. stream_url = traverse_obj(initial_event_info, ('endpoint', {url_or_none}))
  80. formats = self._extract_m3u8_formats(
  81. stream_url, video_id, live=True, fatal=False) if stream_url else []
  82. if not formats:
  83. self.raise_no_formats(msg, expected=expected)
  84. thumbnail_urls = [
  85. traverse_obj(initial_event_info, ('poster_url', {url_or_none})),
  86. self._og_search_thumbnail(self._download_webpage(
  87. f'https://zaiko.io/event/{video_id}', video_id, 'Downloading event page', fatal=False) or ''),
  88. ]
  89. return {
  90. 'id': video_id,
  91. 'formats': formats,
  92. 'live_status': live_status,
  93. **traverse_obj(stream_meta, {
  94. 'title': ('event', 'name', {str}),
  95. 'uploader': ('profile', 'name', {str}),
  96. 'uploader_id': ('profile', 'id', {str_or_none}),
  97. 'release_timestamp': ('stream', 'start', 'timestamp', {int_or_none}),
  98. 'categories': ('event', 'genres', ..., filter),
  99. }),
  100. 'alt_title': traverse_obj(initial_event_info, ('title', {str})),
  101. 'thumbnails': [{'url': url, 'id': url_basename(url)} for url in thumbnail_urls if url_or_none(url)],
  102. }
  103. class ZaikoETicketIE(ZaikoBaseIE):
  104. _VALID_URL = r'https?://(?:www.)?zaiko\.io/account/eticket/(?P<id>[\w=-]{49})'
  105. _TESTS = [{
  106. 'url': 'https://zaiko.io/account/eticket/TZjMwMzQ2Y2EzMXwyMDIzMDYwNzEyMTMyNXw1MDViOWU2Mw==',
  107. 'playlist_count': 1,
  108. 'info_dict': {
  109. 'id': 'f30346ca31-20230607121325-505b9e63',
  110. 'title': 'ZAIKO STREAMING TEST',
  111. 'thumbnail': 'https://media.zkocdn.net/pf_1/1_3wdyjcjyupseatkwid34u',
  112. },
  113. 'skip': 'Only available with the ticketholding account',
  114. }]
  115. def _real_extract(self, url):
  116. ticket_id = self._match_id(url)
  117. ticket_id = try_call(
  118. lambda: base64.urlsafe_b64decode(ticket_id[1:]).decode().replace('|', '-')) or ticket_id
  119. webpage = self._download_real_webpage(url, ticket_id)
  120. eticket = self._parse_vue_element_attr('eticket', webpage, ticket_id)
  121. return self.playlist_result(
  122. [self.url_result(stream, ZaikoIE) for stream in traverse_obj(eticket, ('streams', ..., 'url'))],
  123. ticket_id, **traverse_obj(eticket, ('ticket-details', {
  124. 'title': 'event_name',
  125. 'thumbnail': 'event_img_url',
  126. })))