]>
Commit | Line | Data |
---|---|---|
9997eee4 THD |
1 | from .common import InfoExtractor |
2 | from ..utils import ( | |
3 | ExtractorError, | |
6e6beffd | 4 | get_first, |
b8eeced2 | 5 | int_or_none, |
9997eee4 | 6 | traverse_obj, |
265e586d | 7 | try_get, |
b8eeced2 | 8 | unified_strdate, |
6e6beffd | 9 | unified_timestamp, |
9997eee4 THD |
10 | ) |
11 | from ..compat import compat_str | |
12 | ||
13 | ||
b8eeced2 LTHD |
14 | class OpenRecBaseIE(InfoExtractor): |
15 | def _extract_pagestore(self, webpage, video_id): | |
16 | return self._parse_json( | |
9997eee4 | 17 | self._search_regex(r'(?m)window\.pageStore\s*=\s*(\{.+?\});$', webpage, 'window.pageStore'), video_id) |
b8eeced2 | 18 | |
265e586d LNO |
19 | def _expand_media(self, video_id, media): |
20 | for name, m3u8_url in (media or {}).items(): | |
21 | if not m3u8_url: | |
22 | continue | |
23 | yield from self._extract_m3u8_formats( | |
24 | m3u8_url, video_id, ext='mp4', m3u8_id=name) | |
25 | ||
b8eeced2 LTHD |
26 | def _extract_movie(self, webpage, video_id, name, is_live): |
27 | window_stores = self._extract_pagestore(webpage, video_id) | |
6e6beffd LNO |
28 | movie_stores = [ |
29 | # extract all three important data (most of data are duplicated each other, but slightly different!) | |
30 | traverse_obj(window_stores, ('v8', 'state', 'movie'), expected_type=dict), | |
31 | traverse_obj(window_stores, ('v8', 'movie'), expected_type=dict), | |
32 | traverse_obj(window_stores, 'movieStore', expected_type=dict), | |
33 | ] | |
34 | if not any(movie_stores): | |
b8eeced2 | 35 | raise ExtractorError(f'Failed to extract {name} info') |
9997eee4 | 36 | |
265e586d | 37 | formats = list(self._expand_media(video_id, get_first(movie_stores, 'media'))) |
fdfc8149 L |
38 | if not formats: |
39 | # archived livestreams or subscriber-only videos | |
265e586d LNO |
40 | cookies = self._get_cookies('https://www.openrec.tv/') |
41 | detail = self._download_json( | |
42 | f'https://apiv5.openrec.tv/api/v5/movies/{video_id}/detail', video_id, | |
43 | headers={ | |
44 | 'Origin': 'https://www.openrec.tv', | |
45 | 'Referer': 'https://www.openrec.tv/', | |
46 | 'access-token': try_get(cookies, lambda x: x.get('access_token').value), | |
47 | 'uuid': try_get(cookies, lambda x: x.get('uuid').value), | |
48 | }) | |
49 | new_media = traverse_obj(detail, ('data', 'items', ..., 'media'), get_all=False) | |
50 | formats = list(self._expand_media(video_id, new_media)) | |
51 | is_live = False | |
9997eee4 | 52 | |
9997eee4 THD |
53 | return { |
54 | 'id': video_id, | |
6e6beffd LNO |
55 | 'title': get_first(movie_stores, 'title'), |
56 | 'description': get_first(movie_stores, 'introduction'), | |
57 | 'thumbnail': get_first(movie_stores, 'thumbnailUrl'), | |
9997eee4 | 58 | 'formats': formats, |
6e6beffd LNO |
59 | 'uploader': get_first(movie_stores, ('channel', 'user', 'name')), |
60 | 'uploader_id': get_first(movie_stores, ('channel', 'user', 'id')), | |
61 | 'timestamp': int_or_none(get_first(movie_stores, ['publishedAt', 'time']), scale=1000) or unified_timestamp(get_first(movie_stores, 'publishedAt')), | |
b8eeced2 | 62 | 'is_live': is_live, |
9997eee4 THD |
63 | } |
64 | ||
65 | ||
b8eeced2 LTHD |
66 | class OpenRecIE(OpenRecBaseIE): |
67 | IE_NAME = 'openrec' | |
68 | _VALID_URL = r'https?://(?:www\.)?openrec\.tv/live/(?P<id>[^/]+)' | |
69 | _TESTS = [{ | |
70 | 'url': 'https://www.openrec.tv/live/2p8v31qe4zy', | |
71 | 'only_matching': True, | |
72 | }, { | |
73 | 'url': 'https://www.openrec.tv/live/wez93eqvjzl', | |
74 | 'only_matching': True, | |
75 | }] | |
76 | ||
77 | def _real_extract(self, url): | |
78 | video_id = self._match_id(url) | |
6e6beffd | 79 | webpage = self._download_webpage(f'https://www.openrec.tv/live/{video_id}', video_id) |
b8eeced2 LTHD |
80 | |
81 | return self._extract_movie(webpage, video_id, 'live', True) | |
82 | ||
83 | ||
84 | class OpenRecCaptureIE(OpenRecBaseIE): | |
9997eee4 THD |
85 | IE_NAME = 'openrec:capture' |
86 | _VALID_URL = r'https?://(?:www\.)?openrec\.tv/capture/(?P<id>[^/]+)' | |
87 | _TESTS = [{ | |
88 | 'url': 'https://www.openrec.tv/capture/l9nk2x4gn14', | |
89 | 'only_matching': True, | |
90 | }, { | |
91 | 'url': 'https://www.openrec.tv/capture/mldjr82p7qk', | |
92 | 'info_dict': { | |
93 | 'id': 'mldjr82p7qk', | |
94 | 'title': 'たいじの恥ずかしい英語力', | |
95 | 'uploader': 'たいちゃんねる', | |
96 | 'uploader_id': 'Yaritaiji', | |
97 | 'upload_date': '20210803', | |
98 | }, | |
99 | }] | |
100 | ||
101 | def _real_extract(self, url): | |
102 | video_id = self._match_id(url) | |
6e6beffd | 103 | webpage = self._download_webpage(f'https://www.openrec.tv/capture/{video_id}', video_id) |
9997eee4 | 104 | |
b8eeced2 | 105 | window_stores = self._extract_pagestore(webpage, video_id) |
9997eee4 THD |
106 | movie_store = window_stores.get('movie') |
107 | ||
108 | capture_data = window_stores.get('capture') | |
109 | if not capture_data: | |
110 | raise ExtractorError('Cannot extract title') | |
9997eee4 | 111 | |
b8eeced2 LTHD |
112 | formats = self._extract_m3u8_formats( |
113 | capture_data.get('source'), video_id, ext='mp4') | |
9997eee4 THD |
114 | |
115 | return { | |
116 | 'id': video_id, | |
6e6beffd LNO |
117 | 'title': capture_data.get('title'), |
118 | 'thumbnail': capture_data.get('thumbnailUrl'), | |
9997eee4 | 119 | 'formats': formats, |
6e6beffd LNO |
120 | 'timestamp': unified_timestamp(traverse_obj(movie_store, 'createdAt', expected_type=compat_str)), |
121 | 'uploader': traverse_obj(movie_store, ('channel', 'name'), expected_type=compat_str), | |
122 | 'uploader_id': traverse_obj(movie_store, ('channel', 'id'), expected_type=compat_str), | |
123 | 'upload_date': unified_strdate(capture_data.get('createdAt')), | |
9997eee4 | 124 | } |
b8eeced2 LTHD |
125 | |
126 | ||
127 | class OpenRecMovieIE(OpenRecBaseIE): | |
128 | IE_NAME = 'openrec:movie' | |
129 | _VALID_URL = r'https?://(?:www\.)?openrec\.tv/movie/(?P<id>[^/]+)' | |
130 | _TESTS = [{ | |
131 | 'url': 'https://www.openrec.tv/movie/nqz5xl5km8v', | |
132 | 'info_dict': { | |
133 | 'id': 'nqz5xl5km8v', | |
134 | 'title': '限定コミュニティ(Discord)参加方法ご説明動画', | |
135 | 'description': 'md5:ebd563e5f5b060cda2f02bf26b14d87f', | |
136 | 'thumbnail': r're:https://.+', | |
137 | 'uploader': 'タイキとカズヒロ', | |
138 | 'uploader_id': 'taiki_to_kazuhiro', | |
139 | 'timestamp': 1638856800, | |
140 | }, | |
141 | }] | |
142 | ||
143 | def _real_extract(self, url): | |
144 | video_id = self._match_id(url) | |
6e6beffd | 145 | webpage = self._download_webpage(f'https://www.openrec.tv/movie/{video_id}', video_id) |
b8eeced2 LTHD |
146 | |
147 | return self._extract_movie(webpage, video_id, 'movie', False) |