Fix scheduler timezone and taglib type errors
- cl-cron uses local time, not UTC: add utc-hour-to-local-hour conversion so schedule hours fire at correct UTC times (e.g. 12:00 UTC now fires at 15:00 local on UTC+3) - Wrap each taglib field read in safe-tag with per-field error handling so a type error in one field (e.g. album with non-simple string) doesn't crash play-file or skip the track - Use (coerce ... 'simple-string) instead of copy-seq for guaranteed simple-string output from ensure-simple-string
This commit is contained in:
parent
9ae7546466
commit
47e6c5da46
|
|
@ -217,18 +217,25 @@
|
|||
;;; ---- Metadata ----
|
||||
|
||||
(defun ensure-simple-string (s)
|
||||
"Coerce S to a simple-string if it's a string, or return NIL."
|
||||
"Coerce S to a simple-string if it's a string, or return NIL.
|
||||
Uses coerce to guarantee SIMPLE-STRING type for downstream consumers."
|
||||
(when (stringp s)
|
||||
(copy-seq (string-trim '(#\Space #\Nul) s))))
|
||||
(coerce (string-trim '(#\Space #\Nul) s) 'simple-string)))
|
||||
|
||||
(defun safe-tag (fn audio-file)
|
||||
"Safely read a tag field, coercing to simple-string. Returns NIL on any error."
|
||||
(handler-case
|
||||
(ensure-simple-string (funcall fn audio-file))
|
||||
(error () nil)))
|
||||
|
||||
(defun read-audio-metadata (file-path)
|
||||
"Read metadata (artist, title, album) from an audio file using taglib.
|
||||
Returns a plist (:artist ... :title ... :album ...) or NIL on failure."
|
||||
(handler-case
|
||||
(let ((audio-file (audio-streams:open-audio-file (namestring file-path))))
|
||||
(list :artist (ensure-simple-string (abstract-tag:artist audio-file))
|
||||
:title (ensure-simple-string (abstract-tag:title audio-file))
|
||||
:album (ensure-simple-string (abstract-tag:album audio-file))))
|
||||
(list :artist (safe-tag #'abstract-tag:artist audio-file)
|
||||
:title (safe-tag #'abstract-tag:title audio-file)
|
||||
:album (safe-tag #'abstract-tag:album audio-file)))
|
||||
(error (e)
|
||||
(log:debug "Could not read tags from ~A: ~A" file-path e)
|
||||
nil)))
|
||||
|
|
|
|||
|
|
@ -101,16 +101,30 @@
|
|||
|
||||
;;; Cron Job Management
|
||||
|
||||
(defun utc-hour-to-local-hour (utc-hour)
|
||||
"Convert a UTC hour (0-23) to the local timezone hour for cl-cron.
|
||||
cl-cron uses decode-universal-time which returns local time."
|
||||
(let* ((now (get-universal-time))
|
||||
(local-hour (nth-value 2 (decode-universal-time now)))
|
||||
(utc-hour-now (nth-value 2 (decode-universal-time now 0)))
|
||||
(offset (- local-hour utc-hour-now)))
|
||||
(mod (+ utc-hour offset) 24)))
|
||||
|
||||
(defun setup-playlist-cron-jobs ()
|
||||
"Set up cl-cron jobs for all scheduled playlists."
|
||||
"Set up cl-cron jobs for all scheduled playlists.
|
||||
Schedule hours are in UTC; cl-cron fires in local time,
|
||||
so we convert UTC hours to local hours."
|
||||
(unless *scheduler-running*
|
||||
(dolist (entry *playlist-schedule*)
|
||||
(let ((hour (car entry))
|
||||
(playlist (cdr entry)))
|
||||
(let* ((utc-hour (car entry))
|
||||
(playlist (cdr entry))
|
||||
(local-hour (utc-hour-to-local-hour utc-hour)))
|
||||
(log:info "Scheduling ~A at ~2,'0d:00 UTC (~2,'0d:00 local)"
|
||||
playlist utc-hour local-hour)
|
||||
(cl-cron:make-cron-job
|
||||
(scheduled-playlist-loader hour playlist)
|
||||
(scheduled-playlist-loader utc-hour playlist)
|
||||
:minute 0
|
||||
:hour hour)))
|
||||
:hour local-hour)))
|
||||
(setf *scheduler-running* t)))
|
||||
|
||||
(defun start-playlist-scheduler ()
|
||||
|
|
|
|||
Loading…
Reference in New Issue