source: ipk/source.arm/playersnp_vavoo/_path_/python/vavoo.py@ 48302

Last change on this file since 48302 was 47966, checked in by aafsvn, 3 months ago

add vav as plugin

File size: 9.2 KB
Line 
1###############################################################
2PORT = 4323
3ADDON_SIG_TTL = 600 # 10 Minuten
4###############################################################
5
6import json
7import gzip
8import requests
9import os
10from io import BytesIO
11import uuid
12from flask import Flask, request, Response, abort
13import time
14import socket
15import threading
16
17# ---------------- LÄNDER ----------------
18
19REGIONS = [
20 {"language": "nl", "region": "BE"},
21]
22
23GEOIP_URL = "https://www.vavoo.tv/geoip"
24PING_URL = "https://www.vavoo.tv/api/app/ping"
25CATALOG_URL = "https://vavoo.to/mediahubmx-catalog.json"
26RESOLVE_URL = "https://vavoo.to/mediahubmx-resolve.json"
27
28LANGUAGE = "de"
29REGION = "DE"
30
31HEADERS = {
32 "accept": "*/*",
33 "user-agent": "electron-fetch/1.0 electron (+https://github.com/arantes555/electron-fetch)",
34 "Accept-Language": LANGUAGE,
35 "Accept-Encoding": "gzip, deflate",
36 "Connection": "close",
37}
38
39def decode_response(resp):
40 if resp.content[:2] == b'\x1f\x8b':
41 return json.loads(gzip.decompress(resp.content))
42 return resp.json()
43
44session = requests.Session()
45session.headers.update(HEADERS)
46
47# ---------------- GEO + INITIAL PING ----------------
48
49r_geo = session.get(GEOIP_URL)
50r_geo.raise_for_status()
51geo_data = decode_response(r_geo)
52
53unique_id = str(uuid.uuid4())
54current_timestamp = int(time.time() * 1000)
55
56initial_payload = {
57 "reason": "app-focus",
58 "locale": "de",
59 "theme": "dark",
60 "metadata": {
61 "device": {"type": "desktop", "uniqueId": unique_id},
62 "os": {"name": "win32", "version": "Windows 10 Pro", "abis": ["x64"], "host": "Lenovo"},
63 "app": {"platform": "electron"},
64 "version": {"package": "tv.vavoo.app", "binary": "3.1.8", "js": "3.1.8"},
65 },
66 "appFocusTime": 0,
67 "playerActive": False,
68 "playDuration": 0,
69 "devMode": False,
70 "hasAddon": True,
71 "castConnected": False,
72 "package": "tv.vavoo.app",
73 "version": "3.1.8",
74 "process": "app",
75 "firstAppStart": current_timestamp,
76 "lastAppStart": current_timestamp,
77 "ipLocation": None,
78 "adblockEnabled": True,
79 "proxy": {"supported": ["ss"], "engine": "Mu", "enabled": False, "autoServer": True},
80 "iap": {"supported": False},
81}
82
83r1 = session.post(PING_URL, json=initial_payload)
84r1.raise_for_status()
85data1 = decode_response(r1)
86
87# ---------------- ADDON SIG MANAGEMENT ----------------
88
89addon_sig_lock = threading.Lock()
90addon_sig_data = {
91 "sig": data1.get("addonSig"),
92 "ts": time.time()
93}
94
95def refresh_addon_sig_if_needed(force=False):
96 with addon_sig_lock:
97 now = time.time()
98 if not force and now - addon_sig_data["ts"] < ADDON_SIG_TTL:
99 return addon_sig_data["sig"]
100
101 payload = initial_payload.copy()
102 payload["lastAppStart"] = int(time.time() * 1000)
103
104 r = session.post(PING_URL, json=payload)
105 r.raise_for_status()
106 data = decode_response(r)
107
108 sig = data.get("addonSig")
109 if not sig:
110 raise RuntimeError("No addonSig received")
111
112 addon_sig_data["sig"] = sig
113 addon_sig_data["ts"] = now
114
115 print("[✓] addonSig refreshed")
116 return sig
117
118# ---------------- CATALOG LOAD (MULTI-REGION) ----------------
119
120items_by_region = {}
121
122for entry in REGIONS:
123 LANGUAGE = entry["language"]
124 REGION = entry["region"]
125 region_key = f"{LANGUAGE}-{REGION}"
126
127 print(f"[+] Lade Katalog für {region_key}")
128
129 catalog_headers = {
130 "content-type": "application/json; charset=utf-8",
131 "mediahubmx-signature": addon_sig_data["sig"],
132 "user-agent": "MediaHubMX/2",
133 "accept": "*/*",
134 "Accept-Language": LANGUAGE,
135 "Accept-Encoding": "gzip, deflate",
136 "Connection": "close",
137 }
138
139 cursor = None
140
141 while True:
142 catalog_payload = {
143 "language": LANGUAGE,
144 "region": REGION,
145 "catalogId": "iptv",
146 "id": "iptv",
147 "adult": False,
148 "search": "",
149 "sort": "",
150 "filter": {},
151 "cursor": cursor,
152 "clientVersion": "3.0.2"
153 }
154
155 r_catalog = session.post(CATALOG_URL, json=catalog_payload, headers=catalog_headers)
156 r_catalog.raise_for_status()
157 catalog_data = decode_response(r_catalog)
158
159 for item in catalog_data.get("items", []):
160 if item.get("type") == "iptv":
161 items_by_region.setdefault(region_key, []).append({
162 "id": item["ids"]["id"],
163 "url": item["url"],
164 "name": item["name"],
165 "group": item["group"],
166 "logo": item["logo"],
167 "language": LANGUAGE,
168 "region": REGION
169 })
170
171 cursor = catalog_data.get("nextCursor")
172 if not cursor:
173 break
174
175print(f"[✓] Gesamtanzahl IPTV-Sender: {sum(len(v) for v in items_by_region.values())}")
176
177# ---------------- M3U SAVE (MULTI-FILE) ----------------
178
179LOCAL_IP = "127.0.0.1"
180
181def get_local_ip():
182 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
183 s.connect(("8.8.8.8", 80)) # Connecting to Google's DNS server
184 local_ip = s.getsockname()[0] # Getting the local IP address
185 s.close()
186 print("Local IP address:", local_ip)
187 return local_ip
188# return LOCAL_IP
189
190# Unterordner erstellen
191PLAYLIST_DIR = "playlists"
192os.makedirs(PLAYLIST_DIR, exist_ok=True)
193
194def save_m3u_files():
195 local_ip = get_local_ip()
196
197 for region_key, items in items_by_region.items():
198 filename = os.path.join(PLAYLIST_DIR, f"vavoo_{region_key}.m3u")
199 m3u = "#EXTM3U\n"
200
201 for item in items:
202 m3u += (
203 f'#EXTINF:-1 tvg-id="{item["id"]}" '
204 f'tvg-name="{item["name"]}" '
205 f'tvg-logo="{item["logo"]}" '
206# f'group-title="{item["group"]} ({item["region"]})",{item["name"]}\n'
207 f'group-title="{item["group"]}",{item["name"]}\n'
208 )
209 m3u += f"http://{local_ip}:{PORT}/vavoo?channel={item['id']}\n"
210
211 with open(filename, "w", encoding="utf-8") as f:
212 f.write(m3u)
213
214 print(f"[✓] {filename} geschrieben")
215
216def save_master_m3u():
217 master = "#EXTM3U\n"
218
219 for region_key in items_by_region.keys():
220 filename = f"playlists/vavoo_{region_key}.m3u"
221 master += f'#EXTINF:-1 group-title="Master",{region_key}\n'
222 master += f"{filename}\n"
223
224 with open("vavoo_master.m3u", "w", encoding="utf-8") as f:
225 f.write(master)
226
227 print("[✓] vavoo_master.m3u geschrieben")
228
229save_m3u_files()
230save_master_m3u()
231
232# ---------------- FLASK APP ----------------
233
234app = Flask(__name__)
235
236@app.route("/vavoo")
237def stream_proxy():
238 channel_id = request.args.get("channel")
239 if not channel_id:
240 abort(400)
241
242 channel = None
243 for region_items in items_by_region.values():
244 for i in region_items:
245 if i["id"] == channel_id:
246 channel = i
247 break
248
249 if not channel:
250 abort(404)
251
252 try:
253 sig = refresh_addon_sig_if_needed()
254 except Exception as e:
255 abort(502, str(e))
256
257 resolve_headers = {
258 "content-type": "application/json; charset=utf-8",
259 "mediahubmx-signature": sig,
260 "user-agent": "MediaHubMX/2",
261 "accept": "*/*",
262 "Accept-Language": channel["language"],
263 "Accept-Encoding": "gzip, deflate",
264 "Connection": "close",
265 }
266
267 resolve_payload = {
268 "language": channel["language"],
269 "region": channel["region"],
270 "url": channel["url"],
271 "clientVersion": "3.0.2"
272 }
273
274 r_resolve = session.post(RESOLVE_URL, json=resolve_payload, headers=resolve_headers)
275
276 if r_resolve.status_code == 403:
277 sig = refresh_addon_sig_if_needed(force=True)
278 resolve_headers["mediahubmx-signature"] = sig
279 r_resolve = session.post(RESOLVE_URL, json=resolve_payload, headers=resolve_headers)
280
281 r_resolve.raise_for_status()
282 result = decode_response(r_resolve)
283
284 if result:
285 return Response(status=302, headers={"Location": result[0]["url"]})
286
287 abort(404)
288
289
290# ---------------- ALL-IN-ONE PLAYLIST ----------------
291
292@app.route("/playlist.m3u")
293def playlist():
294 local_ip = get_local_ip()
295 m3u = "#EXTM3U\n"
296
297 for region_items in items_by_region.values():
298 for item in region_items:
299 m3u += (
300 f'#EXTINF:-1 tvg-id="{item["id"]}" '
301 f'tvg-name="{item["name"]}" '
302 f'tvg-logo="{item["logo"]}" '
303# f'group-title="{item["group"]} ({item["region"]})",{item["name"]}\n'
304 f'group-title="{item["group"]}",{item["name"]}\n'
305 )
306 m3u += f"http://{local_ip}:{PORT}/vavoo?channel={item['id']}\n"
307
308 return Response(m3u, mimetype="application/x-mpegURL")
309
310
311# ---------------- MASTER PLAYLIST ----------------
312
313@app.route("/master.m3u")
314def master_playlist():
315 try:
316 with open("vavoo_master.m3u", "r", encoding="utf-8") as f:
317 content = f.read()
318 return Response(content, mimetype="application/x-mpegURL")
319 except FileNotFoundError:
320 return Response("#EXTM3U\n# Master playlist not found\n", mimetype="application/x-mpegURL")
321
322
323# ---------------- START SERVER ----------------
324
325if __name__ == "__main__":
326 app.run(host="0.0.0.0", port=PORT)
Note: See TracBrowser for help on using the repository browser.