main.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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. maxOrder, err := queryInt(db, "SELECT MAX(`order`) FROM `song` WHERE pid = ?", pid)
  65. if err != nil {
  66. db.Rollback()
  67. http.Error(w, err.String(), http.StatusInternalServerError)
  68. return
  69. }
  70. _, err = prepare(db,
  71. "INSERT INTO `song` (`pid`,`yid`,`title`,`user`,`order`) VALUES(?, ?, ?, ?, ?)",
  72. pid, q.Get("yid"), q.Get("title"), q.Get("user"), maxOrder + 1)
  73. if err != nil {
  74. db.Rollback()
  75. http.Error(w, err.String(), http.StatusInternalServerError)
  76. return
  77. }
  78. err = db.Commit()
  79. if err != nil {
  80. http.Error(w, err.String(), http.StatusInternalServerError)
  81. return
  82. }
  83. w.Write([]byte("1"))
  84. addUpdate(pid, addAction,
  85. &Song{Yid: q.Get("yid"), Title: q.Get("title"), User: q.Get("user")})
  86. }
  87. func remove(w http.ResponseWriter, r *http.Request) {
  88. q := r.URL.Query()
  89. db := <-dbPool
  90. defer func () {dbPool <- db}()
  91. pid := getpid(db, q.Get("pid"))
  92. if pid == -1 {
  93. http.Error(w, "invalid pid", http.StatusBadRequest)
  94. return
  95. }
  96. err := db.Start()
  97. if err != nil {
  98. http.Error(w, err.String(), http.StatusInternalServerError)
  99. return
  100. }
  101. order, err := queryInt(db, "SELECT `order` FROM `song` WHERE `yid` = ? AND `pid` = ?",
  102. q.Get("yid"), pid)
  103. if err != nil {
  104. db.Rollback()
  105. http.Error(w, err.String(), http.StatusInternalServerError)
  106. return
  107. }
  108. _, err = prepare(db, "DELETE FROM `song` WHERE `pid` = ? AND yid = ?",
  109. pid, q.Get("yid"))
  110. if err != nil {
  111. db.Rollback()
  112. http.Error(w, err.String(), http.StatusInternalServerError)
  113. return
  114. }
  115. _, err = prepare(db, "UPDATE `song` SET `order` = `order`-1 WHERE `order` > ? AND `pid` = ?",
  116. order, pid)
  117. if err != nil {
  118. db.Rollback()
  119. http.Error(w, err.String(), http.StatusInternalServerError)
  120. return
  121. }
  122. err = db.Commit()
  123. if err != nil {
  124. http.Error(w, err.String(), http.StatusInternalServerError)
  125. return
  126. }
  127. w.Write([]byte("1"))
  128. addUpdate(pid, removeAction, &Song{Yid: q.Get("yid")})
  129. }
  130. func move(w http.ResponseWriter, r *http.Request) {
  131. q := r.URL.Query()
  132. db := <-dbPool
  133. defer func () {dbPool <- db}()
  134. pid := getpid(db, q.Get("pid"))
  135. if pid == -1 {
  136. http.Error(w, "invalid pid", http.StatusBadRequest)
  137. return
  138. }
  139. direction, err := strconv.Atoui(q.Get("direction"))
  140. if err != nil {
  141. http.Error(w, err.String(), http.StatusInternalServerError)
  142. return
  143. }
  144. err = db.Start()
  145. if err != nil {
  146. http.Error(w, err.String(), http.StatusInternalServerError)
  147. return
  148. }
  149. order, err := queryInt(db, "SELECT `order` FROM `song` WHERE `yid` = ? AND `pid` = ?",
  150. q.Get("yid"), pid)
  151. if err != nil {
  152. db.Rollback()
  153. http.Error(w, err.String(), http.StatusInternalServerError)
  154. return
  155. }
  156. newOrder := order
  157. if direction == moveUpAction && order > 0 {
  158. newOrder--
  159. } else if direction == moveDownAction {
  160. newOrder++
  161. } else {
  162. db.Rollback()
  163. http.Error(w, "invalid direction or cannot move up", http.StatusBadRequest)
  164. return
  165. }
  166. query, err := prepare(db, "UPDATE `song` SET `order` = ? WHERE `order` = ? AND `pid` = ?",
  167. order, newOrder, pid)
  168. if err != nil {
  169. db.Rollback()
  170. http.Error(w, err.String(), http.StatusInternalServerError)
  171. return
  172. } else if query.AffectedRows != 1 {
  173. db.Rollback()
  174. http.Error(w, "invalid direction for this song", http.StatusBadRequest)
  175. return
  176. }
  177. // there are now two songs with that order, so also check yid
  178. _, err = prepare(db, "UPDATE `song` SET `order` = ? WHERE `order` = ? AND `pid` = ? AND `yid` = ?",
  179. newOrder, order, pid, q.Get("yid"))
  180. if err != nil {
  181. db.Rollback()
  182. http.Error(w, err.String(), http.StatusInternalServerError)
  183. return
  184. }
  185. err = db.Commit()
  186. if err != nil {
  187. http.Error(w, err.String(), http.StatusInternalServerError)
  188. return
  189. }
  190. w.Write([]byte("1"))
  191. addUpdate(pid, direction, &Song{Yid: q.Get("yid")})
  192. }
  193. func poll(w http.ResponseWriter, r *http.Request) {
  194. q := r.URL.Query()
  195. timestamp := q.Get("timestamp")
  196. if timestamp == "0" {
  197. db := <-dbPool
  198. defer func () {dbPool <- db}()
  199. query, err := prepare(db,
  200. "SELECT `yid`,`title`,`user` FROM `playlist` JOIN `song` WHERE `id` = ? ORDER BY `order` ASC",
  201. q.Get("pid"))
  202. updates := make([]Update, 0, 2)
  203. for {
  204. song := new(Song)
  205. query.BindResult(&song.Yid, &song.Title, &song.User)
  206. eof, err := query.Fetch()
  207. if err != nil {
  208. http.Error(w, err.String(), http.StatusInternalServerError)
  209. return
  210. }
  211. if eof {
  212. break
  213. }
  214. updates = append(updates, Update{Song: song, Action: addAction, Timestamp: time.Nanoseconds()})
  215. }
  216. err = query.FreeResult()
  217. if err != nil {
  218. http.Error(w, err.String(), http.StatusInternalServerError)
  219. return
  220. }
  221. output, err := json.MarshalForHTML(updates)
  222. if err != nil {
  223. http.Error(w, err.String(), http.StatusInternalServerError)
  224. return
  225. }
  226. w.Write(output)
  227. } else {
  228. timestamp, err := strconv.Atoi64(q.Get("timestamp"))
  229. if err != nil {
  230. http.Error(w, err.String(), http.StatusInternalServerError)
  231. return
  232. }
  233. var update *Update
  234. for i := 0; i < 30; i++ {
  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. time.Sleep(1e9) // 1 second
  252. }
  253. w.Write([]byte("[]"))
  254. }
  255. }
  256. func main() {
  257. templates = make(map[string]*template.Template)
  258. for _, path := range []string{"p"} {
  259. t, err := template.ParseFile("templates/" + path + ".html")
  260. if err != nil {
  261. fmt.Println(err)
  262. return
  263. }
  264. templates[path] = t
  265. }
  266. initDb()
  267. http.HandleFunc("/", home)
  268. http.HandleFunc("/p/", playlist)
  269. http.HandleFunc("/add/", add)
  270. http.HandleFunc("/remove/", remove)
  271. http.HandleFunc("/move/", move)
  272. http.HandleFunc("/poll/", poll)
  273. err := http.ListenAndServe("localhost:8000", nil)
  274. if err != nil {
  275. fmt.Println(err)
  276. os.Exit(1)
  277. }
  278. }