Gpsirdb / app.py
gopallikehack's picture
Update app.py
5170d08 verified
Raw History Blame Contribute Delete
4.67 kB
import re
from urllib.parse import urljoin, quote
import requests
from flask import Flask, request, Response, jsonify, send_from_directory
app = Flask(__name__, static_folder="static", static_url_path="/static")
UA = (
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/124.0 Safari/537.36"
)
TIMEOUT = 20
def fetch(url, stream=False):
headers = {"User-Agent": UA, "Accept": "*/*"}
return requests.get(
url, headers=headers, timeout=TIMEOUT, stream=stream, allow_redirects=True
)
# ─────────────────────────────────────────────
# /api/parse β†’ fetch M3U, return JSON of channels
# ─────────────────────────────────────────────
@app.route("/api/parse")
def api_parse():
url = request.args.get("url")
if not url:
return jsonify({"error": "missing url"}), 400
try:
r = fetch(url)
r.raise_for_status()
text = r.text
except Exception as e:
return jsonify({"error": f"fetch failed: {e}"}), 502
channels = parse_m3u(text)
return jsonify({"count": len(channels), "channels": channels})
def parse_m3u(text):
channels = []
lines = [ln.strip() for ln in text.splitlines() if ln.strip()]
pending = None
for line in lines:
if line.startswith("#EXTINF"):
info = {"name": "", "logo": "", "group": "", "tvg_id": ""}
m = re.search(r'tvg-logo="([^"]+)"', line)
if m: info["logo"] = m.group(1)
m = re.search(r'group-title="([^"]+)"', line)
if m: info["group"] = m.group(1)
m = re.search(r'tvg-id="([^"]+)"', line)
if m: info["tvg_id"] = m.group(1)
if "," in line:
info["name"] = line.split(",", 1)[1].strip()
pending = info
elif line.startswith("#"):
continue
else:
if pending is not None:
pending["url"] = line
channels.append(pending)
pending = None
return channels
# ─────────────────────────────────────────────
# /api/proxy β†’ proxy HLS playlists + segments with CORS
# ─────────────────────────────────────────────
@app.route("/api/proxy")
def api_proxy():
target = request.args.get("url")
if not target:
return Response("missing url", status=400)
try:
upstream = fetch(target, stream=True)
except Exception as e:
return Response(f"fetch failed: {e}", status=502)
content_type = upstream.headers.get("Content-Type", "")
if "mpegurl" in content_type.lower() or target.endswith(".m3u8"):
body = upstream.content.decode("utf-8", errors="replace")
rewritten = rewrite_playlist(body, target)
return Response(
rewritten,
headers={
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/vnd.apple.mpegurl",
},
)
def generate():
for chunk in upstream.iter_content(chunk_size=64 * 1024):
if chunk:
yield chunk
return Response(
generate(),
headers={
"Access-Control-Allow-Origin": "*",
"Content-Type": content_type or "application/octet-stream",
},
)
def rewrite_playlist(body, base_url):
out = []
for line in body.splitlines():
s = line.strip()
if not s:
out.append(line)
continue
if s.startswith("#"):
def repl(m):
u = m.group(1)
abs_u = urljoin(base_url, u)
return f'URI="/api/proxy?url={quote(abs_u, safe="")}"'
out.append(re.sub(r'URI="([^"]+)"', repl, line))
else:
abs_u = urljoin(base_url, s)
out.append(f"/api/proxy?url={quote(abs_u, safe='')}")
return "\n".join(out)
# ─────────────────────────────────────────────
# Serve the frontend
# ─────────────────────────────────────────────
@app.route("/")
def index():
return send_from_directory("static", "index.html")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860)