handlers.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. package main
  2. import (
  3. "encoding/json"
  4. "log"
  5. "net/http"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "github.com/gabriel-vasile/mimetype"
  13. )
  14. type Status struct {
  15. Playing bool //If the device is playing
  16. Loop bool //If playback as loop
  17. PlayingFileName string //The playing filename
  18. PlayingURL string //The playing URL
  19. Gain float64 //The volume currently is playing at, in percentage
  20. Mute bool //If the playback should be muted
  21. Status string //Status of the device, support {downloading, converting, playing, paused, ready}
  22. }
  23. type Endpoint struct {
  24. Name string
  25. RelPath string
  26. Desc string
  27. Type string
  28. AllowRead bool
  29. AllowWrite bool
  30. Min int
  31. Max int
  32. Steps float64
  33. Regex string
  34. }
  35. type SongData struct {
  36. Filename string
  37. Filesize int64
  38. Ext string
  39. }
  40. func handleIndex(w http.ResponseWriter, r *http.Request) {
  41. //Serve the index
  42. sendOK(w)
  43. }
  44. func handleStatus(w http.ResponseWriter, r *http.Request) {
  45. //Send out the current device status
  46. js, _ := json.Marshal(deviceStatus)
  47. sendJSONResponse(w, string(js))
  48. }
  49. func handleEndpoints(w http.ResponseWriter, r *http.Request) {
  50. endpoints := []Endpoint{}
  51. endpoints = append(endpoints, Endpoint{
  52. Name: "Play",
  53. RelPath: "play",
  54. Desc: "Toggle player to play or pause",
  55. Type: "none",
  56. })
  57. endpoints = append(endpoints, Endpoint{
  58. Name: "Stop",
  59. RelPath: "stop",
  60. Desc: "Stop and flush the buffer of the playing song",
  61. Type: "none",
  62. })
  63. endpoints = append(endpoints, Endpoint{
  64. Name: "PlayingURL",
  65. RelPath: "load",
  66. Desc: "Load a network resources to play",
  67. Type: "string",
  68. })
  69. endpoints = append(endpoints, Endpoint{
  70. Name: "Loop",
  71. RelPath: "loop",
  72. Desc: "Playback in normal or loop mode",
  73. Type: "bool",
  74. })
  75. endpoints = append(endpoints, Endpoint{
  76. Name: "Mute",
  77. RelPath: "mute",
  78. Desc: "Mute the playback",
  79. Type: "bool",
  80. })
  81. endpoints = append(endpoints, Endpoint{
  82. Name: "FileData",
  83. RelPath: "name",
  84. Desc: "Set the file data of the playing song",
  85. Type: "string",
  86. })
  87. endpoints = append(endpoints, Endpoint{
  88. Name: "Gain",
  89. RelPath: "gain",
  90. Desc: "Set the gain of the device playback, 0 means default",
  91. Type: "integer",
  92. Max: 2,
  93. Min: -6,
  94. Steps: 0.1,
  95. })
  96. js, _ := json.Marshal(endpoints)
  97. sendJSONResponse(w, string(js))
  98. }
  99. func handlePlayToggle(w http.ResponseWriter, r *http.Request) {
  100. deviceStatus.Playing = !deviceStatus.Playing
  101. if deviceStatus.Playing == true {
  102. deviceStatus.Status = "playing"
  103. } else {
  104. deviceStatus.Status = "paused"
  105. }
  106. sendOK(w)
  107. }
  108. func handleStop(w http.ResponseWriter, r *http.Request) {
  109. globalStopFlag = true
  110. }
  111. func handleMute(w http.ResponseWriter, r *http.Request) {
  112. value, err := mv(r, "value", true)
  113. if err != nil {
  114. sendErrorResponse(w, "Invalid value given")
  115. return
  116. }
  117. if value == "true" {
  118. deviceStatus.Mute = true
  119. } else {
  120. deviceStatus.Mute = false
  121. }
  122. }
  123. func handleLoad(w http.ResponseWriter, r *http.Request) {
  124. _, err := mv(r, "value", false)
  125. if err != nil {
  126. //Nothing is passed in
  127. sendErrorResponse(w, "Invalid value given")
  128. return
  129. }
  130. //There is something here. But for full request purposes, use raw input and split it
  131. urlChunks := strings.Split(r.RequestURI, "?value=")
  132. finalResourcesEndpoing := strings.Join(urlChunks[1:], "?value=")
  133. //Download the file
  134. deviceStatus.Status = "downloading"
  135. tmpFilename := strconv.Itoa(int(time.Now().Unix()))
  136. downloadedFile := filepath.Join("./tmp", tmpFilename)
  137. os.Mkdir("./tmp", 0775)
  138. log.Println("Downlaod audio file from: ", finalResourcesEndpoing)
  139. err = DownloadFile(downloadedFile, finalResourcesEndpoing)
  140. if err != nil {
  141. log.Println(err.Error())
  142. sendErrorResponse(w, err.Error())
  143. return
  144. }
  145. //Check if this is a valid audio file
  146. mime, err := mimetype.DetectFile(downloadedFile)
  147. if err != nil {
  148. log.Println(err.Error())
  149. sendErrorResponse(w, err.Error())
  150. return
  151. }
  152. if strings.Contains(mime.String(), "audio/") {
  153. log.Println("Download file is " + mime.String() + ". Starting Convertsion...")
  154. } else {
  155. log.Println("Not supported media type")
  156. sendErrorResponse(w, "Not supported media type")
  157. return
  158. }
  159. //Set the correct extension for the file
  160. var fileMime = strings.Split(mime.String(), "/")[1]
  161. var needConv = true
  162. var finalFilename = ""
  163. if fileMime == "mpeg" {
  164. //mp3 file
  165. os.Rename(downloadedFile, downloadedFile+".mp3")
  166. finalFilename = downloadedFile + ".mp3"
  167. } else if fileMime == "flac" {
  168. //flac
  169. os.Rename(downloadedFile, downloadedFile+".flac")
  170. finalFilename = downloadedFile + ".flac"
  171. } else if fileMime == "wav" {
  172. //wav file
  173. os.Rename(downloadedFile, downloadedFile+".wav")
  174. finalFilename = downloadedFile + ".wav"
  175. needConv = false
  176. } else {
  177. //Unknown. Just append the mime to it
  178. os.Rename(downloadedFile, downloadedFile+"."+fileMime)
  179. finalFilename = downloadedFile + "." + fileMime
  180. }
  181. //Start the conversion process (As we need to force the device only playback wav for compeitbility purposes)
  182. playReadyFile := filepath.Join("./tmp", strings.TrimSuffix(filepath.Base(downloadedFile), filepath.Ext(filepath.Base(downloadedFile)))+".wav")
  183. if needConv {
  184. log.Println("Conversion started")
  185. deviceStatus.Status = "converting"
  186. cmd := exec.Command("ffmpeg", "-i", finalFilename, playReadyFile)
  187. out, err := cmd.CombinedOutput()
  188. if err != nil {
  189. log.Println("Conversion failed: ", string(out))
  190. sendErrorResponse(w, err.Error())
  191. return
  192. }
  193. log.Println(string(out))
  194. }
  195. //Start playing the file
  196. log.Println("Conversion finished. Playing audio file.")
  197. deviceStatus.Status = "playing" //Set status text to playing
  198. deviceStatus.Playing = true //Unpause the playback
  199. deviceStatus.PlayingURL = finalResourcesEndpoing //Set the playing URL to target url
  200. playFile(playReadyFile)
  201. }
  202. func handleVol(w http.ResponseWriter, r *http.Request) {
  203. val, err := mv(r, "value", false)
  204. if err != nil {
  205. sendErrorResponse(w, "Invalid gain set")
  206. return
  207. }
  208. //Parse the string to float
  209. if s, err := strconv.ParseFloat(val, 64); err == nil {
  210. if s > 2 || s < -6 {
  211. sendErrorResponse(w, "Invalid ranged gain value")
  212. return
  213. }
  214. //Update the new volume gain
  215. deviceStatus.Gain = s
  216. } else {
  217. sendErrorResponse(w, "Unable to parse new value")
  218. return
  219. }
  220. sendOK(w)
  221. }
  222. //Handle set name of the playing file. No logical purpose, just for display only
  223. //Require struct matching. See the SongInfo struct
  224. //Remember to pass it in as encodedURL string
  225. func handleSetName(w http.ResponseWriter, r *http.Request) {
  226. //Get the song data
  227. value, err := mv(r, "value", true)
  228. if err != nil {
  229. sendErrorResponse(w, "value invalid")
  230. return
  231. }
  232. //Parse the song data
  233. thisSongdata := SongData{}
  234. err = json.Unmarshal([]byte(value), &thisSongdata)
  235. if err != nil {
  236. sendErrorResponse(w, "Failed to parse song data.")
  237. return
  238. }
  239. deviceStatus.PlayingFileName = thisSongdata.Filename
  240. playingSongData = thisSongdata
  241. sendOK(w)
  242. }
  243. func handleLoop(w http.ResponseWriter, r *http.Request) {
  244. value, err := mv(r, "value", true)
  245. if err != nil {
  246. sendErrorResponse(w, "value invalid")
  247. return
  248. }
  249. if value == "true" {
  250. deviceStatus.Loop = true
  251. } else {
  252. deviceStatus.Loop = false
  253. }
  254. }