version updates

This commit is contained in:
Simon
2026-06-24 20:54:16 +00:00
parent 455e5cf8d8
commit 382a637b95
5 changed files with 257 additions and 0 deletions

View File

@@ -12,6 +12,7 @@ from curl_cffi import requests as impersonate_requests
import threading
import io
import time
import hashlib
from urllib.parse import urljoin
# Browser to impersonate at the TLS/HTTP layer. Some origins (e.g. the
@@ -355,6 +356,63 @@ def index():
def favicon():
return send_from_directory(app.static_folder, 'favicon.ico')
# --- Frontend asset version tracking -------------------------------------
# The client polls /api/version and, when a tracked file's content hash
# changes, hot-swaps CSS in place or reloads the page. This lets a deploy
# reach already-open tabs without a manual refresh.
_FRONTEND_DIR = os.path.abspath(app.static_folder)
_VERSION_EXTS = ('.html', '.css', '.js')
_version_cache = {'mtime': None, 'payload': None}
_version_lock = threading.Lock()
def _scan_frontend_files():
"""Map served relative paths -> absolute paths for tracked frontend files."""
files = {}
for root, _dirs, names in os.walk(_FRONTEND_DIR):
for name in names:
if os.path.splitext(name)[1].lower() not in _VERSION_EXTS:
continue
path = os.path.join(root, name)
rel = os.path.relpath(path, _FRONTEND_DIR).replace(os.sep, '/')
files[rel] = path
return files
def _compute_version_payload(files):
"""Hash each tracked file's contents plus a combined version fingerprint."""
file_hashes = {}
combined = hashlib.md5()
for rel in sorted(files):
try:
with open(files[rel], 'rb') as fh:
digest = hashlib.md5(fh.read()).hexdigest()
except OSError:
continue
file_hashes[rel] = digest
combined.update(rel.encode('utf-8'))
combined.update(digest.encode('utf-8'))
return {'version': combined.hexdigest(), 'files': file_hashes}
@app.route('/api/version', methods=['GET'])
def frontend_version():
files = _scan_frontend_files()
# Use the newest mtime across tracked files as a cheap cache key so frequent
# polls only re-hash contents when something on disk actually changed.
try:
latest_mtime = max((os.path.getmtime(p) for p in files.values()), default=0)
except OSError:
latest_mtime = 0
with _version_lock:
if _version_cache['mtime'] != latest_mtime or _version_cache['payload'] is None:
_version_cache['payload'] = _compute_version_payload(files)
_version_cache['mtime'] = latest_mtime
payload = _version_cache['payload']
resp = jsonify(payload)
resp.headers['Cache-Control'] = 'no-store'
return resp
@app.route('/api/stream', methods=['POST', 'GET', 'HEAD'])
def stream_video():
# Note: <video> tags perform GET. To support your POST requirement,