136 lines
6.5 KiB
Common 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))))))
|