Subject: Re: bug#76350: [PATCH] Add MPD stats viewer to 'mpc'

From: john muhl <[email-masked]>
Date: Mon, 17 Feb 2025 15:03:51 -0600

Stefan Monnier <[email-masked]> writes:

>> +;;; Stats viewer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>> +
>> +(defcustom mpc-mpd-stats-date-format "%c"
>> +  "Format used for dates in `mpc-mpd-stats'.
>> +See `format-time-string' for formatting details."
>> +  :version "31.1"
>> +  :type 'string)
>> +
>> +(defcustom mpc-mpd-stats-duration-format "%Y, %D, %z%h:%.2m:%.2s"
>> +  "Format used for durations in `mpc-mpd-stats'.
>> +See `format-seconds' for formatting details."
>> +  :version "31.1"
>> +  :type 'string)
>
> Do we need this level of customization?

I hope not.

>> +         :objects (let-alist (mpc-proc-cmd-to-alist (list "stats"))
>> +                    `(("Artists" ,.artists)
>> +                      ("Albums" ,.albums)
>> +                      ("Songs" ,.songs)
>> +                      ("Play Time" ,(mpc--mpd-stats-duration .playtime))
>> +                      ("Uptime" ,(mpc--mpd-stats-duration .uptime))
>> +                      ("DB Play Time" ,(mpc--mpd-stats-duration .db_playtime))
>> +                      ("DB Updated" ,(mpc--mpd-stats-date .db_update))))))
>
> But here I'd be tempted not to hardcode the list, so it will always
> print all the info returned by the server:
>
> - Maybe have a generic heuristic to make the keys pretty (e.g. turn
>   "albums" into "Albums" and "db_update" into "DB Update")
> - Provide ad-hoc elements for those specific entries where we want to
>   prettify the raw value before displaying it (i.e. where we want to
>   apply `mpc--mpd-stats-duration` or `mpc--mpd-stats-date`).
>
> WDYT?

Sounds good. How about the attached?

>From a9de8d5f9bed2b569c0146a5afacccac469b819e Mon Sep 17 00:00:00 2001
From: john muhl <[email-masked]>
Date: Tue, 5 Nov 2024 10:24:28 -0600
Subject: [PATCH] Add MPD stats viewer to 'mpc' (Bug#76350)

* lisp/mpc.el (mpc-server-stats): New command.
(mpc-mode-menu): Add menu item.
(mpc--server-stats-date, mpc--server-stats-duration):
New variable.
(mpc--server-stats-date, mpc--server-stats-duration):
(mpc--server-stats-format): New function.
(mpc-song-viewer-value, mpc-song-viewer-tag):
(mpc-song-viewer-empty): Remove face.
(mpc-table-value, mpc-table-key, mpc-table-empty): New face.
(mpc-describe-song): Use new table face names.
---
 etc/NEWS    | 10 ++++++--
 lisp/mpc.el | 73 +++++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 70 insertions(+), 13 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 6d934b2029c..309fea79ce0 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1134,8 +1134,14 @@ of seconds.  Crossfading can be toggled using the command
 This command displays information about the currently playing song or
 song at point in the "*MPC-Songs*" buffer.  The list of tags to display
 can be customized using the new user option 'mpc-song-viewer-tags' and
-the appearance of the list with the new faces 'mpc-song-viewer-tag',
-'mpc-song-viewer-value', and 'mpc-song-viewer-empty'.
+the appearance of the list with the new faces 'mpc-table-key',
+'mpc-table-value', and 'mpc-table-empty'.
+
+---
+*** New command 'mpc-server-stats'.
+This command displays information about the connected MPD server.  The
+appearance of the list can be customized with the new faces
+'mpc-table-key' and 'mpc-table-value'.
 
 ** VC
 
diff --git a/lisp/mpc.el b/lisp/mpc.el
index e25983e48c9..e639129d86a 100644
--- a/lisp/mpc.el
+++ b/lisp/mpc.el
@@ -1212,6 +1212,7 @@ mpc-mode-menu
      :selected (alist-get 'xfade mpc-status)]
     "--"
     ["Add new browser" mpc-tagbrowser]
+    ["Server Stats" mpc-server-stats]
     ["Update DB" mpc-update]
     ["Quit" mpc-quit]))
 
