]>
Commit | Line | Data |
---|---|---|
d6166a76 | 1 | from .common import InfoExtractor |
a42839e5 S |
2 | from ..utils import ( |
3 | ExtractorError, | |
4 | js_to_json, | |
a42839e5 | 5 | ) |
d6166a76 PG |
6 | |
7 | ||
8 | class PicartoIE(InfoExtractor): | |
cce889b9 | 9 | _VALID_URL = r'https?://(?:www.)?picarto\.tv/(?P<id>[a-zA-Z0-9]+)' |
d6166a76 PG |
10 | _TEST = { |
11 | 'url': 'https://picarto.tv/Setz', | |
12 | 'info_dict': { | |
13 | 'id': 'Setz', | |
14 | 'ext': 'mp4', | |
15 | 'title': 're:^Setz [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$', | |
16 | 'timestamp': int, | |
17 | 'is_live': True | |
18 | }, | |
a42839e5 | 19 | 'skip': 'Stream is offline', |
d6166a76 PG |
20 | } |
21 | ||
a42839e5 S |
22 | @classmethod |
23 | def suitable(cls, url): | |
24 | return False if PicartoVodIE.suitable(url) else super(PicartoIE, cls).suitable(url) | |
25 | ||
d6166a76 | 26 | def _real_extract(self, url): |
cce889b9 | 27 | channel_id = self._match_id(url) |
28 | ||
29 | data = self._download_json( | |
30 | 'https://ptvintern.picarto.tv/ptvapi', channel_id, query={ | |
31 | 'query': '''{ | |
32 | channel(name: "%s") { | |
33 | adult | |
34 | id | |
35 | online | |
36 | stream_name | |
37 | title | |
38 | } | |
39 | getLoadBalancerUrl(channel_name: "%s") { | |
40 | url | |
41 | } | |
42 | }''' % (channel_id, channel_id), | |
43 | })['data'] | |
44 | metadata = data['channel'] | |
45 | ||
46 | if metadata.get('online') == 0: | |
d6166a76 | 47 | raise ExtractorError('Stream is offline', expected=True) |
cce889b9 | 48 | title = metadata['title'] |
d6166a76 | 49 | |
a42839e5 | 50 | cdn_data = self._download_json( |
cce889b9 | 51 | data['getLoadBalancerUrl']['url'] + '/stream/json_' + metadata['stream_name'] + '.js', |
52 | channel_id, 'Downloading load balancing info') | |
a42839e5 | 53 | |
a42839e5 | 54 | formats = [] |
cce889b9 | 55 | for source in (cdn_data.get('source') or []): |
56 | source_url = source.get('url') | |
57 | if not source_url: | |
a42839e5 | 58 | continue |
cce889b9 | 59 | source_type = source.get('type') |
60 | if source_type == 'html5/application/vnd.apple.mpegurl': | |
61 | formats.extend(self._extract_m3u8_formats( | |
62 | source_url, channel_id, 'mp4', m3u8_id='hls', fatal=False)) | |
63 | elif source_type == 'html5/video/mp4': | |
64 | formats.append({ | |
65 | 'url': source_url, | |
66 | }) | |
d6166a76 | 67 | |
f17a24a6 | 68 | mature = metadata.get('adult') |
a42839e5 S |
69 | if mature is None: |
70 | age_limit = None | |
71 | else: | |
72 | age_limit = 18 if mature is True else 0 | |
73 | ||
d6166a76 PG |
74 | return { |
75 | 'id': channel_id, | |
39ca3b5c | 76 | 'title': title.strip(), |
d6166a76 | 77 | 'is_live': True, |
730c0d12 | 78 | 'channel': channel_id, |
cce889b9 | 79 | 'channel_id': metadata.get('id'), |
730c0d12 | 80 | 'channel_url': 'https://picarto.tv/%s' % channel_id, |
a42839e5 S |
81 | 'age_limit': age_limit, |
82 | 'formats': formats, | |
d6166a76 PG |
83 | } |
84 | ||
85 | ||
86 | class PicartoVodIE(InfoExtractor): | |
a42839e5 S |
87 | _VALID_URL = r'https?://(?:www.)?picarto\.tv/videopopout/(?P<id>[^/?#&]+)' |
88 | _TESTS = [{ | |
89 | 'url': 'https://picarto.tv/videopopout/ArtofZod_2017.12.12.00.13.23.flv', | |
90 | 'md5': '3ab45ba4352c52ee841a28fb73f2d9ca', | |
d6166a76 | 91 | 'info_dict': { |
a42839e5 | 92 | 'id': 'ArtofZod_2017.12.12.00.13.23.flv', |
d6166a76 | 93 | 'ext': 'mp4', |
a42839e5 S |
94 | 'title': 'ArtofZod_2017.12.12.00.13.23.flv', |
95 | 'thumbnail': r're:^https?://.*\.jpg' | |
96 | }, | |
97 | }, { | |
98 | 'url': 'https://picarto.tv/videopopout/Plague', | |
99 | 'only_matching': True, | |
100 | }] | |
d6166a76 PG |
101 | |
102 | def _real_extract(self, url): | |
103 | video_id = self._match_id(url) | |
a42839e5 | 104 | |
d6166a76 PG |
105 | webpage = self._download_webpage(url, video_id) |
106 | ||
a42839e5 S |
107 | vod_info = self._parse_json( |
108 | self._search_regex( | |
109 | r'(?s)#vod-player["\']\s*,\s*(\{.+?\})\s*\)', webpage, | |
a4211baf | 110 | 'vod player'), |
a42839e5 S |
111 | video_id, transform_source=js_to_json) |
112 | ||
113 | formats = self._extract_m3u8_formats( | |
114 | vod_info['vod'], video_id, 'mp4', entry_protocol='m3u8_native', | |
115 | m3u8_id='hls') | |
d6166a76 PG |
116 | |
117 | return { | |
118 | 'id': video_id, | |
119 | 'title': video_id, | |
d6166a76 | 120 | 'thumbnail': vod_info.get('vodThumb'), |
a42839e5 | 121 | 'formats': formats, |
d6166a76 | 122 | } |