Image, video and music previews for Dirvish that look the same in GUI, terminal and TRAMP directories (ffmpeg-based)
  • Emacs Lisp 98.6%
  • Makefile 1.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Paul Provost d0183e9983 Put the remote pty in raw mode so PNG output isn't CRLF-translated
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 16:47:59 -04:00
test Put the remote pty in raw mode so PNG output isn't CRLF-translated 2026-10-24 16:47:59 -04:00
.gitignore Initial commit: ffmpeg-based media previews for Dirvish 2026-10-21 05:42:09 -04:00
dirvish-ffpreview.el Put the remote pty in raw mode so PNG output isn't CRLF-translated 2026-10-24 16:47:59 -04:00
Makefile Initial commit: ffmpeg-based media previews for Dirvish 2026-10-21 05:42:09 -04:00
README.md Add dirvish-ffpreview-mode to install and keep the dispatcher first 2026-10-21 05:44:05 -04:00

dirvish-ffpreview

Image, video and music previews for Dirvish that look the same wherever Emacs runs: GUI frames, terminal frames, an emacsclient -t reached over SSH, and remote (TRAMP) directories.

Every preview has the same three parts:

  1. the file name
  2. a picture: the image itself, a frame from the video, or the album cover
  3. a metadata block: format, size, duration, bit rate, tags (title, artist, album, ...), video codec, resolution and frame rate, audio codec, sample rate, bit depth and channels

Everything comes from ffprobe and ffmpeg. Only the way the picture is drawn depends on the frame:

Frame Picture drawn with
GUI create-image
Terminal with kitty-graphics.el enabled kitty-graphics.el (kitty protocol or Sixel)
Any other terminal no picture, metadata only

Remote directories

Dirvish gives remote directories a text-only preview (head/ls) and skips every media previewer. dirvish-ffpreview-mode puts this previewer back in front of it (unless dirvish-ffpreview-remote is nil).

Remote files are probed and thumbnailed on their own host, through a direct-async TRAMP process (a plain ssh host cmd that reuses TRAMP's user, port and multi-hop settings). Only the PNG thumbnail, usually a few hundred KB, travels back. The media file is never copied.

The remote command runs in a POSIX login shell (sh -lc, see dirvish-ffpreview-remote-shell), so ffmpeg installed by Homebrew or in ~/.local/bin is found even though a bare ssh host cmd would not search those directories.

Requirements

  • Emacs 29.1 or later, and Dirvish.
  • ffmpeg and ffprobe on Emacs's exec-path for local files.
  • For remote directories: direct-async TRAMP processes, and ffmpeg and ffprobe on the remote host's login-shell PATH.
  • For pictures in terminal frames: kitty-graphics.el with kitty-graphics-mode on, in a terminal it supports (kitty, Ghostty, WezTerm, or a Sixel terminal).

Installation

With straight.el and use-package:

(use-package dirvish-ffpreview
  :straight (dirvish-ffpreview
             :type git
             :repo "ssh://git@git.home.provost.one:222/paul/dirvish-ffpreview.git")
  :after dirvish
  :config (dirvish-ffpreview-mode 1))

dirvish-ffpreview-mode puts the ffpreview previewer first in dirvish-preview-dispatchers and removes the ones it replaces (dirvish-ffpreview-superseded-dispatchers: Dirvish's image, video, audio and gif, and kitty-graphics' kitty-image). kitty-graphics.el adds kitty-image back whenever kitty-graphics-mode turns on, which in a daemon happens at the first terminal frame, long after init, so the mode fixes the list again from kitty-graphics-mode-hook. Changes apply to new Dirvish sessions.

Direct-async processes for SSH connections:

(connection-local-set-profile-variables
 'remote-direct-async-process
 '((tramp-direct-async-process . t)))
(dolist (protocol '("ssh" "scp" "sshx"))
  (connection-local-set-profiles
   `(:application tramp :protocol ,protocol)
   'remote-direct-async-process))

How it works

When point lands on a media file, the previewer:

  1. looks up the cache, keyed by the file's full (possibly TRAMP) name, size, modification time and dirvish-ffpreview-thumbnail-width;
  2. if there's no metadata yet, runs ffprobe -show_format -show_streams -of json in the background and shows "Reading metadata…";
  3. then, if the file has a picture (any video stream, or embedded cover art for music), runs ffmpeg ... -frames:v 1 -f image2pipe -c:v png - in the background and shows the metadata with "Generating preview…". Videos are captured at dirvish-ffpreview-video-seek-fraction (10%) of their duration, since the first frames are often black;
  4. shows the cached picture and metadata, instantly from then on.

Only one job runs at a time. Moving to another file stops the running job, so scrolling quickly through a remote directory doesn't pile up ssh processes. Jobs give up after dirvish-ffpreview-timeout seconds.

Results live under dirvish-cache-dir, in thumbnails/ffpreview/. Delete that directory to clear the cache.

Options

Option Default Meaning
dirvish-ffpreview-superseded-dispatchers (kitty-image image video audio gif) previewers the mode removes
dirvish-ffpreview-remote t also preview in TRAMP directories
dirvish-ffpreview-ffmpeg-program "ffmpeg" local ffmpeg
dirvish-ffpreview-ffprobe-program "ffprobe" local ffprobe
dirvish-ffpreview-remote-ffmpeg-program "ffmpeg" ffmpeg on remote hosts
dirvish-ffpreview-remote-ffprobe-program "ffprobe" ffprobe on remote hosts
dirvish-ffpreview-shell "/bin/sh" local shell used to run them
dirvish-ffpreview-remote-shell ("sh" "-lc") remote command prefix
dirvish-ffpreview-thumbnail-width 800 maximum thumbnail width in pixels
dirvish-ffpreview-video-seek-fraction 0.1 where in a video the thumbnail is taken
dirvish-ffpreview-image-height 0.6 share of the preview window the picture may use
dirvish-ffpreview-timeout 30 seconds before a job is abandoned
dirvish-ffpreview-image-exts, -video-exts, -audio-exts Dirvish's lists which extensions are handled

Limitations

  • SVG images get a picture only in local directories in GUI frames, where Emacs draws them itself. ffmpeg can't rasterize SVG.
  • Formats ffmpeg can't decode (some RAW camera files, for instance) show metadata only, if ffprobe can read that.
  • Connections without direct-async support keep Dirvish's text preview.

Development

make test      # ERT tests, dependencies from the straight.el build directory
make compile   # byte-compile with warnings as errors

Set STRAIGHT_BUILD if your straight.el build directory is elsewhere.

License

GPL-3.0-or-later.