savonet / liquidsoap

Liquidsoap is a statically typed scripting general-purpose language with dedicated operators and backend for all thing media, streaming, file generation, automation, HTTP backend and more.
http://liquidsoap.info
GNU General Public License v2.0
1.4k stars 129 forks source link

Song with silence at end makes LS 2.2.0+git@dd50b0729 jump to 100% CPU and plays Icecast fallback #3132

Closed Moonbase59 closed 1 year ago

Moonbase59 commented 1 year ago

Describe the bug Using AzuraCast and Song with silence at end makes LS 2.2.0+git@dd50b0729 jump to 100% CPU and plays Icecast fallback, in spite of using radio = blank.eat(radio).

For some reason, one of my FLACs end at 9:57 but has a total duration of 14:14, the rest is filled with silence.

Seems something odd happens in that case.

To Reproduce See above.

Expected behavior "Eat" the blank and start next song when silence starts.

Version details

Install method AzuraCast

Common issues

The Liquidsoap log doesn’t show much, just stops outputting anything (for 10 minutes now):

2023/06/08 21:40:23 [lang:3] API nextsong - Sending POST request to 'http://127.0.0.1:6010/api/internal/1/liquidsoap/nextsong' with body: 
2023/06/08 21:40:24 [lang:3] API nextsong - Response (200): annotate:title="Walk With Me",artist="The Tea Party",duration="854.00",song_id="2ff3bf1d34382247de6320fe929d6f3c",media_id="151237",playlist_id="194":media:Tagged/Tea Party, The/Tea Party, The - The Edges of Twilight (1995 album, DE)/Tea Party, The - Walk With Me.flac
2023/06/08 21:40:24 [decoder.ffmpeg:3] Requested content-type for "/var/azuracast/stations/niteradio/media/Tagged/Tea Party, The/Tea Party, The - The Edges of Twilight (1995 album, DE)/Tea Party, The - Walk With Me.flac": {audio=pcm(stereo)}
2023/06/08 21:40:24 [decoder.ffmpeg:3] FFmpeg recognizes "/var/azuracast/stations/niteradio/media/Tagged/Tea Party, The/Tea Party, The - The Edges of Twilight (1995 album, DE)/Tea Party, The - Walk With Me.flac" as video: {codec: mjpeg, 500x499, yuvj420p}, audio: {codec: flac, 44100Hz, 2 channel(s)}
2023/06/08 21:40:24 [decoder.ffmpeg:3] Decoded content-type for "/var/azuracast/stations/niteradio/media/Tagged/Tea Party, The/Tea Party, The - The Edges of Twilight (1995 album, DE)/Tea Party, The - Walk With Me.flac": {audio=pcm(stereo)}
2023/06/08 21:40:24 [cross:3] Analysis: -32.532542dB / -40.937606dB (3.99s / 4.01s)
2023/06/08 21:40:24 [lang:3] Using custom crossfade
2023/06/08 21:40:24 [add.198:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [add.197:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [fade_in.198:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [on_track.397:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [on_metadata.198:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [on_track.396:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [cross_after.99:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [buffer.198:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [fade_in.197:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [fade_out.198:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [on_end.99:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [on_track.395:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [on_metadata.197:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [on_track.394:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [cross_before.99:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [buffer.197:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [fade_out.197:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:40:24 [lang:3] API feedback - Sending POST request to 'http://127.0.0.1:6010/api/internal/1/liquidsoap/feedback' with body: {
2023/06/08 21:40:24 [lang:3]   "song_id": "7a3b1f8cbed4bb0ac633f4a689a3058e",
2023/06/08 21:40:24 [lang:3]   "playlist_id": "194",
2023/06/08 21:40:24 [lang:3]   "media_id": "11734"
2023/06/08 21:40:24 [lang:3] }
2023/06/08 21:40:24 [lang:3] API feedback - Response (200): true
2023/06/08 21:46:24 [decoder:2] Decoding "/var/azuracast/stations/niteradio/media/Tagged/Jane/Jane - Lady/Jane - Music Machine.mp3" ended: Ffmpeg_decoder.End_of_file.
2023/06/08 21:46:25 [next_song:3] Prepared "/var/azuracast/stations/niteradio/media/Tagged/Tea Party, The/Tea Party, The - The Edges of Twilight (1995 album, DE)/Tea Party, The - Walk With Me.flac" (RID 196).
2023/06/08 21:46:25 [cue_next_song:3] Cueing in...
2023/06/08 21:46:25 [lang:3] API nextsong - Sending POST request to 'http://127.0.0.1:6010/api/internal/1/liquidsoap/nextsong' with body: 
2023/06/08 21:46:25 [cross:3] Analysis: -102.026739dB / -72.646218dB (3.98s / 3.98s)
2023/06/08 21:46:25 [lang:3] Using custom crossfade
2023/06/08 21:46:25 [add.200:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [add.199:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [fade_in.200:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [on_track.401:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [on_metadata.200:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [on_track.400:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [cross_after.100:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [buffer.200:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [fade_in.199:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [fade_out.200:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [on_end.100:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [on_track.399:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [on_metadata.199:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [on_track.398:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [cross_before.100:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [buffer.199:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [fade_out.199:3] Content type is {audio=pcm(stereo)}.
2023/06/08 21:46:25 [lang:3] API feedback - Sending POST request to 'http://127.0.0.1:6010/api/internal/1/liquidsoap/feedback' with body: {
2023/06/08 21:46:25 [lang:3]   "song_id": "2ff3bf1d34382247de6320fe929d6f3c",
2023/06/08 21:46:25 [lang:3]   "playlist_id": "194",
2023/06/08 21:46:25 [lang:3]   "media_id": "151237"
2023/06/08 21:46:25 [lang:3] }
2023/06/08 21:46:25 [lang:3] API nextsong - Response (200): annotate:title="promo-electronic",artist="Nite Radio",duration="10.00",song_id="4bd88f094d5ca04d2fdeb74dbffd33de",media_id="160254",liq_cross_duration="0.10",liq_fade_in="0.10",liq_fade_out="0.10",playlist_id="204",jingle_mode="true":media:Other/Jingles/Programm/promo-electronic.mp3
[mp3 @ 0x7f53c00265c0] Estimating duration from bitrate, this may be inaccurate
2023/06/08 21:46:25 [decoder.ffmpeg:3] Requested content-type for "/var/azuracast/stations/niteradio/media/Other/Jingles/Programm/promo-electronic.mp3": {audio=pcm(stereo)}
2023/06/08 21:46:25 [decoder.ffmpeg:3] FFmpeg recognizes "/var/azuracast/stations/niteradio/media/Other/Jingles/Programm/promo-electronic.mp3" as audio: {codec: mp3, 44100Hz, 2 channel(s)}
2023/06/08 21:46:25 [decoder.ffmpeg:3] Decoded content-type for "/var/azuracast/stations/niteradio/media/Other/Jingles/Programm/promo-electronic.mp3": {audio=pcm(stereo)}
[mp3 @ 0x7f53c00265c0] Estimating duration from bitrate, this may be inaccurate
2023/06/08 21:46:25 [lang:3] API feedback - Response (200): true
[mp3 @ 0x7f53bc055b00] Estimating duration from bitrate, this may be inaccurate
2023/06/08 22:00:04 [decoder.ffmpeg:3] Requested content-type for "/var/azuracast/stations/niteradio/media/time/time.de-DE.mp3": {audio=pcm(stereo)}
2023/06/08 22:00:04 [decoder.ffmpeg:3] FFmpeg recognizes "/var/azuracast/stations/niteradio/media/time/time.de-DE.mp3" as audio: {codec: mp3, 44100Hz, 2 channel(s)}
2023/06/08 22:00:04 [decoder.ffmpeg:3] Decoded content-type for "/var/azuracast/stations/niteradio/media/time/time.de-DE.mp3": {audio=pcm(stereo)}
[mp3 @ 0x7f53bc055b00] Estimating duration from bitrate, this may be inaccurate

The song in question was /var/azuracast/stations/niteradio/media/Tagged/Tea Party, The/Tea Party, The - The Edges of Twilight (1995 album, DE)/Tea Party, The - Walk With Me.flac.

The following jingles and next song(s) never played.

Moonbase59 commented 1 year ago

Forgot my liquidsoap.liq:

# WARNING! This file is automatically generated by AzuraCast.
# Do not update it directly!

init.daemon.set(false)
init.daemon.pidfile.path.set("/var/azuracast/stations/niteradio/config/liquidsoap.pid")

log.stdout.set(true)
log.file.set(false)

settings.server.log.level.set(4)

settings.server.socket.set(true)
settings.server.socket.permissions.set(0o660)
settings.server.socket.path.set("/var/azuracast/stations/niteradio/config/liquidsoap.sock")

settings.harbor.bind_addrs.set(["0.0.0.0"])
settings.encoder.metadata.export.set(["artist","title","album","song"])

environment.set("TZ", "Europe/Berlin")

autodj_is_loading = ref(true)
ignore(autodj_is_loading)

autodj_ping_attempts = ref(0)
ignore(autodj_ping_attempts)

# Track live-enabled status.
live_enabled = ref(false)
ignore(live_enabled)

# Track live transition for crossfades.
to_live = ref(false)
ignore(to_live)

azuracast_api_url = "http://127.0.0.1:6010/api/internal/1/liquidsoap"
azuracast_api_key = "(PASSWORD)"

def azuracast_api_call(~timeout=2.0, url, payload) =
    full_url = "#{azuracast_api_url}/#{url}"

    log("API #{url} - Sending POST request to '#{full_url}' with body: #{payload}")
    try
        response = http.post(full_url,
            headers=[
                ("Content-Type", "application/json"),
                ("User-Agent", "Liquidsoap AzuraCast"),
                ("X-Liquidsoap-Api-Key", "#{azuracast_api_key}")
            ],
            timeout=timeout,
            data=payload
        )

        log("API #{url} - Response (#{response.status_code}): #{response}")
        "#{response}"
    catch err do
        log("API #{url} - Error: #{error.kind(err)} - #{error.message(err)}")
        "false"
    end
end

station_media_dir = "/var/azuracast/stations/niteradio/media"
def azuracast_media_protocol(~rlog=_,~maxtime=_,arg) =
    ["#{station_media_dir}/#{arg}"]
end

protocol.add(
    "media",
    azuracast_media_protocol,
    doc="Pull files from AzuraCast media directory.",
    syntax="media:uri"
)

playlist_st__patricks_day = playlist(id="playlist_st__patricks_day",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_st__patricks_day.m3u")
playlist_st__patricks_day = cue_cut(id="cue_playlist_st__patricks_day", playlist_st__patricks_day)

playlist_1970s_rock = playlist(id="playlist_1970s_rock",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_1970s_rock.m3u")
playlist_1970s_rock = cue_cut(id="cue_playlist_1970s_rock", playlist_1970s_rock)

playlist_deep_trance = playlist(id="playlist_deep_trance",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_deep_trance.m3u")
playlist_deep_trance = cue_cut(id="cue_playlist_deep_trance", playlist_deep_trance)

playlist_disco = playlist(id="playlist_disco",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_disco.m3u")
playlist_disco = cue_cut(id="cue_playlist_disco", playlist_disco)

playlist_easy_listening = playlist(id="playlist_easy_listening",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_easy_listening.m3u")
playlist_easy_listening = cue_cut(id="cue_playlist_easy_listening", playlist_easy_listening)

playlist_electro = playlist(id="playlist_electro",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_electro.m3u")
playlist_electro = cue_cut(id="cue_playlist_electro", playlist_electro)

playlist_folk_rock = playlist(id="playlist_folk_rock",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_folk_rock.m3u")
playlist_folk_rock = cue_cut(id="cue_playlist_folk_rock", playlist_folk_rock)

playlist_glam_rock = playlist(id="playlist_glam_rock",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_glam_rock.m3u")
playlist_glam_rock = cue_cut(id="cue_playlist_glam_rock", playlist_glam_rock)

playlist_gothic2C_darkwave = playlist(id="playlist_gothic2C_darkwave",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_gothic2C_darkwave.m3u")
playlist_gothic2C_darkwave = cue_cut(id="cue_playlist_gothic2C_darkwave", playlist_gothic2C_darkwave)

playlist_heavy_metal = playlist(id="playlist_heavy_metal",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_heavy_metal.m3u")
playlist_heavy_metal = cue_cut(id="cue_playlist_heavy_metal", playlist_heavy_metal)

playlist_house = playlist(id="playlist_house",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_house.m3u")
playlist_house = cue_cut(id="cue_playlist_house", playlist_house)

playlist_irish_pub = playlist(id="playlist_irish_pub",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_irish_pub.m3u")
playlist_irish_pub = cue_cut(id="cue_playlist_irish_pub", playlist_irish_pub)

playlist_lounge = playlist(id="playlist_lounge",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_lounge.m3u")
playlist_lounge = cue_cut(id="cue_playlist_lounge", playlist_lounge)

playlist_motown2C_philly_soul = playlist(id="playlist_motown2C_philly_soul",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_motown2C_philly_soul.m3u")
playlist_motown2C_philly_soul = cue_cut(id="cue_playlist_motown2C_philly_soul", playlist_motown2C_philly_soul)

playlist_new_age = playlist(id="playlist_new_age",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_new_age.m3u")
playlist_new_age = cue_cut(id="cue_playlist_new_age", playlist_new_age)

playlist_oldies = playlist(id="playlist_oldies",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_oldies.m3u")
playlist_oldies = cue_cut(id="cue_playlist_oldies", playlist_oldies)

playlist_power_metal = playlist(id="playlist_power_metal",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_power_metal.m3u")
playlist_power_metal = cue_cut(id="cue_playlist_power_metal", playlist_power_metal)

playlist_schlager = playlist(id="playlist_schlager",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_schlager.m3u")
playlist_schlager = cue_cut(id="cue_playlist_schlager", playlist_schlager)

playlist_soul = playlist(id="playlist_soul",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_soul.m3u")
playlist_soul = cue_cut(id="cue_playlist_soul", playlist_soul)

playlist_southern_rock = playlist(id="playlist_southern_rock",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_southern_rock.m3u")
playlist_southern_rock = cue_cut(id="cue_playlist_southern_rock", playlist_southern_rock)

playlist_synthpop = playlist(id="playlist_synthpop",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_synthpop.m3u")
playlist_synthpop = cue_cut(id="cue_playlist_synthpop", playlist_synthpop)

playlist_gr___classic_rock = playlist(id="playlist_gr___classic_rock",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_gr___classic_rock.m3u")
playlist_gr___classic_rock = cue_cut(id="cue_playlist_gr___classic_rock", playlist_gr___classic_rock)

playlist_electronic = playlist(id="playlist_electronic",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_electronic.m3u")
playlist_electronic = cue_cut(id="cue_playlist_electronic", playlist_electronic)

playlist_hard_rock = playlist(id="playlist_hard_rock",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_hard_rock.m3u")
playlist_hard_rock = cue_cut(id="cue_playlist_hard_rock", playlist_hard_rock)

playlist_pop = playlist(id="playlist_pop",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_pop.m3u")
playlist_pop = cue_cut(id="cue_playlist_pop", playlist_pop)

playlist_prog_rock = playlist(id="playlist_prog_rock",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_prog_rock.m3u")
playlist_prog_rock = cue_cut(id="cue_playlist_prog_rock", playlist_prog_rock)

playlist_gr___folk_rock = playlist(id="playlist_gr___folk_rock",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_gr___folk_rock.m3u")
playlist_gr___folk_rock = cue_cut(id="cue_playlist_gr___folk_rock", playlist_gr___folk_rock)

playlist_gr___hard_rock = playlist(id="playlist_gr___hard_rock",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_gr___hard_rock.m3u")
playlist_gr___hard_rock = cue_cut(id="cue_playlist_gr___hard_rock", playlist_gr___hard_rock)

playlist_zeitansage_volle_stunde = playlist(id="playlist_zeitansage_volle_stunde",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_zeitansage_volle_stunde.m3u")
playlist_zeitansage_volle_stunde = cue_cut(id="cue_playlist_zeitansage_volle_stunde", playlist_zeitansage_volle_stunde)
playlist_zeitansage_volle_stunde = drop_metadata(playlist_zeitansage_volle_stunde)

playlist_station_id = playlist(id="playlist_station_id",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_station_id.m3u")
playlist_station_id = cue_cut(id="cue_playlist_station_id", playlist_station_id)
playlist_station_id = drop_metadata(playlist_station_id)

playlist_gothic2C_darkwave___promo = playlist(id="playlist_gothic2C_darkwave___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_gothic2C_darkwave___promo.m3u")
playlist_gothic2C_darkwave___promo = cue_cut(id="cue_playlist_gothic2C_darkwave___promo", playlist_gothic2C_darkwave___promo)
playlist_gothic2C_darkwave___promo = drop_metadata(playlist_gothic2C_darkwave___promo)

playlist_electro___promo = playlist(id="playlist_electro___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_electro___promo.m3u")
playlist_electro___promo = cue_cut(id="cue_playlist_electro___promo", playlist_electro___promo)
playlist_electro___promo = drop_metadata(playlist_electro___promo)

playlist_pop___promo = playlist(id="playlist_pop___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_pop___promo.m3u")
playlist_pop___promo = cue_cut(id="cue_playlist_pop___promo", playlist_pop___promo)
playlist_pop___promo = drop_metadata(playlist_pop___promo)

playlist_easy_listening___promo = playlist(id="playlist_easy_listening___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_easy_listening___promo.m3u")
playlist_easy_listening___promo = cue_cut(id="cue_playlist_easy_listening___promo", playlist_easy_listening___promo)
playlist_easy_listening___promo = drop_metadata(playlist_easy_listening___promo)

playlist_schlager___promo = playlist(id="playlist_schlager___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_schlager___promo.m3u")
playlist_schlager___promo = cue_cut(id="cue_playlist_schlager___promo", playlist_schlager___promo)
playlist_schlager___promo = drop_metadata(playlist_schlager___promo)

playlist_synthpop___promo = playlist(id="playlist_synthpop___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_synthpop___promo.m3u")
playlist_synthpop___promo = cue_cut(id="cue_playlist_synthpop___promo", playlist_synthpop___promo)
playlist_synthpop___promo = drop_metadata(playlist_synthpop___promo)

playlist_promo_mittwoch = playlist(id="playlist_promo_mittwoch",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_promo_mittwoch.m3u")
playlist_promo_mittwoch = cue_cut(id="cue_playlist_promo_mittwoch", playlist_promo_mittwoch)
playlist_promo_mittwoch = drop_metadata(playlist_promo_mittwoch)

playlist_prog_rock___promo = playlist(id="playlist_prog_rock___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_prog_rock___promo.m3u")
playlist_prog_rock___promo = cue_cut(id="cue_playlist_prog_rock___promo", playlist_prog_rock___promo)
playlist_prog_rock___promo = drop_metadata(playlist_prog_rock___promo)

playlist_electronic___promo = playlist(id="playlist_electronic___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_electronic___promo.m3u")
playlist_electronic___promo = cue_cut(id="cue_playlist_electronic___promo", playlist_electronic___promo)
playlist_electronic___promo = drop_metadata(playlist_electronic___promo)

playlist_promo_freitag = playlist(id="playlist_promo_freitag",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_promo_freitag.m3u")
playlist_promo_freitag = cue_cut(id="cue_playlist_promo_freitag", playlist_promo_freitag)
playlist_promo_freitag = drop_metadata(playlist_promo_freitag)

playlist_promo_samstag = playlist(id="playlist_promo_samstag",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_promo_samstag.m3u")
playlist_promo_samstag = cue_cut(id="cue_playlist_promo_samstag", playlist_promo_samstag)
playlist_promo_samstag = drop_metadata(playlist_promo_samstag)

playlist_new_age___promo = playlist(id="playlist_new_age___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_new_age___promo.m3u")
playlist_new_age___promo = cue_cut(id="cue_playlist_new_age___promo", playlist_new_age___promo)
playlist_new_age___promo = drop_metadata(playlist_new_age___promo)

playlist_lounge___promo = playlist(id="playlist_lounge___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_lounge___promo.m3u")
playlist_lounge___promo = cue_cut(id="cue_playlist_lounge___promo", playlist_lounge___promo)
playlist_lounge___promo = drop_metadata(playlist_lounge___promo)

playlist_house___promo = playlist(id="playlist_house___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_house___promo.m3u")
playlist_house___promo = cue_cut(id="cue_playlist_house___promo", playlist_house___promo)
playlist_house___promo = drop_metadata(playlist_house___promo)

playlist_deep_trance___promo = playlist(id="playlist_deep_trance___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_deep_trance___promo.m3u")
playlist_deep_trance___promo = cue_cut(id="cue_playlist_deep_trance___promo", playlist_deep_trance___promo)
playlist_deep_trance___promo = drop_metadata(playlist_deep_trance___promo)

playlist_irish_pub___promo = playlist(id="playlist_irish_pub___promo",mime_type="audio/x-mpegurl",mode="randomize",reload_mode="watch","/var/azuracast/stations/niteradio/playlists/playlist_irish_pub___promo.m3u")
playlist_irish_pub___promo = cue_cut(id="cue_playlist_irish_pub___promo", playlist_irish_pub___promo)
playlist_irish_pub___promo = drop_metadata(playlist_irish_pub___promo)

# Standard Playlists
radio = random(id="standard_playlists", weights=[3, 1, 1], [playlist_gr___classic_rock, playlist_gr___folk_rock, playlist_gr___hard_rock])

# Standard Schedule Switches

radio = switch(id="schedule_switch", track_sensitive=true, [ ({ (18h0m-23h59m59s) or (00h00m-0h0m) }, playlist_st__patricks_day), ({ (3w) and 18h0m-20h0m }, playlist_1970s_rock), ({ ((0w) and 22h0m-23h59m59s) or ((1w) and 00h00m-0h0m) }, playlist_deep_trance), ({ (6w) and 20h0m-22h0m }, playlist_disco), ({ 12h0m-15h0m }, playlist_easy_listening), ({ ((2w) and 22h0m-23h59m59s) or ((3w) and 00h00m-0h0m) }, playlist_electro), ({ ((1w) and 22h0m-23h59m59s) or ((2w) and 00h00m-0h0m) }, playlist_folk_rock), ({ (3w) and 20h0m-22h0m }, playlist_glam_rock), ({ (2w) and 20h0m-22h0m }, playlist_gothic2C_darkwave), ({ (5w) and 20h0m-22h0m }, playlist_heavy_metal), ({ (0w) and 20h0m-22h0m }, playlist_house), ({ (1w) and 20h0m-22h0m }, playlist_irish_pub), ({ (0w) and 17h0m-20h0m }, playlist_lounge), ({ (6w) and 18h0m-20h0m }, playlist_motown2C_philly_soul), ({ (0w) and 15h0m-17h0m }, playlist_new_age), ({ (1w or 2w or 3w or 4w or 5w or 6w) and 16h0m-17h0m }, playlist_oldies), ({ ((5w) and 22h0m-23h59m59s) or ((6w) and 00h00m-0h0m) }, playlist_power_metal), ({ (1w or 2w or 3w or 4w or 5w or 6w) and 15h0m-16h0m }, playlist_schlager), ({ (0w) and 6h0m-12h0m }, playlist_schlager), ({ ((6w) and 22h0m-23h59m59s) or ((0w) and 00h00m-0h0m) }, playlist_soul), ({ ((3w) and 22h0m-23h59m59s) or ((4w) and 00h00m-0h0m) }, playlist_southern_rock), ({ (2w) and 18h0m-20h0m }, playlist_synthpop), ({ ((4w) and 22h0m-23h59m59s) or ((5w) and 00h00m-0h0m) }, playlist_electronic), ({ (5w) and 18h0m-20h0m }, playlist_hard_rock), ({ 6h0m-12h0m }, playlist_pop), ({ (4w) and 20h0m-22h0m }, playlist_prog_rock), ({ (2w) and 20h0m-22h0m }, fallback(track_sensitive=true, [delay(2700., playlist_gothic2C_darkwave___promo), radio])), ({ ((2w) and 22h0m-23h59m59s) or ((3w) and 00h00m-0h0m) }, fallback(track_sensitive=true, [delay(2700., playlist_electro___promo), radio])), ({ 6h0m-12h0m }, fallback(track_sensitive=true, [delay(2700., playlist_pop___promo), radio])), ({ 12h0m-15h0m }, fallback(track_sensitive=true, [delay(2700., playlist_easy_listening___promo), radio])), ({ (1w or 2w or 3w or 4w or 5w or 6w) and 15h0m-17h0m }, fallback(track_sensitive=true, [delay(2700., playlist_schlager___promo), radio])), ({ (2w) and 18h0m-20h0m }, fallback(track_sensitive=true, [delay(2700., playlist_synthpop___promo), radio])), ({ ((3w) and 18h0m-23h59m59s) or ((4w) and 00h00m-0h0m) }, fallback(track_sensitive=true, [delay(2700., playlist_promo_mittwoch), radio])), ({ (4w) and 20h0m-22h0m }, fallback(track_sensitive=true, [delay(2700., playlist_prog_rock___promo), radio])), ({ ((4w) and 22h0m-23h59m59s) or ((5w) and 00h00m-0h0m) }, fallback(track_sensitive=true, [delay(2700., playlist_electronic___promo), radio])), ({ ((5w) and 18h0m-23h59m59s) or ((6w) and 00h00m-0h0m) }, fallback(track_sensitive=true, [delay(2700., playlist_promo_freitag), radio])), ({ ((6w) and 18h0m-23h59m59s) or ((0w) and 00h00m-0h0m) }, fallback(track_sensitive=true, [delay(2700., playlist_promo_samstag), radio])), ({ (0w) and 15h0m-17h0m }, fallback(track_sensitive=true, [delay(2700., playlist_new_age___promo), radio])), ({ (0w) and 17h0m-20h0m }, fallback(track_sensitive=true, [delay(2700., playlist_lounge___promo), radio])), ({ (0w) and 20h0m-22h0m }, fallback(track_sensitive=true, [delay(2700., playlist_house___promo), radio])), ({ ((0w) and 22h0m-23h59m59s) or ((1w) and 00h00m-0h0m) }, fallback(track_sensitive=true, [delay(2700., playlist_deep_trance___promo), radio])), ({ ((1w) and 20h0m-23h59m59s) or ((2w) and 00h00m-0h0m) }, fallback(track_sensitive=true, [delay(2700., playlist_irish_pub___promo), radio])), ({true}, radio) ])

# Once per x Minutes Playlists
radio = fallback(track_sensitive=true, [delay(1800., playlist_station_id), radio])

# Interrupting Schedule Switches

radio = switch(id="schedule_switch", track_sensitive=false, [ ({ 0m }, playlist_zeitansage_volle_stunde), ({true}, radio) ])

# AutoDJ Next Song Script
def autodj_next_song() =
    response = azuracast_api_call(
        "nextsong",
        ""
    )
    if (response == "") or (response == "false") then
        null()
    else
        r = request.create(response)
        if request.resolve(r) then
            r
        else
            null()
       end
    end
end

# Delayed ping for AutoDJ Next Song
def wait_for_next_song(autodj)
    autodj_ping_attempts.set(autodj_ping_attempts() + 1)

    if source.is_ready(autodj) then
        log("AutoDJ is ready!")
        autodj_is_loading.set(false)
        -1.0
    elsif autodj_ping_attempts() > 200 then
        log("AutoDJ could not be initialized within the specified timeout.")
        autodj_is_loading.set(false)
        -1.0
    else
        0.5
    end
end

dynamic = request.dynamic(id="next_song", timeout=20.0, retry_delay=10., autodj_next_song)
dynamic = cue_cut(id="cue_next_song", dynamic)

dynamic_startup = fallback(
    id = "dynamic_startup",
    track_sensitive = false,
    [
        dynamic,
        source.available(
            blank(id = "autodj_startup_blank", duration = 120.),
            predicate.activates({autodj_is_loading()})
        )
    ]
)
radio = fallback(id="autodj_fallback", track_sensitive = true, [dynamic_startup, radio])

ref_dynamic = ref(dynamic);
thread.run.recurrent(delay=0.25, { wait_for_next_song(ref_dynamic()) })

requests = request.queue(id="requests")
requests = cue_cut(id="cue_requests", requests)
radio = fallback(id="requests_fallback", track_sensitive = true, [requests, radio])

interrupting_queue = request.queue(id="interrupting_requests")
interrupting_queue = cue_cut(id="cue_interrupting_requests", interrupting_queue)
radio = fallback(id="interrupting_fallback", track_sensitive = false, [interrupting_queue, radio])

def add_skip_command(s) =
    def skip(_) =
        source.skip(s)
        "Done!"
    end

    server.register(namespace="radio", usage="skip", description="Skip the current song.", "skip",skip)
end

add_skip_command(radio)

# Custom Configuration (Specified in Station Profile)
# Be sure to have ReplayGain applied before crossing.
radio = amplify(1.,override="replaygain_track_gain",radio)
radio = blank.eat(radio)

def live_aware_crossfade(old, new) =
    if to_live() then
        # If going to the live show, play a simple sequence
        sequence([fade.out(old.source),fade.in(new.source)])
    else
        # Otherwise, use the smart transition
        log("Using custom crossfade")
        cross.simple(old.source, new.source, fade_in=0.00, fade_out=3.50)
    end
end

radio = cross(minimum=0., duration=4.00, width=2.00, live_aware_crossfade, radio)

def live_aware_crossfade(old, new) =
    if to_live() then
        # If going to the live show, play a simple sequence
        sequence([fade.out(old.source),fade.in(new.source)])
    else
        # Otherwise, use the smart transition
        cross.simple(old.source, new.source, fade_in=2.00, fade_out=2.00)
    end
end

radio = cross(minimum=0., duration=3.00, live_aware_crossfade, radio)

# DJ Authentication
last_authenticated_dj = ref("")
live_dj = ref("")

def dj_auth(login) =
    auth_info =
        if (login.user == "source" or login.user == "") and (string.match(pattern="(:|,)+", login.password)) then
            auth_string = string.split(separator="(:|,)", login.password)
            {user = list.nth(default="", auth_string, 0),
            password = list.nth(default="", auth_string, 2)}
        else
            {user = login.user, password = login.password}
        end

    response = azuracast_api_call(
        timeout=5.0,
        "auth",
        json.stringify(auth_info)
    )

    if (response == "true") then
        last_authenticated_dj.set(auth_info.user)
        true
    else
        false
    end
end

def live_connected(header) =
    dj = last_authenticated_dj()
    log("DJ Source connected! Last authenticated DJ: #{dj} - #{header}")

    live_enabled.set(true)
    live_dj.set(dj)

    _ = azuracast_api_call(
        timeout=5.0,
        "djon",
        json.stringify({user = dj})
    )
end

def live_disconnected() =
    _ = azuracast_api_call(
        timeout=5.0,
        "djoff",
        json.stringify({user = live_dj()})
    )

    live_enabled.set(false)
    live_dj.set("")
end

# A Pre-DJ source of radio that can be broadcast if needed',
radio_without_live = radio
ignore(radio_without_live)

# Live Broadcasting
live = input.harbor("/live", id = "input_streamer", port = 8005, auth = dj_auth, icy = true, icy_metadata_charset = "UTF-8", metadata_charset = "UTF-8", on_connect = live_connected, on_disconnect = live_disconnected, buffer = 5.00, max = 10.00)

def insert_missing(m) =
    if m == [] then
        [("title", "Live Broadcast"), ("is_live", "true")]
    else
        [("is_live", "true")]
    end
end
live = metadata.map(insert_missing, live)

radio = fallback(id="live_fallback", replay_metadata=true, [live, radio])

# Skip non-live track when live DJ goes live.
def check_live() =
    if live.is_ready() then
        if not to_live() then
            to_live.set(true)
            radio_without_live.skip()
        end
    else
        to_live.set(false)
    end
end

# Continuously check on live.
radio = source.on_frame(radio, check_live)

# Allow for Telnet-driven insertion of custom metadata.
radio = server.insert_metadata(id="custom_metadata", radio)

# Apply amplification metadata (if supplied)
radio = amplify(override="liq_amplify", 1., radio)

error_file = single(id="error_jingle", "/usr/local/share/icecast/web/error.mp3")

def tag_error_file(m) =
    ignore(m)
    [("is_error_file", "true")]
end
error_file = metadata.map(tag_error_file, error_file)

radio = fallback(id="safe_fallback", track_sensitive = false, [radio, error_file])

# Send metadata changes back to AzuraCast
last_title = ref("")
last_artist = ref("")

def metadata_updated(m) =
    def f() =
        if (m["is_error_file"] != "true") then
            if (m["title"] != last_title() or m["artist"] != last_artist()) then
                last_title.set(m["title"])
                last_artist.set(m["artist"])

                j = json()

                if (m["song_id"] != "") then
                    j.add("song_id", m["song_id"])
                    j.add("media_id", m["media_id"])
                    j.add("playlist_id", m["playlist_id"])
                else
                    j.add("artist", m["artist"])
                    j.add("title", m["title"])
                end

                _ = azuracast_api_call(
                    "feedback",
                    json.stringify(j)
                )
            end
        end
    end

    thread.run(f)
end

radio.on_metadata(metadata_updated)

# Handle "Jingle Mode" tracks by replaying the previous metadata.
last_metadata = ref([])
def handle_jingle_mode(m) =
    if (m["jingle_mode"] == "true") then
        last_metadata()
    else
        last_metadata.set(m)
        m
    end
end

radio = metadata.map(update=false, strip=true, handle_jingle_mode, radio)

# Local Broadcasts
output.icecast(%mp3(samplerate=44100, stereo=true, bitrate=128, id3v2=true), id="local_1", host = "127.0.0.1", port = 8000, password = "(PASSWORD)", mount = "/radio.mp3", name = "Nite Radio", description = "Non-public test & evaluation server only", genre = "Various", url = "https://radio.niteradio.net/public/niteradio", public = false, encoding = "UTF-8", radio)
output.icecast(%fdkaac(channels=2, samplerate=44100, bitrate=128, afterburner=false, aot="mpeg4_aac_lc", sbr_mode=true), id="local_2", host = "127.0.0.1", port = 8000, password = "(PASSWORD)", mount = "/radio.aac", name = "Nite Radio", description = "Non-public test & evaluation server only", genre = "Various", url = "https://radio.niteradio.net/public/niteradio", public = false, encoding = "UTF-8", radio)

# HLS Broadcasting
aac_lofi = %ffmpeg(
    format="mpegts",
    %audio(
        codec="aac",
        samplerate=44100,
        channels=2,
        b="48k",
        profile="aac_low"
    )
)
aac_midfi = %ffmpeg(
    format="mpegts",
    %audio(
        codec="aac",
        samplerate=44100,
        channels=2,
        b="96k",
        profile="aac_low"
    )
)
aac_hifi = %ffmpeg(
    format="mpegts",
    %audio(
        codec="aac",
        samplerate=44100,
        channels=2,
        b="192k",
        profile="aac_low"
    )
)
hls_streams = [("aac_lofi", aac_lofi), ("aac_midfi", aac_midfi), ("aac_hifi", aac_hifi)]

def hls_segment_name(~position,~extname,stream_name) =
    timestamp = int_of_float(time())
    duration = 4
    "#{stream_name}_#{duration}_#{timestamp}_#{position}.#{extname}"
end

output.file.hls(playlist="live.m3u8",
    segment_duration=4.0,
    segments=5,
    segments_overhead=2,
    segment_name=hls_segment_name,
    persist_at="/var/azuracast/stations/niteradio/config/hls.config",
    "/var/azuracast/stations/niteradio/hls",
    hls_streams,
    radio
)

# Remote Relays
toots commented 1 year ago

Thank you for reporting. The code for blank.eat had not been updated ever since we introduced clocks and broke with the recent tightening up of clocks. This should be fixed in https://github.com/savonet/liquidsoap/pull/3133 which will be merged once it passes the CI.

Moonbase59 commented 1 year ago

@toots Build failed, can you fix that? Thanks in advance!

Moonbase59 commented 1 year ago

Confirm blank.eat() works as intended in LS 2.2.0+git@50cc747f6. It does use quite a bit CPU when "eating" longer silence parts (went to ~80% shortly), but quickly goes down again, and playout is fine.

Thanks for fixing!

toots commented 1 year ago

Great. Yes, increased CPU usage is to be expected. Basically the operator goes into fast forward mode until there's no blank.