;;; emms-playing-time.el --- Display emms playing time on mode line -*- lexical-binding: t; -*-
;; Copyright (C) 2005-2021 Free Software Foundation, Inc.
;; Author: William Xu <william.xwl@gmail.com>, Yoni Rabkin (yrk@gnu.org)
;; This file is part of EMMS.
;; EMMS is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; EMMS is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with EMMS; if not, write to the Free Software Foundation,
;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
;;; Commentary:
;; Display playing time on mode line, it looks like: 01:32/04:09.
;; Put this file into your load-path and the following into your
;; ~/.emacs:
;; (require 'emms-playing-time)
;; (emms-playing-time 1)
;; Note: `(emms-playing-time -1)' will disable emms-playing-time module
;; completely, and is not recommended. (since some other emms modules
;; may rely on it, such as `emms-lastfm.el')
;; Instead, to toggle displaying playing time on mode line, one could
;; call `emms-playing-time-enable-display' and
;; `emms-playing-time-disable-display'."
;;; Code:
(require 'cl-lib)
(require 'emms-info)
(require 'emms-player-simple)
;;; Customizations
(defgroup emms-playing-time nil
"Playing-time module for EMMS."
:group 'emms)
(defcustom emms-playing-time-display-short-p nil
"Non-nil will only display elapsed time.
e.g., display 02:37 instead of 02:37/05:49."
:type 'boolean)
(defcustom emms-playing-time-display-format " %s "
"Format used for displaying playing time."
:type 'string)
(defcustom emms-playing-time-style 'time
"Style used for displaying playing time.
Valid styles are `time' (e.g., 01:30/4:20),
`bar' (e.g., [===> ]),
and `downtime' (e.g. -03:58)."
:type 'symbol)
(defcustom emms-playing-time-resume-from-last-played nil
"If set to Non-nil, emms will resume / seek to
the last playing time when the track is started again."
:type 'boolean)
;;; Emms Playing Time
(define-obsolete-variable-alias 'emms-playing-time-display-p
'emms-playing-time-display-mode "Apr 2021")
(defvar emms-playing-time-display-mode)
(defvar emms-playing-time 0
"Time elapsed in current track.")
(defvar emms-playing-time-string "")
(defvar emms-playing-time-display-timer nil)
(define-obsolete-variable-alias 'emms-playing-time-p
'emms-playing-time-mode "Apr 2021")
(defun emms-playing-time-start ()
"Get ready for display playing time."
(setq emms-playing-time 0)
(unless emms-playing-time-display-timer
(setq emms-playing-time-display-timer
(run-at-time t 1 #'emms-playing-time-display))))
(defun emms-playing-time-stop ()
"Remove playing time on the mode line."
(if (or (not emms-player-paused-p)
emms-player-stopped-p)
(progn
(setq emms-playing-time-string "")
(force-mode-line-update)))
(emms-cancel-timer emms-playing-time-display-timer)
(setq emms-playing-time-display-timer nil))
(defun emms-playing-time-pause ()
"Pause playing time."
(if emms-player-paused-p
(emms-playing-time-stop)
(unless emms-playing-time-display-timer
(setq emms-playing-time-display-timer
(run-at-time t 1 #'emms-playing-time-display)))))
(defun emms-playing-time-seek (sec)
"Seek forward or backward SEC playing time."
(setq emms-playing-time (+ emms-playing-time sec))
(when (< emms-playing-time 0) ; back to start point
(setq emms-playing-time 0)))
(defun emms-playing-time-set (sec)
"Set the playing time to SEC."
(setq emms-playing-time sec)
(when (< emms-playing-time 0) ; back to start point
(setq emms-playing-time 0)))
(defun emms-playing-time (arg)
(declare (obsolete emms-playing-time-mode "Apr 2021"))
(emms-playing-time-mode (if (and arg (> arg 0)) 1 -1)))
(defun emms-playing-time-track-reset ()
(emms-track-set (emms-playlist-current-selected-track)
'playing-time nil))
(defun emms-playing-time-maybe-seek-to-last-played ()
(when-let ((last-playing-time
(emms-track-get (emms-playlist-current-selected-track)
'playing-time)))
(emms-seek-to last-playing-time)))
(define-minor-mode emms-playing-time-mode
"Turn on emms playing time if ARG is positive, off otherwise.
Note: `(emms-playing-time -1)' will disable emms-playing-time
module completely, and is not recommended. (since some other emms
modules may rely on it, such as `emms-lastfm.el')
Instead, to toggle displaying playing time on mode line, one
could call `emms-playing-time-enable-display' and
`emms-playing-time-disable-display'."
:global t
(if emms-playing-time-mode
(progn
;; FIXME: Maybe we shouldn't set this here, and instead the users
;; should call `emms-playing-time-display-mode' if that's what
;; they want.
(setq emms-playing-time-display-mode t)
(emms-playing-time-mode-line)
(add-hook 'emms-player-started-hook #'emms-playing-time-start)
(add-hook 'emms-player-stopped-hook #'emms-playing-time-stop)
(add-hook 'emms-player-finished-hook
#'emms-playing-time-track-reset)
(add-hook 'emms-player-finished-hook #'emms-playing-time-stop)
(add-hook 'emms-player-paused-hook #'emms-playing-time-pause)
(add-hook 'emms-player-seeked-functions #'emms-playing-time-seek)
(add-hook 'emms-player-time-set-functions #'emms-playing-time-set)
(when emms-playing-time-resume-from-last-played
(add-hook 'emms-player-started-hook
#'emms-playing-time-maybe-seek-to-last-played)))
(setq emms-playing-time-display-mode nil)
(emms-playing-time-stop)
(emms-playing-time-restore-mode-line)
(remove-hook 'emms-player-started-hook #'emms-playing-time-start)
(remove-hook 'emms-player-stopped-hook #'emms-playing-time-stop)
(remove-hook 'emms-player-finished-hook #'emms-playing-time-stop)
(remove-hook 'emms-player-finished-hook
#'emms-playing-time-track-reset)
(remove-hook 'emms-player-paused-hook #'emms-playing-time-pause)
(remove-hook 'emms-player-seeked-functions #'emms-playing-time-seek)
(remove-hook 'emms-player-time-set-functions #'emms-playing-time-set)
(remove-hook 'emms-player-started-hook
#'emms-playing-time-maybe-seek-to-last-played)))
;;;###autoload
(define-minor-mode emms-playing-time-display-mode
"Minor mode to display playing time on mode line."
:global t)
;;;###autoload
(defun emms-playing-time-enable-display ()
"Display playing time on mode line."
(declare (obsolete emms-playing-time-display-mode "Apr 2021"))
(interactive)
(setq emms-playing-time-display-mode t))
;;;###autoload
(defun emms-playing-time-disable-display ()
"Remove playing time from mode line."
(declare (obsolete emms-playing-time-display-mode "Apr 2021"))
(interactive)
(setq emms-playing-time-display-mode nil))
(defun emms-playing-time-display ()
"Display playing time on the mode line."
(setq emms-playing-time (round (1+ emms-playing-time)))
(emms-track-set (emms-playlist-current-selected-track)
'playing-time emms-playing-time)
(setq emms-playing-time-string
(if (null emms-playing-time-display-mode)
""
(let* ((min (/ emms-playing-time 60))
(sec (% emms-playing-time 60))
(total-playing-time
(or (emms-track-get
(emms-playlist-current-selected-track)
'info-playing-time)
0))
(total-min-only (/ total-playing-time 60))
(total-sec-only (% total-playing-time 60))
(string
(cl-case emms-playing-time-style
((downtime) ; `downtime' style
(emms-replace-regexp-in-string
" " "0"
(if (or emms-playing-time-display-short-p
;; unable to get total playing-time
(eq total-playing-time 0))
(format "%2d:%2d" min sec)
(format "-%2d:%2d"
(/ (- total-playing-time emms-playing-time) 60)
(% (- total-playing-time sec) 60)))))
((bar) ; `bar' style
(if (zerop total-playing-time)
"[==>........]"
(let (;; percent based on 10
(percent (/ (* emms-playing-time 10)
total-playing-time)))
(concat "["
(make-string percent ?=)
">"
(make-string (- 10 percent) ?\s)
"]"))))
(t ; `time' style
(emms-replace-regexp-in-string
" " "0"
(if (or emms-playing-time-display-short-p
;; unable to get total playing-time
(eq total-playing-time 0))
(format "%2d:%2d" min sec)
(format "%2d:%2d/%2s:%2s"
min sec total-min-only total-sec-only)))))))
(format emms-playing-time-display-format string))))
(force-mode-line-update))
(defun emms-playing-time-mode-line ()
"Add playing time to the mode line."
(or global-mode-string (setq global-mode-string '("")))
(unless (member 'emms-playing-time-string
global-mode-string)
(setq global-mode-string
(append global-mode-string
'(emms-playing-time-string)))))
(defun emms-playing-time-restore-mode-line ()
"Restore the mode line."
(setq global-mode-string
(remove 'emms-playing-time-string global-mode-string))
(force-mode-line-update))
(provide 'emms-playing-time)
;;; emms-playing-time.el ends here