def test_basic(self): assert validate(1, 1) == 1 assert validate(int, 1) == 1 assert validate(transform(int), "1") == 1 assert validate(text, "abc") == "abc" assert validate(text, u"日本語") == u"日本語" assert validate(transform(text), 1) == "1" assert validate(list, ["a", 1]) == ["a", 1] assert validate(dict, {"a": 1}) == {"a": 1} assert validate(lambda n: 0 < n < 5, 3) == 3
def test_all(self): assert validate(all(int, lambda n: 0 < n < 5), 3) == 3 assert validate(all(transform(int), lambda n: 0 < n < 5), 3.33) == 3
CHANNEL_RESULT_OK = 1 QUALITYS = ["original", "hd", "sd"] QUALITY_WEIGHTS = { "original": 1080, "hd": 720, "sd": 480 } _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") ),
"73": "token" } BLOCKED_MSG_FORMAT = ( "You have crossed the free viewing limit. You have been blocked for " "{0} minutes. Try again in {1} minutes" ) BLOCK_TYPE_VIEWING_LIMIT = 1 BLOCK_TYPE_NO_SLOTS = 11 _url_re = re.compile(r"http(s)?://(\w+\.)?weeb.tv/(channel|online)/(?P<channel>[^/&?]+)") _schema = validate.Schema( dict, validate.map(lambda k, v: (PARAMS_KEY_MAP.get(k, k), v)), validate.any( { "status": validate.transform(int), "rtmp": validate.url(scheme="rtmp"), "playpath": validate.text, "multibitrate": validate.all( validate.transform(int), validate.transform(bool) ), "block_type": validate.transform(int), validate.optional("token"): validate.text, validate.optional("block_time"): validate.text, validate.optional("reconnect_time"): validate.text, }, { "status": validate.transform(int), }, )
import re from streamlink.compat import urlparse from streamlink.plugin import Plugin from streamlink.plugin.api import StreamMapper, http, validate from streamlink.stream import HLSStream, RTMPStream CHANNEL_URL = "http://www.mobileonline.tv/channel.php" _url_re = re.compile("http(s)?://(\w+\.)?(ilive.to|streamlive.to)/.*/(?P<channel>\d+)") _link_re = re.compile("<a href=(\S+) target=\"_blank\"") _schema = validate.Schema( validate.transform(_link_re.findall), ) class StreamLive(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _create_hls_streams(self, url): try: streams = HLSStream.parse_variant_playlist(self.session, url) return streams.items() except IOError as err: self.logger.warning("Failed to extract HLS streams: {0}", err) def _create_rtmp_stream(self, url): parsed = urlparse(url) if parsed.query:
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(lambda e: (e.attrib["src"], e.attrib[ "system-bitrate"]))) ], ) })) class Livestream(Plugin): @classmethod def default_stream_types(cls, streams): return ["akamaihd", "hls"] @classmethod def can_handle_url(self, 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 }, validate.get("session_id") ) class CrunchyrollAPIError(Exception):
from streamlink.plugin import Plugin, PluginError, PluginArguments, PluginArgument from streamlink.plugin.api import validate, useragents from streamlink.plugin.api.utils import itertags, parse_query from streamlink.stream import HTTPStream, HLSStream from streamlink.stream.ffmpegmux import MuxedStream from streamlink.utils import parse_json, search_dict from streamlink.utils.encoding import maybe_decode log = logging.getLogger(__name__) _config_schema = validate.Schema( { validate.optional("player_response"): validate.all( validate.text, validate.transform(parse_json), { validate.optional("streamingData"): { validate.optional("hlsManifestUrl"): validate.text, validate.optional("formats"): [{ "itag": int, validate.optional("url"): validate.text, validate.optional("cipher"): validate.text, "qualityLabel": validate.text }], validate.optional("adaptiveFormats"): [{ "itag": int, "mimeType": validate.all( validate.text, validate.transform( lambda t:
_url_re = re.compile(""" http(s)?://(\w+\.)?aliez.tv (?: /live/[^/]+ )? (?: /video/\d+/[^/]+ )? """, re.VERBOSE) _file_re = re.compile("\"?file\"?:\s+['\"]([^'\"]+)['\"]") _swf_url_re = re.compile("swfobject.embedSWF\(\"([^\"]+)\",") _schema = validate.Schema( validate.union({ "urls": validate.all( validate.transform(_file_re.findall), validate.map(unquote), [validate.url()] ), "swf": validate.all( validate.transform(_swf_url_re.search), validate.any( None, validate.all( validate.get(1), validate.url( scheme="http", path=validate.endswith("swf") ) ) )
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 = {
from streamlink.stream import HLSStream try: from HTMLParser import HTMLParser 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") ) }] }] )
import re from itertools import chain from streamlink.compat import urlparse from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream, HTTPStream, RTMPStream SWF_URL = "http://www.arte.tv/player/v2/jwplayer6/mediaplayer.6.6.swf" _url_re = re.compile("http(s)?://(\w+\.)?arte.tv/") _json_re = re.compile("arte_vp_(?:live-)?url=(['\"])(.+?)\\1") _schema = validate.Schema( validate.transform(_json_re.search), validate.any( None, validate.all( validate.get(2), validate.url(scheme="http") ) ) ) _video_schema = validate.Schema({ "videoJsonPlayer": { "VSR": validate.any( [], { validate.text: { "height": int,
from streamlink.plugin.api import http, validate from streamlink.plugin.api.utils import parse_query from streamlink.stream import RTMPStream VIEW_LIVE_API_URL = "http://api.afreeca.tv/live/view_live.php" VIEW_LIVE_API_URL_TW = "http://api.afreecatv.com.tw/live/view_live.php" VIEW_LIVE_API_URL_JP = "http://api.afreecatv.jp/live/view_live.php" _url_re = re.compile("http(s)?://(\w+\.)?(afreecatv.com.tw|afreeca.tv|afreecatv.jp)/(?P<channel>[\w\-_]+)") _url_re_tw = re.compile("http(s)?://(\w+\.)?(afreecatv.com.tw)/(?P<channel>[\w\-_]+)") _url_re_jp = re.compile("http(s)?://(\w+\.)?(afreecatv.jp)/(?P<channel>[\w\-_]+)") _flashvars_re = re.compile('<param name="flashvars" value="([^"]+)" />') _flashvars_schema = validate.Schema( validate.transform(_flashvars_re.findall), validate.get(0), validate.transform(parse_query), validate.any( { "s": validate.text, "id": validate.text }, {} ) ) _view_live_schema = validate.Schema( { "channel": { "strm": [{ "bps": validate.text,
validate.optional("state"): { "stream": { "qualities": [validate.text], "rootUrl": validate.url(scheme="rtmp") } } }, validate.get("state") ) _vod_schema = validate.Schema( { "name": validate.text, "channel_slug": validate.text, "title": validate.text, "created_at": validate.transform(int) }, ) class GamingLive(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) @classmethod def stream_weight(cls, key): weight = QUALITY_WEIGHTS.get(key) if weight: return weight, "gaminglive" return Plugin.stream_weight(key)
def test_dict_keys(self): assert validate({text: int}, {"a": 1, "b": 2}) == {"a": 1, "b": 2} assert validate({transform(text): transform(int)}, {1: 3.14, 3.14: 1}) == {"1": 3, "3.14": 1}
class Gulli(Plugin): LIVE_PLAYER_URL = 'https://replay.gulli.fr/jwplayer/embedstreamtv' VOD_PLAYER_URL = 'https://replay.gulli.fr/jwplayer/embed/{0}' _url_re = re.compile( r'https?://replay\.gulli\.fr/(?:Direct|.+/(?P<video_id>VOD[0-9]+))') _playlist_re = re.compile(r'sources: (\[.+?\])', re.DOTALL) _vod_video_index_re = re.compile( r'jwplayer\(idplayer\).playlistItem\((?P<video_index>[0-9]+)\)') _mp4_bitrate_re = re.compile(r'.*_(?P<bitrate>[0-9]+)\.mp4') _video_schema = validate.Schema( validate.all( validate.transform( lambda x: re.sub(r'"?file"?:\s*[\'"](.+?)[\'"],?', r'"file": "\1"', x, flags=re.DOTALL)), validate.transform( lambda x: re.sub(r'"?\w+?"?:\s*function\b.*?(?<={).*(?=})', "", x, flags=re.DOTALL)), validate.transform(parse_json), [validate.Schema({'file': validate.url()})])) @classmethod def can_handle_url(cls, url): return Gulli._url_re.match(url) def _get_streams(self): match = self._url_re.match(self.url) video_id = match.group('video_id') if video_id is not None: # VOD live = False player_url = self.VOD_PLAYER_URL.format(video_id) else: # Live live = True player_url = self.LIVE_PLAYER_URL res = self.session.http.get(player_url) playlist = re.findall(self._playlist_re, res.text) index = 0 if not live: # Get the index for the video on the playlist match = self._vod_video_index_re.search(res.text) if match is None: return index = int(match.group('video_index')) if not playlist: return videos = self._video_schema.validate(playlist[index]) for video in videos: video_url = video['file'] # Ignore non-supported MSS streams if 'isml/Manifest' in video_url: continue try: if '.m3u8' in video_url: for stream in HLSStream.parse_variant_playlist( self.session, video_url).items(): yield stream elif '.mp4' in video_url: match = self._mp4_bitrate_re.match(video_url) if match is not None: bitrate = '%sk' % match.group('bitrate') else: bitrate = 'vod' yield bitrate, HTTPStream(self.session, video_url) except IOError as err: if '403 Client Error' in str(err): self.logger.error( 'Failed to access stream, may be due to geo-restriction' ) raise
class Reuters(Plugin): _url_re = re.compile(r'https?://(.*?\.)?reuters\.(com|tv)') _id_re = re.compile(r'(/l/|id=)(?P<id>.*?)(/|\?|$)') _iframe_url = 'https://www.reuters.tv/l/{0}/?nonav=true' _hls_re = re.compile(r'''(?<!')https://[^"';!<>]+\.m3u8''') _json_re = re.compile(r'''(?P<data>{.*});''') _data_schema = validate.Schema( validate.transform(_json_re.search), validate.any( None, validate.all( validate.get('data'), validate.transform(parse_json), { 'title': validate.text, 'items': [{ 'title': validate.text, 'type': validate.text, 'resources': [{ 'mimeType': validate.text, 'uri': validate.url(), validate.optional('protocol'): validate.text, validate.optional('entityType'): validate.text, }] }], }))) def __init__(self, url): super().__init__(url) self.session.http.headers.update({'User-Agent': useragents.FIREFOX}) self.title = None @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None def get_title(self): if not self.title: self._get_data() return self.title def _get_data(self): res = self.session.http.get(self.url) for script in itertags(res.text, 'script'): if script.attributes.get( 'type' ) == 'text/javascript' and '#rtvIframe' in script.text: m = self._id_re.search(self.url) if m and m.group('id'): log.debug('ID: {0}'.format(m.group('id'))) res = self.session.http.get( self._iframe_url.format(m.group('id'))) for script in itertags(res.text, 'script'): if script.attributes.get( 'type') == 'text/javascript' and 'RTVJson' in script.text: data = self._data_schema.validate(script.text) if not data: continue self.title = data['title'] for item in data['items']: if data['title'] == item['title']: log.trace('{0!r}'.format(item)) log.debug('Type: {0}'.format(item['type'])) for res in item['resources']: if res['mimeType'] == 'application/x-mpegURL': return res['uri'] # fallback for title in itertags(res.text, 'title'): self.title = title.text m = self._hls_re.search(res.text) if not m: log.error('Unsupported PageType.') return return m.group(0) def _get_streams(self): hls_url = self._get_data() if not hls_url: return log.debug('URL={0}'.format(hls_url)) return HLSStream.parse_variant_playlist(self.session, hls_url)
import re from streamlink.compat import urlparse 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):
class OKru(Plugin): _data_re = re.compile( r'''data-options=(?P<q>["'])(?P<data>{[^"']+})(?P=q)''') _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 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({'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']})
import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate 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)
class Picarto(Plugin): CHANNEL_API_URL = "https://api.picarto.tv/v1/channel/name/{channel}" VIDEO_API_URL = "https://picarto.tv/process/channel" RTMP_URL = "rtmp://{server}:1935/play/" RTMP_PLAYPATH = "golive+{channel}?token={token}" HLS_URL = "https://{server}/hls/{channel}/index.m3u8?token={token}" # Regex for all usable URLs _url_re = re.compile( r""" https?://(?:\w+\.)?picarto\.tv/(?:videopopout/)?([^&?/]+) """, re.VERBOSE) # Regex for VOD extraction _vod_re = re.compile(r'''(?<=#vod-player", )(\{.*?\})''') data_schema = validate.Schema( validate.transform(_vod_re.search), validate.any( None, validate.all(validate.get(0), validate.transform(parse_json), { "vod": validate.url(), }))) @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None def _create_hls_stream(self, server, channel, token): streams = HLSStream.parse_variant_playlist(self.session, self.HLS_URL.format( server=server, channel=channel, token=token), verify=False) if len(streams) > 1: self.logger.debug("Multiple HLS streams found") return streams elif len(streams) == 0: self.logger.warning("No HLS streams found when expected") return {} else: # one HLS streams, rename it to live return {"live": list(streams.values())[0]} def _create_flash_stream(self, server, channel, token): params = { "rtmp": self.RTMP_URL.format(server=server), "playpath": self.RTMP_PLAYPATH.format(token=token, channel=channel) } return RTMPStream(self.session, params=params) def _get_vod_stream(self, page): data = self.data_schema.validate(page.text) if data: return HLSStream.parse_variant_playlist(self.session, data["vod"]) def _get_streams(self): url_channel_name = self._url_re.match(self.url).group(1) # Handle VODs first, since their "channel name" is different if url_channel_name.endswith(".flv"): self.logger.debug("Possible VOD stream...") page = self.session.http.get(self.url) vod_streams = self._get_vod_stream(page) if vod_streams: for s in vod_streams.items(): yield s return else: self.logger.warning("Probably a VOD stream but no VOD found?") ci = self.session.http.get( self.CHANNEL_API_URL.format(channel=url_channel_name), raise_for_status=False) if ci.status_code == 404: self.logger.error( "The channel {0} does not exist".format(url_channel_name)) return channel_api_json = json.loads(ci.text) if channel_api_json["online"] != True: self.logger.error("The channel {0} is currently offline".format( url_channel_name)) return server = None token = "public" channel = channel_api_json["name"] # Extract preferred edge server and available techs from the undocumented channel API channel_server_res = self.session.http.post( self.VIDEO_API_URL, data={"loadbalancinginfo": channel}) info_json = json.loads(channel_server_res.text) pref = info_json["preferedEdge"] for i in info_json["edges"]: if i["id"] == pref: server = i["ep"] break self.logger.debug( "Using load balancing server {0} : {1} for channel {2}", pref, server, channel) for i in info_json["techs"]: if i["label"] == "HLS": for s in self._create_hls_stream(server, channel, token).items(): yield s elif i["label"] == "RTMP Flash": stream = self._create_flash_stream(server, channel, token) yield "live", stream
if not formatsmap: return formats for format in formatsmap.split(","): s = format.split("/") (w, h) = s[1].split("x") formats[int(s[0])] = "{0}p".format(h) return formats _config_schema = validate.Schema( { validate.optional("fmt_list"): validate.all( validate.text, validate.transform(parse_fmt_list) ), validate.optional("url_encoded_fmt_stream_map"): validate.all( validate.text, validate.transform(parse_stream_map), [{ "itag": validate.all( validate.text, validate.transform(int) ), "quality": validate.text, "url": validate.url(scheme="http"), validate.optional("s"): validate.text, validate.optional("stereo3d"): validate.all( validate.text, validate.transform(int),
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))
) _video_flashvars_re = re.compile( "<embed width=\"486\" height=\"326\" flashvars=\"([^\"]+)\"" ) _live_schema = validate.Schema({ "streams": [{ "name": validate.text, "quality": validate.text, "url": validate.url(scheme="rtmp") }] }) _schema = validate.Schema( validate.union({ "export_url": validate.all( validate.transform(_live_export_re.search), validate.any( None, validate.get(1), ) ), "video_flashvars": validate.all( validate.transform(_video_flashvars_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(parse_query), { "_111pix_serverURL": validate.url(scheme="rtmp"), "en_flash_providerName": validate.text
from streamlink.plugin import Plugin from streamlink.plugin.api import validate from streamlink.stream import HLSStream 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")
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( lambda e: (e.attrib["src"], e.attrib["system-bitrate"]) ) ) ], ) })) class Livestream(Plugin): @classmethod def default_stream_types(cls, streams): return ["akamaihd", "hls"]
import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream _url_re = re.compile(r"http(s)?://(www\.)?openrec.tv/(live|movie)/[^/?&]+") _playlist_url_re = re.compile(r"data-(source)?file=\"(?P<url>[^\"]+)\"") _schema = validate.Schema(validate.transform(_playlist_url_re.findall), [ validate.union({ "isSource": validate.all(validate.get(0), validate.transform(lambda s: s == "source")), "url": validate.all( validate.get(1), validate.url(scheme="http", path=validate.endswith(".m3u8"))) }) ]) class OPENRECtv(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) @classmethod def stream_weight(cls, stream): if stream == "source": return 1080 + 1, "openrectv" return Plugin.stream_weight(stream)
) 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, [{ "name": validate.text, "streams": validate.all( _amf3_array, [{ "streamName": validate.text, "bitrate": float, }],
_quality_re = re.compile("(\d+p)$") _url_re = re.compile(""" http(s)?://(www\.)?hitbox.tv /(?P<channel>[^/]+) (?: /(?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")),
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(Filmon, self).__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 is_py3: adapter = TLSSecLevel1Adapter() self.session.http.mount("https://filmon.com", adapter) self.session.http.mount("https://www.filmon.com", adapter) self.session.http.mount("https://vms-admin.filmon.com/", adapter) # get cookies self.session.http.get(self.url) if vod_id: data = self.api.vod(vod_id) for _, stream in data["streams"].items(): if stream["url"].endswith(".m3u8"): streams = HLSStream.parse_variant_playlist(self.session, stream["url"]) if not streams: yield stream["quality"], HLSStream(self.session, stream["url"]) else: for s in streams.items(): yield s elif stream["url"].endswith(".mp4"): yield stream["quality"], HTTPStream(self.session, stream["url"]) else: log.error("Unsupported stream type") return 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("Found channel ID: {0}".format(_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("Found cached channel ID: {0}".format(_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("Reset cached channel: {0}".format(channel)) raise
class CeskatelevizeAPI2: _player_api = 'https://playlist.ceskatelevize.cz/' _url_re = re.compile(r'http(s)?://([^.]*.)?ceskatelevize.cz') _playlist_info_re = re.compile( r'{\s*"type":\s*"([a-z]+)",\s*"id":\s*"(\w+)"') _playlist_schema = validate.Schema({ "CODE": validate.contains("OK"), "RESULT": { "playlist": [{ "streamUrls": { "main": validate.url(), } }] } }) _ctcomp_re = re.compile( r'data-ctcomp="Video"\sdata-video-id="(?P<val1>[^"]*)"\sdata-ctcomp-data="(?P<val2>[^"]+)">' ) _ctcomp_schema = validate.Schema( validate.text, validate.transform(_ctcomp_re.findall), validate.transform( lambda vl: [{ "video-id": v[0], "ctcomp-data": json.loads(html_unescape(v[1])) } for v in vl])) _playlist_info_schema = validate.Schema({ "type": validate.text, "id": validate.any(validate.text, int), "key": validate.text, "date": validate.text, "requestSource": validate.text, "drm": int, validate.optional("canBePlay"): int, validate.optional("assetId"): validate.text, "quality": validate.text, validate.optional("region"): int }) def __init__(self, session, url, res=None): self.session = session self.url = url self.response = res def _get_streams(self): if self.response is None: infos = self.session.http.get(self.url, schema=self._ctcomp_schema) else: infos = self.session.http.json(self.response, schema=self._ctcomp_schema) if not infos: # playlist infos not found raise PluginError('Cannot find playlist infos!') vod_prio = len(infos) == 2 for info in infos: try: pl = info['ctcomp-data']['source']['playlist'][0] except KeyError: raise PluginError('Cannot find playlist info!') pl = self._playlist_info_schema.validate(pl) if vod_prio and pl['type'] != 'VOD': continue log.trace('{0!r}'.format(info)) if pl['type'] == 'LIVE': data = { "contentType": "live", "items": [{ "id": pl["id"], "assetId": pl["assetId"], "key": pl["key"], "playerType": "dash", "date": pl["date"], "requestSource": pl["requestSource"], "drm": pl["drm"], "quality": pl["quality"], }] } elif pl['type'] == 'VOD': data = { "contentType": "vod", "items": [{ "id": pl["id"], "key": pl["key"], "playerType": "dash", "date": pl["date"], "requestSource": pl["requestSource"], "drm": pl["drm"], "canBePlay": pl["canBePlay"], "quality": pl["quality"], "region": pl["region"] }] } headers = { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", } data = json.dumps(data) response = self.session.http.post(self._player_api, data="data={}".format(quote(data)), headers=headers) json_data = self.session.http.json(response, schema=self._playlist_schema) log.trace('{0!r}'.format(json_data)) playlist = json_data['RESULT']['playlist'][0]['streamUrls']['main'] yield from DASHStream.parse_manifest(self.session, playlist).items()
class Mitele(Plugin): url_re = re.compile(r"https?://(?:www.)?mitele.es/directo/(\w+)") supported_channels = ("telecinco", "bemad", "boing", "cuatro") app_key = "56c3464fe4b0b8a18ac02511" session_url = "https://appgrid-api.cloud.accedo.tv/session" config_url = "https://appgrid-api.cloud.accedo.tv/metadata/general_configuration, web_configuration?" \ "sessionKey={key}" channel_id_url = "http://indalo.mediaset.es/mmc-player/api/mmc/v1/{channel}/live/flash.json" stream_info_url = "http://player.ooyala.com/sas/player_api/v2/authorization/embed_code/{key}/{yoo}?" \ "device=html5&domain=www.mitele.es" session_schema = validate.Schema({ "sessionKey": validate.text, "expiration": validate.transform( lambda d: datetime.strptime(d, "%Y%m%dT%H:%M:%S+0000")) }) config_schema = validate.Schema({ "general_configuration": { "api_configuration": { "ooyala_discovery": { "api_key": validate.text } } } }) channel_id_schema = validate.Schema( validate.all({"locations": [{ "yoo": validate.text }]}, validate.get("locations"), validate.get(0), validate.get("yoo"))) stream_info_schema = validate.Schema({ "authorization_data": { validate.text: { "authorized": bool, "message": validate.text, validate.optional("streams"): [{ "delivery_type": validate.text, "url": { "format": "encoded", "data": validate.all( validate.text, validate.transform(b64decode), validate.transform(lambda d: d.decode("utf8")), validate.url()) } }] } } }) @classmethod def can_handle_url(cls, url): m = cls.url_re.match(url) return m and m.group(1) in cls.supported_channels @property def session_key(self): """ Get a cached or new session key, uuid is a random uuid (type 4) :return: """ session_key = self.cache.get("sessionKey") if session_key: self.logger.debug("Using cached sessionKey") return session_key else: self.logger.debug("Requesting new sessionKey") uuid = uuid4() res = http.get(self.session_url, params=dict(appKey=self.app_key, uuid=uuid)) data = parse_json(res.text, schema=self.session_schema) # when to expire the sessionKey, -1 hour for good measure expires_in = (data["expiration"] - datetime.now()).total_seconds() - 3600 self.cache.set("sessionKey", data["sessionKey"], expires=expires_in) return data["sessionKey"] @property def config(self): """ Get the API config data """ config_res = http.get(self.config_url.format(key=self.session_key)) return parse_json(config_res.text, schema=self.config_schema) def get_channel_id(self, channel): """ Get the ID of the channel form the name :param channel: channel name :return: channel id """ channel_id_res = http.get(self.channel_id_url.format(channel=channel)) return parse_json(channel_id_res.text, schema=self.channel_id_schema) def get_stream_info(self, key, channel_id): """ Get details about the streams :param key: API key :param channel_id: channel id :return: stream info """ stream_info_res = http.get( self.stream_info_url.format(key=key, yoo=channel_id)) return parse_json(stream_info_res.text, schema=self.stream_info_schema) def _get_streams(self): channel = self.url_re.match(self.url).group(1) key, sig = self.config["general_configuration"]["api_configuration"][ "ooyala_discovery"]["api_key"].split(".") self.logger.debug("Got api key: {}.{}", key, sig) channel_id = self.get_channel_id(channel) self.logger.debug("Got channel ID {} for channel {}", channel_id, channel) data = self.get_stream_info(key, channel_id) stream_info = data["authorization_data"][channel_id] if stream_info["authorized"]: for stream in stream_info["streams"]: if stream["delivery_type"] == "hls": for s in HLSStream.parse_variant_playlist( self.session, stream["url"]["data"]).items(): yield s else: self.logger.error("Cannot load streams: {}", stream_info["message"])
class SteamBroadcastPlugin(Plugin): _url_re = re.compile(r"https?://steamcommunity.com/broadcast/watch/(\d+)") _steamtv_url_re = re.compile(r"https?://steam.tv/(\w+)") _watch_broadcast_url = "https://steamcommunity.com/broadcast/watch/" _get_broadcast_url = "https://steamcommunity.com/broadcast/getbroadcastmpd/" _user_agent = "streamlink/{}".format(streamlink.__version__) _broadcast_schema = Schema({ "success": validate.any("ready", "unavailable", "waiting", "waiting_to_start", "waiting_for_start"), "retry": int, "broadcastid": validate.any(validate.text, int), validate.optional("url"): validate.url(), validate.optional("viewertoken"): validate.text }) _get_rsa_key_url = "https://steamcommunity.com/login/getrsakey/" _rsa_key_schema = validate.Schema({ "publickey_exp": validate.all(validate.text, validate.transform(lambda x: int(x, 16))), "publickey_mod": validate.all(validate.text, validate.transform(lambda x: int(x, 16))), "success": True, "timestamp": validate.text, "token_gid": validate.text }) _dologin_url = "https://steamcommunity.com/login/dologin/" _dologin_schema = validate.Schema({ "success": bool, "requires_twofactor": bool, validate.optional("message"): validate.text, validate.optional("emailauth_needed"): bool, validate.optional("emaildomain"): validate.text, validate.optional("emailsteamid"): validate.text, validate.optional("login_complete"): bool, validate.optional("captcha_needed"): bool, validate.optional("captcha_gid"): validate.any(validate.text, int) }) _captcha_url = "https://steamcommunity.com/public/captcha.php?gid={}" arguments = PluginArguments( PluginArgument("email", metavar="EMAIL", requires=["password"], help=""" A Steam account email address to access friends/private streams """), PluginArgument("password", metavar="PASSWORD", sensitive=True, help=""" A Steam account password to use with --steam-email. """)) def __init__(self, url): super(SteamBroadcastPlugin, self).__init__(url) self.session.http.headers["User-Agent"] = self._user_agent @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None or cls._steamtv_url_re.match( url) is not None @property def donotcache(self): return str(int(time.time() * 1000)) def encrypt_password(self, email, password): """ Get the RSA key for the user and encrypt the users password :param email: steam account :param password: password for account :return: encrypted password """ res = self.session.http.get(self._get_rsa_key_url, params=dict(username=email, donotcache=self.donotcache)) rsadata = self.session.http.json(res, schema=self._rsa_key_schema) rsa = RSA.construct( (rsadata["publickey_mod"], rsadata["publickey_exp"])) cipher = PKCS1_v1_5.new(rsa) return base64.b64encode(cipher.encrypt( password.encode("utf8"))), rsadata["timestamp"] def dologin(self, email, password, emailauth="", emailsteamid="", captchagid="-1", captcha_text="", twofactorcode=""): """ Logs in to Steam """ epassword, rsatimestamp = self.encrypt_password(email, password) login_data = { 'username': email, "password": epassword, "emailauth": emailauth, "loginfriendlyname": "Streamlink", "captchagid": captchagid, "captcha_text": captcha_text, "emailsteamid": emailsteamid, "rsatimestamp": rsatimestamp, "remember_login": True, "donotcache": self.donotcache, "twofactorcode": twofactorcode } res = self.session.http.post(self._dologin_url, data=login_data) resp = self.session.http.json(res, schema=self._dologin_schema) if not resp[u"success"]: if resp.get(u"captcha_needed"): # special case for captcha captchagid = resp[u"captcha_gid"] log.error( "Captcha result required, open this URL to see the captcha: {}" .format(self._captcha_url.format(captchagid))) try: captcha_text = self.input_ask("Captcha text") except FatalPluginError: captcha_text = None if not captcha_text: return False else: # If the user must enter the code that was emailed to them if resp.get(u"emailauth_needed"): if not emailauth: try: emailauth = self.input_ask( "Email auth code required") except FatalPluginError: emailauth = None if not emailauth: return False else: raise SteamLoginFailed("Email auth key error") # If the user must enter a two factor auth code if resp.get(u"requires_twofactor"): try: twofactorcode = self.input_ask( "Two factor auth code required") except FatalPluginError: twofactorcode = None if not twofactorcode: return False if resp.get(u"message"): raise SteamLoginFailed(resp[u"message"]) return self.dologin(email, password, emailauth=emailauth, emailsteamid=resp.get(u"emailsteamid", u""), captcha_text=captcha_text, captchagid=captchagid, twofactorcode=twofactorcode) elif resp.get("login_complete"): return True else: log.error("Something when wrong when logging in to Steam") return False def login(self, email, password): log.info("Attempting to login to Steam as {}".format(email)) return self.dologin(email, password) def _get_broadcast_stream(self, steamid, viewertoken=0, sessionid=None): log.debug("Getting broadcast stream: sessionid={0}".format(sessionid)) res = self.session.http.get(self._get_broadcast_url, params=dict(broadcastid=0, steamid=steamid, viewertoken=viewertoken, sessionid=sessionid)) return self.session.http.json(res, schema=self._broadcast_schema) def _get_streams(self): streamdata = None if self.get_option("email"): if self.login(self.get_option("email"), self.get_option("password")): log.info("Logged in as {0}".format(self.get_option("email"))) self.save_cookies(lambda c: "steamMachineAuth" in c.name) # Handle steam.tv URLs if self._steamtv_url_re.match(self.url) is not None: # extract the steam ID from the page res = self.session.http.get(self.url) for div in itertags(res.text, 'div'): if div.attributes.get("id") == "webui_config": broadcast_data = html_unescape( div.attributes.get("data-broadcast")) steamid = parse_json(broadcast_data).get("steamid") self.url = self._watch_broadcast_url + steamid # extract the steam ID from the URL steamid = self._url_re.match(self.url).group(1) res = self.session.http.get( self.url) # get the page to set some cookies sessionid = res.cookies.get('sessionid') while streamdata is None or streamdata[u"success"] in ( "waiting", "waiting_for_start"): streamdata = self._get_broadcast_stream(steamid, sessionid=sessionid) if streamdata[u"success"] == "ready": return DASHStream.parse_manifest(self.session, streamdata["url"]) elif streamdata[u"success"] == "unavailable": log.error("This stream is currently unavailable") return else: r = streamdata[u"retry"] / 1000.0 log.info( "Waiting for stream, will retry again in {} seconds...". format(r)) time.sleep(r)
class Mjunoon(Plugin): login_url = 'https://cdn2.mjunoon.tv:9191/v2/auth/login' stream_url = 'https://cdn2.mjunoon.tv:9191/v2/streaming-url' is_live_channel_re = re.compile(r'"isLiveBroadcast":\s*"(true|undefined)"') main_chunk_js_url_re = re.compile( r'<script src="(/static/js/main\.\w+\.chunk\.js)"></script>') js_credentials_re = re.compile( r'data:{email:"(?P<email>.*?)",password:"******"}') js_cipher_data_re = re.compile( r'createDecipheriv\("(?P<algorithm>.*?)","(?P<key>.*?)","(?P<iv>.*?)"\)' ) token_schema = validate.Schema({ 'token': validate.text, 'token_type': validate.text, 'expires_in': int, }) encrypted_data_schema = validate.Schema({ 'eData': validate.text, }, validate.get('eData')) stream_schema = validate.Schema( { 'data': { 'live_stream_url': validate.url(), 'channel_name': validate.text, 'meta_title': validate.any(None, validate.text), 'genres': validate.all( validate.transform(lambda x: x.split(',')[0]), validate.text, ), }, }, validate.get('data')) encryption_algorithm = { 'aes-256-cbc': AES.MODE_CBC, } def get_data(self): js_data = {} res = self.session.http.get(self.url) m = self.is_live_channel_re.search(res.text) if not m: return if m.group(1) == "true": js_data['type'] = 'channel' else: js_data['type'] = 'episode' m = self.main_chunk_js_url_re.search(res.text) if not m: log.error('Failed to get main chunk JS URL') return res = self.session.http.get(urljoin(self.url, m.group(1))) m = self.js_credentials_re.search(res.text) if not m: log.error('Failed to get credentials') return js_data['credentials'] = m.groupdict() m = self.js_cipher_data_re.search(res.text) if not m: log.error('Failed to get cipher data') return js_data['cipher_data'] = m.groupdict() return js_data def decrypt_data(self, cipher_data, encrypted_data): cipher = AES.new( bytes(cipher_data['key'], 'utf-8'), self.encryption_algorithm[cipher_data['algorithm']], bytes(cipher_data['iv'], 'utf-8'), ) return unpad(cipher.decrypt(binascii.unhexlify(encrypted_data)), 16, 'pkcs7') def get_stream(self, slug, js_data): token_data = {} token_data['token'] = self.cache.get('token') token_data['token_type'] = self.cache.get('token_type') if token_data['token'] and token_data['token_type']: log.debug('Using cached token') else: log.debug('Getting new token') res = self.session.http.post( self.login_url, json=js_data['credentials'], ) token_data = self.session.http.json(res, schema=self.token_schema) log.debug('Token={0}'.format(token_data["token"])) self.cache.set('token', token_data['token'], expires=token_data['expires_in']) self.cache.set('token_type', token_data['token_type'], expires=token_data['expires_in']) headers = { 'Authorization': '{0} {1}'.format(token_data["token_type"], token_data["token"]) } data = { 'slug': slug, 'type': js_data['type'], } res = self.session.http.post( self.stream_url, headers=headers, json=data, ) encrypted_data = self.session.http.json( res, schema=self.encrypted_data_schema) stream_data = parse_json( self.decrypt_data(js_data['cipher_data'], encrypted_data), schema=self.stream_schema, ) self.author = stream_data['channel_name'] self.category = stream_data['genres'] self.title = stream_data['meta_title'] return stream_data['live_stream_url'] def _get_streams(self): slug = self.match.group(1) log.debug('Slug={0}'.format(slug)) js_data = self.get_data() if not js_data: return log.debug('JS data={0}'.format(js_data)) hls_url = self.get_stream(slug, js_data) log.debug('HLS URL={0}'.format(hls_url)) return HLSStream.parse_variant_playlist(self.session, hls_url)
class YouTube(Plugin): _oembed_url = "https://www.youtube.com/oembed" _video_info_url = "https://youtube.com/get_video_info" _oembed_schema = validate.Schema( { "author_name": validate.all(validate.text, validate.transform(maybe_decode)), "title": validate.all(validate.text, validate.transform(maybe_decode)) } ) # There are missing itags adp_video = { 137: "1080p", 299: "1080p60", # HFR 264: "1440p", 308: "1440p60", # HFR 266: "2160p", 315: "2160p60", # HFR 138: "2160p", 302: "720p60", # HFR 135: "480p", 133: "240p", 160: "144p", } adp_audio = { 140: 128, 141: 256, 171: 128, 249: 48, 250: 64, 251: 160, 256: 256, 258: 258, } arguments = PluginArguments( PluginArgument( "api-key", sensitive=True, help=argparse.SUPPRESS # no longer used ) ) def __init__(self, url): super(YouTube, self).__init__(url) parsed = urlparse(self.url) if parsed.netloc == 'gaming.youtube.com': self.url = urlunparse(parsed._replace(netloc='www.youtube.com')) self.author = None self.title = None self.video_id = None self.session.http.headers.update({'User-Agent': useragents.CHROME}) def get_author(self): if self.author is None: self.get_oembed return self.author def get_title(self): if self.title is None: self.get_oembed return self.title @classmethod def can_handle_url(cls, url): return _url_re.match(url) @classmethod def stream_weight(cls, stream): match_3d = re.match(r"(\w+)_3d", stream) match_hfr = re.match(r"(\d+p)(\d+)", stream) if match_3d: weight, group = Plugin.stream_weight(match_3d.group(1)) weight -= 1 group = "youtube_3d" elif match_hfr: weight, group = Plugin.stream_weight(match_hfr.group(1)) weight += 1 group = "high_frame_rate" else: weight, group = Plugin.stream_weight(stream) return weight, group @property def get_oembed(self): if self.video_id is None: self.video_id = self._find_video_id(self.url) params = { "url": "https://www.youtube.com/watch?v={0}".format(self.video_id), "format": "json" } res = self.session.http.get(self._oembed_url, params=params) data = self.session.http.json(res, schema=self._oembed_schema) self.author = data["author_name"] self.title = data["title"] def _create_adaptive_streams(self, info, streams): adaptive_streams = {} best_audio_itag = None # Extract audio streams from the adaptive format list streaming_data = info.get("player_response", {}).get("streamingData", {}) for stream_info in streaming_data.get("adaptiveFormats", []): if "url" not in stream_info: continue stream_params = dict(parse_qsl(stream_info["url"])) if "itag" not in stream_params: continue itag = int(stream_params["itag"]) # extract any high quality streams only available in adaptive formats adaptive_streams[itag] = stream_info["url"] stream_type, stream_format = stream_info["mimeType"] if stream_type == "audio": stream = HTTPStream(self.session, stream_info["url"]) name = "audio_{0}".format(stream_format) streams[name] = stream # find the best quality audio stream m4a, opus or vorbis if best_audio_itag is None or self.adp_audio[itag] > self.adp_audio[best_audio_itag]: best_audio_itag = itag if best_audio_itag and adaptive_streams and MuxedStream.is_usable(self.session): aurl = adaptive_streams[best_audio_itag] for itag, name in self.adp_video.items(): if itag in adaptive_streams: vurl = adaptive_streams[itag] log.debug("MuxedStream: v {video} a {audio} = {name}".format( audio=best_audio_itag, name=name, video=itag, )) streams[name] = MuxedStream(self.session, HTTPStream(self.session, vurl), HTTPStream(self.session, aurl)) return streams def _find_video_id(self, url): m = _url_re.match(url) if m.group("video_id"): log.debug("Video ID from URL") return m.group("video_id") res = self.session.http.get(url) datam = _ytdata_re.search(res.text) if datam: data = parse_json(datam.group(1)) # find the videoRenderer object, where there is a LVE NOW badge for vid_ep in search_dict(data, 'currentVideoEndpoint'): video_id = vid_ep.get("watchEndpoint", {}).get("videoId") if video_id: log.debug("Video ID from currentVideoEndpoint") return video_id for x in search_dict(data, 'videoRenderer'): if x.get("viewCountText", {}).get("runs"): if x.get("videoId"): log.debug("Video ID from videoRenderer (live)") return x["videoId"] for bstyle in search_dict(x.get("badges", {}), "style"): if bstyle == "BADGE_STYLE_TYPE_LIVE_NOW": if x.get("videoId"): log.debug("Video ID from videoRenderer (live)") return x["videoId"] if "/embed/live_stream" in url: for link in itertags(res.text, "link"): if link.attributes.get("rel") == "canonical": canon_link = link.attributes.get("href") if canon_link != url: if canon_link.endswith("v=live_stream"): log.debug("The video is not available") break else: log.debug("Re-directing to canonical URL: {0}".format(canon_link)) return self._find_video_id(canon_link) raise PluginError("Could not find a video on this page") def _get_stream_info(self, video_id): # normal _params_1 = {"el": "detailpage"} # age restricted _params_2 = {"el": "embedded"} # embedded restricted _params_3 = {"eurl": "https://youtube.googleapis.com/v/{0}".format(video_id)} count = 0 info_parsed = None for _params in (_params_1, _params_2, _params_3): count += 1 params = {"video_id": video_id} params.update(_params) res = self.session.http.get(self._video_info_url, params=params) info_parsed = parse_query(res.content if is_py2 else res.text, name="config", schema=_config_schema) player_response = info_parsed.get("player_response", {}) playability_status = player_response.get("playabilityStatus", {}) if (playability_status.get("status") != "OK"): reason = playability_status.get("reason") log.debug("get_video_info - {0}: {1}".format( count, reason) ) continue self.author = player_response.get("videoDetails", {}).get("author") self.title = player_response.get("videoDetails", {}).get("title") log.debug("get_video_info - {0}: Found data".format(count)) break return info_parsed def _get_streams(self): is_live = False self.video_id = self._find_video_id(self.url) log.debug("Using video ID: {0}", self.video_id) info = self._get_stream_info(self.video_id) if info and info.get("status") == "fail": log.error("Could not get video info: {0}".format(info.get("reason"))) return elif not info: log.error("Could not get video info") return if info.get("player_response", {}).get("videoDetails", {}).get("isLive"): log.debug("This video is live.") is_live = True streams = {} protected = False if (info.get("player_response", {}).get("streamingData", {}).get("adaptiveFormats", [{}])[0].get("cipher") or info.get("player_response", {}).get("streamingData", {}).get("adaptiveFormats", [{}])[0].get("signatureCipher") or info.get("player_response", {}).get("streamingData", {}).get("formats", [{}])[0].get("cipher")): protected = True log.debug("This video may be protected.") for stream_info in info.get("player_response", {}).get("streamingData", {}).get("formats", []): if "url" not in stream_info: continue stream = HTTPStream(self.session, stream_info["url"]) name = stream_info["qualityLabel"] streams[name] = stream if not is_live: streams = self._create_adaptive_streams(info, streams) hls_manifest = info.get("player_response", {}).get("streamingData", {}).get("hlsManifestUrl") if hls_manifest: try: hls_streams = HLSStream.parse_variant_playlist( self.session, hls_manifest, namekey="pixels" ) streams.update(hls_streams) except IOError as err: log.warning("Failed to extract HLS streams: {0}", err) if not streams and protected: raise PluginError("This plugin does not support protected videos, " "try youtube-dl instead") return streams
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 Beam(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 DeutscheWelle(Plugin): default_channel = "1" url_re = re.compile(r"https?://(?:www\.)?dw\.com/") channel_re = re.compile(r'''<a.*?data-id="(\d+)".*?class="ici"''') live_stream_div = re.compile( r''' <div\s+class="mediaItem"\s+data-channel-id="(\d+)".*?>.*? <input\s+type="hidden"\s+name="file_name"\s+value="(.*?)"\s*>.*?<div ''', re.DOTALL | re.VERBOSE) smil_api_url = "http://www.dw.com/smil/{}" html5_api_url = "http://www.dw.com/html5Resource/{}" vod_player_type_re = re.compile( r'<input type="hidden" name="player_type" value="(?P<stream_type>.+?)">' ) stream_vod_data_re = re.compile( r'<input\s+type="hidden"\s+name="file_name"\s+value="(?P<stream_url>.+?)">.*?' r'<input\s+type="hidden"\s+name="media_id"\s+value="(?P<stream_id>\d+)">', re.DOTALL) smil_schema = validate.Schema( validate.union({ "base": validate.all(validate.xml_find(".//meta"), validate.xml_element(attrib={"base": validate.text}), validate.get("base")), "streams": validate.all(validate.xml_findall(".//switch/*"), [ validate.all( validate.getattr("attrib"), { "src": validate.text, "system-bitrate": validate.all( validate.text, validate.transform(int), ), validate.optional("width"): validate.all(validate.text, validate.transform(int)) }) ]) })) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _create_stream(self, url, quality=None): if url.startswith('rtmp://'): return (quality, RTMPStream(self.session, {'rtmp': url})) if url.endswith('.m3u8'): return HLSStream.parse_variant_playlist(self.session, url).items() return (quality, HTTPStream(self.session, url)) def _get_live_streams(self, page): # check if a different language has been selected qs = dict(parse_qsl(urlparse(self.url).query)) channel = qs.get("channel") if not channel: m = self.channel_re.search(page.text) channel = m and m.group(1) self.logger.debug("Using sub-channel ID: {0}", channel) # extract the streams from the page, mapping between channel-id and stream url media_items = self.live_stream_div.finditer(page.text) stream_map = dict([m.groups((1, 2)) for m in media_items]) stream_url = stream_map.get(str(channel) or self.default_channel) if stream_url: return self._create_stream(stream_url) def _get_vod_streams(self, stream_type, page): m = self.stream_vod_data_re.search(page.text) if m is None: return stream_url, stream_id = m.groups() if stream_type == "video": stream_api_id = "v-{}".format(stream_id) default_quality = "vod" elif stream_type == "audio": stream_api_id = "a-{}".format(stream_id) default_quality = "audio" else: return # Retrieve stream embedded in web page yield self._create_stream(stream_url, default_quality) # Retrieve streams using API res = http.get(self.smil_api_url.format(stream_api_id)) videos = http.xml(res, schema=self.smil_schema) for video in videos['streams']: url = videos["base"] + video["src"] if url == stream_url or url.replace("_dwdownload.", ".") == stream_url: continue if video["system-bitrate"] > 0: # If width is available, use it to select the best stream # amongst those with same bitrate quality = "{}k".format( (video["system-bitrate"] + video.get("width", 0)) // 1000) else: quality = default_quality yield self._create_stream(url, quality) def _get_streams(self): res = http.get(self.url) m = self.vod_player_type_re.search(res.text) if m is None: return stream_type = m.group("stream_type") if stream_type == "dwlivestream": return self._get_live_streams(res) return self._get_vod_streams(stream_type, res)
import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream _url_re = re.compile(r"http(s)?://(\w+.)?chaturbate.com/[^/?&]+") _playlist_url_re = re.compile(r"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:
from streamlink.plugin import Plugin, pluginmatcher from streamlink.plugin.api import validate from streamlink.plugin.api.useragents import CHROME as USER_AGENT from streamlink.stream.hls import HLSStream from streamlink.stream.http import HTTPStream HUAJIAO_URL = "http://www.huajiao.com/l/{}" 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" _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))))) @pluginmatcher( re.compile(r"https?://(www\.)?huajiao\.com/l/(?P<channel>[^/]+)")) class Huajiao(Plugin): def _get_streams(self): channel = self.match.group("channel") self.session.http.headers.update({"User-Agent": USER_AGENT}) self.session.http.verify = False
STREAM_INFO_URL = "http://live.daserste.de/{0}/livestream.xml" SWF_URL = "http://live.daserste.de/lib/br-player/swf/main.swf" STREAMING_TYPES = { "streamingUrlLive": ("HDS", partial(HDSStream.parse_manifest, pvswf=SWF_URL)), "streamingUrlIPhone": ("HLS", HLSStream.parse_variant_playlist) } _url_re = re.compile("http(s)?://live.daserste.de/(?P<channel>[^/?]+)?") _livestream_schema = validate.Schema( validate.xml_findall("video/*"), validate.filter(lambda e: e.tag in STREAMING_TYPES), validate.map(lambda e: (STREAMING_TYPES.get(e.tag), e.text)), validate.transform(dict), ) class ard_live(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(STREAM_INFO_URL.format(channel)) urls = http.xml(res, schema=_livestream_schema) streams = {}
STREAMING_TYPES = { "h264_aac_f4f_http_f4m_http": ( "HDS", HDSStream.parse_manifest ), "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( {
class AfreecaTV(Plugin): _re_bno = re.compile(r"var nBroadNo = (?P<bno>\d+);") _re_url = re.compile( r"https?://play\.afreecatv\.com/(?P<username>\w+)(?:/(?P<bno>:\d+))?") CHANNEL_API_URL = "http://live.afreecatv.com/afreeca/player_live_api.php" CHANNEL_RESULT_OK = 1 QUALITYS = ["original", "hd", "sd"] QUALITY_WEIGHTS = { "original": 1080, "hd": 720, "sd": 480, } _schema_channel = validate.Schema( { "CHANNEL": { "RESULT": validate.transform(int), validate.optional("BPWD"): str, validate.optional("BNO"): str, validate.optional("RMD"): str, validate.optional("AID"): str, validate.optional("CDN"): str, } }, validate.get("CHANNEL")) _schema_stream = validate.Schema({ validate.optional("view_url"): validate.url(scheme=validate.any("rtmp", "http")), "stream_status": str, }) arguments = PluginArguments( PluginArgument( "username", sensitive=True, requires=["password"], metavar="USERNAME", help="The username used to register with afreecatv.com."), PluginArgument( "password", sensitive=True, metavar="PASSWORD", help= "A afreecatv.com account password to use with --afreeca-username." ), PluginArgument("purge-credentials", action="store_true", help=""" Purge cached AfreecaTV credentials to initiate a new session and reauthenticate. """), ) def __init__(self, url): super().__init__(url) self._authed = (self.session.http.cookies.get("PdboxBbs") and self.session.http.cookies.get("PdboxSaveTicket") and self.session.http.cookies.get("PdboxTicket") and self.session.http.cookies.get("PdboxUser") and self.session.http.cookies.get("RDB")) @classmethod def can_handle_url(cls, url): return cls._re_url.match(url) is not None @classmethod def stream_weight(cls, key): weight = cls.QUALITY_WEIGHTS.get(key) if weight: return weight, "afreeca" return Plugin.stream_weight(key) def _get_channel_info(self, broadcast, username): data = { "bid": username, "bno": broadcast, "mode": "landing", "player_type": "html5", "type": "live", } res = self.session.http.post(self.CHANNEL_API_URL, data=data) return self.session.http.json(res, schema=self._schema_channel) def _get_hls_key(self, broadcast, username, quality): data = { "bid": username, "bno": broadcast, "pwd": "", "quality": quality, "type": "pwd" } res = self.session.http.post(self.CHANNEL_API_URL, data=data) return self.session.http.json(res, schema=self._schema_channel) def _get_stream_info(self, broadcast, quality, cdn, rmd): params = { "return_type": cdn, "broad_key": f"{broadcast}-flash-{quality}-hls", } res = self.session.http.get(f"{rmd}/broad_stream_assign.html", params=params) return self.session.http.json(res, schema=self._schema_stream) def _get_hls_stream(self, broadcast, username, quality, cdn, rmd): keyjson = self._get_hls_key(broadcast, username, quality) if keyjson["RESULT"] != self.CHANNEL_RESULT_OK: return key = keyjson["AID"] info = self._get_stream_info(broadcast, quality, cdn, rmd) if "view_url" in info: return AfreecaHLSStream(self.session, info["view_url"], params={"aid": key}) def _login(self, username, password): data = { "szWork": "login", "szType": "json", "szUid": username, "szPassword": password, "isSaveId": "true", "isSavePw": "false", "isSaveJoin": "false", "isLoginRetain": "Y", } res = self.session.http.post( "https://login.afreecatv.com/app/LoginAction.php", data=data) data = self.session.http.json(res) log.trace(f"{data!r}") if data["RESULT"] == self.CHANNEL_RESULT_OK: self.save_cookies() return True else: return False def _get_streams(self): login_username = self.get_option("username") login_password = self.get_option("password") self.session.http.headers.update({"Referer": self.url}) if self.options.get("purge_credentials"): self.clear_cookies() self._authed = False log.info("All credentials were successfully removed") if self._authed: log.debug("Attempting to authenticate using cached cookies") elif login_username and login_password: log.debug("Attempting to login using username and password") if self._login(login_username, login_password): log.info("Login was successful") else: log.error("Failed to login") m = self._re_url.match(self.url).groupdict() username = m["username"] bno = m["bno"] if bno is None: res = self.session.http.get(self.url) m = self._re_bno.search(res.text) if not m: log.error("Could not find broadcast number.") return bno = m.group("bno") channel = self._get_channel_info(bno, username) log.trace(f"{channel!r}") if channel.get("BPWD") == "Y": log.error("Stream is Password-Protected") return elif channel.get("RESULT") == -6: log.error("Login required") return elif channel.get("RESULT") != self.CHANNEL_RESULT_OK: return (broadcast, rmd, cdn) = (channel["BNO"], channel["RMD"], channel["CDN"]) if not (broadcast and rmd and cdn): return for qkey in self.QUALITYS: hls_stream = self._get_hls_stream(broadcast, username, qkey, cdn, rmd) if hls_stream: yield qkey, hls_stream
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.any(validate.text, None), "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}, validate.get("session_id")) class CrunchyrollAPIError(Exception): """Exception thrown by the Crunchyroll API when an error occurs""" def __init__(self, msg, code): Exception.__init__(self, msg) self.msg = msg self.code = code
class Rtve(Plugin): secret_key = base64.b64decode("eWVMJmRhRDM=") url_re = re.compile(r""" https?://(?:www\.)?rtve\.es/(?:directo|infantil|noticias|television|deportes|alacarta|drmn)/.*?/? """, re.VERBOSE) 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) ) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None 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
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 BBCiPlayer(Plugin): url_re = re.compile( r"""https?://(?:www\.)?bbc.co.uk/iplayer/ ( episode/(?P<episode_id>\w+)| live/(?P<channel_name>\w+) ) """, re.VERBOSE) mediator_re = re.compile( r'window\.mediatorDefer\s*=\s*page\([^,]*,\s*(\{.*?})\);', re.DOTALL) tvip_re = re.compile(r'event_master_brand=(\w+?)&') account_locals_re = re.compile(r'window.bbcAccount.locals\s*=\s*(\{.*?});') swf_url = "http://emp.bbci.co.uk/emp/SMPf/1.18.3/StandardMediaPlayerChromelessFlash.swf" hash = base64.b64decode( b"N2RmZjc2NzFkMGM2OTdmZWRiMWQ5MDVkOWExMjE3MTk5MzhiOTJiZg==") api_url = ( "http://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") config_url = "http://www.bbc.co.uk/idcta/config" auth_url = "https://account.bbc.com/signin" config_schema = validate.Schema( validate.transform(parse_json), { "signin_url": validate.url(), "identity": { "cookieAgeDays": int, "accessTokenCookieName": validate.text, "idSignedInCookieName": validate.text } }) mediator_schema = validate.Schema( {"episode": { "versions": [{ "id": validate.text }] }}, validate.get("episode"), validate.get("versions"), validate.get(0), validate.get("id")) mediaselector_schema = validate.Schema( validate.transform(parse_json), { "media": [{ "connection": [{ validate.optional("href"): validate.url(), validate.optional("transferFormat"): validate.text }], "kind": validate.text }] }, validate.get("media"), validate.filter(lambda x: x["kind"] == "video")) options = PluginOptions({"password": None, "username": None}) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None @classmethod def _hash_vpid(cls, vpid): return sha1(cls.hash + str(vpid).encode("utf8")).hexdigest() def find_vpid(self, url, res=None): self.logger.debug("Looking for vpid on {0}", url) # Use pre-fetched page if available res = res or 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): self.logger.debug("Looking for tvip on {0}", url) res = http.get(url) m = self.tvip_re.search(res.text) return m and m.group(1) def mediaselector(self, vpid): for platform in self.platforms: url = self.api_url.format(vpid=vpid, vpid_hash=self._hash_vpid(vpid), platform=platform) self.logger.debug("Info API request: {0}", url) stream_urls = http.get(url, schema=self.mediaselector_schema) for media in stream_urls: for connection in media["connection"]: if connection.get("transferFormat") == "hds": for s in HDSStream.parse_manifest( self.session, connection["href"]).items(): yield s if connection.get("transferFormat") == "hls": for s in HLSStream.parse_variant_playlist( self.session, connection["href"]).items(): yield s def login(self, ptrt_url, context="tvandiplayer"): # get the site config, to find the signin url config = http.get(self.config_url, params=dict(ptrt=ptrt_url), schema=self.config_schema) res = http.get(config["signin_url"], params=dict(userOrigin=context, context=context), headers={"Referer": self.url}) m = self.account_locals_re.search(res.text) if m: auth_data = parse_json(m.group(1)) res = http.post(self.auth_url, params=dict(context=auth_data["userOrigin"], ptrt=auth_data["ptrt"]["value"], userOrigin=auth_data["userOrigin"], nonce=auth_data["nonce"]), data=dict(jsEnabled="false", attempts=0, username=self.get_option("username"), password=self.get_option("password"))) # redirects to ptrt_url on successful login if res.url == ptrt_url: return res else: self.logger.error( "Could not authenticate, could not find the authentication nonce" ) def _get_streams(self): if not self.get_option("username"): self.logger.error( "BBC iPlayer requires an account you must login using " "--bbciplayer-username and --bbciplayer-password") return self.logger.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") page_res = self.login(self.url) if not page_res: self.logger.error( "Could not authenticate, check your username and password") return m = self.url_re.match(self.url) episode_id = m.group("episode_id") channel_name = m.group("channel_name") if episode_id: self.logger.debug("Loading streams for episode: {0}", episode_id) vpid = self.find_vpid(self.url, res=page_res) if vpid: self.logger.debug("Found VPID: {0}", vpid) for s in self.mediaselector(vpid): yield s else: self.logger.error("Could not find VPID for episode {0}", episode_id) elif channel_name: self.logger.debug("Loading stream for live channel: {0}", channel_name) tvip = self.find_tvip(self.url) if tvip: self.logger.debug("Found TVIP: {0}", tvip) for s in self.mediaselector(tvip): yield s
from streamlink.plugin.api.utils import parse_json 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 }
STATUS_OFFLINE = 0 #hls source is not stable, lower priority STREAM_WEIGHTS = {"live": 1080} _url_re = re.compile( r""" http(s)?://(www\.)?zhanqi.tv /(?P<channel>[^/]+) """, re.VERBOSE) _json_re = re.compile(r"window\.oPageConfig\.oRoom\s*=\s*({.+?});") _roomID_schema = validate.Schema( validate.all( validate.transform(_json_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(parse_json), {"id": validate.all(validate.text, validate.transform(int)) })))) _room_schema = validate.Schema( { "data": validate.any( None, { "status": validate.all(validate.text, validate.transform(int)), "videoId": validate.text })
import re from streamlink.plugin import Plugin from streamlink.plugin.api import validate from streamlink.plugin.api import useragents from streamlink.stream import HLSStream _url_re = re.compile(r"https?://(www\.)?ok\.ru/(live|video)/\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/ and for ok.ru VoDs: http://www.ok.ru/video/ """ @classmethod def can_handle_url(cls, url): return _url_re.match(url) is not None def _get_streams(self): headers = {'User-Agent': useragents.CHROME, 'Referer': self.url} hls = self.session.http.get(self.url, headers=headers, schema=_schema) if hls: hls = hls.replace(u'\\\\u0026', u'&') return HLSStream.parse_variant_playlist(self.session, hls,
API_URL = "https://www.zhanqi.tv/api/static/v2.1/room/domain/{0}.json" STATUS_ONLINE = 4 STATUS_OFFLINE = 0 _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)
class BTV(Plugin): options = PluginOptions({"username": None, "password": None}) 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 = 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 = 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 = 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")
"low": 540, "middle": 720, "source": 1080 } _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,
class WWENetwork(Plugin): url_re = re.compile(r"https?://network.wwe.com") content_id_re = re.compile(r'''"content_id" : "(\d+)"''') playback_scenario = "HTTP_CLOUD_WIRED" login_url = "https://secure.net.wwe.com/workflow.do" login_page_url = "https://secure.net.wwe.com/enterworkflow.do?flowId=account.login&forwardUrl=http%3A%2F%2Fnetwork.wwe.com" api_url = "https://ws.media.net.wwe.com/ws/media/mf/op-findUserVerifiedEvent/v-2.3" _info_schema = validate.Schema( validate.union({ "status": validate.union({ "code": validate.all(validate.xml_findtext(".//status-code"), validate.transform(int)), "message": validate.xml_findtext(".//status-message"), }), "urls": validate.all(validate.xml_findall(".//url"), [validate.getattr("text")]), validate.optional("fingerprint"): validate.xml_findtext(".//updated-fingerprint"), validate.optional("session_key"): validate.xml_findtext(".//session-key"), "session_attributes": validate.all(validate.xml_findall(".//session-attribute"), [ validate.getattr("attrib"), validate.union({ "name": validate.get("name"), "value": validate.get("value") }) ]) })) options = PluginOptions({ "email": None, "password": None, }) def __init__(self, url): super(WWENetwork, self).__init__(url) http.headers.update({"User-Agent": useragents.CHROME}) self._session_attributes = Cache(filename="plugin-cache.json", key_prefix="wwenetwork:attributes") self._session_key = self.cache.get("session_key") self._authed = self._session_attributes.get( "ipid") and self._session_attributes.get("fprt") @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def login(self, email, password): self.logger.debug("Attempting login as {0}", email) # sets some required cookies to login http.get(self.login_page_url) # login res = http.post(self.login_url, data=dict(registrationAction='identify', emailAddress=email, password=password, submitButton=""), headers={"Referer": self.login_page_url}, allow_redirects=False) self._authed = "Authentication Error" not in res.text if self._authed: self._session_attributes.set("ipid", res.cookies.get("ipid"), expires=3600 * 1.5) self._session_attributes.set("fprt", res.cookies.get("fprt"), expires=3600 * 1.5) return self._authed def _update_session_attribute(self, key, value): if value: self._session_attributes.set(key, value, expires=3600 * 1.5) # 1h30m expiry http.cookies.set(key, value) @property def session_key(self): return self._session_key @session_key.setter def session_key(self, value): self.cache.set("session_key", value) self._session_key = value def _get_media_info(self, content_id): """ Get the info about the content, based on the ID :param content_id: :return: """ params = { "identityPointId": self._session_attributes.get("ipid"), "fingerprint": self._session_attributes.get("fprt"), "contentId": content_id, "playbackScenario": self.playback_scenario, "platform": "WEB_MEDIAPLAYER_5", "subject": "LIVE_EVENT_COVERAGE", "frameworkURL": "https://ws.media.net.wwe.com", "_": int(time.time()) } if self.session_key: params["sessionKey"] = self.session_key url = self.api_url.format(id=content_id) res = http.get(url, params=params) return http.xml(res, ignore_ns=True, schema=self._info_schema) def _get_content_id(self): # check the page to find the contentId res = http.get(self.url) m = self.content_id_re.search(res.text) if m: return m.group(1) def _get_streams(self): email = self.get_option("email") password = self.get_option("password") if not self._authed and (not email and not password): self.logger.error( "A login for WWE Network is required, use --wwenetwork-email/" "--wwenetwork-password to set them") return if not self._authed: if not self.login(email, password): self.logger.error( "Failed to login, check your username/password") return content_id = self._get_content_id() if content_id: self.logger.debug("Found content ID: {0}", content_id) info = self._get_media_info(content_id) if info["status"]["code"] == 1: # update the session attributes self._update_session_attribute("fprt", info.get("fingerprint")) for attr in info["session_attributes"]: self._update_session_attribute(attr["name"], attr["value"]) if info.get("session_key"): self.session_key = info.get("session_key") for url in info["urls"]: for s in HLSStream.parse_variant_playlist( self.session, url, name_fmt="{pixels}_{bitrate}").items(): yield s else: raise PluginError( "Could not load streams: {message} ({code})".format( **info["status"]))
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( {
def _get_streams(self): parsed_html = self.session.http.get(self.url, schema=validate.Schema( validate.parse_html())) iframe_url = validate.validate( validate.Schema( validate.xml_xpath_string( ".//script[contains(text(), '/embedIframeJs/')]/text()"), validate.any( None, validate.all( validate.transform(self.iframe_url_re.search), validate.any( None, validate.all(validate.get(1), validate.url())), )), ), parsed_html) if not iframe_url: return m = self.partner_ids_re.search(iframe_url) if not m: log.error("Failed to find partner IDs in IFRAME URL") return p = m.group(1) sp = m.group(2) json = self.session.http.get( iframe_url, schema=validate.Schema( validate.transform(self.json_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(self.unescape_quotes), validate.parse_json(), validate.any( { "entryResult": { "contextData": { "isSiteRestricted": bool, "isCountryRestricted": bool, "isSessionRestricted": bool, "isIpAddressRestricted": bool, "isUserAgentRestricted": bool, "flavorAssets": [{ "id": str, }], }, "meta": { "id": str, "name": str, "categories": validate.any(None, str), }, }, }, {"error": str}), )), )) if not json: return if "error" in json: log.error(f"API error: {json['error']}") return json = json.get("entryResult") if self.is_restricted(json["contextData"]): return self.id = json["meta"]["id"] self.title = json["meta"]["name"] self.author = validate.validate( validate.Schema( validate.xml_xpath_string( ".//h1[contains(@class, 'btn-title')]/text()"), ), parsed_html) if json["meta"]["categories"]: self.category = json["meta"]["categories"] for asset in json["contextData"]["flavorAssets"]: yield from HLSStream.parse_variant_playlist( self.session, (f"https://cdnapisec.kaltura.com/p/{p}/sp/{sp}/playManifest/entryId/{json['meta']['id']}" f"/flavorIds/{asset['id']}/format/applehttp/protocol/https/a.m3u8" ), name_fmt="{pixels}_{bitrate}", ).items()
from streamlink.stream import (HTTPStream, HLSStream) USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36" HUAJIAO_URL = "http://www.huajiao.com/l/{}" 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)