3 from .common
import InfoExtractor
12 class PicartoIE(InfoExtractor
):
13 _VALID_URL
= r
'https?://(?:www.)?picarto\.tv/(?P<id>[a-zA-Z0-9]+)'
15 'url': 'https://picarto.tv/Setz',
19 'title': 're:^Setz [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
23 'skip': 'Stream is offline',
27 def suitable(cls
, url
):
28 return False if PicartoVodIE
.suitable(url
) else super().suitable(url
)
30 def _real_extract(self
, url
):
31 channel_id
= self
._match
_id
(url
)
33 data
= self
._download
_json
(
34 'https://ptvintern.picarto.tv/ptvapi', channel_id
, query
={
43 getLoadBalancerUrl(channel_name: "%s") {
46 }''' % (channel_id
, channel_id
), # noqa: UP031
47 }, headers
={'Accept': '*/*', 'Content-Type': 'application/json'})['data']
48 metadata
= data
['channel']
50 if metadata
.get('online') == 0:
51 raise ExtractorError('Stream is offline', expected
=True)
52 title
= metadata
['title']
54 cdn_data
= self
._download
_json
(''.join((
55 update_url(data
['getLoadBalancerUrl']['url'], scheme
='https'),
56 '/stream/json_', metadata
['stream_name'], '.js')),
57 channel_id
, 'Downloading load balancing info')
60 for source
in (cdn_data
.get('source') or []):
61 source_url
= source
.get('url')
64 source_type
= source
.get('type')
65 if source_type
== 'html5/application/vnd.apple.mpegurl':
66 formats
.extend(self
._extract
_m
3u8_formats
(
67 source_url
, channel_id
, 'mp4', m3u8_id
='hls', fatal
=False))
68 elif source_type
== 'html5/video/mp4':
73 mature
= metadata
.get('adult')
77 age_limit
= 18 if mature
is True else 0
81 'title': title
.strip(),
83 'channel': channel_id
,
84 'channel_id': metadata
.get('id'),
85 'channel_url': f
'https://picarto.tv/{channel_id}',
86 'age_limit': age_limit
,
91 class PicartoVodIE(InfoExtractor
):
92 _VALID_URL
= r
'https?://(?:www\.)?picarto\.tv/(?:videopopout|\w+/videos)/(?P<id>[^/?#&]+)'
94 'url': 'https://picarto.tv/videopopout/ArtofZod_2017.12.12.00.13.23.flv',
95 'md5': '3ab45ba4352c52ee841a28fb73f2d9ca',
97 'id': 'ArtofZod_2017.12.12.00.13.23.flv',
99 'title': 'ArtofZod_2017.12.12.00.13.23.flv',
100 'thumbnail': r
're:^https?://.*\.jpg',
102 'skip': 'The VOD does not exist',
104 'url': 'https://picarto.tv/ArtofZod/videos/771008',
105 'md5': 'abef5322f2700d967720c4c6754b2a34',
109 'title': 'Art of Zod - Drawing and Painting',
110 'thumbnail': r
're:^https?://.*\.jpg',
111 'channel': 'ArtofZod',
115 'url': 'https://picarto.tv/videopopout/Plague',
116 'only_matching': True,
119 def _real_extract(self
, url
):
120 video_id
= self
._match
_id
(url
)
122 data
= self
._download
_json
(
123 'https://ptvintern.picarto.tv/ptvapi', video_id
, query
={
125 video(id: "{video_id}") {{
130 video_recording_image_url
136 }, headers
={'Accept': '*/*', 'Content-Type': 'application/json'})['data']['video']
138 file_name
= data
['file_name']
139 netloc
= urllib
.parse
.urlparse(data
['video_recording_image_url']).netloc
141 formats
= self
._extract
_m
3u8_formats
(
142 f
'https://{netloc}/stream/hls/{file_name}/index.m3u8', video_id
, 'mp4', m3u8_id
='hls')
146 **traverse_obj(data
, {
147 'id': ('id', {str_or_none}
),
148 'title': ('title', {str}
),
149 'thumbnail': 'video_recording_image_url',
150 'channel': ('channel', 'name', {str}
),
151 'age_limit': ('adult', {lambda x
: 18 if x
else 0}),