main.go 6.8 KB

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