From 745ada638b3f82b4e3076dd75d0fe8c0a47bfb31 Mon Sep 17 00:00:00 2001
From: Grant Shangreaux <grant@unabridgedsoftware.com>
Date: Fri, 30 Dec 2022 20:55:05 -0600
Subject: Add: zip/download UI and deletion of archive on playlist edit

- Clicking the button will zip the playlist (if there is no zip
  archive already existing)
- editing a playlist will delete the zip file
---
 playlist.lisp | 30 ++++++++++++++++--------------
 vampire.asd   |  3 ++-
 zipper.lisp   | 20 +++++++++++++++-----
 3 files changed, 33 insertions(+), 20 deletions(-)

diff --git a/playlist.lisp b/playlist.lisp
index f7f0b04..7a2884b 100644
--- a/playlist.lisp
+++ b/playlist.lisp
@@ -3,7 +3,7 @@
 (in-package :vampire)
 
 
-;;; CLIENT STATE 
+;;; CLIENT STATE
 
 (defclass/std playlist-ctl ()
   ((playlist :std nil :doc "The playlist instance.")
@@ -14,7 +14,6 @@
              :std nil :doc "Now Playing Elements")
    (pl-title pl-tracks pl-dur
              :std nil :doc "Playlist Elements")
-   (zipped :std nil :doc "Path to the zipped playlist, if it exists."))
   (:documentation "Holds the complete state for this session's viewing of a particular playlist."))
 
 (defclass/std track-ctl ()
@@ -49,7 +48,7 @@
     (when (plusp pos)
       (nth (1- pos) (tracks ctl)))))
 
-;;; SESSION UTIL 
+;;; SESSION UTIL
 
 (defparameter +playlist-connection-key+ "playlist-connection-key"
   "Stored in the clog connection object")
@@ -65,6 +64,7 @@
   (setf (cur-playlist-ctl body)
         (make-instance 'playlist-ctl
                        :playlist playlist
+                       :zipped-tracks (when (zipped-playlist-exists-p playlist) (zipped-playlist-url playlist))
                        :editorp (can-edit-p (session-user body) playlist))))
 
 ;;; SYNCHRONIZATION
@@ -189,10 +189,11 @@
               (curctl (cur-playlist-ctl container))
               (pos (position track-ctl (tracks curctl))))
 
-    (when (delete-track-at (playlist curctl) pos) 
+    (when (delete-track-at (playlist curctl) pos)
+      (delete-zipped-playlist (playlist curctl))
       (for-playlist-viewers container ctl
         (let ((track-ctl (nth pos (tracks ctl))))
-          (destroy (container track-ctl)) 
+          (destroy (container track-ctl))
           (setf (tracks ctl) (delete track-ctl (tracks ctl))
                 (text (pl-dur ctl)) (secs-to-hms (playlist-duration (playlist ctl)))))))))
 
