aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-18 17:32:13 -0500
committerCraig Jennings <c@cjennings.net>2026-07-18 17:32:13 -0500
commit347e36c6fd4b52e14731594e6f745f6efdccadb1 (patch)
tree47afd50f2be12aa5292b90989480a0a1f46f8001
parentc3720afd638270dc3fcc2b6ee4f7d26bdde634e8 (diff)
downloaddotemacs-347e36c6fd4b52e14731594e6f745f6efdccadb1.tar.gz
dotemacs-347e36c6fd4b52e14731594e6f745f6efdccadb1.zip
refactor(media): extract the playback sentinel as a named builder
- cj/media--play-sentinel returns the per-process closure. - Three new tests pin the reap-on-exit behavior.
-rw-r--r--modules/media-utils.el24
-rw-r--r--tests/test-media-utils.el34
2 files changed, 48 insertions, 10 deletions
diff --git a/modules/media-utils.el b/modules/media-utils.el
index e3f4022f..99b5dc37 100644
--- a/modules/media-utils.el
+++ b/modules/media-utils.el
@@ -162,6 +162,19 @@ nothing."
(error "yt-dlp resolved no stream URL for %s" url))))
(delete-file err-file))))
+(defun cj/media--play-sentinel (url-display)
+ "A process sentinel reporting playback of URL-DISPLAY.
+Messages success or failure and reaps the process buffer once the
+player finishes or exits."
+ (lambda (proc event)
+ (cond
+ ((string-match-p "finished" event)
+ (message "✓ Finished playing: %s" url-display))
+ ((string-match-p "exited abnormally" event)
+ (message "✗ Playback failed: %s" url-display)))
+ (when (string-match-p "finished\\|exited" event)
+ (kill-buffer (process-buffer proc)))))
+
(defun cj/media-play-it (url)
"Play the URL with the configured media player in an async process.
A player flagged :needs-stream-url gets the URL resolved first via a
@@ -193,16 +206,7 @@ with a plain argv list -- no shell anywhere in the pipeline."
(cj/log-silently "DEBUG: Executing: %s" (string-join argv " "))
(let ((process (apply #'start-process player-name buffer-name argv)))
- (set-process-sentinel
- process
- (lambda (proc event)
- (cond
- ((string-match-p "finished" event)
- (message "✓ Finished playing: %s" url-display))
- ((string-match-p "exited abnormally" event)
- (message "✗ Playback failed: %s" url-display)))
- (when (string-match-p "finished\\|exited" event)
- (kill-buffer (process-buffer proc)))))))))
+ (set-process-sentinel process (cj/media--play-sentinel url-display))))))
;; ------------------------- Media-Download Via yt-dlp -------------------------
diff --git a/tests/test-media-utils.el b/tests/test-media-utils.el
index c682dffe..23b36eeb 100644
--- a/tests/test-media-utils.el
+++ b/tests/test-media-utils.el
@@ -114,6 +114,40 @@ launches the player with the resolved URL as argv -- no shell either step."
(should-error (cj/media-play-it "https://example.com/v")))
(should-not launched)))
+;; -------------------------- cj/media--play-sentinel --------------------------
+
+(ert-deftest test-media-utils--play-sentinel-normal-finished-kills-buffer ()
+ "Normal: a finished event reports success and reaps the process buffer."
+ (let ((buf (generate-new-buffer " *sentinel-test*"))
+ (said nil))
+ (cl-letf (((symbol-function 'process-buffer) (lambda (_p) buf))
+ ((symbol-function 'message)
+ (lambda (fmt &rest args) (setq said (apply #'format fmt args)))))
+ (funcall (cj/media--play-sentinel "https://a/v") 'proc "finished\n"))
+ (should (string-match-p "Finished" said))
+ (should-not (buffer-live-p buf))))
+
+(ert-deftest test-media-utils--play-sentinel-normal-abnormal-exit-kills-buffer ()
+ "Normal: an abnormal exit reports failure and reaps the process buffer."
+ (let ((buf (generate-new-buffer " *sentinel-test*"))
+ (said nil))
+ (cl-letf (((symbol-function 'process-buffer) (lambda (_p) buf))
+ ((symbol-function 'message)
+ (lambda (fmt &rest args) (setq said (apply #'format fmt args)))))
+ (funcall (cj/media--play-sentinel "https://a/v") 'proc "exited abnormally with code 2\n"))
+ (should (string-match-p "failed" said))
+ (should-not (buffer-live-p buf))))
+
+(ert-deftest test-media-utils--play-sentinel-boundary-other-event-keeps-buffer ()
+ "Boundary: a non-terminal event (e.g. stop) leaves the buffer alone."
+ (let ((buf (generate-new-buffer " *sentinel-test*")))
+ (unwind-protect
+ (cl-letf (((symbol-function 'process-buffer) (lambda (_p) buf))
+ ((symbol-function 'message) #'ignore))
+ (funcall (cj/media--play-sentinel "https://a/v") 'proc "stopped\n")
+ (should (buffer-live-p buf)))
+ (when (buffer-live-p buf) (kill-buffer buf)))))
+
;; ------------------------------- cj/yt-dl-it ---------------------------------
(ert-deftest test-media-yt-dl-it-errors-without-yt-dlp ()