Fix playlist resume and SIMPLE-ARRAY pathname errors

- Add *resumed-from-saved-state* flag to prevent scheduler's db:connected
  trigger from overwriting resumed playlist position with full playlist
- Use sb-ext:parse-native-namestring in play-file to prevent SBCL from
  interpreting brackets in directory names (e.g. [WEB FLAC]) as wildcard
  patterns, which caused non-simple-string pathname components that broke
  cl-flac's CFFI calls
This commit is contained in:
Glenn Thompson 2026-03-05 18:21:32 +03:00
parent 1807e58971
commit f39abeb8f8
3 changed files with 26 additions and 7 deletions

View File

@ -290,12 +290,19 @@
FILE-PATH can be a string or pathname.
ON-END is passed to harmony:play (default :free).
UPDATE-METADATA controls whether ICY metadata is updated immediately."
(let* ((path (pathname file-path))
(let* ((path-string (etypecase file-path
(string file-path)
(pathname (namestring file-path))))
;; Use parse-native-namestring to prevent SBCL from interpreting
;; brackets as wildcard patterns. Standard (pathname ...) turns
;; "[FLAC]" into a wild component with non-simple strings, which
;; causes SIMPLE-ARRAY errors in cl-flac's CFFI calls.
(path (sb-ext:parse-native-namestring path-string))
(server (pipeline-harmony-server pipeline))
(harmony:*server* server)
(tags (read-audio-metadata path))
(display-title (format-display-title path title))
(track-info (list :file (namestring path)
(track-info (list :file path-string
:display-title display-title
:artist (getf tags :artist)
:title (getf tags :title)

View File

@ -312,14 +312,21 @@
;;; This ensures the scheduler starts after the server is fully initialized
(define-trigger db:connected ()
"Start the playlist scheduler after database connection is established"
"Start the playlist scheduler after database connection is established.
Loads the current scheduled playlist only if the pipeline has no tracks
(i.e., we did NOT just resume from saved state)."
(handler-case
(progn
(load-schedule-from-db)
(start-playlist-scheduler)
;; Only load scheduled playlist if we didn't just resume from saved state
(if *resumed-from-saved-state*
(progn
(setf *resumed-from-saved-state* nil)
(log:info "Playlist scheduler started (resumed from saved state, skipping initial load)"))
(let ((current-playlist (get-current-scheduled-playlist)))
(when current-playlist
(load-scheduled-playlist current-playlist)))
(log:info "Playlist scheduler started"))
(load-scheduled-playlist current-playlist))
(log:info "Playlist scheduler started"))))
(error (e)
(log:error "Scheduler failed to start: ~a" e))))

View File

@ -27,6 +27,10 @@
(defvar *current-playlist-path* nil
"Path of the currently active playlist file.")
(defvar *resumed-from-saved-state* nil
"Set to T when startup successfully resumed from saved playback state.
Prevents the scheduler from overwriting the resumed position.")
(defun save-playback-state (track-file-path)
"Save the current track file path and playlist to the state file.
Called on each track change so we can resume after restart."
@ -72,6 +76,7 @@
(m3u-to-file-list playlist-path))))
(when file-list
(setf *current-playlist-path* playlist-path)
(setf *resumed-from-saved-state* t)
(let ((pos (when saved-file
(position saved-file file-list :test #'string=))))
(if pos