@@ -2874,17 +2875,17 @@ mpc-notifications-notify
 
 ;;; Song Viewer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(defface mpc-song-viewer-value
+(defface mpc-table-value
   '((t (:inherit vtable)))
-  "Face for tag values in the MPC song viewer.")
+  "Face for values in MPC tables.")
 
-(defface mpc-song-viewer-tag
-  '((t (:inherit (mpc-song-viewer-value bold))))
-  "Face for tag types in the MPC song viewer.")
+(defface mpc-table-key
+  '((t (:inherit (mpc-table-value bold))))
+  "Face for keys in MPC tables.")
 
-(defface mpc-song-viewer-empty
-  '((t (:inherit (mpc-song-viewer-value italic shadow))))
-  "Face for empty tag values in the MPC song viewer.")
+(defface mpc-table-empty
+  '((t (:inherit (mpc-table-value italic shadow))))
+  "Face for empty values in MPC tables.")
 
 (defcustom mpc-song-viewer-tags
   '("Title" "Artist" "Album" "Performer" "Composer"
@@ -2929,15 +2930,15 @@ mpc-describe-song
                         :min-width 3
                         :displayer
                         (lambda (tag &rest _)
-                          (propertize tag 'face 'mpc-song-viewer-tag)))
+                          (propertize tag 'face 'mpc-table-key)))
                       ( :name "Value"
                         :align left
                         :min-width 5
                         :displayer
                         (lambda (value &rest _)
                           (if (and value (not (string-blank-p value)))
-                              (propertize value 'face 'mpc-song-viewer-value)
-                            (propertize "empty" 'face 'mpc-song-viewer-empty)))))
+                              (propertize value 'face 'mpc-table-value)
+                            (propertize "empty" 'face 'mpc-table-empty)))))
            :objects (mapcar
                      (lambda (tag)
                        (pcase tag
@@ -2964,6 +2965,56 @@ mpc-describe-song
                                display-buffer-same-window)
                               (reusable-frames . t))))))
 
+;;; Stats viewer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar mpc--server-stats-date "%c"
+  "Format used for dates in `mpc-server-stats'.
+See `format-time-string' for formatting details.")
+
+(defvar mpc--server-stats-duration "%Y, %D, %z%h:%.2m:%.2s"
+  "Format used for durations in `mpc-server-stats'.
+See `format-seconds' for formatting details.")
+
+(defun mpc--server-stats-date (seconds)
+  "Format SECONDS as a date for display by `mpc-server-stats'."
+  (format-time-string mpc--server-stats-date (string-to-number seconds)))
+
+(defun mpc--server-stats-duration (seconds)
+  "Format SECONDS as a duration for display by `mpc-server-stats'."
+  (format-seconds mpc--server-stats-duration (string-to-number seconds)))
+
+(defun mpc--server-stats-format (stat)
+  "Format STAT data for display by `mpc-server-stats'."
+  (let ((key (seq-reduce (lambda (string from-to)
+                           (string-replace (car from-to) (cdr from-to) string))
+                         '(("Db_" . "DB_") ("_" . " "))
+                         (capitalize (symbol-name (car stat)))))
+        (val (cdr stat)))
+    (list key (cond
+               ((string-match-p "time\\'" key) (mpc--server-stats-duration val))
+               ((string-match-p "date\\'" key) (mpc--server-stats-date val))
+               (t val)))))
+
+(defun mpc-server-stats ()
+  "Display stats about the connected MPD server."
+  (interactive)
+  (let ((buffer "*MPC Stats Viewer*"))
+    (with-current-buffer (get-buffer-create buffer)
+      (special-mode)
+      (let ((inhibit-read-only t))
+        (erase-buffer)
+        (make-vtable
+         :columns '((:name "Server Stats" :align right :min-width 10) "")
+         :formatter (lambda (value column &rest _)
+                      (propertize value 'face (if (= column 0)
+                                                  'mpc-table-key
+                                                'mpc-table-value)))
+         :objects (mapcar #'mpc--server-stats-format
+                          (mpc-proc-cmd-to-alist (list "stats")))))
+      (pop-to-buffer buffer '((display-buffer-reuse-window)
+                              (reusable-frames . t)
+                              (window-height . 10))))))
+
 ;;; Toplevel ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (defcustom mpc-frame-alist '((name . "MPC") (tool-bar-lines . 1)
-- 
2.48.1