diff --git a/subed/subed-common.el b/subed/subed-common.el index e53d98e..97edf7a 100644 --- a/subed/subed-common.el +++ b/subed/subed-common.el @@ -1294,5 +1294,143 @@ be run in `after-change-functions'." 'after-string (propertize (format " %.1f CPS" cps) 'face 'shadow 'display '(height 0.9))))))) +;;; handle overlapping subtitle timecodes + +;; NB: runs in a hook, so this version cannot send prefix arg to +;; subed-sanitize-overlaps +(defun subed-check-overlaps () + "Test all subtitles for overlapping timecodes. + +Creates a list of the ids of overlapping subtitles, moves point +to the to the end time of the first one, and prompts to trim +them. Designed to be run as a subed-mode-hook." + (interactive) + (let ((overlap-ids ())) + (save-excursion + (goto-char (point-min)) + (while (save-excursion (subed-forward-subtitle-time-start)) + (let ((next-sub-start-time (save-excursion + (subed-forward-subtitle-time-start) + (subed-subtitle-msecs-start)))) + (if (>= (subed-subtitle-msecs-stop) next-sub-start-time) + (progn (subed-jump-to-subtitle-id) + (push (string-to-number (word-at-point)) + overlap-ids)))) + (subed-forward-subtitle-time-start))) + (when overlap-ids + (setq overlap-ids (nreverse overlap-ids)) + (subed-jump-to-subtitle-id (car overlap-ids)) + (subed-jump-to-subtitle-time-stop) + (when (yes-or-no-p "Overlapping subtitles found. Trim them? ") + (subed-sanitize-overlaps))))) + +(defun subed-sanitize-overlaps (&optional arg) + "Adjust all overlapping times in current file. + +Uses either `subed-trim-overlap-start-times' or +`subed-trim-overlapping-end-times', the latter being the default. +See `subed-trim-overlapping-subtitle-trim-start' to customize +this option. + +With a non-numerical prefix ARG, or if +`subed-trim-overlapping-use-subed-subtitle-spacing' is t, make a gap the +between subtitles the length of `subed-subtitle-spacing'. With a numerical +prefix ARG, make the gap that many milliseconds." + (interactive "P") + (let ((cpsp (subed-show-cps-p))) + (when cpsp + (subed-disable-show-cps t)) + (switch-to-buffer (current-buffer)) + (save-excursion + (goto-char (point-min)) + (while (save-excursion (subed-forward-subtitle-time-start)) + (if subed-trim-overlapping-subtitle-trim-start + (subed-trim-overlap-start-time arg) + (subed-trim-overlap-end-time arg)) + (subed-forward-subtitle-time-start))) + (when cpsp + (subed-enable-show-cps t)))) + +(defun subed-trim-overlap-end-time (&optional arg) + "Check if end time of current subtitle is after start time of next. + +If so, trim the end time of current subtitle to 1 millisecond +less than the start time of the next one. + +With a non-numerical prefix ARG, or if +`subed-trim-overlapping-use-subed-subtitle-spacing' is t, make a +gap the between subtitles the length of `subed-subtitle-spacing'. +With a numerical prefix ARG, make the gap that many +milliseconds." + (interactive "P") + (let ((next-sub-start-time (save-excursion + (subed-forward-subtitle-time-start) + (subed-subtitle-msecs-start)))) + (if (>= (subed-subtitle-msecs-stop) next-sub-start-time) + (subed-set-subtitle-time-stop + (cond + ;; if numeric prefix arg, use it as gap in ms: + ((integerp arg) + (- next-sub-start-time arg)) + ;; if plain C-u or custom option set: + ((or (consp arg) + subed-trim-overlapping-use-subed-subtitle-spacing) + (- next-sub-start-time subed-subtitle-spacing)) + ;; else just make 1 ms difference: + (t + (1- next-sub-start-time))))))) + +(defun subed-trim-overlap-start-time (&optional arg) + "Check if end time of current subtitle is after start time of next. + +If so, trim the start time of current subtitle to 1 millisecond +less than the end time of the current one. + +With a non-numerical prefix ARG, or if +`subed-trim-overlapping-use-subed-subtitle-spacing' is t, make a +gap the between subtitles the length of `subed-subtitle-spacing'. +With a numerical prefix ARG make the gap that many miliseconds." + (interactive "P") + (let ((this-sub-stop-time (subed-subtitle-msecs-stop)) + (next-sub-start-time (save-excursion + (subed-forward-subtitle-time-start) + (subed-subtitle-msecs-start)))) + (if (>= this-sub-stop-time next-sub-start-time) + (save-excursion + (subed-forward-subtitle-time-start) + (subed-set-subtitle-time-start + (cond + ;; if numeric prefix arg, use it as gap in ms: + ((integerp arg) + (+ this-sub-stop-time arg)) + ;; if plain C-u or custom option set: + ((or (consp arg) + subed-trim-overlapping-use-subed-subtitle-spacing) + (+ this-sub-stop-time subed-subtitle-spacing)) + ;; else just make 1 ms difference: + (t + (1+ this-sub-stop-time)))))))) + +(defun subed-sort () + "Sanitize, then sort subtitles by start time and re-number them." + (interactive) + (atomic-change-group + (subed-sanitize) + (when subed-trim-overlapping-subtitle-times-on-save + (subed-sanitize-overlaps)) + (subed-validate) + (subed-save-excursion + (goto-char (point-min)) + (sort-subr nil + ;; nextrecfun (move to next record/subtitle or to end-of-buffer + ;; if there are no more records) + (lambda () (unless (subed-forward-subtitle-id) + (goto-char (point-max)))) + ;; endrecfun (move to end of current record/subtitle) + #'subed-jump-to-subtitle-end + ;; startkeyfun (return sort value of current record/subtitle) + #'subed-subtitle-msecs-start)) + (subed-regenerate-ids))) + (provide 'subed-common) ;;; subed-common.el ends here diff --git a/subed/subed-config.el b/subed/subed-config.el index de5d221..25b77dc 100644 --- a/subed/subed-config.el +++ b/subed/subed-config.el @@ -210,6 +210,38 @@ hardcoded." "Return base name of buffer file name or a default name." (file-name-nondirectory (or (buffer-file-name) "unnamed"))) +;; checked by subed-sort +(defcustom subed-trim-overlapping-subtitle-times-on-save nil + "Whether all overlapping subtitles should be trimmed on saving. +Subtitles are trimmed according to +`subed-trim-overlapping-subtitle-start-or-end'." + :type 'boolean + :group 'subed) + +;; checked by subed mode hook +(defcustom subed-check-overlapping-subtitle-times-on-load nil + "Whether to check for, and optionally trim, overlapping +subtitles on entering subed mode. Subtitles are trimmed according +to `subed-trim-overlapping-subtitle-start'. Defaults to nil." + :type 'boolean + :group 'subed) + +(defcustom subed-trim-overlapping-subtitle-trim-start nil + "How overlapping subtitles should be trimmed. If t, adjust the +start time of the following subtitle, if nil, adjust the end of +the current subtitle. Defaults to nil." + :type 'boolean + :group 'subed) + +(defcustom subed-trim-overlapping-use-subed-subtitle-spacing nil + "Whether `subed-subtitle-spacing' should be used when trimming +overlapping subtitles. If nil, subtitles will trimmed to one +millisecond less than adjacent one. Defaults to nil. + +Spacing can also be set by using a prefix arg when calling +`subed-sanitize-overlaps', which see." + :type 'boolean + :group 'subed) ;; Hooks diff --git a/subed/subed.el b/subed/subed.el index cdb9842..4826c05 100644 --- a/subed/subed.el +++ b/subed/subed.el @@ -197,6 +197,8 @@ Key bindings: (add-hook 'after-save-hook #'subed-mpv-reload-subtitles :append :local) (add-hook 'kill-buffer-hook #'subed-mpv-kill :append :local) (add-hook 'kill-emacs-hook #'subed-mpv-kill :append :local) + (when subed-check-overlapping-subtitle-times-on-load + (add-hook 'subed-mode-hook #'subed-check-overlaps :append :local)) (when subed-auto-find-video (let ((video-file (subed-guess-video-file))) (when video-file