Skip to content
Snippets Groups Projects
Forked from AURA / engine
1704 commits behind, 24 commits ahead of the upstream repository.
playd.liq 9.94 KiB
#!/usr/bin/liquidsoap

#set("log.stdout", true)

inst = if argv(1) != "" then string_of(argv(1)) else 'playd' end
instance = ref inst

%include "readini.liq"

inifile = '/etc/aura/aura.ini'
ini = read_ini(inifile)

# send alive 
ignore(system('#{list.assoc("install_dir", ini)}/modules/liquidsoap/helpers/message.py -c #{!instance} -t sayAlive'))
# send alive frequently
exec_at(freq=20., pred={true}, {ignore(system('#{list.assoc("install_dir", ini)}/modules/liquidsoap/helpers/message.py -c #{!instance} -t sayAlive'))})

# set current playlist
ignore(system('#{list.assoc("install_dir", ini)}/modules/liquidsoap/helpers/message.py -c #{!instance} --task=setState -n playlistcurrent -v ""'))

# load data from ini file
#daemongroup = list.assoc("daemongroup", ini)
#daemonuser = list.assoc("daemonuser", ini)


socketdir = list.assoc("socketdir", ini)

# ALSA settings
use_alsa = list.assoc("use_alsa", ini)
alsa_buffer = int_of_string(list.assoc("alsa_buffer", ini))
alsa_periods = int_of_string(list.assoc("alsa_periods", ini))

# set player i/o devices
player_input = list.assoc("input_device", ini)
#player_second_input = list.assoc("player_second")
player_output = list.assoc("output_device", ini)

# fallback settings
fallback_audio_folder = list.assoc("fallback_audio_folder", ini)
fallback_max_blank = float_of_string(list.assoc("fallback_max_blank", ini))
fallback_min_noise = float_of_string(list.assoc("fallback_min_noise", ini))
fallback_threshold = float_of_string(list.assoc("fallback_threshold", ini))

# channel names from config
channelnames = ref string.split(separator=',', list.assoc("channels", ini))

# alsa settings
# buffer - decrease latency: eg. alsa_buffer="2024"
set("alsa.alsa_buffer", alsa_buffer)
set("alsa.periods", alsa_periods)

# enable socketserver
set("server.socket", true)
set("server.socket.path", socketdir^"/<script>.sock")
set("server.telnet", true)
set("server.telnet.port", 1234)

# enable daemon
#set("init.daemon", true)
#set("init.daemon.change_user.group", daemongroup)
#set("init.daemon.change_user.user", daemonuser)
#set("init.daemon.pidfile.path", socketdir^"/<script>.pid")
#set("init.daemon.pidfile.perms", 0o666)

# set logging
set("log.file",true)
set("log.file.path", list.assoc("logdir", ini)^"/<script>.log")
set("log.file.perms",0o640)
set("log.level", 10)
# allowed mime types
set("playlists.mime_types.xml",["text/xml","application/xml","application/xspf+xml"])

# load functions
# dir = list.assoc("install_dir", ini)

%include "library.liq"
%include "playlist.liq"

# Der input wie oben definiert
def get_input()
    def get_input()
        if use_alsa == "y" then
            if player_input == "soundcard" then
                print("autodetect device")
                input.alsa(id="sound_input", fallible=true, clock_safe=false)
            else
                print("manual set device: "^player_input)
                input.alsa(id="sound_input", fallible=true, clock_safe=false, device=player_input)
            end
        else
            input.pulseaudio(id="sound_input")
        end
    end
    def get_fallback()
        if fallback_audio_folder != "" then
            print("fallbackfolder chosen: "^fallback_audio_folder)
            playlist.safe("/var/audio/fallback/music.flac")
            #playlist.safe(fallback_audio_folder)
        else
            blank(duration=20.0)
        end
    end

    if player_input == "" then
        blank(duration=0.1) # wait...
    else
        # start silence detector on input alsa and set a fallback
        fallback(track_sensitive=false,
                 [ strip_blank(max_blank=fallback_max_blank, min_noise=fallback_min_noise, threshold=fallback_threshold, get_input()),
                   get_fallback() ]
        )
    end
end

playlistrunning = ref false

playlist_recorded = playlist.xml(id='playlist', on_done=fun() -> begin ignore(system('#{list.assoc("install_dir", ini)}/modules/soundengine/helpers/message.py -c #{!instance} --task=setState -n playlistcurrent -v ""')) ignore(server.execute('mixer.select 0 false')) end, 'none')
# Die Source aus der Playlist
recorded = snd(playlist_recorded)

# Skippen erlauben
add_skip_command(recorded)

# Funktion zum laden einer neuen Playlist
playlist_funcs = fst(playlist_recorded)

# Flush functions
flush_recorded = snd(fst(playlist_funcs))

# Reload function
reload_recorded = fst(fst(playlist_funcs))

#up funtction
move_recorded = fst(fst(snd(playlist_funcs)))

#insert funtction
insert_recorded = fst(snd(fst(snd(playlist_funcs))))

# remove function
remove_recorded = snd(snd(fst(snd(playlist_funcs))))

#push function
push_recorded = snd(snd(snd(playlist_funcs)))

#show playlist function
data_recorded = fst(snd(snd(playlist_funcs)))

