├── .gitignore ├── .travis.yml ├── tests ├── files │ └── SampleCaptions.srt ├── TimeTest.php ├── CaptionTest.php └── ParserTest.php ├── phpunit.xml ├── src └── SrtParser │ ├── Exceptions │ └── FileNotFoundException.php │ ├── Caption.php │ ├── Time.php │ └── Parser.php ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | .DS_Store 4 | .idea/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0 5 | - 7.1 6 | 7 | install: 8 | - composer install 9 | -------------------------------------------------------------------------------- /tests/files/SampleCaptions.srt: -------------------------------------------------------------------------------- 1 | 1 2 | 00:00:00,000 --> 00:00:03,000 3 | Type Caption Text HereCar is backing up a little bit 4 | 5 | 2 6 | 00:00:03,000 --> 00:00:04,000 7 | Yep there it goes 8 | 9 | 3 10 | 00:00:04,000 --> 00:00:05,000 11 | Don't hit it! 12 | Testing Multi 13 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | tests 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/SrtParser/Exceptions/FileNotFoundException.php: -------------------------------------------------------------------------------- 1 | =7.0" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "6.0.*" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "Benlipp\\SrtParser\\": "src/SrtParser/" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/TimeTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(is_object($var)); 16 | unset($var); 17 | } 18 | 19 | /** 20 | * @dataProvider timeProvider 21 | */ 22 | public function testTimes($time, $expected) 23 | { 24 | $this->assertEquals(Time::get($time), $expected); 25 | } 26 | 27 | public function timeProvider() 28 | { 29 | return [ 30 | ['00:00:00,000', 0], 31 | ['00:04:30,000', 270], 32 | ['01:20:30,999', 4830.999], 33 | ]; 34 | } 35 | } -------------------------------------------------------------------------------- /src/SrtParser/Caption.php: -------------------------------------------------------------------------------- 1 | startTime = Time::get($startTime); 17 | $this->endTime = Time::get($endTime); 18 | $this->text = $text; 19 | } 20 | 21 | /** 22 | * @param mixed $text 23 | * @return Caption 24 | */ 25 | public function setText($text) 26 | { 27 | $this->text = $text; 28 | 29 | return $this; 30 | } 31 | 32 | /** 33 | * Returns an array with snake_case formatted keys for Laravel 34 | * @return array 35 | */ 36 | public function toArray() 37 | { 38 | return [ 39 | 'start_time' => $this->startTime, 40 | 'end_time' => $this->endTime, 41 | 'text' => $this->text 42 | ]; 43 | } 44 | } -------------------------------------------------------------------------------- /src/SrtParser/Time.php: -------------------------------------------------------------------------------- 1 | assertTrue(is_object($var)); 17 | unset($var); 18 | } 19 | 20 | public function testFormatting() 21 | { 22 | $startTime = "00:00:00,000"; 23 | $endTime = "00:00:05,000"; 24 | $text = "Caption"; 25 | 26 | $expectedStart = Time::get($startTime); 27 | $expectedEnd = Time::get($endTime); 28 | 29 | $caption = new Caption($startTime, $endTime, $text); 30 | $this->assertEquals($expectedStart, $caption->startTime); 31 | $this->assertEquals($expectedEnd, $caption->endTime); 32 | $this->assertEquals($text, $caption->text); 33 | } 34 | 35 | public function testToArray() 36 | { 37 | $startTime = "00:00:00,000"; 38 | $endTime = "00:00:05,000"; 39 | $text = "Caption"; 40 | 41 | $expectedStart = Time::get($startTime); 42 | $expectedEnd = Time::get($endTime); 43 | 44 | $caption = new Caption($startTime, $endTime, $text); 45 | $captionArray = $caption->toArray(); 46 | $this->assertTrue(is_array($captionArray)); 47 | 48 | $expectedArray = [ 49 | 'start_time' => $expectedStart, 50 | 'end_time' => $expectedEnd, 51 | 'text' => $text 52 | ]; 53 | $this->assertEquals($captionArray, $expectedArray); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SRT Parser 2 | = 3 | [![Build Status](https://travis-ci.org/benlipp/srt-parser.svg?branch=master)](https://travis-ci.org/benlipp/srt-parser) 4 | 5 | A PHP library to parse SRT files. 6 | Built by Ben Lippincott for [LiveTech](http://www.liveteched.com/) 7 | 8 | Installation / Requirements 9 | - 10 | Run 11 | `composer require "benlipp/srt-parser"` 12 | and let Composer do the work. 13 | 14 | PHP 7+ is **REQUIRED**! This isn't amateur hour. 15 | 16 | Usage 17 | - 18 | Import the `Parser` class: `use Benlipp\SrtParser\Parser;` 19 | 20 | Use it: 21 | ```` 22 | $parser = new Parser(); 23 | 24 | $parser->loadFile('/path/to/srtfile.srt'); 25 | 26 | $captions = $parser->parse(); 27 | ```` 28 | or 29 | ```` 30 | $parser = new Parser(); 31 | 32 | $parser->loadString($formatted_caption_input); 33 | 34 | $captions = $parser->parse(); 35 | ```` 36 | 37 | `parse()` returns an array of captions. Use them like so: 38 | 39 | ```` 40 | foreach($captions as $caption){ 41 | echo "Start Time: " . $caption->startTime; 42 | echo "End Time: " . $caption->endTime; 43 | echo "Text: " . $caption->text; 44 | } 45 | ```` 46 | A caption can be returned as an array instead of an object, if you prefer. The array is `snake_case` for compatibility with Laravel's attributes. 47 | ```` 48 | foreach($captions as $caption){ 49 | $caption = $caption->toArray(); 50 | echo "Start Time: " . $caption['start_time']; 51 | echo "End Time: " . $caption['end_time']; 52 | echo "Text: " . $caption['text']; 53 | } 54 | ```` 55 | For Laravel usage with a model: 56 | ```` 57 | $url = "https://youtu.be/dQw4w9WgXcQ"; 58 | $video = new Video($url); 59 | foreach ($captions as $caption) { 60 | $data = new VideoMetadata($caption->toArray()); 61 | $video->videoMetadata()->save($data); 62 | } 63 | ```` 64 | 65 | You can also chain the `parse()` method: 66 | ```` 67 | $parser = new Parser(); 68 | $captions = $parser->loadFile($srtPath)->parse(); 69 | ```` 70 | 71 | Contributing 72 | - 73 | Run PHPUnit on your changes, pretty please. If you add a new feature, add tests for that feature. 74 | -------------------------------------------------------------------------------- /tests/ParserTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(is_object($var)); 17 | unset($var); 18 | } 19 | 20 | public function testFileNotFound() 21 | { 22 | $parser = new Parser(); 23 | $this->expectException(FileNotFoundException::class); 24 | $result = $parser->loadFile('NotARealFile'); 25 | $this->assertFalse(is_object($result)); 26 | } 27 | 28 | public function testReadFile() 29 | { 30 | $parser = new Parser(); 31 | $result = $parser->loadFile(__DIR__ . '/files/SampleCaptions.srt'); 32 | $this->assertTrue(is_object($result)); 33 | } 34 | 35 | public function testLoadString() 36 | { 37 | $captionString = file_get_contents(__DIR__ . '/files/SampleCaptions.srt'); 38 | $parser = new Parser(); 39 | $result = $parser->loadString($captionString); 40 | $this->assertTrue(is_object($result)); 41 | } 42 | 43 | 44 | public function testParse() 45 | { 46 | $testData = $this->captionData(); 47 | $parser = new Parser(); 48 | $parser->loadFile(__DIR__ . '/files/SampleCaptions.srt'); 49 | $captions = $parser->parse(); 50 | foreach ($captions as $key => $caption) { 51 | $this->assertEquals($testData[$key]['startTime'], $caption->startTime); 52 | $this->assertEquals($testData[$key]['endTime'], $caption->endTime); 53 | $this->assertEquals($testData[$key]['text'], $caption->text); 54 | } 55 | } 56 | 57 | public function captionData() 58 | { 59 | return [ 60 | [ 61 | 'startTime' => 0, 62 | 'endTime' => 3, 63 | 'text' => "Type Caption Text HereCar is backing up a little bit" 64 | ], 65 | [ 66 | 'startTime' => 3, 67 | 'endTime' => 4, 68 | 'text' => "Yep there it goes" 69 | ], 70 | [ 71 | 'startTime' => 4, 72 | 'endTime' => 5, 73 | 'text' => "Don't hit it!\nTesting Multi" 74 | ], 75 | 76 | ]; 77 | } 78 | } -------------------------------------------------------------------------------- /src/SrtParser/Parser.php: -------------------------------------------------------------------------------- 1 | data = $fileContents; 21 | 22 | return $this; 23 | } 24 | 25 | public function loadString($string) 26 | { 27 | $this->data = $string; 28 | 29 | return $this; 30 | } 31 | 32 | public function parse() 33 | { 34 | $splitData = self::splitData($this->data); 35 | $captions = self::buildCaptions($splitData); 36 | 37 | return $captions; 38 | } 39 | 40 | /** 41 | * split data into workable chunks 42 | * @return array 43 | */ 44 | private static function splitData($data) 45 | { 46 | //find digits followed by a single line break and timestamps 47 | $sections = preg_split('/\d+(?:\r\n|\r|\n)(?=(?:\d\d:\d\d:\d\d,\d\d\d)\s-->\s(?:\d\d:\d\d:\d\d,\d\d\d))/m', $data,-1,PREG_SPLIT_NO_EMPTY); 48 | $matches = []; 49 | foreach ($sections as $section) { 50 | //cleans out control characters, borrowed from https://stackoverflow.com/a/23066553 51 | $section = preg_replace('/[^\PC\s]/u', '', $section); 52 | if(trim($section) == '') continue; 53 | $matches[] = preg_split('/(\r\n|\r|\n)/', $section, 2,PREG_SPLIT_NO_EMPTY); 54 | } 55 | return $matches; 56 | } 57 | 58 | private static function buildCaptions($matches) 59 | { 60 | $captions = []; 61 | foreach ($matches as $match) { 62 | $times = self::timeMatch($match[0]); 63 | $text = self::textMatch($match[1]); 64 | 65 | $captions[] = new Caption($times['start_time'], $times['end_time'], $text); 66 | } 67 | 68 | return $captions; 69 | } 70 | 71 | private static function timeMatch($timeString) 72 | { 73 | $matches = []; 74 | preg_match_all('/(\d\d:\d\d:\d\d,\d\d\d)\s-->\s(\d\d:\d\d:\d\d,\d\d\d)/', $timeString, $matches, 75 | PREG_SET_ORDER); 76 | $time = $matches[0]; 77 | 78 | return [ 79 | 'start_time' => $time[1], 80 | 'end_time' => $time[2] 81 | ]; 82 | } 83 | 84 | private static function textMatch($textString) 85 | { 86 | $text = rtrim($textString); 87 | $text = str_replace("\r\n", "\n", $text); 88 | 89 | return $text; 90 | } 91 | } 92 | --------------------------------------------------------------------------------