mers/musicdb_remote.txt
Dummi26 ce61749260 - Added assume_no_enum() because the Err enum is used at least as often as [] for reporting fails.
- changed substring(a b) behavior from "b is the max length of the resulting string" to "b is the exclusive end index, unless it is negative, in which case its abs() value is the maximum length".
- fixed a bug in libs/path
- added the http_requests library, which can be used to make very basic GET requests
- fixed a bug in the gui library that would mess up button handling
- you can now escape comments using a backslash `\`: \// will turn into the literal //, \/* will turn into the literal /*. Useful for URLs (because comments work in string literals). Putting a backslash before a linebreak will also ignore that linebreak (useful in long string literals)
2023-04-13 03:04:47 +02:00

206 lines
7.0 KiB
Plaintext
Executable File

lib mers_libs/http_requests
lib mers_libs/gui
// MusicDB is a private project of mine.
// Basically, a SBC is hooked up to some speakers.
// It has a database of my songs and can play them.
// It can be controlled through HTTP requests, such as
// p- to stop
// p+ to play
// qi to get the queue index / playback position
// l to list all the songs
// ql or qL to list the queue
// ...
// this mers program should work as a very simple gui to remotely control
// the playback of songs. It should be able to add/remove songs to/from the queue,
// to start/stop playback, and to display some information about the current song.
// the url used to connect to the SBC and use the HTTP api
api_url = "http:\//192.168.2.103:26315/api/raw?"
// start the GUI
base = gui_init()
set_title("Mers MusicDB Remote")
playback_controls = base.gui_add(Row: [])
play_button = playback_controls.gui_add(Button: "Play")
stop_button = playback_controls.gui_add(Button: "Stop")
// these functions abstract away the api interactions
// because they aren't pretty.
// feel free to skip past this section and look at the gui code instead
fn make_api_request(to_api string) {
http_get(api_url.add(to_api))
}
fn get_queue_index() {
r = make_api_request("qi")
switch! r {
string {
r.regex("[0-9]").assume_no_enum().get(0).assume1().parse_int().assume1()
}
// return the error
Err(ErrBuildingRequest(string)/ErrGettingResponseText(string)) r
}
}
fn get_db_contents() {
// list only 7 = 1 (title) + 2 (artist) + 4 (album)
r = make_api_request("lo7,")
switch! r {
string {
entries = r.regex("#.*:\n.*\n.*\n.*").assume_no_enum()
// initialize the list with a default value
// because mers doesnt know the inner type without it.
db = [[0 "title" "artist" "album"] ...]
&db.remove(0)
for entry entries {
lines = entry.regex(".*")
switch! lines {
[string] {
index_line = &lines.get(0).assume1()
index = index_line.substring(1 index_line.len().sub(1)).parse_int().assume1()
title = &lines.get(1).assume1().substring(1)
artist = &lines.get(2).assume1().substring(1)
album = &lines.get(3).assume1().substring(1)
&db.push([index title artist album])
}
Err(string) {
println("invalid response from server; 3u123128731289")
exit(1)
}
}
}
db
}
Err(ErrBuildingRequest(string)/ErrGettingResponseText(string)) {
println("couldn't request db from server:")
print(" ")
println(r.to_string())
exit(1)
}
}
}
fn get_queue() {
r = make_api_request("ql")
switch! r {
string {
queue = [0 ...]
&queue.remove(0)
for index r.regex("[0-9]+[ \n]").assume_no_enum() {
&queue.push(index.substring(0 index.len().sub(1)).parse_int().assume1())
}
queue
}
Err(ErrBuildingRequest(string)/ErrGettingResponseText(string)) {
println("couldn't request queue from server:")
print(" ")
println(r.to_string())
exit(1)
}
}
}
fn play() {
r = make_api_request("p+")
switch! r {
// success! but nothing to return.
string []
// return the error
Err(ErrBuildingRequest(string)/ErrGettingResponseText(string)) r
}
}
fn stop() {
r = make_api_request("p-")
switch! r {
// success! but nothing to return.
string []
// return the error
Err(ErrBuildingRequest(string)/ErrGettingResponseText(string)) r
}
}
fn queue_song(song_id int) {
make_api_request("q+{0}".format(song_id))
}
// fetch the database contents (a list of songs)
database = get_db_contents()
// this is where all the songs will be displayed.
songs_gui = base.gui_add(Row: [])
queue_list = songs_gui.gui_add(Column: [])
library = songs_gui.gui_add(Column: [])
limit = 0 // set to 0 to disable library length limit
for entry database {
// <SONG> by <ARTIST> on <ALBUM>
song = library.gui_add(Row: [])
song.gui_add(Button: entry.1)
song.gui_add(Text: "by {0} on {1}".format(entry.2 entry.3))
limit = limit.sub(1)
if limit.eq(0) [[]] else []
}
// regularly update the queue
thread(() {
queue_index = get_queue_index().assume_no_enum("if the server isn't reachable, it's ok to crash")
queue_list_inner = queue_list.gui_add(Column: [])
prev_artist = ""
prev_album = ""
for song_id get_queue() {
this_is_playing = if queue_index.eq(0) { queue_index = -1 true } else if queue_index.gt(0) { queue_index = queue_index.sub(1) false } else { false }
song = &database.get(song_id).assume1()
row = queue_list_inner.gui_add(Row: [])
text = if this_is_playing ">> " else ""
text = text.add(song.1)
if prev_artist.eq(song.2) {
if prev_album.eq(song.3) {
} else {
text = text.add("(on {0})".format(song.3))
}
} else {
text = text.add("(by {0} on {1})".format(song.2 song.3))
}
row.gui_add(Text: text)
[]
}
sleep(10)
queue_list_inner.gui_remove()
})
while {
for event gui_updates() {
switch! event {
ButtonPressed([int]) {
e = event.noenum()
println("Pressed button {0}".format(e.to_string()))
match e {
&e.eq(&play_button) {
println("pressed play.")
play()
}
&e.eq(&stop_button) {
println("pressed stop.")
stop()
}
{
// the button is in a row that is in the library,
// so pop()ing twice gets the library path.
// THIS WILL BREAK IF THE GUI LIBRARY SWITCHES TO IDs INSTEAD OF PATHS!
last = &e.pop().assume1()
slast = &e.pop().assume1()
matches = &e.eq(&library)
&e.push(slast)
&e.push(last)
if matches e else []
} {
// the second to last part of the path is the row within library, which is also the index in the db
song = &database.get(&e.get(e.len().sub(2)).assume1()).assume1()
println("Added song \"{0}\" to queue.".format(song.1))
queue_song(song.0)
}
true println("A different button was pressed (unreachable)")
}
}
}
}
[]
}