branch: elpa/subed commit 9bf3fcc4d51d48687b99151d318ce2ccb42c55e2 Author: Random User <rnd...@posteo.de> Commit: Random User <rnd...@posteo.de>
Make timestamp adjustment limitations generic The subtitle format implementation now has to only provide subed-*--set-subtitle-time-start/stop functions, which simply replace a timestamp with another one without checking anything. All the complexity is now in the generic functions subed-adjust-subtitle-time-start/stop to make adding support for other subtitle formats easier. --- subed/subed-srt.el | 112 ++++------------- subed/subed.el | 118 +++++++++++++++--- tests/test-subed-srt.el | 314 ++++++++---------------------------------------- tests/test-subed.el | 273 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 448 insertions(+), 369 deletions(-) diff --git a/subed/subed-srt.el b/subed/subed-srt.el index 7b9e77d..d26ea62 100644 --- a/subed/subed-srt.el +++ b/subed/subed-srt.el @@ -315,93 +315,31 @@ Return point or nil if there is no previous subtitle." ;;; Manipulation -(defun subed-srt--adjust-subtitle-start (msecs &optional - ignore-negative-duration - ignore-spacing) - "Add MSECS milliseconds to start time (use negative value to subtract). - -Unless IGNORE-NEGATIVE-DURATION is non-nil, reduce MSECS so that -the start time isn't larger than the stop time. Zero-length -subtiltes are always allowed. - -Unless IGNORE-SPACING is non-nil, if the adjustment would result -in gaps between subtitles being smaller than -`subed-subtitle-spacing', reduce MSECS so that this doesn't -happen. - -Return the number of milliseconds the start time was adjusted or -nil if nothing changed." - (subed-disable-sync-point-to-player-temporarily) - (let* ((msecs-start (subed-srt--subtitle-msecs-start)) - (msecs-new (when msecs-start (+ msecs-start msecs)))) - (when msecs-new - (if (> msecs 0) - ;; Adding to start time - (unless ignore-negative-duration - (let ((msecs-stop (subed-srt--subtitle-msecs-stop))) - (setq msecs-new (min msecs-new msecs-stop)))) - ;; Subtracting from start time - (unless ignore-spacing - (let* ((msecs-prev-stop (save-excursion (when (subed-srt--backward-subtitle-id) - (subed-srt--subtitle-msecs-stop)))) - (msecs-min (if msecs-prev-stop - (+ msecs-prev-stop subed-subtitle-spacing) 0))) - (when msecs-min - (setq msecs-new (max msecs-new msecs-min)))))) - ;; msecs-new must be bigger than the current start time if we are adding - ;; or smaller if we are subtracting. - (when (or (and (> msecs 0) (> msecs-new msecs-start)) ;; Adding - (and (< msecs 0) (< msecs-new msecs-start))) ;; Subtracting - (save-excursion - (subed-srt--jump-to-subtitle-time-start) - (when (looking-at subed-srt--regexp-timestamp) - (replace-match (subed-srt--msecs-to-timestamp msecs-new)) - (subed--run-subtitle-time-adjusted-hook) - (- msecs-new msecs-start))))))) - -(defun subed-srt--adjust-subtitle-stop (msecs &optional - ignore-negative-duration - ignore-spacing) - "Add MSECS milliseconds to stop time (use negative value to subtract). - -Unless IGNORE-NEGATIVE-DURATION is non-nil, increase MSECS so -that the stop time isn't smaller than the start time. -Zero-length subtiltes are always allowed. - -Unless IGNORE-SPACING is non-nil, if the adjustment would result -in gaps between subtitles being smaller than -`subed-subtitle-spacing', reduce MSECS so that this doesn't -happen. - -Return the number of milliseconds the stop time was adjusted or -nil if nothing changed." - (subed-disable-sync-point-to-player-temporarily) - (let* ((msecs-stop (subed-srt--subtitle-msecs-stop)) - (msecs-new (when msecs-stop (+ msecs-stop msecs)))) - (when msecs-new - (if (> msecs 0) - ;; Adding to stop time - (unless ignore-spacing - (let* ((msecs-next-start (save-excursion (when (subed-srt--forward-subtitle-id) - (subed-srt--subtitle-msecs-start)))) - (msecs-max (when msecs-next-start - (- msecs-next-start subed-subtitle-spacing)))) - (when msecs-max - (setq msecs-new (min msecs-new msecs-max))))) - ;; Subtracting from stop time - (unless ignore-negative-duration - (let ((msecs-start (subed-srt--subtitle-msecs-start))) - (setq msecs-new (max msecs-new msecs-start))))) - ;; msecs-new must be bigger than the current stop time if we are adding or - ;; smaller if we are subtracting. - (when (or (and (> msecs 0) (> msecs-new msecs-stop)) ;; Adding - (and (< msecs 0) (< msecs-new msecs-stop))) ;; Subtracting - (save-excursion - (subed-srt--jump-to-subtitle-time-stop) - (when (looking-at subed-srt--regexp-timestamp) - (replace-match (subed-srt--msecs-to-timestamp msecs-new)) - (subed--run-subtitle-time-adjusted-hook) - (- msecs-new msecs-stop))))))) +(defun subed-srt--set-subtitle-time-start (msecs &optional sub-id) + "Set subtitle start time to MSECS milliseconds. + +If SUB-ID is not given, set the start of the current subtitle. + +Return the new subtitle start time in milliseconds." + (save-excursion + (when (or (not sub-id) + (and sub-id (subed-srt--jump-to-subtitle-id sub-id))) + (subed-srt--jump-to-subtitle-time-start) + (when (looking-at subed-srt--regexp-timestamp) + (replace-match (subed-srt--msecs-to-timestamp msecs)))))) + +(defun subed-srt--set-subtitle-time-stop (msecs &optional sub-id) + "Set subtitle stop time to MSECS milliseconds. + +If SUB-ID is not given, set the stop of the current subtitle. + +Return the new subtitle stop time in milliseconds." + (save-excursion + (when (or (not sub-id) + (and sub-id (subed-srt--jump-to-subtitle-id sub-id))) + (subed-srt--jump-to-subtitle-time-stop) + (when (looking-at subed-srt--regexp-timestamp) + (replace-match (subed-srt--msecs-to-timestamp msecs)))))) (defun subed-srt--subtitle-insert (&optional arg) "Insert subtitle(s). diff --git a/subed/subed.el b/subed/subed.el index 7ae3630..61d6b71 100644 --- a/subed/subed.el +++ b/subed/subed.el @@ -43,8 +43,8 @@ (defalias 'subed-subtitle-msecs-stop #'subed-srt--subtitle-msecs-stop) (defalias 'subed-subtitle-text #'subed-srt--subtitle-text) (defalias 'subed-subtitle-relative-point #'subed-srt--subtitle-relative-point) -(defalias 'subed-adjust-subtitle-start #'subed-srt--adjust-subtitle-start) -(defalias 'subed-adjust-subtitle-stop #'subed-srt--adjust-subtitle-stop) +(defalias 'subed-set-subtitle-time-start #'subed-srt--set-subtitle-time-start) +(defalias 'subed-set-subtitle-time-stop #'subed-srt--set-subtitle-time-stop) (defalias 'subed-jump-to-subtitle-id #'subed-srt--jump-to-subtitle-id) (defalias 'subed-jump-to-subtitle-time-start #'subed-srt--jump-to-subtitle-time-start) @@ -190,6 +190,88 @@ Before BODY is run, point is placed on the subtitle's ID." ;;; Adjusting start/stop time individually +(defun subed-adjust-subtitle-time-start (msecs &optional + ignore-negative-duration + ignore-spacing) + "Add MSECS milliseconds to start time (use negative value to subtract). + +Unless IGNORE-NEGATIVE-DURATION is non-nil, reduce MSECS so that +the start time isn't larger than the stop time. Zero-length +subtiltes are always allowed. + +Unless IGNORE-SPACING is non-nil, if the adjustment would result +in gaps between subtitles being smaller than +`subed-subtitle-spacing', reduce MSECS so that this doesn't +happen. + +Return the number of milliseconds the start time was adjusted or +nil if nothing changed." + (subed-disable-sync-point-to-player-temporarily) + (let* ((msecs-start (subed-subtitle-msecs-start)) + (msecs-new (when msecs-start (+ msecs-start msecs)))) + (when msecs-new + (if (> msecs 0) + ;; Adding to start time + (unless ignore-negative-duration + (let ((msecs-stop (subed-subtitle-msecs-stop))) + (setq msecs-new (min msecs-new msecs-stop)))) + ;; Subtracting from start time + (unless ignore-spacing + (let* ((msecs-prev-stop (save-excursion (when (subed-backward-subtitle-id) + (subed-subtitle-msecs-stop)))) + (msecs-min (if msecs-prev-stop + (+ msecs-prev-stop subed-subtitle-spacing) 0))) + (when msecs-min + (setq msecs-new (max msecs-new msecs-min)))))) + ;; msecs-new must be bigger than the current start time if we are adding + ;; or smaller if we are subtracting. + (when (or (and (> msecs 0) (> msecs-new msecs-start)) ;; Adding + (and (< msecs 0) (< msecs-new msecs-start))) ;; Subtracting + (subed-set-subtitle-time-start msecs-new) + (subed--run-subtitle-time-adjusted-hook) + (- msecs-new msecs-start))))) + +(defun subed-adjust-subtitle-time-stop (msecs &optional + ignore-negative-duration + ignore-spacing) + "Add MSECS milliseconds to stop time (use negative value to subtract). + +Unless IGNORE-NEGATIVE-DURATION is non-nil, increase MSECS so +that the stop time isn't smaller than the start time. +Zero-length subtiltes are always allowed. + +Unless IGNORE-SPACING is non-nil, if the adjustment would result +in gaps between subtitles being smaller than +`subed-subtitle-spacing', reduce MSECS so that this doesn't +happen. + +Return the number of milliseconds the stop time was adjusted or +nil if nothing changed." + (subed-disable-sync-point-to-player-temporarily) + (let* ((msecs-stop (subed-subtitle-msecs-stop)) + (msecs-new (when msecs-stop (+ msecs-stop msecs)))) + (when msecs-new + (if (> msecs 0) + ;; Adding to stop time + (unless ignore-spacing + (let* ((msecs-next-start (save-excursion (when (subed-forward-subtitle-id) + (subed-subtitle-msecs-start)))) + (msecs-max (when msecs-next-start + (- msecs-next-start subed-subtitle-spacing)))) + (when msecs-max + (setq msecs-new (min msecs-new msecs-max))))) + ;; Subtracting from stop time + (unless ignore-negative-duration + (let ((msecs-start (subed-subtitle-msecs-start))) + (setq msecs-new (max msecs-new msecs-start))))) + ;; msecs-new must be bigger than the current stop time if we are adding or + ;; smaller if we are subtracting. + (when (or (and (> msecs 0) (> msecs-new msecs-stop)) ;; Adding + (and (< msecs 0) (< msecs-new msecs-stop))) ;; Subtracting + (subed-set-subtitle-time-stop msecs-new) + (subed--run-subtitle-time-adjusted-hook) + (- msecs-new msecs-stop))))) + (defun subed-increase-start-time (&optional arg) "Add `subed-milliseconds-adjust' milliseconds to start time. @@ -208,7 +290,7 @@ Example usage: \\[universal-argument] \\[subed-increase-start-time] Increase start time by 100ms (the default) \\[subed-increase-start-time] Increase start time by 100ms (the default) again" (interactive "P") - (subed-adjust-subtitle-start (subed-get-milliseconds-adjust arg))) + (subed-adjust-subtitle-time-start (subed-get-milliseconds-adjust arg))) (defun subed-decrease-start-time (&optional arg) "Subtract `subed-milliseconds-adjust' milliseconds from start time. @@ -217,7 +299,7 @@ Return new start time in milliseconds or nil if it didn't change. See `subed-increase-start-time' about ARG." (interactive "P") - (subed-adjust-subtitle-start (* -1 (subed-get-milliseconds-adjust arg)))) + (subed-adjust-subtitle-time-start (* -1 (subed-get-milliseconds-adjust arg)))) (defun subed-increase-stop-time (&optional arg) "Add `subed-milliseconds-adjust' milliseconds to stop time. @@ -226,7 +308,7 @@ Return new stop time in milliseconds or nil if it didn't change. See `subed-increase-start-time' about ARG." (interactive "P") - (subed-adjust-subtitle-stop (subed-get-milliseconds-adjust arg))) + (subed-adjust-subtitle-time-stop (subed-get-milliseconds-adjust arg))) (defun subed-decrease-stop-time (&optional arg) "Subtract `subed-milliseconds-adjust' milliseconds from stop time. @@ -235,7 +317,7 @@ Return new stop time in milliseconds or nil if it didn't change. See `subed-increase-start-time' about ARG." (interactive "P") - (subed-adjust-subtitle-stop (* -1 (subed-get-milliseconds-adjust arg)))) + (subed-adjust-subtitle-time-stop (* -1 (subed-get-milliseconds-adjust arg)))) (defun subed-copy-player-pos-to-start-time () "Replace current subtitle's start timestamp with mpv player's current timestamp." @@ -272,19 +354,19 @@ When moving subtitles backward (MSECS < 0), it's the same thing but we move the start time first." (if (> msecs 0) (lambda (msecs &optional ignore-limits) - (let ((msecs (subed-adjust-subtitle-stop msecs - :ignore-negative-duration - ignore-limits))) - (when msecs (subed-adjust-subtitle-start msecs - :ignore-negative-duration - ignore-limits)))) + (let ((msecs (subed-adjust-subtitle-time-stop msecs + :ignore-negative-duration + ignore-limits))) + (when msecs (subed-adjust-subtitle-time-start msecs + :ignore-negative-duration + ignore-limits)))) (lambda (msecs &optional ignore-limits) - (let ((msecs (subed-adjust-subtitle-start msecs - :ignore-negative-duration - ignore-limits))) - (when msecs (subed-adjust-subtitle-stop msecs - :ignore-negative-duration - ignore-limits)))))) + (let ((msecs (subed-adjust-subtitle-time-start msecs + :ignore-negative-duration + ignore-limits))) + (when msecs (subed-adjust-subtitle-time-stop msecs + :ignore-negative-duration + ignore-limits)))))) (defun subed--move-current-subtitle (msecs) "Move subtitle on point by MSECS milliseconds." diff --git a/tests/test-subed-srt.el b/tests/test-subed-srt.el index 168cd85..b19df2d 100644 --- a/tests/test-subed-srt.el +++ b/tests/test-subed-srt.el @@ -105,6 +105,14 @@ Baz. (subed-srt--jump-to-subtitle-text 2) (expect (subed-srt--subtitle-msecs-start) :to-equal (+ (* 2 60000) (* 2 1000) 234)) (expect (subed-srt--subtitle-msecs-stop) :to-equal (+ (* 2 60000) (* 10 1000) 345)))) + (it "handles lack of digits in milliseconds gracefully." + (with-temp-buffer + (insert mock-srt-data) + (subed-jump-to-subtitle-id 3) + (expect (save-excursion (subed-jump-to-subtitle-time-start) + (thing-at-point 'line)) :to-equal "00:03:03,45 --> 00:03:15,5\n") + (expect (subed-srt--subtitle-msecs-start) :to-equal (+ (* 3 60 1000) (* 3 1000) 450)) + (expect (subed-srt--subtitle-msecs-stop) :to-equal (+ (* 3 60 1000) (* 15 1000) 500)))) (it "returns nil if time can't be found." (with-temp-buffer (expect (subed-srt--subtitle-msecs-start) :to-be nil) @@ -604,285 +612,67 @@ Baz. ) ) -(describe "Adjusting subtitle start/stop time" - :var (subed-subtitle-time-adjusted-hook) - (it "runs the appropriate hook." - (let ((foo (setf (symbol-function 'foo) (lambda (msecs) ())))) - (spy-on 'foo) - (add-hook 'subed-subtitle-time-adjusted-hook 'foo) - (with-temp-buffer - (insert mock-srt-data) - (subed-srt--jump-to-subtitle-id 1) - (subed-srt--adjust-subtitle-start 100) - (expect 'foo :to-have-been-called-with 61100) - (expect 'foo :to-have-been-called-times 1) - (subed-srt--adjust-subtitle-stop 123) - (expect 'foo :to-have-been-called-with 61100) - (expect 'foo :to-have-been-called-times 2) - (subed-srt--jump-to-subtitle-id 2) - (subed-srt--adjust-subtitle-start 6) - (expect 'foo :to-have-been-called-with 122240) - (expect 'foo :to-have-been-called-times 3) - (subed-srt--adjust-subtitle-stop 123) - (expect 'foo :to-have-been-called-with 122240) - (expect 'foo :to-have-been-called-times 4)) - (remove-hook 'subed-subtitle-time-adjusted-hook 'foo))) - (it "adjusts the start/stop time." - (with-temp-buffer - (insert mock-srt-data) - (subed-srt--jump-to-subtitle-id 1) - (expect (subed-srt--adjust-subtitle-start 100) :to-equal 100) - (expect (save-excursion (subed-srt--jump-to-subtitle-time-start) - (thing-at-point 'line)) :to-equal "00:01:01,100 --> 00:01:05,123\n") - (expect (subed-srt--adjust-subtitle-start -200) :to-equal -200) - (expect (save-excursion (subed-srt--jump-to-subtitle-time-start) - (thing-at-point 'line)) :to-equal "00:01:00,900 --> 00:01:05,123\n") - (expect (subed-srt--adjust-subtitle-stop 200) :to-equal 200) - (expect (save-excursion (subed-srt--jump-to-subtitle-time-start) - (thing-at-point 'line)) :to-equal "00:01:00,900 --> 00:01:05,323\n") - (expect (subed-srt--adjust-subtitle-stop -100) :to-equal -100) - (expect (save-excursion (subed-srt--jump-to-subtitle-time-start) - (thing-at-point 'line)) :to-equal "00:01:00,900 --> 00:01:05,223\n"))) - (it "adjusts the start/stop time if milliseconds lack digits." - (with-temp-buffer - (insert mock-srt-data) - (subed-srt--jump-to-subtitle-id 3) - (expect (subed-srt--adjust-subtitle-start 200) :to-equal 200) - (expect (subed-srt--adjust-subtitle-start -100) :to-equal -100) - (expect (save-excursion (subed-srt--jump-to-subtitle-time-start) - (thing-at-point 'line)) :to-equal "00:03:03,550 --> 00:03:15,5\n") - (expect (subed-srt--adjust-subtitle-stop 100) :to-equal 100) - (expect (subed-srt--adjust-subtitle-stop -200) :to-equal -200) - (expect (save-excursion (subed-srt--jump-to-subtitle-time-stop) - (thing-at-point 'line)) :to-equal "00:03:03,550 --> 00:03:15,400\n"))) - (describe "enforces limits" - (describe "when decreasing start time" - (it "of the first subtitle." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n")) - (expect (subed-srt--adjust-subtitle-start -999) :to-be -999) - (expect (subed-srt--subtitle-msecs-start) :to-be 1) - (expect (subed-srt--adjust-subtitle-start -1) :to-be -1) - (expect (subed-srt--subtitle-msecs-start) :to-be 0) - (expect (subed-srt--adjust-subtitle-start -1) :to-be nil) - (expect (subed-srt--subtitle-msecs-start) :to-be 0))) - (it "of a non-first subtitle." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n" - "2\n" - "00:00:03,000 --> 00:00:04,000\n" - "Bar.\n\n")) - (subed-srt--jump-to-subtitle-id 2) - (expect (subed-srt--adjust-subtitle-start -899) :to-be -899) - (expect (subed-srt--subtitle-msecs-start) :to-be 2101) - (expect (subed-srt--adjust-subtitle-start -1) :to-be -1) - (expect (subed-srt--subtitle-msecs-start) :to-be 2100) - (expect (subed-srt--adjust-subtitle-start -1) :to-be nil) - (expect (subed-srt--subtitle-msecs-start) :to-be 2100))) - ) - (it "when increasing start time." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n" - "2\n" - "00:00:03,000 --> 00:00:04,000\n" - "Bar.\n\n")) - (insert mock-srt-data) - (subed-srt--jump-to-subtitle-id 2) - (expect (subed-srt--adjust-subtitle-start 999) :to-be 999) - (expect (subed-srt--subtitle-msecs-start) :to-be 3999) - (expect (subed-srt--adjust-subtitle-start 1) :to-be 1) - (expect (subed-srt--subtitle-msecs-start) :to-be 4000) - (expect (subed-srt--adjust-subtitle-start 1) :to-be nil) - (expect (subed-srt--subtitle-msecs-start) :to-be 4000))) - (it "when decreasing stop time." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n" - "2\n" - "00:00:03,000 --> 00:00:04,000\n" - "Bar.\n\n")) - (subed-srt--jump-to-subtitle-id 2) - (expect (subed-srt--adjust-subtitle-stop -999) :to-be -999) - (expect (subed-srt--subtitle-msecs-stop) :to-be 3001) - (expect (subed-srt--adjust-subtitle-stop -1) :to-be -1) - (expect (subed-srt--subtitle-msecs-stop) :to-be 3000) - (expect (subed-srt--adjust-subtitle-stop -1) :to-be nil) - (expect (subed-srt--subtitle-msecs-stop) :to-be 3000))) - (describe "when increasing stop time" - (it "of the last subtitle." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n" - "2\n" - "00:00:03,000 --> 00:00:04,000\n" - "Bar.\n\n")) - (subed-srt--jump-to-subtitle-id 3) - (expect (subed-srt--adjust-subtitle-stop 1000000):to-be 1000000) - (expect (subed-srt--subtitle-msecs-stop) :to-be 1004000))) - (it "of a non-last subtitle." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n" - "2\n" - "00:00:03,000 --> 00:00:04,000\n" - "Bar.\n\n")) - (subed-srt--jump-to-subtitle-id 1) - (expect (subed-srt--adjust-subtitle-stop 899) :to-be 899) - (expect (subed-srt--subtitle-msecs-stop) :to-be 2899) - (expect (subed-srt--adjust-subtitle-stop 1) :to-be 1) - (expect (subed-srt--subtitle-msecs-stop) :to-be 2900) - (expect (subed-srt--adjust-subtitle-stop 1) :to-be nil) - (expect (subed-srt--subtitle-msecs-stop) :to-be 2900))) - ) - (it "without undershooting the target time." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n" - "2\n" - "00:00:02,000 --> 00:00:03,000\n" - "Bar.\n")) - (subed-jump-to-subtitle-id 1) - (expect (subed-srt--adjust-subtitle-stop 1) :to-be nil) - (expect (subed-subtitle-msecs-stop) :to-equal 2000))) - (it "without overshooting the target time." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n" - "2\n" - "00:00:02,000 --> 00:00:03,000\n" - "Bar.\n")) - (subed-jump-to-subtitle-id 2) - (expect (subed-srt--adjust-subtitle-start -1) :to-be nil) - (expect (subed-subtitle-msecs-start) :to-equal 2000))) - ) - (describe "ignores negative duration if the first argument is truthy" - (it "when adjusting start time." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n")) - (expect (subed-srt--adjust-subtitle-start 2000 t) :to-be 2000) - (expect (subed-srt--subtitle-msecs-start) :to-be 3000) - (expect (subed-srt--subtitle-msecs-stop) :to-be 2000) - (expect (subed-srt--adjust-subtitle-start -500 t) :to-be -500) - (expect (subed-srt--subtitle-msecs-start) :to-be 2500) - (expect (subed-srt--subtitle-msecs-stop) :to-be 2000))) - (it "when adjusting stop time." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n")) - (expect (subed-srt--adjust-subtitle-stop -1500 t) :to-be -1500) - (expect (subed-srt--subtitle-msecs-stop) :to-be 500) - (expect (subed-srt--subtitle-msecs-start) :to-be 1000) - (expect (subed-srt--adjust-subtitle-stop 200 t) :to-be 200) - (expect (subed-srt--subtitle-msecs-stop) :to-be 700) - (expect (subed-srt--subtitle-msecs-start) :to-be 1000))) - ) - (describe "ignores subtitle spacing if the second argument is truthy" - (it "when adjusting start time." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n" - "2\n" - "00:00:02,200 --> 00:00:03,000\n" - "Bar.\n")) - (subed-srt--jump-to-subtitle-id 2) - (expect (subed-srt--adjust-subtitle-start -150 nil t) :to-be -150) - (expect (subed-srt--subtitle-msecs-start 2) :to-be 2050) - (expect (subed-srt--subtitle-msecs-stop 1) :to-be 2000) - (expect (subed-srt--adjust-subtitle-start -51 nil t) :to-be -51) - (expect (subed-srt--subtitle-msecs-start 2) :to-be 1999) - (expect (subed-srt--subtitle-msecs-stop 1) :to-be 2000))) - (it "when adjusting stop time." - (with-temp-buffer - (insert (concat "1\n" - "00:00:01,000 --> 00:00:02,000\n" - "Foo.\n\n" - "2\n" - "00:00:02,200 --> 00:00:03,000\n" - "Bar.\n")) - (subed-srt--jump-to-subtitle-id 1) - (expect (subed-srt--adjust-subtitle-stop 150 nil t) :to-be 150) - (expect (subed-srt--subtitle-msecs-stop 1) :to-be 2150) - (expect (subed-srt--subtitle-msecs-start 2) :to-be 2200) - (expect (subed-srt--adjust-subtitle-stop 51 nil t) :to-be 51) - (expect (subed-srt--subtitle-msecs-stop 1) :to-be 2201) - (expect (subed-srt--subtitle-msecs-start 2) :to-be 2200))) - ) - (it "does nothing if no timestamp can be found." - (with-temp-buffer - (insert "foo") - (goto-char (point-min)) - (expect (subed-srt--adjust-subtitle-start 123) :to-be nil) - (expect (buffer-string) :to-equal "foo") - (expect (subed-srt--adjust-subtitle-start -123) :to-be nil) - (expect (buffer-string) :to-equal "foo"))) - ) - -(describe "Killing a subtitle" - (it "removes it when it is the first one." +(describe "Setting start/stop time" + (it "of current subtitle." (with-temp-buffer (insert mock-srt-data) - (subed-srt--jump-to-subtitle-text 1) - (subed-srt--subtitle-kill) - (expect (buffer-string) :to-equal (concat "2\n" - "00:02:02,234 --> 00:02:10,345\n" - "Bar.\n" - "\n" + (subed-srt--jump-to-subtitle-end 2) + (subed-srt--set-subtitle-time-start (+ (* 1 60 60 1000) (* 2 60 1000) (* 3 1000) 400)) + (expect (buffer-string) :to-equal (concat "1\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n\n" + "2\n" + "01:02:03,400 --> 00:02:10,345\n" + "Bar.\n\n" "3\n" "00:03:03,45 --> 00:03:15,5\n" - "Baz.\n")))) - (it "removes it when it is in the middle." - (with-temp-buffer - (insert mock-srt-data) - (subed-srt--jump-to-subtitle-text 2) - (subed-srt--subtitle-kill) + "Baz.\n")) + (subed-srt--set-subtitle-time-stop (+ (* 5 60 60 1000) (* 6 60 1000) (* 7 1000) 800)) (expect (buffer-string) :to-equal (concat "1\n" "00:01:01,000 --> 00:01:05,123\n" - "Foo.\n" - "\n" + "Foo.\n\n" + "2\n" + "01:02:03,400 --> 05:06:07,800\n" + "Bar.\n\n" "3\n" "00:03:03,45 --> 00:03:15,5\n" "Baz.\n")))) - (it "removes it when it is the last one." + (it "of specific subtitle." (with-temp-buffer (insert mock-srt-data) - (subed-srt--jump-to-subtitle-text 3) - (subed-srt--subtitle-kill) + (subed-srt--jump-to-subtitle-time-stop 3) + (subed-srt--set-subtitle-time-start (+ (* 2 60 60 1000) (* 4 60 1000) (* 6 1000) 800) 1) (expect (buffer-string) :to-equal (concat "1\n" - "00:01:01,000 --> 00:01:05,123\n" - "Foo.\n" - "\n" + "02:04:06,800 --> 00:01:05,123\n" + "Foo.\n\n" "2\n" "00:02:02,234 --> 00:02:10,345\n" - "Bar.\n")))) - (it "removes the previous subtitle when point is right above an ID." - (with-temp-buffer - (insert mock-srt-data) - (subed-srt--jump-to-subtitle-id 3) - (backward-char) - (expect (looking-at "^\n3\n") :to-be t) - (subed-srt--subtitle-kill) - (expect (buffer-string) :to-equal (concat "1\n" - "00:01:01,000 --> 00:01:05,123\n" - "Foo.\n" - "\n" + "Bar.\n\n" "3\n" "00:03:03,45 --> 00:03:15,5\n" + "Baz.\n")) + (subed-srt--jump-to-subtitle-text 1) + (subed-srt--set-subtitle-time-stop (+ (* 3 60 60 1000) (* 5 60 1000) (* 7 1000) 900) 3) + (expect (buffer-string) :to-equal (concat "1\n" + "02:04:06,800 --> 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,45 --> 03:05:07,900\n" "Baz.\n")))) + (it "when milliseconds lack digits." + (with-temp-buffer + (insert mock-srt-data) + (subed-jump-to-subtitle-id 3) + (subed-srt--set-subtitle-time-start (+ (* 1 60 60 1000) (* 2 60 1000) (* 3 1000) 4) 3) + (expect (save-excursion (subed-jump-to-subtitle-time-start) + (thing-at-point 'line)) :to-equal "01:02:03,004 --> 00:03:15,5\n") + (subed-srt--set-subtitle-time-stop (+ (* 2 60 60 1000) (* 3 60 1000) (* 4 1000) 60) 3) + (expect (save-excursion (subed-jump-to-subtitle-time-start) + (thing-at-point 'line)) :to-equal "01:02:03,004 --> 02:03:04,060\n"))) + ) (describe "Inserting" diff --git a/tests/test-subed.el b/tests/test-subed.el index 40b37a0..e5ca350 100644 --- a/tests/test-subed.el +++ b/tests/test-subed.el @@ -137,6 +137,275 @@ ) ) +(describe "Adjusting subtitle start/stop time" + :var (subed-subtitle-time-adjusted-hook) + (it "runs the appropriate hook." + (let ((foo (setf (symbol-function 'foo) (lambda (msecs) ())))) + (spy-on 'foo) + (add-hook 'subed-subtitle-time-adjusted-hook 'foo) + (with-temp-buffer + (insert mock-srt-data) + (subed-jump-to-subtitle-id 1) + (expect (subed-adjust-subtitle-time-start 100) :to-equal 100) + (expect 'foo :to-have-been-called-with 61100) + (expect 'foo :to-have-been-called-times 1) + (expect (subed-adjust-subtitle-time-stop 123) :to-equal 123) + (expect 'foo :to-have-been-called-with 61100) + (expect 'foo :to-have-been-called-times 2) + (subed-jump-to-subtitle-id 2) + (expect (subed-adjust-subtitle-time-start 6) :to-equal 6) + (expect 'foo :to-have-been-called-with 122240) + (expect 'foo :to-have-been-called-times 3) + (expect (subed-adjust-subtitle-time-stop 123) :to-equal 123) + (expect 'foo :to-have-been-called-with 122240) + (expect 'foo :to-have-been-called-times 4)) + (remove-hook 'subed-subtitle-time-adjusted-hook 'foo))) + (it "adjusts the start/stop time." + (with-temp-buffer + (insert mock-srt-data) + (subed-jump-to-subtitle-id 1) + (expect (subed-adjust-subtitle-time-start 100) :to-equal 100) + (expect (save-excursion (subed-jump-to-subtitle-time-start) + (thing-at-point 'line)) :to-equal "00:01:01,100 --> 00:01:05,123\n") + (expect (subed-adjust-subtitle-time-start -200) :to-equal -200) + (expect (save-excursion (subed-jump-to-subtitle-time-start) + (thing-at-point 'line)) :to-equal "00:01:00,900 --> 00:01:05,123\n") + (expect (subed-adjust-subtitle-time-stop 200) :to-equal 200) + (expect (save-excursion (subed-jump-to-subtitle-time-start) + (thing-at-point 'line)) :to-equal "00:01:00,900 --> 00:01:05,323\n") + (expect (subed-adjust-subtitle-time-stop -100) :to-equal -100) + (expect (save-excursion (subed-jump-to-subtitle-time-start) + (thing-at-point 'line)) :to-equal "00:01:00,900 --> 00:01:05,223\n"))) + (describe "enforces limits" + (describe "when decreasing start time" + (it "of the first subtitle." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n")) + (expect (subed-adjust-subtitle-time-start -999) :to-be -999) + (expect (subed-subtitle-msecs-start) :to-be 1) + (expect (subed-adjust-subtitle-time-start -1) :to-be -1) + (expect (subed-subtitle-msecs-start) :to-be 0) + (expect (subed-adjust-subtitle-time-start -1) :to-be nil) + (expect (subed-subtitle-msecs-start) :to-be 0))) + (it "of a non-first subtitle." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n" + "2\n" + "00:00:03,000 --> 00:00:04,000\n" + "Bar.\n\n")) + (subed-jump-to-subtitle-id 2) + (expect (subed-adjust-subtitle-time-start -899) :to-be -899) + (expect (subed-subtitle-msecs-start) :to-be 2101) + (expect (subed-adjust-subtitle-time-start -1) :to-be -1) + (expect (subed-subtitle-msecs-start) :to-be 2100) + (expect (subed-adjust-subtitle-time-start -1) :to-be nil) + (expect (subed-subtitle-msecs-start) :to-be 2100))) + ) + (it "when increasing start time." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n" + "2\n" + "00:00:03,000 --> 00:00:04,000\n" + "Bar.\n\n")) + (insert mock-srt-data) + (subed-jump-to-subtitle-id 2) + (expect (subed-adjust-subtitle-time-start 999) :to-be 999) + (expect (subed-subtitle-msecs-start) :to-be 3999) + (expect (subed-adjust-subtitle-time-start 1) :to-be 1) + (expect (subed-subtitle-msecs-start) :to-be 4000) + (expect (subed-adjust-subtitle-time-start 1) :to-be nil) + (expect (subed-subtitle-msecs-start) :to-be 4000))) + (it "when decreasing stop time." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n" + "2\n" + "00:00:03,000 --> 00:00:04,000\n" + "Bar.\n\n")) + (subed-jump-to-subtitle-id 2) + (expect (subed-adjust-subtitle-time-stop -999) :to-be -999) + (expect (subed-subtitle-msecs-stop) :to-be 3001) + (expect (subed-adjust-subtitle-time-stop -1) :to-be -1) + (expect (subed-subtitle-msecs-stop) :to-be 3000) + (expect (subed-adjust-subtitle-time-stop -1) :to-be nil) + (expect (subed-subtitle-msecs-stop) :to-be 3000))) + (describe "when increasing stop time" + (it "of the last subtitle." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n" + "2\n" + "00:00:03,000 --> 00:00:04,000\n" + "Bar.\n\n")) + (subed-jump-to-subtitle-id 3) + (expect (subed-adjust-subtitle-time-stop 1000000):to-be 1000000) + (expect (subed-subtitle-msecs-stop) :to-be 1004000))) + (it "of a non-last subtitle." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n" + "2\n" + "00:00:03,000 --> 00:00:04,000\n" + "Bar.\n\n")) + (subed-jump-to-subtitle-id 1) + (expect (subed-adjust-subtitle-time-stop 899) :to-be 899) + (expect (subed-subtitle-msecs-stop) :to-be 2899) + (expect (subed-adjust-subtitle-time-stop 1) :to-be 1) + (expect (subed-subtitle-msecs-stop) :to-be 2900) + (expect (subed-adjust-subtitle-time-stop 1) :to-be nil) + (expect (subed-subtitle-msecs-stop) :to-be 2900))) + ) + (it "without undershooting the target time." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n" + "2\n" + "00:00:02,000 --> 00:00:03,000\n" + "Bar.\n")) + (subed-jump-to-subtitle-id 1) + (expect (subed-adjust-subtitle-time-stop 1) :to-be nil) + (expect (subed-subtitle-msecs-stop) :to-equal 2000))) + (it "without overshooting the target time." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n" + "2\n" + "00:00:02,000 --> 00:00:03,000\n" + "Bar.\n")) + (subed-jump-to-subtitle-id 2) + (expect (subed-adjust-subtitle-time-start -1) :to-be nil) + (expect (subed-subtitle-msecs-start) :to-equal 2000))) + ) + (describe "ignores negative duration if the first argument is truthy" + (it "when adjusting start time." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n")) + (expect (subed-adjust-subtitle-time-start 2000 t) :to-be 2000) + (expect (subed-subtitle-msecs-start) :to-be 3000) + (expect (subed-subtitle-msecs-stop) :to-be 2000) + (expect (subed-adjust-subtitle-time-start -500 t) :to-be -500) + (expect (subed-subtitle-msecs-start) :to-be 2500) + (expect (subed-subtitle-msecs-stop) :to-be 2000))) + (it "when adjusting stop time." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n")) + (expect (subed-adjust-subtitle-time-stop -1500 t) :to-be -1500) + (expect (subed-subtitle-msecs-stop) :to-be 500) + (expect (subed-subtitle-msecs-start) :to-be 1000) + (expect (subed-adjust-subtitle-time-stop 200 t) :to-be 200) + (expect (subed-subtitle-msecs-stop) :to-be 700) + (expect (subed-subtitle-msecs-start) :to-be 1000))) + ) + (describe "ignores subtitle spacing if the second argument is truthy" + (it "when adjusting start time." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n" + "2\n" + "00:00:02,200 --> 00:00:03,000\n" + "Bar.\n")) + (subed-jump-to-subtitle-id 2) + (expect (subed-adjust-subtitle-time-start -150 nil t) :to-be -150) + (expect (subed-subtitle-msecs-start 2) :to-be 2050) + (expect (subed-subtitle-msecs-stop 1) :to-be 2000) + (expect (subed-adjust-subtitle-time-start -51 nil t) :to-be -51) + (expect (subed-subtitle-msecs-start 2) :to-be 1999) + (expect (subed-subtitle-msecs-stop 1) :to-be 2000))) + (it "when adjusting stop time." + (with-temp-buffer + (insert (concat "1\n" + "00:00:01,000 --> 00:00:02,000\n" + "Foo.\n\n" + "2\n" + "00:00:02,200 --> 00:00:03,000\n" + "Bar.\n")) + (subed-jump-to-subtitle-id 1) + (expect (subed-adjust-subtitle-time-stop 150 nil t) :to-be 150) + (expect (subed-subtitle-msecs-stop 1) :to-be 2150) + (expect (subed-subtitle-msecs-start 2) :to-be 2200) + (expect (subed-adjust-subtitle-time-stop 51 nil t) :to-be 51) + (expect (subed-subtitle-msecs-stop 1) :to-be 2201) + (expect (subed-subtitle-msecs-start 2) :to-be 2200))) + ) + (it "does nothing if no timestamp can be found." + (with-temp-buffer + (insert "foo") + (goto-char (point-min)) + (expect (subed-adjust-subtitle-time-start 123) :to-be nil) + (expect (buffer-string) :to-equal "foo") + (expect (subed-adjust-subtitle-time-start -123) :to-be nil) + (expect (buffer-string) :to-equal "foo"))) + ) + +(describe "Killing a subtitle" + (it "removes it when it is the first one." + (with-temp-buffer + (insert mock-srt-data) + (subed-srt--jump-to-subtitle-text 1) + (subed-srt--subtitle-kill) + (expect (buffer-string) :to-equal (concat "2\n" + "00:02:02,234 --> 00:02:10,345\n" + "Bar.\n" + "\n" + "3\n" + "00:03:03,45 --> 00:03:15,5\n" + "Baz.\n")))) + (it "removes it when it is in the middle." + (with-temp-buffer + (insert mock-srt-data) + (subed-srt--jump-to-subtitle-text 2) + (subed-srt--subtitle-kill) + (expect (buffer-string) :to-equal (concat "1\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n" + "\n" + "3\n" + "00:03:03,45 --> 00:03:15,5\n" + "Baz.\n")))) + (it "removes it when it is the last one." + (with-temp-buffer + (insert mock-srt-data) + (subed-srt--jump-to-subtitle-text 3) + (subed-srt--subtitle-kill) + (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")))) + (it "removes the previous subtitle when point is right above an ID." + (with-temp-buffer + (insert mock-srt-data) + (subed-jump-to-subtitle-id 3) + (backward-char) + (expect (looking-at "^\n3\n") :to-be t) + (subed-srt--subtitle-kill) + (expect (buffer-string) :to-equal (concat "1\n" + "00:01:01,000 --> 00:01:05,123\n" + "Foo.\n" + "\n" + "3\n" + "00:03:03,45 --> 00:03:15,5\n" + "Baz.\n")))) + ) + (describe "Moving" (it "adjusts start and stop time by the same amount." (with-temp-buffer @@ -539,7 +808,7 @@ (subed-enable-replay-adjusted-subtitle :quiet) (spy-on 'subed-enable-replay-adjusted-subtitle :and-call-through) (spy-on 'subed-disable-replay-adjusted-subtitle :and-call-through) - (spy-on 'subed-adjust-subtitle-start :and-call-fake + (spy-on 'subed-adjust-subtitle-time-start :and-call-fake (lambda (msecs &optional a b) (expect (subed-replay-adjusted-subtitle-p) :to-be nil))) (spy-on 'subed-adjust-subtitle-stop :and-call-fake (lambda (msecs &optional a b) (expect (subed-replay-adjusted-subtitle-p) :to-be nil))) @@ -555,7 +824,7 @@ (subed-disable-replay-adjusted-subtitle :quiet) (spy-on 'subed-enable-replay-adjusted-subtitle :and-call-through) (spy-on 'subed-disable-replay-adjusted-subtitle :and-call-through) - (spy-on 'subed-adjust-subtitle-start :and-call-fake + (spy-on 'subed-adjust-subtitle-time-start :and-call-fake (lambda (msecs &optional a b) (expect (subed-replay-adjusted-subtitle-p) :to-be nil))) (spy-on 'subed-adjust-subtitle-stop :and-call-fake (lambda (msecs &optional a b) (expect (subed-replay-adjusted-subtitle-p) :to-be nil)))