{ "data": validate.any( validate.text, dict, { "videoinfo": validate.any( validate.text, { "plflag_list": validate.text, "plflag": validate.text } ) } ) }, validate.get("data")) class Pandatv(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) def _get_streams(self): match = _url_re.match(self.url) channel = match.group("channel") res = http.get(self.url) try: channel = int(channel)
class IDF1(Plugin): DACAST_API_URL = 'https://json.dacast.com/b/{}/{}/{}' DACAST_TOKEN_URL = 'https://services.dacast.com/token/i/b/{}/{}/{}' _video_id_re = re.compile( r""" dacast\('(?P<broadcaster_id>\d+)_(?P<video_type>[a-z]+)_(?P<video_id>\d+)',\s*'replay_content',\s*data\); """, re.VERBOSE) _video_id_alt_re = re.compile( r''' <script\s+ src="https://player.dacast.com/js/player.js"\s+ id="(?P<broadcaster_id>\d+)_(?P<video_type>[cf])_(?P<video_id>\d+)" ''', re.VERBOSE) _player_url = 'http://ssl.p.jwpcdn.com/player/v/7.12.6/jwplayer.flash.swf' _api_schema = validate.Schema( validate.parse_json(), { validate.optional('html5'): validate.all([ { 'src': validate.url() }, ], ), 'hls': validate.url(), 'hds': validate.url() }, validate.transform( lambda x: [update_scheme(IDF1.DACAST_API_URL, x['hls']), x['hds'] ] + [y['src'] for y in x.get('html5', [])])) _token_schema = validate.Schema(validate.parse_json(), {'token': validate.text}, validate.get('token')) _user_agent = useragents.IE_11 def _get_streams(self): res = self.session.http.get(self.url) match = self._video_id_re.search( res.text) or self._video_id_alt_re.search(res.text) if match is None: return broadcaster_id = match.group('broadcaster_id') video_type = match.group('video_type') video_id = match.group('video_id') videos = self.session.http.get(self.DACAST_API_URL.format( broadcaster_id, video_type, video_id), schema=self._api_schema) token = self.session.http.get(self.DACAST_TOKEN_URL.format( broadcaster_id, video_type, video_id), schema=self._token_schema, headers={'referer': self.url}) parsed = [] for video_url in videos: video_url += token # Ignore duplicate video URLs if video_url in parsed: continue parsed.append(video_url) # Ignore HDS streams (broken) if '.m3u8' in video_url: for s in HLSStream.parse_variant_playlist( self.session, video_url).items(): yield s
None, { "streams": validate.all( [{ "quality": validate.any(validate.text, None), "url": validate.url( scheme="http", path=validate.endswith(".m3u8") ), validate.optional("video_encode_id"): validate.text }] ) } ) }, validate.get("stream_data") ) _login_schema = validate.Schema({ "auth": validate.text, "expires": validate.all( validate.text, validate.transform(parse_timestamp) ), "user": { "username": validate.any(validate.text, None), "email": validate.text } }) _session_schema = validate.Schema( { "session_id": validate.text
_embed_url_re = re.compile( '<meta itemprop="embedURL" content="http://www.viagame.com/embed/video/([^"]+)"' ) _store_data_re = re.compile("window.fluxData\s*=\s*JSON.parse\(\"(.+)\"\);") _url_re = re.compile("http(s)?://(www\.)?viagame.com/channels/.+") _store_schema = validate.Schema( { "initialStoresData": [{ "instanceName": validate.text, "storeName": validate.text, "initialData": validate.any(dict, list) }] }, validate.get("initialStoresData") ) _match_store_schema = validate.Schema( { "match": { "id": validate.text, "type": validate.text, "videos": [{ "id": validate.text, "play_id": validate.text, }] } }, validate.get("match") )
/(?P<media_id>[^/]+) )? """, re.VERBOSE) _live_schema = validate.Schema( { "livestream": [{ "media_is_live": validate.all( validate.text, validate.transform(int), validate.transform(bool) ), "media_id": validate.text }], }, validate.get("livestream"), validate.length(1), validate.get(0) ) _player_schema = validate.Schema( { "clip": { "baseUrl": validate.any(None, validate.text), "bitrates": validate.all( validate.filter(lambda b: b.get("url") and b.get("label")), [{ "label": validate.text, "url": validate.text, }], ) },
import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate YOUTUBE_URL = "https://www.youtube.com/watch?v={0}" _url_re = re.compile(r'http(s)?://www\.skai.gr/.*') _youtube_id = re.compile(r'<span\s+itemprop="contentUrl"\s+href="(.*)"></span>', re.MULTILINE) _youtube_url_schema = validate.Schema( validate.all( validate.transform(_youtube_id.search), validate.any( None, validate.all( validate.get(1), validate.text ) ) ) ) class Skai(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) def _get_streams(self): channel_id = http.get(self.url, schema=_youtube_url_schema) if channel_id: return self.session.streams(YOUTUBE_URL.format(channel_id))
def _get_streams(self): channels = self.get_channels() if not channels: log.error("No channels found") return log.debug(f"channels={channels}") channel_id = self.match.group("channel") if channel_id is None: channel_id, channel_code = next(iter(channels.items())) elif channel_id in channels: channel_code = channels[channel_id] else: log.error(f"Unknown channel ID: {channel_id}") return log.info(f"Channel: {channel_code}") json = self.session.http.post( "https://www.htv.com.vn/HTVModule/Services/htvService.aspx", data={ "method": "GetScheduleList", "channelid": channel_id, "template": "AjaxSchedules.xslt", "channelcode": channel_code, "date": date.today().strftime("%d-%m-%Y"), }, schema=validate.Schema( validate.parse_json(), { "success": bool, "chanelUrl": validate.url(), }, ), ) if not json["success"]: log.error("API error: success not true") return hls_url = self.session.http.get( json["chanelUrl"], headers={"Referer": self.url}, schema=validate.Schema( validate.parse_html(), validate.xml_xpath_string( ".//script[contains(text(), 'playlist.m3u8')]/text()"), validate.any( None, validate.all( validate.transform(self.hls_url_re.search), validate.any( None, validate.all(validate.get(1), validate.url())), )), ), ) if hls_url: return HLSStream.parse_variant_playlist( self.session, hls_url, headers={"Referer": "https://hplus.com.vn/"}, )
from streamlink.stream import HLSStream from streamlink.plugin.api import useragents from streamlink.utils import update_scheme HUYA_URL = "http://m.huya.com/%s" _url_re = re.compile(r'http(s)?://(www\.)?huya.com/(?P<channel>[^/]+)', re.VERBOSE) _hls_re = re.compile(r'^\s*<video\s+id="html5player-video"\s+src="(?P<url>[^"]+)"', re.MULTILINE) _hls_schema = validate.Schema( validate.all( validate.transform(_hls_re.search), validate.any( None, validate.all( validate.get('url'), validate.transform(str) ) ) ) ) class Huya(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_streams(self): match = _url_re.match(self.url) channel = match.group("channel")
class Schoolism(Plugin): url_re = re.compile( r"https?://(?:www\.)?schoolism\.com/(viewAssignment|watchLesson).php") login_url = "https://www.schoolism.com/index.php" key_time_url = "https://www.schoolism.com/video-html/key-time.php" playlist_re = re.compile(r"var allVideos\s*=\s*(\[.*\]);", re.DOTALL) js_to_json = partial(re.compile(r'(?!<")(\w+):(?!/)').sub, r'"\1":') fix_brackets = partial(re.compile(r',\s*\}').sub, r'}') playlist_schema = validate.Schema( validate.transform(playlist_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(js_to_json), validate.transform(fix_brackets), # remove invalid , validate.transform(parse_json), [{ "sources": validate.all( [{ validate.optional("playlistTitle"): validate.text, "title": validate.text, "src": validate.text, "type": validate.text, }], # only include HLS streams # validate.filter(lambda s: s["type"] == "application/x-mpegurl") ) }]))) arguments = PluginArguments( PluginArgument("email", required=True, requires=["password"], metavar="EMAIL", help=""" The email associated with your Schoolism account, required to access any Schoolism stream. """), PluginArgument( "password", sensitive=True, metavar="PASSWORD", help="A Schoolism account password to use with --schoolism-email." ), PluginArgument("part", type=int, default=1, metavar="PART", help=""" Play part number PART of the lesson, or assignment feedback video. Defaults is 1. """)) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def login(self, email, password): """ Login to the schoolism account and return the users account :param email: (str) email for account :param password: (str) password for account :return: (str) users email """ if self.options.get("email") and self.options.get("password"): res = self.session.http.post(self.login_url, data={ "email": email, "password": password, "redirect": None, "submit": "Login" }) if res.cookies.get("password") and res.cookies.get("email"): return res.cookies.get("email") else: log.error( "Failed to login to Schoolism, incorrect email/password combination" ) else: log.error( "An email and password are required to access Schoolism streams" ) def _get_streams(self): user = self.login(self.options.get("email"), self.options.get("password")) if user: log.debug("Logged in to Schoolism as {0}", user) res = self.session.http.get( self.url, headers={"User-Agent": useragents.SAFARI_8}) lesson_playlist = self.playlist_schema.validate(res.text) part = self.options.get("part") video_type = "Lesson" if "lesson" in self.url_re.match( self.url).group(1).lower() else "Assignment Feedback" log.info("Attempting to play {0} Part {1}", video_type, part) found = False # make request to key-time api, to get key specific headers _ = self.session.http.get( self.key_time_url, headers={"User-Agent": useragents.SAFARI_8}) for i, video in enumerate(lesson_playlist, 1): if video["sources"] and i == part: found = True for source in video["sources"]: if source['type'] == "video/mp4": yield "live", HTTPStream(self.session, source["src"], headers={ "User-Agent": useragents.SAFARI_8, "Referer": self.url }) elif source['type'] == "application/x-mpegurl": for s in HLSStream.parse_variant_playlist( self.session, source["src"], headers={ "User-Agent": useragents.SAFARI_8, "Referer": self.url }).items(): yield s if not found: log.error("Could not find {0} Part {1}", video_type, part)
""", re.VERBOSE) _live_schema = validate.Schema( { "livestream": [{ "media_user_name": validate.text, validate.optional("media_hosted_media"): object, "media_is_live": validate.all(validate.text, validate.transform(int), validate.transform(bool)), "media_id": validate.text }], }, validate.get("livestream"), validate.length(1), validate.get(0)) _player_schema = validate.Schema({ "clip": { "baseUrl": validate.any(None, validate.text), "bitrates": validate.all( validate.filter(lambda b: b.get("url") and b.get("label")), [{ "label": validate.text, "url": validate.text, }], ) }, validate.optional("playlist"): [{ validate.optional("connectionProvider"):
CHANNEL_INFO_URL = "http://api.plu.cn/tga/streams/%s" QQ_STREAM_INFO_URL = "http://info.zb.qq.com/?cnlid=%d&cmd=2&stream=%d&system=1&sdtfrom=113" PLU_STREAM_INFO_URL = "http://livestream.plu.cn/live/getlivePlayurl?roomId=%d" _quality_re = re.compile(r"\d+x(\d+)$") _url_re = re.compile(r"http://(star|y)\.longzhu\.(?:tv|com)/(m\/)?(?P<domain>[a-z0-9]+)") _channel_schema = validate.Schema( { "data": validate.any(None, { "channel": validate.any(None, { "id": int, "vid": int }) }) }, validate.get("data") ) _plu_schema = validate.Schema( { "playLines": [{ "urls": [{ "securityUrl": validate.url(scheme=validate.any("rtmp", "http")), "resolution": validate.text, "ext": validate.text }] }] } ) _qq_schema = validate.Schema(
}) _media_schema = validate.Schema( { "stream_data": validate.any( None, { "streams": validate.all([{ "quality": validate.text, "url": validate.url(scheme="http", path=validate.endswith(".m3u8")) }], validate.filter(lambda s: s["quality"] != "adaptive")) }) }, validate.get("stream_data")) _login_schema = validate.Schema({ "auth": validate.text, "expires": validate.all(validate.text, validate.transform(parse_timestamp)), "user": { "username": validate.text } }) _session_schema = validate.Schema({"session_id": validate.text}, validate.get("session_id")) class CrunchyrollAPIError(Exception): """Exception thrown by the Crunchyroll API when an error occurs"""
/ (?P<video_id>\d+) )? (?: /(?:clip/)? (?P<clip_name>[\w]+) )? """, re.VERBOSE) _access_token_schema = validate.Schema( { "token": validate.text, "sig": validate.text }, validate.union(( validate.get("sig"), validate.get("token") )) ) _token_schema = validate.Schema( { "chansub": { "restricted_bitrates": validate.all( [validate.text], validate.filter( lambda n: not re.match(r"(.+_)?archives|live|chunked", n) ) ) } }, validate.get("chansub")
# Regex to match video ID _id_re = re.compile(r"""data-video-id=['"](?P<id>[^'"]+)['"]""") _old_id_re = re.compile(r"/(?:video|klipp)/(?P<id>[0-9]+)/") # New video schema used with API call _video_schema = validate.Schema( { "videoReferences": validate.all( [{ "url": validate.text, "format": validate.text }], ), }, validate.get("videoReferences") ) # Old video schema _old_video_schema = validate.Schema( { "video": { "videoReferences": validate.all( [{ "url": validate.text, "playerType": validate.text }], ), } }, validate.get("video"),
class N13TV(Plugin): url_re = re.compile(r"https?://(?:www\.)?13tv\.co\.il/(live|.*?/)") api_url = "https://13tv-api.oplayer.io/api/getlink/" main_js_url_re = re.compile( r'type="text/javascript" src="(.*?main\..+\.js)"') user_id_re = re.compile(r'"data-ccid":"(.*?)"') video_name_re = re.compile(r'"videoRef":"(.*?)"') server_addr_re = re.compile(r'(.*[^/])(/.*)') media_file_re = re.compile(r'(.*)(\.[^\.].*)') live_schema = validate.Schema( validate.all([{ 'Link': validate.url() }], validate.get(0), validate.get('Link'))) vod_schema = validate.Schema( validate.all([{ 'ShowTitle': validate.text, 'ProtocolType': validate.all(validate.text, validate.transform(lambda x: x.replace("://", ""))), 'ServerAddress': validate.text, 'MediaRoot': validate.text, 'MediaFile': validate.text, 'Bitrates': validate.text, 'StreamingType': validate.text, 'Token': validate.all(validate.text, validate.transform(lambda x: x.lstrip("?"))) }], validate.get(0))) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _get_live(self, user_id): res = self.session.http.get(self.api_url, params=dict(userId=user_id, serverType="web", ch=1, cdnName="casttime")) url = self.session.http.json(res, schema=self.live_schema) log.debug("URL={0}".format(url)) return HLSStream.parse_variant_playlist(self.session, url) def _get_vod(self, user_id, video_name): res = self.session.http.get(urljoin(self.api_url, "getVideoByFileName"), params=dict(userId=user_id, videoName=video_name, serverType="web", callback="x")) vod_data = self.session.http.json(res, schema=self.vod_schema) if video_name == vod_data['ShowTitle']: host, base_path = self.server_addr_re.search( vod_data['ServerAddress']).groups() if not host or not base_path: raise PluginError("Could not split 'ServerAddress' components") base_file, file_ext = self.media_file_re.search( vod_data['MediaFile']).groups() if not base_file or not file_ext: raise PluginError("Could not split 'MediaFile' components") media_path = "{0}{1}{2}{3}{4}{5}".format( base_path, vod_data['MediaRoot'], base_file, vod_data['Bitrates'], file_ext, vod_data['StreamingType']) log.debug("Media path={0}".format(media_path)) vod_url = urlunparse((vod_data['ProtocolType'], host, media_path, '', vod_data['Token'], '')) log.debug("URL={0}".format(vod_url)) return HLSStream.parse_variant_playlist(self.session, vod_url) def _get_streams(self): m = self.url_re.match(self.url) url_type = m and m.group(1) log.debug("URL type={0}".format(url_type)) res = self.session.http.get(self.url) if url_type != "live": m = self.video_name_re.search(res.text) video_name = m and m.group(1) if not video_name: raise PluginError('Could not determine video_name') log.debug("Video name={0}".format(video_name)) m = self.main_js_url_re.search(res.text) main_js_path = m and m.group(1) if not main_js_path: raise PluginError('Could not determine main_js_path') log.debug("Main JS path={0}".format(main_js_path)) res = self.session.http.get(urljoin(self.url, main_js_path)) m = self.user_id_re.search(res.text) user_id = m and m.group(1) if not user_id: raise PluginError('Could not determine user_id') log.debug("User ID={0}".format(user_id)) if url_type == "live": return self._get_live(user_id) else: return self._get_vod(user_id, video_name)
import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.plugin.api import useragents from streamlink.stream import HLSStream _url_re = re.compile(r"https?://(www\.)?ok\.ru/live/\d+") _vod_re = re.compile(r";(?P<hlsurl>[^;]+video\.m3u8.+?)\\"") _schema = validate.Schema( validate.transform(_vod_re.search), validate.any( None, validate.all( validate.get("hlsurl"), validate.url() ) ) ) class OK_live(Plugin): """ Support for ok.ru live stream: http://www.ok.ru/live/ """ @classmethod def can_handle_url(cls, url): return _url_re.match(url) is not None def _get_streams(self): headers = {
class Zattoo(Plugin): API_CHANNELS = '{0}/zapi/v2/cached/channels/{1}?details=False' API_HELLO = '{0}/zapi/session/hello' API_HELLO_V2 = '{0}/zapi/v2/session/hello' API_HELLO_V3 = '{0}/zapi/v3/session/hello' API_LOGIN = '******' API_LOGIN_V3 = '{0}/zapi/v3/account/login' API_SESSION = '{0}/zapi/v2/session' API_WATCH = '{0}/zapi/watch' API_WATCH_REC = '{0}/zapi/watch/recording/{1}' API_WATCH_VOD = '{0}/zapi/avod/videos/{1}/watch' STREAMS_ZATTOO = ['dash', 'hls', 'hls5'] TIME_CONTROL = 60 * 60 * 2 TIME_SESSION = 60 * 60 * 24 * 30 _url_re = re.compile(r'''(?x) https?:// (?P<base_url> (?:(?: iptv\.glattvision|www\.(?:myvisiontv|saktv|vtxtv) )\.ch )|(?:(?: mobiltv\.quickline|www\.quantum-tv|zattoo )\.com )|(?:(?: tvonline\.ewe|nettv\.netcologne|tvplus\.m-net )\.de )|(?:(?: player\.waly|www\.(?:1und1|netplus) )\.tv) |www\.bbv-tv\.net |www\.meinewelt\.cc )/ (?: (?: recording(?:s\?recording=|/) | (?:ondemand/)?(?:watch/(?:[^/\s]+)(?:/[^/]+/)) )(?P<recording_id>\d+) | (?: (?:live/|watch/)|(?:channels(?:/\w+)?|guide)\?channel= )(?P<channel>[^/\s]+) | ondemand(?:\?video=|/watch/)(?P<vod_id>[^-]+) ) ''') _app_token_re = re.compile(r"""window\.appToken\s+=\s+'([^']+)'""") _channels_schema = validate.Schema( { 'success': bool, 'channel_groups': [{ 'channels': [ { 'display_alias': validate.text, 'cid': validate.text }, ] }] }, validate.get('channel_groups'), ) _session_schema = validate.Schema( { 'success': bool, 'session': { 'loggedin': bool } }, validate.get('session')) arguments = PluginArguments( PluginArgument("email", requires=["password"], metavar="EMAIL", help=""" The email associated with your zattoo account, required to access any zattoo stream. """), PluginArgument("password", sensitive=True, metavar="PASSWORD", help=""" A zattoo account password to use with --zattoo-email. """), PluginArgument("purge-credentials", action="store_true", help=""" Purge cached zattoo credentials to initiate a new session and reauthenticate. """), PluginArgument('stream-types', metavar='TYPES', type=comma_list_filter(STREAMS_ZATTOO), default=['hls'], help=''' A comma-delimited list of stream types which should be used, the following types are allowed: - {0} Default is "hls". '''.format('\n - '.join(STREAMS_ZATTOO)))) def __init__(self, url): super().__init__(url) self.domain = self._url_re.match(url).group('base_url') self._session_attributes = Cache( filename='plugin-cache.json', key_prefix='zattoo:attributes:{0}'.format(self.domain)) self._uuid = self._session_attributes.get('uuid') self._authed = (self._session_attributes.get('power_guide_hash') and self._uuid and self.session.http.cookies.get( 'pzuid', domain=self.domain) and self.session.http.cookies.get('beaker.session.id', domain=self.domain)) self._session_control = self._session_attributes.get( 'session_control', False) self.base_url = 'https://{0}'.format(self.domain) self.headers = { 'User-Agent': useragents.CHROME, 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest', 'Referer': self.base_url } @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None def _hello(self): log.debug('_hello ...') # a new session is required for the app_token self.session.http.cookies = cookiejar_from_dict({}) if self.base_url == 'https://zattoo.com': app_token_url = 'https://zattoo.com/client/token-2fb69f883fea03d06c68c6e5f21ddaea.json' elif self.base_url == 'https://www.quantum-tv.com': app_token_url = 'https://www.quantum-tv.com/token-4d0d61d4ce0bf8d9982171f349d19f34.json' else: app_token_url = self.base_url res = self.session.http.get(app_token_url) if self.base_url == 'https://www.quantum-tv.com': app_token = self.session.http.json(res)["session_token"] hello_url = self.API_HELLO_V3.format(self.base_url) elif self.base_url == 'https://zattoo.com': app_token = self.session.http.json(res)['app_tid'] hello_url = self.API_HELLO_V2.format(self.base_url) else: match = self._app_token_re.search(res.text) app_token = match.group(1) hello_url = self.API_HELLO.format(self.base_url) if self._uuid: __uuid = self._uuid else: __uuid = str(uuid.uuid4()) self._session_attributes.set('uuid', __uuid, expires=self.TIME_SESSION) if self.base_url == 'https://zattoo.com': params = { 'uuid': __uuid, 'app_tid': app_token, 'app_version': '1.0.0' } else: params = { 'client_app_token': app_token, 'uuid': __uuid, } if self.base_url == 'https://www.quantum-tv.com': params['app_version'] = '3.2028.3' else: params['lang'] = 'en' params['format'] = 'json' res = self.session.http.post(hello_url, headers=self.headers, data=params) def _login(self, email, password): log.debug('_login ... Attempting login as {0}'.format(email)) params = {'login': email, 'password': password, 'remember': 'true'} if self.base_url == 'https://quantum-tv.com': login_url = self.API_LOGIN_V3.format(self.base_url) else: login_url = self.API_LOGIN.format(self.base_url) try: res = self.session.http.post(login_url, headers=self.headers, data=params) except Exception as e: if '400 Client Error' in str(e): raise PluginError( 'Failed to login, check your username/password') raise e data = self.session.http.json(res) self._authed = data['success'] log.debug('New Session Data') self.save_cookies(default_expires=self.TIME_SESSION) self._session_attributes.set('power_guide_hash', data['session']['power_guide_hash'], expires=self.TIME_SESSION) self._session_attributes.set('session_control', True, expires=self.TIME_CONTROL) def _watch(self): log.debug('_watch ...') match = self._url_re.match(self.url) if not match: log.debug('_watch ... no match') return channel = match.group('channel') vod_id = match.group('vod_id') recording_id = match.group('recording_id') params = {'https_watch_urls': True} if channel: watch_url = self.API_WATCH.format(self.base_url) params_cid = self._get_params_cid(channel) if not params_cid: return params.update(params_cid) elif vod_id: log.debug('Found vod_id: {0}'.format(vod_id)) watch_url = self.API_WATCH_VOD.format(self.base_url, vod_id) elif recording_id: log.debug('Found recording_id: {0}'.format(recording_id)) watch_url = self.API_WATCH_REC.format(self.base_url, recording_id) else: log.debug('Missing watch_url') return zattoo_stream_types = self.get_option('stream-types') or ['hls'] for stream_type in zattoo_stream_types: params_stream_type = {'stream_type': stream_type} params.update(params_stream_type) try: res = self.session.http.post(watch_url, headers=self.headers, data=params) except Exception as e: if '404 Client Error' in str(e): log.error('Unfortunately streaming is not permitted in ' 'this country or this channel does not exist.') elif '402 Client Error: Payment Required' in str(e): log.error('Paid subscription required for this channel.') log.info('If paid subscription exist, use --zattoo-purge' '-credentials to start a new session.') elif '403 Client Error' in str(e): log.debug('Force session reset for watch_url') self.reset_session() else: log.error(str(e)) return data = self.session.http.json(res) log.debug('Found data for {0}'.format(stream_type)) if data['success'] and stream_type in ['hls', 'hls5']: for url in data['stream']['watch_urls']: for s in HLSStream.parse_variant_playlist( self.session, url['url']).items(): yield s elif data['success'] and stream_type == 'dash': for url in data['stream']['watch_urls']: for s in DASHStream.parse_manifest(self.session, url['url']).items(): yield s def _get_params_cid(self, channel): log.debug('get channel ID for {0}'.format(channel)) channels_url = self.API_CHANNELS.format( self.base_url, self._session_attributes.get('power_guide_hash')) try: res = self.session.http.get(channels_url, headers=self.headers) except Exception: log.debug('Force session reset for _get_params_cid') self.reset_session() return False data = self.session.http.json(res, schema=self._channels_schema) c_list = [] for d in data: for c in d['channels']: c_list.append(c) cid = [] zattoo_list = [] for c in c_list: zattoo_list.append(c['display_alias']) if c['display_alias'] == channel: cid = c['cid'] log.debug('Available zattoo channels in this country: {0}'.format( ', '.join(sorted(zattoo_list)))) if not cid: cid = channel log.debug('CHANNEL ID: {0}'.format(cid)) return {'cid': cid} def reset_session(self): self._session_attributes.set('power_guide_hash', None, expires=0) self._session_attributes.set('uuid', None, expires=0) self.clear_cookies() self._authed = False def _get_streams(self): email = self.get_option('email') password = self.get_option('password') if self.options.get('purge_credentials'): self.reset_session() log.info('All credentials were successfully removed.') elif (self._authed and not self._session_control): # check every two hours, if the session is actually valid log.debug('Session control for {0}'.format(self.domain)) res = self.session.http.get(self.API_SESSION.format(self.base_url)) res = self.session.http.json(res, schema=self._session_schema) if res['loggedin']: self._session_attributes.set('session_control', True, expires=self.TIME_CONTROL) else: log.debug('User is not logged in') self._authed = False if not self._authed and (not email and not password): log.error( 'A login for Zattoo is required, use --zattoo-email EMAIL' ' --zattoo-password PASSWORD to set them') return if not self._authed: self._hello() self._login(email, password) return self._watch()
COOKIE_PARAMS = ( "devicetype=desktop&" "preferred-player-odm=hlslink&" "preferred-player-live=hlslink" ) _id_re = re.compile(r"/(?:program|direkte|serie/[^/]+)/([^/]+)") _url_re = re.compile(r"https?://(tv|radio).nrk.no/") _api_baseurl_re = re.compile(r'''apiBaseUrl:\s*["'](?P<baseurl>[^"']+)["']''') _schema = validate.Schema( validate.transform(_api_baseurl_re.search), validate.any( None, validate.all( validate.get("baseurl"), validate.url( scheme="http" ) ) ) ) _mediaelement_schema = validate.Schema({ "mediaUrl": validate.url( scheme="http", path=validate.endswith(".m3u8") ) })
STREAM_API_URL = "https://playapi.mtgx.tv/v3/videos/stream/{0}" _swf_url_re = re.compile(r"data-flashplayer-url=\"([^\"]+)\"") _player_data_re = re.compile(r"window.fluxData\s*=\s*JSON.parse\(\"(.+)\"\);") _stream_schema = validate.Schema( validate.any( None, validate.all({"msg": validate.text}), validate.all( { "streams": validate.all( {validate.text: validate.any(validate.text, int, None)}, validate.filter(lambda k, v: isinstance(v, validate.text))) }, validate.get("streams")))) class Viasat(Plugin): """Streamlink Plugin for Viasat""" _iframe_re = re.compile( r"""<iframe.+src=["'](?P<url>[^"']+)["'].+allowfullscreen""") _image_re = re.compile( r"""<meta\sproperty=["']og:image["']\scontent=".+/(?P<stream_id>\d+)/[^/]+\.jpg""" ) _url_re = re.compile( r"""https?://(?:www\.)? (?: juicyplay\.dk
"h264_aac_ts_http_m3u8_http": ( "HLS", HLSStream.parse_variant_playlist ) } _url_re = re.compile(r""" http(s)?://(\w+\.)?zdf.de/ """, re.VERBOSE | re.IGNORECASE) _api_json_re = re.compile(r'''data-zdfplayer-jsb=["'](?P<json>{.+?})["']''', re.S) _api_schema = validate.Schema( validate.transform(_api_json_re.search), validate.any( None, validate.all( validate.get("json"), validate.transform(parse_json), { "content": validate.text, "apiToken": validate.text }, ) ) ) _documents_schema = validate.Schema( { "mainVideoContent": { "http://zdf.de/rels/target": { "http://zdf.de/rels/streams/ptmd": validate.text },
_url_re = re.compile(r"http(s)?://(?:(\w+\.)?ardmediathek.de/tv|mediathek.daserste.de/)") _media_id_re = re.compile(r"/play/(?:media|config)/(\d+)") _media_schema = validate.Schema({ "_mediaArray": [{ "_mediaStreamArray": [{ validate.optional("_server"): validate.text, "_stream": validate.any(validate.text, [validate.text]), "_quality": validate.any(int, validate.text) }] }] }) _smil_schema = validate.Schema( validate.union({ "base": validate.all( validate.xml_find("head/meta"), validate.get("base"), validate.url(scheme="http") ), "cdn": validate.all( validate.xml_find("head/meta"), validate.get("cdn") ), "videos": validate.all( validate.xml_findall("body/seq/video"), [validate.get("src")] ) }) ) class ard_mediathek(Plugin):
STREAM_API_URL = "https://playapi.mtgx.tv/v3/videos/stream/{0}" _swf_url_re = re.compile(r"data-flashplayer-url=\"([^\"]+)\"") _player_data_re = re.compile(r"window.fluxData\s*=\s*JSON.parse\(\"(.+)\"\);") _stream_schema = validate.Schema( validate.any( None, validate.all({"msg": validate.text}), validate.all({ "streams": validate.all( {validate.text: validate.any(validate.text, int, None)}, validate.filter(lambda k, v: isinstance(v, validate.text)) ) }, validate.get("streams")) ) ) class Viasat(Plugin): """Streamlink Plugin for Viasat""" _iframe_re = re.compile(r"""<iframe.+src=["'](?P<url>[^"']+)["'].+allowfullscreen""") _image_re = re.compile(r"""<meta\sproperty=["']og:image["']\scontent=".+/(?P<stream_id>\d+)/[^/]+\.jpg""") _url_re = re.compile(r"""https?://(?:www\.)? (?: juicyplay\.dk | play\.nova\.bg
from streamlink.plugin.api import useragents from streamlink.utils import update_scheme HUYA_URL = "http://m.huya.com/%s" _url_re = re.compile(r'http(s)?://(www\.)?huya.com/(?P<channel>[^/]+)', re.VERBOSE) _hls_re = re.compile( r'^\s*<video\s+id="html5player-video"\s+src="(?P<url>[^"]+)"', re.MULTILINE) _hls_schema = validate.Schema( validate.all( validate.transform(_hls_re.search), validate.any( None, validate.all(validate.get('url'), validate.transform(str))))) class Huya(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_streams(self): match = _url_re.match(self.url) channel = match.group("channel") self.session.http.headers.update({"User-Agent": useragents.IPAD}) # Some problem with SSL on huya.com now, do not use https hls_url = self.session.http.get(HUYA_URL % channel, schema=_hls_schema)
import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream API_BASE = "http://gox.gomexp.com/cgi-bin" API_URL_APP = API_BASE + "/app_api.cgi" API_URL_LIVE = API_BASE + "/gox_live.cgi" _url_re = re.compile(r"http(s)?://(www\.)?gomexp.com") _entries_schema = validate.Schema( validate.xml_findall("./ENTRY/*/[@reftype='live'][@href]"), [validate.get("href")] ) class GOMeXP(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_live_cubeid(self): res = http.get(API_URL_APP, params=dict(mode="get_live")) root = http.xml(res) return root.findtext("./cube/cubeid") def _get_streams(self): cubeid = self._get_live_cubeid()
)? douyu.com/ (?: show/(?P<vid>[^/&?]+)| (?P<channel>[^/&?]+) ) """, re.VERBOSE) _room_id_re = re.compile(r'"room_id\\*"\s*:\s*(\d+),') _room_id_alt_re = re.compile(r'data-onlineid=(\d+)') _room_id_schema = validate.Schema( validate.all( validate.transform(_room_id_re.search), validate.any(None, validate.all(validate.get(1), validate.transform(int))))) _room_id_alt_schema = validate.Schema( validate.all( validate.transform(_room_id_alt_re.search), validate.any(None, validate.all(validate.get(1), validate.transform(int))))) _room_schema = validate.Schema( { "data": validate.any( None, { "show_status": validate.all(validate.text, validate.transform(int)), "rtmp_url":
} _url_re = re.compile(r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?\.com/(?P<username>\w+)(/\d+)?") _channel_schema = validate.Schema( { "CHANNEL": { "RESULT": validate.transform(int), validate.optional("BPWD"): validate.text, validate.optional("BNO"): validate.text, validate.optional("RMD"): validate.text, validate.optional("AID"): validate.text, validate.optional("CDN"): validate.text } }, validate.get("CHANNEL") ) _stream_schema = validate.Schema( { validate.optional("view_url"): validate.url( scheme=validate.any("rtmp", "http") ), "stream_status": validate.text } ) class AfreecaTV(Plugin): login_url = "https://member.afreecatv.com:8111/login/LoginAction.php"
class DLive(Plugin): _re_url = re.compile( r""" https?://(?:www\.)?dlive\.tv/ (?: (?:p/(?P<video>[^/]+)) | (?P<channel>[^/]+) ) """, re.VERBOSE) _re_videoPlaybackUrl = re.compile(r'"playbackUrl"\s*:\s*"([^"]+\.m3u8)"') _schema_userByDisplayName = validate.Schema( { "data": { "userByDisplayName": { "livestream": validate.any(None, {"title": validate.text}), "username": validate.text } } }, validate.get("data"), validate.get("userByDisplayName")) _schema_videoPlaybackUrl = validate.Schema( validate.transform(_re_videoPlaybackUrl.search), validate.any( None, validate.all( validate.get(1), validate.transform(unquote_plus), validate.transform( lambda url: (bytes(url, "utf-8").decode("unicode_escape") if is_py3 else url.decode("unicode_escape"))), validate.url()))) @classmethod def can_handle_url(cls, url): return cls._re_url.match(url) @classmethod def stream_weight(cls, key): weight = QUALITY_WEIGHTS.get(key) if weight: return weight, "dlive" return Plugin.stream_weight(key) def __init__(self, *args, **kwargs): super(DLive, self).__init__(*args, **kwargs) self.author = None self.title = None match = self._re_url.match(self.url) self.video = match.group("video") self.channel = match.group("channel") def get_author(self): return self.author def get_title(self): return self.title def _get_streams_video(self): log.debug("Getting video HLS streams for {0}".format(self.video)) try: hls_url = self.session.http.get( self.url, schema=self._schema_videoPlaybackUrl) if hls_url is None: return except PluginError: return return HLSStream.parse_variant_playlist(self.session, hls_url) def _get_streams_live(self): log.debug("Getting live HLS streams for {0}".format(self.channel)) try: data = json.dumps({ "query": """query {{ userByDisplayName(displayname:"{displayname}") {{ livestream {{ title }} username }} }}""".format(displayname=self.channel) }) res = self.session.http.post("https://graphigo.prd.dlive.tv/", data=data) res = self.session.http.json(res, schema=self._schema_userByDisplayName) if res["livestream"] is None: return except PluginError: return self.author = self.channel self.title = res["livestream"]["title"] hls_url = "https://live.prd.dlive.tv/hls/live/{0}.m3u8".format( res["username"]) return HLSStream.parse_variant_playlist(self.session, hls_url) def _get_streams(self): if self.video: return self._get_streams_video() elif self.channel: return self._get_streams_live()
from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream STREAM_INFO_URL = "https://api.periscope.tv/api/v2/getAccessPublic" STATUS_GONE = 410 STATUS_UNAVAILABLE = (STATUS_GONE,) _url_re = re.compile(r"http(s)?://(www\.)?(periscope|pscp)\.tv/[^/]+/(?P<broadcast_id>[\w\-\=]+)") _stream_schema = validate.Schema( validate.any( None, validate.union({ "hls_url": validate.all( {"hls_url": validate.url(scheme="http")}, validate.get("hls_url") ), }), validate.union({ "replay_url": validate.all( {"replay_url": validate.url(scheme="http")}, validate.get("replay_url") ), }), ), ) class Periscope(Plugin): @classmethod def can_handle_url(cls, url):
def clips(self, clipname): queries = [ self._gql_persisted_query( "VideoAccessToken_Clip", "36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11", slug=clipname), self._gql_persisted_query( "ClipsView", "4480c1dcc2494a17bb6ef64b94a5213a956afb8a45fe314c66b0d04079a93a8f", slug=clipname), self._gql_persisted_query( "ClipsTitle", "f6cca7f2fdfbfc2cecea0c88452500dae569191e58a265f97711f8f2a838f5b4", slug=clipname) ] return self.call( queries, schema=validate.Schema([ validate.all( { "data": { "clip": { "playbackAccessToken": validate.all({ "signature": str, "value": str }, validate.union_get("signature", "value")), "videoQualities": [ validate.all( { "frameRate": validate.transform(int), "quality": str, "sourceURL": validate.url() }, validate.transform(lambda q: ( f"{q['quality']}p{q['frameRate']}", q["sourceURL"]))) ] } } }, validate.get(("data", "clip")), validate.union_get("playbackAccessToken", "videoQualities")), validate.all( { "data": { "clip": { "broadcaster": { "displayName": str }, "game": { "name": str } } } }, validate.get(("data", "clip")), validate.union_get(("broadcaster", "displayName"), ("game", "name"))), validate.all({"data": { "clip": { "title": str } }}, validate.get(("data", "clip", "title"))) ]))
class BBCiPlayer(Plugin): """ Allows streaming of live channels from bbc.co.uk/iplayer/live/* and of iPlayer programmes from bbc.co.uk/iplayer/episode/* """ mediator_re = re.compile(r'window\.__IPLAYER_REDUX_STATE__\s*=\s*({.*?});', re.DOTALL) state_re = re.compile( r'window.__IPLAYER_REDUX_STATE__\s*=\s*({.*?});</script>') account_locals_re = re.compile(r'window.bbcAccount.locals\s*=\s*({.*?});') hash = base64.b64decode( b"N2RmZjc2NzFkMGM2OTdmZWRiMWQ5MDVkOWExMjE3MTk5MzhiOTJiZg==") api_url = "https://open.live.bbc.co.uk/mediaselector/6/select/version/2.0/mediaset/" \ "{platform}/vpid/{vpid}/format/json/atk/{vpid_hash}/asn/1/" platforms = ("pc", "iptv-all") session_url = "https://session.bbc.com/session" auth_url = "https://account.bbc.com/signin" mediator_schema = validate.Schema({"versions": [{ "id": validate.text }]}, validate.get("versions"), validate.get(0), validate.get("id")) mediaselector_schema = validate.Schema( validate.parse_json(), { "media": [{ "connection": validate.all( [{ validate.optional("href"): validate.url(), validate.optional("transferFormat"): validate.text }], validate.filter(lambda c: c.get("href"))), "kind": validate.text }] }, validate.get("media"), validate.filter(lambda x: x["kind"] == "video")) arguments = PluginArguments( PluginArgument("username", requires=["password"], metavar="USERNAME", help="The username used to register with bbc.co.uk."), PluginArgument( "password", sensitive=True, metavar="PASSWORD", help= "A bbc.co.uk account password to use with --bbciplayer-username.", prompt="Enter bbc.co.uk account password"), PluginArgument("hd", action="store_true", help=""" Prefer HD streams over local SD streams, some live programmes may not be broadcast in HD. """), ) def __init__(self, url): super().__init__(url) self.url = urlunparse(urlparse(self.url)._replace(scheme="https")) @classmethod def _hash_vpid(cls, vpid): return sha1(cls.hash + str(vpid).encode("utf8")).hexdigest() def find_vpid(self, url, res=None): """ Find the Video Packet ID in the HTML for the provided URL :param url: URL to download, if res is not provided. :param res: Provide a cached version of the HTTP response to search :type url: string :type res: requests.Response :return: Video Packet ID for a Programme in iPlayer :rtype: string """ log.debug(f"Looking for vpid on {url}") # Use pre-fetched page if available res = res or self.session.http.get(url) m = self.mediator_re.search(res.text) vpid = m and parse_json(m.group(1), schema=self.mediator_schema) return vpid def find_tvip(self, url, master=False): log.debug("Looking for {0} tvip on {1}".format( "master" if master else "", url)) res = self.session.http.get(url) m = self.state_re.search(res.text) data = m and parse_json(m.group(1)) if data: channel = data.get("channel") if master: return channel.get("masterBrand") return channel.get("id") def mediaselector(self, vpid): urls = defaultdict(set) for platform in self.platforms: url = self.api_url.format(vpid=vpid, vpid_hash=self._hash_vpid(vpid), platform=platform) log.debug(f"Info API request: {url}") medias = self.session.http.get(url, schema=self.mediaselector_schema) for media in medias: for connection in media["connection"]: urls[connection.get("transferFormat")].add( connection["href"]) for stream_type, urls in urls.items(): log.debug(f"{len(urls)} {stream_type} streams") for url in list(urls): try: if stream_type == "hls": yield from HLSStream.parse_variant_playlist( self.session, url).items() if stream_type == "dash": yield from DASHStream.parse_manifest( self.session, url).items() log.debug(f" OK: {url}") except Exception: log.debug(f" FAIL: {url}") def login(self, ptrt_url): """ Create session using BBC ID. See https://www.bbc.co.uk/usingthebbc/account/ :param ptrt_url: The snapback URL to redirect to after successful authentication :type ptrt_url: string :return: Whether authentication was successful :rtype: bool """ def auth_check(res): return ptrt_url in ([h.url for h in res.history] + [res.url]) # make the session request to get the correct cookies session_res = self.session.http.get(self.session_url, params=dict(ptrt=ptrt_url)) if auth_check(session_res): log.debug("Already authenticated, skipping authentication") return True res = self.session.http.post(self.auth_url, params=urlparse(session_res.url).query, data=dict( jsEnabled=True, username=self.get_option("username"), password=self.get_option('password'), attempts=0), headers={"Referer": self.url}) return auth_check(res) def _get_streams(self): if not self.get_option("username"): log.error("BBC iPlayer requires an account you must login using " "--bbciplayer-username and --bbciplayer-password") return log.info( "A TV License is required to watch BBC iPlayer streams, see the BBC website for more " "information: https://www.bbc.co.uk/iplayer/help/tvlicence") if not self.login(self.url): log.error( "Could not authenticate, check your username and password") return episode_id = self.match.group("episode_id") channel_name = self.match.group("channel_name") if episode_id: log.debug(f"Loading streams for episode: {episode_id}") vpid = self.find_vpid(self.url) if vpid: log.debug(f"Found VPID: {vpid}") yield from self.mediaselector(vpid) else: log.error(f"Could not find VPID for episode {episode_id}") elif channel_name: log.debug(f"Loading stream for live channel: {channel_name}") if self.get_option("hd"): tvip = self.find_tvip(self.url, master=True) + "_hd" if tvip: log.debug(f"Trying HD stream {tvip}...") try: yield from self.mediaselector(tvip) except PluginError: log.error( "Failed to get HD streams, falling back to SD") else: return tvip = self.find_tvip(self.url) if tvip: log.debug(f"Found TVIP: {tvip}") yield from self.mediaselector(tvip)
class BTV(Plugin): arguments = PluginArguments( PluginArgument("username", metavar="USERNAME", requires=["password"], help=""" A BTV username required to access any stream. """), PluginArgument("password", sensitive=True, metavar="PASSWORD", help=""" A BTV account password to use with --btv-username. """)) url_re = re.compile(r"https?://(?:www\.)?btv\.bg/live/?") api_url = "http://www.btv.bg/lbin/global/player_config.php" check_login_url = "http://www.btv.bg/lbin/userRegistration/check_user_login.php" login_url = "https://www.btv.bg/bin/registration2/login.php?action=login&settings=0" media_id_re = re.compile(r"media_id=(\d+)") src_re = re.compile(r"src: \"(http.*?)\"") api_schema = validate.Schema( validate.all({ "status": "ok", "config": validate.text }, validate.get("config"), validate.all( validate.transform(src_re.search), validate.any(None, validate.get(1), validate.url())))) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def login(self, username, password): res = self.session.http.post(self.login_url, data={ "username": username, "password": password }) if "success_logged_in" in res.text: return True else: return False def get_hls_url(self, media_id): res = self.session.http.get(self.api_url, params=dict(media_id=media_id)) try: return parse_json(res.text, schema=self.api_schema) except PluginError: return def _get_streams(self): if not self.options.get("username") or not self.options.get( "password"): self.logger.error( "BTV requires registration, set the username and password" " with --btv-username and --btv-password") elif self.login(self.options.get("username"), self.options.get("password")): res = self.session.http.get(self.url) media_match = self.media_id_re.search(res.text) media_id = media_match and media_match.group(1) if media_id: self.logger.debug("Found media id: {0}", media_id) stream_url = self.get_hls_url(media_id) if stream_url: return HLSStream.parse_variant_playlist( self.session, stream_url) else: self.logger.error( "Login failed, a valid username and password is required")
except ImportError: from html.parser import HTMLParser def html_unescape(s): parser = HTMLParser() return parser.unescape(s) _url_re = re.compile(r"https?://(?:www\.)?vidio\.com/(?P<type>live|watch)/(?P<id>\d+)-(?P<name>[^/?#&]+)") _clipdata_re = re.compile(r"""data-json-clips\s*=\s*(['"])(.*?)\1""") _schema = validate.Schema( validate.transform(_clipdata_re.search), validate.any( None, validate.all( validate.get(2), validate.transform(html_unescape), validate.transform(parse_json), [{ "sources": [{ "file": validate.url( scheme="http", path=validate.endswith(".m3u8") ) }] }] ) ) ) class Vidio(Plugin):
from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream COOKIE_PARAMS = ("devicetype=desktop&" "preferred-player-odm=hlslink&" "preferred-player-live=hlslink") _id_re = re.compile("/(?:program|direkte|serie/[^/]+)/([^/]+)") _url_re = re.compile("https?://(tv|radio).nrk.no/") _api_baseurl_re = re.compile('apiBaseUrl:\s*"(?P<baseurl>[^"]+)"') _schema = validate.Schema( validate.transform(_api_baseurl_re.search), validate.any( None, validate.all(validate.get("baseurl"), validate.url(scheme="http")))) _mediaelement_schema = validate.Schema( {"mediaUrl": validate.url(scheme="http", path=validate.endswith(".m3u8"))}) class NRK(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_streams(self): # Get the stream type from the url (tv/radio). stream_type = _url_re.match(self.url).group(1).upper() cookie = {"NRK_PLAYER_SETTINGS_{0}".format(stream_type): COOKIE_PARAMS}
from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream, HTTPStream _url_re = re.compile(r"http(s)?://(\w+\.)?seemeplay.ru/") _player_re = re.compile(r""" SMP.(channel|video).player.init\({ \s+file:\s+"([^"]+)" """, re.VERBOSE) _schema = validate.Schema( validate.transform(_player_re.search), validate.any( None, validate.union({ "type": validate.get(1), "url": validate.all( validate.get(2), validate.url(scheme="http"), ), }) ) ) class SeeMePlay(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) def _get_streams(self):
_url_re = re.compile( r""" http(s)?://(www\.)?zhanqi.tv /(?P<channel>[^/]+) """, re.VERBOSE) _room_schema = validate.Schema( { "data": validate.any( None, { "status": validate.all(validate.text, validate.transform(int)), "videoId": validate.text }) }, validate.get("data")) class Zhanqitv(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_streams(self): match = _url_re.match(self.url) channel = match.group("channel") res = http.get(API_URL.format(channel)) room = http.json(res, schema=_room_schema) if not room: self.logger.info("Not a valid room url.")
), validate.optional("hlsvp"): validate.text, validate.optional("live_playback"): validate.transform(bool), validate.optional("reason"): validate.text, "status": validate.text } ) _search_schema = validate.Schema( { "items": [{ "id": { "videoId": validate.text } }] }, validate.get("items") ) _channelid_re = re.compile(r'meta itemprop="channelId" content="([^"]+)"') _livechannelid_re = re.compile(r'meta property="og:video:url" content="([^"]+)') _url_re = re.compile(r""" http(s)?://(\w+\.)?youtube.com (?: (?: /(watch.+v=|embed/|v/) (?P<video_id>[0-9A-z_-]{11}) ) | (?: /(user|channel)/(?P<user>[^/?]+) )
def _schema_canonical(self, data): schema_canonical = validate.Schema( validate.parse_html(), validate.xml_xpath_string(".//link[@rel='canonical'][1]/@href"), validate.transform(self.matcher.match), validate.get("video_id")) return schema_canonical.validate(data)
import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import RTMPStream _url_re = re.compile("http(s)?://(\w+.)?beam.pro/(?P<channel>[^/]+)") CHANNEL_INFO = "https://beam.pro/api/v1/channels/{0}" CHANNEL_MANIFEST = "https://beam.pro/api/v1/channels/{0}/manifest.smil" _assets_schema = validate.Schema( validate.union({ "base": validate.all( validate.xml_find("./head/meta"), validate.get("base"), validate.url(scheme="rtmp") ), "videos": validate.all( validate.xml_findall(".//video"), [ validate.union({ "src": validate.all( validate.get("src"), validate.text ), "height": validate.all( validate.get("height"), validate.text, validate.transform(int) )
class NPO(Plugin): api_url = "http://ida.omroep.nl/app.php/{endpoint}" url_re = re.compile(r"https?://(\w+\.)?(npo\.nl|zapp\.nl|zappelin\.nl)/") media_id_re = re.compile(r'''<npo-player\smedia-id=["'](?P<media_id>[^"']+)["']''') prid_re = re.compile(r'''(?:data(-alt)?-)?prid\s*[=:]\s*(?P<q>["'])(\w+)(?P=q)''') react_re = re.compile(r'''data-react-props\s*=\s*(?P<q>["'])(?P<data>.*?)(?P=q)''') auth_schema = validate.Schema({"token": validate.text}, validate.get("token")) streams_schema = validate.Schema({ "items": [ [{ "label": validate.text, "contentType": validate.text, "url": validate.url(), "format": validate.text }] ] }, validate.get("items"), validate.get(0)) stream_info_schema = validate.Schema(validate.any( validate.url(), validate.all({"errorcode": 0, "url": validate.url()}, validate.get("url")) )) arguments = PluginArguments( PluginArgument( "subtitles", action="store_true", help=""" Include subtitles for the deaf or hard of hearing, if available. """ ) ) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def __init__(self, url): super(NPO, self).__init__(url) self._token = None self.session.http.headers.update({"User-Agent": useragents.CHROME}) def api_call(self, endpoint, schema=None, params=None): url = self.api_url.format(endpoint=endpoint) res = self.session.http.get(url, params=params) return self.session.http.json(res, schema=schema) @property def token(self): if not self._token: self._token = self.api_call("auth", schema=self.auth_schema) return self._token def _get_prid(self, subtitles=False): res = self.session.http.get(self.url) bprid = None # Locate the asset id for the content on the page for alt, _, prid in self.prid_re.findall(res.text): if alt and subtitles: bprid = prid elif bprid is None: bprid = prid if bprid is None: m = self.react_re.search(res.text) if m: data = parse_json(m.group("data").replace(""", '"')) bprid = data.get("mid") if bprid is None: m = self.media_id_re.search(res.text) if m: bprid = m.group('media_id') return bprid def _get_streams(self): asset_id = self._get_prid(self.get_option("subtitles")) if asset_id: self.logger.debug("Found asset id: {0}", asset_id) streams = self.api_call(asset_id, params=dict(adaptive="yes", token=self.token), schema=self.streams_schema) for stream in streams: if stream["format"] in ("adaptive", "hls", "mp4"): if stream["contentType"] == "url": stream_url = stream["url"] else: # using type=json removes the javascript function wrapper info_url = stream["url"].replace("type=jsonp", "type=json") # find the actual stream URL stream_url = self.session.http.json(self.session.http.get(info_url), schema=self.stream_info_schema) if stream["format"] in ("adaptive", "hls"): for s in HLSStream.parse_variant_playlist(self.session, stream_url).items(): yield s elif stream["format"] in ("mp3", "mp4"): yield "vod", HTTPStream(self.session, stream_url)
)? (?: (?P<minutes>\d+)m )? (?: (?P<seconds>\d+)s )? """, re.VERBOSE) _access_token_schema = validate.Schema( { "token": validate.text, "sig": validate.text }, validate.union(( validate.get("sig"), validate.get("token") )) ) _token_schema = validate.Schema( { "chansub": { "restricted_bitrates": validate.all( [validate.text], validate.filter( lambda n: not re.match(r"(.+_)?archives|live|chunked", n) ) ) } }, validate.get("chansub")
class OKru(Plugin): _data_re = re.compile( r'''data-options=(?P<q>["'])(?P<data>{[^"']+})(?P=q)''') _url_re = re.compile(r'''https?://(?:www\.)?ok\.ru/''') _metadata_schema = validate.Schema( validate.transform(parse_json), validate.any( { 'videos': validate.any([], [{ 'name': validate.text, 'url': validate.text, }]), validate.optional('hlsManifestUrl'): validate.text, validate.optional('hlsMasterPlaylistUrl'): validate.text, validate.optional('liveDashManifestUrl'): validate.text, validate.optional('rtmpUrl'): validate.text, }, None)) _data_schema = validate.Schema( validate.all( validate.transform(_data_re.search), validate.get('data'), validate.transform(html_unescape), validate.transform(parse_json), validate.get('flashvars'), validate.any({'metadata': _metadata_schema}, {'metadataUrl': validate.transform(unquote)}, None))) QUALITY_WEIGHTS = { 'full': 1080, '1080': 1080, 'hd': 720, '720': 720, 'sd': 480, '480': 480, '360': 360, 'low': 360, 'lowest': 240, 'mobile': 144, } @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None @classmethod def stream_weight(cls, key): weight = cls.QUALITY_WEIGHTS.get(key) if weight: return weight, 'okru' return Plugin.stream_weight(key) def _get_streams(self): self.session.http.headers.update({ 'User-Agent': useragents.FIREFOX, 'Referer': self.url, }) try: data = self.session.http.get(self.url, schema=self._data_schema) except PluginError: log.error('unable to validate _data_schema for {0}'.format( self.url)) return metadata = data.get('metadata') metadata_url = data.get('metadataUrl') if metadata_url and not metadata: metadata = self.session.http.post(metadata_url, schema=self._metadata_schema) if metadata: log.trace('{0!r}'.format(metadata)) for hls_url in [ metadata.get('hlsManifestUrl'), metadata.get('hlsMasterPlaylistUrl') ]: if hls_url is not None: yield from HLSStream.parse_variant_playlist( self.session, hls_url).items() if metadata.get('videos'): for http_stream in metadata['videos']: http_name = http_stream['name'] http_url = http_stream['url'] try: http_name = '{0}p'.format( self.QUALITY_WEIGHTS[http_name]) except KeyError: pass yield http_name, HTTPStream(self.session, http_url) if metadata.get('rtmpUrl'): yield 'live', RTMPStream(self.session, params={'rtmp': metadata['rtmpUrl']})
_channels_schema = validate.Schema( { "Data": [{ "Slug": validate.text, "StreamingServers": validate.all( [{ "LinkType": validate.text, "Qualities": [ validate.all( { "Streams": validate.all( [ validate.all( {"Stream": validate.text}, validate.get("Stream") ) ], validate.get(0) ) }, validate.get("Streams") ) ], "Server": validate.text }], validate.filter(lambda s: s["LinkType"] in STREAMING_TYPES) ) }] }, validate.get("Data", {})
class UHSClient(object): """ API Client, reverse engineered by observing the interactions between the web browser and the ustream servers. """ API_URL = "http://r{0}-1-{1}-{2}-{3}.ums.ustream.tv" APP_ID, APP_VERSION = 2, 1 api_schama = validate.Schema([{"args": [object], "cmd": validate.text}]) connect_schama = validate.Schema([{ "args": validate.all([{ "host": validate.text, "connectionId": validate.text }], validate.length(1)), "cmd": "tracking" }], validate.length(1), validate.get(0), validate.get("args"), validate.get(0)) module_info_schema = validate.Schema( [validate.get("stream")], validate.filter(lambda r: r is not None)) def __init__(self, session, media_id, application, **options): self.session = session self.session.http.headers.update({"User-Agent": useragents.IPHONE_6}) self.logger = logging.getLogger("streamlink.plugin.ustream.apiclient") self.media_id = media_id self.application = application self.referrer = options.pop("referrer", None) self._host = None self.rsid = self.generate_rsid() self.rpin = self.generate_rpin() self._connection_id = None self._app_id = options.pop("app_id", self.APP_ID) self._app_version = options.pop("app_version", self.APP_VERSION) self._cluster = options.pop("cluster", "live") self._password = options.pop("password") def connect(self, **options): result = self.send_command(type="viewer", appId=self._app_id, appVersion=self._app_version, rsid=self.rsid, rpin=self.rpin, referrer=self.referrer, media=str(self.media_id), application=self.application, schema=self.connect_schama, password=self._password) self._host = "http://{0}".format(result["host"]) self._connection_id = result["connectionId"] self.logger.debug("Got new host={0}, and connectionId={1}", self._host, self._connection_id) return True def poll(self, schema=None, retries=5, timeout=5.0): stime = time.time() try: r = self.send_command(connectionId=self._connection_id, schema=schema, retries=retries, timeout=timeout) except PluginError as err: self.logger.debug("poll took {0:.2f}s: {1}", time.time() - stime, err) else: self.logger.debug("poll took {0:.2f}s", time.time() - stime) return r def generate_rsid(self): return "{0:x}:{1:x}".format(randint(0, 1e10), randint(0, 1e10)) def generate_rpin(self): return "_rpin.{0}".format(randint(0, 1e15)) def send_command(self, schema=None, retries=5, timeout=5.0, **args): res = self.session.http.get(self.host, params=args, headers={ "Referer": self.referrer, "User-Agent": useragents.IPHONE_6 }, retries=retries, timeout=timeout, retry_max_backoff=0.5) return self.session.http.json(res, schema=schema or self.api_schama) @property def host(self): host = self._host or self.API_URL.format(randint( 0, 0xffffff), self.media_id, self.application, "lp-" + self._cluster) return urljoin(host, "/1/ustream")
import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream _url_re = re.compile("http(s)?://(\w+.)?chaturbate.com/[^/?&]+") _playlist_url_re = re.compile("html \+= \"src='(?P<url>[^']+)'\";") _schema = validate.Schema( validate.transform(_playlist_url_re.search), validate.any( None, validate.all( validate.get("url"), validate.url( scheme="http", path=validate.endswith(".m3u8") ) ) ) ) class Chaturbate(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_streams(self): playlist_url = http.get(self.url, schema=_schema) if not playlist_url:
class Rtve(Plugin): secret_key = base64.b64decode("eWVMJmRhRDM=") cdn_schema = validate.Schema( validate.transform(partial(parse_xml, invalid_char_entities=True)), validate.xml_findall(".//preset"), [ validate.union({ "quality": validate.all(validate.getattr("attrib"), validate.get("type")), "urls": validate.all( validate.xml_findall(".//url"), [validate.getattr("text")] ) }) ] ) subtitles_api = "http://www.rtve.es/api/videos/{id}/subtitulos.json" subtitles_schema = validate.Schema({ "page": { "items": [{ "src": validate.url(), "lang": validate.text }] } }, validate.get("page"), validate.get("items")) video_api = "http://www.rtve.es/api/videos/{id}.json" video_schema = validate.Schema({ "page": { "items": [{ "qualities": [{ "preset": validate.text, "height": int }] }] } }, validate.get("page"), validate.get("items"), validate.get(0)) arguments = PluginArguments( PluginArgument("mux-subtitles", is_global=True) ) def __init__(self, url): Plugin.__init__(self, url) self.session.http.headers = {"User-Agent": useragents.SAFARI_8} self.zclient = ZTNRClient(self.secret_key, self.session) def _get_content_id(self): res = self.session.http.get(self.url) for div in itertags(res.text, "div"): if div.attributes.get("data-id"): return int(div.attributes.get("data-id")) else: log.error("Failed to get content_id") def _get_subtitles(self, content_id): res = self.session.http.get(self.subtitles_api.format(id=content_id)) return self.session.http.json(res, schema=self.subtitles_schema) def _get_quality_map(self, content_id): res = self.session.http.get(self.video_api.format(id=content_id)) data = self.session.http.json(res, schema=self.video_schema) qmap = {} for item in data["qualities"]: qname = {"MED": "Media", "HIGH": "Alta", "ORIGINAL": "Original"}.get(item["preset"], item["preset"]) qmap[qname] = f"{item['height']}p" return qmap def _get_streams(self): streams = [] content_id = self._get_content_id() if content_id: log.debug(f"Found content with id: {content_id}") stream_data = self.zclient.get_cdn_list(content_id, schema=self.cdn_schema) quality_map = None for stream in stream_data: for url in stream["urls"]: if ".m3u8" in url: try: streams.extend(HLSStream.parse_variant_playlist(self.session, url).items()) except OSError as err: log.error(str(err)) elif ((url.endswith("mp4") or url.endswith("mov") or url.endswith("avi")) and self.session.http.head(url, raise_for_status=False).status_code == 200): if quality_map is None: # only make the request when it is necessary quality_map = self._get_quality_map(content_id) # rename the HTTP sources to match the HLS sources quality = quality_map.get(stream["quality"], stream["quality"]) streams.append((quality, HTTPStream(self.session, url))) subtitles = None if self.get_option("mux_subtitles"): subtitles = self._get_subtitles(content_id) if subtitles: substreams = {} for i, subtitle in enumerate(subtitles): substreams[subtitle["lang"]] = HTTPStream(self.session, subtitle["src"]) for q, s in streams: yield q, MuxedStream(self.session, s, subtitles=substreams) else: for s in streams: yield s
from streamlink.stream import HLSStream from streamlink.stream.ffmpegmux import MuxedStream, FFMPEGMuxer from streamlink.stream.file import FileStream HDCORE_VERSION = "3.2.0" _url_re = re.compile(r"https?://www.daisuki.net/[^/]+/[^/]+/anime/watch\..+") _flashvars_re = re.compile(r"var\s+flashvars\s*=\s*\{([^}]*?)};", re.DOTALL) _flashvar_re = re.compile(r"""(['"])(.*?)\1\s*:\s*(['"])(.*?)\3""") _clientlibs_re = re.compile(r"""<script.*?src=(['"])(.*?/clientlibs_anime_watch.*?\.js)\1""") _schema = validate.Schema( validate.union({ "flashvars": validate.all( validate.transform(_flashvars_re.search), validate.get(1), validate.transform(_flashvar_re.findall), validate.map(lambda v: (v[1], v[3])), validate.transform(dict), { "s": validate.text, "country": validate.text, "init": validate.text, validate.optional("ss_id"): validate.text, validate.optional("mv_id"): validate.text, validate.optional("device_cd"): validate.text, validate.optional("ss1_prm"): validate.text, validate.optional("ss2_prm"): validate.text, validate.optional("ss3_prm"): validate.text } ),
class Zbiornik(Plugin): SWF_URL = 'https://zbiornik.tv/wowza.swf' _url_re = re.compile( r'^https?://(?:www\.)?zbiornik\.tv/(?P<channel>[^/]+)/?$') _streams_re = re.compile(r'''var\sstreams\s*=\s*(?P<data>\[.+\]);''') _user_re = re.compile(r'''var\suser\s*=\s*(?P<data>\{[^;]+\});''') _user_schema = validate.Schema({'wowzaIam': { 'phash': validate.text, }}, validate.get('wowzaIam')) _streams_schema = validate.Schema([{ 'nick': validate.text, 'broadcasturl': validate.text, 'server': validate.text, 'id': validate.text, }]) @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None def _get_streams(self): log.debug('Version 2018-07-12') log.info('This is a custom plugin. ') channel = self._url_re.match(self.url).group('channel') log.info('Channel: {0}'.format(channel)) self.session.http.headers.update({'User-Agent': useragents.FIREFOX}) self.session.http.parse_cookies('adult=1') res = self.session.http.get(self.url) m = self._streams_re.search(res.text) if not m: log.debug('No streams data found.') return m2 = self._user_re.search(res.text) if not m: log.debug('No user data found.') return _streams = parse_json(m.group('data'), schema=self._streams_schema) _user = parse_json(m2.group('data'), schema=self._user_schema) _x = [] for _s in _streams: if _s.get('nick') == channel: _x = _s break if not _x: log.error('Channel is not available.') return app = 'videochat/?{0}'.format(_user['phash']) rtmp = 'rtmp://{0}/videochat/'.format(_x['server']) params = { 'rtmp': rtmp, 'pageUrl': self.url, 'app': app, 'playpath': _x['broadcasturl'], 'swfVfy': self.SWF_URL, 'live': True } return {'live': RTMPStream(self.session, params=params)}
"layerList": validate.all( [{ "name": validate.text, validate.optional("param"): dict }], validate.filter(lambda l: l["name"] in ("video", "reporting")) ) }] }] }]) _media_schema = validate.Schema( validate.any( _media_inner_schema, validate.all( {"sequence": _media_inner_schema}, validate.get("sequence") ) ) ) _vod_playlist_schema = validate.Schema({ "duration": float, "fragments": [[int, float]], "template": validate.text }) _vod_manifest_schema = validate.Schema({ "alternates": [{ "height": int, "template": validate.text, validate.optional("failover"): [validate.text] }] })
class TVPlayer(Plugin): context_url = "http://tvplayer.com/watch/context" api_url = "http://api.tvplayer.com/api/v2/stream/live" login_url = "https://tvplayer.com/account/login" update_url = "https://tvplayer.com/account/update-detail" dummy_postcode = "SE1 9LT" # location of ITV HQ in London url_re = re.compile( r"https?://(?:www.)?tvplayer.com/(:?watch/?|watch/(.+)?)") stream_attrs_re = re.compile( r'data-(resource|token|channel-id)\s*=\s*"(.*?)"', re.S) data_id_re = re.compile(r'data-id\s*=\s*"(.*?)"', re.S) login_token_re = re.compile(r'input.*?name="token".*?value="(\w+)"') stream_schema = validate.Schema( { "tvplayer": validate.Schema({ "status": u'200 OK', "response": validate.Schema({ "stream": validate.url(scheme=validate.any("http", "https")), validate.optional("drmToken"): validate.any(None, validate.text) }) }) }, validate.get("tvplayer"), validate.get("response")) context_schema = validate.Schema({ "validate": validate.text, validate.optional("token"): validate.text, "platform": { "key": validate.text } }) arguments = PluginArguments( PluginArgument( "email", help="The email address used to register with tvplayer.com.", metavar="EMAIL", requires=["password"]), PluginArgument("password", sensitive=True, help="The password for your tvplayer.com account.", metavar="PASSWORD")) @classmethod def can_handle_url(cls, url): match = TVPlayer.url_re.match(url) return match is not None def __init__(self, url): super(TVPlayer, self).__init__(url) self.session.http.headers.update({"User-Agent": useragents.CHROME}) def authenticate(self, username, password): res = self.session.http.get(self.login_url) match = self.login_token_re.search(res.text) token = match and match.group(1) res2 = self.session.http.post(self.login_url, data=dict(email=username, password=password, token=token), allow_redirects=False) # there is a 302 redirect on a successful login return res2.status_code == 302 def _get_stream_data(self, resource, channel_id, token, service=1): # Get the context info (validation token and platform) self.logger.debug( "Getting stream information for resource={0}".format(resource)) context_res = self.session.http.get(self.context_url, params={ "resource": resource, "gen": token }) context_data = self.session.http.json(context_res, schema=self.context_schema) self.logger.debug("Context data: {0}", str(context_data)) # get the stream urls res = self.session.http.post( self.api_url, data=dict(service=service, id=channel_id, validate=context_data["validate"], token=context_data.get("token"), platform=context_data["platform"]["key"]), raise_for_status=False) return self.session.http.json(res, schema=self.stream_schema) def _get_stream_attrs(self, page): stream_attrs = dict( (k.replace("-", "_"), v.strip('"')) for k, v in self.stream_attrs_re.findall(page.text)) if not stream_attrs.get("channel_id"): m = self.data_id_re.search(page.text) stream_attrs["channel_id"] = m and m.group(1) self.logger.debug("Got stream attributes: {0}", str(stream_attrs)) valid = True for a in ("channel_id", "resource", "token"): if a not in stream_attrs: self.logger.debug("Missing '{0}' from stream attributes", a) valid = False return stream_attrs if valid else {} def _get_streams(self): if self.get_option("email") and self.get_option("password"): self.logger.debug("Logging in as {0}".format( self.get_option("email"))) if not self.authenticate(self.get_option("email"), self.get_option("password")): self.logger.warning("Failed to login as {0}".format( self.get_option("email"))) # find the list of channels from the html in the page self.url = self.url.replace("https", "http") # https redirects to http res = self.session.http.get(self.url) if "enter your postcode" in res.text: self.logger.info( "Setting your postcode to: {0}. " "This can be changed in the settings on tvplayer.com", self.dummy_postcode) res = self.session.http.post( self.update_url, data=dict(postcode=self.dummy_postcode), params=dict(return_url=self.url)) stream_attrs = self._get_stream_attrs(res) if stream_attrs: stream_data = self._get_stream_data(**stream_attrs) if stream_data: if stream_data.get("drmToken"): self.logger.error( "This stream is protected by DRM can cannot be played") return else: return HLSStream.parse_variant_playlist( self.session, stream_data["stream"]) else: if "need to login" in res.text: self.logger.error( "You need to login using --tvplayer-email/--tvplayer-password to view this stream" )
}, None) }, validate.optional("playerUri"): validate.text, validate.optional("viewerPlusSwfUrl"): validate.url(scheme="http"), validate.optional("lsPlayerSwfUrl"): validate.text, validate.optional("hdPlayerSwfUrl"): validate.text }) _smil_schema = validate.Schema(validate.union({ "http_base": validate.all( validate.xml_find("{http://www.w3.org/2001/SMIL20/Language}head/" "{http://www.w3.org/2001/SMIL20/Language}meta" "[@name='httpBase']"), validate.xml_element(attrib={ "content": validate.text }), validate.get("content") ), "videos": validate.all( validate.xml_findall("{http://www.w3.org/2001/SMIL20/Language}body/" "{http://www.w3.org/2001/SMIL20/Language}switch/" "{http://www.w3.org/2001/SMIL20/Language}video"), [ validate.all( validate.xml_element(attrib={ "src": validate.text, "system-bitrate": validate.all( validate.text, validate.transform(int) ) }), validate.transform(
}]), validate.optional("hlsvp"): validate.text, validate.optional("live_playback"): validate.transform(bool), validate.optional("reason"): validate.text, "status": validate.text }) _search_schema = validate.Schema( {"items": [{ "id": { "videoId": validate.text } }]}, validate.get("items")) _channelid_re = re.compile(r'meta itemprop="channelId" content="([^"]+)"') _livechannelid_re = re.compile( r'meta property="og:video:url" content="([^"]+)') _url_re = re.compile( r""" http(s)?://(\w+\.)?youtube.com (?: (?: /(watch.+v=|embed/|v/) (?P<video_id>[0-9A-z_-]{11}) ) | (?: /(user|channel)/(?P<user>[^/?]+)
_url_re = re.compile(""" http(s)?://(www\.)?douyu.com /(?P<channel>[^/]+) """, re.VERBOSE) _room_id_re = re.compile(r'"room_id"\s*:\s*(\d+),') _room_id_alt_re = re.compile(r'data-room_id="(\d+)"') _room_id_schema = validate.Schema( validate.all( validate.transform(_room_id_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(int) ) ) ) ) _room_id_alt_schema = validate.Schema( validate.all( validate.transform(_room_id_alt_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(int) )
/ (?P<video_type>[bcv])(?:ideo)? / (?P<video_id>\d+) )? (?: / (?P<clip_name>[\w]+) )? """, re.VERBOSE) _access_token_schema = validate.Schema( { "token": validate.text, "sig": validate.text }, validate.union((validate.get("sig"), validate.get("token")))) _token_schema = validate.Schema( { "chansub": { "restricted_bitrates": validate.all([validate.text], validate.filter(lambda n: not re.match( r"(.+_)?archives|live|chunked", n))) } }, validate.get("chansub")) _user_schema = validate.Schema( {validate.optional("display_name"): validate.text}, validate.get("display_name")) _video_schema = validate.Schema({ "chunks": { validate.text: [{
import re from streamlink.plugin import Plugin, PluginError, PluginOptions from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream LOGIN_PAGE_URL = "http://www.livestation.com/en/users/new" LOGIN_POST_URL = "http://www.livestation.com/en/sessions.json" _csrf_token_re = re.compile("<meta content=\"([^\"]+)\" name=\"csrf-token\"") _hls_playlist_re = re.compile("<meta content=\"([^\"]+.m3u8)\" property=\"og:video\" />") _url_re = re.compile("http(s)?://(\w+\.)?livestation.com") _csrf_token_schema = validate.Schema( validate.transform(_csrf_token_re.search), validate.any(None, validate.get(1)) ) _hls_playlist_schema = validate.Schema( validate.transform(_hls_playlist_re.search), validate.any( None, validate.all( validate.get(1), validate.url(scheme="http", path=validate.endswith(".m3u8")) ) ) ) _login_schema = validate.Schema({ "email": validate.text, validate.optional("errors"): validate.all( {
class Filmon(Plugin): _channel_id_re = re.compile( r"""channel_id\s*=\s*(?P<quote>['"]?)(?P<value>\d+)(?P=quote)""") _channel_id_schema = validate.Schema( validate.transform(_channel_id_re.search), validate.any(None, validate.get("value"))) quality_weights = {"high": 720, "low": 480} TIME_CHANNEL = 60 * 60 * 24 * 365 def __init__(self, url): super().__init__(url) parsed = urlparse(self.url) if parsed.path.startswith("/channel/"): self.url = urlunparse( parsed._replace(path=parsed.path.replace("/channel/", "/tv/"))) self.api = FilmOnAPI(self.session) @classmethod def stream_weight(cls, key): weight = cls.quality_weights.get(key) if weight: return weight, "filmon" return Plugin.stream_weight(key) def _get_streams(self): channel = self.match.group("channel") vod_id = self.match.group("vod_id") is_group = self.match.group("is_group") if vod_id: data = self.api.vod(vod_id) for _, stream in data["streams"].items(): streams = HLSStream.parse_variant_playlist( self.session, stream["url"]) if not streams: yield stream["quality"], HLSStream(self.session, stream["url"]) else: yield from streams.items() else: if channel and not channel.isdigit(): _id = self.cache.get(channel) if _id is None: _id = self.session.http.get(self.url, schema=self._channel_id_schema) log.debug(f"Found channel ID: {_id}") # do not cache a group url if _id and not is_group: self.cache.set(channel, _id, expires=self.TIME_CHANNEL) else: log.debug(f"Found cached channel ID: {_id}") else: _id = channel if _id is None: raise PluginError( "Unable to find channel ID: {0}".format(channel)) try: data = self.api.channel(_id) for stream in data["streams"]: yield stream["quality"], FilmOnHLS( self.session, channel=_id, quality=stream["quality"]) except Exception: if channel and not channel.isdigit(): self.cache.set(channel, None, expires=0) log.debug(f"Reset cached channel: {channel}") raise
)? """, re.VERBOSE) _channel_id_re = re.compile("\"channelId\":(\d+)") HLS_PLAYLIST_URL = ( "http://iphone-streaming.ustream.tv" "/uhls/{0}/streams/live/iphone/playlist.m3u8" ) RECORDED_URL = "http://tcdn.ustream.tv/video/{0}" RTMP_URL = "rtmp://r{0}-1-{1}-channel-live.ums.ustream.tv:1935/ustream" SWF_URL = "http://static-cdn1.ustream.tv/swf/live/viewer.rsl:505.swf" _module_info_schema = validate.Schema( list, validate.length(1), validate.get(0), dict ) _amf3_array = validate.Schema( validate.any( validate.all( {int: object}, validate.transform(lambda a: list(a.values())), ), list ) ) _recorded_schema = validate.Schema({ validate.optional("stream"): validate.all( _amf3_array, [{
class RTBF(Plugin): GEO_URL = 'https://www.rtbf.be/api/geoloc' TOKEN_URL = 'https://token.rtbf.be/' RADIO_STREAM_URL = 'http://www.rtbfradioplayer.be/radio/liveradio/rtbf/radios/{}/config.json' _url_re = re.compile( r'https?://(?:www\.)?(?:rtbf\.be/auvio/.*\?l?id=(?P<video_id>[0-9]+)#?|rtbfradioplayer\.be/radio/liveradio/.+)' ) _stream_size_re = re.compile(r'https?://.+-(?P<size>\d+p?)\..+?$') _video_player_re = re.compile( r'<iframe\s+class="embed-responsive-item\s+js-embed-iframe".*src="(?P<player_url>.+?)".*?</iframe>', re.DOTALL) _video_stream_data_re = re.compile( r'<div\s+id="js-embed-player"\s+class="js-embed-player\s+embed-player"\s+data-media="(.+?)"' ) _radio_id_re = re.compile(r'var currentStationKey = "(?P<radio_id>.+?)"') _geo_schema = validate.Schema({ 'country': validate.text, 'zone': validate.text }) _video_stream_schema = validate.Schema( validate.transform(_video_stream_data_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(html_unescape), validate.transform(parse_json), { 'geoLocRestriction': validate.text, validate.optional('isLive'): bool, validate.optional('startDate'): validate.text, validate.optional('endDate'): validate.text, 'sources': validate.any([], validate.Schema({ validate.text: validate.any(None, '', validate.url()) })), validate.optional('urlHls'): validate.any(None, '', validate.url()), validate.optional('urlDash'): validate.any(None, '', validate.url()), validate.optional('streamUrlHls'): validate.any(None, '', validate.url()), validate.optional('streamUrlDash'): validate.any(None, '', validate.url()), validate.optional('drm'): bool, }))) _radio_stream_schema = validate.Schema({ 'audioUrls': validate.all([{ 'url': validate.url(), 'mimeType': validate.text }]) }) def check_geolocation(self, geoloc_flag): if geoloc_flag == 'open': return True res = self.session.http.get(self.GEO_URL) data = self.session.http.json(res, schema=self._geo_schema) return data['country'] == geoloc_flag or data['zone'] == geoloc_flag def tokenize_stream(self, url): res = self.session.http.post(self.TOKEN_URL, data={'streams[url]': url}) data = self.session.http.json(res) return data['streams']['url'] @staticmethod def iso8601_to_epoch(date): # Convert an ISO 8601-formatted string date to datetime return datetime.datetime.strptime(date[:-6], '%Y-%m-%dT%H:%M:%S') + \ datetime.timedelta(hours=int(date[-6:-3]), minutes=int(date[-2:])) @classmethod def can_handle_url(cls, url): return RTBF._url_re.match(url) def _get_radio_streams(self): res = self.session.http.get(self.url) match = self._radio_id_re.search(res.text) if match is None: return radio_id = match.group('radio_id') res = self.session.http.get(self.RADIO_STREAM_URL.format(radio_id)) streams = self.session.http.json(res, schema=self._radio_stream_schema) for stream in streams['audioUrls']: match = self._stream_size_re.match(stream['url']) if match is not None: quality = '{}k'.format(match.group('size')) else: quality = stream['mimetype'] yield quality, HTTPStream(self.session, stream['url']) def _get_video_streams(self): res = self.session.http.get(self.url) match = self._video_player_re.search(res.text) if match is None: return player_url = match.group('player_url') stream_data = self.session.http.get(player_url, schema=self._video_stream_schema) if stream_data is None: return # Check geolocation to prevent further errors when stream is parsed if not self.check_geolocation(stream_data['geoLocRestriction']): log.error('Stream is geo-restricted') return # Check whether streams are DRM-protected if stream_data.get('drm', False): log.error('Stream is DRM-protected') return now = datetime.datetime.now() try: if isinstance(stream_data['sources'], dict): urls = [] for profile, url in stream_data['sources'].items(): if not url or url in urls: continue match = self._stream_size_re.match(url) if match is not None: quality = match.group('size') else: quality = profile yield quality, HTTPStream(self.session, url) urls.append(url) hls_url = stream_data.get('urlHls') or stream_data.get( 'streamUrlHls') if hls_url: if stream_data.get('isLive', False): # Live streams require a token hls_url = self.tokenize_stream(hls_url) for stream in HLSStream.parse_variant_playlist( self.session, hls_url).items(): yield stream dash_url = stream_data.get('urlDash') or stream_data.get( 'streamUrlDash') if dash_url: if stream_data.get('isLive', False): # Live streams require a token dash_url = self.tokenize_stream(dash_url) for stream in DASHStream.parse_manifest( self.session, dash_url).items(): yield stream except IOError as err: if '403 Client Error' in str(err): # Check whether video is expired if 'startDate' in stream_data: if now < self.iso8601_to_epoch(stream_data['startDate']): log.error('Stream is not yet available') elif 'endDate' in stream_data: if now > self.iso8601_to_epoch(stream_data['endDate']): log.error('Stream has expired') def _get_streams(self): match = self.can_handle_url(self.url) if match.group('video_id'): return self._get_video_streams() return self._get_radio_streams()
LAPI_URL = "http://g2.live.360.cn/liveplay?stype=flv&channel={}&bid=huajiao&sn={}&sid={}&_rate=xd&ts={}&r={}&_ostype=flash&_delay=0&_sign=null&_ver=13" _url_re = re.compile(r""" http(s)?://(www\.)?huajiao.com /l/(?P<channel>[^/]+) """, re.VERBOSE) _feed_json_re = re.compile(r'^\s*var\s*feed\s*=\s*(?P<feed>{.*})\s*;', re.MULTILINE) _feed_json_schema = validate.Schema( validate.all( validate.transform(_feed_json_re.search), validate.any( None, validate.all( validate.get('feed'), validate.transform(json.loads) ) ) ) ) class Huajiao(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_streams(self): match = _url_re.match(self.url) channel = match.group("channel")
{ "type": "dash", "file": validate.url(scheme="http") }, { "type": "hls", "file": validate.url(scheme="http") }, { "type": "rtmp", "file": validate.text, "streamer": validate.url(scheme="rtmp") } ) ] } ], validate.get(0), validate.get("sources") ) class Rtvs(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) def _get_streams(self): res = http.get(self.url) match = _playlist_url_re.search(res.text) if match is None: return