emms-help
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[emms-help] Dynamic cover thumbnail caching


From: Pierre Neidhardt
Subject: [emms-help] Dynamic cover thumbnail caching
Date: Mon, 23 Oct 2017 23:42:02 +0100
User-agent: mu4e 0.9.18; emacs 25.2.1

Hi!

EMMS can display covers and that's awesome.  It's somewhat impractical
however that covers need to match some special names and dimensions.

Of course users can run a script through their libraries, but:

- Either it means "polluting" the libraries with thumbnails.

- Or it forces the user to modify their cover artworks destructively.

- It's not dynamic in the sense it needs to be re-run everytime we add
  music to the library.  And to many of us, I'm sure, it's simply to
  much hassle.

After a quick look into the code, I discovered that
`emms-browser-covers' is the perfect place to hook in some code that:

- Finds the track/album cover according to a user-defined function (the
  name/ext/quality can be changed).

- Resizes the picture (if needed) and caches it into some user-defined
  location.  This cache means there will be no need to resize the cover
  next time it is queried.

- Is fully dynamic: if the cover changes (better quality) or a new one
  is added, the cache will be updated.  New albums/tracks are
  automatically cached.

Here follows a draft (plenty of room for optimization).  If feedback is
positive I'll submit a patch :)

--------------------------------------------------------------------------------

(defvar emms-cache-cover-small-size 128)
(defvar emms-cache-cover-medium-size 256)
(defvar emms-cache-cover-large-size 1024) ; Emms does not use it as of this 
writing

(defun emms-cache-cover-filter-default (dir)
  "Select covers containing 'front' in DIR.
If none was found, fallback on all files matching 'cover'.
If none, fallback on `emms-cache-cover-filter-all'.

See `emms-cache-cover-filter'."
  (let (covers)
    ;; TODO: Refactor this code.
    (dolist (ext emms-browser-covers-file-extensions)
      (setq covers (append (file-expand-wildcards (expand-file-name (concat 
"*front*." ext) dir)) covers))
      (setq covers (append (file-expand-wildcards (expand-file-name (concat 
"*Front*." ext) dir)) covers)))
    (unless covers
      (dolist (ext emms-browser-covers-file-extensions)
        (setq covers (append (file-expand-wildcards (expand-file-name (concat 
"*cover*." ext) dir)) covers))
        (setq covers (append (file-expand-wildcards (expand-file-name (concat 
"*Cover*." ext) dir)) covers)))
      (unless covers
        (setq covers (emms-cache-cover-filter-all dir))))
    covers))

(defun emms-cache-cover-filter-all (dir)
  "Return the list of all files with `emms-browser-covers-file-extensions' in 
DIR.

See `emms-cache-cover-filter'."
  (let (covers)
    (dolist (ext emms-browser-covers-file-extensions)
      (setq covers (append (file-expand-wildcards (expand-file-name (concat 
"*." ext) dir)) covers)))))

(defvar emms-cache-cover-filter 'emms-cache-cover-filter-default
  "This filter must hold a function that takes a directory argument and returns 
a list of cover file names.
The list will be processed by `emms-cache-covers'.
See also `emms-cache-cover-filter-default'.")


(defun emms-cache-covers (dir size)
  "Return cached cover SIZE for album in DIR.

SIZE must be 'small, 'medium or 'large.  It will determine the
resolution of the cached file.  See `emms-cache-cover-SIZE-size'.

If cover is not cached or if cache is out-of-date, re-cache it.
If the cover is smaller than `emms-cache-cover-SIZE-size', it
need not be cached and will be used directly.

Emms assumes that you have one album per folder. This function
will always use the same cover per folder."
  (if (eq size 'large)
      ;; 'large is unused for now. Return empty.
      nil
    (let (covers
          cover
          (cover-width 0) (cover-height 0)
          (size-value (symbol-value (intern (concat "emms-cache-cover-" 
(symbol-name size) "-size"))))
          cache-dest-file)
      (setq covers (funcall emms-cache-cover-filter dir))
      (if (not covers)
          nil
        ;; Find best quality cover.
        (let (res)
          (dolist (c covers)
            (setq res (image-size (create-image c) t))
            ;; image-size does not error, it returns (30. 30) instead.
            (and (> (car res) 30) (> (cdr res) 30)
                 (< cover-width (car res)) (< cover-height (cdr res))
                 (setq cover-width (car res) cover-height (cdr res) cover c))))
        (if (and (>= size-value cover-width) (>= size-value cover-height))
            ;; No need to resize and cache.
            cover
          (let ((cache-dest (concat (expand-file-name "cover-cache" 
emms-directory) (file-name-directory cover))))
            (mkdir cache-dest t)
            (setq cache-dest-file (concat
                                   (expand-file-name "cover_" cache-dest)
                                   (symbol-name size)
                                   "." (file-name-extension cover))))
          (and (executable-find "convert")
               (or (not (file-exists-p cache-dest-file))
                   (time-less-p (nth 5 (file-attributes cache-dest-file))
                                (nth 5 (file-attributes cover)) ))
               (let (err msg)
                 ;; An Elisp function would be faster, but Emacs does not seem 
be be
                 ;; able to resize image files. It can resize image displays 
though.
                 (setq msg (with-output-to-string
                             (with-current-buffer standard-output
                               (setq err (call-process "convert" nil '(t t) nil
                                                       "-resize" (format 
"%sx%s" size-value size-value)
                                                       cover
                                                       cache-dest-file)))))
                 ;; (message "EMMS convert status: %s, msg %s" err msg)
                 (when (/= err 0)
                   (warn "%s" msg)
                   (setq cache-dest-file nil))))
          cache-dest-file)))))

;;; Apply the changes.
(setq emms-browser-covers (function emms-cache-covers))

--
Pierre Neidhardt

A journey of a thousand miles starts under one's feet.
                -- Lao Tsu



reply via email to

[Prev in Thread] Current Thread [Next in Thread]