diff --git a/CHANGELOG.md b/CHANGELOG.md index cab0c88..c4b2650 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ All Notable changes to `pbmedia/laravel-ffmpeg` will be documented in this file +## 7.0.4 - 2020-06-03 + +### Added + +- Added an `each` method to the `MediaOpener` + +### Deprecated + +- Nothing + +### Fixed + +- Nothing + +### Removed + +- Nothing + ## 7.0.3 - 2020-06-01 ### Added diff --git a/README.md b/README.md index ede08db..8c40b45 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,28 @@ $contents = FFMpeg::open('video.mp4') ->getFrameContents(); ``` +### Multiple exports using loops + +Chaining multiple conversions works because the `save` method of the `MediaExporter` returns a fresh instance of the `MediaOpener`. You can use this to loop through items, for example, to exports multiple frames from one video: + +```php +$mediaOpener = FFMpeg::open('video.mp4'); + +foreach ([5, 15, 25] as $key => $seconds) { + $mediaOpener = $mediaOpener->getFrameFromSeconds($seconds) + ->export() + ->save("thumb_{$key}.png"); +} +``` + +The `MediaOpener` comes with an `each` method as well. The example above could be refactored like this: + +```php +FFMpeg::open('video.mp4')->each([5, 15, 25], function ($ffmpeg, $seconds, $key) { + $ffmpeg->getFrameFromSeconds($seconds)->export()->save("thumb_{$key}.png"); +}); +``` + ### Create a timelapse You can create a timelapse from a sequence of images by using the `asTimelapseWithFramerate` method on the exporter diff --git a/src/MediaOpener.php b/src/MediaOpener.php index 50180b5..b1eb934 100755 --- a/src/MediaOpener.php +++ b/src/MediaOpener.php @@ -6,6 +6,7 @@ use FFMpeg\Media\AbstractMediaType; use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Support\Arr; +use Illuminate\Support\Collection; use Illuminate\Support\Traits\ForwardsCalls; use ProtoneMedia\LaravelFFMpeg\Drivers\PHPFFMpeg; use ProtoneMedia\LaravelFFMpeg\Exporters\HLSExporter; @@ -56,6 +57,15 @@ public function __construct($disk = null, PHPFFMpeg $driver = null, MediaCollect $this->collection = $mediaCollection ?: new MediaCollection; } + public function clone(): self + { + return new MediaOpener( + $this->disk, + $this->driver, + $this->collection + ); + } + /** * Set the disk to open files from. */ @@ -156,6 +166,15 @@ public function cleanupTemporaryFiles(): self return $this; } + public function each($items, callable $callback): self + { + Collection::make($items)->each(function ($item, $key) use ($callback) { + return $callback($this->clone(), $item, $key); + }); + + return $this; + } + /** * Returns the Media object from the driver. */ diff --git a/tests/ExportTest.php b/tests/ExportTest.php index 8803c9a..7f966c0 100644 --- a/tests/ExportTest.php +++ b/tests/ExportTest.php @@ -127,6 +127,21 @@ public function it_can_chain_multiple_exports() $this->assertTrue(Storage::disk('local')->has('new_video2.mp4')); } + /** @test */ + public function it_can_chain_multiple_exports_using_the_each_method() + { + $this->fakeLocalVideoFile(); + + (new MediaOpener) + ->open('video.mp4') + ->each(['new_video1.mp4', 'new_video2.mp4'], function ($ffmpeg, $filename) { + $ffmpeg->export()->inFormat($this->x264())->save($filename); + }); + + $this->assertTrue(Storage::disk('local')->has('new_video1.mp4')); + $this->assertTrue(Storage::disk('local')->has('new_video2.mp4')); + } + /** @test */ public function it_can_export_a_with_a_single_filter() { diff --git a/tests/FrameTest.php b/tests/FrameTest.php index 9ad57a6..c32714c 100644 --- a/tests/FrameTest.php +++ b/tests/FrameTest.php @@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Storage; use ProtoneMedia\LaravelFFMpeg\MediaOpener; +use ProtoneMedia\LaravelFFMpeg\Support\FFMpeg; class FrameTest extends TestCase { @@ -23,6 +24,7 @@ public function it_can_only_export_a_frame_from_a_video_file() $this->fail('Should have thrown an exception'); } + /** @test */ public function it_can_export_a_frame_using_seconds() { @@ -38,6 +40,38 @@ public function it_can_export_a_frame_using_seconds() $this->assertTrue(Storage::disk('local')->has('thumb.png')); } + /** @test */ + public function it_can_loop_through_the_exporter() + { + $this->fakeLocalVideoFile(); + + $ffmpeg = FFMpeg::open('video.mp4'); + + foreach ([1,2,3] as $key => $frame) { + $ffmpeg = $ffmpeg->getFrameFromSeconds($frame) + ->export() + ->save("thumb_{$key}.png"); + } + + $this->assertTrue(Storage::disk('local')->has('thumb_0.png')); + $this->assertTrue(Storage::disk('local')->has('thumb_1.png')); + $this->assertTrue(Storage::disk('local')->has('thumb_2.png')); + } + + /** @test */ + public function it_can_loop_through_the_exporter_with_the_foreach_method() + { + $this->fakeLocalVideoFile(); + + FFMpeg::open('video.mp4')->each([1, 2, 3], function ($ffmpeg, $timestamp, $key) { + $ffmpeg->getFrameFromSeconds($timestamp)->export()->save("thumb_{$timestamp}.png"); + }); + + $this->assertTrue(Storage::disk('local')->has('thumb_1.png')); + $this->assertTrue(Storage::disk('local')->has('thumb_2.png')); + $this->assertTrue(Storage::disk('local')->has('thumb_3.png')); + } + /** @test */ public function it_can_export_a_frame_as_base64() {