SPDX-FileCopyrightText: 2019-2021 The subed Authors
SPDX-License-Identifier: GPL-3.0-or-later
subed subed is an Emacs major mode for editing subtitles while playing the corresponding media file with [[https://mpv.io/][mpv]]. At the moment, the only supported formats are:
SubRip ( ~.srt~)
WebVTT ( ~.vtt~ )
Advanced SubStation Alpha ( ~.ass~, experimental )
Tab-separated values ( ~.tsv~, experimental ) - as exported by Audacity for labels. TSVs are not recognized automatically because it's a common data format, but you can use ~subed-tsv-mode~ to turn it on in a buffer.
[[file:https://raw.githubusercontent.com/sachac/subed/master/screenshot.jpg]]
** Features
*** mpv integration (optional)
Using network sockets to control MPV works on Linux and on Mac OS X, but not on Microsoft Windows due to the lack of Unix-style sockets. On Microsoft Windows, you will not be able to synchronize with MPV.
** Installation *** Installing the subed package from NonGNU Elpa ~subed~ is now on [[https://elpa.nongnu.org/nongnu/subed.html][NonGNU ELPA]]. On Emacs 28 and later, you can install it with ~M-x package-install~ ~subed~.
To install it on Emacs 27 or earlier, add the following to your Emacs configuration file:
(with-eval-after-load 'package (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/")))
Use ~M-x eval-buffer~ to run the code, use ~M-x package-refresh-contents~ to load the package archives, and then use ~M-x package-install~ ~subed~.
Sample configuration:
(with-eval-after-load 'subed-mode ;; Remember cursor position between sessions (add-hook 'subed-mode-hook 'save-place-local-mode) ;; Break lines automatically while typing (add-hook 'subed-mode-hook 'turn-on-auto-fill) ;; Break lines at 40 characters (add-hook 'subed-mode-hook (lambda () (setq-local fill-column 40))) ;; Some reasonable defaults (add-hook 'subed-mode-hook 'subed-enable-pause-while-typing) ;; As the player moves, update the point to show the current subtitle (add-hook 'subed-mode-hook 'subed-enable-sync-point-to-player) ;; As your point moves in Emacs, update the player to start at the current subtitle (add-hook 'subed-mode-hook 'subed-enable-sync-player-to-point) ;; Replay subtitles as you adjust their start or stop time with M-[, M-], M-{, or M-} (add-hook 'subed-mode-hook 'subed-enable-replay-adjusted-subtitle) ;; Loop over subtitles (add-hook 'subed-mode-hook 'subed-enable-loop-over-current-subtitle) ;; Show characters per second (add-hook 'subed-mode-hook 'subed-enable-show-cps))
*** Manual installation
If that doesn't work, you can install it manually. To install from the main branch:
git clone https://github.com/sachac/subed.git
This will create a =subed= directory with the code.
If you have the =make= utility, you can regenerate the autoload definitions with
make autoloads
If you don't have =make= installed, you can generate the autoloads with:
emacs --quick --batch --eval "(progn (setq generated-autoload-file (expand-file-name \"subed-autoloads.el\" \"subed\") backup-inhibited t) \ (update-directory-autoloads \"./subed\"))"
Then you can add the following to your Emacs configuration (typically =~/.config/emacs/init.el=, =~/.emacs.d/init.el=, or =~/.emacs=; you can create this file if it doesn't exist yet). Here's a configuration example:
;; Note the reference to the subed subdirectory, instead of the one at the root of the checkout (add-to-list 'load-path "/path/to/subed/subed") (require 'subed-autoloads)
(with-eval-after-load 'subed-mode ;; Remember cursor position between sessions (add-hook 'subed-mode-hook 'save-place-local-mode) ;; Break lines automatically while typing (add-hook 'subed-mode-hook 'turn-on-auto-fill) ;; Break lines at 40 characters (add-hook 'subed-mode-hook (lambda () (setq-local fill-column 40))) ;; Some reasonable defaults (add-hook 'subed-mode-hook 'subed-enable-pause-while-typing) ;; As the player moves, update the point to show the current subtitle (add-hook 'subed-mode-hook 'subed-enable-sync-point-to-player) ;; As your point moves in Emacs, update the player to start at the current subtitle (add-hook 'subed-mode-hook 'subed-enable-sync-player-to-point) ;; Replay subtitles as you adjust their start or stop time with M-[, M-], M-{, or M-} (add-hook 'subed-mode-hook 'subed-enable-replay-adjusted-subtitle) ;; Loop over subtitles (add-hook 'subed-mode-hook 'subed-enable-loop-over-current-subtitle) ;; Show characters per second (add-hook 'subed-mode-hook 'subed-enable-show-cps))
You can reload your configuration with =M-x eval-buffer= or restart Emacs.
If you want to try a branch (ex: =derived-mode=), you can use the following command inside the =subed= directory:
git checkout branchname
*** use-package configuration
Here's an example setup if you use [[https://github.com/jwiegley/use-package][use-package]]:
(use-package subed :ensure t :config ;; Remember cursor position between sessions (add-hook 'subed-mode-hook 'save-place-local-mode) ;; Break lines automatically while typing (add-hook 'subed-mode-hook 'turn-on-auto-fill) ;; Break lines at 40 characters (add-hook 'subed-mode-hook (lambda () (setq-local fill-column 40))) ;; Some reasonable defaults (add-hook 'subed-mode-hook 'subed-enable-pause-while-typing) ;; As the player moves, update the point to show the current subtitle (add-hook 'subed-mode-hook 'subed-enable-sync-point-to-player) ;; As your point moves in Emacs, update the player to start at the current subtitle (add-hook 'subed-mode-hook 'subed-enable-sync-player-to-point) ;; Replay subtitles as you adjust their start or stop time with M-[, M-], M-{, or M-} (add-hook 'subed-mode-hook 'subed-enable-replay-adjusted-subtitle) ;; Loop over subtitles (add-hook 'subed-mode-hook 'subed-enable-loop-over-current-subtitle) ;; Show characters per second (add-hook 'subed-mode-hook 'subed-enable-show-cps) )
*** straight configuration
If you use [[https://github.com/radian-software/straight.el][straight.el]], you can install subed with the following recipe:
(straight-use-package '(subed :type git :host github :repo "sachac/subed" :files ("subed/*.el")))
** Getting started
~C-h f subed-mode~ should get you started. This is the parent mode for ~subed-srt-mode~, ~subed-vtt-mode~, and ~subed-ass-mode~. When manually loading a mode, use those specific format modes instead of ~subed-mode~. ** Some workflow ideas
*** Reflowing subtitles into shorter or longer lines
You may want to use ~set-fill-column~ and ~display-fill-column-indicator-mode~ to show the target number of characters.
Use ~subed-split-subtitle~ (~M-.~), ~subed-merge-dwim~ (~M-b~), and ~subed-merge-with-previous~ (~M-M~) to split lines.
Splitting will use the current MPV position if available. If not, it will guess where to split based on the the number of characters in the subtitle. You can use ~subed-mpv-jump-to-current-subtitle~ (~M-j~) to play the current subtitle manually and use ~subed-mpv-toggle-pause~ (~M-SPC~) to stop at the right time. Use ~subed-toggle-loop-over-current-subtitle~ (~C-c C-l~) if you want to keep looping. ~subed-waveform-show-current~ can help you fine-tune the split.
*** Adjusting timestamps
You can use ~subed-mpv-jump-to-current-subtitle~ (~M-j~) to play the current subtitle manually. Use ~subed-mpv-jump-to-current-subtitle-near-end~ (~M-J~) to jump to near the end of the subtitle in order to test it. Use ~subed-toggle-loop-over-current-subtitle~ (~C-c C-l~) if you want to keep looping automatically. Use ~subed-mpv-toggle-pause~ (~M-SPC~) to stop at the right time.
~subed-waveform-show-current~ or ~subed-waveform-show-all~ can be useful for adjusting start and end timestamps. Use ~subed-waveform-set-start~ (~mouse-1~, which is left click) or ~subed-waveform-set-stop~ (~mouse-3~, which is right-click) to adjust only the current subtitle's timestamps, or use ~subed-waveform-set-start-and-copy-to-previous~ (~S-mouse-1~ or ~M-mouse-1~) or ~subed-waveform-set-stop-and-copy-to-next~ (~S-mouse-3~ or ~M-mouse-3~) to adjust adjacent subtitles as well.
You can also manually adjust
A prefix argument sets the number of milliseconds (e.g. ~C-u 1000 M-[ M-[ M-[~ decreases start time by 3 seconds).
*** Editing subtitles
You can use ~subed-mpv-jump-to-current-subtitle~ (~M-j~) to play the current subtitle and use ~subed-mpv-toggle-pause~ (~M-SPC~) to stop at the right time. Use ~subed-toggle-loop-over-current-subtitle~ (~C-c C-l~) if you want to keep looping automatically.
If you have wdiff installed, you can use ~subed-wdiff-subtitle-text-with-file~ to compare the subtitle text with a script or another subtitle file.
*** Writing subtitles from scratch
One way is to start with one big subtitle that covers the whole media file, and then split it using ~subed-split-subtitle~ (~M-.~).
Another way is to type as much of the text as you can without worrying about timestamps, putting each caption on a separate line. Then you can use ~subed-align~ to convert it into timestamped captions.
*** Resynchronizing subtitles
If you're using ~subed-waveform-show-current~ or ~subed-waveform-show-all~, you can use ~M-mouse-2~ (Meta-middle-click, ~subed-waveform-shift-subtitles~) to shift the current subtitle and succeeding subtitles so that they start at the position you clicked on.
To do this with the keyboard, you can use ~subed-shift-subtitles-to-start-at-timestamp~ if you want to specify a timestamp or ~subed-shift-subtitles~ to specify a millisecond offset.
*** Exporting text for review
You can use ~subed-copy-region-text~ to copy the text of the subtitles for pasting into another buffer. Call it with the universal prefix ~C-u~ to copy comments as well.
** Troubleshooting *** subed-mpv: Service name too long
If =subed-mpv-client= reports =(error "Service name too long")=, this is probably because the path to the socket used to communicate with MPV is too long for your operating system. You can use =M-x customize= to set =subed-mpv-socket-dir= to a shorter path.
** Important change in v1.0.0
~subed~ now uses ~subed-srt-mode~, ~subed-vtt-mode~, and ~subed-ass-mode~ instead of directly using ~subed-mode~. These modes should be automatically associated with the ~.vtt~, ~.srt~, and ~.ass~ extensions. If the generic ~subed-mode~ is loaded instead of the format-specific mode, you may get an error such as:
Error in post-command-hook (subed--post-command-handler): (cl-no-applicable-method subed--subtitle-id)
If you set ~auto-mode-alist~ manually in your config, please make sure you associate extensions the appropriate format-specific mode instead of ~subed-mode~. The specific backend functions (ex: ~subed-srt--jump-to-subtitle-id~) are also deprecated in favor of using generic functions such as ~subed-jump-to-subtitle-id~.
** Contributions Contributions would be really appreciated! subed conforms to the [[https://reuse.software/spec/][REUSE Specification]]; this means that every file has copyright and license information. If you modify a file, please update the year shown after ~SPDX-FileCopyrightText~. Thank you!
There's a list of authors in the file ~AUTHORS.org~. If you have at any point contributed to subed, you are most welcome to add your name (and email address if you like) to the list.
** License subed is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the [[https://www.gnu.org/licenses/gpl-3.0.txt][GNU General Public License]] for more details.
Other resources
[[https://mbork.pl/2023-09-18_Making_Anki_flashcards_from_subtitles][Marcin Borkowski: 2023-09-18 Making Anki flashcards from subtitles]]
[[https://github.com/sachac/subed-record][sachac/subed-record: Record audio in segments and compile it into a file]]
[[https://emacsconf.org/captioning/][EmacsConf - Captioning tips]]