| 1 | ;;; emms-playing-time.el --- Display emms playing time on mode line -*- lexical-binding: t; -*- |
| 2 | |
| 3 | ;; Copyright (C) 2005-2024 Free Software Foundation, Inc. |
| 4 | |
| 5 | ;; Author: William Xu <william.xwl@gmail.com>, Yoni Rabkin (yrk@gnu.org) |
| 6 | |
| 7 | ;; This file is part of EMMS. |
| 8 | |
| 9 | ;; EMMS is free software; you can redistribute it and/or modify |
| 10 | ;; it under the terms of the GNU General Public License as published by |
| 11 | ;; the Free Software Foundation; either version 3, or (at your option) |
| 12 | ;; any later version. |
| 13 | ;; |
| 14 | ;; EMMS is distributed in the hope that it will be useful, |
| 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | ;; GNU General Public License for more details. |
| 18 | ;; |
| 19 | ;; You should have received a copy of the GNU General Public License |
| 20 | ;; along with EMMS; if not, write to the Free Software Foundation, |
| 21 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. |
| 22 | |
| 23 | ;;; Commentary: |
| 24 | |
| 25 | ;; Display playing time on mode line, it looks like: 01:32/04:09. |
| 26 | |
| 27 | ;; Put this file into your load-path and the following into your |
| 28 | ;; ~/.emacs: |
| 29 | ;; (require 'emms-playing-time) |
| 30 | ;; (emms-playing-time 1) |
| 31 | |
| 32 | ;; Note: `(emms-playing-time -1)' will disable emms-playing-time module |
| 33 | ;; completely, and is not recommended. (since some other emms modules |
| 34 | ;; may rely on it') |
| 35 | |
| 36 | ;; Instead, to toggle displaying playing time on mode line, one could |
| 37 | ;; call `emms-playing-time-enable-display' and |
| 38 | ;; `emms-playing-time-disable-display'." |
| 39 | |
| 40 | ;;; Code: |
| 41 | |
| 42 | (require 'emms-info) |
| 43 | (require 'emms-player-simple) |
| 44 | |
| 45 | ;;; Customizations |
| 46 | |
| 47 | (defgroup emms-playing-time nil |
| 48 | "Playing-time module for EMMS." |
| 49 | :group 'emms) |
| 50 | |
| 51 | (defcustom emms-playing-time-display-short-p nil |
| 52 | "Non-nil will only display elapsed time. |
| 53 | e.g., display 02:37 instead of 02:37/05:49." |
| 54 | :type 'boolean) |
| 55 | |
| 56 | (defcustom emms-playing-time-display-hours-p t |
| 57 | "Non-nil will display hours in all times sixty minutes or longer. |
| 58 | e.g., display 1:19:55 instead of 79:55." |
| 59 | :type 'boolean) |
| 60 | |
| 61 | (defcustom emms-playing-time-display-format " %s " |
| 62 | "Format used for displaying playing time." |
| 63 | :type 'string) |
| 64 | |
| 65 | (defcustom emms-playing-time-style 'time |
| 66 | "Style used for displaying playing time. |
| 67 | Valid styles are `time' (e.g., 01:30/4:20), |
| 68 | `bar' (e.g., [===> ]), |
| 69 | and `downtime' (e.g. -03:58)." |
| 70 | :type 'symbol) |
| 71 | |
| 72 | |
| 73 | ;;; Emms Playing Time |
| 74 | |
| 75 | (define-obsolete-variable-alias 'emms-playing-time-display-p |
| 76 | 'emms-playing-time-display-mode "Apr 2021") |
| 77 | (defvar emms-playing-time-display-mode) |
| 78 | |
| 79 | (defvar emms-playing-time 0 |
| 80 | "Time elapsed in current track.") |
| 81 | |
| 82 | (defvar emms-playing-time-string "") |
| 83 | |
| 84 | (defvar emms-playing-time-display-timer nil) |
| 85 | |
| 86 | (define-obsolete-variable-alias 'emms-playing-time-p |
| 87 | 'emms-playing-time-mode "Apr 2021") |
| 88 | |
| 89 | (defun emms-playing-time-start () |
| 90 | "Get ready for display playing time." |
| 91 | (setq emms-playing-time 0) |
| 92 | (unless emms-playing-time-display-timer |
| 93 | (setq emms-playing-time-display-timer |
| 94 | (run-at-time t 1 #'emms-playing-time-display)))) |
| 95 | |
| 96 | (defun emms-playing-time-stop () |
| 97 | "Remove playing time on the mode line." |
| 98 | (if (or (not emms-player-paused-p) |
| 99 | emms-player-stopped-p) |
| 100 | (progn |
| 101 | (setq emms-playing-time-string "") |
| 102 | (force-mode-line-update))) |
| 103 | (emms-cancel-timer emms-playing-time-display-timer) |
| 104 | (setq emms-playing-time-display-timer nil)) |
| 105 | |
| 106 | (defun emms-playing-time-pause () |
| 107 | "Pause playing time." |
| 108 | (if emms-player-paused-p |
| 109 | (emms-playing-time-stop) |
| 110 | (unless emms-playing-time-display-timer |
| 111 | (setq emms-playing-time-display-timer |
| 112 | (run-at-time t 1 #'emms-playing-time-display))))) |
| 113 | |
| 114 | (defun emms-playing-time-seek (sec) |
| 115 | "Seek forward or backward SEC playing time." |
| 116 | (setq emms-playing-time (+ emms-playing-time sec)) |
| 117 | (when (< emms-playing-time 0) ; back to start point |
| 118 | (setq emms-playing-time 0))) |
| 119 | |
| 120 | (defun emms-playing-time-set (sec) |
| 121 | "Set the playing time to SEC." |
| 122 | (setq emms-playing-time sec) |
| 123 | (when (< emms-playing-time 0) ; back to start point |
| 124 | (setq emms-playing-time 0))) |
| 125 | |
| 126 | (defun emms-playing-time (arg) |
| 127 | (declare (obsolete emms-playing-time-mode "Apr 2021")) |
| 128 | (emms-playing-time-mode (if (and arg (> arg 0)) 1 -1))) |
| 129 | |
| 130 | |
| 131 | (define-minor-mode emms-playing-time-mode |
| 132 | "Turn on emms playing time if ARG is positive, off otherwise. |
| 133 | |
| 134 | Note: `(emms-playing-time -1)' will disable emms-playing-time |
| 135 | module completely, and is not recommended. (since some other emms |
| 136 | modules may rely on it.) |
| 137 | |
| 138 | Instead, to toggle displaying playing time on mode line, one |
| 139 | could call `emms-playing-time-enable-display' and |
| 140 | `emms-playing-time-disable-display'." |
| 141 | :global t |
| 142 | (if emms-playing-time-mode |
| 143 | (progn |
| 144 | ;; FIXME: Maybe we shouldn't set this here, and instead the users |
| 145 | ;; should call `emms-playing-time-display-mode' if that's what |
| 146 | ;; they want. |
| 147 | (setq emms-playing-time-display-mode t) |
| 148 | (emms-playing-time-mode-line) |
| 149 | (add-hook 'emms-player-started-hook #'emms-playing-time-start) |
| 150 | (add-hook 'emms-player-stopped-hook #'emms-playing-time-stop) |
| 151 | (add-hook 'emms-player-finished-hook #'emms-playing-time-stop) |
| 152 | (add-hook 'emms-player-paused-hook #'emms-playing-time-pause) |
| 153 | (add-hook 'emms-player-seeked-functions #'emms-playing-time-seek) |
| 154 | (add-hook 'emms-player-time-set-functions #'emms-playing-time-set)) |
| 155 | (setq emms-playing-time-display-mode nil) |
| 156 | (emms-playing-time-stop) |
| 157 | (emms-playing-time-restore-mode-line) |
| 158 | (remove-hook 'emms-player-started-hook #'emms-playing-time-start) |
| 159 | (remove-hook 'emms-player-stopped-hook #'emms-playing-time-stop) |
| 160 | (remove-hook 'emms-player-finished-hook #'emms-playing-time-stop) |
| 161 | (remove-hook 'emms-player-paused-hook #'emms-playing-time-pause) |
| 162 | (remove-hook 'emms-player-seeked-functions #'emms-playing-time-seek) |
| 163 | (remove-hook 'emms-player-time-set-functions #'emms-playing-time-set))) |
| 164 | |
| 165 | ;;;###autoload |
| 166 | (define-minor-mode emms-playing-time-display-mode |
| 167 | "Minor mode to display playing time on mode line." |
| 168 | :global t |
| 169 | ;; When disabling the mode, don't disable `emms-playing-time-display-mode' |
| 170 | ;; since that may be used by other packages. |
| 171 | (if emms-playing-time-display-mode |
| 172 | (emms-playing-time-display-mode 1))) |
| 173 | |
| 174 | ;;;###autoload |
| 175 | (defun emms-playing-time-enable-display () |
| 176 | "Display playing time on mode line." |
| 177 | (declare (obsolete emms-playing-time-display-mode "Apr 2021")) |
| 178 | (interactive) |
| 179 | (setq emms-playing-time-display-mode t)) |
| 180 | |
| 181 | ;;;###autoload |
| 182 | (defun emms-playing-time-disable-display () |
| 183 | "Remove playing time from mode line." |
| 184 | (declare (obsolete emms-playing-time-display-mode "Apr 2021")) |
| 185 | (interactive) |
| 186 | (setq emms-playing-time-display-mode nil)) |
| 187 | |
| 188 | (defun emms-playing-time-display () |
| 189 | "Display playing time on the mode line." |
| 190 | (setq emms-playing-time (round (1+ emms-playing-time))) |
| 191 | (setq emms-playing-time-string |
| 192 | (if (null emms-playing-time-display-mode) |
| 193 | "" |
| 194 | (let* ((total-playing-time |
| 195 | (or (emms-track-get |
| 196 | (emms-playlist-current-selected-track) |
| 197 | 'info-playing-time) |
| 198 | 0)) |
| 199 | (string |
| 200 | (pcase emms-playing-time-style |
| 201 | ('downtime ; `downtime' style |
| 202 | (if (or emms-playing-time-display-short-p |
| 203 | ;; unable to get total playing-time |
| 204 | (eq total-playing-time 0)) |
| 205 | (emms-playing-time-format-time emms-playing-time) |
| 206 | (concat "-" (emms-playing-time-format-time |
| 207 | (- total-playing-time emms-playing-time))))) |
| 208 | ('bar ; `bar' style |
| 209 | (if (zerop total-playing-time) |
| 210 | "[==>........]" |
| 211 | (let (;; percent based on 10 |
| 212 | (percent (/ (* emms-playing-time 10) |
| 213 | total-playing-time))) |
| 214 | (concat "[" |
| 215 | (make-string percent ?=) |
| 216 | ">" |
| 217 | (make-string (- 10 percent) ?\s) |
| 218 | "]")))) |
| 219 | (_ ; `time' style |
| 220 | (concat |
| 221 | (emms-playing-time-format-time emms-playing-time) |
| 222 | (unless (or emms-playing-time-display-short-p |
| 223 | ;; unable to get total playing-time |
| 224 | (eq total-playing-time 0)) |
| 225 | (concat "/" (emms-playing-time-format-time total-playing-time)))))))) |
| 226 | (format emms-playing-time-display-format string)))) |
| 227 | (force-mode-line-update)) |
| 228 | |
| 229 | (defun emms-playing-time-format-time (duration) |
| 230 | "Format DURATION, a positive number of seconds, into a string. |
| 231 | |
| 232 | The string will show the minutes and seconds and, if |
| 233 | `emms-playing-time-display-hours-p' is non-nil and the duration is |
| 234 | sixty minutes or more, the hours." |
| 235 | (let ((minutes (/ duration 60)) |
| 236 | (seconds (% duration 60))) |
| 237 | (if (and emms-playing-time-display-hours-p (>= minutes 60)) |
| 238 | (format "%d:%02d:%02d" (/ minutes 60) (% minutes 60) seconds) |
| 239 | (format "%02d:%02d" minutes seconds)))) |
| 240 | |
| 241 | (defun emms-playing-time-mode-line () |
| 242 | "Add playing time to the mode line." |
| 243 | (or global-mode-string (setq global-mode-string '(""))) |
| 244 | (unless (member 'emms-playing-time-string |
| 245 | global-mode-string) |
| 246 | (setq global-mode-string |
| 247 | (append global-mode-string |
| 248 | '(emms-playing-time-string))))) |
| 249 | |
| 250 | (defun emms-playing-time-restore-mode-line () |
| 251 | "Restore the mode line." |
| 252 | (setq global-mode-string |
| 253 | (remove 'emms-playing-time-string global-mode-string)) |
| 254 | (force-mode-line-update)) |
| 255 | |
| 256 | (provide 'emms-playing-time) |
| 257 | |
| 258 | ;;; emms-playing-time.el ends here |