# “AutoDJ” liquidsoap code for 4.5.0 (updated)

**URL:** <https://discourse.libretime.org/t/autodj-liquidsoap-code-for-4-5-0-updated/1927>\
**Category:** Uncategorized\
**Created:** [July 22, 2025, 2:23pm UTC](https://discourse.libretime.org/t/autodj-liquidsoap-code-for-4-5-0-updated/1927 "2025-07-22T14:23:56Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![NessRadio](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.libretime.org/nessradio/32/839_2.png) [@NessRadio](https://discourse.libretime.org/u/NessRadio)\
**Post date:** [July 22, 2025, 2:23pm UTC](https://discourse.libretime.org/t/autodj-liquidsoap-code-for-4-5-0-updated/1927/1 "2025-07-22T14:23:56Z")

</div>

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

```auto
# 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:

```auto
%include "autodj.liq"

```

**3- Comment (disable) those line in `ls_script.liq`**

```auto
#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.

```auto
# 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 -

```auto
#s = switch(id="switch:blank+schedule",
# track_sensitive=false,
# transitions=[transition_default, transition],
# [({!schedule_streaming}, stream_queue), ({true}, default)]
# )

```

for -

```auto
# 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))

```

---

<div class="post-metadata">

**Author:** ![NessRadio](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.libretime.org/nessradio/32/839_2.png) [@NessRadio](https://discourse.libretime.org/u/NessRadio)\
**Post date:** [July 31, 2025, 1:59pm UTC](https://discourse.libretime.org/t/autodj-liquidsoap-code-for-4-5-0-updated/1927/2 "2025-07-31T13:59:34Z")

</div>

Because it’s impossible to edit my original post I’m sharing an updated **`autodj.liq`** script for LibreTime 4.5.0, where I’ve made a few small tweaks to:

- **Instantly detect new music files:** Lowering reload from 60 to 1 makes Liquidsoap notice new or removed files immediately after playing all tracks, which is useful if you’re swapping music in real time (e.g. uploading fresh tracks to the directory).
- **Remove extended silences between tracks:** Introducing skip\_blank strips out any pauses longer than 4 s under -40 dB, so the stream never stalls on extended silence between tracks.
- **Refine the crossfade thresholds for smoother transitions:** By raising the “high” level from –20 dB to –15 dB, you allow somewhat louder tracks to be considered for overlapping fades, which can produce a smoother handoff on dynamic material.

Feel free to give it a try and let me know what you think!

```auto
# Your jingles
jingles = playlist("/myradio/jingle")

# Your backup playlist
musicsraw = playlist(mode="randomize", reload=1, reload_mode="rounds","/myradio/music")

# Remove any silence > 4 s below -40 dB
musics = skip_blank(musicsraw,
  max_blank = 4.,
  threshold = -40.
)

# 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 = -15.
  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 ?

    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)

```

```auto

```

---

<div class="post-metadata">

**Author:** ![Mladen28](https://avatars.discourse-cdn.com/v4/letter/m/eb9ed0/32.png) [@Mladen28](https://discourse.libretime.org/u/Mladen28)\
**Post date:** [November 3, 2025, 7:31pm UTC](https://discourse.libretime.org/t/autodj-liquidsoap-code-for-4-5-0-updated/1927/3 "2025-11-03T19:31:02Z")

</div>

Hey thanks for you script! Where do I locate my playlist path?

---

<div class="post-metadata">

**Author:** ![NessRadio](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.libretime.org/nessradio/32/839_2.png) [@NessRadio](https://discourse.libretime.org/u/NessRadio)\
**Post date:** [November 7, 2025, 2:04pm UTC](https://discourse.libretime.org/t/autodj-liquidsoap-code-for-4-5-0-updated/1927/4 "2025-11-07T14:04:41Z")

</div>

Hey, glad you found the script useful! 😊  
The playlist path depends on your installation setup — by default, LibreTime uses a standard media or playlist folder, but you can also create your own directory and update the path in the script accordingly. It just needs to point to where your audio files or playlists are stored.
