radieo/stream/ingest_proxy.liq
Pierre-Olivier Mercier 8054c98dd1 stream: split radio.liq into pipeline, web and ingest-proxy parts
Extract the HTTP surface out of radio.liq into two included files: web.liq
(static assets, PWA, local player API) and ingest_proxy.liq (relays to the
ingest daemon). radio.liq keeps only the streaming pipeline and ends with the
%include directives, evaluated after the pipeline so the handlers see radio,
now_playing, history, etc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 16:11:56 +08:00

117 lines
4.4 KiB
Text

# radieo — reverse-proxy vers le daemon d'ingestion.
#
# Inclus par radio.liq. Le player n'a pas d'accès direct au réseau interne
# (l'ingest n'est joignable que depuis les autres conteneurs) : on relaie donc
# ces quelques endpoints à travers le harbor du flux (port 8000). Ces routes
# sont autonomes — elles ne dépendent que de `http`/`url`, jamais du pipeline —
# et renvoient une valeur neutre plutôt qu'une erreur si l'ingest est
# injoignable, pour ne pas casser le player.
# File d'attente des prochains morceaux, relayée depuis le daemon d'ingestion.
ingest_queue_url = "http://ingest:8080/queue"
harbor.http.register(
port=8000, method="GET", "/queue",
fun(_, resp) -> begin
resp.content_type("application/json; charset=utf-8")
body = http.get(ingest_queue_url, timeout=5.0)
if body.status_code == 200 then
resp.data(string.trim(body) ^ "\n")
else
resp.data("[]")
end
end
)
# État du préchargement {ready, prefetch}. Si le daemon est injoignable on
# renvoie un objet neutre plutôt qu'une erreur, pour ne pas casser le player.
ingest_status_url = "http://ingest:8080/status"
harbor.http.register(
port=8000, method="GET", "/ingest/status",
fun(_, resp) -> begin
resp.content_type("application/json; charset=utf-8")
body = http.get(ingest_status_url, timeout=5.0)
if body.status_code == 200 then
resp.data(string.trim(body) ^ "\n")
else
resp.data("{}")
end
end
)
# Mettre une URL yt-dlp en file d'attente (piste seule, ou playlist/album
# entier). On relaie la demande vers l'ingest, qui résout l'URL et la place en
# file prioritaire (le prochain /next la servira). On renvoie tel quel son code
# et son corps JSON ({queued: N} ou une erreur). Timeout large : résoudre une
# grosse playlist peut prendre du temps. NB : la variable locale s'appelle
# `link`, pas `url`, pour ne pas masquer le module `url` (url.encode).
ingest_enqueue_url = "http://ingest:8080/enqueue"
harbor.http.register(
port=8000, method="POST", "/enqueue",
fun(req, resp) -> begin
link = list.assoc(default="", "url", req.query)
if link == "" then
resp.status_code(400)
resp.data("missing url")
else
body = http.post(
data="", timeout=60.0, "#{ingest_enqueue_url}?url=#{url.encode(link)}"
)
resp.status_code(body.status_code)
resp.content_type("application/json; charset=utf-8")
resp.data(string.trim(body) ^ "\n")
end
end
)
# Retirer un morceau de la file d'attente. Symétrique de /enqueue : on relaie la
# demande (l'`id` opaque fourni par /queue) vers l'ingest, qui retire l'entrée
# correspondante. On renvoie tel quel son code et son corps JSON ({removed: true}
# ou une erreur).
ingest_dequeue_url = "http://ingest:8080/dequeue"
harbor.http.register(
port=8000, method="POST", "/dequeue",
fun(req, resp) -> begin
id = list.assoc(default="", "id", req.query)
if id == "" then
resp.status_code(400)
resp.data("missing id")
else
body = http.post(
data="", timeout=10.0, "#{ingest_dequeue_url}?id=#{url.encode(id)}"
)
resp.status_code(body.status_code)
resp.content_type("application/json; charset=utf-8")
resp.data(string.trim(body) ^ "\n")
end
end
)
# Partage Subsonic à la demande. Un morceau de la bibliothèque Subsonic n'a pas
# d'URL publique : son lien « source » pointe ici avec l'id du morceau. On
# demande alors à l'ingest (qui détient les identifiants Subsonic) de créer un
# partage public via createShare, puis on redirige l'auditeur vers l'URL
# renvoyée. Le partage n'est donc créé que si quelqu'un clique réellement sur le
# lien — jamais à chaque morceau joué. 404 si l'id manque, 502 si l'ingest ne
# peut pas partager (partage désactivé côté serveur, injoignable…).
ingest_share_url = "http://ingest:8080/share"
harbor.http.register(
port=8000, method="GET", "/share",
fun(req, resp) -> begin
song = list.assoc(default="", "song", req.query)
if song == "" then
resp.status_code(404)
resp.data("missing song id")
else
body = http.post(data="", timeout=10.0, "#{ingest_share_url}?id=#{url.encode(song)}")
share = json.parse(default={url=""}, string.trim(body))
if body.status_code == 200 and share.url != "" then
resp.status_code(302)
resp.header("Location", share.url)
resp.data("")
else
resp.status_code(502)
resp.data("share unavailable")
end
end
end
)