branch: elpa/subed commit 7e04e1603f0c484cbd7a86c3d96114e44209a2d6 Author: Random User <rnd...@posteo.de> Commit: Random User <rnd...@posteo.de>
Use C-u ... when inserting subtitles --- subed/subed-config.el | 10 +++ subed/subed-srt.el | 88 ++++++++++++++++---- tests/test-subed-srt.el | 217 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 299 insertions(+), 16 deletions(-) diff --git a/subed/subed-config.el b/subed/subed-config.el index b9ed926..62b7e66 100644 --- a/subed/subed-config.el +++ b/subed/subed-config.el @@ -129,6 +129,16 @@ typed something.") "Whether the player was paused by the user or automatically.") +(defcustom subed-subtitle-spacing 100 + "How many milliseconds to keep between subtitles." + :type 'integer + :group 'subed) + +(defcustom subed-default-subtitle-length 1.0 + "How long to make subtitles in seconds when inserted after the last subtitle." + :type 'float + :group 'subed) + (defcustom subed-loop-seconds-before 0 "When looping over a single subtitle, start the loop this many seconds before the subtitle starts." diff --git a/subed/subed-srt.el b/subed/subed-srt.el index 9f0dbb4..d97a5db 100644 --- a/subed/subed-srt.el +++ b/subed/subed-srt.el @@ -309,22 +309,78 @@ Return point or nil if there is no previous subtitle." (interactive) (subed-srt--adjust-subtitle-stop-relative -100)) -;; TODO: Write tests -;; TODO: Implement support for prefix argument to -;; - insert n subtitles with C-u n M-i. -;; - insert 1 subtitle before the current one with C-u M-i. -(defun subed-srt-subtitle-insert () - "Insert a subtitle after the current." - (interactive) - (let ((start-time (+ (subed-srt--subtitle-msecs-stop) 100)) - (stop-time (- (save-excursion - (subed-srt-forward-subtitle-id) - (subed-srt--subtitle-msecs-start)) 100))) - (subed-srt-forward-subtitle-id) - (insert (format "1\n%s --> %s\n\n\n" - (subed-srt--msecs-to-timestamp start-time) - (subed-srt--msecs-to-timestamp stop-time)))) - (previous-line 2)) +(defun subed-srt-subtitle-insert (arg) + "Insert subtitle(s). +`universal-argument' is used in the following manner: + \\[subed-subtitle-insert] Insert 1 subtitle after the current subtitle + \\[universal-argument] - \\[subed-subtitle-insert] Insert 1 subtitle before the current subtitle + \\[universal-argument] 5 \\[subed-subtitle-insert] Insert 5 subtitles after the current subtitle + \\[universal-argument] - 5 \\[subed-subtitle-insert] Insert 5 subtitles before the current subtitle + \\[universal-argument] \\[subed-subtitle-insert] Insert 1 subtitle before the current subtitle + \\[universal-argument] \\[universal-argument] \\[subed-subtitle-insert] Insert 2 subtitles before the current subtitle" + (interactive "P") + (let* ((number-of-subs (cond ((eq arg nil) 1) ;; M-i + ((integerp arg) arg) ;; C-u N M-i / C-u - N M-i + ;; C-u [C-u ...] M-i / C-u - [C-u ...] M-i + ((consp arg) (* (truncate (log (abs (car arg)) 4)) ;; ([-]64) -> 3 + (/ (car arg) (abs (car arg))))) ;; Restore sign + (t 1))) ;; C-u - M-i (Is there anything else is left?) + (insert-before (or (< number-of-subs 0) ;; C-u - N M-i + (eq arg '-) ;; C-u - M-i + (consp arg))) ;; C-u [C-u ...] M-i + ;; Ensure number-of-subs is positive, now that we figured out `insert-before' + (number-of-subs (abs number-of-subs))) + (subed-debug "Inserting %s subtitle(s) %s the current" number-of-subs (if insert-before "before" "after")) + (subed-srt-move-to-subtitle-id) + ;; Move to the ID of the subtitle we're prepending subtitles to so that we + ;; can do (insert "<new subtitle>") + (if insert-before + (subed-srt-move-to-subtitle-id) + (when (and (not (subed-srt-forward-subtitle-id)) ;; Appending after last subtitle + (> (buffer-size) 0)) ;; Buffer is not empty + ;; There is no ID because we're appending to the last subtitle. We just + ;; have to make sure there is a subtitle delimiter ("\n\n") after the + ;; last subtitle and point is where the new ID will go. + (subed-srt-move-to-subtitle-end) + (forward-line) + (insert "\n"))) + ;; Insert subtitles + (save-excursion + ;; Find out how much time we have per subtitle + (let* + ;; nil when there's no previous subtitle + ((prev-stop-msecs (save-excursion (when (subed-srt-backward-subtitle-id) + (subed-srt--subtitle-msecs-stop)))) + ;; nil when there's no next subtitle + (next-start-msecs (when (looking-at "^[0-9]$") + (subed-srt--subtitle-msecs-start))) + ;; nil when there's no next subtitle + (available-msecs (when next-start-msecs + (- next-start-msecs (or prev-stop-msecs 0)))) + ;; Calculate milliseconds per inserted subtitle or use default value + ;; if we're appending to the last subtitle + (sub-msecs (if available-msecs (/ available-msecs number-of-subs) + (* subed-default-subtitle-length 1000)))) + (dotimes (i number-of-subs) + (let* ((start-msecs (+ (or prev-stop-msecs 0) (* sub-msecs i))) + (stop-msecs (+ start-msecs sub-msecs)) + ;; Apply `subed-subtitle-spacing' + (start-msecs-spaced (if (= i 0) + (+ start-msecs subed-subtitle-spacing) + (+ start-msecs (/ subed-subtitle-spacing 2)))) + (stop-msecs-spaced (if (= i (1- number-of-subs)) + (- stop-msecs subed-subtitle-spacing) + (- stop-msecs (/ subed-subtitle-spacing 2))))) + (insert (format "0\n%s --> %s\n\n\n" + (subed-srt--msecs-to-timestamp start-msecs-spaced) + (subed-srt--msecs-to-timestamp stop-msecs-spaced)))))) + ;; If we're not on an ID, that means we added one or more subtitles after + ;; the last one and we can remove the trailing extra newline + (when (looking-at "^[[:space:]]*$") + (forward-line -1) + (kill-whole-line))) + (subed-srt--regenerate-ids) + (subed-srt-move-to-subtitle-text))) ;; TODO: Implement support for prefix argument to ;; kill n subtitles with C-u n M-k. diff --git a/tests/test-subed-srt.el b/tests/test-subed-srt.el index 2e26458..94a7d3b 100644 --- a/tests/test-subed-srt.el +++ b/tests/test-subed-srt.el @@ -378,6 +378,223 @@ Baz. "Baz.\n")))) ) +(describe "Inserting" + (describe "in an empty buffer," + (describe "appending" + (it "a single subtile." + (cl-loop for arg in (list nil 1) do + (with-temp-buffer + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:00:00,100 --> 00:00:00,900\n\n")) + (expect (point) :to-equal 33)))) + (it "multiple subtiles." + (cl-loop for arg in (list 2) do + (with-temp-buffer + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:00:00,100 --> 00:00:00,950\n\n\n" + "2\n" + "00:00:01,050 --> 00:00:01,900\n\n")) + (expect (point) :to-equal 33))))) + (describe "prepending" + (it "a single subtile." + (cl-loop for arg in (list '- -1 (list 4)) do + (with-temp-buffer + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:00:00,100 --> 00:00:00,900\n\n")) + (expect (point) :to-equal 33)))) + (it "multiple subtiles." + (cl-loop for arg in (list -2 (list -16)) do + (with-temp-buffer + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:00:00,100 --> 00:00:00,950\n\n\n" + "2\n" + "00:00:01,050 --> 00:00:01,900\n\n")) + (expect (point) :to-equal 33))))) + ) + + (describe "in a non-empty buffer" + (describe "before the current subtitle" + (it "a single subtitle." + (cl-loop for arg in (list '- -1 (list 4)) do + (with-temp-buffer + (insert mock-srt-data) + (subed-srt-move-to-subtitle-text 2) + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n\n" + "2\n" + "00:01:05,223 --> 00:02:02,134\n" + "\n\n" + "3\n" + "00:02:02,234 --> 00:02:10,345\n" + "Bar.\n\n" + "4\n" + "00:03:03,456 --> 00:03:15,567\n" + "Baz.\n")) + (expect (point) :to-equal 71)))) + (it "multiple subtitles." + (cl-loop for arg in (list -2 (list 16)) do + (with-temp-buffer + (insert mock-srt-data) + (subed-srt-move-to-subtitle-text 2) + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n\n" + "2\n" + "00:01:05,223 --> 00:01:33,628\n" + "\n\n" + "3\n" + "00:01:33,728 --> 00:02:02,133\n" + "\n\n" + "4\n" + "00:02:02,234 --> 00:02:10,345\n" + "Bar.\n\n" + "5\n" + "00:03:03,456 --> 00:03:15,567\n" + "Baz.\n")) + (expect (point) :to-equal 71)))) + ) + (describe "after the current subtitle" + (it "a single subtitle." + (cl-loop for arg in (list nil 1) do + (with-temp-buffer + (insert mock-srt-data) + (subed-srt-move-to-subtitle-text 1) + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n\n" + "2\n" + "00:01:05,223 --> 00:02:02,134\n" + "\n\n" + "3\n" + "00:02:02,234 --> 00:02:10,345\n" + "Bar.\n\n" + "4\n" + "00:03:03,456 --> 00:03:15,567\n" + "Baz.\n")) + (expect (point) :to-equal 71)))) + (it "multiple subtitles." + (cl-loop for arg in (list 2) do + (with-temp-buffer + (insert mock-srt-data) + (subed-srt-move-to-subtitle-text 1) + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n\n" + "2\n" + "00:01:05,223 --> 00:01:33,628\n" + "\n\n" + "3\n" + "00:01:33,728 --> 00:02:02,133\n" + "\n\n" + "4\n" + "00:02:02,234 --> 00:02:10,345\n" + "Bar.\n\n" + "5\n" + "00:03:03,456 --> 00:03:15,567\n" + "Baz.\n")) + (expect (point) :to-equal 71)))) + ) + + + (describe "before the first subtitle" + (it "a single subtitle." + (cl-loop for arg in (list '- -1 (list 4)) do + (with-temp-buffer + (insert mock-srt-data) + (subed-srt-move-to-subtitle-text 1) + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:00:00,100 --> 00:01:00,900\n" + "\n\n" + "2\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n\n" + "3\n" + "00:02:02,234 --> 00:02:10,345\n" + "Bar.\n\n" + "4\n" + "00:03:03,456 --> 00:03:15,567\n" + "Baz.\n")) + (expect (point) :to-equal 33)))) + (it "multiple subtitles." + (cl-loop for arg in (list -2 (list 16)) do + (with-temp-buffer + (insert mock-srt-data) + (subed-srt-move-to-subtitle-text 1) + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:00:00,100 --> 00:00:30,450\n" + "\n\n" + "2\n" + "00:00:30,550 --> 00:01:00,900\n" + "\n\n" + "3\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n\n" + "4\n" + "00:02:02,234 --> 00:02:10,345\n" + "Bar.\n\n" + "5\n" + "00:03:03,456 --> 00:03:15,567\n" + "Baz.\n")) + (expect (point) :to-equal 33)))) + ) + + + (describe "after the last subtitle" + (it "a single subtitle." + (cl-loop for arg in (list nil 1) do + (with-temp-buffer + (insert mock-srt-data) + (subed-srt-move-to-subtitle-text 3) + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n\n" + "2\n" + "00:02:02,234 --> 00:02:10,345\n" + "Bar.\n\n" + "3\n" + "00:03:03,456 --> 00:03:15,567\n" + "Baz.\n\n" + "4\n" + "00:03:15,667 --> 00:03:16,467\n" + "\n")) + (expect (point) :to-equal 147)))) + (it "multiple subtitles." + (cl-loop for arg in (list 2) do + (with-temp-buffer + (insert mock-srt-data) + (subed-srt-move-to-subtitle-text 3) + (subed-srt-subtitle-insert arg) + (expect (buffer-string) :to-equal (concat "1\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n\n" + "2\n" + "00:02:02,234 --> 00:02:10,345\n" + "Bar.\n\n" + "3\n" + "00:03:03,456 --> 00:03:15,567\n" + "Baz.\n\n" + "4\n" + "00:03:15,667 --> 00:03:16,517\n" + "\n\n" + "5\n" + "00:03:16,617 --> 00:03:17,467\n" + "\n")) + (expect (point) :to-equal 147)))) + ) + ) + ) (describe "Sanitizing" (it "removes trailing tabs and spaces from all lines."