2024-10-27 23:44:59 -07:00
|
|
|
(in-package #:sync-music)
|
|
|
|
|
|
|
|
(defstruct action
|
|
|
|
(target nil :type pathname))
|
|
|
|
(defstruct (copy-action (:include action))
|
|
|
|
(origin nil :type pathname))
|
2024-11-07 15:19:59 -08:00
|
|
|
(defstruct (copy-flac-action (:include copy-action))
|
|
|
|
(start nil :type (or string null))
|
|
|
|
(end nil :type (or string null))
|
|
|
|
(metadata nil :type (soft-alist-of string string)))
|
2024-10-27 23:44:59 -07:00
|
|
|
(defstruct (copy-meta-action (:include copy-action)))
|
|
|
|
(defstruct (delete-action (:include action)))
|
|
|
|
(defstruct (directory-action (:include action)))
|
|
|
|
|
|
|
|
(defgeneric action-describe (action &optional s)
|
|
|
|
(:documentation "Format to stream S a single line describing ACTION in brief."))
|
|
|
|
(defgeneric action-perform (action &optional queue)
|
|
|
|
(:documentation "Perform ACTION.
|
|
|
|
|
|
|
|
Pushes status message about this action to QUEUE if given."))
|
|
|
|
(defgeneric action-perform-describe (action)
|
|
|
|
(:documentation "Status message, as printed during ACTION-PERFORM."))
|
|
|
|
|
|
|
|
(defmethod action-describe ((a copy-action) &optional s)
|
2024-10-31 21:41:34 -07:00
|
|
|
(format s "[C] `~A` -> `~A`~%" (copy-action-origin a) (action-target a)))
|
2024-10-27 23:44:59 -07:00
|
|
|
(defmethod action-describe ((a delete-action) &optional s)
|
2024-10-31 21:41:34 -07:00
|
|
|
(format s "[D] `~A`~%" (action-target a)))
|
2024-10-27 23:44:59 -07:00
|
|
|
(defmethod action-describe ((a directory-action) &optional s)
|
2024-10-31 21:41:34 -07:00
|
|
|
(format s "[M] `~A`~%" (action-target a)))
|
2024-10-27 23:44:59 -07:00
|
|
|
|
|
|
|
(defmethod action-perform ((a copy-action) &optional queue)
|
|
|
|
(declare (ignore queue))
|
|
|
|
(uiop:copy-file (copy-action-origin a) (action-target a)))
|
|
|
|
|
|
|
|
(defmethod action-perform ((a copy-flac-action) &optional queue)
|
|
|
|
(declare (ignore queue))
|
2024-11-07 15:19:59 -08:00
|
|
|
(uiop:run-program
|
|
|
|
`("ffmpeg" "-n"
|
|
|
|
"-filter_threads" "1"
|
|
|
|
"-i" ,(uiop:native-namestring (copy-action-origin a))
|
|
|
|
,@(when-let ((start (copy-flac-action-start a)))
|
|
|
|
`("-ss" ,(convert-timestamp start)))
|
|
|
|
,@(when-let ((end (copy-flac-action-end a)))
|
|
|
|
`("-to" ,(convert-timestamp end)))
|
|
|
|
,@(loop :for (tag . value) :in (copy-flac-action-metadata a)
|
|
|
|
:collect "-metadata"
|
|
|
|
:collect (format nil "~A=~A" tag value))
|
|
|
|
"-c:a" "libopus"
|
|
|
|
"-map" "0:a"
|
|
|
|
"-ac" "2"
|
|
|
|
"-b:a" ,*opus-bitrate*
|
|
|
|
,(uiop:native-namestring (action-target a)))))
|
2024-10-27 23:44:59 -07:00
|
|
|
|
|
|
|
(defmethod action-perform ((a copy-meta-action) &optional queue)
|
|
|
|
(declare (ignore queue))
|
|
|
|
(let (buf)
|
|
|
|
(with-open-file (input
|
|
|
|
(copy-action-origin a)
|
|
|
|
:element-type 'octet)
|
|
|
|
(setf buf (make-array (file-length input) :element-type 'octet))
|
|
|
|
(read-sequence buf input))
|
|
|
|
(setf buf (replace-text-buffer buf ".flac" ".opus"))
|
2024-11-06 00:02:40 -08:00
|
|
|
(setf buf (replace-text-buffer buf ".wav" ".opus"))
|
2024-10-27 23:44:59 -07:00
|
|
|
(with-open-file (output
|
|
|
|
(action-target a)
|
|
|
|
:element-type 'octet
|
|
|
|
:direction :output)
|
|
|
|
(write-sequence buf output))))
|
|
|
|
|
|
|
|
(defmethod action-perform ((a delete-action) &optional queue)
|
|
|
|
(declare (ignore queue))
|
|
|
|
(delete-file (action-target a)))
|
|
|
|
|
|
|
|
(defmethod action-perform ((a directory-action) &optional queue)
|
|
|
|
(declare (ignore queue))
|
|
|
|
(ensure-directories-exist (action-target a)))
|
|
|
|
|
|
|
|
(defmethod action-perform :before ((a action) &optional queue)
|
|
|
|
(when queue
|
|
|
|
(lparallel.queue:push-queue (action-perform-describe a) queue)))
|
|
|
|
|
|
|
|
(defmethod action-perform-describe ((a copy-action))
|
2024-10-31 21:41:34 -07:00
|
|
|
(format nil "Copying `~A` to `~A`..." (copy-action-origin a) (action-target a)))
|
2024-10-27 23:44:59 -07:00
|
|
|
(defmethod action-perform-describe ((a copy-meta-action))
|
2024-10-31 21:41:34 -07:00
|
|
|
(format nil "Modifying `~A` to `~A`..." (copy-action-origin a) (action-target a)))
|
2024-10-27 23:44:59 -07:00
|
|
|
(defmethod action-perform-describe ((a copy-flac-action))
|
2024-10-31 21:41:34 -07:00
|
|
|
(format nil "Converting `~A` to `~A`..." (copy-action-origin a) (action-target a)))
|
2024-10-27 23:44:59 -07:00
|
|
|
(defmethod action-perform-describe ((a delete-action))
|
2024-10-31 21:41:34 -07:00
|
|
|
(format nil "Deleting `~A`..." (action-target a)))
|
2024-10-27 23:44:59 -07:00
|
|
|
(defmethod action-perform-describe ((a directory-action))
|
2024-10-31 21:41:34 -07:00
|
|
|
(format nil "Ensuring `~A` exists..." (action-target a)))
|
2024-10-27 23:44:59 -07:00
|
|
|
|
2024-11-07 15:18:18 -08:00
|
|
|
;; NOTE: Delete actions are equal to copy actions to the same target
|
2024-10-27 23:44:59 -07:00
|
|
|
(defmethod fset:compare ((a1 action) (a2 action))
|
|
|
|
(fset:compare-slots a1 a2 #'action-target))
|