123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- 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
- }
- }
|