mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00

- 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)
206 lines
7.0 KiB
Plaintext
Executable File
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)")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
[]
|
|
}
|