-fixed liquidsoap not working when the liquidsoap global libraries were not installed

This commit is contained in:
mkonecny 2011-01-11 16:35:43 -05:00
parent 277095a538
commit 692ba3513b
23 changed files with 908 additions and 50 deletions

View File

@ -54,14 +54,11 @@ def copy_dir(src_dir, dest_dir):
try:
# Create users
create_user("pypo")
#create_user("pypo-logger")
print "Creating log directories"
create_path("/var/log/pypo")
os.system("chmod -R 755 /var/log/pypo")
os.system("chown -R pypo:pypo /var/log/pypo")
#os.mkdirs("/var/log/liquidsoap")
#os.system("chown -R liquidsoap:liquidsoap /var/log/liquidsoap")
create_path(BASE_PATH)
create_path(BASE_PATH+"bin")
@ -87,17 +84,7 @@ try:
print "Unknown system architecture."
sys.exit(1)
#shutil.copy("../pypo-cli.py", BASE_PATH+"bin")
#shutil.copy("../pypo-notify.py", BASE_PATH+"bin")
#shutil.copy("../logging.cfg", BASE_PATH+"bin")
#shutil.copy("../config.cfg", BASE_PATH+"bin")
#shutil.copy("../pypo-log.sh", BASE_PATH+"bin")
copy_dir("..", BASE_PATH+"bin/")
#copy_dir("../util", BASE_PATH+"bin/")
#copy_dir("../api_clients", BASE_PATH+"bin/api_clients")
#copy_dir("../scripts", BASE_PATH+"bin/scripts")
#copy_dir("../dls", BASE_PATH+"bin/dls")
#copy_dir("../dls", BASE_PATH+"bin/dls")
print "Setting permissions"
os.system("chmod -R 755 "+BASE_PATH)
@ -120,8 +107,7 @@ try:
os.system("chown -R pypo:pypo /etc/service/pypo-push")
print "Installing daemontool script pypo-liquidsoap"
os.system("svc -dk /etc/service/pypo-liquidsoap > /dev/null 2>&1")
os.system("killall liquidsoap")
os.system("svc -dx /etc/service/pypo-liquidsoap 1>/dev/null 2>&1")
create_path("/etc/service/pypo-liquidsoap")
create_path("/etc/service/pypo-liquidsoap/log")
shutil.copy("pypo-daemontools-liquidsoap.sh", "/etc/service/pypo-liquidsoap/run")
@ -131,6 +117,7 @@ try:
print "Waiting for processes to start..."
time.sleep(5)
os.system("killall liquidsoap")
os.system("python ./pypo-start.py")
time.sleep(2)
@ -148,9 +135,7 @@ try:
p = Popen('svstat /etc/service/pypo-liquidsoap', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
output = p.stdout.read()
print output
#os.symlink(BASE_PATH+"bin/pypo-log.sh", "/usr/local/bin/")
print "Install complete."
except Exception, e:
print "exception:" + str(e)

View File

@ -19,4 +19,4 @@ try:
os.system("svc -t /etc/service/pypo-liquidsoap")
except Exception, e:
print "exception:" + str(e)
print "exception:" + str(e)

View File

@ -21,7 +21,7 @@ def remove_user(username):
print "Waiting for processes to close..."
time.sleep(5)
os.system("deluser --remove-home " + username + " > /dev/null")
os.system("deluser --remove-home " + username + " 1>/dev/null")
try:
os.system("python ./pypo-stop.py")
@ -44,4 +44,4 @@ try:
remove_user("pypo")
print "Uninstall complete."
except Exception, e:
print "exception:" + str(e)
print "exception:" + str(e)

Binary file not shown.

View File

@ -0,0 +1,6 @@
DISTFILES = $(wildcard *.in) Makefile ask-liquidsoap.rb ask-liquidsoap.pl \
$(wildcard *.liq) extract-replaygain
top_srcdir = ..
include $(top_srcdir)/Makefile.rules

View File

@ -0,0 +1,12 @@
#!/usr/bin/perl -w
use strict ;
use Net::Telnet ;
my $telnet = new Net::Telnet ( Timeout=>10, Errmode=>'die', Port=>1234) ;
$telnet->open('localhost') ;
die "Usage: $0 <command>\n" unless @ARGV ;
$telnet->print($ARGV[0]) ;
my ($output,$end) = $telnet->waitfor('/END$/') ;
print $output;

View File

@ -0,0 +1,13 @@
#!/usr/bin/ruby
require 'net/telnet'
liq_host = "localhost"
liq_port = 1234
conn = Net::Telnet::new("Host" => liq_host, "Port" => liq_port)
conn.puts(ARGV[0])
conn.waitfor("Match" => /^END$/) do |data|
puts data.sub(/\nEND\n/,"")
end

View File

@ -0,0 +1,314 @@
# These operators need to be updated..
# Stream data from mplayer
# @category Source / Input
# @param s data URI.
# @param ~restart restart on exit.
# @param ~restart_on_error restart on exit with error.
# @param ~buffer Duration of the pre-buffered data.
# @param ~max Maximum duration of the buffered data.
def input.mplayer(~id="input.mplayer",
~restart=true,~restart_on_error=false,
~buffer=0.2,~max=10.,s) =
input.external(id=id,restart=restart,
restart_on_error=restart_on_error,
buffer=buffer,max=max,
"mplayer -really-quiet -ao pcm:file=/dev/stdout \
-vc null -vo null #{quote(s)} 2>/dev/null")
end
# Output the stream using aplay.
# Using this turns "root.sync" to false
# since aplay will do the synchronisation
# @category Source / Output
# @param ~id Output's ID
# @param ~device Alsa pcm device name
# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop.
# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped.
# @param ~on_start Callback executed when outputting starts.
# @param ~on_stop Callback executed when outputting stops.
# @param s Source to play
def output.aplay(~id="output.aplay",~device="default",
~fallible=false,~on_start={()},~on_stop={()},
~restart_on_crash=false,s)
def aplay_p(m) =
"aplay -D #{device}"
end
log(label=id,level=3,"Setting root.sync to false")
set("root.sync",false)
output.pipe.external(id=id,
fallible=fallible,on_start=on_start,on_stop=on_stop,
restart_on_crash=restart_on_crash,
restart_on_new_track=false,
process=aplay_p,s)
end
%ifdef output.icecast.external
# Output to icecast using the lame command line encoder.
# @category Source / Output
# @param ~id Output's ID
# @param ~start Start output threads on operator initialization.
# @param ~restart Restart output after a failure. By default, liquidsoap will stop if the output failed.
# @param ~restart_delay Delay, in seconds, before attempting new connection, if restart is enabled.
# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop.
# @param ~restart_on_new_track Restart encoder upon new track.
# @param ~restart_encoder_delay Restart the encoder after this delay, in seconds.
# @param ~user User for shout source connection. Useful only in special cases, like with per-mountpoint users.
# @param ~lame The lame binary
# @param ~bitrate Encoder bitrate
# @param ~swap Swap audio samples. Depends on local machine's endianess and lame's version. Test this parameter if you experience garbaged mp3 audio data. On intel 32 and 64 architectures, the parameter should be "true" for lame version >= 3.98.
# @param ~dumpfile Dump stream to file, for debugging purpose. Disabled if empty.
# @param ~protocol Protocol of the streaming server: 'http' for Icecast, 'icy' for Shoutcast.
# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped.
# @param ~on_start Callback executed when outputting starts.
# @param ~on_stop Callback executed when outputting stops.
# @param s The source to output
def output.icecast.lame(
~id="output.icecast.lame",~start=true,
~restart=false,~restart_delay=3,
~host="localhost",~port=8000,
~user="source",~password="hackme",
~genre="Misc",~url="http://savonet.sf.net/",
~description="OCaml Radio!",~public=true,
~dumpfile="",~mount="Use [name]",
~name="Use [mount]",~protocol="http",
~lame="lame",~bitrate=128,~swap=false,
~fallible=false,~on_start={()},~on_stop={()},
~restart_on_crash=false,~restart_on_new_track=false,
~restart_encoder_delay=3600,~headers=[],s)
samplerate = get(default=44100,"frame.samplerate")
samplerate = float_of_int(samplerate) / 1000.
channels = get(default=2,"frame.channels")
swap = if swap then "-x" else "" end
mode =
if channels == 2 then
"j" # Encoding in joint stereo..
else
"m"
end
# Metadata update is set by ICY with icecast
def lame_p(m)
"#{lame} -b #{bitrate} -r --bitwidth 16 -s #{samplerate} \
--signed -m #{mode} --nores #{swap} -t - -"
end
output.icecast.external(id=id,
process=lame_p,bitrate=bitrate,start=start,
restart=restart,restart_delay=restart_delay,
host=host,port=port,user=user,password=password,
genre=genre,url=url,description=description,
public=public,dumpfile=dumpfile,restart_encoder_delay=restart_encoder_delay,
name=name,mount=mount,protocol=protocol,
header=false,restart_on_crash=restart_on_crash,
restart_on_new_track=restart_on_new_track,headers=headers,
fallible=fallible,on_start=on_start,on_stop=on_stop,
s)
end
# Output to shoutcast using the lame encoder.
# @category Source / Output
# @param ~id Output's ID
# @param ~start Start output threads on operator initialization.
# @param ~restart Restart output after a failure. By default, liquidsoap will stop if the output failed.
# @param ~restart_delay Delay, in seconds, before attempting new connection, if restart is enabled.
# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop.
# @param ~restart_on_new_track Restart encoder upon new track.
# @param ~restart_encoder_delay Restart the encoder after this delay, in seconds.
# @param ~user User for shout source connection. Useful only in special cases, like with per-mountpoint users.
# @param ~lame The lame binary
# @param ~bitrate Encoder bitrate
# @param ~icy_reset Reset shoutcast source buffer upon connecting (necessary for NSV).
# @param ~dumpfile Dump stream to file, for debugging purpose. Disabled if empty.
# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped.
# @param ~on_start Callback executed when outputting starts.
# @param ~on_stop Callback executed when outputting stops.
# @param s The source to output
def output.shoutcast.lame(
~id="output.shoutcast.mp3",~start=true,
~restart=false,~restart_delay=3,
~host="localhost",~port=8000,
~user="source",~password="hackme",
~genre="Misc",~url="http://savonet.sf.net/",
~description="OCaml Radio!",~public=true,
~dumpfile="",~name="Use [mount]",~icy_reset=true,
~lame="lame",~aim="",~icq="",~irc="",
~fallible=false,~on_start={()},~on_stop={()},
~restart_on_crash=false,~restart_on_new_track=false,
~restart_encoder_delay=3600,~bitrate=128,s) =
icy_reset = if icy_reset then "1" else "0" end
headers = [("icy-aim",aim),("icy-irc",irc),
("icy-icq",icq),("icy-reset",icy_reset)]
output.icecast.lame(
id=id, headers=headers, lame=lame,
bitrate=bitrate, start=start,
restart=restart, restart_encoder_delay=restart_encoder_delay,
host=host, port=port, user=user, password=password,
genre=genre, url=url, description=description,
public=public, dumpfile=dumpfile,
restart_on_crash=restart_on_crash,
restart_on_new_track=restart_on_new_track,
name=name, mount="/", protocol="icy",
fallible=fallible,on_start=on_start,on_stop=on_stop,
s)
end
# Output to icecast using the flac command line encoder.
# @category Source / Output
# @param ~id Output's ID
# @param ~start Start output threads on operator initialization.
# @param ~restart Restart output after a failure. By default, liquidsoap will stop if the output failed.
# @param ~restart_delay Delay, in seconds, before attempting new connection, if restart is enabled.
# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop.
# @param ~restart_on_new_track Restart encoder upon new track. If false, the resulting stream will have a single track.
# @param ~restart_encoder_delay Restart the encoder after this delay, in seconds.
# @param ~user User for shout source connection. Useful only in special cases, like with per-mountpoint users.
# @param ~flac The flac binary
# @param ~quality Encoder quality (0..8)
# @param ~dumpfile Dump stream to file, for debugging purpose. Disabled if empty.
# @param ~protocol Protocol of the streaming server: 'http' for Icecast, 'icy' for Shoutcast.
# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped.
# @param ~on_start Callback executed when outputting starts.
# @param ~on_stop Callback executed when outputting stops.
# @param s The source to output
def output.icecast.flac(
~id="output.icecast.flac",~start=true,
~restart=false,~restart_delay=3,
~host="localhost",~port=8000,
~user="source",~password="hackme",
~genre="Misc",~url="http://savonet.sf.net/",
~description="OCaml Radio!",~public=true,
~dumpfile="",~mount="Use [name]",
~name="Use [mount]",~protocol="http",
~flac="flac",~quality=6,
~restart_on_crash=false,
~restart_on_new_track=true,
~restart_encoder_delay=(-1),
~fallible=false,~on_start={()},~on_stop={()},
s)
# We will use raw format, to
# bypass input length value in WAV
# header (input length is not known)
channels = get(default=2,"frame.channels")
samplerate = get(default=44100,"frame.samplerate")
def flac_p(m)=
def option(x) =
"-T #{quote(fst(x))}=#{quote(snd(x))}"
end
m = list.map(option,m)
m = string.concat(separator=" ",m)
"#{flac} --force-raw-format --endian=little --channels=#{channels} \
--bps=16 --sample-rate=#{samplerate} --sign=signed #{m} \
-#{quality} --ogg -c -"
end
output.icecast.external(id=id,
process=flac_p,bitrate=(-1),start=start,
restart=restart,restart_delay=restart_delay,
host=host,port=port,user=user,password=password,
genre=genre,url=url,description=description,
public=public,dumpfile=dumpfile,
name=name,mount=mount,protocol=protocol,
fallible=fallible,on_start=on_start,on_stop=on_stop,
restart_on_new_track=restart_on_new_track,
format="ogg",header=false,icy_metadata=false,
restart_on_crash=restart_on_crash,
restart_encoder_delay=restart_encoder_delay,
s)
end
# Output to icecast using the aacplusenc command line encoder.
# @category Source / Output
# @param ~id Output's ID
# @param ~start Start output threads on operator initialization.
# @param ~restart Restart output after a failure. By default, liquidsoap will stop if the output failed.
# @param ~restart_delay Delay, in seconds, before attempting new connection, if restart is enabled.
# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop.
# @param ~restart_on_new_track Restart encoder upon new track.
# @param ~restart_encoder_delay Restart the encoder after this delay, in seconds.
# @param ~user User for shout source connection. Useful only in special cases, like with per-mountpoint users.
# @param ~aacplusenc The aacplusenc binary
# @param ~bitrate Encoder bitrate
# @param ~dumpfile Dump stream to file, for debugging purpose. Disabled if empty.
# @param ~protocol Protocol of the streaming server: 'http' for Icecast, 'icy' for Shoutcast.
# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped.
# @param ~on_start Callback executed when outputting starts.
# @param ~on_stop Callback executed when outputting stops.
# @param s The source to output
def output.icecast.aacplusenc(
~id="output.icecast.aacplusenc",~start=true,
~restart=false,~restart_delay=3,
~host="localhost",~port=8000,
~user="source",~password="hackme",
~genre="Misc",~url="http://savonet.sf.net/",
~description="OCaml Radio!",~public=true,
~dumpfile="",~mount="Use [name]",
~name="Use [mount]",~protocol="http",
~aacplusenc="aacplusenc",~bitrate=64,
~fallible=false,~on_start={()},~on_stop={()},
~restart_on_crash=false,~restart_on_new_track=false,
~restart_encoder_delay=3600,~headers=[],s)
# Metadata update is set by ICY with icecast
def aacplusenc_p(m)
"#{aacplusenc} - - #{bitrate}"
end
output.icecast.external(id=id,
process=aacplusenc_p,bitrate=bitrate,start=start,
restart=restart,restart_delay=restart_delay,
host=host,port=port,user=user,password=password,
genre=genre,url=url,description=description,
public=public,dumpfile=dumpfile,
name=name,mount=mount,protocol=protocol,
fallible=fallible,on_start=on_start,on_stop=on_stop,
header=true,restart_on_crash=restart_on_crash,
restart_on_new_track=restart_on_new_track,headers=headers,
restart_encoder_delay=restart_encoder_delay,format="audio/aacp",s)
end
# Output to shoutcast using the aacplusenc encoder.
# @category Source / Output
# @param ~id Output's ID
# @param ~start Start output threads on operator initialization.
# @param ~restart Restart output after a failure. By default, liquidsoap will stop if the output failed.
# @param ~restart_delay Delay, in seconds, before attempting new connection, if restart is enabled.
# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop.
# @param ~restart_on_new_track Restart encoder upon new track.
# @param ~restart_encoder_delay Restart the encoder after this delay, in seconds.
# @param ~user User for shout source connection. Useful only in special cases, like with per-mountpoint users.
# @param ~aacplusenc The aacplusenc binary
# @param ~bitrate Encoder bitrate
# @param ~icy_reset Reset shoutcast source buffer upon connecting (necessary for NSV).
# @param ~dumpfile Dump stream to file, for debugging purpose. Disabled if empty.
# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped.
# @param ~on_start Callback executed when outputting starts.
# @param ~on_stop Callback executed when outputting stops.
# @param s The source to output
def output.shoutcast.aacplusenc(
~id="output.shoutcast.aacplusenc",~start=true,
~restart=false,~restart_delay=3,
~host="localhost",~port=8000,
~user="source",~password="hackme",
~genre="Misc",~url="http://savonet.sf.net/",
~description="OCaml Radio!",~public=true,
~fallible=false,~on_start={()},~on_stop={()},
~dumpfile="",~name="Use [mount]",~icy_reset=true,
~aim="",~icq="",~irc="",~aacplusenc="aacplusenc",
~restart_on_crash=false,~restart_on_new_track=false,
~restart_encoder_delay=3600,~bitrate=64,s) =
icy_reset = if icy_reset then "1" else "0" end
headers = [("icy-aim",aim),("icy-irc",irc),
("icy-icq",icq),("icy-reset",icy_reset)]
output.icecast.aacplusenc(
id=id, headers=headers, aacplusenc=aacplusenc,
bitrate=bitrate, start=start,
restart=restart, restart_delay=restart_delay,
host=host, port=port, user=user, password=password,
genre=genre, url=url, description=description,
public=public, dumpfile=dumpfile,
fallible=fallible,on_start=on_start,on_stop=on_stop,
restart_on_crash=restart_on_crash, restart_encoder_delay=restart_encoder_delay,
restart_on_new_track=restart_on_new_track,
name=name, mount="/", protocol="icy",
s)
end
%endif

View File

@ -35,7 +35,7 @@ if test_process("which flac") then
add_decoder(name="FLAC",description="Decode files using the flac \
decoder binary.", test=test_flac,flac_p)
else
log(level=3,"flac binary not found: flac decoder disabled.")
log(level=3,"Did not find flac binary: flac decoder disabled.")
end
%endif
@ -63,15 +63,16 @@ if os.type != "Win32" then
end
add_metadata_resolver("FLAC",flac_meta)
else
log(level=3,"metaflac binary not found: flac metadata resolver disabled.")
log(level=3,"Did not find metaflac binary: flac metadata resolver disabled.")
end
end
# A list of know extensions and content-type for AAC.
# Values from http://en.wikipedia.org/wiki/Advanced_Audio_Coding
# TODO: can we register a setting for that ??
aac_mimes = ["audio/aac", "audio/aacp", "audio/3gpp", "audio/3gpp2", "audio/mp4",
"audio/MP4A-LATM", "audio/mpeg4-generic", "audio/x-hx-aac-adts"]
aac_mimes =
["audio/aac", "audio/aacp", "audio/3gpp", "audio/3gpp2", "audio/mp4",
"audio/MP4A-LATM", "audio/mpeg4-generic", "audio/x-hx-aac-adts"]
aac_filexts = ["m4a", "m4b", "m4p", "m4v",
"m4r", "3gp", "mp4", "aac"]
@ -143,7 +144,48 @@ if os.type != "Win32" then
end
add_metadata_resolver("FAAD",faad_meta)
else
log(level=3,"faad binary not found: faad decoder disabled.")
log(level=3,"Did not find faad binary: faad decoder disabled.")
end
end
# Standard function for displaying metadata.
# Shows artist and title, using "Unknown" when a field is empty.
# @param m Metadata packet to be displayed.
def string_of_metadata(m)
artist = m["artist"]
title = m["title"]
artist = if ""==artist then "Unknown" else artist end
title = if ""==title then "Unknown" else title end
"#{artist} -- #{title}"
end
# Use X On Screen Display to display metadata info.
# @param ~color Color of the text.
# @param ~position Position of the text (top|middle|bottom).
# @param ~font Font used (xfontsel is your friend...)
# @param ~display Function used to display a metadata packet.
def osd_metadata(~color="green",~position="top",
~font="-*-courier-*-r-*-*-*-240-*-*-*-*-*-*",
~display=string_of_metadata,
s)
osd = 'osd_cat -p #{position} --font #{quote(font)}'
^ ' --color #{color}'
def feedback(m)
system("echo #{quote(display(m))} | #{osd} &")
end
on_metadata(feedback,s)
end
# Use notify to display metadata info.
# @param ~urgency Urgency (low|normal|critical).
# @param ~icon Icon filename or stock icon to display.
# @param ~time Timeout in milliseconds.
# @param ~display Function used to display a metadata packet.
# @param ~title Title of the notification message.
def notify_metadata(~urgency="low",~icon="stock_smiley-22",~time=3000,
~display=string_of_metadata,
~title="Liquidsoap: new track",s)
send = 'notify-send -i #{icon} -u #{urgency}'
^ ' -t #{time} #{quote(title)} '
on_metadata(fun (m) -> system(send^quote(display(m))),s)
end

View File

@ -15,7 +15,7 @@ if (($file =~ /\.mp3$/i) || (test_mime($file) =~ /audio\/mpeg/)) {
if (`which mp3gain`) {
my $out = `nice -n 20 mp3gain -q "$file" 2> /dev/null` ;
my $out = `mp3gain -q "$file" 2> /dev/null` ;
$out =~ /Recommended "Track" dB change: (.*)$/m || die ;
print "$1 dB\n" ;
@ -29,7 +29,7 @@ if (($file =~ /\.mp3$/i) || (test_mime($file) =~ /audio\/mpeg/)) {
if ((`which vorbisgain`) && (`which ogginfo`)) {
system("nice -n 20 vorbisgain -q -f \"$file\" 2>/dev/null >/dev/null") ;
system("vorbisgain -q -f \"$file\" 2>/dev/null >/dev/null") ;
my $info = `ogginfo "$file"` ;
$info =~ /REPLAYGAIN_TRACK_GAIN=(.*) dB/ || die ;
print "$1 dB\n" ;
@ -52,7 +52,7 @@ if (($file =~ /\.mp3$/i) || (test_mime($file) =~ /audio\/mpeg/)) {
} else {
system("nice -n 20 metaflac --add-replay-gain \"$file\" \
system("metaflac --add-replay-gain \"$file\" \
2>/dev/null >/dev/null") ;
$info = `metaflac --show-tag=REPLAYGAIN_TRACK_GAIN "$file"` ;
$info =~ /REPLAYGAIN_TRACK_GAIN=(.*) dB/ || die "Error in $file" ;

View File

@ -0,0 +1,15 @@
# Launch with: screen -c interactive.screen
screen -t Liquidsoap liquidsoap --interactive 'set("log.file.path","/tmp/interactive.log") system("echo \"setenv PID #{getpid()}\" > /tmp/interactive.env")'
verbose
# Yeah, this is a trick
# to wait for interactive.env
# to be created
logfile /dev/null
log
source /tmp/interactive.env
screen -t Log tail --pid=$PID -f /tmp/interactive.log
split -v
select 0
focus
select 1
focus

View File

@ -0,0 +1,43 @@
#!/sbin/runscript
user=liquidsoap
group=liquidsoap
prefix=/usr/local
exec_prefix=${prefix}
confdir=${prefix}/etc/liquidsoap
liquidsoap=${exec_prefix}/bin/liquidsoap
rundir=${prefix}/var/run/liquidsoap
depend() {
after net icecast
}
start() {
cd $confdir
for liq in *.liq ; do
if test $liq != '*.liq' ; then
ebegin "Starting $liq"
start-stop-daemon --start --quiet --pidfile $rundir/${liq%.liq}.pid \
--chuid $user:$group --exec $liquidsoap -- -d $confdir/$liq
eend $?
fi
done
}
stop() {
cd $rundir
for liq in *.pid ; do
if test $liq != '*.pid' ; then
ebegin "Stopping $liq"
start-stop-daemon --stop --quiet --pidfile $liq
eend $?
fi
done
}
restart() {
svc_stop
einfo "Sleeping 4 seconds ..."
sleep 4
svc_start
}

View File

@ -0,0 +1,43 @@
#!/sbin/runscript
user=@install_user@
group=@install_group@
prefix=@prefix@
exec_prefix=@exec_prefix@
confdir=@sysconfdir@/liquidsoap
liquidsoap=@bindir@/liquidsoap
rundir=@localstatedir@/run/liquidsoap
depend() {
after net icecast
}
start() {
cd $confdir
for liq in *.liq ; do
if test $liq != '*.liq' ; then
ebegin "Starting $liq"
start-stop-daemon --start --quiet --pidfile $rundir/${liq%.liq}.pid \
--chuid $user:$group --exec $liquidsoap -- -d $confdir/$liq
eend $?
fi
done
}
stop() {
cd $rundir
for liq in *.pid ; do
if test $liq != '*.pid' ; then
ebegin "Stopping $liq"
start-stop-daemon --stop --quiet --pidfile $liq
eend $?
fi
done
}
restart() {
svc_stop
einfo "Sleeping 4 seconds ..."
sleep 4
svc_start
}

View File

@ -0,0 +1,63 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: liquidsoap
# Required-Start: $remote_fs $network $time
# Required-Stop: $remote_fs $network $time
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Starts the liquidsoap daemon
# Description:
### END INIT INFO
user=liquidsoap
group=liquidsoap
prefix=/usr/local
exec_prefix=${prefix}
confdir=${prefix}/etc/liquidsoap
liquidsoap=${exec_prefix}/bin/liquidsoap
rundir=${prefix}/var/run/liquidsoap
# Test if $rundir exists
if [ ! -d $rundir ]; then
mkdir -p $rundir;
chown $user:$group $rundir
fi
case "$1" in
stop)
echo -n "Stopping channels: "
cd $rundir
for liq in *.pid ; do
if test $liq != '*.pid' ; then
echo -n "$liq "
start-stop-daemon --stop --quiet --pidfile $liq --retry 4
fi
done
echo "OK"
;;
start)
echo -n "Starting channels: "
cd $confdir
for liq in *.liq ; do
if test $liq != '*.liq' ; then
echo -n "$liq "
start-stop-daemon --start --quiet --pidfile $rundir/${liq%.liq}.pid \
--chuid $user:$group --exec $liquidsoap -- -d $confdir/$liq
fi
done
echo "OK"
;;
restart|force-reload)
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload}"
exit 1
;;
esac

View File

@ -0,0 +1,63 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: liquidsoap
# Required-Start: $remote_fs $network $time
# Required-Stop: $remote_fs $network $time
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Starts the liquidsoap daemon
# Description:
### END INIT INFO
user=@install_user@
group=@install_group@
prefix=@prefix@
exec_prefix=@exec_prefix@
confdir=@sysconfdir@/liquidsoap
liquidsoap=@bindir@/liquidsoap
rundir=@localstatedir@/run/liquidsoap
# Test if $rundir exists
if [ ! -d $rundir ]; then
mkdir -p $rundir;
chown $user:$group $rundir
fi
case "$1" in
stop)
echo -n "Stopping channels: "
cd $rundir
for liq in *.pid ; do
if test $liq != '*.pid' ; then
echo -n "$liq "
start-stop-daemon --stop --quiet --pidfile $liq --retry 4
fi
done
echo "OK"
;;
start)
echo -n "Starting channels: "
cd $confdir
for liq in *.liq ; do
if test $liq != '*.liq' ; then
echo -n "$liq "
start-stop-daemon --start --quiet --pidfile $rundir/${liq%.liq}.pid \
--chuid $user:$group --exec $liquidsoap -- -d $confdir/$liq
fi
done
echo "OK"
;;
restart|force-reload)
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload}"
exit 1
;;
esac

