main.go 5.8 KB

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