2 from __future__
import unicode_literals
7 from .common
import InfoExtractor
8 from ..downloader
.websocket
import has_websockets
25 class TwitCastingIE(InfoExtractor
):
26 _VALID_URL
= r
'https?://(?:[^/]+\.)?twitcasting\.tv/(?P<uploader_id>[^/]+)/(?:movie|twplayer)/(?P<id>\d+)'
28 'url': 'https://twitcasting.tv/ivetesangalo/movie/2357609',
29 'md5': '745243cad58c4681dc752490f7540d7f',
33 'title': 'Live #2357609',
34 'uploader_id': 'ivetesangalo',
35 'description': 'Twitter Oficial da cantora brasileira Ivete Sangalo.',
36 'thumbnail': r
're:^https?://.*\.jpg$',
37 'upload_date': '20110822',
38 'timestamp': 1314010824,
43 'skip_download': True,
46 'url': 'https://twitcasting.tv/mttbernardini/movie/3689740',
50 'title': 'Live playing something #3689740',
51 'uploader_id': 'mttbernardini',
52 '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ù.',
53 'thumbnail': r
're:^https?://.*\.jpg$',
54 'upload_date': '20120212',
55 'timestamp': 1329028024,
60 'skip_download': True,
61 'videopassword': 'abc',
65 def _real_extract(self
, url
):
66 uploader_id
, video_id
= self
._match
_valid
_url
(url
).groups()
68 video_password
= self
.get_param('videopassword')
71 request_data
= urlencode_postdata({
72 'password': video_password
,
74 webpage
= self
._download
_webpage
(
75 url
, video_id
, data
=request_data
,
76 headers
={'Origin': 'https://twitcasting.tv'}
)
78 title
= (clean_html(get_element_by_id('movietitle', webpage
))
79 or self
._html
_search
_meta
(['og:title', 'twitter:title'], webpage
, fatal
=True))
82 m3u8_url
= self
._search
_regex
(
83 r
'data-movie-url=(["\'])(?P
<url
>(?
:(?
!\
1).)+)\
1',
84 webpage, 'm3u8 url
', group='url
', default=None)
86 video_js_data = self._parse_json(self._search_regex(
87 r'data
-movie
-playlist
=(["\'])(?P<url>(?:(?!\1).)+)',
88 webpage, 'movie playlist', group='url', default='[{}]'), video_id)
89 if isinstance(video_js_data, dict):
90 video_js_data = list(video_js_data.values())[0]
91 video_js_data = video_js_data[0]
92 m3u8_url = try_get(video_js_data, lambda x: x['source']['url'])
94 stream_server_data = self._download_json(
95 'https://twitcasting.tv/streamserver.php?target=%s&mode=client' % uploader_id, video_id,
96 'Downloading live info', fatal=False)
98 is_live = 'data-status="online
"' in webpage
100 if is_live and not m3u8_url:
101 m3u8_url = 'https://twitcasting.tv/%s/metastream.m3u8' % uploader_id
102 if is_live and has_websockets and stream_server_data:
103 qq = qualities(['base', 'mobilesource', 'main'])
104 for mode, ws_url in stream_server_data['llfmp4']['streams'].items():
107 'format_id': 'ws-%s' % mode,
110 'protocol': 'websocket_frag', # TwitCasting simply sends moof atom directly over WS
113 thumbnail = video_js_data.get('thumbnailUrl') or self._og_search_thumbnail(webpage)
114 description = clean_html(get_element_by_id(
115 'authorcomment', webpage)) or self._html_search_meta(
116 ['description', 'og:description', 'twitter:description'], webpage)
117 duration = float_or_none(video_js_data.get(
118 'duration'), 1000) or parse_duration(clean_html(
119 get_element_by_class('tw-player-duration-time', webpage)))
120 view_count = str_to_int(self._search_regex(
121 r'Total\s*:\s*([\d,]+)\s*Views', webpage, 'views', None))
122 timestamp = unified_timestamp(self._search_regex(
123 r'data-toggle="true
"[^>]+datetime="([^
"]+)"',
124 webpage, 'datetime
', None))
127 formats.extend(self._extract_m3u8_formats(
128 m3u8_url, video_id, 'mp4
', 'm3u8_native
', m3u8_id='hls
', live=is_live))
129 self._sort_formats(formats)
134 'description
': description,
135 'thumbnail
': thumbnail,
136 'timestamp
': timestamp,
137 'uploader_id
': uploader_id,
138 'duration
': duration,
139 'view_count
': view_count,
145 class TwitCastingLiveIE(InfoExtractor):
146 _VALID_URL = r'https?
://(?
:[^
/]+\
.)?twitcasting\
.tv
/(?P
<id>[^
/]+)/?
(?
:[#?]|$)'
148 'url': 'https://twitcasting.tv/ivetesangalo',
149 'only_matching': True,
152 def _real_extract(self
, url
):
153 uploader_id
= self
._match
_id
(url
)
155 'Downloading live video of user {0}. '
156 'Pass "https://twitcasting.tv/{0}/show" to download the history'.format(uploader_id
))
158 webpage
= self
._download
_webpage
(url
, uploader_id
)
159 current_live
= self
._search
_regex
(
160 (r
'data-type="movie" data-id="(\d+)">',
161 r
'tw-sound-flag-open-link" data-id="(\d+)" style=',),
162 webpage
, 'current live ID', default
=None)
164 raise ExtractorError('The user is not currently live')
165 return self
.url_result('https://twitcasting.tv/%s/movie/%s' % (uploader_id
, current_live
))
168 class TwitCastingUserIE(InfoExtractor
):
169 _VALID_URL
= r
'https?://(?:[^/]+\.)?twitcasting\.tv/(?P<id>[^/]+)/show/?(?:[#?]|$)'
171 'url': 'https://twitcasting.tv/noriyukicas/show',
172 'only_matching': True,
175 def _entries(self
, uploader_id
):
176 base_url
= next_url
= 'https://twitcasting.tv/%s/show' % uploader_id
177 for page_num
in itertools
.count(1):
178 webpage
= self
._download
_webpage
(
179 next_url
, uploader_id
, query
={'filter': 'watchable'}
, note
='Downloading page %d' % page_num
)
180 matches
= re
.finditer(
181 r
'''(?isx)<a\s+class="tw-movie-thumbnail"\s*href="(?P<url>/[^/]+/movie/\d+)"\s*>.+?</a>''',
184 yield self
.url_result(urljoin(base_url
, mobj
.group('url')))
186 next_url
= self
._search
_regex
(
187 r
'<a href="(/%s/show/%d-\d+)[?"]' % (re
.escape(uploader_id
), page_num
),
188 webpage
, 'next url', default
=None)
189 next_url
= urljoin(base_url
, next_url
)
193 def _real_extract(self
, url
):
194 uploader_id
= self
._match
_id
(url
)
195 return self
.playlist_result(
196 self
._entries
(uploader_id
), uploader_id
, '%s - Live History' % uploader_id
)