View File

@ -0,0 +1,15 @@
/usr/local/var/log/liquidsoap/*.log {
compress
rotate 5
size 300k
missingok
notifempty
sharedscripts
postrotate
for liq in /usr/local/var/run/liquidsoap/*.pid ; do
if test $liq != '/usr/local/var/run/liquidsoap/*.pid' ; then
start-stop-daemon --stop --signal USR1 --quiet --pidfile $liq
fi
done
endscript
}

View File

@ -0,0 +1,15 @@
@localstatedir@/log/liquidsoap/*.log {
compress
rotate 5
size 300k
missingok
notifempty
sharedscripts
postrotate
for liq in @localstatedir@/run/liquidsoap/*.pid ; do
if test $liq != '@localstatedir@/run/liquidsoap/*.pid' ; then
start-stop-daemon --stop --signal USR1 --quiet --pidfile $liq
fi
done
endscript
}

View File

@ -0,0 +1,11 @@
#!/bin/sh
# This script is called from liquidsoap for generating a file
# for "say:voice/text" URIs.
# Usage: liquidtts text output_file voice
echo $1 | @TEXT2WAVE@ -f 44100 > $2.tmp.wav && @SOX@ 2> /dev/null > /dev/null
return=$?
@RM@ $2.tmp.wav
@NORMALIZE@ $2 2> /dev/null > /dev/null
exit $return

View File

@ -41,7 +41,7 @@ def output.shoutcast(
restart=restart, restart_delay=restart_delay,
host=host, port=port, user=user, password=password,
genre=genre, url=url, description="UNUSED",
public=public, dumpfile=dumpfile,
public=public, dumpfile=dumpfile,encoding="ISO-8859-1",
name=name, mount="/", protocol="icy",
fallible=fallible,on_start=on_start,on_stop=on_stop,
s)

View File

@ -0,0 +1,33 @@
set("log.file",false)
echo = fun (x) -> system("echo "^quote(x))
def test(lbl,f)
if f() then echo(lbl) else system("echo fail "^lbl) end
end
test("1",{ 1==1 })
test("2",{ 1+1==2 })
test("3",{ (-1)+2==1 })
test("4",{ (-1)+2 <= 3*2 })
test("5",{ true })
test("6",{ true and true })
test("7",{ 1==1 and 1==1 })
test("8",{ (1==1) and (1==1) })
test("9",{ true and (-1)+2 <= 3*2 })
l = [ ("bla",""), ("bli","x"), ("blo","xx"), ("blu","xxx"), ("dix","10") ]
echo(l["dix"])
test("11",{ 2 == list.length(string.split(separator="",l["blo"])) })
%ifdef foobarbaz
if = if is not a well-formed expression, and we do not care...
%endif
echo("1#{1+1}")
echo(string_of(int_of_float(float_of_string(default=13.,"blah"))))
f=fun(x)->x
# Checking that the following is not recursive:
f=fun(x)->f(x)
print(f(14))

View File

@ -0,0 +1,112 @@
# Check these examples with: liquidsoap --no-libs -i -c typing.liq
# TODO Throughout this file, parsing locations displayed in error messages
# are often much too inaccurate.
set("log.file",false)
# Check that some polymorphism is allowed.
# id :: (string,'a)->'a
def id(a,b)
log(a)
b
end
ignore("bla"==id("bla","bla"))
ignore(0==id("zero",0))
# Reporting locations for the next error is non-trivial, because it is about
# an instantiation of the type of id. The deep error doesn't have relevant
# information about why the int should be a string, the outer one has.
# id(0,0)
# Polymorphism is limited to outer generalizations, this is not system F.
# apply :: ((string)->'a)->'a
def apply(f)
f("bla")
# f is not polymorphic, the following is forbidden:
# f(0)
# f(f)
end
# The level checks forbid abusive generalization.
# id' :: ('a)->'a
def id'(x)
# If one isn't careful about levels/scoping, f2 gets the type ('a)->'b
# and so does twisted_id.
def f2(y)
x
end
f2(x)
end
# More errors...
# 0=="0"
# [3,""]
# Subtyping, functions and lists.
f1 = fun () -> 3
f2 = fun (a=1) -> a
# This is OK, l1 is a list of elements of type f1.
l1 = [f1,f2]
list.iter(fun (f) -> log(string_of(f())), l1)
# Forbidden. Indeed, f1 doesn't accept any argument -- although f2 does.
# Here the error message may even be too detailed since it goes back to the
# definition of l1 and requires that f1 has type (int)->int.
# list.iter(fun (f) -> log(string_of(f(42))), l1)
# Actually, this is forbidden too, but the reason is more complex...
# The infered type for the function is ((int)->int)->unit,
# and (int)->int is not a subtype of (?int)->int.
# There's no most general answer here since (?int)->int is not a
# subtype of (int)->int either.
# list.iter(fun (f) -> log(string_of(f(42))), [f2])
# Unlike l1, this is not OK, since we don't leave open subtyping constraints
# while infering types.
# I hope we can make the inference smarter in the future, without obfuscating
# the error messages too much.
# The type error here shows the use of all the displayed positions:
# f1 has type t1, f2 has type t2, t1 should be <: t2
# l2 = [ f2, f1 ]
# An error where contravariance flips the roles of both sides..
# [fun (x) -> x+1, fun (y) -> y^"."]
# An error without much locations..
# TODO An explaination about the missing label would help a lot here.
# def f(f)
# f(output.icecast.vorbis)
# f(output.icecast.mp3)
# end
# This causes an occur-check error.
# TODO The printing of the types breaks the sharing of one EVAR
# across two types. Here the sharing is actually the origin of the occur-check
# error. And it's not easy to understand..
# omega = fun (x) -> x(x)
# Now let's test ad-hoc polymorphism.
echo = fun(x) -> system("echo #{quote(string_of(x))}")
echo("bla")
echo((1,3.12))
echo(1 + 1)
echo(1. + 2.14)
# string is not a Num
# echo("bl"+"a")
echo(1 <= 2)
echo((1,2) == (1,3))
# float <> int
# echo(1 == 2.)
# source is not an Ord
# echo(blank()==blank())
def sum_eq(a,b)
a+b == a
end

View File

@ -2,7 +2,7 @@
# Turn a source into an infaillible source.
# by adding blank when the source is not available.
# @param s the source to turn infaillible
# @category Source / Input
# @category Source / Track Processing
def mksafe(s)
fallback(id="mksafe",track_sensitive=false,[s,blank(id="safe_blank")])
end
@ -87,11 +87,14 @@ end
# Removes all metadata coming from a source
# @category Source / Track Processing
def clear_metadata(s)
def map(m)
[]
end
map_metadata(map,update=false,strip=true,s)
def drop_metadata(s)
map_metadata(fun(_)->[],update=false,strip=true,insert_missing=false,s)
end
# Merge all tracks from a source, provided that it does not fail
# @category Source / Track Processing
def merge_tracks(s)
sequence(merge=true,[s])
end
output.prefered=output.dummy
@ -125,7 +128,7 @@ in = fun () -> blank()
in = fun () -> input.portaudio(id="pa_mic")
%endif
# Create a source from the first available input driver in this list:
# portaudio, alsa, oss, blank
# portaudio, alsa, oss, blank.
# @category Source / Input
def in()
in()
@ -208,12 +211,6 @@ def say_metadata
interactive=false))
end
# Relay the audio stream of Dolebraï, a libre music netradio running liquidsoap.
# @category Source / Input
def dolebrai ()
input.http(id="dolebrai","http://dolebrai.net:8000/dolebrai.ogg")
end
%ifdef soundtouch
# Increases the pitch, making voices sound like on helium.
# @category Source / Sound Processing
@ -237,6 +234,72 @@ def test_process(command)
end
end
# Split an url of the form foo?arg=bar&arg2=bar2
# into ("foo",[("arg","bar"),("arg2","bar2")]
# @category String
# @param uri Url to split
def url.split(uri) =
ret = string.extract(pattern="([^\?]*)\?(.*)",uri)
args = ret["2"]
if args != "" then
l = string.split(separator="&",args)
def f(x) =
ret = string.split(separator="=",x)
(url.decode(list.nth(ret,0)),
url.decode(list.nth(ret,1)))
end
l = list.map(f,l)
(ret["1"],l)
else
(uri,[])
end
end
# Register a server/telnet command to
# update a source's metadata. Returns
# a new source, which will receive the
# updated metadata. Semantics is the
# same as pre 1.0 insert_metadata operator,
# i.e. @insert key1="val1",key2="val2",..@
# @category Source / Track Processing
# @param ~id Force the value of the source ID.
def server.insert_metadata(~id="",s) =
x = insert_metadata(id=id,s)
insert = fst(x)
s = snd(x)
def insert(s) =
l = string.split(separator='([^=]+\s*=\s*"(\\"|[^"])*")\s*,\s*',s)
def f(l,x) =
sub = fun (s) -> string.replace(pattern='\\"',fun (_) -> '"',s)
if x != "" then
ret = string.extract(pattern='([^=]+)\s*=\s*"((?:\\"|[^"])*)"',x)
if ret["1"] != "" then
list.append(l,[(ret["1"],
sub(ret["2"]))])
else
l
end
else
l
end
end
meta = list.fold(f,[],l)
if meta != [] then
insert(meta)
"Done"
else
"Syntax error or no metadata given. \
Use key1=\"val1\",key2=\"val2\",.."
end
end
id = source.id(s)
server.register(namespace="#{id}",
description="Insert a metadata chunk.",
usage="insert key1=\"val1\",key2=\"val2\",..",
"insert",insert)
s
end
# Get the base name of a path.
# Implemented using the corresponding shell command.
# @category System
@ -559,14 +622,18 @@ def enable_replaygain_metadata(
end
# Create a log of clock times for all the clocks initially present.
# The log is in simple format, which you can notably directly use with gnuplot.
# The log is in a simple format which you can directly use with gnuplot.
# @category Liquidsoap
# @param ~interval Polling interval.
def log_clocks(~interval=1.,logfile)
# @param ~delay Delay before setting up the clock logger. This should \
# be used to ensure that the logger starts only after \
# the clocks are created.
# @param unlabeled Path of the log file.
def log_clocks(~delay=0.,~interval=1.,logfile)
# Get the current clocks
clocks = list.map(fst,get_clock_status())
# Column headers
system("echo \# #{string.concat(separator=' ',clocks)} > #{logfile}")
system("echo \# #{string.concat(separator=' ',clocks)} > #{(logfile:string)}")
def report()
status = get_clock_status()
status = list.map(fun (x) -> (fst(x),string_of(snd(x))), status)
@ -574,5 +641,9 @@ def log_clocks(~interval=1.,logfile)
system("echo #{string.concat(separator=' ',status)} >> #{logfile}")
interval
end
add_timeout(interval,report)
if delay<=0. then
add_timeout(interval,report)
else
add_timeout(delay,{add_timeout(interval,report) (-1.)})
end
end

View File

@ -7,6 +7,7 @@
# include configuration #
########################################
%include "library/pervasives.liq"
%include "ls_config.liq"
%include "library.liq"
%include "include_dynamic_vars.liq"
@ -49,8 +50,9 @@ end
#######################################################################
silence = single("/opt/pypo/files/basic/silence.mp3")
jingles_cc = playlist("/opt/pypo/files/jingles/jcc")
fallback_couchcaster = playlist("/opt/pypo/files/fallback_couchcaster")
fallback_couchcaster = audio_to_stereo(fallback_couchcaster)
fallback_airtime = playlist("/opt/pypo/files/basic/silence-playlist.lsp")
fallback_airtime = audio_to_stereo(fallback_airtime)
# default
default = silence
@ -69,7 +71,7 @@ source = fallback(track_sensitive=false,transitions=[dp_to_scheduler],[strip_bla
%include "include_live_in.liq"
live = fallback(track_sensitive=false,[strip_blank(threshold=silence_threshold,length=silence_time,live),fallback_couchcaster])
live = fallback(track_sensitive=false,[strip_blank(threshold=silence_threshold,length=silence_time,live),fallback_airtime])
live = switch(track_sensitive=false, [({!live_active},live)])
source = fallback(track_sensitive=false,transitions=[to_live_s, to_scheduler_s],[live, source])