2 from __future__
import unicode_literals
7 from .common
import InfoExtractor
8 from ..downloader
.websocket
import has_websockets
26 class TwitCastingIE(InfoExtractor
):
27 _VALID_URL
= r
'https?://(?:[^/]+\.)?twitcasting\.tv/(?P<uploader_id>[^/]+)/(?:movie|twplayer)/(?P<id>\d+)'
29 'url': 'https://twitcasting.tv/ivetesangalo/movie/2357609',
30 'md5': '745243cad58c4681dc752490f7540d7f',
34 'title': 'Live #2357609',
35 'uploader_id': 'ivetesangalo',
36 'description': 'Twitter Oficial da cantora brasileira Ivete Sangalo.',
37 'thumbnail': r
're:^https?://.*\.jpg$',
38 'upload_date': '20110822',
39 'timestamp': 1314010824,
44 'skip_download': True,
47 'url': 'https://twitcasting.tv/mttbernardini/movie/3689740',
51 'title': 'Live playing something #3689740',
52 'uploader_id': 'mttbernardini',
53 'description': 'Salve, io sono Matto (ma con la e). Questa è la mia presentazione, in quanto sono letteralmente matto (nel senso di strano), con qualcosa in più.',
54 'thumbnail': r
're:^https?://.*\.jpg$',
55 'upload_date': '20120212',
56 'timestamp': 1329028024,
61 'skip_download': True,
62 'videopassword': 'abc',
66 def _real_extract(self
, url
):
67 uploader_id
, video_id
= self
._match
_valid
_url
(url
).groups()
69 video_password
= self
.get_param('videopassword')
72 request_data
= urlencode_postdata({
73 'password': video_password
,
75 webpage
= self
._download
_webpage
(
76 url
, video_id
, data
=request_data
,
77 headers
={'Origin': 'https://twitcasting.tv'}
)
79 title
= (clean_html(get_element_by_id('movietitle', webpage
))
80 or self
._html
_search
_meta
(['og:title', 'twitter:title'], webpage
, fatal
=True))
83 m3u8_url
= self
._search
_regex
(
84 r
'data-movie-url=(["\'])(?P
<url
>(?
:(?
!\
1).)+)\
1',
85 webpage, 'm3u8 url
', group='url
', default=None)
87 video_js_data = self._parse_json(self._search_regex(
88 r'data
-movie
-playlist
=(["\'])(?P<url>(?:(?!\1).)+)',
89 webpage, 'movie playlist', group='url', default='[{}]'), video_id)
90 if isinstance(video_js_data, dict):
91 video_js_data = list(video_js_data.values())[0]
92 video_js_data = video_js_data[0]
93 m3u8_url = try_get(video_js_data, lambda x: x['source']['url'])
95 stream_server_data = self._download_json(
96 'https://twitcasting.tv/streamserver.php?target=%s&mode=client' % uploader_id, video_id,
97 'Downloading live info', fatal=False)
99 is_live = 'data-status="online
"' in webpage
101 if not traverse_obj(stream_server_data, 'llfmp4') and is_live:
102 self.raise_login_required(method='cookies')
105 if is_live and not m3u8_url:
106 m3u8_url = 'https://twitcasting.tv/%s/metastream.m3u8' % uploader_id
107 if is_live and has_websockets and stream_server_data:
108 qq = qualities(['base', 'mobilesource', 'main'])
109 streams = traverse_obj(stream_server_data, ('llfmp4', 'streams')) or {}
110 for mode, ws_url in streams.items():
113 'format_id': 'ws-%s' % mode,
116 'protocol': 'websocket_frag', # TwitCasting simply sends moof atom directly over WS
119 thumbnail = video_js_data.get('thumbnailUrl') or self._og_search_thumbnail(webpage)
120 description = clean_html(get_element_by_id(
121 'authorcomment', webpage)) or self._html_search_meta(
122 ['description', 'og:description', 'twitter:description'], webpage)
123 duration = float_or_none(video_js_data.get(
124 'duration'), 1000) or parse_duration(clean_html(
125 get_element_by_class('tw-player-duration-time', webpage)))
126 view_count = str_to_int(self._search_regex(
127 r'Total\s*:\s*([\d,]+)\s*Views', webpage, 'views', None))
128 timestamp = unified_timestamp(self._search_regex(
129 r'data-toggle="true
"[^>]+datetime="([^
"]+)"',
130 webpage, 'datetime
', None))
133 formats.extend(self._extract_m3u8_formats(
134 m3u8_url, video_id, 'mp4
', 'm3u8_native
', m3u8_id='hls
', live=is_live))
135 self._sort_formats(formats)
140 'description
': description,
141 'thumbnail
': thumbnail,
142 'timestamp
': timestamp,
143 'uploader_id
': uploader_id,
144 'duration
': duration,
145 'view_count
': view_count,
151 class TwitCastingLiveIE(InfoExtractor):
152 _VALID_URL = r'https?
://(?
:[^
/]+\
.)?twitcasting\
.tv
/(?P
<id>[^
/]+)/?
(?
:[#?]|$)'
154 'url': 'https://twitcasting.tv/ivetesangalo',
155 'only_matching': True,
158 def _real_extract(self
, url
):
159 uploader_id
= self
._match
_id
(url
)
161 'Downloading live video of user {0}. '
162 'Pass "https://twitcasting.tv/{0}/show" to download the history'.format(uploader_id
))
164 webpage
= self
._download
_webpage
(url
, uploader_id
)
165 current_live
= self
._search
_regex
(
166 (r
'data-type="movie" data-id="(\d+)">',
167 r
'tw-sound-flag-open-link" data-id="(\d+)" style=',),
168 webpage
, 'current live ID', default
=None)
170 raise ExtractorError('The user is not currently live')
171 return self
.url_result('https://twitcasting.tv/%s/movie/%s' % (uploader_id
, current_live
))
174 class TwitCastingUserIE(InfoExtractor
):
175 _VALID_URL
= r
'https?://(?:[^/]+\.)?twitcasting\.tv/(?P<id>[^/]+)/show/?(?:[#?]|$)'
177 'url': 'https://twitcasting.tv/noriyukicas/show',
178 'only_matching': True,
181 def _entries(self
, uploader_id
):
182 base_url
= next_url
= 'https://twitcasting.tv/%s/show' % uploader_id
183 for page_num
in itertools
.count(1):
184 webpage
= self
._download
_webpage
(
185 next_url
, uploader_id
, query
={'filter': 'watchable'}
, note
='Downloading page %d' % page_num
)
186 matches
= re
.finditer(
187 r
'''(?isx)<a\s+class="tw-movie-thumbnail"\s*href="(?P<url>/[^/]+/movie/\d+)"\s*>.+?</a>''',
190 yield self
.url_result(urljoin(base_url
, mobj
.group('url')))
192 next_url
= self
._search
_regex
(
193 r
'<a href="(/%s/show/%d-\d+)[?"]' % (re
.escape(uploader_id
), page_num
),
194 webpage
, 'next url', default
=None)
195 next_url
= urljoin(base_url
, next_url
)
199 def _real_extract(self
, url
):
200 uploader_id
= self
._match
_id
(url
)
201 return self
.playlist_result(
202 self
._entries
(uploader_id
), uploader_id
, '%s - Live History' % uploader_id
)