aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/dwim-shell-config.el76
-rw-r--r--tests/test-dwim-shell-config-command-fixes.el55
2 files changed, 101 insertions, 30 deletions
diff --git a/modules/dwim-shell-config.el b/modules/dwim-shell-config.el
index ad17ea913..230a8532c 100644
--- a/modules/dwim-shell-config.el
+++ b/modules/dwim-shell-config.el
@@ -210,6 +210,41 @@ The timestamp is interpolated here with `format-time-string' so it can't sit
dead inside the shell's single quotes the way a literal =$(date ...)= did."
(format "cp -p '<<f>>' '<<f>>.%s.bak'" (format-time-string "%Y%m%d_%H%M%S")))
+(defun cj/dwim-shell--tar-gzip-command (single-p)
+ "Return the tar-gzip command template.
+SINGLE-P non-nil names the archive after the lone file (=<fne>.tar.gz=);
+otherwise a shared =archive.tar.gz= over all marked files."
+ (if single-p
+ "tar czf '<<fne>>.tar.gz' '<<f>>'"
+ "tar czf '<<archive.tar.gz(u)>>' '<<*>>'"))
+
+(defun cj/dwim-shell--text-to-speech-command (system voice)
+ "Return the text-to-speech command template for SYSTEM using VOICE.
+SYSTEM is a `system-type' symbol: `darwin' synthesizes with `say' and VOICE;
+any other system uses `espeak' (VOICE unused)."
+ (if (eq system 'darwin)
+ (format "say -v %s -o '<<fne>>.aiff' -f '<<f>>'" voice)
+ "espeak -f '<<f>>' -w '<<fne>>.wav'"))
+
+(defun cj/dwim-shell--video-trim-command (trim-type start end)
+ "Return the ffmpeg video-trim command template for TRIM-TYPE.
+TRIM-TYPE is \"Beginning\", \"End\", or \"Both\". START trims that many
+seconds off the front, END off the back (each ignored for the side it does
+not apply to). Signals a `user-error' when a used second count is negative."
+ (pcase trim-type
+ ("Beginning"
+ (when (< start 0) (user-error "Seconds must be non-negative"))
+ (format "ffmpeg -i '<<f>>' -y -ss %d -c:v copy -c:a copy '<<fne>>_trimmed.<<e>>'"
+ start))
+ ("End"
+ (when (< end 0) (user-error "Seconds must be non-negative"))
+ (format "ffmpeg -sseof -%d -i '<<f>>' -y -c:v copy -c:a copy '<<fne>>_trimmed.<<e>>'"
+ end))
+ ("Both"
+ (when (or (< start 0) (< end 0)) (user-error "Seconds must be non-negative"))
+ (format "ffmpeg -i '<<f>>' -y -ss %d -sseof -%d -c:v copy -c:a copy '<<fne>>_trimmed.<<e>>'"
+ start end))))
+
;; ----------------------------- Dwim Shell Command ----------------------------
(use-package dwim-shell-command
@@ -357,9 +392,8 @@ Otherwise, unzip it to an appropriately named subdirectory "
"Tar gzip all marked files into archive.tar.gz."
(interactive)
(dwim-shell-command-on-marked-files
- "Tar gzip" (if (eq 1 (seq-length (dwim-shell-command--files)))
- "tar czf '<<fne>>.tar.gz' '<<f>>'"
- "tar czf '<<archive.tar.gz(u)>>' '<<*>>'")
+ "Tar gzip" (cj/dwim-shell--tar-gzip-command
+ (eq 1 (seq-length (dwim-shell-command--files))))
:utils "tar"))
(defun cj/dwim-shell-commands-epub-to-org ()
@@ -448,34 +482,18 @@ process list, and the file is removed only after the spawned process exits."
"Trim video with options for beginning, end, or both."
(interactive)
(let* ((trim-type (completing-read "Trim from: "
- '("Beginning" "End" "Both")
- nil t))
- (command (pcase trim-type
- ("Beginning"
- (let ((seconds (read-number "Seconds to trim from beginning: " 5)))
- (when (< seconds 0)
- (user-error "Seconds must be non-negative"))
- (format "ffmpeg -i '<<f>>' -y -ss %d -c:v copy -c:a copy '<<fne>>_trimmed.<<e>>'"
- seconds)))
- ("End"
- (let ((seconds (read-number "Seconds to trim from end: " 5)))
- (when (< seconds 0)
- (user-error "Seconds must be non-negative"))
- (format "ffmpeg -sseof -%d -i '<<f>>' -y -c:v copy -c:a copy '<<fne>>_trimmed.<<e>>'"
- seconds)))
- ("Both"
- (let ((start (read-number "Seconds to trim from beginning: " 5))
- (end (read-number "Seconds to trim from end: " 5)))
- (when (or (< start 0) (< end 0))
- (user-error "Seconds must be non-negative"))
- (format "ffmpeg -i '<<f>>' -y -ss %d -sseof -%d -c:v copy -c:a copy '<<fne>>_trimmed.<<e>>'"
- start end))))))
- (dwim-shell-command-on-marked-files
+ '("Beginning" "End" "Both")
+ nil t))
+ (start (if (member trim-type '("Beginning" "Both"))
+ (read-number "Seconds to trim from beginning: " 5) 0))
+ (end (if (member trim-type '("End" "Both"))
+ (read-number "Seconds to trim from end: " 5) 0))
+ (command (cj/dwim-shell--video-trim-command trim-type start end)))
+ (dwim-shell-command-on-marked-files
(format "Trim video (%s)" trim-type)
command
:silent-success t
:utils "ffmpeg")))
-
(defun cj/dwim-shell-commands-drop-audio-from-video ()
"Drop audio from all marked videos."
(interactive)
@@ -694,9 +712,7 @@ all marked files rather than once per file."
"en")))
(dwim-shell-command-on-marked-files
"Text to speech"
- (if (eq system-type 'darwin)
- (format "say -v %s -o '<<fne>>.aiff' -f '<<f>>'" voice)
- "espeak -f '<<f>>' -w '<<fne>>.wav'")
+ (cj/dwim-shell--text-to-speech-command system-type voice)
:utils (if (eq system-type 'darwin) "say" "espeak"))))
(defun cj/dwim-shell-commands-remove-empty-directories ()
diff --git a/tests/test-dwim-shell-config-command-fixes.el b/tests/test-dwim-shell-config-command-fixes.el
index 2f49a868f..2cc3ae72b 100644
--- a/tests/test-dwim-shell-config-command-fixes.el
+++ b/tests/test-dwim-shell-config-command-fixes.el
@@ -29,5 +29,60 @@ so the substitution can't sit dead inside single quotes."
(should (string-match-p "\\.[0-9]\\{8\\}_[0-9]\\{6\\}\\.bak'" cmd))
(should-not (string-match-p "\\$(date" cmd))))
+;;; ----------------------- tar-gzip command builder --------------------------
+
+(ert-deftest test-dwim-tar-gzip-command-single-names-after-file ()
+ "Normal: a single marked file names the archive <fne>.tar.gz over <<f>>."
+ (let ((cmd (cj/dwim-shell--tar-gzip-command t)))
+ (should (string-match-p "'<<fne>>\\.tar\\.gz'" cmd))
+ (should (string-match-p "'<<f>>'" cmd))))
+
+(ert-deftest test-dwim-tar-gzip-command-multi-uses-shared-archive ()
+ "Boundary: multiple files tar into a shared archive.tar.gz over <<*>>."
+ (let ((cmd (cj/dwim-shell--tar-gzip-command nil)))
+ (should (string-match-p "archive\\.tar\\.gz" cmd))
+ (should (string-match-p "'<<\\*>>'" cmd))))
+
+;;; --------------------- text-to-speech command builder ----------------------
+
+(ert-deftest test-dwim-text-to-speech-command-darwin-uses-say-voice ()
+ "Normal: on darwin the command uses `say' with the chosen voice."
+ (let ((cmd (cj/dwim-shell--text-to-speech-command 'darwin "Samantha")))
+ (should (string-match-p "\\`say -v Samantha " cmd))
+ (should (string-match-p "'<<fne>>\\.aiff'" cmd))))
+
+(ert-deftest test-dwim-text-to-speech-command-linux-uses-espeak ()
+ "Boundary: a non-darwin system uses `espeak' and ignores the voice."
+ (let ((cmd (cj/dwim-shell--text-to-speech-command 'gnu/linux "ignored")))
+ (should (string-match-p "\\`espeak " cmd))
+ (should (string-match-p "'<<fne>>\\.wav'" cmd))
+ (should-not (string-match-p "ignored" cmd))))
+
+;;; ----------------------- video-trim command builder ------------------------
+
+(ert-deftest test-dwim-video-trim-command-beginning-uses-ss ()
+ "Normal: trimming the beginning emits a leading -ss with the start seconds."
+ (let ((cmd (cj/dwim-shell--video-trim-command "Beginning" 7 0)))
+ (should (string-match-p "-ss 7 " cmd))
+ (should-not (string-match-p "-sseof" cmd))))
+
+(ert-deftest test-dwim-video-trim-command-end-uses-sseof ()
+ "Normal: trimming the end emits -sseof with the end seconds, no -ss."
+ (let ((cmd (cj/dwim-shell--video-trim-command "End" 0 9)))
+ (should (string-match-p "-sseof -9 " cmd))
+ (should-not (string-match-p "-ss [0-9]" cmd))))
+
+(ert-deftest test-dwim-video-trim-command-both-uses-ss-and-sseof ()
+ "Normal: trimming both ends emits both -ss start and -sseof end."
+ (let ((cmd (cj/dwim-shell--video-trim-command "Both" 3 4)))
+ (should (string-match-p "-ss 3 " cmd))
+ (should (string-match-p "-sseof -4 " cmd))))
+
+(ert-deftest test-dwim-video-trim-command-negative-seconds-errors ()
+ "Error: a negative second count for the used side signals a user-error."
+ (should-error (cj/dwim-shell--video-trim-command "Beginning" -1 0) :type 'user-error)
+ (should-error (cj/dwim-shell--video-trim-command "End" 0 -1) :type 'user-error)
+ (should-error (cj/dwim-shell--video-trim-command "Both" 0 -2) :type 'user-error))
+
(provide 'test-dwim-shell-config-command-fixes)
;;; test-dwim-shell-config-command-fixes.el ends here