yama6a / subtitle-toolbox-php
A PHP library and command line tool that reads, edits and writes subtitles, transcripts and chapter lists in more than 30 formats.
Requires
- php: ^8.2
- ext-dom: *
- ext-iconv: *
- ext-mbstring: *
Requires (Dev)
- phpunit/phpunit: ^11.5
- yama6a/php-glyph-ocr: ^0.3
Suggests
- ext-curl: for DeepLEngine, GoogleTranslateEngine and the translate command
- ext-zlib: to decode PNG images, which PgsFormatter needs, to compress PNG images and to read zlib-compressed MKV tracks
- yama6a/php-glyph-ocr: ^0.3, for GlyphOcrEngine, the pure PHP OCR of PGS and VobSub image cues where Tesseract is not installed
Provides
None
Conflicts
- yama6a/php-glyph-ocr: <0.3 || >=0.4
Replaces
None
- dev-master
- 5.17.1
- 5.17.0
- 5.16.0
- 5.15.0
- 5.14.0
- 5.13.0
- 5.12.0
- 5.11.0
- 5.10.0
- 5.9.0
- 5.8.0
- 5.7.2
- 5.7.1
- 5.7.0
- 5.6.4
- 5.6.3
- 5.6.2
- 5.6.1
- 5.6.0
- 5.5.1
- 5.5.0
- 5.4.0
- 5.3.1
- 5.3.0
- 5.2.0
- 5.1.1
- 5.1.0
- 5.0.4
- 5.0.3
- 5.0.2
- 5.0.1
- 5.0.0
- 4.16.1
- 4.16.0
- 4.15.0
- 4.14.5
- 4.14.4
- 4.14.3
- 4.14.2
- 4.14.1
- 4.14.0
- 4.13.2
- 4.13.1
- 4.13.0
- 4.12.4
- 4.12.3
- 4.12.2
- 4.12.1
- 4.12.0
- 4.11.1
- 4.11.0
- 4.10.1
- 4.10.0
- 4.9.1
- 4.9.0
- 4.8.1
- 4.8.0
- 4.7.10
- 4.7.9
- 4.7.8
- 4.7.7
- 4.7.6
- 4.7.5
- 4.7.4
- 4.7.3
- 4.7.2
- 4.7.1
- 4.7.0
- 4.6.4
- 4.6.3
- 4.6.2
- 4.6.1
- 4.6.0
- 4.5.0
- 4.4.7
- 4.4.6
- 4.4.5
- 4.4.4
- 4.4.3
- 4.4.2
- 4.4.1
- 4.4.0
- 4.3.2
- 4.3.1
- 4.3.0
- 4.2.0
- 4.1.0
- 4.0.5
- 4.0.4
- 4.0.3
- 4.0.2
- 4.0.1
- 4.0.0
- 3.26.10
- 3.26.9
- 3.26.8
- 3.26.7
- 3.26.6
- 3.26.5
- 3.26.4
- 3.26.3
- 3.26.2
- 3.26.1
- 3.26.0
- 3.25.10
- 3.25.9
- 3.25.8
- 3.25.7
- 3.25.6
- 3.25.5
- 3.25.4
- 3.25.3
- 3.25.2
- 3.25.1
- 3.25.0
- 3.24.4
- 3.24.3
- 3.24.2
- 3.24.1
- 3.24.0
- 3.23.0
- 3.22.1
- 3.22.0
- 3.21.0
- 3.20.3
- 3.20.2
- 3.20.1
- 3.20.0
- 3.19.2
- 3.19.1
- 3.19.0
- 3.18.0
- 3.17.0
- 3.16.0
- 3.15.2
- 3.15.1
- 3.15.0
- 3.14.0
- 3.13.0
- 3.12.1
- 3.12.0
- 3.11.1
- 3.11.0
- 3.10.0
- 3.9.0
- 3.8.1
- 3.8.0
- 3.7.0
- 3.6.4
- 3.6.3
- 3.6.2
- 3.6.1
- 3.6.0
- 3.5.0
- 3.4.0
- 3.3.2
- 3.3.1
- 3.3.0
- 3.2.7
- 3.2.6
- 3.2.5
- 3.2.4
- 3.2.3
- 3.2.2
- 3.2.1
- 3.2.0
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.1
- 3.0.0
- 2.18.8
- 2.18.7
- 2.18.6
- 2.18.5
- 2.18.4
- 2.18.3
- 2.18.2
- 2.18.1
- 2.18.0
- 2.17.23
- 2.17.22
- 2.17.21
- 2.17.20
- 2.17.19
- 2.17.18
- 2.17.17
- 2.17.16
- 2.17.15
- 2.17.14
- 2.17.13
- 2.17.12
- 2.17.11
- 2.17.10
- 2.17.9
- 2.17.8
- 2.17.7
- 2.17.6
- 2.17.5
- 2.17.4
- 2.17.3
- 2.17.2
- 2.17.1
- 2.17.0
- 2.16.1
- 2.16.0
- 2.15.0
- 2.14.0
- 2.13.0
- 2.12.0
- 2.11.0
- 2.10.0
- 2.9.0
- 2.8.0
- 2.7.4
- 2.7.3
- 2.7.2
- 2.7.1
- 2.7.0
- 2.6.1
- 2.6.0
- 2.5.0
- 2.4.2
- 2.4.1
- 2.4.0
- 2.3.3
- 2.3.2
- 2.3.1
- 2.3.0
- 2.2.0
- 2.1.0
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 1.70.6
- 1.70.5
- 1.70.4
- 1.70.3
- 1.70.2
- 1.70.1
- 1.70.0
- 1.69.0
- 1.68.0
- 1.67.1
- 1.67.0
- 1.66.0
- 1.65.1
- 1.65.0
- 1.64.0
- 1.63.0
- 1.62.1
- 1.62.0
- 1.61.0
- 1.60.0
- 1.59.0
- 1.58.0
- 1.57.0
- 1.56.0
- 1.55.0
- 1.54.1
- 1.54.0
- 1.53.0
- 1.52.0
- 1.51.0
- 1.50.0
- 1.49.0
- 1.48.0
- 1.47.0
- 1.46.0
- 1.45.0
- 1.44.0
- 1.43.0
- 1.42.0
- 1.41.0
- 1.40.0
- 1.39.0
- 1.38.0
- 1.37.0
- 1.36.0
- 1.35.0
- 1.34.0
- 1.33.0
- 1.32.0
- 1.31.0
- 1.30.0
- 1.29.1
- 1.29.0
- 1.28.0
- 1.27.1
- 1.27.0
- 1.26.0
- 1.25.0
- 1.24.0
- 1.23.0
- 1.22.0
- 1.21.0
- 1.20.0
- 1.19.0
- 1.18.0
- 1.17.0
- 1.16.0
- 1.15.0
- 1.14.0
- 1.13.1
- 1.13.0
- 1.12.0
- 1.11.0
- 1.10.8
- 1.10.7
- 1.10.6
- 1.10.5
- 1.10.4
- 1.10.3
- 1.10.2
- 1.10.1
- 1.10.0
- 1.9.1
- 1.9.0
- 1.8.0
- 1.7.0
- 1.6.0
- 1.5.0
- 1.4.1
- 1.4.0
- 1.3.0
- 1.2.0
- 1.1.0
- 1.0.10
- 1.0.9
- 1.0.8
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- 0.1.9
- 0.1.8
- 0.1.7
- 0.1.6
- 0.1.5
- 0.1.4
- 0.1.3
- 0.1.2
- 0.1.1
- 0.1
- dev-freeze-option-defaults
- dev-errors-lone-lowercase-i
- dev-ass-style-flags
- dev-sync-by-points
This package is auto-updated.
Last update: 2026-10-15 20:34:36 UTC
README
A PHP library and command line tool that reads, edits and writes subtitles, transcripts and chapter lists in more than 30 formats.
Upgrading from 2.x? The package has a new name. See the upgrade guide.
Upgrading from 3.x? See the 4.0 upgrade guide.
Install
composer require yama6a/subtitle-toolbox-php # Optional, for OCR of PGS, VobSub and DVB image subtitles: composer require yama6a/php-glyph-ocr:^0.3 # php-glyph-ocr, the pure PHP OCR engine apt install tesseract-ocr # or Tesseract on Debian and Ubuntu, for more than 100 languages
The core package needs neither OCR engine. See ocr.md for the install commands of other systems and languages.
The library needs PHP 8.2 or later with ext-dom, ext-iconv and ext-mbstring. These extensions are optional:
ext-zlibfor PGS output and compressed MKV tracksext-curlfor the translation engines
The command line tool also comes as a PHAR file and as two container images. The -tesseract image includes Tesseract for OCR. See Install without Composer for the commands.
Supported formats
Case is the case of the enum Format, for example Format::SubRip. Name is the format name for --from and --to. An image cue holds a bitmap in place of text.
Text subtitles
| Format | Case | Name | Extensions | Read | Write | Notes |
|---|---|---|---|---|---|---|
| ASS, SSA | Ass |
ass |
.ass, .ssa |
✅ | ✅ | |
| Cavena 890 | Cavena890 |
cavena890 |
.890 |
✅ | ✅ | binary, no public spec, not detected from the content |
| CSV, TSV | Csv, Tsv |
csv, tsv |
.csv, .tsv |
✅ | ✅ | not detected from the content |
| DCP subtitles: SMPTE ST 428-7, Interop | Dcp |
dcp |
.xml |
✅ | ✅ | digital cinema text subtitles |
| EBU STL | EbuStl |
stl |
.stl |
✅ | ✅ | binary, 25 or 30 fps |
| iTunes Timed Text | Itt |
itt |
.itt |
✅ | ✅ | needs a frame rate to write |
| JSON of this library | Json |
json |
.json |
✅ | ✅ | |
| LRC | Lyrics |
lrc |
.lrc |
✅ | ✅ | with enhanced LRC word times |
| MCC | Mcc |
mcc |
.mcc |
✅ | ✅ | CEA-608 and CEA-708 closed captions of MacCaption and CaptionMaker |
| MicroDVD | MicroDvd |
microdvd |
.sub |
✅ | ✅ | needs the frame rate of the video |
| MPL2 | Mpl2 |
mpl2 |
.txt |
✅ | ✅ | |
| MPSub | MpSub |
mpsub |
.mpsub |
✅ | ✅ | |
| PAC by Screen Electronics, PAC Unicode | Pac |
pac |
.pac, .fpc |
✅ | ✅ | binary, no public spec, code page detected |
| QuickTime text | QuickTimeText |
qttext |
.txt |
✅ | ✅ | {QTtext} text tracks of QuickTime |
| SAMI | Sami |
sami |
.smi, .sami |
✅ | ✅ | one language class per parse |
| SBV | Sbv |
sbv |
.sbv |
✅ | ✅ | |
| SCC | Scc |
scc |
.scc |
✅ | ✅ | CEA-608 closed captions |
| Spruce STL | SpruceStl |
spruce-stl |
.stl |
✅ | ✅ | DVD Studio Pro text subtitles |
| SubRip | SubRip |
srt |
.srt |
✅ | ✅ | |
| SubViewer 1 and 2 | SubViewer |
subviewer |
.sub |
✅ | ✅ | |
| TMPlayer | TmPlayer |
tmplayer |
.txt |
✅ | ✅ | |
| TTML, IMSC, DFXP | Ttml |
ttml |
.ttml, .dfxp, .xml |
✅ | ✅ | |
| WebVTT | WebVtt |
vtt |
.vtt |
✅ | ✅ | also chapters |
| YouTube timed text | YouTubeTimedText |
youtube |
.srv3, .json3, .srv1, .ytt |
✅ | ✅ | json3, srv1, srv2, srv3 and transcript XML |
Image subtitles
| Format | Case | Name | Extensions | Read | Write | Notes |
|---|---|---|---|---|---|---|
| PGS | Pgs |
pgs |
.sup |
✅ | ✅ | Blu-ray bitmaps as image cues |
| VobSub | VobSub |
vobsub |
.idx with .sub |
✅ | ❌ | DVD bitmaps as image cues |
Transcripts
| Format | Case | Name | Extensions | Read | Write | Notes |
|---|---|---|---|---|---|---|
| Cloud speech-to-text JSON: AWS Transcribe, Deepgram, AssemblyAI, Google Speech-to-Text | AwsTranscribe, Deepgram, AssemblyAi, GoogleSpeech |
aws-transcribe, deepgram, assemblyai, google-speech |
.json |
✅ | ✅ | not detected from the content |
| HTML transcript | HtmlTranscript |
html |
.html, .htm |
✅ | ✅ | the Podcasting 2.0 HTML format |
| Plain text | PlainText |
txt |
.txt |
❌ | ✅ | transcript |
| Podcasting 2.0 transcript JSON | PodcastTranscript |
podcast-transcript |
.json |
✅ | ✅ | |
| Whisper JSON | Whisper |
whisper |
.json |
✅ | ✅ | OpenAI API, openai-whisper, faster-whisper, WhisperX, whisper.cpp |
Chapters
| Format | Case | Name | Extensions | Read | Write | Notes |
|---|---|---|---|---|---|---|
| FFmpeg metadata chapters | FfMetadataChapters |
ffmeta-chapters |
.ffmeta |
✅ | ✅ | not detected from the content |
| OGM chapters | OgmChapters |
ogm-chapters |
.txt |
✅ | ✅ | not detected from the content |
| Podcasting 2.0 chapters | PodcastChapters |
podcast-chapters |
.json |
✅ | ✅ | not detected from the content |
| YouTube chapters | YouTubeChapters |
youtube-chapters |
.txt |
✅ | ✅ | not detected from the content |
Containers
| Format | Case | Name | Extensions | Read | Write | Notes |
|---|---|---|---|---|---|---|
| MKV and WebM tracks | .mkv, .webm |
✅ | ❌ | text, ASS, SSA, WebVTT, PGS, VobSub and DVB subtitle tracks | ||
| MP4 tracks | .mp4, .m4v, .mov |
✅ | ❌ | tx3g (mov_text) tracks, c608 and c708 closed caption tracks |
||
| MPEG-TS ARIB captions | .ts, .m2ts |
✅ | ❌ | Japanese ISDB and Brazilian ISDB-Tb captions with furigana and gaiji | ||
| MPEG-TS DVB subtitles | .ts |
✅ | ❌ | DVB bitmap subtitles as image cues | ||
| MPEG-TS teletext | .ts |
✅ | ❌ | DVB teletext subtitle pages, see ETS 300 706 |
Command line tool
Composer installs vendor/bin/stbox. Optional parts are in brackets.
stbox convert movie.srt --to vtt [--timing-fix-overlaps] [-o movie.vtt] stbox convert movie.mkv --to srt --track 3 [-o movie.srt] stbox convert movie.sup --to srt --ocr [--ocr-language deu] [-o movie.srt] stbox convert movie.sub --input-fps 25 --from-fps 25 --to-fps 23.976 [-o movie.fixed.sub] stbox info movie.srt [--json] stbox validate movie.srt --preset netflix-en [--video-fps 23.976] stbox sync movie.de.srt --reference movie.en.srt [-o movie.de.synced.srt] stbox diff movie.v1.srt movie.v2.srt [--text-only] stbox translate movie.de.srt --engine deepl --target-language en-US [-o movie.en.srt] stbox dual --primary movie.en.srt --secondary movie.de.srt --to ass [--mode stack] [-o movie.en-de.ass] stbox hls movie.vtt --output-dir hls/ [--segment 6] stbox formats
- Without
-o, the output goes to standard output. - Several inputs need
--output-dir, for exampleconvert season1/ --shift 2 --output-dir fixed/. - No command overwrites a file.
- When detection fails, pass
--from.
stbox convert --help lists the option groups of convert. See cli.md for all commands and options.
Library
Load and write
use SubtitleToolbox\Format; use SubtitleToolbox\LineEnding; use SubtitleToolbox\Subtitle; use SubtitleToolbox\WriteOptions; $subtitle = Subtitle::load('movie.srt', Format::SubRip); $subtitle = Subtitle::load('movie.srt'); $subtitle->findFormat(); // Format::SubRip $subtitle->save('movie.vtt'); // the format comes from the extension $vtt = $subtitle->toString(Format::WebVtt, new WriteOptions(lineEnding: LineEnding::Crlf, stripTags: true));
Read options
use SubtitleToolbox\Format; use SubtitleToolbox\Parsers\Options\MicroDvdReadOptions; use SubtitleToolbox\ReadOptions; use SubtitleToolbox\Subtitle; $latin1 = Subtitle::load('latin1.srt', Format::SubRip, new ReadOptions(encoding: 'Windows-1252')); $microDvd = Subtitle::load('movie.sub', Format::MicroDvd, new ReadOptions(formats: [new MicroDvdReadOptions(frameRate: 23.976)]));
See read-options.md for the options of each format.
Edit
use SubtitleToolbox\CaseMode; use SubtitleToolbox\Format; use SubtitleToolbox\HearingImpaired\HearingImpairedOptions; use SubtitleToolbox\HearingImpaired\HearingImpairedRemover; use SubtitleToolbox\Subtitle; $subtitle = Subtitle::load('movie.srt', Format::SubRip); $subtitle->shift(-2.5) // all cues 2.5 s earlier ->convertFrameRate(25, 23.976) ->fixOverlaps(0.083) // a gap of at least 0.083 s between cues ->wrapLines(42) // at most 42 characters per line, 2 lines ->changeCase(CaseMode::Sentence); $firstMinute = $subtitle->withSlice(0, 60); // a new Subtitle, $subtitle stays as it is $report = HearingImpairedRemover::apply($subtitle, new HearingImpairedOptions(parentheses: false)); echo "$report->removedLines lines removed\n";
A service such as HearingImpairedRemover changes the subtitle in place and returns a report.
Validate and count
use SubtitleToolbox\Format; use SubtitleToolbox\Subtitle; use SubtitleToolbox\SubtitleStatistics; use SubtitleToolbox\Validation\ValidationRules; $subtitle = Subtitle::load('movie.srt', Format::SubRip); foreach ($subtitle->validate(ValidationRules::netflixEnglish(23.976)) as $violation) { echo "cue index $violation->cueIndex: {$violation->rule->value} is $violation->value\n"; } $stats = SubtitleStatistics::of($subtitle); echo "$stats->cueCount cues, $stats->wordCount words\n";
MKV and WebM tracks
use SubtitleToolbox\Subtitle; foreach (Subtitle::tracks('movie.mkv') as $track) { echo "$track->number: $track->codecId, $track->language, $track->name\n"; // 3: S_TEXT/UTF8, de, Deutsch (Forced) } $subtitle = Subtitle::loadTrack('movie.mkv', 3); $subtitle = Subtitle::loadTrack('recording.ts', 777); // teletext page 777 of a DVB recording $subtitle = Subtitle::loadTrack('recording.ts', 2050); // DVB bitmap subtitles with PID 2050, as image cues
OCR
use SubtitleToolbox\Format; use SubtitleToolbox\Ocr\OcrEngineChooser; use SubtitleToolbox\Ocr\OcrEngineName; use SubtitleToolbox\Subtitle; $subtitle = Subtitle::load('movie.sup', Format::Pgs); $subtitle->recognizeText(OcrEngineChooser::create()); // Tesseract if installed, otherwise php-glyph-ocr $subtitle->recognizeText(OcrEngineChooser::create(OcrEngineName::Glyph)); // always php-glyph-ocr $subtitle->save('movie.srt');
create() takes the Tesseract language as its second argument, for example 'deu+eng'. For engine settings, pass TesseractOcrOptions to new TesseractOcrEngine() or GlyphOcrOptions to new GlyphOcrEngine().
Translate
use SubtitleToolbox\Format; use SubtitleToolbox\Subtitle; use SubtitleToolbox\Translation\DeepLEngine; use SubtitleToolbox\Translation\DeepLOptions; use SubtitleToolbox\Translation\TranslationRunner; $subtitle = Subtitle::load('movie.de.srt', Format::SubRip); (new TranslationRunner(new DeepLEngine(new DeepLOptions(apiKey: $apiKey))))->translate($subtitle, 'de', 'en-US'); $subtitle->save('movie.en.srt');
GoogleTranslateEngine works the same way. See translation.md.
Sync to a reference
use SubtitleToolbox\Format; use SubtitleToolbox\Subtitle; use SubtitleToolbox\Sync\ReferenceSync; use SubtitleToolbox\Sync\ReferenceSyncOptions; $german = Subtitle::load('movie.de.srt', Format::SubRip); $english = Subtitle::load('movie.en.srt', Format::SubRip); $report = ReferenceSync::apply($german, new ReferenceSyncOptions(reference: $english)); echo "offset $report->offset s, scale $report->scale, score $report->score\n";
Every exception implements SubtitleToolboxException. See errors.md.
OCR
OCR turns the bitmaps of PGS, VobSub and DVB subtitles into text. The library uses Tesseract when it is installed. Otherwise it uses php-glyph-ocr. When neither engine is installed, the call throws InvalidArgumentException that names both engines. Tesseract reads more than 100 languages. php-glyph-ocr reads only Latin-script fonts. See ocr.md for the install commands and a comparison of the engines.
Compatibility
See compatibility.md for the parts that semantic versioning covers and for what a minor or patch release can change.
Documentation
docs/README.md lists every page. The most used pages:
- cli.md: all commands and options
- formats.md: what each parser reads and each formatter writes
- editing.md: retiming, cutting, joining and splitting cues
- text.md: text changes, hearing-impaired removal, common error fixes
- validation.md: rules and presets
- sync.md: sync to a reference or to the speech
- subtitle.md: metadata, comments, cue lookup, statistics
Contributing
Pull requests are welcome. See CONTRIBUTING.md for the licence rules and the credit rule. Run the tests with composer test. Each pull request carries one label that sets the version bump: major, minor, patch or skip-release. Every merge to master publishes a release.
License
MIT, see LICENSE. Material from third parties keeps its own licence, see THIRD_PARTY_NOTICES.md.
Credits
The PHP libraries mantas-done/subtitles and captioning/captioning shaped this library. Their formats, test files and user reports guided many decisions here. THIRD_PARTY_NOTICES.md lists every file copied from other projects.