Skip to content

Instantly share code, notes, and snippets.

@FelixWolf
Created August 9, 2024 04:43
Show Gist options
  • Save FelixWolf/fe32ea648b1abcbb7c66a9665e608581 to your computer and use it in GitHub Desktop.
Save FelixWolf/fe32ea648b1abcbb7c66a9665e608581 to your computer and use it in GitHub Desktop.
Liquidsoap server code (Don't expose the harbor HTTP to public, that's not password protected.. yet)
#!/usr/bin/liquidsoap -v
#settings.init.daemon := true
settings.init.daemon.change_user := true
settings.init.daemon.change_user.group := "liquidsoap"
settings.init.daemon.change_user.user := "liquidsoap"
settings.init.daemon.pidfile := true
settings.init.daemon.pidfile.path := "/etc/liquidsoap/main.pid"
settings.log.level := 2
settings.server.telnet := true
settings.harbor.bind_addrs := ["127.0.0.1"]
let requests = request.queue(id="request")
let playlist = playlist(
"/etc/liquidsoap/music/",
id = "playlist",
mode = "randomize",
reload = 1,
reload_mode = "rounds",
prefetch = 10
)
let placeholder = single(id="fallback", "/etc/liquidsoap/empty.wav")
def update_metadata(m) =
if m["artist"] == "" or m["title"] == "" then
let v = string.split(separator="/", m["filename"])
let basename = list.nth(default="empty.wav", v, list.length(v)-1)
if basename == "empty.wav" then
[("artist", "Softhyena.com"), ("title", "Placeholder")]
else
v = string.split(separator="\\.", basename)
v = list.rev(v)
v = list.tl(v)
v = list.rev(v)
basename = string.concat(separator=".", v)
v = string.split(separator=" - ", basename)
[("artist",list.hd(default="Unknown Artist", v)),("title",string.concat(separator=" - ", list.tl(v)))]
end
else
#log("Have metadata")
[("artist",m["artist"]),("title", m["title"])]
end
end
let radio = fallback(
track_sensitive = true,
[
requests,
playlist,
placeholder
]
)
let radio_out = metadata.map(update_metadata, radio)
output.icecast(
%vorbis.cbr(
bytes_per_page = 1024,
samplerate = 44100,
channels = 2,
bitrate = 64
),
host = "localhost", port = 4232,
password = "<PASSWORD>",
mount = "listen.ogg",
send_icy_metadata = true,
mksafe(radio_out),
name = "<NAME>",
user = "source",
url = "<STREAM URL (Info only)>"
)
################################ HTTP Handling ################################
def util_metadata_to_json(m) =
m = update_metadata(m)
m = metadata.cover.remove(m)
m = metadata.export(m)
data = json()
list.iter(fun (v) -> data.add(fst(v), snd(v)), m)
data
end
def util_request_to_json(x) =
let j = json()
j.add("filename", request.filename(x))
j.add("id", request.id(x))
j.add("metadata", util_metadata_to_json(request.metadata(x)))
j.add("resolved", request.resolved(x))
j.add("status", request.status(x))
j.add("uri", request.uri(x))
j
end
################ Queue ################
def http_request_push(req, response) =
try
# Parse the incoming request body as JSON
let json.parse ({
file
} : {
file: string
}) = req.body()
if null.defined(file) then
let song_path = "/etc/liquidsoap/music/" ^ file
let req = request.create(song_path)
requests.push(req)
response.status_code(200)
response.content_type("application/json; charset=utf-8")
j = json()
j.add("status", "OK")
j.add("queue", requests.length())
response.data(json.stringify(compact=true, j))
else
response.status_code(400)
response.json({
status = "Error",
message = "Invalid request. 'file' must be specified."
})
end
catch err do
response.status_code(500)
response.data(err.message)
end
end
def http_request_skip(_, response) =
try
requests.skip()
response.status_code(200)
response.content_type("application/json; charset=utf-8")
j = json()
j.add("status", "OK")
j.add("queue", requests.length())
response.data(json.stringify(compact=true, j))
catch err do
response.status_code(500)
response.data(err.message)
end
end
def http_request_queue(_, response) =
try
response.status_code(200)
response.content_type("application/json; charset=utf-8")
response.data(json.stringify(compact=true, list.map(util_request_to_json, requests.queue())))
catch err do
response.status_code(500)
response.data(err.message)
end
end
def http_request_info(_, response) =
try
let test = requests.current()
if null.defined(test) then
response.status_code(200)
response.content_type("application/json; charset=utf-8")
response.data(json.stringify(compact=true, util_request_to_json(null.get(test))))
else
response.status_code(404)
end
catch err do
response.status_code(500)
response.data(err.message)
end
end
################ Playlist ################
def http_playlist_skip(_, response) =
try
playlist.skip()
response.status_code(200)
response.content_type("application/json; charset=utf-8")
j = json()
j.add("status", "OK")
j.add("queue", playlist.length())
response.data(json.stringify(compact=true, j))
catch err do
response.status_code(500)
response.data(err.message)
end
end
def http_playlist_queue(_, response) =
try
response.status_code(200)
response.content_type("application/json; charset=utf-8")
response.data(json.stringify(compact=true, list.map(util_request_to_json, playlist.queue())))
catch err do
response.status_code(500)
response.data(err.message)
end
end
def http_playlist_info(_, response) =
try
let test = playlist.current()
if null.defined(test) then
response.status_code(200)
response.content_type("application/json; charset=utf-8")
response.data(json.stringify(compact=true, util_request_to_json(null.get(test))))
else
response.status_code(404)
end
catch err do
response.status_code(500)
response.data(err.message)
end
end
################ Global ################
def http_info(_, response) =
try
response.status_code(200)
j = json()
j.add("duration", radio.duration())
j.add("elapsed", radio.elapsed())
j.add("source", null.case(radio.selected(),
{"No source"},
fun(x)->source.id(x)
))
j.add("remaining", radio.remaining())
j.add("time", radio.time())
j.add("last_metadata", null.case(radio_out.last_metadata(), {json()}, fun(x) -> util_metadata_to_json(x)))
response.content_type("application/json; charset=utf-8")
response.data(json.stringify(compact=true, j))
catch err do
response.status_code(500)
response.data(err.message)
end
end
def http_skip(_, response) =
try
radio.skip()
response.status_code(200)
response.content_type("application/json; charset=utf-8")
j = json()
j.add("status", "OK")
response.data(json.stringify(compact=true, j))
catch err do
response.status_code(500)
response.data(err.message)
end
end
def http_queue(_, response) =
try
response.status_code(200)
response.content_type("application/json; charset=utf-8")
response.data(json.stringify(compact=true, list.map(util_request_to_json, [...requests.queue(), ...playlist.queue()])))
catch err do
response.status_code(500)
response.data(err.message)
end
end
harbor.http.register(port=54777, method="POST", "/request/push", http_request_push)
harbor.http.register(port=54777, method="POST", "/request/skip", http_request_skip)
harbor.http.register(port=54777, method="GET", "/request/queue", http_request_queue)
harbor.http.register(port=54777, method="GET", "/request/info", http_request_info)
harbor.http.register(port=54777, method="POST", "/playlist/skip", http_playlist_skip)
harbor.http.register(port=54777, method="GET", "/playlist/queue", http_playlist_queue)
harbor.http.register(port=54777, method="GET", "/playlist/info", http_playlist_info)
harbor.http.register(port=54777, method="GET", "/info", http_info)
harbor.http.register(port=54777, method="POST", "/skip", http_skip)
harbor.http.register(port=54777, method="GET", "/queue", http_queue)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment