From 83df3b4602e190297bbbff62fc43d17225512063 Mon Sep 17 00:00:00 2001 From: devilreef Date: Thu, 30 Apr 2026 15:04:25 +0600 Subject: [PATCH] feat(liquidsoap): emit cover_path + up_next in now-playing json also fix path.extension -> file.extension (path module has no .extension in 2.3.3) --- config/liquidsoap.liq | 57 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/config/liquidsoap.liq b/config/liquidsoap.liq index 4fd08d8..5d637b4 100644 --- a/config/liquidsoap.liq +++ b/config/liquidsoap.liq @@ -17,7 +17,7 @@ def make_deck(dir) = all = file.ls(dir, recursive=true, absolute=true) audio_exts = ["flac", "mp3", "opus", "m4a", "wav", "ogg"] audio = list.filter( - fun(f) -> list.mem(string.case(lower=true, path.extension(leading_dot=false, f)), audio_exts), + fun(f) -> list.mem(string.case(lower=true, file.extension(leading_dot=false, f)), audio_exts), all ) files := list.shuffle(audio) @@ -56,20 +56,55 @@ def make_deck(dir) = { source = request.dynamic(next_request), peek = peek } end -def emit_now_playing(station, m) = +# walk up from the audio file to find cover.{jpg,png,webp}. +# falls back to /cover.* (one level above tracks/). +def cover_for(filename) = + def find_in(dir) = + candidates = ["#{dir}/cover.jpg", "#{dir}/cover.png", "#{dir}/cover.webp"] + list.fold( + fun(found, c) -> if found == "" and file.exists(c) then c else found end, + "", + candidates + ) + end + album_dir = path.dirname(filename) + c1 = find_in(album_dir) + if c1 != "" then c1 else + tracks_dir = path.dirname(album_dir) + station_root = path.dirname(tracks_dir) + find_in(station_root) + end +end + +# build a json object for one upcoming track from its filename. +# reads tags via request.metadata; uses request.duration for length. +def track_meta_json(filename) = + r = request.create(filename) + ignore(request.resolve(r)) + m = request.metadata(r) + request.destroy(r) + d = null.get(default=-1., request.duration(filename)) + obj = json() + obj.add("title", m["title"]) + obj.add("artist", m["artist"]) + obj.add("album", m["album"]) + obj.add("duration", if d > 0. then string(int_of_float(d)) else "" end) + obj.add("cover_path", cover_for(filename)) + obj +end + +def emit_now_playing(station, m, upcoming) = filename = m["filename"] if filename != "" then - # decoder-reported metadata may be empty; fall back to request.duration which - # reads it directly from the file. -1.0 means unknown. meta_dur = m["duration"] dur_str = if meta_dur != "" then meta_dur else - # request.duration returns float? in liquidsoap 2.3 — unwrap with default -1. d = null.get(default=-1., request.duration(filename)) if d > 0. then string(int_of_float(d)) else "" end end + payload = json() payload.add("station", station) payload.add("artist", m["artist"]) @@ -78,7 +113,17 @@ def emit_now_playing(station, m) = payload.add("filename", filename) payload.add("duration", dur_str) payload.add("started_at", string(int_of_float(time()))) - file.write(data=payload.stringify(), atomic=true, temp_dir="/now-playing", "/now-playing/#{station}.json") + payload.add("cover_path", cover_for(filename)) + + up_arr = list.map(track_meta_json, upcoming) + payload.add("up_next", up_arr) + + file.write( + data=payload.stringify(), + atomic=true, + temp_dir="/now-playing", + "/now-playing/#{station}.json" + ) end end