main.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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. sql := "INSERT INTO `song` (`pid`,`yid`,`title`,`user`,`order`) VALUES(%d,'%s','%s','%s','%d')"
  69. sql = fmt.Sprintf(sql, pid,
  70. db.Escape(q.Get("yid")),
  71. db.Escape(q.Get("title")),
  72. db.Escape(q.Get("user")),
  73. maxOrder + 1)
  74. err = execute(sql)
  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. pid := getpid(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("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. sql := "DELETE FROM `song` WHERE `pid` = %d AND yid = '%s'"
  109. sql = fmt.Sprintf(sql, pid, db.Escape(q.Get("yid")))
  110. err = execute(sql)
  111. if err != nil {
  112. db.Rollback()
  113. http.Error(w, err.String(), http.StatusInternalServerError)
  114. return
  115. }
  116. sql = "UPDATE `song` SET `order` = `order`-1 WHERE `order` > %d AND `pid` = %d"
  117. sql = fmt.Sprintf(sql, order, pid)
  118. err = execute(sql)
  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. pid := getpid(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("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. sql := "UPDATE `song` SET `order` = %d WHERE `order` = %d AND pid = %d"
  167. sql = fmt.Sprintf(sql, order, newOrder, pid)
  168. err = execute(sql)
  169. if err != nil {
  170. db.Rollback()
  171. http.Error(w, err.String(), http.StatusInternalServerError)
  172. return
  173. } else if db.AffectedRows != 1 {
  174. db.Rollback()
  175. http.Error(w, "invalid direction for this song", http.StatusBadRequest)
  176. return
  177. }
  178. // there are now two songs with that order, so also check yid
  179. sql = "UPDATE `song` SET `order` = %d WHERE `order` = %d AND pid = %d AND yid = '%s'"
  180. sql = fmt.Sprintf(sql, newOrder, order, pid, q.Get("yid"))
  181. err = db.Query(sql)
  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 == "0" {
  199. query, err := prepare(
  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(getpid(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. }