2020-02-10 15:52:44 +00:00
|
|
|
%include "secrets.liq"
|
|
|
|
|
|
|
|
set("log.file", true)
|
|
|
|
set("log.file.path", "/home/liquidsoap/spradio.log")
|
|
|
|
set("log.stdout", true)
|
|
|
|
set("log.level", 4)
|
|
|
|
|
|
|
|
set("server.telnet", false)
|
|
|
|
|
|
|
|
set("scheduler.generic_queues", 5)
|
|
|
|
set("scheduler.fast_queues", 3)
|
|
|
|
set("scheduler.non_blocking_queues", 3)
|
|
|
|
|
|
|
|
set("audio.converter.samplerate.libsamplerate.quality", "best")
|
|
|
|
|
|
|
|
security = single(id="default", fallback_audio)
|
|
|
|
|
|
|
|
# Tweaked custom crossfade to deal with jingles..
|
|
|
|
def smart_cross(~start_next=5.,~fade_in=3.,~fade_out=3.,
|
|
|
|
~default=(fun (a,b) -> sequence([a, b])),
|
|
|
|
~high=-15., ~medium=-32., ~margin=4.,
|
|
|
|
~width=2.,~conservative=false,s)
|
|
|
|
fade.out = fade.out(type="sin", duration=fade_out)
|
|
|
|
fade.in = fade.in(type="sin", duration=fade_in)
|
|
|
|
add = fun (a,b) -> add(normalize=false, [b, a])
|
|
|
|
log = log(label="smart_cross")
|
|
|
|
|
|
|
|
def transition(a,b,ma,mb,sa,sb)
|
|
|
|
list.iter(fun(x)-> log(level=4, "Before: #{x}"), ma)
|
|
|
|
list.iter(fun(x)-> log(level=4, "After : #{x}"), mb)
|
|
|
|
|
|
|
|
if ma["type"] == "J" or mb["type"] == "J" then
|
|
|
|
log("Old or new file is a jingle: sequenced transition.")
|
|
|
|
sequence([sa, sb])
|
|
|
|
elsif
|
|
|
|
# Do not fade if it's already very low.
|
|
|
|
b >= a + margin and a <= medium and b <= high
|
|
|
|
then
|
|
|
|
log("new >= old + margin, old <= medium and new <= high.")
|
|
|
|
log("Do not fade if it's already very low.")
|
|
|
|
log("Transition: crossed, no fade.")
|
|
|
|
add(sa, sb)
|
|
|
|
|
|
|
|
# What to do with a loud end and a quiet beginning ?
|
|
|
|
# A good idea is to use a jingle to separate the two tracks,
|
|
|
|
# but that's another story.
|
|
|
|
else
|
|
|
|
# Otherwise, A and B are just too loud to overlap nicely,
|
|
|
|
# or the difference between them is too large and overlapping would
|
|
|
|
# completely mask one of them.
|
|
|
|
# log("No transition: using default.")
|
|
|
|
# default(sa, sb)
|
|
|
|
log("Transition: crossed, fade-in, fade-out.")
|
|
|
|
add(fade.out(sa), fade.in(sb))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
cross(width=width, duration=start_next,
|
|
|
|
conservative=conservative, transition,s)
|
|
|
|
end
|
|
|
|
|
2020-02-10 17:19:28 +00:00
|
|
|
def get_http_data(headers, url) =
|
|
|
|
if string.contains(prefix='https', url) then
|
|
|
|
let (status, _, data) = https.get(headers=headers, url)
|
|
|
|
let (_, status_code, _) = status
|
|
|
|
(status_code, data)
|
|
|
|
else
|
|
|
|
let (status, _, data) = http.get(headers=headers, url)
|
|
|
|
let (_, status_code, _) = status
|
|
|
|
(status_code, data)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def post_http_data(headers, postdata, url) =
|
|
|
|
if string.contains(prefix='https', url) then
|
|
|
|
let (status, _, data) = https.post(headers=headers, data=postdata, url)
|
|
|
|
let (_, status_code, _) = status
|
|
|
|
(status_code, data)
|
|
|
|
else
|
|
|
|
let (status, _, data) = http.post(headers=headers, data=postdata, url)
|
|
|
|
let (_, status_code, _) = status
|
|
|
|
(status_code, data)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-02-10 15:52:44 +00:00
|
|
|
def next_song() =
|
|
|
|
log = log(label="next_song")
|
|
|
|
|
2020-02-10 16:00:12 +00:00
|
|
|
log("Requesting the next song from #{url_next}")
|
2020-02-10 17:19:28 +00:00
|
|
|
let (status_code, data) = get_http_data(api_headers_next, url_next)
|
2020-02-10 15:52:44 +00:00
|
|
|
|
|
|
|
log(data)
|
|
|
|
|
|
|
|
if status_code == 200 then
|
|
|
|
request.create(data)
|
|
|
|
else
|
|
|
|
request.create(fallback_annotate)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def change_meta(m) =
|
|
|
|
log = log(label="change_meta")
|
|
|
|
|
|
|
|
artist = m["artist"]
|
|
|
|
game = m["game"]
|
|
|
|
title = m["title"]
|
|
|
|
|
|
|
|
log("Request ID: "^string.quote(m["req_id"]))
|
|
|
|
log("Artist: #{artist} -- Game: #{game} -- Title: #{title}")
|
|
|
|
|
|
|
|
[("artist","#{artist}"),
|
|
|
|
("title","#{title} [#{game}]"),
|
|
|
|
("req_id",m["req_id"]),
|
|
|
|
("type",m["type"]),
|
|
|
|
("game",m["game"]),
|
|
|
|
("replay_gain",m["replay_gain"])]
|
|
|
|
end
|
|
|
|
|
|
|
|
def just_played(m) =
|
|
|
|
log = log(label="just_played")
|
|
|
|
|
|
|
|
if m["req_id"] != "" then
|
2020-02-10 16:01:57 +00:00
|
|
|
log('Letting server know we played request ID #{m["req_id"]} here: #{url_played}')
|
2020-02-10 15:52:44 +00:00
|
|
|
played_song = json_of(compact=true, [("song_request", int_of_string(m["req_id"]))])
|
2020-02-10 17:19:28 +00:00
|
|
|
let (status_code, data) = post_http_data(api_headers_played, played_song, url_played)
|
2020-02-10 15:52:44 +00:00
|
|
|
|
|
|
|
if status_code == 204 then
|
|
|
|
log('Successfully reported that the song was played.')
|
|
|
|
else
|
|
|
|
log('Error while reporting the song was played: #{data}')
|
|
|
|
end
|
|
|
|
else
|
|
|
|
log("No request ID! Stream just started or there are other problems.")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
radio = request.dynamic(id="main", default_duration=120., length=60., next_song)
|
|
|
|
radio = fallback(track_sensitive=false, [radio, security])
|
|
|
|
radio = map_metadata(update=false, change_meta, radio)
|
|
|
|
radio = on_metadata(id="main", just_played, radio)
|
|
|
|
radio = amplify(1., override="replay_gain", radio)
|
|
|
|
radio = smart_cross(radio)
|
|
|
|
|
|
|
|
output.icecast(%mp3,
|
|
|
|
encoding="UTF-8", protocol="http",
|
|
|
|
name=radio_name, description=radio_description, genre=radio_genre, url=radio_url_main,
|
|
|
|
host=stream_address, port=stream_port, password=stream_password, mount="stream128.mp3",
|
|
|
|
radio)
|
|
|
|
output.icecast(%vorbis(samplerate=44100, channels=2, quality=0.4),
|
|
|
|
encoding="UTF-8", protocol="http",
|
|
|
|
name=radio_name, description=radio_description, genre=radio_genre, url=radio_url_main,
|
|
|
|
host=stream_address, port=stream_port, password=stream_password, mount="stream128.ogg",
|
|
|
|
radio)
|
|
|
|
output.icecast(%vorbis(samplerate=44100, channels=2, quality=0.0),
|
|
|
|
encoding="UTF-8", protocol="http",
|
|
|
|
name=radio_name, description=radio_description, genre=radio_genre, url=radio_url_main,
|
|
|
|
host=stream_address, port=stream_port, password=stream_password, mount="stream64.ogg",
|
|
|
|
radio)
|