server.lisp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. (in-package :cl-user)
  2. (defpackage chad-music.server
  3. (:use :cl #:alexandria #:chad-music.db #:jonathan))
  4. (in-package :chad-music.server)
  5. (defvar *db* nil "Metadata database")
  6. (defvar *path-url-mappings*
  7. '(("/media/pogo/Music/" . "/music/")) "Map database paths to urls")
  8. (defun get-url (path)
  9. (when (and path (pathnamep path))
  10. (let ((path (namestring path)))
  11. (loop for (path-prefix . url-prefix) in *path-url-mappings*
  12. do (multiple-value-bind (foundp suffix) (starts-with-subseq path-prefix path :return-suffix t)
  13. (when foundp
  14. (return-from get-url (concatenate 'string url-prefix suffix))))))))
  15. (declaim (inline js-null))
  16. (defun js-null (obj)
  17. (if obj obj
  18. :null))
  19. (defmethod %to-json ((album album))
  20. (with-object
  21. (write-key-value "id" (js-null (album-id album)))
  22. (write-key-value "artist" (js-null (album-artist album)))
  23. (write-key-value "year" (js-null (album-year album)))
  24. (write-key-value "album" (js-null (album-album album)))
  25. (write-key-value "original_date" (js-null (album-original-date album)))
  26. (write-key-value "genre" (js-null (album-genre album)))
  27. (write-key-value "type" (js-null (album-type album)))
  28. (write-key-value "status" (js-null (album-status album)))
  29. (write-key-value "mb_id" (js-null (album-mb-id album)))
  30. (write-key-value "track_count" (js-null (album-track-count album)))
  31. (write-key-value "total_duration" (js-null (album-total-duration album)))
  32. (write-key-value "cover" (js-null (get-url (album-cover album))))))
  33. (defmethod %to-json ((track track))
  34. (with-object
  35. (write-key-value "artist" (js-null (track-artist track)))
  36. (write-key-value "album" (js-null (album-album (track-album track))))
  37. (write-key-value "no" (js-null (clear-track-no (track-no track))))
  38. (write-key-value "title" (js-null (track-title track)))
  39. (write-key-value "bit_rate" (js-null (track-bit-rate track)))
  40. (write-key-value "vbr" (if (track-is-vbr track) :true :false))
  41. (write-key-value "duration" (js-null (track-duration track)))
  42. (write-key-value "url" (js-null (get-url (track-path track))))))
  43. (defparameter +400+ '(400 nil #.(trivial-utf-8:string-to-utf-8-bytes "400")))
  44. (defparameter +404+ '(404 nil #.(trivial-utf-8:string-to-utf-8-bytes "404")))
  45. (defparameter +200-empty+ '(200 (:content-type "application/json")
  46. #.(trivial-utf-8:string-to-utf-8-bytes "{}")))
  47. (defun 200-json (data)
  48. (declare #.*standard-optimize-settings*)
  49. `(200 (:content-type "application/json")
  50. ,(trivial-utf-8:string-to-utf-8-bytes (to-json data))))
  51. (defun get-category-list (params)
  52. (declare #.*standard-optimize-settings*
  53. (ignore params))
  54. (200-json '("artist" "year" "album" "genre" "type" "status")))
  55. (let ((db-package (find-package :chad-music.db)))
  56. (defun getsym (place indicator)
  57. (declare #.*standard-optimize-settings*
  58. (type list place)
  59. (type symbol indicator))
  60. (intern (string-upcase (getf place indicator)) db-package)))
  61. (defun aget (place indicator &key (test #'equal))
  62. (declare #.*standard-optimize-settings*
  63. (type list place)
  64. (type (or string symbol) indicator)
  65. (type function test))
  66. (cdr (assoc indicator place :test test)))
  67. (defun may-integer (string)
  68. (declare #.*standard-optimize-settings*
  69. (type (or null string) string))
  70. (when string
  71. (parse-integer string)))
  72. (defun get-restrictions (query-params)
  73. (declare #.*standard-optimize-settings*
  74. (type list query-params))
  75. (loop for key in '(artist year album genre type status)
  76. for value = (aget query-params (string-downcase (symbol-name key)))
  77. when value collect (cons key (case key
  78. (year (parse-integer value :junk-allowed t))
  79. (otherwise value)))))
  80. (defparameter +max-limit+ 1000)
  81. (defmacro with-category ((params category filter restrictions offset limit) &body body)
  82. (with-gensyms (query-string query-params)
  83. `(let ((,category (getsym ,params :category)))
  84. (case ,category
  85. ((artist year album genre type status)
  86. (let* ((,query-string (getf myway:*env* :query-string))
  87. (,query-params (and ,query-string (quri:url-decode-params ,query-string)))
  88. (,filter (aget ,query-params "filter"))
  89. (,restrictions (get-restrictions ,query-params))
  90. (,offset (or (may-integer (aget ,query-params "offset")) 0))
  91. (,limit (min (the fixnum +max-limit+) (the fixnum (or (may-integer (aget ,query-params "limit")) +max-limit+)))))
  92. ,@body))
  93. (otherwise +404+)))))
  94. (defun get-category-size (params)
  95. (declare #.*standard-optimize-settings*)
  96. (with-category (params category filter restrictions offset limit)
  97. (declare (ignore offset limit))
  98. (200-json (query-category (cdr *db*) category :filter filter :restrictions restrictions :count-only t))))
  99. (defun get-category (params)
  100. (declare #.*standard-optimize-settings*)
  101. (with-category (params category filter restrictions offset limit)
  102. (200-json (query-category (cdr *db*) category
  103. :filter filter :restrictions restrictions
  104. :limit limit :offset offset))))
  105. (defun album-tracks (params)
  106. (declare #.*standard-optimize-settings*)
  107. (200-json (query-tracks (car *db*)
  108. :restrictions `((id . ,(getf params :id))))))
  109. (defvar *mapper* (myway:make-mapper))
  110. (myway:connect *mapper* "/cat/:category/size" 'get-category-size)
  111. (myway:connect *mapper* "/cat/:category" 'get-category)
  112. (myway:connect *mapper* "/cat" 'get-category-list)
  113. (myway:connect *mapper* "/album/:id/tracks" 'album-tracks)
  114. ;;(myway:connect *mapper* "*" (lambda (p) (declare (ignore p)) +404+))
  115. (defun main (&rest args &key (port 5000) (debug nil) (use-thread t) &allow-other-keys)
  116. (apply #'clack:clackup
  117. (myway:to-app *mapper*)
  118. :server :woo
  119. :port port
  120. :debug debug
  121. :use-default-middlewares nil
  122. :use-thread use-thread
  123. (alexandria:remove-from-plist args :data :port :debug :use-thread)))