VNEWS / _run.py
bep40's picture
HLS fix: self-heal + auto-patch 8 segments + live Hls.js config, persist lên repo
bb60960 verified
Raw History Blame Contribute Delete
5.99 kB
"""VNEWS boot entry — self-heal + HLS live fixes before import.
1) If main.py is the PLACEHOLDER stub (or otherwise damaged), restore the
known-good version from commit 5b6000c via huggingface_hub.
2) Apply the m3u_hls live fix to main.py if it still has the old 3-segment
manifest: bump to 8 repeating #EXTINF:8.0 segments, add
#EXT-X-DISCONTINUITY-SEQUENCE:0, keep the playlist open (no ENDLIST, no
trailing #EXT-X-DISCONTINUITY).
3) Apply Hls.js live tuning to static/vtv_init.js if not present:
liveDurationInfinity / liveSyncDurationCount / maxBufferLength.
4) Persist any local changes back to the repo 'main' branch (best-effort,
requires a write token in the Space env, e.g. HF_TOKEN secret).
"""
import os
_APP_DIR = os.path.dirname(os.path.abspath(__file__))
_MAIN = os.path.join(_APP_DIR, "main.py")
_VTV = os.path.join(_APP_DIR, "static", "vtv_init.js")
_GOOD_REV = "5b6000c58f89723b3d228afb06c891ce909ac488"
_REPO = "bep40/VNEWS"
def _read(p):
try:
with open(p, encoding="utf-8", errors="replace") as f:
return f.read()
except Exception:
return ""
def _write(p, s):
with open(p, "w", encoding="utf-8") as f:
f.write(s)
def _main_is_broken(path):
try:
if os.path.getsize(path) < 50000:
return True
head = _read(path)[:2048]
return "PLACEHOLDER" in head or "app = FastAPI()" not in head
except Exception:
return True
# --- old 3-segment manifest block in main.py (verified against 5b6000c) ---
_OLD_MANIFEST = (
' _seg = seg_proxy_base\n'
' manifest = (\n'
' "#EXTM3U\\n"\n'
' "#EXT-X-VERSION:6\\n"\n'
' "#EXT-X-TARGETDURATION:8\\n"\n'
' "#EXT-X-MEDIA-SEQUENCE:0\\n"\n'
' "#EXT-X-START:TIME-OFFSET=0,PRECISE=YES\\n"\n'
' "#EXTINF:8.0,\\n"\n'
' + _seg + "\\n"\n'
' "#EXTINF:8.0,\\n"\n'
' + _seg + "\\n"\n'
' "#EXTINF:8.0,\\n"\n'
' + _seg + "\\n"\n'
' )\n'
)
_NEW_MANIFEST = (
' _seg = seg_proxy_base\n'
' manifest = (\n'
' "#EXTM3U\\n"\n'
' "#EXT-X-VERSION:6\\n"\n'
' "#EXT-X-TARGETDURATION:8\\n"\n'
' "#EXT-X-MEDIA-SEQUENCE:0\\n"\n'
' "#EXT-X-DISCONTINUITY-SEQUENCE:0\\n"\n'
' "#EXT-X-START:TIME-OFFSET=0,PRECISE=YES\\n"\n'
+ ''.join(' "#EXTINF:8.0,\\n"\n + _seg + "\\n"\n' for _ in range(8))
+ ' )\n'
)
def _fix_main_hls(content):
if "#EXT-X-DISCONTINUITY-SEQUENCE" in content:
return content, False
if content.count("#EXTINF:8.0") >= 8:
return content, False
if _OLD_MANIFEST not in content:
return content, False
new = content.replace(_OLD_MANIFEST, _NEW_MANIFEST, 1)
if new != content:
return new, True
return content, False
_OLD_HLS_CONFIG = "new Hls({ enableWorker: false })"
_NEW_HLS_CONFIG = (
"new Hls({ enableWorker: false, liveDurationInfinity: true, "
"liveSyncDurationCount: 5, maxBufferLength: 60, maxMaxBufferLength: 240 })"
)
def _fix_vtv(content):
if "liveDurationInfinity" in content:
return content, False
if _OLD_HLS_CONFIG not in content:
return content, False
new = content.replace(_OLD_HLS_CONFIG, _NEW_HLS_CONFIG, 1)
return new, new != content
def _persist(fn, data):
"""Best-effort: push patched file back to the repo 'main' branch so the
fix is permanent, not just container-local. Never raises."""
try:
token = os.environ.get("HF_TOKEN")
if not token:
print(f"[self-heal] no HF_TOKEN — skip persist {fn}", flush=True)
return False
from huggingface_hub import upload_file
upload_file(
path_or_fileobj=data.encode("utf-8"),
path_in_repo=fn,
repo_id=_REPO,
repo_type="space",
token=token,
commit_message=f"[hls-fix] {fn}: 8 segments + live config",
)
print(f"[self-heal] persisted {fn}", flush=True)
return True
except Exception as e:
print(f"[self-heal] persist {fn} failed: {e}", flush=True)
return False
def _restore_main():
print(f"[self-heal] main.py damaged -> restoring from {_GOOD_REV}", flush=True)
try:
from huggingface_hub import hf_hub_download
good = hf_hub_download(
repo_id=_REPO,
filename="main.py",
revision=_GOOD_REV,
repo_type="space",
)
with open(good, encoding="utf-8") as f:
data = f.read()
assert "app = FastAPI()" in data and "proxy_m3u_hls" in data
_write(_MAIN, data)
print(f"[self-heal] main.py restored ({len(data)} bytes)", flush=True)
return True
except Exception as e:
print(f"[self-heal] FAILED to restore main.py: {e}", flush=True)
return False
def _apply_fixes():
changed = []
try:
if _main_is_broken(_MAIN):
_restore_main()
mc = _read(_MAIN)
if mc:
nm, changed_m = _fix_main_hls(mc)
if changed_m:
_write(_MAIN, nm)
print("[hls] main.py patched: 8 segments + DISCONTINUITY-SEQUENCE:0", flush=True)
changed.append("main.py")
vc = _read(_VTV)
if vc:
nv, changed_v = _fix_vtv(vc)
if changed_v:
_write(_VTV, nv)
print("[hls] vtv_init.js patched: liveDurationInfinity config", flush=True)
changed.append("static/vtv_init.js")
except Exception as e:
print(f"[hls] review fixes failed (non-fatal): {e}", flush=True)
for fn in changed:
try:
_persist(fn, _read(os.path.join(_APP_DIR, fn)))
except Exception as e:
print(f"[hls] persist {fn} error: {e}", flush=True)
_apply_fixes()
from app_v2_entry import app # v5-stable inline bongda proxy