I’ve extended the default Liquidsoap setup to automatically play a random music playlist whenever no shows are scheduled or a DJ isn’t live. I tested it on LibreTime 4.5.0, and it appears to work just as well on earlier releases. The core steps are:
1- Create autodj.liq
sudo nano /opt/libretime/lib/python3.9/site-packages/libretime_playout/liquidsoap/1.4/autodj.liq
Copy&past the code below and change path and .mp3 accordingly
# Your jingles
jingles = playlist("/myradio/jingle")
# Your backup playlist
musics = playlist(mode="randomize",reload=60,reload_mode="rounds","/myradio/music")
# Your hours time signals
clock = single(id="clock","/myradio/clock/clock.mp3")
# Your security jingle if the AutoDJ drop
security = single("/myradio/offair/offair.mp3")
# 1 jingle every 4 songs
streamradio = rotate(weights=[1,4], [jingles, musics])
# Setting up the crossfade (You do not have to change anything here)
def crossfade (~start_next=5.,~fade_in=3.,
~fade_out=3., ~width=2.,
~conservative=false,s)
high = -20.
medium = -32.
margin = 4.
fade.out = fade.out(type="sin",duration=fade_out)
fade.in = fade.in(type="sin",duration=fade_in)
add = fun (a,b) -> add(normalize=false,[b,a])
log = log(label="crossfade")
def transition(a,b,ma,mb,sa,sb)
list.iter(fun(x)->
log(level=4,"Before: #{x}"),ma)
list.iter(fun(x)->
log(level=4,"After : #{x}"),mb)
if
# If A and B and not too loud and close,
# fully cross-fade them.
a <= medium and
b <= medium and
abs(a - b) <= margin
then
log("Transition: crossed, fade-in, fade-out.")
add(fade.out(sa),fade.in(sb))
elsif
# If B is significantly louder than A,
# only fade-out A.
# We don't want to fade almost silent things,
# ask for >medium.
b >= a + margin and a >= medium and b <= high
then
log("Transition: crossed, fade-out.")
add(fade.out(sa),sb)
elsif
# Do not fade if it's already very low.
b >= a + margin and a <= medium and b <= high
then
log("Transition: crossed, no fade-out.")
add(sa,sb)
elsif
# Opposite as the previous one.
a >= b + margin and b >= medium and a <= high
then
log("Transition: crossed, fade-in.")
add(sa,fade.in(sb))
# What to do with a loud end and
# a quiet beginning ?
# A good idea is to use a jingle to separate
# the two tracks, but that's another story.
else
# Otherwise, A and B are just too loud
# to overlap nicely, or the difference
# between them is too large and
# overlapping would completely mask one
# of them.
log("No transition: just sequencing.")
sequence([sa, sb])
end
end
cross(width=width, duration=start_next,
conservative=conservative,
transition,s)
end
# Enable crossfade between Tracks and Jingles
streamradio = crossfade(start_next=5.,fade_out=3.,fade_in=3.,streamradio)
# Enable every hours time signals
streamradio = add([streamradio, switch([
({0m0s},clock),
])
])
# Metadata updater
def update_metadata(m) =
title = m["title"]
ignore(title)
artist = m["artist"]
ignore(artist)
[("artist","#{artist}"),("title","#{title}")]
end
# Enable AutoDJ with metadata
autodj = fallback(track_sensitive = false,[streamradio,security])
autodj = map_metadata(update_metadata, autodj)
Save your new created autodj.liq
2- Include autodj.liq
in your ls_script.liq
script
sudo nano /opt/libretime/lib/python3.9/site-packages/libretime_playout/liquidsoap/1.4/ls_script.liq
Find %include "ls_lib.liq"
and copy&past just below:
%include "autodj.liq"
3- Comment (disable) those line in ls_script.liq
#default = amplify(id="silence_src", 0.00001, noise())
#def map_message_offline(m) =
# [("title", message_offline())]
#end
#default = map_metadata(id="map_metadata:offline", map_message_offline, default)
#ignore(output.dummy(default, fallible=true))
4- Smooth fade between AutoDJ and shows in ls_script.liq
Copy&past the following code beneath the lines you just commented.
# Whenever no shows are scheduled and no DJ is live, it switches to AutoDJ.
default = autodj
# AutoDJ → scheduled show: 3-second fade-out, then a clean start
def to_schedule(old, new) =
log("TRANSITION → fade-out 3s autodj → Scheduled show start no fade in")
sequence([
fade.final(duration=3.0, old),
new
])
end
# Scheduled show → AutoDJ: 3-second fade-out, then resume AutoDJ
def to_outro(old, new) =
log("TRANSITION → fade-out 3s shows ending → start autodj")
sequence([
fade.final(duration=3.0, old),
fade.initial(duration=3.0, new)
])
end
# fin
5- Update the primary switch in ls_script.liq
Replace -
#s = switch(id="switch:blank+schedule",
# track_sensitive=false,
# transitions=[transition_default, transition],
# [({!schedule_streaming}, stream_queue), ({true}, default)]
# )
for -
# Switch with fade Intro and Outro
s = switch(id="switch:blank+schedule",
track_sensitive=false,
transitions=[to_schedule, to_outro],
[
({ !schedule_streaming }, stream_queue),
({ true }, autodj)
]
)
# → Final output to Icecast (or other)
ignore(output.dummy(s, fallible=true))