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.to_string())) } // 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 { // by on 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)") } } } } [] }