Created
August 9, 2024 04:43
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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