Liquidsoap example script for smart playlist (kind of)
  • Hi !

    I've just made a modification to /usr/lib/airtime/pypo/bin/liquidsoap_scripts/ls_script.liq and wanted to share it with you. Works well on Airtime 2.0.1

    Modifications are HIGHTLIGHTED

    Keep in mind that any upgrade of Airtime will destroy this script... Keep it in a safe place.

    This script will play stored mp3s in folders depending of time of the day / day of the week + jingle management with weight function when nothing is on air on Airtime. There is also NRJ effect on the output (loud and fat compression, it suits our kind of music).

    Big up to Airtime team for this brillant work, I can't wait for smart playlists and live harbor features.

    Here we go and VIVE LA BRETAGNE.

    Kahuitel aka K8L (L'Eclectic Webradio)


    %include "library/pervasives.liq"
    %include "/etc/airtime/liquidsoap.cfg"

    set("log.file.path", log_file)
    set("log.stdout", true)
    set("server.telnet", true)
    set("server.telnet.port", 1234)

    time = ref string_of(gettimeofday())

    queue = audio_to_stereo(request.queue(id="queue", length=0.5))
    queue = cue_cut(queue)

    pypo_data = ref '0'
    web_stream_enabled = ref false
    stream_metadata_type = ref 0
    station_name = ref ''
    show_name = ref ''

    s1_connected = ref ''
    s2_connected = ref ''
    s3_connected = ref ''
    s1_namespace = ref ''
    s2_namespace = ref ''
    s3_namespace = ref ''

    %include "ls_lib.liq"

    server.register(namespace="vars", "pypo_data", fun (s) -> begin pypo_data := s "Done" end)
    server.register(namespace="vars", "web_stream_enabled", fun (s) -> begin web_stream_enabled := (s == "true") string_of(!web_stream_enabled) end)
    server.register(namespace="vars", "stream_metadata_type", fun (s) -> begin stream_metadata_type := int_of_string(s) s end)
    server.register(namespace="vars", "show_name", fun (s) -> begin show_name := s s end)
    server.register(namespace="vars", "station_name", fun (s) -> begin station_name := s s end)
    server.register(namespace="vars", "bootup_time", fun (s) -> begin time := s s end)
    server.register(namespace="streams", "connection_status", fun (s) -> begin "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected}" end)


    # START MODIFICATION
    # default = amplify(0.00001, noise())
    # default = rewrite_metadata([("artist","Airtime"), ("title", "offline")],default)
    hardcore = playlist(mode='random', "/var/www/downloads/HARDCORE")
    breakcore = playlist(mode='random', "/var/www/downloads/BREAKCORE")
    idm = playlist(mode='random', "/var/www/web/downloads/IDM - EXPERIMENTAL")
    jungle = playlist(mode='random', "/var/www/web/downloads/JUNGLE - DRUM AND BASS")
    speedcore = playlist(mode='random', "/var/www/web/downloads/SPEEDCORE - NOIZE")
    techno = playlist(mode='random', "/var/www/web/downloads/TECHNO - BREAKBEAT - BOOTY")
    podcasts = playlist(mode='random', "/var/www/web/downloads/PODCASTS")
    jingles = playlist(mode='random', "/var/www/web/jingles_radio")

    # Schedule
        weekmorning = random(weights = [2, 1, 1], [jingles,idm,techno])
        weekafternoon = random(weights = [2, 1, 3, 2], [jingles,idm,jungle,techno])
        weeknights = random(weights = [2, 1], [jingles,podcasts])
        fridaymorning = random(weights = [2, 1, 1], [jingles,idm,techno])
        fridayafternoon = random(weights = [2, 1, 4, 3], [jingles,idm,jungle,techno])
        fridaynight = random(weights = [2, 2, 2, 3, 1], [jingles,hardcore,breakcore,jungle,techno])
        saturdaymorning = random(weights = [2, 3, 2, 1], [jingles,idm,jungle,techno])
        saturdayafternoon = random(weights = [2, 3, 2], [jingles,jungle,techno])
        saturdaynight = random(weights = [2, 3, 3, 1, 3, 1], [jingles,hardcore,breakcore,jungle,speedcore,techno])
        sundaymorning = random(weights = [2, 1], [jingles,idm])
        sundayafternoon = random(weights = [2, 1, 2], [jingles,idm,techno])
        sundaynight = random(weights = [2, 3, 1, 2], [jingles,idm,techno,jungle])

        default=fallback([
            switch([
                ({ (1w or 2w or 3w or 4w) and 6h-13h}, weekmorning),
                ({ (1w or 2w or 3w or 4w) and 13h-20h}, weekafternoon),
                ({ (1w or 2w or 3w or 4w) and 20h-23h59}, weeknights),
                ({ (2w or 3w or 4w or 5w) and 0h-6h}, weeknights),
                ({ (5w) and 6h-13h}, fridaymorning),
                ({ (5w) and 13h-20h}, fridayafternoon),
                ({ (5w) and 20h-23h59}, fridaynight),
                ({ (6w) and 0h-6h}, fridaynight),
                ({ (6w) and 6h-13h}, saturdaymorning),
                ({ (6w) and 13h-20h}, saturdayafternoon),
                ({ (6w) and 20h-23h59}, saturdaynight),
                ({ (7w) and 0h-6h}, saturdaynight),
                ({ (7w) and 6h-13h}, sundaymorning),
                ({ (7w) and 13h-20h}, sundayafternoon),
                ({ (7w) and 20h-23h59}, sundaynight),
                ({ (1w) and 0h-6h}, sundaynight),
                ])
                ])
    # Optional Processing (comment out if too loud/pumpy for your kind of music
    default=nrj(default)

    # Let's play!
    default=mksafe(default)
    # s = fallback(track_sensitive=false, [queue, default])
    # s = normalize (s)

    # END MODIFICATION

    s = fallback(track_sensitive=false, [queue, default])

    s = on_metadata(notify, s)
    s = crossfade(s)
    # Attach a skip command to the source s:

    #web_stream_source = input.http(id="web_stream", autostart = false, buffer=0.5, max=20., "")

    #once the stream is started, give it a sink so that liquidsoap doesn't
    #create buffer overflow warnings in the log file.
    #output.dummy(fallible=true, web_stream_source)

    #s = switch(track_sensitive = false,
    #    transitions=[to_live,to_live],
    #    [
    #      ({ !web_stream_enabled }, web_stream_source),
    #      ({ true }, s)
    #    ]
    #)

    add_skip_command(s)
    s = map_metadata(append_title, s)

    if output_sound_device then
       
        success = ref false
       
        log(output_sound_device_type)
       
        %ifdef output.alsa
        if output_sound_device_type == "ALSA" then
            ignore(output.alsa(s))
            success := true
        end
        %endif
       
        %ifdef output.ao
        if output_sound_device_type == "AO" then
            ignore(output.ao(s))
            success := true
        end
        %endif
       
        %ifdef output.oss
        if output_sound_device_type == "OSS" then
            ignore(output.oss(s))
            success := true
        end
        %endif
       
        %ifdef output.portaudio
        if output_sound_device_type == "Portaudio" then
            ignore(output.portaudio(s))
            success := true
        end
        %endif
       
        %ifdef output.pulseaudio
        if output_sound_device_type == "Pulseaudio" then
            ignore(output.pulseaudio(s))
            success := true
        end
        %endif
       
        if (!success == false) then
            ignore(output.prefered(s))
        end
       
    end

    if s1_enable == true then
        if s1_output == 'shoutcast' then
            s1_namespace := "shoutcast_stream_1"
        else
            s1_namespace := s1_mount
        end
        server.register(namespace=!s1_namespace, "connected", fun (s) -> begin !s1_connected end)
        output_to(s1_output, s1_type, s1_bitrate, s1_host, s1_port, s1_pass, s1_mount, s1_url, s1_description, s1_genre, s1_user, s, "1", s1_connected)
    end

    if s2_enable == true then
        if s2_output == 'shoutcast' then
            s2_namespace := "shoutcast_stream_2"
        else
            s2_namespace := s2_mount
        end
        server.register(namespace=!s2_namespace, "connected", fun (s) -> begin !s2_connected end)
        output_to(s2_output, s2_type, s2_bitrate, s2_host, s2_port, s2_pass, s2_mount, s2_url, s2_description, s2_genre, s2_user, s, "2", s2_connected)
       
    end

    if s3_enable == true then
        if s3_output == 'shoutcast' then
            s3_namespace := "shoutcast_stream_3"
        else
            s3_namespace := s3_mount
        end
        server.register(namespace=!s3_namespace, "connected", fun (s) -> begin !s3_connected end)
        output_to(s3_output, s3_type, s3_bitrate, s3_host, s3_port, s3_pass, s3_mount, s3_url, s3_description, s3_genre, s3_user, s, "3", s3_connected)
    end

    ignore(output.dummy(blank()))

    Follow me on Twitter : http://twitter.com/K8L
  • 12 Comments sorted by
  • Vote Up2Vote Down Albert FRAlbert FR
    Posts: 1,978Member, Airtime Moderator
    Hi Kahuitel ;)

    I'm thinking random is not appropriate ;)
    For one reason : a file can be play more than one time (until liquidsoap reading full directory)

    randomize is more efficiant. for example you can change random definition by

    morning= playlist(mode='randomize',reload=1,reload_mode="rounds", "/home/airtime/Musique/")


    and give weights only in the declaration :


    default=fallback([ request.queue(id="request"),switch([({ 6h-18h },
    morning),({ 18h-21h05 }, night),({ 21h05-21h15 }, rfi),({ 21h15-6h },
    night)])])



    my 2 cents ;)
  • Hi Kahuitel,

    This is really cool thanks for this. Hopefully others find this useful while waiting for Airtime to officially support fallback playlists.
    Airtime Pro Hosting: http://airtime.pro
  • My pleasure =)
    Follow me on Twitter : http://twitter.com/K8L
  • Merci for the tip, I'll try to apply your suggestion and post the updated script. I'm quite a noob so I often make syntax mistakes: needs some testing first!
    Follow me on Twitter : http://twitter.com/K8L
  • Vote Up0Vote Down Albert FRAlbert FR
    Posts: 1,978Member, Airtime Moderator
    Ya pas de quoi Kahuitel ;)
  • Anyone had any luck using this on the latest version (2.3.0).  I think the coding in /usr/lib/airtime/pypo/bin/liquidsoap_scripts/ls_script.liq is slightly different and I just can't get it to work.
  • Vote Up0Vote Down hoerichhoerich
    Posts: 627Member, Airtime Moderator
    Until a few weeks, I used the following hack suggested by albert fr in this thread http://forum.sourcefabric.org/discussion/15098/broken-playing-random-songs-on-silence (his last post)

    erase or uncomment the following lines in ls_scrip.liq

    default = amplify(id="silence_src", 0.00001, noise())
    and
    ignore(output.dummy(default, fallible=true))

    and put

    promotions = playlist (mode='random', "/srv/airtime/Jingles/")
    timed_promotions = delay(1200.,promotions)
    default = fallback([timed_promotions, crossfade_airtime(playlist (mode='random', "/srv/airtime/Musiques/"))])

    before

    ref_off_air_meta = ref off_air_meta
    Official Airtime Forum Manager
    --------------------------
    Most of the time an issue is located between keyboard and chair.
  • Will this work with 2.4? Looking to do auto DJ type schedules during the day, switching on time...
  • Duplicate post!
    Post edited by Tim RadioClash at 2013-08-22 22:45:04
  • Anyone reading this a find it funny about my grammar , I make no apology ,Go get a translator.
    "The Problem with education today is that it takes a university degree to switch on a light bulb"
    "You learn from your mistakes but wise people learn from others mistakes avoid Making mistakes there is not sufficient rooms to make them"
    "Innuendo","If's","Assumptions" and "Fear" are for politician.Who,What,where,When and How are for those seeking knowledge and care about Humanity.
    "I might be in Mud but that does not Make me a Wild Hog(pig)"
    “Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius — and a lot of courage to move in the opposite direction.”
    "The only thing that remains constant is change itself"
    May the force be with you,until our path or destiny bring us in tandem.
  • PLEASE SOURCEFABRIC READ THIS: 

    It would be that easy to implement the fallback stream by sourcefabric !! I do not understand why it is taking so long: 

    1. Move all the default stuff in ls_script.liq to one file (ie: /etc/liquidsoap/fallback.liq) that we can accomodate as desired. This file has all the relevant code for the fallback script to work. (the "#default = amplify(id="silence_src", 0.00001, noise())" stuff and the offline tag would go in this file instead of in airtime's ls_script.liq)

    2. In ls_script.liq, replace the code we just moved to /etc/liquidsoap/fallback.liq and include /etc/liquidsoap/fallback.liq. 

    We are done. Next time we update airtime, our custom fallback would not be replaced, and we can customize it as needed.

    Still, it would not be editable from the web interface but at least we have a safe fallback.

    A nice feature would be to be able to store the playlist in /srv/airtime/playlists/ as pls, so that we can use it in the fallback.
    Post edited by Bashatee Radio at 2014-11-28 23:18:12
  • Guess what You do not have the  comment out the default noise. you can Actually use it to your advantage

    Here is the write up

    https://forum.sourcefabric.org/discussion/16909/autodj-sweetest-seen-fake-or-real-for-a-good-deal-free-free-no-unexpected-autoplay


    basically

    1.  you create a liq file   default.liq
    2. strip the noise  using the blank function
    3. redefine your default with a fallback  

    So basically its just this line

    default = fallback(track_sensitive=false,[radio,strip_blank(max_blank=45.,threshold=-45.,default)])

    where radio is your autodj or your playlist

    just add that one line and you are good to go.

    Your playlist is editable from the web interface,just put it in the watch folder,that way if you want to run the show  from the web interface just create a blank show and grab whatever you like and put them in the Now playing and your songs will play.


    The Only disadvantage is that the songs will not appear in the API as they Play and so the best thing is to Fill in Meaningful information in the create show field


    if you are creative use strpos to make a php script (like I do and run it after an upgrade)






    Anyone reading this a find it funny about my grammar , I make no apology ,Go get a translator.
    "The Problem with education today is that it takes a university degree to switch on a light bulb"
    "You learn from your mistakes but wise people learn from others mistakes avoid Making mistakes there is not sufficient rooms to make them"
    "Innuendo","If's","Assumptions" and "Fear" are for politician.Who,What,where,When and How are for those seeking knowledge and care about Humanity.
    "I might be in Mud but that does not Make me a Wild Hog(pig)"
    “Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius — and a lot of courage to move in the opposite direction.”
    "The only thing that remains constant is change itself"
    May the force be with you,until our path or destiny bring us in tandem.