class Gulli(Plugin): LIVE_PLAYER_URL = 'https://replay.gulli.fr/jwplayer/embedstreamtv' VOD_PLAYER_URL = 'https://replay.gulli.fr/jwplayer/embed/{0}' _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.parse_json(), [validate.Schema({'file': validate.url()})])) def _get_streams(self): video_id = self.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: yield from HLSStream.parse_variant_playlist( self.session, video_url).items() 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 OSError as err: if '403 Client Error' in str(err): log.error( 'Failed to access stream, may be due to geo-restriction' ) raise
from streamlink.plugin.api import http, validate from streamlink.stream import FLVPlaylist, HTTPStream API_URL = "http://veetle.com/index.php/stream/ajaxStreamLocation/{0}/flash" _url_re = re.compile(""" http(s)?://(\w+\.)?veetle.com (:? /.*(v|view)/ (?P<channel>[^/]+/[^/&?]+) )? """, re.VERBOSE) _schema = validate.Schema({ validate.optional("isLive"): bool, "payload": validate.any(int, validate.url(scheme="http")), "success": bool }) class Veetle(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_streams(self): self.url = http.resolve_url(self.url) match = _url_re.match(self.url) parsed = urlparse(self.url) if parsed.fragment: channel_id = parsed.fragment
import re from streamlink.plugin import Plugin from streamlink.plugin.api import validate _url_re = re.compile(r'http(s)?://www\.skai(?:tv)?.gr/.*') _api_url = "http://www.skaitv.gr/json/live.php" _api_res_schema = validate.Schema(validate.all( validate.get("now"), { "livestream": validate.url() }, validate.get("livestream")) ) class Skai(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) def _get_streams(self): api_res = self.session.http.get(_api_url) yt_url = self.session.http.json(api_res, schema=_api_res_schema) if yt_url: return self.session.streams(yt_url) __plugin__ = Skai
(?: /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") ) ) ) ) })
} }, validate.get("chansub") ) _user_schema = validate.Schema( { validate.optional("display_name"): validate.text }, validate.get("display_name") ) _video_schema = validate.Schema( { "chunks": { validate.text: [{ "length": int, "url": validate.any(None, validate.url(scheme="http")), "upkeep": validate.any("pass", "fail", None) }] }, "restrictions": {validate.text: validate.text}, "start_offset": int, "end_offset": int, } ) _viewer_info_schema = validate.Schema( { validate.optional("login"): validate.text }, validate.get("login") ) _viewer_token_schema = validate.Schema(
_url_re = re.compile( r'http(s)?://([^.]*.)?ceskatelevize.cz' ) _player_re = re.compile( r'ivysilani/embed/iFramePlayer[^"]+' ) _hash_re = re.compile( r'hash:"([0-9a-z]+)"' ) _playlist_info_re = re.compile( r'{"type":"([a-z]+)","id":"([0-9]+)"' ) _playlist_url_schema = validate.Schema({ validate.optional("streamingProtocol"): validate.text, "url": validate.any( validate.url(), "Error", "error_region" ) }) _playlist_schema = validate.Schema({ "playlist": [{ validate.optional("type"): validate.text, "streamUrls": { "main": validate.url(), } }] }) log = logging.getLogger(__name__)
from streamlink.plugin.api import http, validate from streamlink.stream import RTMPStream, HLSStream RUURL = "b=chrome&p=win&v=56&f=0&d=1" _url_re = re.compile(r"https?://www.rtvs.sk/televizia/live-[\w-]+") _playlist_url_re = re.compile(r'"playlist": "([^"]+)"') _playlist_schema = validate.Schema( [ { "sources": [ validate.any( { "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") )
def _get_streams(self): self.id = self.session.http.get( self.url, schema=validate.Schema( validate.transform( re.compile(r"\bdata-setup='({.+?})'", re.DOTALL).search), validate.any( None, validate.all( validate.get(1), validate.parse_json(), { "idAsset": validate.any( int, validate.all(str, validate.transform(int))), }, validate.get("idAsset"))), )) if not self.id: return urls = self.session.http.get( self.URL_VIDEOS.format(id=self.id), schema=validate.Schema( validate.transform(ZTNR.translate), validate.transform(list), [(str, validate.url())], ), ) url = next( (url for _, url in urls if urlparse(url).path.endswith(".m3u8")), None) if not url: url = next( (url for _, url in urls if urlparse(url).path.endswith(".mp4")), None) if url: yield "vod", HTTPStream(self.session, url) return streams = HLSStream.parse_variant_playlist(self.session, url).items() if self.options.get("mux-subtitles"): subs = self.session.http.get( self.URL_SUBTITLES.format(id=self.id), schema=validate.Schema( validate.parse_json(), { "page": { "items": [{ "lang": str, "src": validate.url(), }] } }, validate.get(("page", "items")), ), ) if subs: subtitles = { s["lang"]: HTTPStream(self.session, update_scheme("https://", s["src"], force=True)) for s in subs } for quality, stream in streams: yield quality, MuxedStream(self.session, stream, subtitles=subtitles) return yield from streams
_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" arguments = PluginArguments( PluginArgument( "username", requires=["password"], metavar="USERNAME", help="The username used to register with afreecatv.com."), PluginArgument(
class Mixer(Plugin): api_url = "https://mixer.com/api/v1/{type}/{id}" _vod_schema = validate.Schema( { "state": "AVAILABLE", "vods": [{ "baseUrl": validate.url(), "data": validate.any(None, { "Height": int }), "format": validate.text }] }, validate.get("vods"), validate.filter(lambda x: x["format"] in ("raw", "hls")), [validate.union({ "url": validate.get("baseUrl"), "format": validate.get("format"), "height": validate.all(validate.get("data"), validate.get("Height")) })]) @classmethod def can_handle_url(cls, url): return _url_re.match(url) def _get_api_res(self, api_type, api_id): try: res = http.get(self.api_url.format(type=api_type, id=api_id)) return res except Exception as e: if "404" in str(e): self.logger.error("invalid {0} - {1}".format(api_type, api_id)) elif "429" in str(e): self.logger.error("Too Many Requests, API rate limit exceeded.") raise NoStreamsError(self.url) def _get_vod_stream(self, vod_id): res = self._get_api_res("recordings", vod_id) for sdata in http.json(res, schema=self._vod_schema): if sdata["format"] == "hls": hls_url = urljoin(sdata["url"], "manifest.m3u8") yield "{0}p".format(sdata["height"]), HLSStream(self.session, hls_url) def _get_live_stream(self, channel): res = self._get_api_res("channels", channel) channel_info = http.json(res) if not channel_info["online"]: return user_id = channel_info["id"] hls_url = self.api_url.format(type="channels", id="{0}/manifest.m3u8".format(user_id)) for s in HLSStream.parse_variant_playlist(self.session, hls_url).items(): yield s def _get_streams(self): params = dict(parse_qsl(urlparse(self.url).query)) vod_id = params.get("vod") match = _url_re.match(self.url) channel = match.group("channel") if vod_id: self.logger.debug("Looking for VOD {0} from channel: {1}", vod_id, channel) return self._get_vod_stream(vod_id) else: self.logger.debug("Looking for channel: {0}", channel) return self._get_live_stream(channel)
class Booyah(Plugin): auth_api_url = 'https://booyah.live/api/v3/auths/sessions' vod_api_url = 'https://booyah.live/api/v3/playbacks/{0}' live_api_url = 'https://booyah.live/api/v3/channels/{0}' streams_api_url = 'https://booyah.live/api/v3/channels/{0}/streams' auth_schema = validate.Schema({ 'expiry_time': int, 'uid': int, }) vod_schema = validate.Schema({ 'user': { 'nickname': validate.text, }, 'playback': { 'name': validate.text, 'endpoint_list': [{ 'stream_url': validate.url(), 'resolution': validate.all( int, validate.transform(lambda x: '{}p'.format(x)), ), }], }, }) live_schema = validate.Schema({ 'user': { 'nickname': validate.text, }, 'channel': { 'channel_id': int, 'name': validate.text, 'is_streaming': bool, validate.optional('hostee'): { 'channel_id': int, 'nickname': validate.text, }, }, }) @classmethod def stream_weight(cls, stream): if stream == "source": return sys.maxsize, "source" return super(Booyah, cls).stream_weight(stream) def do_auth(self): res = self.session.http.post(self.auth_api_url) self.session.http.json(res, self.auth_schema) def get_vod(self, id): res = self.session.http.get(self.vod_api_url.format(id)) user_data = self.session.http.json(res, schema=self.vod_schema) self.author = user_data['user']['nickname'] self.category = 'VOD' self.title = user_data['playback']['name'] for stream in user_data['playback']['endpoint_list']: if stream['stream_url'].endswith('.mp4'): yield stream['resolution'], HTTPStream( self.session, stream['stream_url'], ) else: yield stream['resolution'], HLSStream( self.session, stream['stream_url'], ) def get_live(self, id): res = self.session.http.get(self.live_api_url.format(id)) user_data = self.session.http.json(res, schema=self.live_schema) if user_data['channel']['is_streaming']: self.category = 'Live' stream_id = user_data['channel']['channel_id'] elif 'hostee' in user_data['channel']: self.category = 'Hosted by {}'.format( user_data["channel"]["hostee"]["nickname"]) stream_id = user_data['channel']['hostee']['channel_id'] else: log.info('User is offline') return self.author = user_data['user']['nickname'] self.title = user_data['channel']['name'] res = self.session.http.get(self.streams_api_url.format(stream_id)) streams = self.session.http.json(res, schema=validate.Schema({ "default_mirror": validate.text, "mirror_list": [{ "name": validate.text, "url_domain": validate.url(), }], "source_stream_url_path": validate.text, "stream_addr_list": [{ "resolution": validate.text, "url_path": validate.text, }], })) mirror = (next( filter(lambda item: item["name"] == streams["default_mirror"], streams["mirror_list"]), None) or next(iter(streams["mirror_list"]), None)) if not mirror: return auto = next( filter(lambda item: item["resolution"] == "Auto", streams["stream_addr_list"]), None) if auto: for s in HLSStream.parse_variant_playlist( self.session, urljoin(mirror["url_domain"], auto["url_path"])).items(): yield s if streams["source_stream_url_path"]: yield "source", HLSStream( self.session, urljoin(mirror["url_domain"], streams["source_stream_url_path"])) def _get_streams(self): self.do_auth() url_data = self.match.groupdict() log.debug('ID={}'.format(url_data["id"])) if not url_data['type'] or url_data['type'] == 'channels': log.debug('Type=Live') return self.get_live(url_data['id']) else: log.debug('Type=VOD') return self.get_vod(url_data['id'])
class Dogan(Plugin): """ Support for the live streams from Doğan Media Group channels """ url_re = re.compile( r""" https?://(?:www\.)? (?:cnnturk\.com/(?:action/embedvideo/.*|canli-yayin|tv-cnn-turk|video/.*)| dreamturk\.com\.tr/(?:canli|canli-yayin-izle|dream-turk-ozel/.*|programlar/.*)| dreamtv\.com\.tr/dream-ozel/.*| kanald\.com\.tr/.*| teve2\.com\.tr/(?:canli-yayin|diziler/.*|embed/.*|filmler/.*|programlar/.*)) """, re.VERBOSE) playerctrl_re = re.compile(r'''<div\s+id="video-element".*?>''', re.DOTALL) data_id_re = re.compile( r'''data-id=(?P<quote>["'])/?(?P<id>\w+)(?P=quote)''') content_id_re = re.compile(r'"content[Ii]d",\s*"(\w+)"') item_id_re = re.compile(r"_itemId\s+=\s+'(\w+)';") content_api = "/actions/media?id={id}" dream_api = "/actions/content/media/{id}" new_content_api = "/action/media/{id}" content_api_schema = validate.Schema( { "data": { "id": str, "media": { "link": { validate.optional("defaultServiceUrl"): validate.any(validate.url(), ""), validate.optional("serviceUrl"): validate.any(validate.url(), ""), "securePath": str, }, }, }, }, validate.get("data"), validate.get("media"), validate.get("link"), ) new_content_api_schema = validate.Schema( { "Media": { "Link": { "ContentId": str, validate.optional("DefaultServiceUrl"): validate.any(validate.url(), ""), validate.optional("ServiceUrl"): validate.any(validate.url(), ""), "SecurePath": str, }, }, }, validate.get("Media"), validate.get("Link"), ) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _get_content_id(self): res = self.session.http.get(self.url) # find the contentId content_id_m = self.content_id_re.search(res.text) if content_id_m: log.debug("Found contentId by contentId regex") return content_id_m.group(1) # find the PlayerCtrl div player_ctrl_m = self.playerctrl_re.search(res.text) if player_ctrl_m: # extract the content id from the player control data player_ctrl_div = player_ctrl_m.group(0) content_id_m = self.data_id_re.search(player_ctrl_div) if content_id_m: log.debug("Found contentId by player data-id regex") return content_id_m.group("id") # find the itemId var item_id_m = self.item_id_re.search(res.text) if item_id_m: log.debug("Found contentId by itemId regex") return item_id_m.group(1) def _get_new_content_hls_url(self, content_id, api_url): log.debug("Using new content API url") d = self.session.http.get( urljoin(self.url, api_url.format(id=content_id))) d = self.session.http.json(d, schema=self.new_content_api_schema) if d["DefaultServiceUrl"] == "https://www.kanald.com.tr": self.url = d["DefaultServiceUrl"] return self._get_content_hls_url(content_id) else: if d["SecurePath"].startswith("http"): return d["SecurePath"] else: return urljoin((d["ServiceUrl"] or d["DefaultServiceUrl"]), d["SecurePath"]) def _get_content_hls_url(self, content_id): d = self.session.http.get( urljoin(self.url, self.content_api.format(id=content_id))) d = self.session.http.json(d, schema=self.content_api_schema) return urljoin((d["serviceUrl"] or d["defaultServiceUrl"]), d["securePath"]) def _get_hls_url(self, content_id): # make the api url relative to the current domain if "cnnturk.com" in self.url or "teve2.com.tr" in self.url: return self._get_new_content_hls_url(content_id, self.new_content_api) elif "dreamturk.com.tr" in self.url or "dreamtv.com.tr" in self.url: return self._get_new_content_hls_url(content_id, self.dream_api) else: return self._get_content_hls_url(content_id) def _get_streams(self): content_id = self._get_content_id() if content_id: log.debug(f"Loading content: {content_id}") hls_url = self._get_hls_url(content_id) return HLSStream.parse_variant_playlist(self.session, hls_url) else: log.error("Could not find the contentId for this stream")
USER_INFO_URL = "https://api.dailymotion.com/user/{0}" _media_schema = validate.Schema( validate.any( {"error": { "title": validate.text }}, # "stream_chromecast_url": validate.url(), # Chromecast URL is already available in qualities subdict { "qualities": validate.any({ validate.text: validate.all([{ "type": validate.text, "url": validate.url() }]) }) })) _live_id_schema = validate.Schema({ "total": int, "list": validate.any([], [{ "id": validate.text }]) }) @pluginmatcher( re.compile(
"channel" : validate.any(None, { "id" : validate.all( validate.text, validate.transform(int) ), "vid" : int }) }) }, validate.get("data") ) _plu_schema = validate.Schema( { "urls": [{ "securityUrl": validate.url(scheme=validate.any("rtmp", "http")), "resolution": validate.text, "ext": validate.text }] } ) _qq_schema = validate.Schema( { validate.optional("playurl"): validate.url(scheme="http") }, validate.get("playurl") ) STREAM_WEIGHTS = { "middle": 540,
https://www\.kingkong\.com\.tw/ (?: video/(?P<vid>[0-9]+G[0-9A-Za-z]+)| (?P<channel>[0-9]+) ) """, re.VERBOSE) _room_schema = validate.Schema( { "data": { "live_info": { "live_status": int, "stream_items": [{ "title": validate.text, "video": validate.any('', validate.url( scheme="https", path=validate.endswith(".flv") )) }] } } }, validate.get("data") ) _vod_schema = validate.Schema( { "data": { "live_info": { "video": validate.text } }
class Dogan(Plugin): playerctrl_re = re.compile(r'''<div\s+id="video-element".*?>''', re.DOTALL) data_id_re = re.compile( r'''data-id=(?P<quote>["'])/?(?P<id>\w+)(?P=quote)''') content_id_re = re.compile(r'"content[Ii]d",\s*"(\w+)"') item_id_re = re.compile(r"_itemId\s+=\s+'(\w+)';") content_api = "/actions/media?id={id}" dream_api = "/actions/content/media/{id}" new_content_api = "/action/media/{id}" content_api_schema = validate.Schema( { "data": { "id": validate.text, "media": { "link": { validate.optional("defaultServiceUrl"): validate.any(validate.url(), ""), validate.optional("serviceUrl"): validate.any(validate.url(), ""), "securePath": validate.text, }, }, }, }, validate.get("data"), validate.get("media"), validate.get("link"), ) new_content_api_schema = validate.Schema( { "Media": { "Link": { "ContentId": validate.text, validate.optional("DefaultServiceUrl"): validate.any(validate.url(), ""), validate.optional("ServiceUrl"): validate.any(validate.url(), ""), "SecurePath": validate.text, }, }, }, validate.get("Media"), validate.get("Link"), ) def _get_content_id(self): res = self.session.http.get(self.url) # find the contentId content_id_m = self.content_id_re.search(res.text) if content_id_m: log.debug("Found contentId by contentId regex") return content_id_m.group(1) # find the PlayerCtrl div player_ctrl_m = self.playerctrl_re.search(res.text) if player_ctrl_m: # extract the content id from the player control data player_ctrl_div = player_ctrl_m.group(0) content_id_m = self.data_id_re.search(player_ctrl_div) if content_id_m: log.debug("Found contentId by player data-id regex") return content_id_m.group("id") # find the itemId var item_id_m = self.item_id_re.search(res.text) if item_id_m: log.debug("Found contentId by itemId regex") return item_id_m.group(1) def _get_new_content_hls_url(self, content_id, api_url): log.debug("Using new content API url") d = self.session.http.get( urljoin(self.url, api_url.format(id=content_id))) d = self.session.http.json(d, schema=self.new_content_api_schema) if d["DefaultServiceUrl"] == "https://www.kanald.com.tr": self.url = d["DefaultServiceUrl"] return self._get_content_hls_url(content_id) else: if d["SecurePath"].startswith("http"): return d["SecurePath"] else: return urljoin((d["ServiceUrl"] or d["DefaultServiceUrl"]), d["SecurePath"]) def _get_content_hls_url(self, content_id): d = self.session.http.get( urljoin(self.url, self.content_api.format(id=content_id))) d = self.session.http.json(d, schema=self.content_api_schema) return urljoin((d["serviceUrl"] or d["defaultServiceUrl"]), d["securePath"]) def _get_hls_url(self, content_id): # make the api url relative to the current domain if "cnnturk.com" in self.url or "teve2.com.tr" in self.url: return self._get_new_content_hls_url(content_id, self.new_content_api) elif "dreamturk.com.tr" in self.url or "dreamtv.com.tr" in self.url: return self._get_new_content_hls_url(content_id, self.dream_api) else: return self._get_content_hls_url(content_id) def _get_streams(self): content_id = self._get_content_id() if content_id: log.debug(u"Loading content: {0}".format(content_id)) hls_url = self._get_hls_url(content_id) return HLSStream.parse_variant_playlist(self.session, hls_url) else: log.error(u"Could not find the contentId for this stream")
_media_schema = validate.Schema({ validate.optional("name"): validate.any(validate.text, None), validate.optional("series_name"): validate.any(validate.text, None), validate.optional("media_type"): validate.any(validate.text, None), "stream_data": validate.any( 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 }]) }) }) _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 } })
class App17(Plugin): _url_re = re.compile(r"https://17.live/live/(?P<channel>[^/&?]+)") API_URL = "https://api-dsa.17app.co/api/v1/lives/{0}/viewers/alive" _api_schema = validate.Schema( { "rtmpUrls": [{ validate.optional("provider"): validate.any(int, None), "url": validate.url(), }], }, validate.get("rtmpUrls"), ) @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None def _get_streams(self): match = self._url_re.match(self.url) channel = match.group("channel") self.session.http.headers.update({'User-Agent': useragents.CHROME, 'Referer': self.url}) data = '{"liveStreamID":"%s"}' % (channel) try: res = self.session.http.post(self.API_URL.format(channel), data=data) res_json = self.session.http.json(res, schema=self._api_schema) log.trace("{0!r}".format(res_json)) http_url = res_json[0]["url"] except Exception as e: log.info("Stream currently unavailable.") log.debug(str(e)) return https_url = http_url.replace("http:", "https:") yield "live", HTTPStream(self.session, https_url) if 'pull-rtmp' in http_url: rtmp_url = http_url.replace("http:", "rtmp:").replace(".flv", "") stream = RTMPStream(self.session, { "rtmp": rtmp_url, "live": True, "pageUrl": self.url, }) yield "live", stream if 'wansu-' in http_url: hls_url = http_url.replace(".flv", "/playlist.m3u8") else: hls_url = http_url.replace("live-hdl", "live-hls").replace(".flv", ".m3u8") s = HLSStream.parse_variant_playlist(self.session, hls_url) if not s: yield "live", HLSStream(self.session, hls_url) else: if len(s) == 1: for _n, _s in s.items(): yield "live", _s else: for _s in s.items(): yield _s
"360": 1, "low": 1 } _url_re = re.compile(""" http(s)?://(\w+\.)?gaminglive\.tv /(?P<type>channels|videos)/(?P<name>[^/]+) """, re.VERBOSE) _quality_re = re.compile("[^/]+-(?P<quality>[^/]+)") _channel_schema = validate.Schema( { 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 NimoTV(Plugin): url_re = re.compile(r'https?://(?:www\.)?nimo\.tv/(?P<username>.*)') data_url = 'https://m.nimo.tv/{0}' data_re = re.compile(r'<script>var G_roomBaseInfo = ({.*?});</script>') author = None category = None title = None data_schema = validate.Schema( validate.transform(data_re.search), validate.any(None, validate.all( validate.get(1), validate.transform(parse_json), { 'title': str, 'nickname': str, 'game': str, 'roomLineInfo': validate.any(None, { 'vCodeLines2': [{ 'iBitRate': int, 'vCdns': [{ 'vCdnUrls': [{ 'smediaUrl': validate.url(), }], }], }], }), }, )), ) video_qualities = { 250: '240p', 500: '360p', 1000: '480p', 2000: '720p', 6000: '1080p', } @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def get_author(self): if self.author is not None: return self.author def get_category(self): if self.category is not None: return self.category def get_title(self): if self.title is not None: return self.title def _get_streams(self): m = self.url_re.match(self.url) if m and m.group('username'): username = m.group('username') else: return headers = {'User-Agent': useragents.ANDROID} data = self.session.http.get( self.data_url.format(username), headers=headers, schema=self.data_schema, ) if data is None or data['roomLineInfo'] is None: return self.author = data['nickname'] self.category = data['game'] self.title = data['title'] for vcl2 in data['roomLineInfo']['vCodeLines2']: q = self.video_qualities[vcl2['iBitRate']] for vcdn in vcl2['vCdns']: for vcdnurl in vcdn['vCdnUrls']: if 'tx.hls.nimo.tv' in vcdnurl['smediaUrl']: log.debug(f"HLS URL={vcdnurl['smediaUrl']} ({q})") yield q, HLSStream(self.session, vcdnurl['smediaUrl'])
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 = { 'User-Agent': useragents.CHROME,
} }, validate.get("chansub") ) _user_schema = validate.Schema( { validate.optional("display_name"): validate.text }, validate.get("display_name") ) _video_schema = validate.Schema( { "chunks": { validate.text: [{ "length": int, "url": validate.any(None, validate.url(scheme="http")), "upkeep": validate.any("pass", "fail", None) }] }, "restrictions": {validate.text: validate.text}, "start_offset": int, "end_offset": int, } ) _viewer_info_schema = validate.Schema( { validate.optional("login"): validate.text }, validate.get("login") ) _viewer_token_schema = validate.Schema(
"<iframe src=\"(https://www.filmon.com/channel/export[^\"]+)\"" ) _live_json_re = re.compile("var startupChannel = (.+);") _replay_json_re = re.compile("var standByVideo = encodeURIComponent\('(.+)'\);") _history_re = re.compile( "helpers.common.flash.flashplayerinstall\({url:'([^']+)'," ) _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,
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.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)
} 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), }, ) )
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/(?:webradio-)?(?P<radio>.+))') _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="(.+?)"') _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(HTMLParser().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()) } ) ) ) _radio_stream_schema = validate.Schema( { 'audioUrls': validate.all( [{ 'url': validate.url(), 'mimeType': validate.text }] ) } ) @classmethod def check_geolocation(cls, geoloc_flag): if geoloc_flag == 'open': return True res = http.get(cls.GEO_URL) data = http.json(res, schema=cls._geo_schema) return data['country'] == geoloc_flag or data['zone'] == geoloc_flag @classmethod def tokenize_stream(cls, url): res = http.post(cls.TOKEN_URL, data={'streams[url]': url}) data = 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, radio): res = http.get(self.RADIO_STREAM_URL.format(radio.replace('-', '_'))) streams = 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 = 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 = 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']): self.logger.error('Stream is geo-restricted') 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 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']): self.logger.error('Stream is not yet available') elif 'endDate' in stream_data: if now > self.iso8601_to_epoch(stream_data['endDate']): self.logger.error('Stream has expired') def _get_streams(self): match = self.can_handle_url(self.url) if match.group('radio'): return self._get_radio_streams(match.group('radio')) return self._get_video_streams()
_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( { "base": [validate.text] }, validate.get("base"), ) }) class Livestation(Plugin):
import re from streamlink.plugin import Plugin from streamlink.plugin.api import validate _url_re = re.compile(r'http(s)?://www\.skai(?:tv)?.gr/.*') _api_url = "http://www.skaitv.gr/json/live.php" _api_res_schema = validate.Schema(validate.all( validate.get("now"), { "livestream": validate.url() }, validate.get("livestream")) ) class Skai(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) @Plugin.broken() def _get_streams(self): api_res = self.session.http.get(_api_url) yt_url = self.session.http.json(api_res, schema=_api_res_schema) if yt_url: return self.session.streams(yt_url) __plugin__ = Skai
"_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): @classmethod def can_handle_url(cls, url): return _url_re.match(url) def _get_http_streams(self, info): name = QUALITY_MAP.get(info["_quality"], "vod")
#!/usr/bin/env python import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream USER_AGENT_STRING = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/43.0.2357.65 Safari/537.36") STREAM_INFO_URL = "http://lapi.cdn.tvplayer.com/tvplayer/stream/live/id/{id}" _url_re = re.compile(r"http://(?:www.)?tvplayer.com/watch/(.+)") _channel_map_re = re.compile( r'href="/watch/([a-z]+?)".*?img.*?src=".*?/(\d+).png"', re.S) _channel_schema = validate.Schema( {"stream": validate.url(scheme=validate.any("http"))}) class TVPlayer(Plugin): @classmethod def can_handle_url(cls, url): match = _url_re.match(url) return match def _get_streams(self): url_match = _url_re.match(self.url) if url_match: # find the list of channels from the html in the page res = http.get(self.url) channel_map = dict(_channel_map_re.findall(res.text)) channel_id = channel_map.get(url_match.group(1))
class BrightcovePlayer(object): player_page = "http://players.brightcove.net/{account_id}/{player_id}/index.html" api_url = "https://edge.api.brightcove.com/playback/v1/" amf_broker = "http://c.brightcove.com/services/messagebroker/amf" policy_key_re = re.compile( r'''policyKey\s*:\s*(?P<q>['"])(?P<key>.*?)(?P=q)''') schema = validate.Schema({ "sources": [{ validate.optional("height"): validate.any(int, None), validate.optional("avg_bitrate"): validate.any(int, None), validate.optional("src"): validate.url(), validate.optional("app_name"): validate.any(validate.url(scheme="rtmp"), validate.url(scheme="rtmpe")), validate.optional("stream_name"): validate.text, validate.optional("type"): validate.text }] }) def __init__(self, session, account_id, player_id="default_default"): self.session = session log.debug("Creating player for account {0} (player_id={1})".format( account_id, player_id)) self.account_id = account_id self.player_id = player_id def player_url(self, video_id): return self.player_page.format(account_id=self.account_id, player_id=self.player_id, params=dict(videoId=video_id)) def video_info(self, video_id, policy_key): url = "{base}accounts/{account_id}/videos/{video_id}".format( base=self.api_url, account_id=self.account_id, video_id=video_id) res = self.session.http.get( url, headers={ "User-Agent": useragents.CHROME, "Referer": self.player_url(video_id), "Accept": "application/json;pk={0}".format(policy_key) }) return self.session.http.json(res, schema=self.schema) def policy_key(self, video_id): # Get the embedded player page res = self.session.http.get(self.player_url(video_id)) policy_key_m = self.policy_key_re.search(res.text) policy_key = policy_key_m and policy_key_m.group("key") if not policy_key: raise PluginError("Could not find Brightcove policy key") return policy_key def get_streams(self, video_id): log.debug("Finding streams for video: {0}".format(video_id)) policy_key = self.policy_key(video_id) log.debug("Found policy key: {0}".format(policy_key)) data = self.video_info(video_id, policy_key) headers = {"Referer": self.player_url(video_id)} for source in data.get("sources"): # determine quality name if source.get("height"): q = "{0}p".format(source.get("height")) elif source.get("avg_bitrate"): q = "{0}k".format(source.get("avg_bitrate") // 1000) else: q = "live" if ((source.get("type") == "application/x-mpegURL" and source.get("src")) or (source.get("src") and ".m3u8" in source.get("src"))): yield from HLSStream.parse_variant_playlist( self.session, source.get("src"), headers=headers).items() elif source.get("app_name"): s = RTMPStream( self.session, { "rtmp": source.get("app_name"), "playpath": source.get("stream_name") }) yield q, s elif source.get("src") and source.get("src").endswith(".mp4"): yield q, HTTPStream(self.session, source.get("src"), headers=headers) @classmethod def from_url(cls, session, url): purl = urlparse(url) querys = dict(parse_qsl(purl.query)) account_id, player_id, _ = purl.path.lstrip("/").split("/", 3) video_id = querys.get("videoId") bp = cls(session, account_id=account_id, player_id=player_id) return bp.get_streams(video_id) @classmethod def from_player_key(cls, session, player_id, player_key, video_id, url=None): amf_message = AMFMessage( "com.brightcove.experience.ExperienceRuntimeFacade.getDataForExperience", "/1", [ ''.join([ "{0:02x}".format(random.randint(0, 255)) for _ in range(20) ]), # random id ViewerExperienceRequest(experienceId=int(player_id), URL=url or "", playerKey=player_key, deliveryType=float('nan'), TTLToken="", contentOverrides=[ ContentOverride( featuredRefId=None, contentRefIds=None, contentId=int(video_id)) ]) ]) amf_packet = AMFPacket(version=3) amf_packet.messages.append(amf_message) res = session.http.post(cls.amf_broker, headers={"Content-Type": "application/x-amf"}, data=amf_packet.serialize(), params=dict(playerKey=player_key), raise_for_status=False) data = AMFPacket.deserialize(BytesIO(res.content)) result = data.messages[0].value bp = cls(session=session, account_id=int(result.publisherId)) return bp.get_streams(video_id)
class ITVPlayer(Plugin): _url_re = re.compile(r"https?://(?:www.)?itv.com/hub/(?P<stream>.+)") _video_info_schema = validate.Schema({ "StatusCode": 200, "AdditionalInfo": { "Message": validate.any(None, validate.text) }, "Playlist": { "VideoType": validate.text, "Video": { "Subtitles": validate.any(None, [{ "Href": validate.url(), }]), "Base": validate.url(), "MediaFiles": [ {"Href": validate.text, "KeyServiceUrl": validate.any(None, validate.url())} ] } } }) @classmethod def can_handle_url(cls, url): match = cls._url_re.match(url) return match is not None @property def device_info(self): return {"user": {}, "device": {"manufacturer": "Chrome", "model": "66", "os": {"name": "Windows", "version": "10", "type": "desktop"}}, "client": {"version": "4.1", "id": "browser"}, "variantAvailability": {"featureset": {"min": ["hls", "aes"], "max": ["hls", "aes"]}, "platformTag": "dotcom"}} def video_info(self): page = self.session.http.get(self.url) for div in itertags(page.text, 'div'): if div.attributes.get("id") == "video": return div.attributes def _get_streams(self): """ Find all the streams for the ITV url :return: Mapping of quality to stream """ self.session.http.headers.update({"User-Agent": useragents.FIREFOX}) video_info = self.video_info() video_info_url = video_info.get("data-html5-playlist") or video_info.get("data-video-id") res = self.session.http.post(video_info_url, data=json.dumps(self.device_info), headers={"hmac": video_info.get("data-video-hmac")}) data = self.session.http.json(res, schema=self._video_info_schema) log.debug("Video ID info response: {0}".format(data)) stype = data['Playlist']['VideoType'] for media in data['Playlist']['Video']['MediaFiles']: url = urljoin(data['Playlist']['Video']['Base'], media['Href']) name_fmt = "{pixels}_{bitrate}" if stype == "CATCHUP" else None for s in HLSStream.parse_variant_playlist(self.session, url, name_fmt=name_fmt).items(): yield s
class Dogan(Plugin): """ Support for the live streams from Doğan Media Group channels """ url_re = re.compile( r""" https?://(?:www.)? (?:teve2.com.tr/(?:canli-yayin|filmler/.*|programlar/.*)| kanald.com.tr/.*| cnnturk.com/canli-yayin| dreamtv.com.tr/canli-yayin| dreamturk.com.tr/canli) """, re.VERBOSE) playerctrl_re = re.compile( r'''<div[^>]*?ng-controller=(?P<quote>["'])(?:Live)?PlayerCtrl(?P=quote).*?>''', re.DOTALL) data_id_re = re.compile( r'''data-id=(?P<quote>["'])(?P<id>\w+)(?P=quote)''') content_id_re = re.compile(r'"content(?:I|i)d", "(\w+)"') content_api = "/actions/content/media/{id}" new_content_api = "/action/media/{id}" content_api_schema = validate.Schema({ "Id": validate.text, "Media": { "Link": { "DefaultServiceUrl": validate.url(), validate.optional("ServiceUrl"): validate.any(validate.url(), ""), "SecurePath": validate.text, } } }) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _get_content_id(self): res = http.get(self.url) # find the contentId content_id_m = self.content_id_re.search(res.text) if content_id_m: return content_id_m.group(1) # find the PlayerCtrl div player_ctrl_m = self.playerctrl_re.search(res.text) if player_ctrl_m: # extract the content id from the player control data player_ctrl_div = player_ctrl_m.group(0) content_id_m = self.data_id_re.search(player_ctrl_div) if content_id_m: return content_id_m.group("id") def _get_hls_url(self, content_id): # make the api url relative to the current domain if "cnnturk" in self.url or "teve2.com.tr" in self.url: self.logger.debug("Using new content API url") api_url = urljoin(self.url, self.new_content_api.format(id=content_id)) else: api_url = urljoin(self.url, self.content_api.format(id=content_id)) apires = http.get(api_url) stream_data = http.json(apires, schema=self.content_api_schema) d = stream_data["Media"]["Link"] return urljoin((d["ServiceUrl"] or d["DefaultServiceUrl"]), d["SecurePath"]) def _get_streams(self): content_id = self._get_content_id() if content_id: self.logger.debug(u"Loading content: {}", content_id) hls_url = self._get_hls_url(content_id) return HLSStream.parse_variant_playlist(self.session, hls_url) else: self.logger.error(u"Could not find the contentId for this stream")
class Rtve(Plugin): secret_key = base64.b64decode("eWVMJmRhRDM=") content_id_re = re.compile(r'data-id\s*=\s*"(\d+)"') url_re = re.compile( r""" https?://(?:www\.)?rtve\.es/(?:directo|noticias|television|deportes|alacarta|drmn)/.*?/? """, re.VERBOSE) cdn_schema = validate.Schema( validate.transform(parse_xml), 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)) options = PluginOptions({"mux_subtitles": False}) @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.zclient = ZTNRClient(self.secret_key) http.headers = {"User-Agent": useragents.SAFARI_8} def _get_content_id(self): res = http.get(self.url) m = self.content_id_re.search(res.text) return m and int(m.group(1)) def _get_subtitles(self, content_id): res = http.get(self.subtitles_api.format(id=content_id)) return http.json(res, schema=self.subtitles_schema) def _get_quality_map(self, content_id): res = http.get(self.video_api.format(id=content_id)) data = http.json(res, schema=self.video_schema) qmap = {} for item in data["qualities"]: qname = { "MED": "Media", "HIGH": "Alta" }.get(item["preset"], item["preset"]) qmap[qname] = u"{0}p".format(item["height"]) return qmap def _get_streams(self): streams = [] content_id = self._get_content_id() if content_id: self.logger.debug("Found content with id: {0}", 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 url.endswith("m3u8"): streams.extend( HLSStream.parse_variant_playlist( self.session, url).items()) elif url.endswith("mp4"): 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.plugin.api import http, validate from streamlink.stream import HTTPStream, RTMPStream API_CLIENT_NAME = "Bambuser AS2" API_CONTEXT = "b_broadcastpage" API_KEY = "005f64509e19a868399060af746a00aa" API_URL_VIDEO = "http://player-c.api.bambuser.com/getVideo.json" _url_re = re.compile("http(s)?://(\w+.)?bambuser.com/v/(?P<video_id>\d+)") _video_schema = validate.Schema({ validate.optional("error"): validate.text, validate.optional("result"): { "id": validate.text, "size": validate.text, "url": validate.url( scheme=validate.any("rtmp", "http") ) } }) class Bambuser(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_streams(self): match = _url_re.match(self.url) video_id = match.group("video_id") params = { "client_name": API_CLIENT_NAME,
class Vlive(Plugin): _page_info = re.compile(r"window\.__PRELOADED_STATE__\s*=\s*({.*})\s*(?:<|,\s*function)", re.DOTALL) _playinfo_url = "https://www.vlive.tv/globalv-web/vam-web/old/v3/live/{0}/playInfo" _schema_video = validate.Schema( validate.transform(_page_info.search), validate.any(None, validate.all( validate.get(1), validate.parse_json(), validate.any( validate.all( {"postDetail": {"post": {"officialVideo": { "type": str, "videoSeq": int, validate.optional("status"): str}}}}, validate.get(("postDetail", "post", "officialVideo"))), validate.all( {"postDetail": {"error": {"errorCode": str}}}, validate.get(("postDetail", "error")), ) ) )) ) _schema_stream = validate.Schema( validate.parse_json(), {"result": {"adaptiveStreamUrl": validate.url()}}, validate.get(("result", "adaptiveStreamUrl")), ) def _get_streams(self): self.session.http.headers.update({"Referer": self.url}) video_json = self.session.http.get(self.url, schema=self._schema_video) if video_json is None: log.error('Could not parse video page') return err = video_json.get('errorCode') if err == 'common_700': log.error('Available only to members of the channel') return elif err == 'common_702': log.error('Vlive+ VODs are not supported') return elif err == 'common_404': log.error('Could not find video page') return elif err is not None: log.error('Unknown error code: {0}'.format(err)) return if video_json['type'] == 'VOD': log.error('VODs are not supported') return url_format, video_id = self.match.groups() if url_format == 'post': video_id = str(video_json['videoSeq']) video_status = video_json.get('status') if video_status == 'ENDED': log.error('Stream has ended') return elif video_status != 'ON_AIR': log.error('Unknown video status: {0}'.format(video_status)) return stream_url = self.session.http.get( self._playinfo_url.format(video_id), schema=self._schema_stream) return HLSStream.parse_variant_playlist(self.session, stream_url).items()
def test_url(self): url_ = "https://google.se/path" assert validate(url(), url_) assert validate(url(scheme="http"), url_) assert validate(url(path="/path"), url_)
def test_url(self): url_ = "https://google.se/path" assert validate(url(), url_) assert validate(url(scheme="http"), url_) assert validate(url(path="/path"), url_)
from streamlink.plugin import Plugin, PluginError from streamlink.plugin.api import http, validate from streamlink.stream import RTMPStream, HLSStream SWF_URL = "http://play.streamingvideoprovider.com/player2.swf" API_URL = "http://player.webvideocore.net/index.php" _url_re = re.compile( r"http(s)?://(\w+\.)?streamingvideoprovider.co.uk/(?P<channel>[^/&?]+)" ) _hls_re = re.compile(r"'(http://.+\.m3u8)'") _rtmp_schema = validate.Schema( validate.xml_findtext("./info/url"), validate.url(scheme="rtmp") ) _hls_schema = validate.Schema( validate.transform(_hls_re.search), validate.any( None, validate.all( validate.get(1), validate.url( scheme="http", path=validate.endswith("m3u8") ) ) ) )
"low": 1 } _url_re = re.compile( r""" http(s)?://(\w+\.)?gaminglive\.tv /(?P<type>channels|videos)/(?P<name>[^/]+) """, re.VERBOSE) _quality_re = re.compile(r"[^/]+-(?P<quality>[^/]+)") _channel_schema = validate.Schema( { 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
(?: (/embed)?/(video|live) /(?P<media_id>[^_?/]+) | /(?P<channel_name>[A-Za-z0-9-_]+) ) """, re.VERBOSE) _media_schema = validate.Schema(validate.any( {"error": {"title": validate.text}}, # "stream_chromecast_url": validate.url(), # Chromecast URL is already available in qualities subdict {"qualities": validate.any({ validate.text: validate.all([{ "type": validate.text, "url": validate.url() }]) }) })) _live_id_schema = validate.Schema( { "total": int, "list": validate.any( [], [{"id": validate.text}] ) } ) class DailyMotion(Plugin):
class PlayTV(Plugin): FORMATS_URL = 'http://playtv.fr/player/initialize/{0}/' API_URL = 'http://playtv.fr/player/play/{0}/?format={1}&language={2}&bitrate={3}' _url_re = re.compile(r'http://playtv\.fr/television/(?P<channel>[^/]+)/?') _formats_schema = validate.Schema({ 'streams': validate.any( [], { validate.text: validate.Schema({ validate.text: { 'bitrates': validate.all( [validate.Schema({'value': int})]) } }) }) }) _api_schema = validate.Schema({'url': validate.url()}) @classmethod def can_handle_url(cls, url): return PlayTV._url_re.match(url) def _get_streams(self): match = self._url_re.match(self.url) channel = match.group('channel') res = http.get(self.FORMATS_URL.format(channel)) streams = http.json(res, schema=self._formats_schema)['streams'] if streams == []: self.logger.error( 'Channel may be geo-restricted, not directly provided by PlayTV or not freely available' ) return for language in streams: for protocol, bitrates in list(streams[language].items()): # - Ignore non-supported protocols (RTSP, DASH) # - Ignore deprecated Flash (RTMPE/HDS) streams (PlayTV doesn't provide anymore a Flash player) if protocol in ['rtsp', 'flash', 'dash', 'hds']: continue for bitrate in bitrates['bitrates']: if bitrate['value'] == 0: continue api_url = self.API_URL.format(channel, protocol, language, bitrate['value']) res = http.get(api_url) video_url = http.json(res, schema=self._api_schema)['url'] bs = '{0}k'.format(bitrate['value']) if protocol == 'hls': for _, stream in HLSStream.parse_variant_playlist( self.session, video_url).items(): yield bs, stream elif protocol == 'hds': for _, stream in HDSStream.parse_manifest( self.session, video_url).items(): yield bs, stream
import re from streamlink.compat import urlparse from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import RTMPStream, HTTPStream, HLSStream from streamlink.utils import parse_json, rtmpparse, swfdecompress _url_re = re.compile("http(s)?://api.dmcloud.net/player/embed/[^/]+/[^/]+") _rtmp_re = re.compile(b"customURL[^h]+(https://.*?)\\\\") _info_re = re.compile("var info = (.*);") _schema = validate.Schema( { "mode": validate.text, validate.optional("mp4_url"): validate.url(scheme="http"), validate.optional("ios_url"): validate.url(scheme="http"), validate.optional("swf_url"): validate.url(scheme="http"), } ) class DMCloud(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_rtmp_stream(self, swfurl): res = http.get(swfurl) swf = swfdecompress(res.content) match = _rtmp_re.search(swf) if not match:
def _get_streams(self): self.session.http.headers.update({"Referer": self.url}) schema_data = validate.Schema( validate.parse_html(), validate.xml_xpath_string( ".//script[@id='__NEXT_DATA__'][text()]/text()"), str, validate.parse_json(), { "props": { "pageProps": { "data": { "liveBroadcast": { # "id": str, "current": validate.any( None, { "channel": str, "channelName": str, "legacyEncoder": str, }), "next": validate.any( None, { "channel": str, "channelName": str, "legacyEncoder": str, }) } } } } }, validate.get(("props", "pageProps", "data", "liveBroadcast")), validate.union_get("current", "next"), ) try: data_current, data_next = self.session.http.get(self.url, schema=schema_data) except PluginError: return log.debug(f"current={data_current!r}") log.debug(f"next={data_next!r}") data = data_current or data_next video_id = data["legacyEncoder"] self.title = data["channelName"] _hash = self.session.http.get( "https://www.ceskatelevize.cz/v-api/iframe-hash/", schema=validate.Schema(str)) res = self.session.http.get( "https://www.ceskatelevize.cz/ivysilani/embed/iFramePlayer.php", params={ "hash": _hash, "origin": "iVysilani", "autoStart": "true", "videoID": video_id, }, ) m = self._re_playlist_info.search(res.text) if not m: return _type, _id = m.groups() data = self.session.http.post( "https://www.ceskatelevize.cz/ivysilani/ajax/get-client-playlist/", data={ "playlist[0][type]": _type, "playlist[0][id]": _id, "requestUrl": "/ivysilani/embed/iFramePlayer.php", "requestSource": "iVysilani", "type": "html", "canPlayDRM": "false", }, headers={ "x-addr": "127.0.0.1", }, schema=validate.Schema( validate.parse_json(), { validate.optional("streamingProtocol"): str, "url": validate.any(validate.url(), "Error", "error_region") }), ) if data["url"] in ["Error", "error_region"]: log.error("This stream is not available") return url = self.session.http.get( data["url"], schema=validate.Schema( validate.parse_json(), { "playlist": [{ validate.optional("type"): str, "streamUrls": { "main": validate.url(), } }] }, validate.get(("playlist", 0, "streamUrls", "main")))) return DASHStream.parse_manifest(self.session, url)
_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): res = http.get(self.url, schema=_schema) if not res: return
class Bloomberg(Plugin): VOD_API_URL = 'https://www.bloomberg.com/api/embed?id={0}' PLAYER_URL = 'https://cdn.gotraffic.net/projector/latest/bplayer.js' CHANNEL_MAP = { 'audio': 'BBG_RADIO', 'live/europe': 'EU', 'live/us': 'US', 'live/asia': 'ASIA', 'live/stream': 'EVENT', 'live/emea': 'EMEA_EVENT', 'live/asia_stream': 'ASIA_EVENT' } _url_re = re.compile( r''' https?://www\.bloomberg\.com/( news/videos/[^/]+/[^/]+ | (?P<channel>live/(?:stream|emea|asia_stream|europe|us|asia)|audio)/? ) ''', re.VERBOSE) _live_player_re = re.compile( r'{APP_BUNDLE:"(?P<live_player_url>.+?/app.js)"') _js_to_json_re = partial( re.compile(r'(\w+):(["\']|\d?\.?\d+,|true|false|\[|{)').sub, r'"\1":\2') _video_id_re = re.compile(r'data-bmmr-id=\\"(?P<video_id>.+?)\\"') _mp4_bitrate_re = re.compile(r'.*_(?P<bitrate>[0-9]+)\.mp4') _live_streams_schema = validate.Schema( validate.transform(_js_to_json_re), validate.transform(lambda x: x.replace(':.', ':0.')), validate.transform(parse_json), validate.Schema( { 'cdns': validate.all([ validate.Schema( { 'streams': validate.all([ validate.Schema( { 'url': validate.transform(lambda x: re.sub( r'(https?:/)([^/])', r'\1/\2', x)) }, validate.get('url'), validate.url()) ]), }, validate.get('streams')) ], validate.transform(lambda x: [i for y in x for i in y])) }, validate.get('cdns'))) _vod_api_schema = validate.Schema( { 'secureStreams': validate.all([ validate.Schema({'url': validate.url()}, validate.get('url')) ]), 'streams': validate.all([ validate.Schema({'url': validate.url()}, validate.get('url')) ]), 'contentLoc': validate.url(), }, validate.transform(lambda x: list( set(x['secureStreams'] + x['streams'] + [x['contentLoc']])))) @classmethod def can_handle_url(cls, url): return Bloomberg._url_re.match(url) def _get_live_streams(self): # Get channel id match = self._url_re.match(self.url) channel = match.group('channel') # Retrieve live player URL res = http.get(self.PLAYER_URL) match = self._live_player_re.search(res.text) if match is None: return [] live_player_url = update_scheme(self.url, match.group('live_player_url')) # Extract streams from the live player page res = http.get(live_player_url) stream_datas = re.findall( r'{0}(?:_MINI)?:({{.+?}}]}}]}})'.format(self.CHANNEL_MAP[channel]), res.text) streams = [] for s in stream_datas: for u in self._live_streams_schema.validate(s): if u not in streams: streams.append(u) return streams def _get_vod_streams(self): # Retrieve URL page and search for video ID res = http.get(self.url) match = self._video_id_re.search(res.text) if match is None: return [] video_id = match.group('video_id') res = http.get(self.VOD_API_URL.format(video_id)) streams = http.json(res, schema=self._vod_api_schema) return streams def _get_streams(self): if '/news/videos/' in self.url: # VOD streams = self._get_vod_streams() else: # Live streams = self._get_live_streams() for video_url in streams: if '.f4m' in video_url: for stream in HDSStream.parse_manifest(self.session, video_url).items(): yield stream elif '.m3u8' in video_url: for stream in HLSStream.parse_variant_playlist( self.session, video_url).items(): yield stream if '.mp4' in video_url: match = self._mp4_bitrate_re.match(video_url) if match is not None: bitrate = match.group('bitrate') + 'k' else: bitrate = 'vod' yield bitrate, HTTPStream(self.session, video_url)
"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") ) }) class NRK(Plugin): @classmethod
{ "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: [{ "length": int, "url": validate.any(None, validate.url(scheme="http")), "upkeep": validate.any("pass", "fail", None) }] }, "restrictions": { validate.text: validate.text }, "start_offset": int, "end_offset": int, }) _viewer_info_schema = validate.Schema( {validate.optional("login"): validate.text}, validate.get("login")) _viewer_token_schema = validate.Schema( {validate.optional("token"): validate.text}, validate.get("token"))
_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), validate.transform(bool) ), }] ), validate.optional("adaptive_fmts"): validate.all( validate.text, validate.transform(parse_stream_map), [{ validate.optional("s"): validate.text, "type": validate.all( validate.text,
class Mitele(Plugin): _url_re = re.compile( r"https?://(?:www\.)?mitele\.es/directo/(?P<channel>[\w-]+)") pdata_url = "https://indalo.mediaset.es/mmc-player/api/mmc/v1/{channel}/live/html5.json" gate_url = "https://gatekeeper.mediaset.es" error_schema = validate.Schema({ "code": validate.any(validate.text, int), "message": validate.text, }) pdata_schema = validate.Schema( validate.transform(parse_json), validate.any( validate.all( { "locations": [{ "gcp": validate.text, "ogn": validate.any(None, validate.text), }], }, validate.get("locations"), validate.get(0), ), error_schema, )) gate_schema = validate.Schema( validate.transform(parse_json), validate.any( { "mimeType": validate.text, "stream": validate.url(), }, error_schema, )) def __init__(self, url): super(Mitele, self).__init__(url) self.session.http.headers.update({ "User-Agent": useragents.FIREFOX, "Referer": self.url }) @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None def _get_streams(self): channel = self._url_re.match(self.url).group("channel") pdata = self.session.http.get(self.pdata_url.format(channel=channel), acceptable_status=(200, 403, 404), schema=self.pdata_schema) log.trace("{0!r}".format(pdata)) if pdata.get("code"): log.error("{0} - {1}".format(pdata["code"], pdata["message"])) return gdata = self.session.http.post(self.gate_url, acceptable_status=(200, 403, 404), data=pdata, schema=self.gate_schema) log.trace("{0!r}".format(gdata)) if gdata.get("code"): log.error("{0} - {1}".format(gdata["code"], gdata["message"])) return log.debug("Stream: {0} ({1})".format(gdata["stream"], gdata.get("mimeType", "n/a"))) for s in HLSStream.parse_variant_playlist( self.session, gdata["stream"], name_fmt="{pixels}_{bitrate}").items(): yield s
from streamlink.compat import urljoin from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.plugin.api.utils import parse_json from streamlink.stream import AkamaiHDStream, HLSStream _url_re = re.compile(r"http(s)?://(www\.)?livestream.com/") _stream_config_schema = validate.Schema({ "event": { "stream_info": validate.any({ "is_live": bool, "qualities": [{ "bitrate": int, "height": int }], validate.optional("play_url"): validate.url(scheme="http"), validate.optional("m3u8_url"): validate.url( scheme="http", path=validate.endswith(".m3u8") ), }, 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"
class TV5Monde(Plugin): _url_re = re.compile(r'http://(.+\.)?(tv|tivi)5monde(plus(afrique)?)?\.com') _videos_re = re.compile(r'"?(?:files|sources)"?:\s*(?P<videos>\[.+?\])') _videos_embed_re = re.compile(r'(?:file:\s*|src=)"(?P<embed>.+?\.mp4|.+?/embed/.+?)"') _videos_schema = validate.Schema( validate.transform(_js_to_json), validate.transform(parse_json), validate.all([ validate.any( validate.Schema( {'url': validate.url()}, validate.get('url') ), validate.Schema( {'file': validate.url()}, validate.get('file') ), ) ]) ) @classmethod def can_handle_url(cls, url): return TV5Monde._url_re.match(url) def _get_non_embed_streams(self, page): match = self._videos_re.search(page) if match is not None: videos = self._videos_schema.validate(match.group('videos')) return videos return [] def _get_embed_streams(self, page): match = self._videos_embed_re.search(page) if match is None: return [] url = match.group('embed') if '.mp4' in url: return [url] res = self.session.http.get(url) videos = self._get_non_embed_streams(res.text) if videos: return videos return [] def _get_streams(self): res = self.session.http.get(self.url) match = self._videos_re.search(res.text) if match is not None: videos = self._videos_schema.validate(match.group('videos')) else: videos = self._get_embed_streams(res.text) for url in videos: if '.m3u8' in url: for stream in HLSStream.parse_variant_playlist(self.session, url).items(): yield stream elif 'rtmp' in url: yield 'vod', RTMPStream(self.session, {'rtmp': url}) elif '.mp4' in url: yield 'vod', HTTPStream(self.session, url)
"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" arguments = PluginArguments( PluginArgument( "username", requires=["password"], metavar="USERNAME", help="The username used to register with afreecatv.com."
class IDF1(Plugin): DACAST_API_URL = 'https://json.dacast.com/b/{}/{}/{}' DACAST_TOKEN_URL = 'https://services.dacast.com/token/i/b/{}/{}/{}' _url_re = re.compile( r'http://www\.idf1\.fr/(videos/[^/]+/[^/]+\.html|live\b)') _video_id_re = re.compile( r"dacast\('(?P<broadcaster_id>\d+)_(?P<video_type>[a-z]+)_(?P<video_id>\d+)', 'replay_content', data\);" ) _video_id_alt_re = re.compile( r'<script src="//player.dacast.com/js/player.js" id="(?P<broadcaster_id>\d+)_(?P<video_type>[cf])_(?P<video_id>\d+)"' ) _player_url = 'http://ssl.p.jwpcdn.com/player/v/7.12.6/jwplayer.flash.swf' _api_schema = validate.Schema( validate.transform(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.transform(parse_json), {'token': validate.text}, validate.get('token')) _user_agent = useragents.IE_11 @classmethod def can_handle_url(cls, url): return IDF1._url_re.match(url) 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) 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
from streamlink.plugin import Plugin 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
""", re.VERBOSE) _room_id_schema = validate.Schema( { "data": validate.any(None, { "room_id": int, "live_status": int }) }, validate.get("data") ) _room_stream_list_schema = validate.Schema( { "data": validate.any(None, { "durl": [{"url": validate.url()}] }) }, validate.get("data") ) class Bilibili(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) @classmethod def stream_weight(cls, stream): if stream in STREAM_WEIGHTS: return STREAM_WEIGHTS[stream], "Bilibili"