github savonet/liquidsoap v2.4.6
Liquidsoap 2.4.6

latest release: v2.5.0-rc1
2 hours ago

This is the sixth bugfix release of Liquidsoap 2.4.x.

While most of our energy goes into 2.5.x, bugs keep being reported and fixed along the way, and the ones that apply to 2.4.x deserve to reach the people running it in production!

There's also a trend of issues and fixes reported by automated tools that is, in fact, quite helpful.

It really helps when a user can come with a problem, a full description and analysis and sometimes even a proposed solution. We are aware of the complexity of the current changes but it's a positive effect when people who just love radio and have no previous OCaml expertise can come up with solutions to their own problem that we can effectively implement..

It should be safe to upgrade from any 2.4.x version. As usual, we recommend trying it on a staging environment before rolling out to production.

Debian and Ubuntu packages now ship our own FFmpeg

Starting with this release, our debian and ubuntu packages are statically linked against our own build of the FFmpeg libraries, maintained at savonet/ffmpeg-static-build. The packages install on a stock system, without pulling the distribution's FFmpeg, and every one of them comes with the same recent FFmpeg and the same set of codecs, whatever the distribution provides. This includes fdk-aac encoding in %ffmpeg.

Until now, our debian packages depended on the FFmpeg libraries from deb-multimedia.org, a third-party repository replacing the distribution's own packages, which caused all sorts of binary compatibility issues. This is no longer needed: you can remove that repository from your system if liquidsoap was your only reason for having it.

Package repositories

We now publish apt and apk repositories at https://repo.liquidsoap.info, so that installing and upgrading liquidsoap on Debian, Ubuntu and Alpine goes through your package manager instead of downloading a package by hand. A single command configures the repository matching your system and asks which release you want:

curl -fsSL https://repo.liquidsoap.info/setup.sh | sudo sh

After that, apt-get install liquidsoap or apk add liquidsoap does the rest. You can also name the release up front, which is what you want in a Dockerfile:

curl -fsSL https://repo.liquidsoap.info/setup.sh | sudo sh -s -- --channel v2.4.6

Each release has its own channel, and there is a rolling channel for each supported version, rebuilt on every commit and picked up by an ordinary apt-get upgrade or apk upgrade. Both the liquidsoap and liquidsoap-minimal packages are available.

