antv-live / app.py
ken1402's picture
Update app.py
5085bad verified
Raw
History Blame Contribute Delete
7.1 kB
import os
import requests
from urllib.parse import urljoin, quote, unquote
from flask import Flask, Response, send_from_directory, request
app = Flask(__name__)
# ============================================================================
# TỰ ĐỘNG CẬP NHẬT DUCKDNS KHI APP KHỞI CHẠY
# ============================================================================
DUCKDNS_DOMAIN = "chrtv"
DUCKDNS_TOKEN = "0a7471b5-56fc-4b60-a5d9-5381f66930d9"
def update_duckdns():
try:
url = f"https://www.duckdns.org/update?domains={DUCKDNS_DOMAIN}&token={DUCKDNS_TOKEN}&ip="
res = requests.get(url, timeout=10)
if "OK" in res.text:
print(f"[DuckDNS] Cập nhật Domain {DUCKDNS_DOMAIN}.duckdns.org THÀNH CÔNG!")
else:
print(f"[DuckDNS] Cập nhật thất bại: {res.text}")
except Exception as e:
print(f"[DuckDNS] Lỗi kết nối DuckDNS: {e}")
# Gọi hàm cập nhật DuckDNS ngay khi load file app.py
update_duckdns()
# ============================================================================
# CẤU HÌNH BIẾN & LINK LUỒNG
# ============================================================================
TARGET_SERVER = "http://23.237.104.106:8080"
AMAGI_STUDIO_C = "https://amg12058-c15studio-amg12058c1-lg-us-5787.playouts.now.amagi.tv/playlist.m3u8"
WARNER_TV = "http://190.61.90.17:40000/play/a0ho/index.m3u8"
PARAMOUNT = "http://206.212.244.63/137/index.m3u8"
KENSCOM_180 = "http://103.152.216.26:8443/live/DUC3mlZ3O2Z0i4May6u1cA/1785547208/180.m3u8"
KENSCOM_191 = "http://103.152.216.26:8443/live/DUC3mlZ3O2Z0i4May6u1cA/1785547208/191.m3u8"
HEADERS = {
"User-Agent": "VLC/3.0.20 LibVLC/3.0.20",
"Accept": "*/*",
"Accept-Language": "vi-VN,vi;q=0.9,en-US;q=0.8,en;q=0.7",
"Connection": "keep-alive",
"Host": "103.152.216.26:8443"
}
PUBLIC_BASE_URL = os.environ.get("PUBLIC_BASE_URL", "https://ken1402-antv-live.hf.space")
def add_cors_headers(response):
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS, HEAD'
response.headers['Access-Control-Allow-Headers'] = '*'
return response
@app.route('/')
def home():
return f"IPTV Server & ErsatzTV Engine is Running! DuckDNS Active: {DUCKDNS_DOMAIN}.duckdns.org", 200
# ============================================================================
# 1. PLAYLIST M3U TỔNG HỢP
# ============================================================================
@app.route('/playlist.m3u', methods=['GET', 'OPTIONS'])
def get_playlist():
if request.method == 'OPTIONS':
return add_cors_headers(Response(status=204))
m3u_content = f"""#EXTM3U
#EXTINF:-1 tvg-id="HBO" group-title="US Live" tvg-logo="https://i.imgur.com/vHqQZ4u.png",HBO Movies HD
{PUBLIC_BASE_URL}/stream/hbo/master.m3u8
#EXTINF:-1 tvg-id="WarnerTV" group-title="US Live" tvg-logo="https://i.imgur.com/2Xy349p.png",Warner TV HD
{PUBLIC_BASE_URL}/stream/warnertv
#EXTINF:-1 tvg-id="Paramount" group-title="US Live" tvg-logo="https://i.imgur.com/Q23kLMl.png",Paramount Network HD
{PUBLIC_BASE_URL}/stream/paramount
#EXTINF:-1 tvg-id="StudioC" group-title="US Live",Amagi Studio C HD
{PUBLIC_BASE_URL}/stream/studioc
#EXTINF:-1 tvg-id="Live180" group-title="VN Live",KensCom Channel 180 HD
{PUBLIC_BASE_URL}/stream/180.m3u8
#EXTINF:-1 tvg-id="Live191" group-title="VN Live",KensCom Channel 191 HD
{PUBLIC_BASE_URL}/stream/191.m3u8"""
res = Response(m3u_content.strip(), mimetype='application/x-mpegurl')
return add_cors_headers(res)
# ============================================================================
# 2. RESTREAM HBO
# ============================================================================
@app.route('/stream/hbo/<path:filename>', methods=['GET', 'OPTIONS'])
def serve_hbo(filename):
if request.method == 'OPTIONS':
return add_cors_headers(Response(status=204))
folder_path = '/app/hls/hbo'
if filename in ["index.m3u8", "master.m3u8"]:
filename = "master.m3u8"
file_path = os.path.join(folder_path, filename)
if not os.path.exists(file_path):
return "Stream đang khởi tạo...", 503
res = send_from_directory(folder_path, filename)
add_cors_headers(res)
if filename.endswith('.m3u8'):
res.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate, max-age=0'
else:
res.headers['Cache-Control'] = 'max-age=3600'
return res
# ============================================================================
# 3. PROXY HLS KHÁC
# ============================================================================
def handle_hls_proxy(default_target_url, endpoint_path):
if request.method == 'OPTIONS':
return add_cors_headers(Response(status=204))
target_url = request.args.get('url') or default_target_url
target_url = unquote(target_url)
req_headers = HEADERS.copy()
if "amagi.tv" in target_url:
req_headers.pop("Host", None)
try:
resp = requests.get(target_url, headers=req_headers, timeout=10, stream=True)
if resp.status_code != 200:
return add_cors_headers(Response(f"Upstream Error: HTTP {resp.status_code}", status=resp.status_code))
raw_bytes = resp.content
raw_text = raw_bytes.decode('utf-8-sig', errors='ignore')
if raw_text.strip().startswith('#EXTM3U'):
lines = raw_text.splitlines()
rewritten_lines = []
for line in lines:
line_str = line.strip()
if not line_str or line_str.startswith('#'):
rewritten_lines.append(line_str)
else:
full_url = urljoin(target_url, line_str)
rewritten_lines.append(f"{endpoint_path}?url={quote(full_url)}")
res = Response("\n".join(rewritten_lines).strip(), status=200, mimetype='application/x-mpegurl')
res.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
return add_cors_headers(res)
else:
res = Response(raw_bytes, status=200, mimetype='video/mp2t')
return add_cors_headers(res)
except Exception as e:
return add_cors_headers(Response(f"Proxy Exception: {str(e)}", status=502))
@app.route('/stream/studioc', methods=['GET', 'OPTIONS'])
def proxy_studioc(): return handle_hls_proxy(AMAGI_STUDIO_C, "/stream/studioc")
@app.route('/stream/warnertv', methods=['GET', 'OPTIONS'])
def proxy_warnertv(): return handle_hls_proxy(WARNER_TV, "/stream/warnertv")
@app.route('/stream/paramount', methods=['GET', 'OPTIONS'])
def proxy_paramount(): return handle_hls_proxy(PARAMOUNT, "/stream/paramount")
@app.route('/stream/180.m3u8', methods=['GET', 'OPTIONS'])
def proxy_180(): return handle_hls_proxy(KENSCOM_180, "/stream/180.m3u8")
@app.route('/stream/191.m3u8', methods=['GET', 'OPTIONS'])
def proxy_191(): return handle_hls_proxy(KENSCOM_191, "/stream/191.m3u8")
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860, threaded=True)