diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-23 20:01:25 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-23 20:01:25 -0500 |
| commit | 5de470b1030c932b4e99ce685c0f27078a5db428 (patch) | |
| tree | 3e9cb37a953a50fe9a81633067dc9c51ef9944c8 /modules/dwim-shell-config.el | |
| parent | c0237adec5a92d6a2814f23b40be4d5415bcd9c8 (diff) | |
| download | dotemacs-5de470b1030c932b4e99ce685c0f27078a5db428.tar.gz dotemacs-5de470b1030c932b4e99ce685c0f27078a5db428.zip | |
fix(dwim-shell): build video-concat filelist in elisp
cj/dwim-shell-commands-concatenate-videos built the ffmpeg concat list with echo '<<*>>' | tr ' ' '\n' | sed 's/^/file /'. That splits on spaces, so any video whose name contains a space produced a broken list, and a name with a quote broke the echo outright.
I extracted cj/dwim-shell--build-concat-filelist, which renders each path as an escaped file '...' line. I write that list to a temp file and run ffmpeg against the quoted listfile, with a trailing rm to clean up after the process exits. The <<*>> token stays only as an inert shell comment, since dwim-shell needs it to run one command over all marked files instead of once per file.
Diffstat (limited to 'modules/dwim-shell-config.el')
| -rw-r--r-- | modules/dwim-shell-config.el | 35 |
1 files changed, 29 insertions, 6 deletions
diff --git a/modules/dwim-shell-config.el b/modules/dwim-shell-config.el index 41b9231f..a68f0a22 100644 --- a/modules/dwim-shell-config.el +++ b/modules/dwim-shell-config.el @@ -169,6 +169,18 @@ single-quoted destination it is interpolated into." (and (stringp prefix) (string-match-p "\\`[[:alnum:] ._-]*\\'" prefix))) +(defun cj/dwim-shell--build-concat-filelist (files) + "Return ffmpeg concat-demuxer filelist text for FILES. +Each path becomes a single-quoted `file' line with embedded single quotes +escaped as \\='\\\\\\='\\=', so paths with spaces, quotes, or shell +metacharacters survive intact — unlike an echo/tr/sed pipeline over the raw +file list." + (mapconcat + (lambda (f) + (format "file '%s'" + (replace-regexp-in-string "'" "'\\\\''" (expand-file-name f)))) + files "\n")) + ;; ----------------------------- Dwim Shell Command ---------------------------- (use-package dwim-shell-command @@ -578,12 +590,23 @@ clipboard contents cannot inject shell commands." :utils "ffmpeg"))) (defun cj/dwim-shell-commands-concatenate-videos () - "Concatenate multiple videos into one." - (interactive) - (dwim-shell-command-on-marked-files - "Concatenate videos" - "echo '<<*>>' | tr ' ' '\n' | sed 's/^/file /' > '<<td>>/filelist.txt' && ffmpeg -f concat -safe 0 -i '<<td>>/filelist.txt' -c copy '<<concatenated.mp4(u)>>'" - :utils "ffmpeg")) + "Concatenate multiple videos into one. +Builds the ffmpeg concat filelist in Elisp so paths with spaces or quotes are +handled, instead of reconstructing it with echo/tr/sed over the raw file list. +The temp filelist is removed after ffmpeg finishes. The trailing =<<*>>= is an +inert shell comment whose only job is to make dwim-shell run one command over +all marked files rather than once per file." + (interactive) + (let ((listfile (make-temp-file "dwim-concat-" nil ".txt"))) + (with-temp-file listfile + (insert (cj/dwim-shell--build-concat-filelist (dwim-shell-command--files)) + "\n")) + (dwim-shell-command-on-marked-files + "Concatenate videos" + (format "ffmpeg -f concat -safe 0 -i %s -c copy '<<concatenated.mp4(u)>>'; rm -f %s # <<*>>" + (shell-quote-argument listfile) + (shell-quote-argument listfile)) + :utils "ffmpeg"))) (defun cj/dwim-shell-commands-create-video-thumbnail () "Extract thumbnail from video at specific time." |
