main.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package main
  2. import (
  3. "fmt"
  4. "http"
  5. "exp/template"
  6. "os"
  7. "json"
  8. "strconv"
  9. "time"
  10. )
  11. type Song struct {
  12. Yid string
  13. Title string
  14. User string
  15. }
  16. type Playlist struct {
  17. Id string
  18. }
  19. var templates map[string]*template.Template
  20. const debug = true
  21. func home(w http.ResponseWriter, r *http.Request) {
  22. fmt.Fprintf(w, "Welcome to Audio Axis!")
  23. }
  24. func playlist(w http.ResponseWriter, r *http.Request) {
  25. id := r.URL.Path[len("/p/"):]
  26. if len(id) != 8 {
  27. http.Redirect(w, r, "/", 303)
  28. return
  29. }
  30. playlist := Playlist{Id: id}
  31. if debug {
  32. t, err := template.ParseFile("templates/p.html")
  33. if err != nil {
  34. http.Error(w, err.String(), http.StatusInternalServerError)
  35. return
  36. }
  37. err = t.Execute(w, playlist)
  38. if err != nil {
  39. w.Write([]byte(err.String()))
  40. fmt.Fprintln(os.Stderr, err.String())
  41. return
  42. }
  43. } else {
  44. err := templates["p"].Execute(w, playlist)
  45. if err != nil {
  46. fmt.Fprintln(os.Stderr, err.String())
  47. }
  48. }
  49. }
  50. func add(w http.ResponseWriter, r *http.Request) {
  51. q := r.URL.Query()
  52. db := <-dbPool
  53. defer func () {dbPool <- db}()
  54. pid := getpid(db, q.Get("pid"))
  55. if pid == -1 {
  56. http.Error(w, "invalid pid", http.StatusBadRequest)
  57. return
  58. }
  59. err := db.Start()
  60. if err != nil {
  61. http.Error(w, err.String(), http.StatusInternalServerError)
  62. return
  63. }
  64. // unfortunately, MAX(`order`) returns NULL when there is nothing
  65. // so query for COUNT(`sid`)
  66. count, err := queryInt(db, "SELECT COUNT(`sid`) FROM `song` WHERE `pid` = ?", pid)
  67. if err != nil {
  68. db.Rollback()
  69. http.Error(w, err.String(), http.StatusInternalServerError)
  70. return
  71. }
  72. _, err = prepare(db,
  73. "INSERT INTO `song` (`pid`,`yid`,`title`,`user`,`order`) VALUES(?, ?, ?, ?, ?)",
  74. pid, q.Get("yid"), q.Get("title"), q.Get("user"), count)
  75. if err != nil {
  76. db.Rollback()
  77. http.Error(w, err.String(), http.StatusInternalServerError)
  78. return
  79. }
  80. err = db.Commit()
  81. if err != nil {
  82. http.Error(w, err.String(), http.StatusInternalServerError)
  83. return
  84. }
  85. w.Write([]byte("1"))
  86. addUpdate(pid, addAction,
  87. &Song{Yid: q.Get("yid"), Title: q.Get("title"), User: q.Get("user")})
  88. }
  89. func remove(w http.ResponseWriter, r *http.Request) {
  90. q := r.URL.Query()
  91. db := <-dbPool
  92. defer func () {dbPool <- db}()
  93. pid := getpid(db, q.Get("pid"))
  94. if pid == -1 {
  95. http.Error(w, "invalid pid", http.StatusBadRequest)
  96. return
  97. }
  98. err := db.Start()
  99. if err != nil {
  100. http.Error(w, err.String(), http.StatusInternalServerError)
  101. return
  102. }
  103. order, err := queryInt(db, "SELECT `order` FROM `song` WHERE `yid` = ? AND `pid` = ?",
  104. q.Get("yid"), pid)
  105. if err != nil {
  106. db.Rollback()
  107. http.Error(w, err.String(), http.StatusInternalServerError)
  108. return
  109. }
  110. _, err = prepare(db, "DELETE FROM `song` WHERE `pid` = ? AND yid = ?",
  111. pid, q.Get("yid"))
  112. if err != nil {
  113. db.Rollback()
  114. http.Error(w, err.String(), http.StatusInternalServerError)
  115. return
  116. }
  117. _, err = prepare(db, "UPDATE `song` SET `order` = `order`-1 WHERE `order` > ? AND `pid` = ?",
  118. order, pid)
  119. if err != nil {
  120. db.Rollback()
  121. http.Error(w, err.String(), http.StatusInternalServerError)
  122. return
  123. }
  124. err = db.Commit()
  125. if err != nil {
  126. http.Error(w, err.String(), http.StatusInternalServerError)
  127. return
  128. }
  129. w.Write([]byte("1"))
  130. addUpdate(pid, removeAction, &Song{Yid: q.Get("yid")})
  131. }
  132. func move(w http.ResponseWriter, r *http.Request) {
  133. q := r.URL.Query()
  134. db := <-dbPool
  135. defer func () {dbPool <- db}()
  136. pid := getpid(db, q.Get("pid"))
  137. if pid == -1 {
  138. http.Error(w, "invalid pid", http.StatusBadRequest)
  139. return
  140. }
  141. direction, err := strconv.Atoui(q.Get("direction"))
  142. if err != nil {
  143. http.Error(w, err.String(), http.StatusInternalServerError)
  144. return
  145. }
  146. err = db.Start()
  147. if err != nil {
  148. http.Error(w, err.String(), http.StatusInternalServerError)
  149. return
  150. }
  151. order, err := queryInt(db, "SELECT `order` FROM `song` WHERE `yid` = ? AND `pid` = ?",
  152. q.Get("yid"), pid)
  153. if err != nil {
  154. db.Rollback()
  155. http.Error(w, err.String(), http.StatusInternalServerError)
  156. return
  157. }
  158. newOrder := order
  159. if direction == moveUpAction && order > 0 {
  160. newOrder--
  161. } else if direction == moveDownAction {
  162. newOrder++
  163. } else {
  164. db.Rollback()
  165. http.Error(w, "invalid direction or cannot move up", http.StatusBadRequest)
  166. return
  167. }
  168. query, err := prepare(db, "UPDATE `song` SET `order` = ? WHERE `order` = ? AND `pid` = ?",
  169. order, newOrder, pid)
  170. if err != nil {
  171. db.Rollback()
  172. http.Error(w, err.String(), http.StatusInternalServerError)
  173. return
  174. } else if query.AffectedRows != 1 {
  175. db.Rollback()
  176. http.Error(w, "invalid direction for this song", http.StatusBadRequest)
  177. return
  178. }
  179. // there are now two songs with that order, so also check yid
  180. _, err = prepare(db, "UPDATE `song` SET `order` = ? WHERE `order` = ? AND `pid` = ? AND `yid` = ?",
  181. newOrder, order, pid, q.Get("yid"))
  182. if err != nil {
  183. db.Rollback()
  184. http.Error(w, err.String(), http.StatusInternalServerError)
  185. return
  186. }
  187. err = db.Commit()
  188. if err != nil {
  189. http.Error(w, err.String(), http.StatusInternalServerError)
  190. return
  191. }
  192. w.Write([]byte("1"))
  193. addUpdate(pid, direction, &Song{Yid: q.Get("yid")})
  194. }
  195. func poll(w http.ResponseWriter, r *http.Request) {
  196. q := r.URL.Query()
  197. timestamp := q.Get("timestamp")
  198. if timestamp == "-1" {
  199. db := <-dbPool
  200. defer func () {dbPool <- db}()
  201. query, err := prepare(db,
  202. "SELECT `yid`,`title`,`user` FROM `playlist` JOIN `song` WHERE `id` = ? ORDER BY `order` ASC",
  203. q.Get("pid"))
  204. updates := make([]Update, 0, 2)
  205. for {
  206. song := new(Song)
  207. query.BindResult(&song.Yid, &song.Title, &song.User)
  208. eof, err := query.Fetch()
  209. if err != nil {
  210. http.Error(w, err.String(), http.StatusInternalServerError)
  211. return
  212. }
  213. if eof {
  214. break
  215. }
  216. updates = append(updates, Update{Song: song, Action: addAction, Timestamp: time.Nanoseconds()})
  217. }
  218. err = query.FreeResult()
  219. if err != nil {
  220. http.Error(w, err.String(), http.StatusInternalServerError)
  221. return
  222. }
  223. output, err := json.MarshalForHTML(updates)
  224. if err != nil {
  225. http.Error(w, err.String(), http.StatusInternalServerError)
  226. return
  227. }
  228. w.Write(output)
  229. } else {
  230. timestamp, err := strconv.Atoi64(q.Get("timestamp"))
  231. if err != nil {
  232. http.Error(w, err.String(), http.StatusInternalServerError)
  233. return
  234. }
  235. update := getUpdates(q.Get("pid"), timestamp)
  236. if update != nil {
  237. w.Write([]byte("["))
  238. for update != nil {
  239. output, err := json.MarshalForHTML(update)
  240. if err == nil {
  241. w.Write(output)
  242. }
  243. update = update.Next
  244. if update != nil {
  245. w.Write([]byte(","))
  246. }
  247. }
  248. w.Write([]byte("]"))
  249. return
  250. }
  251. w.Write([]byte("[]"))
  252. }
  253. }
  254. func main() {
  255. templates = make(map[string]*template.Template)
  256. for _, path := range []string{"p"} {
  257. t, err := template.ParseFile("templates/" + path + ".html")
  258. if err != nil {
  259. fmt.Println(err)
  260. return
  261. }
  262. templates[path] = t
  263. }
  264. initDb()
  265. http.HandleFunc("/", home)
  266. http.HandleFunc("/p/", playlist)
  267. http.HandleFunc("/add/", add)
  268. http.HandleFunc("/remove/", remove)
  269. http.HandleFunc("/move/", move)
  270. http.HandleFunc("/poll/", poll)
  271. err := http.ListenAndServe("localhost:8000", nil)
  272. if err != nil {
  273. fmt.Println(err)
  274. os.Exit(1)
  275. }
  276. }