wp pilot extractors
This commit is contained in:
parent
ac8b9e45fb
commit
64ec930237
|
@ -1618,6 +1618,10 @@ from .weibo import (
|
||||||
from .weiqitv import WeiqiTVIE
|
from .weiqitv import WeiqiTVIE
|
||||||
from .wistia import WistiaIE
|
from .wistia import WistiaIE
|
||||||
from .worldstarhiphop import WorldStarHipHopIE
|
from .worldstarhiphop import WorldStarHipHopIE
|
||||||
|
from .wppilot import (
|
||||||
|
WPPilotIE,
|
||||||
|
WPPilotChannelsIE,
|
||||||
|
)
|
||||||
from .wppl import WpPlIE
|
from .wppl import WpPlIE
|
||||||
from .wsj import (
|
from .wsj import (
|
||||||
WSJIE,
|
WSJIE,
|
||||||
|
|
184
haruhi_dl/extractor/wppilot.py
Normal file
184
haruhi_dl/extractor/wppilot.py
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
std_headers,
|
||||||
|
try_get,
|
||||||
|
ExtractorError,
|
||||||
|
)
|
||||||
|
|
||||||
|
import json
|
||||||
|
import random
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class WPPilotBaseIE(InfoExtractor):
|
||||||
|
_NETRC_MACHINE = 'wppilot'
|
||||||
|
_LOGGED_IN = False
|
||||||
|
|
||||||
|
_VIDEO_URL = 'https://pilot.wp.pl/api/v1/channel/%s'
|
||||||
|
_VIDEO_GUEST_URL = 'https://pilot.wp.pl/api/v1/guest/channel/%s'
|
||||||
|
_VIDEO_LIST_URL = 'https://pilot.wp.pl/api/v1/channels/list'
|
||||||
|
_VIDEO_CLOSE_URL = 'https://pilot.wp.pl/api/v1/channels/close'
|
||||||
|
_LOGIN_URL = 'https://pilot.wp.pl/api/v1/user_auth/login'
|
||||||
|
|
||||||
|
_HEADERS_ATV = {
|
||||||
|
'User-Agent': 'ExoMedia 4.3.0 (43000) / Android 8.0.0 / foster_e',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'X-Version': 'pl.videostar|3.25.0|Android|26|foster_e',
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
}
|
||||||
|
_HEADERS_WEB = {
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
'Referer': 'https://pilot.wp.pl/tv/',
|
||||||
|
}
|
||||||
|
_STREAM_HEADERS_WEB = {
|
||||||
|
'Referer': 'https://pilot.wp.pl/',
|
||||||
|
'Origin': 'https://pilot.wp.pl',
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_initialize(self):
|
||||||
|
self._login()
|
||||||
|
|
||||||
|
def _login(self):
|
||||||
|
username, password = self._get_login_info()
|
||||||
|
if not username:
|
||||||
|
return None
|
||||||
|
|
||||||
|
login = self._download_json(
|
||||||
|
self._LOGIN_URL, None, 'Logging in', 'Unable to log in',
|
||||||
|
headers=self._HEADERS_ATV,
|
||||||
|
data=bytes(json.dumps({
|
||||||
|
'device': 'android_tv',
|
||||||
|
'login': username,
|
||||||
|
'password': password,
|
||||||
|
}).encode('utf-8')))
|
||||||
|
|
||||||
|
error = try_get(login, lambda x: x['_meta']['error']['name'])
|
||||||
|
if error:
|
||||||
|
raise ExtractorError(f'WP login error: "{error}"')
|
||||||
|
self._LOGGED_IN = True
|
||||||
|
|
||||||
|
def _get_channel_list(self, cache=True):
|
||||||
|
if cache is True:
|
||||||
|
cache_res = self._downloader.cache.load('wppilot', 'channel-list')
|
||||||
|
if cache_res:
|
||||||
|
cache_res['_hdl_cached'] = True
|
||||||
|
return cache_res
|
||||||
|
res = self._download_json(
|
||||||
|
self._VIDEO_LIST_URL, None, 'Downloading channel list')
|
||||||
|
self._downloader.cache.store('wppilot', 'channel-list', res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _parse_channel(self, chan, categories):
|
||||||
|
thumbnails = []
|
||||||
|
for key in ('thumbnail', 'thumbnail_mobile', 'thumbnail_mobile_bg', 'icon'):
|
||||||
|
if chan.get(key):
|
||||||
|
thumbnails.append({
|
||||||
|
'id': key,
|
||||||
|
'url': chan[key],
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
'id': str(chan['id']),
|
||||||
|
'title': chan['name'],
|
||||||
|
'categories': [categories[str(i)] for i in chan['categories']],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class WPPilotIE(WPPilotBaseIE):
|
||||||
|
_VALID_URL = r'(?:https?://pilot\.wp\.pl/tv/?#|wppilot:)(?P<id>[a-z\d-]+)'
|
||||||
|
IE_NAME = 'wppilot'
|
||||||
|
|
||||||
|
def _get_channel(self, id_or_slug):
|
||||||
|
video_list = self._get_channel_list(cache=True)
|
||||||
|
key = 'id' if re.match(r'^\d+$', id_or_slug) else 'slug'
|
||||||
|
for video in video_list['data']:
|
||||||
|
if video.get(key) == id_or_slug:
|
||||||
|
return self._parse_channel(video, video_list['_meta']['categories'])
|
||||||
|
# if cached channel not found, download and retry
|
||||||
|
if video_list.get('_hdl_cached') is True:
|
||||||
|
video_list = self._get_channel_list(cache=False)
|
||||||
|
for video in video_list['data']:
|
||||||
|
if video.get(key) == id_or_slug:
|
||||||
|
return self._parse_channel(video, video_list['_meta']['categories'])
|
||||||
|
raise ExtractorError('Channel not found')
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
channel = self._get_channel(video_id)
|
||||||
|
video_id = str(channel['id'])
|
||||||
|
if self._LOGGED_IN:
|
||||||
|
video = self._download_json(
|
||||||
|
self._VIDEO_URL % video_id, video_id, query={
|
||||||
|
'format_id': '2',
|
||||||
|
'device_type': 'android',
|
||||||
|
}, headers=self._HEADERS_ATV, expected_status=(200, 422))
|
||||||
|
else:
|
||||||
|
video = self._download_json(
|
||||||
|
self._VIDEO_GUEST_URL % video_id, video_id, query={
|
||||||
|
'device_type': 'web',
|
||||||
|
}, headers=self._HEADERS_WEB, expected_status=(200))
|
||||||
|
|
||||||
|
stream_token = try_get(video, lambda x: x['_meta']['error']['info']['stream_token'])
|
||||||
|
if stream_token:
|
||||||
|
close = self._download_json(
|
||||||
|
self._VIDEO_CLOSE_URL, video_id, 'Invalidating previous stream session',
|
||||||
|
headers=self._HEADERS_ATV,
|
||||||
|
data=bytes(json.dumps({
|
||||||
|
'channelId': video_id,
|
||||||
|
't': stream_token,
|
||||||
|
}).encode('utf-8')))
|
||||||
|
if try_get(close, lambda x: x['data']['status']) == 'ok':
|
||||||
|
return self.url_result('wppilot:%s' % video_id, ie=WPPilotIE.ie_key())
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
stream_headers = {}
|
||||||
|
if self._LOGGED_IN:
|
||||||
|
ua = self._HEADERS_ATV['User-Agent']
|
||||||
|
else:
|
||||||
|
ua = std_headers['User-Agent']
|
||||||
|
stream_headers['User-Agent'] = ua
|
||||||
|
|
||||||
|
for fmt in video['data']['stream_channel']['streams']:
|
||||||
|
# MPD does not work for some reason
|
||||||
|
# if fmt['type'] == 'dash@live:abr':
|
||||||
|
# formats.extend(
|
||||||
|
# self._extract_mpd_formats(
|
||||||
|
# random.choice(fmt['url']), video_id))
|
||||||
|
if fmt['type'] == 'hls@live:abr':
|
||||||
|
formats.extend(
|
||||||
|
self._extract_m3u8_formats(
|
||||||
|
random.choice(fmt['url']),
|
||||||
|
video_id, headers=stream_headers))
|
||||||
|
for i in range(len(formats)):
|
||||||
|
formats[i]['http_headers'] = stream_headers
|
||||||
|
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
channel['formats'] = formats
|
||||||
|
return channel
|
||||||
|
|
||||||
|
|
||||||
|
class WPPilotChannelsIE(WPPilotBaseIE):
|
||||||
|
_VALID_URL = r'(?:https?://pilot\.wp\.pl/(?:tv/?)?(?:\?[^#]*)?#?|wppilot:)$'
|
||||||
|
IE_NAME = 'wppilot:channels'
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
channel_list = self._get_channel_list()
|
||||||
|
categories = channel_list['_meta']['categories']
|
||||||
|
entries = []
|
||||||
|
for chan in channel_list['data']:
|
||||||
|
entry = self._parse_channel(chan, categories)
|
||||||
|
entry.update({
|
||||||
|
'_type': 'url_transparent',
|
||||||
|
'url': f'wppilot:{chan["id"]}',
|
||||||
|
'ie_key': WPPilotIE.ie_key(),
|
||||||
|
})
|
||||||
|
entries.append(entry)
|
||||||
|
return {
|
||||||
|
'_type': 'playlist',
|
||||||
|
'id': 'wppilot',
|
||||||
|
'entries': entries,
|
||||||
|
'title': 'WP Pilot',
|
||||||
|
}
|
Loading…
Reference in a new issue