From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: john muhl Newsgroups: gmane.emacs.bugs Subject: bug#73538: [PATCH] Add notifications support to 'mpc' Date: Sat, 12 Oct 2024 09:43:56 -0500 Message-ID: <87h69hwfdf.fsf@pub.pink> References: <87frpjtmx6.fsf@pub.pink> <87r08lwhi1.fsf@pub.pink> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="26624"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: mu4e 1.12.1; emacs 31.0.50 Cc: 73538@debbugs.gnu.org To: Stefan Monnier Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sat Oct 12 16:45:07 2024 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1szdMf-0006nW-VW for geb-bug-gnu-emacs@m.gmane-mx.org; Sat, 12 Oct 2024 16:45:06 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1szdMQ-0003Zq-2R; Sat, 12 Oct 2024 10:44:50 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1szdMO-0003ZZ-G6 for bug-gnu-emacs@gnu.org; Sat, 12 Oct 2024 10:44:48 -0400 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1szdMO-0004Qn-0K for bug-gnu-emacs@gnu.org; Sat, 12 Oct 2024 10:44:48 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:References:In-Reply-To:From:To:Subject; bh=Axf9VkYh5PNMLo1oCGQmjHcnaVe8XjJ6do5um/FZ0qM=; b=XtNzraYL1QwEe6cTQ7VAoBcLAHja+XN1lojaXuALDsg/fpEkmhmAOv8na75UiJOdsCWuSs+C1JAr4ua2IvVp3Sgq96+fC5Uoawuh8PK7+Y1Te2Yz2I9toQ5dzF/atbFmGEMOBmRCxrAWzsoL9I1H6ErPTlSk4I+DiVQhf20593PPd88ql7Pi4mBXxKw4XmdiKAYaSI/weYr0SPGcMhn0w1NT3Bd19IePFQsiM9tfxyu7sHs/jCEe3qhuSSe+mCmlIZfTNyJBchzAniqgtdacFf1jeul99mBT0qevXmDCrwVo3YqM5FO8obu+l/I0wB7nfaivet3AcgnMwid3fFiVqQ==; Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1szdMc-0003bD-44 for bug-gnu-emacs@gnu.org; Sat, 12 Oct 2024 10:45:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: john muhl Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 12 Oct 2024 14:45:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 73538 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 73538-submit@debbugs.gnu.org id=B73538.172874426113749 (code B ref 73538); Sat, 12 Oct 2024 14:45:02 +0000 Original-Received: (at 73538) by debbugs.gnu.org; 12 Oct 2024 14:44:21 +0000 Original-Received: from localhost ([127.0.0.1]:46448 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1szdLw-0003Zg-H9 for submit@debbugs.gnu.org; Sat, 12 Oct 2024 10:44:21 -0400 Original-Received: from fout-a7-smtp.messagingengine.com ([103.168.172.150]:44003) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1szdLt-0003ZS-J5 for 73538@debbugs.gnu.org; Sat, 12 Oct 2024 10:44:19 -0400 Original-Received: from phl-compute-01.internal (phl-compute-01.phl.internal [10.202.2.41]) by mailfout.phl.internal (Postfix) with ESMTP id DCE1313801F2; Sat, 12 Oct 2024 10:43:57 -0400 (EDT) Original-Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-01.internal (MEProxy); Sat, 12 Oct 2024 10:43:57 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pub.pink; h=cc :cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm2; t=1728744237; x=1728830637; bh=Axf9VkYh5P NMLo1oCGQmjHcnaVe8XjJ6do5um/FZ0qM=; b=Phwxa43dpUmexEX9iSN50lD5j8 O9TCTr5jxFOIexdUq8l06Om6fnbeUbIfHfYG6edn51P7eASqKFEY3WzTkTeL5LBp yF4ukUBzoZV/g/Sp5IVNP//S4yeHHR9gwVF/1CbeDX7Ot1oA1XaFAnlO3/SXHzJx Akk/I8sn1TvpF7yF3JvAQQDNUsbOFymbPEHzsVcJexA8agEUOM4cAo7OMw+ZI5ry UTtFpasuTClE7XxF3uxvq0OsNzYawsaKcWKCXfX8RHdU33mmJ204QujYehI7m0I8 i1EDrI36phaCBU7L9F3Pcu+5HYquVeNb0SXe3G0WDaXW9DBo05LfkbmGBx8A== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; t=1728744237; x=1728830637; bh=Axf9VkYh5PNMLo1oCGQmjHcnaVe8 XjJ6do5um/FZ0qM=; b=jVG33tb5txl5LW1bQ2fbMXnet1wY4JRUGB9WT7F5b0At O5nxXpxgRyv0QA0RU0GQbXQRIUSSMppL4s+saGz6UR+l5rx1XAG4PS4CQmjUJxu2 APwlFBRXK59s48Gby64Ri8bVIS1hQaWMNHRvPJFQBA+uAfci+Sago7HxEKwaXkod TGAgQZNVC5oOE3jJLaZlilk68XhGRRt1SmtBkQqZ42fdXHB5vF6PIvstW9Sb7aNw iAsai/2tRMt4tNkYzTKyPU+P+D59lElbzL7cEUNPcd0nLJBV1EQkZ6OIrGvrEl/s Z+VHvVAaLXe4EEnVoJZzXP+TXvLe/uRv/dO0pDQAlA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeftddrvdeguddgkedtucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggvpdfu rfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnh htshculddquddttddmnecujfgurhephffvvefujghffgffkfggtgesmhdtreertderjeen ucfhrhhomhepjhhohhhnuchmuhhhlhcuoehjmhesphhusgdrphhinhhkqeenucggtffrrg htthgvrhhnpeehkeeiudffvefhieelueduleelvddtjeevtdetgefgieelhfehieekleef ffffteenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpe hjmhesphhusgdrphhinhhkpdhnsggprhgtphhtthhopedvpdhmohguvgepshhmthhpohhu thdprhgtphhtthhopeejfeehfeekseguvggssghughhsrdhgnhhurdhorhhgpdhrtghpth htohepmhhonhhnihgvrhesihhrohdruhhmohhnthhrvggrlhdrtggr X-ME-Proxy: Feedback-ID: i74194916:Fastmail Original-Received: by mail.messagingengine.com (Postfix) with ESMTPA; Sat, 12 Oct 2024 10:43:57 -0400 (EDT) In-Reply-To: <87r08lwhi1.fsf@pub.pink> (john muhl's message of "Fri, 11 Oct 2024 20:39:29 -0500") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:293449 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Here=E2=80=99s the same patch but with the excess whitespace removed. --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: attachment; filename=0001-Add-notifications-support-to-mpc-Bug-73538.patch Content-Transfer-Encoding: quoted-printable >From 2f8a66174e37ab076de82eda3759230ed1c3a58c Mon Sep 17 00:00:00 2001 From: john muhl Date: Sun, 15 Sep 2024 19:52:25 -0500 Subject: [PATCH] Add notifications support to 'mpc' (Bug#73538) * lisp/mpc.el (mpc-notifications, mpc-notifications-title) (mpc-notifications-body): New option. (mpc--notifications-id): New variable. (mpc-notifications-notify, mpc-cover-image-find) (mpc-cover-image-p, mpc--notifications-format): New function. (mpc-format): Use 'mpc-cover-find' and expand docstring to include details about the FORMAT-SPEC. (mpc-status-callbacks): Add file callback for notifications. --- etc/NEWS | 8 +++++ lisp/mpc.el | 93 ++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 93 insertions(+), 8 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index c2919169bbf..6ffec916848 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -535,6 +535,14 @@ instead. *** A new shortcut to navigate to previous menu. The hardcoded "^" shortcut gets you back to the previous menu. =20 +** MPC + +--- +*** New user option 'mpc-notifications'. +When non-nil MPC displays a desktop notification when the song changes. +The notification=E2=80=99s title and body can be customized with +'mpc-notifications-title' and 'mpc-notifications-body'. + * New Modes and Packages in Emacs 31.1 =20 diff --git a/lisp/mpc.el b/lisp/mpc.el index 768c70c2e3a..a8789b19c42 100644 --- a/lisp/mpc.el +++ b/lisp/mpc.el @@ -95,6 +95,8 @@ (require 'cl-lib) (require 'subr-x)) =20 +(require 'notifications) + (defgroup mpc () "Client for the Music Player Daemon (mpd)." :prefix "mpc-" @@ -460,6 +462,7 @@ mpc-status-callbacks (state . mpc--faster-toggle-refresh) ;Only ffwd/rewind while play/pau= se. (volume . mpc-volume-refresh) (file . mpc-songpointer-refresh) + (file . mpc-notifications-notify) ;; The song pointer may need updating even if the file doesn't change, ;; if the same song appears multiple times in a row. (song . mpc-songpointer-refresh) @@ -958,6 +961,20 @@ mpc-file-local-copy ;; aux) )) =20 +(defun mpc-cover-image-find (file) + "Find cover image for FILE." + (and-let* ((default-directory mpc-mpd-music-directory) + (dir (mpc-file-local-copy (file-name-directory file))) + (files (directory-files dir)) + (cover (seq-find #'mpc-cover-image-p files)) + ((expand-file-name cover dir))))) + +(defun mpc-cover-image-p (file) + "Check if FILE is a cover image." + (let ((covers '(".folder.png" "folder.png" "cover.jpg" "folder.jpg"))) + (or (seq-find (lambda (cover) (string=3D file cover)) covers) + (and mpc-cover-image-re (string-match-p mpc-cover-image-re file)))= )) + ;;; Formatter ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; =20 (defcustom mpc-cover-image-re nil ; (rx (or ".jpg" ".jpeg" ".png") string-= end) @@ -992,7 +1009,15 @@ mpc-tempfiles-add (push file mpc-tempfiles)) =20 (defun mpc-format (format-spec info &optional hscroll) - "Format the INFO according to FORMAT-SPEC, inserting the result at point= ." + "Format the INFO according to FORMAT-SPEC, inserting the result at point. + +FORMAT-SPEC is a string of the format '%-WIDTH{NAME-POST}' where the first +'-', WIDTH and -POST are optional. % followed by the optional '-' means +to right align the output. WIDTH limits the output to the specified +number of characters by replacing any further output with a horizontal +ellipsis. The optional -POST means to use the empty string if NAME is +absent or else use the concatenation of the content of NAME with the +string POST." (let* ((pos 0) (start (point)) (col (if hscroll (- hscroll) 0)) @@ -1026,7 +1051,8 @@ mpc-format (substring time (match-end = 0)) time))))) ('Cover - (let ((dir (file-name-directory (cdr (assq 'file info= ))))) + (let* ((file (alist-get 'file info)) + (dir (file-name-directory file))) ;; (debug) (setq pred ;; We want the closure to capture the current @@ -1037,12 +1063,7 @@ mpc-format (and (funcall oldpred info) (equal dir (file-name-directory (cdr (assq 'file info)))= ))))) - (if-let* ((covers '(".folder.png" "folder.png" "cov= er.jpg" "folder.jpg")) - (cover (cl-loop for file in (directory-fi= les (mpc-file-local-copy dir)) - if (or (member (downcase = file) covers) - (and mpc-cover-ima= ge-re - (string-match= mpc-cover-image-re file))) - return (concat dir file))) + (if-let* ((cover (mpc-cover-image-find file)) (file (with-demoted-errors "MPC: %s" (mpc-file-local-copy cover)))) (let (image) @@ -2766,6 +2787,62 @@ mpc-drag-n-drop (t (error "Unsupported drag'n'drop gesture")))))) =20 +;;; Notifications ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;= ;; + +(declare-function notifications-notify "notifications") + +(defcustom mpc-notifications nil + "Non-nil means to display notifications when the song changes." + :version "31.1" + :type 'boolean) + +(defcustom mpc-notifications-title + '("%{Title}" "Unknown Title") + "FORMAT-SPEC used in the notification title. + +The first element that returns a non-emtpy string is used. The last +element is a plain string to use as fallback for when none of the tags +are found. See `mpc-format' for the definition of FORMAT-SPEC." + :version "31.1" + :type '(repeat string)) + +(defcustom mpc-notifications-body + '("%{Artist}" "%{AlbumArtist}" "Unknown Artist") + "FORMAT-SPEC used in the notification body. + +The first element that returns a non-emtpy string is used. The last +element is a plain string to use as fallback for when none of the tags +are found. See `mpc-format' for the definition of FORMAT-SPEC." + :version "31.1" + :type '(repeat string)) + +(defvar mpc--notifications-id nil) + +(defun mpc--notifications-format (format-specs) + "Use FORMAT-SPECS to get string for use in notification." + (seq-some + (lambda (spec) + (let ((text (with-temp-buffer + (mpc-format spec mpc-status) + (buffer-string)))) + (if (string=3D "" text) nil text))) + format-specs)) + +(defun mpc-notifications-notify () + "Display a notification with information about the current song." + (when-let ((mpc-notifications) + ((notifications-get-server-information)) + ((string=3D "play" (alist-get 'state mpc-status))) + (title (mpc--notifications-format mpc-notifications-title)) + (body (mpc--notifications-format mpc-notifications-body)) + (icon (or (mpc-cover-image-find (alist-get 'file mpc-status)) + notifications-application-icon))) + (setq mpc--notifications-id + (notifications-notify :title title + :body body + :app-icon icon + :replaces-id mpc--notifications-id)))) + ;;; Toplevel ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;= ;; =20 (defcustom mpc-frame-alist '((name . "MPC") (tool-bar-lines . 1) --=20 2.46.2 --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable john muhl writes: > Here=E2=80=99s an updated patch. Should have all feedback incorporated and > now with a NEWS entry. > > Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of te= xt editors" writes: > >>> +(defcustom mpc-notifications-function 'mpc-notifications-notify >>> + "Function called to display notification." >>> + :version "31.1" >>> + :type 'function) >> >> Nitpick: I'd prefer it to use #' to quote `mpc-notifications-notify`. >> Nitpick: The docstring should describe how it's called (how many args >> are passed to it, what those args will be, and what it should return). >> Question: Is it worth making this a `defcustom`? I have a hard time >> imagining someone using the Customize UI to set this. > > With the title and body now customizable this isn=E2=80=99t needed anymor= e. > >>> +(defcustom mpc-notifications nil >>> + "Non-nil means to display notifications when the song changes." >>> + :version "31.1" >>> + :type 'boolean >>> + :set (lambda (symbol value) >>> + (if-let ((cb (cons 'file mpc-notifications-function)) >>> + value) >>> + (add-to-list 'mpc-status-callbacks cb) >>> + (setq mpc-status-callbacks (delete cb mpc-status-callbacks)= )) >>> + (set-default-toplevel-value symbol value))) >> >> Comment: I'd assume that `file` changes are rare enough that it's OK to >> always have a callback in there regardless if notifications are enabled = and >> instead test the value of `mpc-notifications` in that callback before >> calling `mpc-notifications-function`. > > Ok. Moved it in with the other callbacks. > >>> +(defvar mpc-notifications-id nil) >> >> Comment: AFAICT this is an internal var so it should use a "--". > > Done. > >>> +(defun mpc-notifications-notify () >>> + "Display a notification with information about the current song." >>> + (interactive) >>> + (mpc-status-refresh) >> >> Question: Why is it interactive? > > I have a global mpc-minor-mode that adds a key binding for > notifications and it must have bled through. > >> Question: Why does it need `mpc-status-refresh`? It seems odd to call >> `mpc-status-refresh` from an "mpc-status-callback" (i.e. a function >> called in response to an `mpc-status-refresh`). > > Looks like debugging debris. > >>> + (when-let (((string=3D "play" (alist-get 'state mpc-status))) >>> + (title (or (alist-get 'Title mpc-status) "Unknown Title")) >>> + (body (or (alist-get 'Artist mpc-status) >>> + (alist-get 'AlbumArtist mpc-status) >>> + "Unknown Artist")) >> >> Comment: I think it would make sense to use `mpc-format` here with >> Custom vars to specify what goes in the title and body. > > Instead of changing mpc-format I made the customizable variables > take a list of specs where the first element to return something > interesting is used and a plain string can be added for fallback: > > (setopt mcp-notifications-body-specs > '("%{Artist}" "%{AlbumArtist}" "Unknown Artist")) > > I added your description of the FORMAT-SPEC to the mpc-format > docstring too. > >>> + (pcase system-type >>> + ("windows-nt" >>> + (w32-notification-notify :title title :body body :icon icon)) >>> + ("haiku" >>> + (setq mpc-notifications-id >>> + (haiku-notifications-notify :title title >>> + :body body >>> + :app-icon icon >>> + :replaces-id mpc-notification= s-id))) >>> + ("android" >>> + (setq mpc-notifications-id >>> + (android-notifications-notify :title title >>> + :body body >>> + :icon icon >>> + :replaces-id mpc-notificati= ons-id)) >>> + (android-notifications-notify :title title :body body :icon ico= n)) >>> + (_ (when (notifications-get-server-information) >>> + (setq mpc-notifications-id >>> + (notifications-notify :title title >>> + :body body >>> + :app-icon icon >>> + :replaces-id mpc-notifications-= id))))))) >> >> Comment: Eww! >> Question: Don't we have a function in Emacs which knows which >> notification backend to use so we don't need to do this ugly dispatch >> dance here? > > Well there are erc-notifications-notify, gnus-notifications-notify > and org-show-notifications but none of those seemed appropriate > for reuse by mpc. > > Rather than add a fourth implementation I removed support for > everything except DBus. I=E2=80=99ll try to work up a patch for > notifications.el that could be used in erc, gnus, org and mpc then > we can revisit support for the rest. Does that sound OK or do you > have another idea? > >>> +(defun mpc-cover-image-find (file) >>> + "Find cover image for FILE." >>> + (and-let* ((default-directory mpc-mpd-music-directory) >>> + (dir (file-name-directory file)) >>> + (files (directory-files (mpc-file-local-copy dir))) >>> + (cover (seq-find #'mpc-cover-image-p files)) >>> + ((expand-file-name cover dir))))) >>> + >>> +(defun mpc-cover-image-p (file) >>> + "Check if FILE is a cover image." >>> + (let ((covers '(".folder.png" "folder.png" "cover.jpg" "folder.jpg")= )) >>> + (or (seq-find (lambda (cover) (string=3D file cover)) covers) >>> + (and mpc-cover-image-re (string-match-p mpc-cover-image-re fil= e))))) >> >> Comment: This should be consolidated with the `Cover` code in `mpc-forma= t`. > > mpc-format now uses mpc-cover-image-find. > > Thanks. > > > [2. text/x-patch; 0001-Add-notifications-support-to-mpc-Bug-73538.patch].= .. --=-=-=--