diff --git a/Src/API/realtime.py b/Src/API/realtime.py new file mode 100644 index 0000000..bb395be --- /dev/null +++ b/Src/API/realtime.py @@ -0,0 +1,292 @@ +from Src.Utilities.info import get_info_tmdb, is_movie, get_info_imdb +import Src.Utilities.config as config +from fake_headers import Headers +from Src.Utilities.loadenv import load_env +from urllib.parse import quote +import logging +from Src.Utilities.config import setup_logging +level = config.LEVEL +import random +Icon = config.Icon +Name = config.Name +logger = setup_logging(level) +env_vars = load_env() +random_headers = Headers() +RT_PROXY = config.RT_PROXY +proxies = {} +if RT_PROXY == "1": + PROXY_CREDENTIALS = env_vars.get('PROXY_CREDENTIALS') + proxy_list = json.loads(PROXY_CREDENTIALS) + proxy = random.choice(proxy_list) + if proxy == "": + proxies = {} + else: + proxies = { + "http": proxy, + "https": proxy + } +RT_ForwardProxy = config.RT_ForwardProxy +if RT_ForwardProxy == "1": + ForwardProxy = env_vars.get('ForwardProxy') +else: + ForwardProxy = "" + + +endpoints = { + 'it' : 'https://public.aurora.enhanced.live', + 'dplay' : 'https://eu1-prod.disco-api.com' +} +async def search(showname,date,client): + showname = showname.split('-')[0] + showname = quote(showname) + response = await client.get(ForwardProxy + f'https://public.aurora.enhanced.live/site/search/page/?include=default&filter[environment]=realtime&v=2&q={showname}&page[number]=1&page[size]=20', proxies = proxies) + data = response.json() + for item in data['data']: + return item['slug'] + + + +async def program_info(slug,season,episode,client): + headers = { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0', + 'Accept': '*/*', + 'Accept-Language': 'en-US,en;q=0.5', + 'Referer': 'https://realtime.it/', + 'Origin': 'https://realtime.it', + 'Sec-GPC': '1', + 'Connection': 'keep-alive', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'cross-site', + 'DNT': '1', + 'Priority': 'u=4', + } + link = f'https://public.aurora.enhanced.live/site/page/{slug}/?include=default&filter[environment]=realtime&v=2&parent_slug=programmi-real-time' + + response = await client.get(ForwardProxy + link,headers = headers, proxies = proxies) + data = response.json() + for item in reversed(data['blocks'][1]['items']): + if season == item['seasonNumber'] and episode == item['episodeNumber']: + if 'aurora' in data['blocks'][0]['item']['poster']['src']: + platform = 'IT' + elif 'eu1-prod' in data['blocks'][0]['item']['poster']['src']: + platform = 'DPLAY' + x_realm_it = '' + x_realm_dplay = '' + if 'X-REALM-IT' in data['userMeta']['realm']: + x_realm_it = data['userMeta']['realm']['X-REALM-IT'] + if 'X-REALM-DPLAY' in data['userMeta']['realm']: + x_realm_dplay = data['userMeta']['realm']['X-REALM-DPLAY'] + return item['id'],x_realm_it,x_realm_dplay, platform + return None,None,None,None + +async def get_token(client): + headers = { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0', + 'Accept': '*/*', + 'Accept-Language': 'en-US,en;q=0.5', + 'Referer': 'https://realtime.it/', + 'Content-Type': 'application/json', + 'X-disco-client': 'WEB:UNKNOWN:wbdatv:2.1.9', + 'X-disco-params': 'realm=dplay', + 'X-Device-Info': 'STONEJS/1 (Unknown/Unknown; Linux/undefined; Unknown)', + 'Origin': 'https://realtime.it', + 'Sec-GPC': '1', + 'Connection': 'keep-alive', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'cross-site', + 'DNT': '1', + 'Priority': 'u=4', + } + response = await client.get(ForwardProxy + 'https://public.aurora.enhanced.live/site/page/casa-a-prima-vista/?include=default&filter[environment]=realtime&v=2&parent_slug=programmi-real-time', headers = headers, proxies = proxies) + data = response.json() + x_realm_it = data['userMeta']['realm']['X-REALM-IT'] + x_realm_dplay = data['userMeta']['realm']['X-REALM-DPLAY'] + return x_realm_it,x_realm_dplay + + + +async def get_url(id, endpoint, x_realm_it,x_realm_dplay,streams,client): + if 'IT' in endpoint: + base_url = endpoints['it'] + token = x_realm_it + elif 'DPLAY' in endpoint: + base_url = endpoints['dplay'] + token = x_realm_dplay + headers = { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0', + 'Accept': '*/*', + 'Accept-Language': 'en-US,en;q=0.5', + 'Referer': 'https://realtime.it/', + 'Content-Type': 'application/json', + 'X-disco-client': 'WEB:UNKNOWN:wbdatv:2.1.9', + 'X-disco-params': 'realm=dplay', + 'X-Device-Info': 'STONEJS/1 (Unknown/Unknown; Linux/undefined; Unknown)', + 'Authorization': f'Bearer {token}', + 'Origin': 'https://realtime.it', + 'Sec-GPC': '1', + 'Connection': 'keep-alive', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'cross-site', + 'DNT': '1', + 'Priority': 'u=4', + } + + + json_data = { + 'deviceInfo': { + 'adBlocker': False, + 'drmSupported': True, + 'hdrCapabilities': [ + 'SDR', + ], + 'hwDecodingCapabilities': [], + 'soundCapabilities': [ + 'STEREO', + ], + }, + 'wisteriaProperties': {}, + 'videoId': id, + } + response = await client.post(ForwardProxy + f'{base_url}/playback/v3/videoPlaybackInfo', headers=headers, json=json_data, proxies = proxies) + data = response.json() + + for item in data['data']['attributes']['streaming']: + if item['type'] == 'hls': + m3u8 = item['url'] + streams['streams'].append({'name': f"{Name}",'title': f'{Icon}▶️ Realtime\n HLS', 'url': m3u8, 'behaviorHints': {'bingeGroup': f'realtimehls'}}) + + elif item['type'] == 'dash': + mpd = item['url'] + streams['streams'].append({'name': f"{Name}",'title': f'{Icon}▶️ Realtime\n MPD', 'url': mpd, 'behaviorHints': {'bingeGroup': f'realtimempd'}}) + + return streams + + + + +async def search_catalog(query,catalog,client): + showname = quote(query) + response = await client.get(f'https://public.aurora.enhanced.live/site/search/page/?include=default&filter[environment]=realtime&v=2&q={showname}&page[number]=1&page[size]=20') + data = response.json() + for item in data['data']: + title = item['title'] + description = item['subtitle'] + date = item['datePublished'].split('-')[0] + id = item['slug'] + image = item['image']['url'] + typeof = item['type'] + catalog['metas'].append({'id': f'realtime{typeof}:'+id, 'type': "series",'name': f'{title}', 'description': description, 'releaseInfo': date, 'background': image, 'poster': image }) + return catalog + +async def meta_catalog(id,catalog,client): + headers = { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0', + 'Accept': '*/*', + 'Accept-Language': 'en-US,en;q=0.5', + 'Referer': 'https://realtime.it/', + 'Origin': 'https://realtime.it', + 'Sec-GPC': '1', + 'Connection': 'keep-alive', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'cross-site', + 'DNT': '1', + 'Priority': 'u=4', + } + parts = id.split(':') + slug = parts[1] + parts2 = parts[0].split('realtime') + typeof= parts2[1] + if typeof == 'showpage': + link = f'https://public.aurora.enhanced.live/site/page/{slug}/?include=default&filter[environment]=realtime&v=2&parent_slug=programmi-real-time' + elif typeof == 'article': + link = f'https://public.aurora.enhanced.live/site/page/{slug}/?include=default&filter[environment]=realtime&v=2' + + response = await client.get(ForwardProxy + link,headers = headers, proxies = proxies) + data = response.json() + title = data['title'] + subtitle = data['subtitle'] + + if data['type'] == 'articlepage': + if data['blocks'][1]['sonicOverrideEnabled'] == True: + platform = 'IT' + else: + platform = 'DPLAY' + elif data['type'] == 'showpage': + + if 'aurora' in data['blocks'][0]['item']['poster']['src']: + platform = 'IT' + elif 'eu1-prod' in data['blocks'][0]['item']['poster']['src']: + platform = 'DPLAY' + if typeof == 'showpage': + for item in reversed(data['blocks'][1]['items']): + id = item['id'] + description = item['description'] + episode = item['episodeNumber'] + season = item['seasonNumber'] + poster = item['poster'] + date = item['publishStart'] + catalog['meta']['videos'].append({'title': f'S{season}'+ f'E{episode}','season': season, 'episode': episode,'firstAired':date,'overview': description, 'thumbnail': poster['src'], 'id': f'realtime{platform}:id:'+id}) + elif typeof == 'article': + item = data['blocks'][1]['item'] + id = item['id'] + description = item['description'] + episode = item['episodeNumber'] + season = item['seasonNumber'] + poster = item['poster'] + date = item['publishStart'] + catalog['meta']['videos'].append({'title': f'{title}','season': season, 'episode': episode,'firstAired':date,'overview': description, 'thumbnail': poster['src'], 'id': f'realtime{platform}:id:'+id}) + + catalog['meta']['name'] = title + catalog['meta']['description'] = subtitle + catalog['meta']['releaseInfo'] = '-' + data['datePublished'].split('-')[0] + catalog['meta']['background'] = data['metaMedia'][0]['media']['url'] + return catalog +async def realtime(streams,id,client): + try: + if 'realtime' in id: + parts = id.split('id:') + id = parts[1] + endpoint = parts[0] + x_realm_it,x_realm_dplay = await get_token(client) + streams = await get_url(id, endpoint, x_realm_it,x_realm_dplay,streams,client) + else: + general = await is_movie(id) + ismovie = general[0] + clean_id = general[1] + type = "Realtime" + if ismovie == 0 : + season = int(general[2]) + episode = int(general[3]) + elif ismovie == 1: + season = None + episode = None + if "tmdb" in id: + showname,date = get_info_tmdb(clean_id,ismovie,type) + else: + showname,date = await get_info_imdb(clean_id,ismovie,type,client) + logger.info(f'RT {showname}') + slug = await search(showname,date,client) + id,x_realm_it,x_realm_dplay,platform = await program_info(slug,season,episode,client) + streams = await get_url(id,platform,x_realm_it,x_realm_dplay,streams,client) + return streams + except Exception as e: + logger.warning(f'RT {e}') + return streams + + + + +async def test_realtime(): + from curl_cffi.requests import AsyncSession + async with AsyncSession() as client: + results = await realtime({'streams': []},"tt5684368:13:10",client) + print(results) + + +if __name__ == "__main__": + import asyncio + asyncio.run(test_realtime()) \ No newline at end of file diff --git a/Src/Utilities/config.py b/Src/Utilities/config.py index 73a2e16..4255a78 100644 --- a/Src/Utilities/config.py +++ b/Src/Utilities/config.py @@ -17,6 +17,7 @@ GHD_DOMAIN = SITE['GuardaHD']['url'] ES_DOMAIN = SITE['Eurostreaming']['url'] GF_DOMAIN = SITE['Guardaflix']['url'] GO_DOMAIN = SITE['Guardoserie']['url'] +RT_DOMAIN = SITE['Realtime']['url'] SC = SITE['StreamingCommunity']['enabled'] SW = SITE['StreamingWatch']['enabled'] AW = SITE['AnimeWorld']['enabled'] @@ -26,6 +27,7 @@ GHD = SITE['GuardaHD']['enabled'] ES= SITE['Eurostreaming']['enabled'] GF = SITE['Guardaflix']['enabled'] GO = SITE['Guardoserie']['enabled'] +RT = SITE['Realtime']['enabled'] SC_ForwardProxy = SITE['StreamingCommunity']["SC_ForwardProxy"] GS_ForwardProxy = SITE['Guardaserie']["GS_ForwardProxy"] GH_ForwardProxy = SITE['GuardaHD']["GH_ForwardProxy"] @@ -37,6 +39,7 @@ ES_ForwardProxy = SITE['Eurostreaming']['ES_ForwardProxy'] GF_ForwardProxy = SITE['Guardaflix']['GF_ForwardProxy'] GO_ForwardProxy = SITE['Guardoserie']['GO_ForwardProxy'] SW_ForwardProxy = SITE['StreamingWatch']['SW_ForwardProxy'] +RT_ForwardProxy = SITE['Realtime']['RT_ForwardProxy'] GS_PROXY = SITE['Guardaserie']["GS_PROXY"] GH_PROXY = SITE['GuardaHD']["GH_PROXY"] CB_PROXY = SITE['CB01']["CB_PROXY"] @@ -48,6 +51,7 @@ ES_PROXY = SITE['Eurostreaming']['ES_PROXY'] GF_PROXY = SITE['Guardaflix']['GF_PROXY'] GO_PROXY = SITE['Guardoserie']['GO_PROXY'] SW_PROXY = SITE['StreamingWatch']['SW_PROXY'] +RT_PROXY = SITE['Realtime']['RT_PROXY'] #General GENERAL = config['General'] dotenv = GENERAL["load_env"] diff --git a/Src/Utilities/dictionaries.py b/Src/Utilities/dictionaries.py index ce37b2a..118b9f9 100644 --- a/Src/Utilities/dictionaries.py +++ b/Src/Utilities/dictionaries.py @@ -413,6 +413,11 @@ STREAM = { ] } + +REALTIME = {} + + + extra_sources = { "rai-1": ["https://ilglobotv-live.akamaized.net/channels/RAI1/Live.m3u8"], "rai-2": ["https://ilglobotv-live.akamaized.net/channels/RAI2/Live.m3u8"], @@ -452,5 +457,6 @@ provider_map = { "OST": "ONLINESERIETV", "ES": "EUROSTREAMING", "GO": "GUARDOSERIE", + "RT": "REALTIME", "GF": "GUARDAFLIX" } \ No newline at end of file diff --git a/Src/Utilities/info.py b/Src/Utilities/info.py index 25ddb85..5f4cb44 100644 --- a/Src/Utilities/info.py +++ b/Src/Utilities/info.py @@ -61,6 +61,9 @@ def get_info_tmdb(tmbda,ismovie,type): date = show.first_air_date date = date.split("-")[0] return showname,date + elif type == 'Realtime': + date = show.first_air_date + return showname,date elif ismovie == 1: movie = Movie() @@ -109,7 +112,9 @@ def get_info_tmdb(tmbda,ismovie,type): date = show.release_date date = date.split("-")[0] return showname,date - + elif type == 'Realtime': + date = show.release_date + return showname,date async def get_info_imdb(imdb_id, ismovie, type,client): resp = await client.get(f'https://api.themoviedb.org/3/find/{imdb_id}?api_key={TMDB_KEY}&language=it&external_source=imdb_id') data = resp.json() @@ -157,6 +162,9 @@ async def get_info_imdb(imdb_id, ismovie, type,client): date = data['tv_results'][0]['first_air_date'] date = date.split("-")[0] return showname,date + elif type == 'Realtime': + return showname,data['tv_results'][0]['first_air_date'] + elif ismovie == 1: showname= data['movie_results'][0]['title'] if type == "Filmpertutti": @@ -195,6 +203,10 @@ async def get_info_imdb(imdb_id, ismovie, type,client): date = data['movie_results'][0]['release_date'] date = date.split("-")[0] return showname,date + elif type == 'Realtime': + date = data['movie_results'][0]['release_date'] + return showname,date + async def get_info_kitsu(kitsu_id,client): diff --git a/config.json b/config.json index cf9e7d3..d326309 100644 --- a/config.json +++ b/config.json @@ -41,7 +41,7 @@ "ES_PROXY": "0" }, "Guardaflix":{ - "url": "https://guardaflix.live", + "url": "https://guardaflix.center", "enabled": "1", "GF_ForwardProxy": "0", "GF_PROXY": "0" @@ -57,6 +57,12 @@ "enabled":"1", "AW_ForwardProxy":"0", "AW_PROXY":"0" + }, + "Realtime": { + "url": "https://realtime.it", + "enabled": "1", + "RT_ForwardProxy": "0", + "RT_PROXY": "0" } }, "General":{ diff --git a/run.py b/run.py index 00ff352..4cf6c77 100644 --- a/run.py +++ b/run.py @@ -16,7 +16,9 @@ from Src.API.animeworld import animeworld from Src.API.guardaflix import guardaflix from Src.API.guardoserie import guardoserie from Src.API.eurostreaming import eurostreaming - +from Src.API.realtime import search_catalog as realtime +from Src.API.realtime import meta_catalog as meta_catalog_realtime +from Src.API.realtime import realtime as streams_realtime from Src.Utilities.dictionaries import STREAM,extra_sources,provider_map from Src.API.epg import tivu, tivu_get,epg_guide,convert_bho_1,convert_bho_2,convert_bho_3 @@ -53,6 +55,7 @@ GHD = config.GHD ES = config.ES GF = config.GF GO = config.GO +RT = config.RT HOST = config.HOST PORT = int(config.PORT) if env_vars.get('PORT_ENV'): @@ -84,6 +87,17 @@ MANIFEST = { "options": ["Rai", "Mediaset", "Sky", "Euronews", "La7", "Warner Bros", "FIT", "Sportitalia","RSI","DAZN", "Rakuten", "Pluto", "A+E", "Paramount", "Chill"] } ] + }, + { + 'id': 'realtime', + 'type': "series", + "name": "realtime", + 'extra' :[ + { + "name": "search", + "isRequired": True + } + ] } ], "resources": ["stream", "catalog", "meta"], @@ -113,13 +127,21 @@ def config(request: Request): def addon_manifest(config: str): manifest_copy = MANIFEST.copy() config = base64.b64decode(config).decode('utf-8') - if "LIVETV" in config: - return respond_with(manifest_copy) - elif "LIVETV" not in config: - manifest_copy["catalogs"] = [] + if "LIVETV" not in config: if "catalog" in manifest_copy["resources"]: - manifest_copy["resources"].remove("catalog") - return respond_with(manifest_copy) + for item in manifest_copy['catalogs']: + if item['id'] == 'tv_channels': + manifest_copy['catalogs'].remove(item) + if not any(manifest_copy['catalogs']): + manifest_copy["resources"].remove('catalog') + if 'RT' not in config or RT == '0': + if "catalog" in manifest_copy["resources"]: + for item in manifest_copy['catalogs']: + if item['id'] == 'realtime': + manifest_copy['catalogs'].remove(item) + if not any(manifest_copy['catalogs']): + manifest_copy["resources"].remove('catalog') + return respond_with(manifest_copy) @app.get('/manifest.json') def manifest(): @@ -164,6 +186,14 @@ async def first_catalog(type: str, id: str, genre: str = None): catalogs = await addon_catalog(type, id,genre) return respond_with(catalogs) +@app.get('/{config:path}/catalog/{type}/{id}/search={query}.json') +async def realtime_catalog(type:str,id:str,query: str = None): + if type != 'series': + raise HTTPException(status_code=404) + catalogs = {"query": query,'cacheMaxAge': 86400,"metas": []} + async with AsyncSession(proxies = proxies) as client: + catalogs = await realtime(query, catalogs,client) + return respond_with(catalogs) @app.get('/{config:path}/meta/tv/{id}.json') @limiter.limit("20/second") async def addon_meta(request: Request,id: str): @@ -199,7 +229,14 @@ async def addon_meta(request: Request,id: str): meta['meta']['url'] = channel['url'] # Using the stream URL as a website link return respond_with(meta) - +@app.get('/{config:path}/meta/series/{id}.json') +async def addon_meta(request: Request,id: str): + meta = {'meta': {'videos':[], 'status': 'Continuing', 'type': 'series', 'id': id}} + async with AsyncSession(proxies = proxies) as client: + if 'realtime' not in id: + raise HTTPException(status_code=404) + meta = await meta_catalog_realtime(id,meta,client) + return respond_with(meta) @app.get('/{config:path}/stream/{type}/{id}.json') @limiter.limit("5/second") async def addon_stream(request: Request,config, type, id,): @@ -249,6 +286,9 @@ async def addon_stream(request: Request,config, type, id,): if not streams['streams']: raise HTTPException(status_code=404) return respond_with(streams) + elif "realtime" in id and RT == '1': + streams = await streams_realtime(streams,id,client) + return respond_with(streams) elif "tt" in id or "tmdb" in id or "kitsu" in id: logger.info(f"Handling movie or series: {id}") if "kitsu" in id: @@ -275,6 +315,9 @@ async def addon_stream(request: Request,config, type, id,): streams = await guardaflix(streams,id,client,MFP,MFP_CREDENTIALS) if provider_maps['GUARDOSERIE'] == "1" and GO == "1": streams = await guardoserie(streams,id,client,MFP,MFP_CREDENTIALS) + if provider_maps['REALTIME'] == '1' and RT == '1': + streams = await streams_realtime(streams,id,client) + return respond_with(streams) if not streams['streams']: raise HTTPException(status_code=404) @@ -283,4 +326,4 @@ async def addon_stream(request: Request,config, type, id,): if __name__ == '__main__': import uvicorn - uvicorn.run("run:app", host=HOST, port=PORT, log_level=level) + uvicorn.run("run:app", host=HOST, port=PORT, log_level=level) \ No newline at end of file diff --git a/static/configure.py b/static/configure.py index fd58491..1bc9a96 100644 --- a/static/configure.py +++ b/static/configure.py @@ -235,6 +235,11 @@ CONFIGURE = """ Eurostreaming 🕵️‍♂️ +
+ +
+
+ +