# create playlist source with smart crossfade
playlist = fallback(id="common", track_sensitive=false, [                                    
    switch(transitions = 
        [crossfade,crossfade], 
        [
            ( {!playlistrunning == true}, recorded ), # play the playlist
            ( {!playlistrunning == false}, get_input() )
        ]
    ),
        if fallback_audio_folder != "" then 
            playlist.safe(fallback_audio_folder)
        else 
            blank(duration=20.0)
        end  
])

# method to dynamicaly create a channel
def addChannel (ret, el, ~base="ch")
    equeue = request.equeue(id = base^el) # create the equeue request
    # add a seek function to the channel
    server.register(namespace="ch"^el,
                description="Seek to a relative position",
                usage="seek <duration>",
                "seek",fun (x) ->  begin seek_track(equeue, x) end)
    # append and return the list
    list.append(ret,[equeue])
end

channels = addChannel([], '1', base='auto')
channels = addChannel(channels, '2', base='auto')
channels = list.fold(addChannel, channels, !channelnames)

mixer = mix(id = "mixer", list.append([playlist], channels))
#ignore(s) # testing


# User may load a XML-Playlist
#server.register(namespace='playlist',
#    description="Load Playlist",
#    usage="load <uri>",
#    "load",fun (p) ->  begin
#        reload_recorded(skip=0, uri=p)
#    end
#)

# Register the seek function
server.register(namespace='playlist',
    description="Seek to a relative position",
    usage="seek <duration>",
    "seek",fun (x) ->  begin seek_track(recorded, x) end
)

# The play function
server.register(namespace='playlist',
    description="Play recorded files",
    usage="play",
    "play",fun (x) -> if !playlistrunning == true then                                    
        message('playlist', false, 'Playlist already playing', get_task_number ('play'), '01', level="info", type="user")
    else 
        ignore(server.execute('mixer.select 0 true')) 
        playlistrunning := true 
        message('playlist', true, 'Start playing' , get_task_number ('play'), '00')
    end
)

# Flush current  playlist
server.register(namespace='playlist',
    description="Flush recorded playlist",
    usage="flush",
    "flush",fun (s) ->  begin flush_recorded() end)

# The remove function
server.register(namespace='playlist',
    description="Remove item from playlist",
    usage="remove <pos>",
    "remove",fun (p) ->  begin remove_recorded(int_of_string(p)) end)

# Let the user move up or down a track
server.register(namespace='playlist',
    description="Move an item up or down",
    usage="move <from> <to>",
    "move",fun (p) ->  begin  
        params=string.split(separator=" ",p) 
        src=if list.length(params)>0 then (int_of_string(list.nth(params,0))+0) else 0 end
        target=if list.length(params)>1 then (int_of_string(list.nth(params,1))+0) else 0 end
        move_recorded(src, target) 
    end
)

# Insert an entry
server.register(namespace='playlist',
    description="Add an entry",
    usage="insert <pos> <uri> [<title> <time>]",
    "insert",fun (p) ->  begin  
        params=string.split(separator=" ",p) 
        pos=if list.length(params)>0 then list.nth(params,0) else '' end
        uri=if list.length(params)>1 then list.nth(params,1) else '' end                                    
        title=if list.length(params)>2 then list.nth(params,2) else '' end
        time=if list.length(params)>3 then list.nth(params,3) else '' end
        insert_recorded(pos=pos, title=title,time=time,uri) 
    end
)

# Insert a track on top of playlist
server.register(namespace='playlist',
    description="Push an item to top and play immediately",
    usage="push <pos>",
    "push",fun (p) ->  begin
        params=string.split(separator=" ",p) 
        pos=if list.length(params)>0 then int_of_string(list.nth(params,0)) else 0 end 
        push_recorded(pos) 
    end
)

# Show metadata
server.register(namespace='playlist',
    description="Show remaining playlist data in json format",
    usage="data",
    "data",fun (s) ->  begin 
        playlist = data_recorded() 
        json_of(compact=true, playlist) 
    end
)

# Pause/stop playlist
server.register(namespace='playlist',
    description="Pause playing recorded files",
    usage="pause",
    "pause", fun (x) -> if !playlistrunning == false then 
        message('playlist', false, 'Playlist already stopped', get_task_number ('pause'), '01', level="info", type="user")
    else 
        playlistrunning := false 
        ignore(server.execute('playlist.skip'))
        ignore(system('#{list.assoc("install_dir", ini)}/modules/soundengine/helpers/message.py -c #{!instance} --task=setState -n playlistcurrent -v ""'))
        message('playlist', true, 'Playlist stopped', get_task_number ('pause'), '00') 
    end
)

# get remaining time
server.register(namespace='playlist',
    description="Remaining time",
    usage = "remaining",
    "remaining", fun(x) -> string_of(source.remaining(recorded)) 
)

#streamm = single("/var/audio/fallback/music.flac")
#ignore(streamm) # testing

# Alsa output
if use_alsa == "y" then
    if player_output == 'soundcard'  then
        output.alsa(id="player", fallible=true, mixer)
    else
        output.alsa(id="player", fallible=true, device=player_output, mixer)
    end
else
    output.pulseaudio(id="player", mixer)
end


# %include "stream.liq"