@@ -200,6 +201,7 @@
   (when-let* ((curctl (cur-playlist-ctl (container track-ctl)))
               (pos (position track-ctl (tracks curctl))))
     (when (swap-tracks (playlist curctl) pos (1+ pos))
+      (delete-zipped-playlist (playlist curctl))
       (for-playlist-viewers (container track-ctl) ctl
         (let* ((cur
                  (nth pos (tracks ctl)))
@@ -215,6 +217,7 @@
   (when-let* ((curctl (cur-playlist-ctl (container track-ctl)))
               (pos (position track-ctl (tracks curctl))))
     (when (swap-tracks (playlist curctl) pos (1- pos))
+      (delete-zipped-playlist (playlist curctl))
       (for-playlist-viewers (container track-ctl) ctl
         (let* ((cur
                  (nth pos (tracks ctl)))
@@ -348,13 +351,14 @@
                (on-ok
                  (lambda (track)
                    (destroy notice)
+                   (delete-zipped-playlist pl)
                    (append-track pl track)
                    (append-track-list-item parent track))))
           (setf (value url-input) "")
           (if-let (track (track-with-source url))
             (funcall on-ok track)
             (add-fetch-track-job
-             url on-ok              
+             url on-ok
              (lambda (err)
                (destroy notice)
                (format t "~a" err)
@@ -393,7 +397,7 @@
          (thunk*
            (let ((user (user-with-name (value userinput))))
              (cond
-               (user 
+               (user
                 (add-editor playlist user)
                 (setf (value userinput) ""
                       (text username-status) "")
@@ -439,20 +443,17 @@
 
 	       (div (:class "row")
                     (div ()
-                         (button (:content "Create Playlist Zip" :bind download))
-                         (a (:link (zipped ctl) :content "Download" :bind dl-link)))
+                         (button (:content "Download Zipped Playlist" :bind zip-download-button)))
                     (div ()
                          (new-track-form (pl))
                          (editor-managment (pl)))))
-        (set-on-click download (thunk*
-                                 (setf (zipped ctl) (zip-playlist pl)
-                                       (link dl-link) (zipped ctl)
-                                       (display dl-link) "inline")))
+        (set-on-click zip-download-button
+                      (thunk* (zip-playlist pl)
+                        (setf (url (location body)) (zipped-playlist-url pl))))
 
         (setf (pl-title ctl) title-elem
               (pl-dur ctl) dur-elem
               (display input) "none"
-              (display dl-link) "none"
               (display title-elem) "inline")
 
         (when (playlist-editors pl)
@@ -471,6 +472,7 @@
            input
            (thunk*
              (when (plusp (length (value input)))
+               (delete-zipped-playlist pl) ;; must happen first
                (update-playlist-title pl (value input))
                (setf (text title-elem) (value input)))
              (setf (display input) "none"
diff --git a/vampire.asd b/vampire.asd
index ac49ea3..9014aa8 100644
--- a/vampire.asd
+++ b/vampire.asd
@@ -33,4 +33,5 @@
                (:file "home")
                (:file "playlist")
                (:file "vampire")
-               (:file "run")))
+               (:file "run")
+               (:file "zipper")))
diff --git a/zipper.lisp b/zipper.lisp
index 36e62f9..ede4a71 100644
--- a/zipper.lisp
+++ b/zipper.lisp
@@ -4,11 +4,13 @@
 
 (defun zip-playlist (playlist)
   "Compresses playlist tracks into a zip archive."
-  (with-temp-dir (tmpdir)
-    (let ((zip-dir (zipped-playlist-path)))
-      (ensure-directories-exist zip-dir)
-      (org.shirakumo.zippy:compress-zip
-       (copy-audio-files-for-download playlist tmpdir) zip-dir :if-exists :overwrite))))
+  (ensure-directories-exist (merge-pathnames "media/bundled-playlists/" (static-directory *config*)))
+  (unless (zipped-playlist-exists-p playlist)
+    (let ((zip-file (zipped-playlist-path playlist)))
+      (with-temp-dir (tmpdir)
+        (with-open-file (_foo zip-file :if-does-not-exist :create :direction :output :if-exists :overwrite)
+          (org.shirakumo.zippy:compress-zip
+           (copy-audio-files-for-download playlist tmpdir) zip-file :if-exists :overwrite))))))
 
 (defun zip-track-filename (track pos)
   "Return a filename for a track. `NN-ARTIST-ALBUM-TITLE.CODEC'"
@@ -29,6 +31,9 @@
   "Returns the url where the playlist's zip file is expected to exist."
   (merge-pathnames (zipped-playlist-filename playlist) "/media/bundled-playlists/"))
 
+(defun zipped-playlist-exists-p (playlist)
+  (uiop:file-exists-p (zipped-playlist-path playlist)))
+
 (defun copy-audio-files-for-download (playlist dir)
   "Copies all playlist tracks into a temporary directory and returns a list of the pathnames."
   (loop for track in (playlist-tracks playlist)
@@ -37,3 +42,8 @@
         for dest = (merge-pathnames (zip-track-filename track pos) dir)
         do (uiop:copy-file file dest)
         collect dest))
+
+(defun delete-zipped-playlist (playlist)
+  "Deletes the zipped playlist file. Returns T if it was deleted, NIL otherwise."
+  (princ "Deleting zipped playlist")
+  (uiop:delete-file-if-exists (zipped-playlist-path playlist)))
-- 
cgit v1.2.3