Notable fixes

  • Metadata and track callbacks after a crossfade: cross and crossfade dropped the metadata of a track repeating the previous one, such as a single file on a loop, and handlers registered after the operator only ever saw the first track. Metadata now comes through and on_track fires on every crossing. A synchronous callback reading the frame of its own source, e.g. s.time() inside s.on_metadata, no longer hangs either. Reported by @caturria (#5360, #5361).
  • Fades: fade.out with track_sensitive=true muted the whole next track after a skip, and a pending liq_fade_*_delay on a track with a 0. fade duration amplified by infinity. Both are fixed. Reported by @NonstopGutesMarketing (#5514).
  • HLS segment drift: Segment boundaries slowly drifted away from segment_duration, at a rate depending on the encoder's frame size, so variants using different codecs diverged from each other. Segments now stay on the segment grid. Reported by @rmens (#5319), who also found that an error raised while output.file.hls shuts down could escape the clock's on_error handler and kill the process (#5260).
  • Icecast and harbor: output.icecast with chunked=true now reconnects after the server goes away, reported by @TheKigen (#5519). output.harbor clients asking for ICY metadata receive it, with a patch from @uhthomas (#5279). input.harbor cleans up its sockets.
  • SRT: output.srt no longer gets stuck after a clock latency reset, with a patch from @gonzafirewall (#5309).
  • Playlists reloaded on file change: file.watch lost track of a file that was replaced rather than written in place, which is how most programs save a file. playlist with reload_mode="watch" now also waits for the writer to finish before reloading. Reported by @vitoyucepi (#3343).
  • output.file retries: The delay returned by reopen_on_error is now honoured when the failure happens while opening the file, instead of retrying on every streaming cycle. Reported by @vitoyucepi (#2437).
  • Long-running scripts: Several sources of unbounded growth are gone: in the type unifier, with patches from @lisfox1 (#5257) and @charles-rc (#5336), and in buffers, reported by @juniorkain (#5287).
  • Catchable errors: Errors raised inside a builtin, such as file.ls on an unreadable directory, are now runtime errors that try ... catch can handle instead of aborting the script. Reported by @rmens (#5454).
  • Running as root: Liquidsoap now logs a warning instead of exiting. The container detection behind that check does not work under cgroup v2, which made it fire on a plain docker run. Reported by @vitoyucepi (#3406), along with log messages lost when liquidsoap exits before streaming (#3375).
  • Audioscrobbler: The Last.fm endpoint is fixed, with a patch from @iibaranov-IG (#5404).

A big thank you to everyone who took the time to report these issues, send patches and test the fixes!

Here's the full changelog:

2.4.6 (2026-10-08)

Fixed:

  • Fixed fade.in/fade.out amplifying by infinity while a liq_fade_*_delay was
    pending on a track whose fade duration is 0..
  • Fixed fade.out with track_sensitive=true muting the whole next track
    after a skip (#5514)
  • Fixed output.icecast with chunked=true never reconnecting after the
    server went away (#5519)
  • cross and crossfade with their default deduplicate=true no longer drop
    the metadata of a track that repeats the one before it, e.g. a single file on
    a loop, which silenced the handlers a script registers after the operator.
    cross now marks the outgoing track's metadata where it replays it into the
    transition, so the repeat it is meant to remove is told apart from a fresh
    announcement rather than guessed at by comparing values (#5360)
  • metadata.deduplicate no longer compares across a track mark, for the same
    reason (#5360)
  • cross and crossfade now fire on_track on every crossing. The default
    transition mixes the two tracks and carries no track mark of its own, so
    handlers registered after a crossfade only ever saw the first track (#5379)
  • Fixed a synchronous callback hanging or looping forever when it reads the
    frame of the source it is registered on, e.g. calling s.time() inside
    s.on_metadata(synchronous=true, ...) (#5361)
  • Fixed file.watch losing track of a file that gets replaced rather than
    written in place: an inotify watch follows the inode, so a writer doing the
    usual write-to-temporary-then-rename silenced the watcher for good. The
    containing directory is watched instead. playlist with reload_mode="watch"
    also waits for a burst of events to settle before reloading, so a writer that
    truncates before writing no longer triggers two racing reloads and plays
    tracks out of order (#3343)
  • Anything logged while evaluating the script, deprecation warnings in
    particular, was silently dropped when liquidsoap exited before reaching the
    streaming loop, e.g. on No output defined, nothing to do. (#3375)
  • Liquidsoap no longer exits when started as root: it now logs a warning
    instead. Container detection through /proc/1/cgroup does not work under
    cgroup v2, which made the check fire on plain docker run (#3406)
  • Fixed output.file and the other piped outputs ignoring the delay returned by
    reopen_on_error when the failure happened while opening the file: the output
    stays Idle in that case and retried on every streaming cycle (#2437)
  • output.file reports Unix errors, e.g. Permission denied when creating the
    destination directory, as system errors, like it already did for Sys_error
    (#2437)
  • rotate and random name the switch they build internally after themselves,
    e.g. schedule_rotate, instead of leaving it with an anonymous id (#3851)
  • Fixed clock.create documenting and reporting sync modes, "CPU" and
    "unsynced", that it does not accept. The accepted values are "auto",
    "cpu", "none" and "passive"
  • Added Unifier path compression to avoid unbounded deref cost growth (#5257)
  • Fixed unbounded buffer growth (#5287)
  • Fixed sequence dropping the first chunk of a source, along with its
    metadata, when the source it follows leaves no room in the current frame.
    This showed up as the track after a cross/crossfade transition built with
    sequence keeping the previous track's metadata until the next track
    boundary (#5296).
  • Fixed ICY metadata negotiation in output.harbor: the Icy-MetaData request
    header was looked up before normalization, so clients never received
    in-stream metadata (thanks to @uhthomas, #5279)
  • Fixed srt restart logic (#5309)
  • Fixed HLS segment boundaries drifting away from segment_duration: a segment
    closing on a stale split position re-anchored the next boundary on it instead
    of the segment grid. The drift rate depends on the encoder's frame size, so
    variants using different codecs, e.g. HE-AAC and AAC-LC, diverged from each
    other without bound (#5319).
  • Fixed a clock's on_error handler being defeated by the shutdown of the
    source it just contained: putting the failed source to sleep runs its cleanup,
    e.g. output.file.hls closing its current segment when persist_at is set,
    and an error raised there escaped uncaught and killed the process. Shutdown
    errors are logged instead (#5260)
  • Fixed switch and sequence not notifying their new sync source when
    switching child, leaving the clock synced on the source they just left
  • Fixed the type unifier retaining collapsed variables forever, by merging
    unified variables using union by rank (#5342)
  • Fixed Audioscrobbler base url (#5404, @iibaranov-IG)
  • Errors raised by the OCaml code behind a builtin, such as file.ls on an
    unreadable directory, are now runtime errors that try ... catch can handle,
    instead of aborting the script (#5454)
  • Fixed input.harbor socket cleanup (#5479)

Don't miss a new liquidsoap release

NewReleases is sending notifications on new releases.