class Garena(Plugin): API_INFO = "https://garena.live/api/channel_info_get" API_STREAM = "https://garena.live/api/channel_stream_get" _info_schema = validate.Schema({ "reply": validate.any({ "channel_id": int, }, None), "result": validate.text }) _stream_schema = validate.Schema({ "reply": validate.any( { "streams": [{ "url": validate.text, "resolution": int, "bitrate": int, "format": int }] }, None), "result": validate.text }) @classmethod def can_handle_url(self, url): return _url_re.match(url) def _post_api(self, api, payload, schema): res = http.post(api, json=payload) data = http.json(res, schema=schema) if data["result"] == "success": post_data = data["reply"] return post_data def _get_streams(self): match = _url_re.match(self.url) if match.group("alias"): payload = {"alias": match.group("alias")} info_data = self._post_api(self.API_INFO, payload, self._info_schema) channel_id = info_data["channel_id"] elif match.group("channel_id"): channel_id = int(match.group("channel_id")) if channel_id: payload = {"channel_id": channel_id} stream_data = self._post_api(self.API_STREAM, payload, self._stream_schema) for stream in stream_data["streams"]: n = "{0}p".format(stream["resolution"]) if stream["format"] == 3: s = HLSStream(self.session, stream["url"]) yield n, s
class LiveMe(Plugin): url_re = re.compile(r"https?://(www.)?liveme\.com/live\.html\?videoid=(\d+)") api_url = "https://live.ksmobile.net/live/queryinfo" api_schema = validate.Schema(validate.all({ "status": "200", "data": { "video_info": { "videosource": validate.any('', validate.url()), "hlsvideosource": validate.any('', validate.url()), } } }, validate.get("data"))) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _random_t(self, t): return "".join(random.choice("ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678") for _ in range(t)) def _make_stream(self, url): if url and url.endswith("flv"): return HTTPStream(self.session, url) elif url and url.endswith("m3u8"): return HLSStream(self.session, url) def _get_streams(self): url_params = dict(parse_qsl(urlparse(self.url).query)) video_id = url_params.get("videoid") if video_id: vali = '{0}l{1}m{2}'.format(self._random_t(4), self._random_t(4), self._random_t(5)) data = { 'userid': 1, 'videoid': video_id, 'area': '', 'h5': 1, 'vali': vali } self.logger.debug("Found Video ID: {0}".format(video_id)) res = http.post(self.api_url, data=data) data = http.json(res, schema=self.api_schema) hls = self._make_stream(data["video_info"]["hlsvideosource"]) video = self._make_stream(data["video_info"]["videosource"]) if hls: yield "live", hls if video: yield "live", video
class Streamable(Plugin): url_re = re.compile(r"https?://(?:www\.)?streamable\.com/(.+)") meta_re = re.compile(r'''var\s*videoObject\s*=\s*({.*});''') config_schema = validate.Schema( validate.transform(meta_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(parse_json), { "files": { validate.text: { "url": validate.url(), "width": int, "height": int, "bitrate": int } } }))) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _get_streams(self): data = http.get(self.url, schema=self.config_schema) for info in data["files"].values(): stream_url = update_scheme(self.url, info["url"]) # pick the smaller of the two dimensions, for landscape v. portrait videos res = min(info["width"], info["height"]) yield "{0}p".format(res), HTTPStream(self.session, stream_url)
class TV8(Plugin): """ Support for the live stream on www.tv8.com.tr """ url_re = re.compile(r"https?://www.tv8.com.tr/canli-yayin") player_config_re = re.compile( r""" configPlayer.source.media.push[ ]*\( [ ]*\{[ ]*'src':[ ]*"(.*?)", [ ]*type:[ ]*"application/x-mpegURL"[ ]*}[ ]*\); """, re.VERBOSE) player_config_schema = validate.Schema( validate.transform(player_config_re.search), validate.any(None, validate.all(validate.get(1), validate.url()))) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _get_streams(self): res = http.get(self.url) stream_url = self.player_config_schema.validate(res.text) if stream_url: return HLSStream.parse_variant_playlist(self.session, stream_url)
class RaiPlay(Plugin): url_re = re.compile(r"https?://(?:www\.)?raiplay\.it/dirette/(\w+)/?") stream_re = re.compile(r"data-video-url.*?=.*?\"([^\"]+)\"") stream_schema = validate.Schema( validate.all( validate.transform(stream_re.search), validate.any(None, validate.all(validate.get(1), validate.url())))) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _get_streams(self): http.headers.update({"User-Agent": useragents.FIREFOX}) channel = self.url_re.match(self.url).group(1) self.logger.debug("Found channel: {0}", channel) stream_url = http.get(self.url, schema=self.stream_schema) if stream_url: try: return HLSStream.parse_variant_playlist( self.session, stream_url) except Exception as e: if "Missing #EXTM3U header" in str(e): raise PluginError( "The streaming of this content is available in Italy only." ) raise e
class Filmon(Plugin): url_re = re.compile( r"""https?://(?:\w+\.)?filmon.(?:tv|com)/ (?: (tv|channel)/(?P<channel>[^/]+)| vod/view/(?P<vod_id>\d+)-| group/ ) """, re.VERBOSE) _channel_id_re = re.compile(r'''channel_id\s*?=\s*["']?(\d+)["']''') _channel_id_schema = validate.Schema( validate.transform(_channel_id_re.search), validate.any(None, validate.get(1))) quality_weights = {"high": 720, "low": 480} def __init__(self, url): super(Filmon, self).__init__(url) self.api = FilmOnAPI() @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None @classmethod def stream_weight(cls, key): weight = cls.quality_weights.get(key) if weight: return weight, "filmon" return Plugin.stream_weight(key) def _get_streams(self): url_m = self.url_re.match(self.url) vod_id = url_m and url_m.group("vod_id") if vod_id: self.logger.debug("vod_id: {0}".format(vod_id)) data = self.api.vod(vod_id) for _, stream in data["streams"].items(): yield stream["quality"], FilmOnHLS(self.session, vod_id=vod_id, quality=stream["quality"]) else: channel = http.get(self.url, schema=self._channel_id_schema) if channel: self.logger.debug("channel_id: {0}".format(channel)) else: channel = url_m and url_m.group("channel") self.logger.debug("channel: {0}".format(channel)) data = self.api.channel(channel) for stream in data["streams"]: yield stream["quality"], FilmOnHLS(self.session, channel=channel, quality=stream["quality"])
class CDNBG(Plugin): url_re = re.compile( r""" https?://(?:www\.)?(?: tv\.bnt\.bg/\w+(?:/\w+)?| bitelevision\.com/live| nova\.bg/live| kanal3\.bg/live| bgonair\.bg/tvonline| tvevropa\.com/na-zhivo| bloombergtv.bg/video )/? """, re.VERBOSE) iframe_re = re.compile( r"iframe .*?src=\"((?:https?:)?//(?:\w+\.)?cdn.bg/live[^\"]+)\"", re.DOTALL) sdata_re = re.compile( r"sdata\.src.*?=.*?(?P<q>[\"'])(?P<url>http.*?)(?P=q)") hls_file_re = re.compile( r"(src|file): (?P<q>[\"'])(?P<url>(https?:)?//.+?m3u8.*?)(?P=q)") hls_src_re = re.compile(r"video src=(?P<url>http[^ ]+m3u8[^ ]*)") stream_schema = validate.Schema( validate.any( validate.all(validate.transform(sdata_re.search), validate.get("url")), validate.all(validate.transform(hls_file_re.search), validate.get("url")), validate.all(validate.transform(hls_src_re.search), validate.get("url")), )) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def find_iframe(self, res): p = urlparse(self.url) for url in self.iframe_re.findall(res.text): if "googletagmanager" not in url: if url.startswith("//"): return "{0}:{1}".format(p.scheme, url) else: return url def _get_streams(self): http.headers = {"User-Agent": useragents.CHROME} res = http.get(self.url) iframe_url = self.find_iframe(res) if iframe_url: self.logger.debug("Found iframe: {0}", iframe_url) res = http.get(iframe_url, headers={"Referer": self.url}) stream_url = update_scheme(self.url, self.stream_schema.validate(res.text)) return HLSStream.parse_variant_playlist( self.session, stream_url, headers={"User-Agent": useragents.CHROME})
class RadioNet(Plugin): _url_re = re.compile(r"https?://(\w+)\.radio\.(net|at|de|dk|es|fr|it|pl|pt|se)") _stream_data_re = re.compile(r'\bstation\s*:\s*(\{.+\}),?\s*') _stream_schema = validate.Schema( validate.transform(_stream_data_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(parse_json), { 'stationType': validate.text, 'streamUrls': validate.all([{ 'bitRate': int, 'streamUrl': validate.url() }]) }, ) ) ) @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) def _get_streams(self): streams = http.get(self.url, schema=self._stream_schema) if streams is None: return # Ignore non-radio streams (podcasts...) if streams['stationType'] != 'radio_station': return stream_urls = [] for stream in streams['streamUrls']: if stream['streamUrl'] in stream_urls: continue if stream['bitRate'] > 0: bitrate = '{}k'.format(stream['bitRate']) else: bitrate = 'live' yield bitrate, HTTPStream(self.session, stream['streamUrl']) stream_urls.append(stream['streamUrl'])
class ard_live(Plugin): swf_url = "http://live.daserste.de/lib/br-player/swf/main.swf" _url_re = re.compile(r"https?://(www.)?daserste.de/", re.I) _player_re = re.compile(r'''dataURL\s*:\s*(?P<q>['"])(?P<url>.*?)(?P=q)''') _player_url_schema = validate.Schema( validate.transform(_player_re.search), validate.any(None, validate.all(validate.get("url"), validate.text))) _livestream_schema = validate.Schema( validate.xml_findall(".//assets"), validate.filter(lambda x: x.attrib.get("type") != "subtitles"), validate.get(0), validate.xml_findall(".//asset"), [ validate.union({ "url": validate.xml_findtext("./fileName"), "bitrate": validate.xml_findtext("./bitrateVideo") }) ]) @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None def _get_streams(self): data_url = http.get(self.url, schema=self._player_url_schema) if data_url: res = http.get(urljoin(self.url, data_url)) stream_info = http.xml(res, schema=self._livestream_schema) for stream in stream_info: url = stream["url"] try: if ".m3u8" in url: for s in HLSStream.parse_variant_playlist( self.session, url, name_key="bitrate").items(): yield s elif ".f4m" in url: for s in HDSStream.parse_manifest( self.session, url, pvswf=self.swf_url, is_akamai=True).items(): yield s elif ".mp4" in url: yield "{0}k".format(stream["bitrate"]), HTTPStream( self.session, url) except IOError as err: self.logger.warning("Error parsing stream: {0}", err)
class TVRPlus(Plugin): url_re = re.compile(r"https?://(?:www\.)tvrplus.ro/live-") hls_file_re = re.compile( r"""src:\s?(?P<q>["'])(?P<url>http.+?m3u8.*?)(?P=q)""") stream_schema = validate.Schema( validate.all(validate.transform(hls_file_re.search), validate.any(None, validate.get("url"))), ) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _get_streams(self): stream_url = self.stream_schema.validate(http.get(self.url).text) if stream_url: headers = {"Referer": self.url} return HLSStream.parse_variant_playlist(self.session, stream_url, headers=headers)
class RTBF(Plugin): """Livecli Plugin for RTBF""" _url_re = re.compile(r"""https?://(?:www\.)rtbf\.be/auvio/""") api_live_url = "https://www.rtbf.be/embed/d/ajax/refresh?id={0}" _live_schema = validate.Schema( { "data": validate.any( None, [], { "streamUrlHls": validate.text, }, ), }, validate.get("data"), ) @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) def _get_live_stream(self, live_id): res = http.get(self.api_live_url.format(live_id)) live_data = http.json(res, schema=self._live_schema) if not live_data: self.logger.debug("No data found.") return for s in HLSStream.parse_variant_playlist( self.session, live_data["streamUrlHls"]).items(): yield s def _get_streams(self): params = dict(parse_qsl(urlparse(self.url).query)) live_id = params.get("lid") if live_id: return self._get_live_stream(live_id)
class INE(Plugin): url_re = re.compile(r"""https://streaming.ine.com/play\#?/ ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/? (.*?)""", re.VERBOSE) play_url = "https://streaming.ine.com/play/{vid}/watch" js_re = re.compile(r'''script type="text/javascript" src="(https://content.jwplatform.com/players/.*?)"''') jwplayer_re = re.compile(r'''jwConfig\s*=\s*(\{.*\});''', re.DOTALL) setup_schema = validate.Schema( validate.transform(jwplayer_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(json.loads), {"playlist": [ {"sources": [{"file": validate.text, "type": validate.text}]} ]} ) ) ) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _get_streams(self): vid = self.url_re.match(self.url).group(1) self.logger.debug("Found video ID: {0}", vid) page = http.get(self.play_url.format(vid=vid)) js_url_m = self.js_re.search(page.text) if js_url_m: js_url = js_url_m.group(1) self.logger.debug("Loading player JS: {0}", js_url) res = http.get(js_url) data = self.setup_schema.validate(res.text) for source in data["playlist"][0]["sources"]: if source["type"] == "hls": return HLSStream.parse_variant_playlist(self.session, "https:" + source["file"])
class RaiPlay(Plugin): url_re = re.compile(r"https?://(?:www\.)?raiplay\.it/dirette/(\w+)/?") stream_re = re.compile(r"data-video-url.*?=.*?\"([^\"]+)\"") stream_schema = validate.Schema( validate.all( validate.transform(stream_re.search), validate.any( None, validate.all(validate.get(1), validate.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(1) self.logger.debug("Found channel: {0}", channel) stream_url = http.get(self.url, schema=self.stream_schema) if stream_url: return HLSStream.parse_variant_playlist(self.session, stream_url)
class CinerGroup(Plugin): """ Support for the live stream on www.showtv.com.tr """ url_re = re.compile( r"""https?://(?:www.)? (?: showtv.com.tr/canli-yayin(/showtv)?| haberturk.com/canliyayin| showmax.com.tr/canliyayin| showturk.com.tr/canli-yayin/showturk| bloomberght.com/tv| haberturk.tv/canliyayin )/?""", re.VERBOSE) stream_re = re.compile( r"""div .*? data-ht=(?P<quote>["'])(?P<data>.*?)(?P=quote)""", re.DOTALL) stream_data_schema = validate.Schema( validate.transform(stream_re.search), validate.any( None, validate.all( validate.get("data"), validate.transform(unquote), validate.transform(lambda x: x.replace(""", '"')), validate.transform(json.loads), {"ht_stream_m3u8": validate.url()}, validate.get("ht_stream_m3u8")))) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _get_streams(self): res = http.get(self.url) stream_url = self.stream_data_schema.validate(res.text) if stream_url: return HLSStream.parse_variant_playlist(self.session, stream_url)
class FilmOnAPI(object): channel_url = "http://www.filmon.com/api-v2/channel/{0}?protocol=hls" vod_url = "http://www.filmon.com/vod/info/{0}" stream_schema = { "quality": validate.text, "url": validate.url(), "watch-timeout": int } api_schema = validate.Schema( { "data": { "streams": validate.any({validate.text: stream_schema}, [stream_schema]) } }, validate.get("data")) def channel(self, channel): res = http.get(self.channel_url.format(channel)) return http.json(res, schema=self.api_schema) def vod(self, vod_id): res = http.get(self.vod_url.format(vod_id)) return http.json(res, schema=self.api_schema)
__livecli_docs__ = { "domains": [ "camsoda.com", ], "geo_blocked": [], "notes": "", "live": True, "vod": False, "last_update": "2017-11-18", } _url_re = re.compile(r"http(s)?://(www\.)?camsoda\.com/(?P<username>[^\"\']+)") _api_user_schema = validate.Schema({ "status": validate.any(int, validate.text), validate.optional("user"): { "online": validate.any(int, validate.text), "chatstatus": validate.text, } }) _api_video_schema = validate.Schema({ "token": validate.text, "app": validate.text, "edge_servers": [validate.text], "stream_name": validate.text }) class Camsoda(Plugin):
"broken": True, } BALANCER_URL = "http://www.mips.tv:1935/loadbalancer" PLAYER_URL = "http://mips.tv/embedplayer/{0}/1/500/400" SWF_URL = "http://mips.tv/content/scripts/eplayer.swf" _url_re = re.compile(r"http(s)?://(\w+.)?mips.tv/(?P<channel>[^/&?]+)") _flashvars_re = re.compile(r"'FlashVars', '([^']+)'") _rtmp_re = re.compile(r"redirect=(.+)") _schema = validate.Schema( validate.transform(_flashvars_re.search), validate.any( None, validate.all(validate.get(1), validate.transform(parse_query), { "id": validate.transform(int), validate.optional("s"): validate.text }))) _rtmp_schema = validate.Schema( validate.transform(_rtmp_re.search), validate.get(1), ) class Mips(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) @Plugin.broken() def _get_streams(self):
{ "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")) _quality_options_schema = validate.Schema( {
LAPI_URL = "http://g2.live.360.cn/liveplay?stype=flv&channel={}&bid=huajiao&sn={}&sid={}&_rate=xd&ts={}&r={}&_ostype=flash&_delay=0&_sign=null&_ver=13" _url_re = re.compile( r""" http(s)?://(www\.)?huajiao.com /l/(?P<channel>[^/]+) """, re.VERBOSE) _feed_json_re = re.compile(r'^\s*var\s*feed\s*=\s*(?P<feed>{.*})\s*;', re.MULTILINE) _feed_json_schema = validate.Schema( validate.all( validate.transform(_feed_json_re.search), validate.any( None, validate.all(validate.get('feed'), validate.transform(json.loads))))) class Huajiao(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url) def _get_streams(self): match = _url_re.match(self.url) channel = match.group("channel") http.headers.update({"User-Agent": USER_AGENT}) http.verify = False
_api_schema = validate.Schema({ "error": bool, validate.optional("code"): validate.text, validate.optional("message"): validate.text, validate.optional("data"): object, }) _media_schema = validate.Schema( { "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 }]) }) }, 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
class CanalPlus(Plugin): # NOTE : no live url for the moment API_URL = 'https://secure-service.canal-plus.com/video/rest/getVideos/cplus/{0}?format=json' HDCORE_VERSION = '3.1.0' # Secret parameter needed to download HTTP videos on canalplus.fr SECRET = 'pqzerjlsmdkjfoiuerhsdlfknaes' _url_re = re.compile(r''' (https|http):// ( www.mycanal.fr/(.*)/(.*)/p/(?P<video_id>[0-9]+) | www\.cnews\.fr/.+ ) ''', re.VERBOSE) _video_id_re = re.compile(r'(\bdata-video="|<meta property="og:video" content=".+?&videoId=)(?P<video_id>[0-9]+)"') _mp4_bitrate_re = re.compile(r'.*_(?P<bitrate>[0-9]+k)\.mp4') _api_schema = validate.Schema({ 'ID_DM': validate.text, 'TYPE': validate.text, 'MEDIA': validate.Schema({ 'VIDEOS': validate.Schema({ validate.text: validate.any( validate.url(), '' ) }) }) }) _user_agent = useragents.CHROME @classmethod def can_handle_url(cls, url): return CanalPlus._url_re.match(url) def _get_streams(self): # Get video ID and channel from URL match = self._url_re.match(self.url) video_id = match.group('video_id') if video_id is None: # 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.API_URL.format(video_id)) videos = http.json(res, schema=self._api_schema) parsed = [] headers = {'User-Agent': self._user_agent} # Some videos may be also available on Dailymotion (especially on CNews) if videos['ID_DM'] != '': for stream in self.session.streams('https://www.dailymotion.com/video/' + videos['ID_DM']).items(): yield stream for quality, video_url in list(videos['MEDIA']['VIDEOS'].items()): # Ignore empty URLs if video_url == '': continue # Ignore duplicate video URLs if video_url in parsed: continue parsed.append(video_url) try: # HDS streams don't seem to work for live videos if '.f4m' in video_url and 'LIVE' not in videos['TYPE']: for stream in HDSStream.parse_manifest(self.session, video_url, params={'hdcore': self.HDCORE_VERSION}, headers=headers).items(): yield stream elif '.m3u8' in video_url: for stream in HLSStream.parse_variant_playlist(self.session, video_url, headers=headers).items(): yield stream elif '.mp4' in video_url: # Get bitrate from video filename match = self._mp4_bitrate_re.match(video_url) if match is not None: bitrate = match.group('bitrate') else: bitrate = quality yield bitrate, HTTPStream(self.session, video_url, params={'secret': self.SECRET}, headers=headers) except IOError as err: if '403 Client Error' in str(err): self.logger.error('Failed to access stream, may be due to geo-restriction')
class WebTV(Plugin): _url_re = re.compile(r"http(?:s)?://(\w+)\.web.tv/?") _sources_re = re.compile(r'"sources": (\[.*?\]),', re.DOTALL) _sources_schema = validate.Schema([{ u"src": validate.any( validate.contains("m3u8"), validate.all( validate.text, validate.transform(lambda x: WebTV.decrypt_stream_url(x)), validate.contains("m3u8"))), u"type": validate.text, u"label": validate.text }]) @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None @staticmethod def decrypt_stream_url(encoded_url): data = base64.b64decode(encoded_url) cipher_text = binascii.unhexlify(data[96:]) decryptor = crypto_AES.new(binascii.unhexlify(data[32:96]), crypto_AES.MODE_CBC, binascii.unhexlify(data[:32])) return unpad_pkcs5(decryptor.decrypt(cipher_text)).decode("utf8") def _get_streams(self): """ Find the streams for web.tv :return: """ headers = {} res = http.get(self.url, headers=headers) headers["Referer"] = self.url sources = self._sources_re.findall(res.text) if len(sources): sdata = parse_json(sources[0], schema=self._sources_schema) for source in sdata: self.logger.debug("Found stream of type: {}", source[u'type']) if source[u'type'] == u"application/vnd.apple.mpegurl": url = update_scheme(self.url, source[u"src"]) try: # try to parse the stream as a variant playlist variant = HLSStream.parse_variant_playlist( self.session, url, headers=headers) if variant: for q, s in variant.items(): yield q, s else: # and if that fails, try it as a plain HLS stream yield 'live', HLSStream(self.session, url, headers=headers) except IOError: self.logger.warning( "Could not open the stream, perhaps the channel is offline" )
"geo_blocked": [ "ES", ], "notes": "", "live": True, "vod": True, "last_update": "2015-03-13", } STREAM_INFO_URL = "http://dinamics.ccma.cat/pvideo/media.jsp?media=video&version=0s&idint={ident}&profile=pc&desplacament=0" _url_re = re.compile(r"http://(?:www.)?ccma.cat/tv3/directe/(.+?)/") _media_schema = validate.Schema({ "geo": validate.text, "url": validate.url(scheme=validate.any("http")) }) _channel_schema = validate.Schema( {"media": validate.any([_media_schema], _media_schema)}) class TV3Cat(Plugin): @classmethod def can_handle_url(self, url): match = _url_re.match(url) return match def _get_streams(self): match = _url_re.match(self.url) if match:
_live_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"))) }) ] ) _movie_schema = validate.Schema( validate.transform(_movie_data_re.search), validate.any( None, validate.all( validate.get(1), validate.transform(parse_json), validate.get("contentUrl") ) ) ) class OPENRECtv(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) is not None @classmethod def stream_weight(cls, stream): if stream == "source": return 1080 + 1, "openrectv"
https?://(?:\w+\.)?arte\.tv/(?:guide/)? (?P<language>[a-z]{2})/ (?: (?:videos/)?(?P<video_id>(?!RC\-|videos)[^/]+?)/.+ | # VOD (?:direct|live) # Live TV ) """, re.VERBOSE) _video_schema = validate.Schema({ "videoJsonPlayer": { "VSR": validate.any( [], { validate.text: { "height": int, "mediaType": validate.text, "url": validate.text, "versionShortLibelle": validate.text }, }, ) } }) class ArteTV(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) def _create_stream(self, stream, language):
_room_key_re = re.compile(r'"room_key"\s*:\s*"(.+?)"') _hostid_re = re.compile(r'\\"hostid\\"\s*:\s*\\"(.+?)\\"') _param_re = re.compile(r'"param"\s*:\s*"(.+?)"\s*,\s*"time"') _time_re = re.compile(r'"time"\s*:\s*(\d+)') _sign_re = re.compile(r'"sign"\s*:\s*"(.+?)"') _sd_re = re.compile(r'"SD"\s*:\s*"(\d+)"') _hd_re = re.compile(r'"HD"\s*:\s*"(\d+)"') _od_re = re.compile(r'"OD"\s*:\s*"(\d+)"') _room_schema = validate.Schema( { "data": validate.any( validate.text, dict, { "videoinfo": validate.any(validate.text, { "plflag_list": validate.text, "plflag": validate.text }) }) }, validate.get("data")) class Pandatv(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) def _get_streams(self): match = _url_re.match(self.url) channel = match.group("channel")
def test_any(self): assert validate(any(int, dict), 5) == 5 assert validate(any(int, dict), {}) == {} assert validate(any(int), 4) == 4
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) videoelement_re = re.compile( r'''<div[^>]*?id=(?P<quote>["'])video-element(?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") # find <div id="video-element" videoelement_m = self.videoelement_re.search(res.text) if videoelement_m: # extract the content id from the player control data videoelement_div = videoelement_m.group(0) content_id_m = self.data_id_re.search(videoelement_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")
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") ) class Rtvs(Plugin):
API_SECRET = "95acd7f6cc3392f3" SHOW_STATUS_ONLINE = 1 SHOW_STATUS_OFFLINE = 2 STREAM_WEIGHTS = { "source": 1080 } _url_re = re.compile(r""" http(s)?://live.bilibili.com /(?P<channel>[^/]+) """, re.VERBOSE) _room_id_schema = validate.Schema( { "data": validate.any(None, { "room_id": int }) }, 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"