]>
Commit | Line | Data |
---|---|---|
22a510ff | 1 | from .common import InfoExtractor |
c59de48e | 2 | from ..networking.exceptions import HTTPError |
b38018b7 M |
3 | from ..utils import ( |
4 | ExtractorError, | |
5 | UserNotLive, | |
6 | int_or_none, | |
7 | str_or_none, | |
8 | url_or_none, | |
9 | ) | |
c59de48e | 10 | from ..utils.traversal import traverse_obj |
22a510ff THD |
11 | |
12 | ||
13 | class MixchIE(InfoExtractor): | |
14 | IE_NAME = 'mixch' | |
15 | _VALID_URL = r'https?://(?:www\.)?mixch\.tv/u/(?P<id>\d+)' | |
16 | ||
ba1c671d | 17 | _TESTS = [{ |
b38018b7 | 18 | 'url': 'https://mixch.tv/u/16943797/live', |
22a510ff THD |
19 | 'skip': 'don\'t know if this live persists', |
20 | 'info_dict': { | |
b38018b7 M |
21 | 'id': '16943797', |
22 | 'ext': 'mp4', | |
23 | 'title': '#EntView #カリナ #セブチ 2024-05-05 06:58', | |
24 | 'comment_count': int, | |
25 | 'view_count': int, | |
26 | 'timestamp': 1714726805, | |
27 | 'uploader': 'Ent.View K-news🎶💕', | |
28 | 'uploader_id': '16943797', | |
29 | 'live_status': 'is_live', | |
30 | 'upload_date': '20240503', | |
31 | }, | |
22a510ff THD |
32 | }, { |
33 | 'url': 'https://mixch.tv/u/16137876/live', | |
34 | 'only_matching': True, | |
35 | }] | |
36 | ||
37 | def _real_extract(self, url): | |
38 | video_id = self._match_id(url) | |
4c3b7a07 | 39 | data = self._download_json(f'https://mixch.tv/api-web/users/{video_id}/live', video_id) |
40 | if not traverse_obj(data, ('liveInfo', {dict})): | |
4af47a00 | 41 | raise UserNotLive(video_id=video_id) |
22a510ff THD |
42 | |
43 | return { | |
44 | 'id': video_id, | |
22a510ff | 45 | 'uploader_id': video_id, |
4c3b7a07 | 46 | **traverse_obj(data, { |
47 | 'title': ('liveInfo', 'title', {str}), | |
48 | 'comment_count': ('liveInfo', 'comments', {int_or_none}), | |
49 | 'view_count': ('liveInfo', 'visitor', {int_or_none}), | |
50 | 'timestamp': ('liveInfo', 'created', {int_or_none}), | |
51 | 'uploader': ('broadcasterInfo', 'name', {str}), | |
52 | }), | |
22a510ff THD |
53 | 'formats': [{ |
54 | 'format_id': 'hls', | |
4c3b7a07 | 55 | 'url': data['liveInfo']['hls'], |
22a510ff THD |
56 | 'ext': 'mp4', |
57 | 'protocol': 'm3u8', | |
4af47a00 | 58 | }], |
59 | 'is_live': True, | |
b38018b7 | 60 | '__post_extractor': self.extract_comments(video_id), |
22a510ff | 61 | } |
ba1c671d | 62 | |
b38018b7 M |
63 | def _get_comments(self, video_id): |
64 | yield from traverse_obj(self._download_json( | |
65 | f'https://mixch.tv/api-web/lives/{video_id}/messages', video_id, | |
66 | note='Downloading comments', errnote='Failed to download comments'), (..., { | |
67 | 'author': ('name', {str}), | |
68 | 'author_id': ('user_id', {str_or_none}), | |
69 | 'id': ('message_id', {str}, {lambda x: x or None}), | |
70 | 'text': ('body', {str}), | |
71 | 'timestamp': ('created', {int}), | |
72 | })) | |
73 | ||
ba1c671d LNO |
74 | |
75 | class MixchArchiveIE(InfoExtractor): | |
76 | IE_NAME = 'mixch:archive' | |
77 | _VALID_URL = r'https?://(?:www\.)?mixch\.tv/archive/(?P<id>\d+)' | |
78 | ||
79 | _TESTS = [{ | |
80 | 'url': 'https://mixch.tv/archive/421', | |
81 | 'skip': 'paid video, no DRM. expires at Jan 23', | |
82 | 'info_dict': { | |
83 | 'id': '421', | |
c59de48e | 84 | 'ext': 'mp4', |
ba1c671d LNO |
85 | 'title': '96NEKO SHOW TIME', |
86 | } | |
c59de48e M |
87 | }, { |
88 | 'url': 'https://mixch.tv/archive/1213', | |
89 | 'skip': 'paid video, no DRM. expires at Dec 31, 2023', | |
90 | 'info_dict': { | |
91 | 'id': '1213', | |
92 | 'ext': 'mp4', | |
93 | 'title': '【特別トーク番組アーカイブス】Merm4id×燐舞曲 2nd LIVE「VERSUS」', | |
94 | 'release_date': '20231201', | |
95 | 'thumbnail': str, | |
96 | } | |
97 | }, { | |
98 | 'url': 'https://mixch.tv/archive/1214', | |
99 | 'only_matching': True, | |
ba1c671d LNO |
100 | }] |
101 | ||
102 | def _real_extract(self, url): | |
103 | video_id = self._match_id(url) | |
ba1c671d | 104 | |
c59de48e M |
105 | try: |
106 | info_json = self._download_json( | |
107 | f'https://mixch.tv/api-web/archive/{video_id}', video_id)['archive'] | |
108 | except ExtractorError as e: | |
109 | if isinstance(e.cause, HTTPError) and e.cause.status == 401: | |
110 | self.raise_login_required() | |
111 | raise | |
ba1c671d | 112 | |
c59de48e M |
113 | return { |
114 | 'id': video_id, | |
115 | 'title': traverse_obj(info_json, ('title', {str})), | |
116 | 'formats': self._extract_m3u8_formats(info_json['archiveURL'], video_id), | |
117 | 'thumbnail': traverse_obj(info_json, ('thumbnailURL', {url_or_none})), | |
118 | } |