package main import ( "encoding/json" "log" "net/http" "os" "os/exec" "path/filepath" "strconv" "strings" "time" "github.com/gabriel-vasile/mimetype" ) type Status struct { Playing bool //If the device is playing Loop bool //If playback as loop PlayingFileName string //The playing filename PlayingURL string //The playing URL Gain float64 //The volume currently is playing at, in percentage Mute bool //If the playback should be muted Status string //Status of the device, support {downloading, converting, playing, paused, ready} } type Endpoint struct { Name string RelPath string Desc string Type string AllowRead bool AllowWrite bool Min int Max int Steps float64 Regex string } type SongData struct { Filename string Filesize int64 Ext string } func handleIndex(w http.ResponseWriter, r *http.Request) { //Serve the index sendOK(w) } func handleStatus(w http.ResponseWriter, r *http.Request) { //Send out the current device status js, _ := json.Marshal(deviceStatus) sendJSONResponse(w, string(js)) } func handleEndpoints(w http.ResponseWriter, r *http.Request) { endpoints := []Endpoint{} endpoints = append(endpoints, Endpoint{ Name: "Play", RelPath: "play", Desc: "Toggle player to play or pause", Type: "none", }) endpoints = append(endpoints, Endpoint{ Name: "Stop", RelPath: "stop", Desc: "Stop and flush the buffer of the playing song", Type: "none", }) endpoints = append(endpoints, Endpoint{ Name: "PlayingURL", RelPath: "load", Desc: "Load a network resources to play", Type: "string", }) endpoints = append(endpoints, Endpoint{ Name: "Loop", RelPath: "loop", Desc: "Playback in normal or loop mode", Type: "bool", }) endpoints = append(endpoints, Endpoint{ Name: "Mute", RelPath: "mute", Desc: "Mute the playback", Type: "bool", }) endpoints = append(endpoints, Endpoint{ Name: "FileData", RelPath: "name", Desc: "Set the file data of the playing song", Type: "string", }) endpoints = append(endpoints, Endpoint{ Name: "Gain", RelPath: "gain", Desc: "Set the gain of the device playback, 0 means default", Type: "integer", Max: 2, Min: -6, Steps: 0.1, }) js, _ := json.Marshal(endpoints) sendJSONResponse(w, string(js)) } func handlePlayToggle(w http.ResponseWriter, r *http.Request) { deviceStatus.Playing = !deviceStatus.Playing if deviceStatus.Playing == true { deviceStatus.Status = "playing" } else { deviceStatus.Status = "paused" } sendOK(w) } func handleStop(w http.ResponseWriter, r *http.Request) { globalStopFlag = true } func handleMute(w http.ResponseWriter, r *http.Request) { value, err := mv(r, "value", true) if err != nil { sendErrorResponse(w, "Invalid value given") return } if value == "true" { deviceStatus.Mute = true } else { deviceStatus.Mute = false } } func handleLoad(w http.ResponseWriter, r *http.Request) { _, err := mv(r, "value", false) if err != nil { //Nothing is passed in sendErrorResponse(w, "Invalid value given") return } //There is something here. But for full request purposes, use raw input and split it urlChunks := strings.Split(r.RequestURI, "?value=") finalResourcesEndpoing := strings.Join(urlChunks[1:], "?value=") //Download the file deviceStatus.Status = "downloading" tmpFilename := strconv.Itoa(int(time.Now().Unix())) downloadedFile := filepath.Join("./tmp", tmpFilename) os.Mkdir("./tmp", 0775) log.Println("Downlaod audio file from: ", finalResourcesEndpoing) err = DownloadFile(downloadedFile, finalResourcesEndpoing) if err != nil { log.Println(err.Error()) sendErrorResponse(w, err.Error()) return } //Check if this is a valid audio file mime, err := mimetype.DetectFile(downloadedFile) if err != nil { log.Println(err.Error()) sendErrorResponse(w, err.Error()) return } if strings.Contains(mime.String(), "audio/") { log.Println("Download file is " + mime.String() + ". Starting Convertsion...") } else { log.Println("Not supported media type") sendErrorResponse(w, "Not supported media type") return } //Set the correct extension for the file var fileMime = strings.Split(mime.String(), "/")[1] var needConv = true var finalFilename = "" if fileMime == "mpeg" { //mp3 file os.Rename(downloadedFile, downloadedFile+".mp3") finalFilename = downloadedFile + ".mp3" } else if fileMime == "flac" { //flac os.Rename(downloadedFile, downloadedFile+".flac") finalFilename = downloadedFile + ".flac" } else if fileMime == "wav" { //wav file os.Rename(downloadedFile, downloadedFile+".wav") finalFilename = downloadedFile + ".wav" needConv = false } else { //Unknown. Just append the mime to it os.Rename(downloadedFile, downloadedFile+"."+fileMime) finalFilename = downloadedFile + "." + fileMime } //Start the conversion process (As we need to force the device only playback wav for compeitbility purposes) playReadyFile := filepath.Join("./tmp", strings.TrimSuffix(filepath.Base(downloadedFile), filepath.Ext(filepath.Base(downloadedFile)))+".wav") if needConv { log.Println("Conversion started") deviceStatus.Status = "converting" cmd := exec.Command("ffmpeg", "-i", finalFilename, playReadyFile) out, err := cmd.CombinedOutput() if err != nil { log.Println("Conversion failed: ", string(out)) sendErrorResponse(w, err.Error()) return } log.Println(string(out)) } //Start playing the file log.Println("Conversion finished. Playing audio file.") deviceStatus.Status = "playing" //Set status text to playing deviceStatus.Playing = true //Unpause the playback deviceStatus.PlayingURL = finalResourcesEndpoing //Set the playing URL to target url playFile(playReadyFile) } func handleVol(w http.ResponseWriter, r *http.Request) { val, err := mv(r, "value", false) if err != nil { sendErrorResponse(w, "Invalid gain set") return } //Parse the string to float if s, err := strconv.ParseFloat(val, 64); err == nil { if s > 2 || s < -6 { sendErrorResponse(w, "Invalid ranged gain value") return } //Update the new volume gain deviceStatus.Gain = s } else { sendErrorResponse(w, "Unable to parse new value") return } sendOK(w) } //Handle set name of the playing file. No logical purpose, just for display only //Require struct matching. See the SongInfo struct //Remember to pass it in as encodedURL string func handleSetName(w http.ResponseWriter, r *http.Request) { //Get the song data value, err := mv(r, "value", true) if err != nil { sendErrorResponse(w, "value invalid") return } //Parse the song data thisSongdata := SongData{} err = json.Unmarshal([]byte(value), &thisSongdata) if err != nil { sendErrorResponse(w, "Failed to parse song data.") return } deviceStatus.PlayingFileName = thisSongdata.Filename playingSongData = thisSongdata sendOK(w) } func handleLoop(w http.ResponseWriter, r *http.Request) { value, err := mv(r, "value", true) if err != nil { sendErrorResponse(w, "value invalid") return } if value == "true" { deviceStatus.Loop = true } else { deviceStatus.Loop = false } }