stream: surface ingest prefetch progress in the player
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c12e522fee
commit
1648030eba
4 changed files with 66 additions and 2 deletions
|
|
@ -5,9 +5,12 @@ Endpoints:
|
|||
is ready (Liquidsoap then falls back to /fallback.m3u).
|
||||
GET /fallback.m3u -> playlist of already-aired files, the stream's safety
|
||||
net; empty (→ silence) until something has played.
|
||||
GET /status -> JSON prefetch state {ready, prefetch}, surfaced to the
|
||||
player (proxied by the stream) so it can show buffering.
|
||||
GET /healthz -> "ok"
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
|
|
@ -50,6 +53,8 @@ class _Handler(BaseHTTPRequestHandler):
|
|||
self._serve_next()
|
||||
elif self.path == "/fallback.m3u":
|
||||
self._serve_fallback()
|
||||
elif self.path == "/status":
|
||||
self._serve_status()
|
||||
elif self.path == "/healthz":
|
||||
self._text(200, "ok\n")
|
||||
else:
|
||||
|
|
@ -73,6 +78,15 @@ class _Handler(BaseHTTPRequestHandler):
|
|||
lines.append(p)
|
||||
self._text(200, "\n".join(lines) + "\n", "audio/x-mpegurl; charset=utf-8")
|
||||
|
||||
def _serve_status(self):
|
||||
# Prefetch progress: how many tracks are buffered vs. the target. The
|
||||
# stream's initial buffer is "full" once ready reaches PREFETCH.
|
||||
body = json.dumps({
|
||||
"ready": self.server.queue.ready_count(),
|
||||
"prefetch": config.PREFETCH,
|
||||
})
|
||||
self._text(200, body + "\n", "application/json; charset=utf-8")
|
||||
|
||||
def _text(self, code: int, body: str, ctype: str = "text/plain; charset=utf-8"):
|
||||
data = body.encode("utf-8")
|
||||
self.send_response(code)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,13 @@ class TrackQueue:
|
|||
with self._lock:
|
||||
self._ready.append((path, track))
|
||||
|
||||
# --- introspection ----------------------------------------------------
|
||||
|
||||
def ready_count(self) -> int:
|
||||
"""Number of tracks currently downloaded and ready to play."""
|
||||
with self._lock:
|
||||
return len(self._ready)
|
||||
|
||||
# --- serving ----------------------------------------------------------
|
||||
|
||||
def pop_next(self) -> tuple[Path, Track] | None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue