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>
117 lines
4.4 KiB
Text
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
|
|
)
|