asteroid/playlist-management.lisp

136 lines
6.5 KiB
Common Lisp

;;;; playlist-management.lisp - Playlist Management for Asteroid Radio
;;;; Database operations and functions for user playlists
(in-package :asteroid)
;; Playlist management functions
(defun create-playlist (user-id name &optional description)
"Create a new playlist for a user"
(unless (db:collection-exists-p "playlists")
(error "Playlists collection does not exist in database"))
(let ((playlist-data `(("user-id" ,user-id)
("name" ,name)
("description" ,(or description ""))
("track-ids" "") ; Empty string for text field
("created-date" ,(local-time:timestamp-to-unix (local-time:now))))))
(format t "Creating playlist with user-id: ~a (type: ~a)~%" user-id (type-of user-id))
(format t "Playlist data: ~a~%" playlist-data)
(db:insert "playlists" playlist-data)
t))
(defun get-user-playlists (user-id)
"Get all playlists for a user"
(format t "Querying playlists with user-id: ~a (type: ~a)~%" user-id (type-of user-id))
(let ((all-playlists (db:select "playlists" (db:query :all))))
(format t "Total playlists in database: ~a~%" (length all-playlists))
(when (> (length all-playlists) 0)
(let ((first-playlist (first all-playlists)))
(format t "First playlist user-id: ~a (type: ~a)~%"
(gethash "user-id" first-playlist)
(type-of (gethash "user-id" first-playlist)))))
;; Filter manually since DB stores user-id as a list (2) instead of 2
(remove-if-not (lambda (playlist)
(let ((stored-user-id (gethash "user-id" playlist)))
(or (equal stored-user-id user-id)
(and (listp stored-user-id)
(equal (first stored-user-id) user-id)))))
all-playlists)))
(defun get-playlist-by-id (playlist-id)
"Get a specific playlist by ID"
(format t "get-playlist-by-id called with: ~a (type: ~a)~%" playlist-id (type-of playlist-id))
;; Try direct query first
(let ((playlists (db:select "playlists" (db:query (:= "_id" playlist-id)))))
(if (> (length playlists) 0)
(progn
(format t "Found via direct query~%")
(first playlists))
;; If not found, search manually (ID might be stored as list)
(let ((all-playlists (db:select "playlists" (db:query :all))))
(format t "Searching through ~a playlists manually~%" (length all-playlists))
(find-if (lambda (playlist)
(let ((stored-id (gethash "_id" playlist)))
(format t "Checking playlist _id: ~a (type: ~a)~%" stored-id (type-of stored-id))
(or (equal stored-id playlist-id)
(and (listp stored-id) (equal (first stored-id) playlist-id)))))
all-playlists)))))
(defun add-track-to-playlist (playlist-id track-id)
"Add a track to a playlist"
(let ((playlist (get-playlist-by-id playlist-id)))
(when playlist
(let* ((current-track-ids-raw (gethash "track-ids" playlist))
;; Handle database storing as list - extract string
(current-track-ids (if (listp current-track-ids-raw)
(first current-track-ids-raw)
current-track-ids-raw))
;; Parse comma-separated string into list
(tracks-list (if (and current-track-ids
(stringp current-track-ids)
(not (string= current-track-ids "")))
(mapcar #'parse-integer
(cl-ppcre:split "," current-track-ids))
nil))
(new-tracks (append tracks-list (list track-id)))
;; Convert back to comma-separated string
(track-ids-str (format nil "~{~a~^,~}" new-tracks)))
(format t "Adding track ~a to playlist ~a~%" track-id playlist-id)
(format t "Current track-ids raw: ~a (type: ~a)~%" current-track-ids-raw (type-of current-track-ids-raw))
(format t "Current track-ids: ~a~%" current-track-ids)
(format t "Tracks list: ~a~%" tracks-list)
(format t "New tracks: ~a~%" new-tracks)
(format t "Track IDs string: ~a~%" track-ids-str)
;; Update using track-ids field (defined in schema)
(db:update "playlists"
(db:query (:= "_id" playlist-id))
`(("track-ids" ,track-ids-str)))
(format t "Update complete~%")
t))))
(defun remove-track-from-playlist (playlist-id track-id)
"Remove a track from a playlist"
(let ((playlist (get-playlist-by-id playlist-id)))
(when playlist
(let* ((current-track-ids-raw (gethash "track-ids" playlist))
;; Handle database storing as list - extract string
(current-track-ids (if (listp current-track-ids-raw)
(first current-track-ids-raw)
current-track-ids-raw))
;; Parse comma-separated string into list
(tracks-list (if (and current-track-ids
(stringp current-track-ids)
(not (string= current-track-ids "")))
(mapcar #'parse-integer
(cl-ppcre:split "," current-track-ids))
nil))
(new-tracks (remove track-id tracks-list :test #'equal))
;; Convert back to comma-separated string
(track-ids-str (format nil "~{~a~^,~}" new-tracks)))
(db:update "playlists"
(db:query (:= "_id" playlist-id))
`(("track-ids" ,track-ids-str)))
t))))
(defun delete-playlist (playlist-id)
"Delete a playlist"
(db:remove "playlists" (db:query (:= "_id" playlist-id)))
t)
(defun ensure-playlists-collection ()
"Ensure playlists collection exists in database"
(unless (db:collection-exists-p "playlists")
(format t "Creating playlists collection...~%")
(db:create "playlists"))
;; Debug: Print the actual structure
(format t "~%=== PLAYLISTS COLLECTION STRUCTURE ===~%")
(format t "Structure: ~a~%~%" (db:structure "playlists"))
;; Debug: Check existing playlists
(let ((playlists (db:select "playlists" (db:query :all))))
(when playlists
(format t "Sample playlist fields: ~{~a~^, ~}~%~%"
(alexandria:hash-table-keys (first playlists))))))