[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/listen f23fc0096a 07/12: Merge: Read track durations wi
From: |
ELPA Syncer |
Subject: |
[elpa] externals/listen f23fc0096a 07/12: Merge: Read track durations with ffprobe |
Date: |
Thu, 29 Feb 2024 12:58:32 -0500 (EST) |
branch: externals/listen
commit f23fc0096a7ada6eecdc46c96da224a98f210a03
Merge: 33cc5e8766 1a8c98f99c
Author: Adam Porter <adam@alphapapa.net>
Commit: Adam Porter <adam@alphapapa.net>
Merge: Read track durations with ffprobe
---
README.org | 3 ++-
listen-lib.el | 6 ++++-
listen-library.el | 19 +++++++++------
listen-queue.el | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
listen.el | 12 ++++-----
5 files changed, 92 insertions(+), 21 deletions(-)
diff --git a/README.org b/README.org
index 2d408c52e5..e1217f125d 100644
--- a/README.org
+++ b/README.org
@@ -29,7 +29,7 @@ Note a silly limitation: a track may be present in a queue
only once (but who wo
* Installation
-Note that Listen.el uses [[https://www.videolan.org/vlc/][VLC]] to play audio,
so it must be installed.
+Note that Listen.el uses [[https://www.videolan.org/vlc/][VLC]] to play audio,
so it must be installed. Also, ~ffprobe~ (part of
[[https://ffmpeg.org/ffprobe.html][FFmpeg]]) is used to read track durations
when available, but it is not required.
** GNU ELPA
@@ -69,6 +69,7 @@ Use the command ~listen~ to show the Transient menu. From
there, it is--hopeful
*Additions*
+ Command ~listen-queue-deduplicate~ removes duplicate tracks from a queue (by
comparing artist, album, and title metadata case-insensitively).
++ Read track durations with ~ffprobe~ and show in library and queue views.
** v0.3
diff --git a/listen-lib.el b/listen-lib.el
index 2afd109a1a..5da08872e4 100644
--- a/listen-lib.el
+++ b/listen-lib.el
@@ -35,7 +35,7 @@
name tracks current etc)
(cl-defstruct listen-track
- filename artist title album number genre length date rating etc)
+ filename artist title album number genre duration date rating etc)
(cl-defmethod cl-print-object ((track listen-track) stream)
(prin1 (listen-track-filename track) stream))
@@ -77,6 +77,10 @@
(or listen-player
(setf listen-player (make-listen-player-vlc))))
+(defun listen-format-seconds (seconds)
+ "Return SECONDS formatted as an hour:minute:second-style duration."
+ (format-seconds "%h:%z%.2m:%.2s" seconds))
+
;;;; Methods
(cl-defmethod listen--running-p ((player listen-player))
diff --git a/listen-library.el b/listen-library.el
index 577a93fc24..6984b5df65 100644
--- a/listen-library.el
+++ b/listen-library.el
@@ -60,26 +60,29 @@
(`nil nil)
(date (format " (%s)" date)))))
"[unknown album]"))
- (title (track)
- (or (with-face 'listen-title (listen-track-title track))
- "[unknown title]"))
(number (track)
(or (listen-track-number track) ""))
- (track-string (track)
+ (title (track)
(concat (pcase (number track)
("" "")
(else (format "%s: " else)))
- (title track)))
+ (or (with-face 'listen-title (listen-track-title
track))
+ "[unknown title]")))
+ (format-track (track)
+ (let* ((duration (listen-track-duration track)))
+ (when duration
+ (setf duration (concat "(" (listen-format-seconds
duration) ")" " ")))
+ (concat duration (listen-track-filename track))))
(make-fn (&rest args)
(apply #'make-taxy-magit-section
:make #'make-fn
- :format-fn #'cl-prin1-to-string
+ :format-fn #'format-track
args)))
(make-fn
:name "Genres"
:take (apply-partially #'taxy-take-keyed
(list #'genre #'artist ;; #'date
- #'album #'track-string)))))
+ #'album #'title)))))
;;;; Mode
@@ -108,7 +111,7 @@ show the view."
if (file-directory-p path)
append (directory-files-recursively path "." t)
else collect path))
- (tracks (remq nil (mapcar #'listen-queue-track filenames)))
+ (tracks (listen-queue-tracks-for filenames))
(buffer-name (if name
(format "*Listen library: %s" name)
(generate-new-buffer-name (format "*Listen
library*"))))
diff --git a/listen-queue.el b/listen-queue.el
index 6313ae94f0..e4ef459fc3 100644
--- a/listen-queue.el
+++ b/listen-queue.el
@@ -51,10 +51,17 @@
(defvar listen-mode)
+(defvar listen-queue-ffprobe-p (executable-find "ffprobe")
+ "Whether \"ffprobe\" is available.")
+
(defgroup listen-queue nil
"Queues."
:group 'listen)
+(defcustom listen-queue-max-probe-processes 16
+ "Maximum number of processes to run while probing track durations."
+ :type 'natnum)
+
;;;; Commands
;; (defmacro listen-queue-command (command)
@@ -95,6 +102,10 @@
(list :name "#" :primary 'descend
:getter (lambda (track _table)
(cl-position track (listen-queue-tracks
queue))))
+ (list :name "Duration"
+ :getter (lambda (track _table)
+ (when-let ((duration (listen-track-duration
track)))
+ (listen-format-seconds duration))))
(list :name "Artist" :max-width 20 :align 'right
:getter (lambda (track _table)
(propertize (or (listen-track-artist track)
"")
@@ -303,7 +314,7 @@ which see."
(directory-files-recursively path ".")
(list path))
queue)))
- (cl-callf append (listen-queue-tracks queue) (delq nil (mapcar
#'listen-queue-track files)))
+ (cl-callf append (listen-queue-tracks queue) (listen-queue-tracks-for files))
(listen-queue queue)
(listen-queue-play queue)
queue)
@@ -372,6 +383,16 @@ buffer, if any)."
:date (map-elt metadata "date")
:genre (map-elt metadata "genre"))))
+(defun listen-queue-tracks-for (filenames)
+ "Return tracks for FILENAMES.
+When `listen-queue-ffprobe-p' is non-nil, adds durations read
+with \"ffprobe\"."
+ (with-demoted-errors "listen-queue-tracks-for: %S"
+ (let ((tracks (remq nil (mapcar #'listen-queue-track filenames))))
+ (when listen-queue-ffprobe-p
+ (listen-queue--add-track-durations tracks))
+ tracks)))
+
(defun listen-queue-shuffle (queue)
"Shuffle QUEUE."
(interactive (list (listen-queue-complete)))
@@ -476,9 +497,7 @@ disk."
(defun listen-queue-refresh (queue)
"Refresh QUEUE's tracks from disk."
(setf (listen-queue-tracks queue)
- (delq nil (mapcar (lambda (track)
- (listen-queue-track (listen-track-filename track)))
- (listen-queue-tracks queue)))))
+ (listen-queue-tracks-for (mapcar #'listen-track-filename
(listen-queue-tracks queue)))))
(defun listen-queue-order-by ()
"Order the queue by the column at point.
@@ -542,6 +561,52 @@ Expands filenames relative to playlist's directory."
(cl-loop while (re-search-forward (rx bol (group (not (any "#")) (1+
nonl)) eol) nil t)
collect (expand-file-name (match-string 1))))))
+;;;;; ffprobe queue
+
+(cl-defun listen-queue--add-track-durations (tracks &key (max-processes
listen-queue-max-probe-processes))
+ "Add durations to TRACKS by probing with \"ffprobe\".
+MAX-PROCESSES limits the number of parallel probing processes."
+ ;; Because running "ffprobe" sequentially can be quite slow, we do
+ ;; it asynchronously in a queue.
+ ;; TODO: Generalize this.
+ (let (processes)
+ (cl-labels
+ ((probe-duration (track)
+ (with-demoted-errors "Unable to get duration for %S"
+ (with-current-buffer (get-buffer-create (generate-new-buffer "
*listen: ffprobe*"))
+ (let* ((sentinel (lambda (process status)
+ (unwind-protect
+ (pcase status
+ ((or "killed\n" "interrupt\n"
+ (pred numberp)
+ (rx "exited abnormally with code
" (1+ digit))))
+ ("finished\n"
+ (with-current-buffer (process-buffer
process)
+ (goto-char (point-min))
+ (let ((duration (read
(current-buffer))))
+ (cl-check-type duration number )
+ (setf (listen-track-duration
track) duration)))))
+ (kill-buffer (process-buffer process))
+ (cl-callf2 remove process processes)
+ (probe-more))))
+ (process (make-process
+ :name "listen:ffprobe" :noquery t :type 'pipe
:buffer (current-buffer)
+ :sentinel sentinel
+ :command (list "ffprobe" "-v" "quiet"
"-print_format"
+
"compact=print_section=0:nokey=1:escape=csv"
+ "-show_entries"
"format=duration"
+ (expand-file-name
(listen-track-filename track))))))
+ process))))
+ (probe-more ()
+ (while (and tracks (length< processes max-processes))
+ (let ((track (pop tracks)))
+ (push (probe-duration track) processes)))))
+ (with-timeout ((* 0.05 (length tracks)) (error "Probing for track
duration timed out"))
+ (while (or tracks processes)
+ (probe-more)
+ (while (accept-process-output nil 0.01))
+ (sleep-for 0.01))))))
+
;;;; Footer
(provide 'listen-queue)
diff --git a/listen.el b/listen.el
index afcc1407c2..b30bfc5bd3 100755
--- a/listen.el
+++ b/listen.el
@@ -195,9 +195,7 @@ command with completion."
(defun listen-mode-lighter ()
"Return lighter for `listen-mode'."
- (cl-labels ((format-time (seconds)
- (format-seconds "%h:%z%.2m:%.2s" seconds))
- (format-track ()
+ (cl-labels ((format-track ()
(when-let ((info (listen--info listen-player))
;; Sometimes when paused/stopped, the artist and/or
;; title are nil even if info isn't, so we must
@@ -218,11 +216,11 @@ command with completion."
(list (format-status) " " (format-track)
" ("
(pcase listen-lighter-format
- ('remaining (concat "-" (format-time (- (listen--length
listen-player)
-
(listen--elapsed listen-player)))))
- (_ (concat (format-time (listen--elapsed listen-player))
+ ('remaining (concat "-" (listen-format-seconds (-
(listen--length listen-player)
+
(listen--elapsed listen-player)))))
+ (_ (concat (listen-format-seconds (listen--elapsed
listen-player))
"/"
- (format-time (listen--length
listen-player)))))
+ (listen-format-seconds (listen--length
listen-player)))))
") ")
'("■ ")))))
- [elpa] externals/listen updated (33cc5e8766 -> c250f72f14), ELPA Syncer, 2024/02/29
- [elpa] externals/listen 569018b4eb 05/12: Change: (listen-library-taxy) Show duration with filename, ELPA Syncer, 2024/02/29
- [elpa] externals/listen ee88cbf3d9 01/12: Add: (listen-format-seconds), ELPA Syncer, 2024/02/29
- [elpa] externals/listen d84783ae2d 04/12: Change: Use listen-queue-tracks-for, ELPA Syncer, 2024/02/29
- [elpa] externals/listen dec87e2b82 02/12: Add: Probe track duration with ffprobe, ELPA Syncer, 2024/02/29
- [elpa] externals/listen c0532bdbe9 03/12: Change: (listen-track) Rename length -> duration, ELPA Syncer, 2024/02/29
- [elpa] externals/listen f23fc0096a 07/12: Merge: Read track durations with ffprobe,
ELPA Syncer <=
- [elpa] externals/listen 4c0c6cadba 08/12: Change: Bind "?" to listen menu in library and queue, ELPA Syncer, 2024/02/29
- [elpa] externals/listen ecf0264641 09/12: Fix: (listen-queue-transpose-forward) Keep point on track, ELPA Syncer, 2024/02/29
- [elpa] externals/listen 1a8c98f99c 06/12: Docs: Update readme, ELPA Syncer, 2024/02/29
- [elpa] externals/listen 6763d2f75f 10/12: Tidy: (listen-queue-transpose-forward) Use cl-rotatef, ELPA Syncer, 2024/02/29
- [elpa] externals/listen 32fcea9a9f 11/12: Fix: (listen) Autoload, ELPA Syncer, 2024/02/29
- [elpa] externals/listen c250f72f14 12/12: Tidy: Compiler warnings, ELPA Syncer, 2024/02/29