mirror of
https://github.com/yt-dlp/yt-dlp
synced 2025-02-07 08:46:30 +01:00
fix live support
This commit is contained in:
parent
97f1f1e4e3
commit
89aa985a47
1 changed files with 50 additions and 57 deletions
|
@ -1,6 +1,12 @@
|
||||||
|
import base64
|
||||||
|
import datetime as dt
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..aes import aes_cbc_encrypt_bytes
|
||||||
|
from ..networking.exceptions import HTTPError
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
clean_html,
|
clean_html,
|
||||||
|
@ -22,6 +28,7 @@ from ..utils.traversal import traverse_obj
|
||||||
|
|
||||||
|
|
||||||
class VidioBaseIE(InfoExtractor):
|
class VidioBaseIE(InfoExtractor):
|
||||||
|
_GEO_COUNTRIES = ['ID']
|
||||||
_LOGIN_URL = 'https://www.vidio.com/users/login'
|
_LOGIN_URL = 'https://www.vidio.com/users/login'
|
||||||
_NETRC_MACHINE = 'vidio'
|
_NETRC_MACHINE = 'vidio'
|
||||||
|
|
||||||
|
@ -67,15 +74,15 @@ class VidioBaseIE(InfoExtractor):
|
||||||
'https://www.vidio.com/auth', None, data=b'')['api_key']
|
'https://www.vidio.com/auth', None, data=b'')['api_key']
|
||||||
self._ua = self.get_param('http_headers')['User-Agent']
|
self._ua = self.get_param('http_headers')['User-Agent']
|
||||||
|
|
||||||
def _call_api(self, url, video_id, note=None):
|
def _call_api(self, url, video_id, note=None, headers=None):
|
||||||
return self._download_json(url, video_id, note=note, headers={
|
return self._download_json(url, video_id, note=note, headers={
|
||||||
'Content-Type': 'application/vnd.api+json',
|
'Content-Type': 'application/vnd.api+json',
|
||||||
'X-API-KEY': self._api_key,
|
'X-API-KEY': self._api_key,
|
||||||
|
**(headers or {}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class VidioIE(VidioBaseIE):
|
class VidioIE(VidioBaseIE):
|
||||||
_GEO_COUNTRIES = ['ID']
|
|
||||||
_VALID_URL = r'https?://(?:www\.)?vidio\.com/(?:watch|embed)/(?P<id>\d+)-(?P<display_id>[^/?#&]+)'
|
_VALID_URL = r'https?://(?:www\.)?vidio\.com/(?:watch|embed)/(?P<id>\d+)-(?P<display_id>[^/?#&]+)'
|
||||||
_EMBED_REGEX = [rf'(?x)<iframe[^>]+\bsrc=[\'"](?P<url>{_VALID_URL})']
|
_EMBED_REGEX = [rf'(?x)<iframe[^>]+\bsrc=[\'"](?P<url>{_VALID_URL})']
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
|
@ -477,29 +484,11 @@ class VidioLiveIE(VidioBaseIE):
|
||||||
'upload_date': '20160421',
|
'upload_date': '20160421',
|
||||||
'tags': [],
|
'tags': [],
|
||||||
'genres': [],
|
'genres': [],
|
||||||
'age_limit': 13,
|
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
|
# Premier-exclusive livestream
|
||||||
'url': 'https://vidio.com/live/733-trans-tv',
|
'url': 'https://vidio.com/live/733-trans-tv',
|
||||||
'info_dict': {
|
'only_matching': True,
|
||||||
'id': '733',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': r're:TRANS TV \d{4}-\d{2}-\d{2} \d{2}:\d{2}',
|
|
||||||
'display_id': 'trans-tv',
|
|
||||||
'uploader': 'transtv',
|
|
||||||
'uploader_id': '551300',
|
|
||||||
'uploader_url': 'https://www.vidio.com/@transtv',
|
|
||||||
'thumbnail': r're:^https?://thumbor\.prod\.vidiocdn\.com/.+\.jpg$',
|
|
||||||
'live_status': 'is_live',
|
|
||||||
'description': r're:^Trans TV adalah stasiun televisi swasta Indonesia.+',
|
|
||||||
'like_count': int,
|
|
||||||
'dislike_count': int,
|
|
||||||
'timestamp': 1461355080,
|
|
||||||
'upload_date': '20160422',
|
|
||||||
'tags': [],
|
|
||||||
'genres': [],
|
|
||||||
'age_limit': 13,
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
# Premier-exclusive livestream
|
# Premier-exclusive livestream
|
||||||
'url': 'https://www.vidio.com/live/6362-tvn',
|
'url': 'https://www.vidio.com/live/6362-tvn',
|
||||||
|
@ -510,55 +499,59 @@ class VidioLiveIE(VidioBaseIE):
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
_WEB_CLIENT_SECRTE = b'dPr0QImQ7bc5o9LMntNba2DOsSbZcjUh'
|
||||||
|
_WEB_CLIENT_IV = b'C8RWsrtFsoeyCyPt'
|
||||||
|
|
||||||
|
def _yield_formats(self, url, video_id):
|
||||||
|
client_id = str(dt.datetime.now().timestamp())[:-3]
|
||||||
|
try:
|
||||||
|
stream_info = self._call_api(
|
||||||
|
f'https://api.vidio.com/livestreamings/{video_id}/stream?initialize=true', video_id,
|
||||||
|
headers={
|
||||||
|
'X-API-KEY': base64.b64encode(aes_cbc_encrypt_bytes(
|
||||||
|
self._api_key.encode(),
|
||||||
|
self._WEB_CLIENT_SECRTE,
|
||||||
|
self._WEB_CLIENT_IV,
|
||||||
|
)),
|
||||||
|
'X-API-Platform': 'web-desktop',
|
||||||
|
'X-Client': client_id,
|
||||||
|
'X-Request-From': url,
|
||||||
|
'X-Secure-Level': 2,
|
||||||
|
'X-Signature': hmac.new(f'V1d10D3v:{client_id}'.encode(), client_id.encode(), hashlib.sha256).hexdigest(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
except ExtractorError as e:
|
||||||
|
if isinstance(e.cause, HTTPError) and e.cause.status == 401:
|
||||||
|
self.raise_login_required('This show requires subscription', metadata_available=True)
|
||||||
|
return []
|
||||||
|
raise
|
||||||
|
|
||||||
|
if m3u8_url := traverse_obj(stream_info, ('data', 'attributes', 'hls', {url_or_none})):
|
||||||
|
yield from self._extract_m3u8_formats(m3u8_url, video_id, ext='mp4', m3u8_id='hls', fatal=False, live=True)
|
||||||
|
if mpd_url := traverse_obj(stream_info, ('data', 'attributes', 'dash', {url_or_none})):
|
||||||
|
yield from self._extract_mpd_formats(mpd_url, video_id, mpd_id='dash', fatal=False)
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id, display_id = self._match_valid_url(url).groups()
|
video_id, display_id = self._match_valid_url(url).groups()
|
||||||
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
attrs = extract_attributes(get_element_html_by_id(
|
||||||
|
f'player-data-{video_id}', self._download_webpage(url, video_id, fatal=False) or ''))
|
||||||
stream_meta = traverse_obj(self._call_api(
|
stream_meta = traverse_obj(self._call_api(
|
||||||
f'https://www.vidio.com/api/livestreamings/{video_id}/detail', video_id),
|
f'https://www.vidio.com/api/livestreamings/{video_id}/detail', video_id),
|
||||||
('livestreamings', 0, {dict}), default={})
|
('livestreamings', 0, {dict}), default={})
|
||||||
tokenized_playlist_urls = self._download_json(
|
|
||||||
f'https://www.vidio.com/live/{video_id}/tokens', video_id,
|
|
||||||
query={'type': 'dash'}, note='Downloading tokenized playlist',
|
|
||||||
errnote='Unable to download tokenized playlist', data=b'')
|
|
||||||
interactions_stream = self._download_json(
|
|
||||||
'https://www.vidio.com/interactions_stream.json', video_id,
|
|
||||||
query={'video_id': video_id, 'type': 'videos'}, note='Downloading stream info',
|
|
||||||
errnote='Unable to download stream info')
|
|
||||||
|
|
||||||
attrs = extract_attributes(get_element_html_by_id(f'player-data-{video_id}', webpage))
|
|
||||||
|
|
||||||
if traverse_obj(attrs, ('data-drm-enabled', {lambda x: x == 'true'})):
|
|
||||||
self.report_drm(video_id)
|
|
||||||
if traverse_obj(attrs, ('data-geoblock', {lambda x: x == 'true'})):
|
|
||||||
self.raise_geo_restricted(
|
|
||||||
'This show isn\'t available in your country', countries=['ID'], metadata_available=True)
|
|
||||||
|
|
||||||
formats = []
|
|
||||||
|
|
||||||
for m3u8_url in traverse_obj([
|
|
||||||
tokenized_playlist_urls.get('hls_url'),
|
|
||||||
interactions_stream.get('source')], (..., {url_or_none})):
|
|
||||||
formats.extend(self._extract_m3u8_formats(m3u8_url, video_id, ext='mp4', m3u8_id='hls'))
|
|
||||||
|
|
||||||
for mpd_url in traverse_obj([
|
|
||||||
tokenized_playlist_urls.get('dash_url'),
|
|
||||||
interactions_stream.get('source_dash')], (..., {url_or_none})):
|
|
||||||
formats.extend(self._extract_mpd_formats(mpd_url, video_id, mpd_id='dash'))
|
|
||||||
|
|
||||||
uploader = attrs.get('data-video-username')
|
|
||||||
uploader_url = f'https://www.vidio.com/@{uploader}'
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'title': attrs.get('data-video-title'),
|
'title': attrs.get('data-video-title'),
|
||||||
'live_status': 'is_live',
|
'live_status': 'is_live',
|
||||||
'formats': formats,
|
'formats': list(self._yield_formats(url, video_id)),
|
||||||
'genres': traverse_obj(attrs, ('data-genres', {str_or_none}, filter, {lambda x: x.split(',')}), default=[]),
|
'genres': traverse_obj(attrs, ('data-genres', {str_or_none}, filter, {lambda x: x.split(',')}), default=[]),
|
||||||
'uploader': uploader,
|
|
||||||
'uploader_id': traverse_obj(attrs, ('data-video-user-id', {str_or_none})),
|
'uploader_id': traverse_obj(attrs, ('data-video-user-id', {str_or_none})),
|
||||||
'uploader_url': uploader_url,
|
**traverse_obj(attrs, ('data-video-username', {lambda x: {
|
||||||
|
'uploader': x,
|
||||||
|
'uploader_url': f'https://www.vidio.com/@{x}',
|
||||||
|
}}), default={}),
|
||||||
'thumbnail': traverse_obj(attrs, ('data-video-image-url', {url_or_none})),
|
'thumbnail': traverse_obj(attrs, ('data-video-image-url', {url_or_none})),
|
||||||
'description': traverse_obj(attrs, ('data-video-description', {str})),
|
'description': traverse_obj(attrs, ('data-video-description', {str})),
|
||||||
'availability': self._availability(needs_premium=(attrs.get('data-access-type') == 'premium')),
|
'availability': self._availability(needs_premium=(attrs.get('data-access-type') == 'premium')),
|
||||||
|
|
Loading…
Add table
Reference in a new issue