diff --git a/README.md b/README.md index fcb4957..3be68f2 100644 --- a/README.md +++ b/README.md @@ -328,6 +328,7 @@ Once the last chunk has been uploaded, a `Jobtech\LaravelChunky\Jobs\MergeChunks 'mime_types' => [ 'video/*' => \Jobtech\LaravelChunky\Strategies\VideoStrategy::class, + 'audio/*' => \Jobtech\LaravelChunky\Strategies\AudioStrategy::class, ], 'connection' => env('CHUNKY_MERGE_CONNECTION', 'default'), @@ -405,7 +406,7 @@ namespace App\MergeStrategies; use Jobtech\LaravelChunky\Strategies\MergeStrategy; use Jobtech\LaravelChunky\Strategies\Concerns\ChecksIntegrity; -class AudioStrategy extends MergeStrategy +class PDFStrategy extends MergeStrategy { use ChecksIntegrity; @@ -414,7 +415,7 @@ class AudioStrategy extends MergeStrategy */ public function merge() { - // Implement here your logic to merge audio chunks + // Implement here your logic to merge pdf chunks } } ``` @@ -432,8 +433,9 @@ Once completed, add your strategy into the configuration file, so you can automa 'mime_types' => [ 'video/*' => \Jobtech\LaravelChunky\Strategies\VideoStrategy::class, + 'audio/*' => \Jobtech\LaravelChunky\Strategies\AudioStrategy::class, // Add here - 'audio/*' => \App\MergeStrategies\AudioStrategy::class, + 'application/pdf' => \App\MergeStrategies\PDFStrategy::class, ], ], @@ -443,17 +445,21 @@ Once completed, add your strategy into the configuration file, so you can automa ## Testing -You can run the tests with: +You can run the tests with PHP unit: ```sh $ vendor/bin/phpunit ``` -or with the composer script +If you want to set custom environment variable, you can add a `.env` file for custom disks, queue or whatever you need. Tests anyway set a temporary local disk by default. -```sh -$ composer test ``` +CHUNKY_CHUNK_DISK=s3 +CHUNKY_MERGE_DISK=public +CHUNKY_AUTO_MERGE=false +CHUNKY_MERGE_CONNECTION=redis +CHUNKY_MERGE_QUEUE=my-custom-queue +``` ## Roadmap @@ -461,7 +467,8 @@ See the [open issues](https://github.com/jobtech-dev/laravel-chunky/issues) for We're working on: -* Implement more merge strategies +* Implement more merge strategies for specific mime types + * Video and audio should have dedicated strategy for each codec. * Integrate frontend chunk upload (Not sure if necessary... there are so many packages that does it) * Better tests * Laravel 5.5+ compatibility diff --git a/composer.json b/composer.json index c0bd579..d0d69e7 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ } ], "require": { - "php": "^7.2", + "php": "^7.3", "ext-fileinfo": "*", "ext-json": "*", "illuminate/contracts": "~5.8.0|^6.0|^7.0", diff --git a/scrutinizer.yml b/scrutinizer.yml new file mode 100644 index 0000000..ac46cad --- /dev/null +++ b/scrutinizer.yml @@ -0,0 +1,33 @@ +filter: + excluded_paths: [tests/*] +checks: + php: + code_rating: true + remove_extra_empty_lines: true + remove_php_closing_tag: true + remove_trailing_whitespace: true + fix_use_statements: + remove_unused: true + preserve_multiple: false + preserve_blanklines: true + order_alphabetically: true + fix_php_opening_tag: true + fix_linefeed: true + fix_line_ending: true + fix_identation_4spaces: true + fix_doc_comments: true +tools: + external_code_coverage: false + php_analyzer: true + php_code_coverage: false + php_code_sniffer: + config: + standard: PSR2 + filter: + paths: ['src'] + php_loc: + enabled: true + excluded_dirs: [vendor, tests] + php_cpd: + enabled: true + excluded_dirs: [vendor, tests] \ No newline at end of file diff --git a/src/Strategies/AudioStrategy.php b/src/Strategies/AudioStrategy.php new file mode 100644 index 0000000..7b33cbb --- /dev/null +++ b/src/Strategies/AudioStrategy.php @@ -0,0 +1,51 @@ +mergeWithFFMpeg(); + + $this->deleteChunks($this->folder); + + return $this->mergeContents(); + } + + /** + * {@inheritdoc} + */ + public function guessFormat() + { + $extension = strtolower(Arr::last(explode('.', $this->destination()))); + + switch ($extension) { + case 'aac': + return new Aac; + case 'flac': + return new Flac; + case 'mp3': + return new Mp3; + case 'oog': + return new Vorbis; + case 'wav': + return new Wav; + default: + return new CopyFormat; + } + } +} diff --git a/src/Strategies/Concerns/HandlesFFMpeg.php b/src/Strategies/Concerns/HandlesFFMpeg.php new file mode 100644 index 0000000..1794e18 --- /dev/null +++ b/src/Strategies/Concerns/HandlesFFMpeg.php @@ -0,0 +1,46 @@ +manager->chunksFilesystem()) + ->open($this->mapChunksToArray()) + ->export() + ->inFormat($this->guessFormat()) + ->concatWithoutTranscoding(); + + if (! empty($visibility = $this->visibility())) { + $exporter->withVisibility($visibility); + } + + $exporter->toDisk( + $this->manager->getMergeDisk() + )->save($this->destination); + } + + /** + * Retrieve visibility option. + * + * @return string|null + */ + public function visibility(): ?string + { + return Arr::get( + $this->manager->getMergeOptions(), + 'visibility' + ); + } + + /** + * Guess format from destination file extension. + * + * @return mixed + */ + abstract public function guessFormat(); +} diff --git a/src/Strategies/VideoStrategy.php b/src/Strategies/VideoStrategy.php index 9ca544b..b8a2622 100644 --- a/src/Strategies/VideoStrategy.php +++ b/src/Strategies/VideoStrategy.php @@ -2,50 +2,47 @@ namespace Jobtech\LaravelChunky\Strategies; +use FFMpeg\Format\Video\Ogg; +use FFMpeg\Format\Video\WebM; +use FFMpeg\Format\Video\WMV; +use FFMpeg\Format\Video\WMV3; +use FFMpeg\Format\Video\X264; use Illuminate\Support\Arr; use Jobtech\LaravelChunky\Strategies\Concerns\ChecksIntegrity; -use ProtoneMedia\LaravelFFMpeg\Support\FFMpeg; +use Jobtech\LaravelChunky\Strategies\Concerns\HandlesFFMpeg; +use ProtoneMedia\LaravelFFMpeg\FFMpeg\CopyFormat; class VideoStrategy extends MergeStrategy { - use ChecksIntegrity; + use ChecksIntegrity, + HandlesFFMpeg; public function merge() { - $this->handleVideoMerge(); + $this->mergeWithFFMpeg(); $this->deleteChunks($this->folder); return $this->mergeContents(); } - public function handleVideoMerge(): void + public function guessFormat() { - $exporter = FFMpeg::fromDisk($this->manager->chunksFilesystem()) - ->open($this->mapChunksToArray()) - ->export(); - - $exporter->concatWithoutTranscoding(); - - if (! empty($visibility = $this->visibility())) { - $exporter->withVisibility($visibility); + $extension = strtolower(Arr::last(explode('.', $this->destination()))); + + switch ($extension) { + case 'oog': + return new Ogg; + case 'webm': + return new WebM; + case 'wmv': + return new WMV; + case 'wmv3': + return new WMV3; + case 'mp4': + return new X264; + default: + return new CopyFormat; } - - $exporter->toDisk( - $this->manager->getMergeDisk() - )->save($this->destination); - } - - /** - * Retrieve visibility option. - * - * @return string|null - */ - public function visibility(): ?string - { - return Arr::get( - $this->manager->getMergeOptions(), - 'visibility' - ); } } diff --git a/tests/Unit/Merge/MergeHandlerTest.php b/tests/Unit/Handlers/MergeHandlerTest.php similarity index 98% rename from tests/Unit/Merge/MergeHandlerTest.php rename to tests/Unit/Handlers/MergeHandlerTest.php index 741880e..d4acf0a 100644 --- a/tests/Unit/Merge/MergeHandlerTest.php +++ b/tests/Unit/Handlers/MergeHandlerTest.php @@ -1,6 +1,6 @@ manager = $this->app->make('chunky'); + } + + /** @test */ + public function strategy_retrieves_visibility_from_options() + { + $mock = $this->mock(ChunksManager::class, function ($mock) { + $mock->shouldReceive('getMergeOptions') + ->once() + ->andReturn([]); + + $mock->shouldReceive('getMergeOptions') + ->once() + ->andReturn([ + 'visibility' => 'public', + ]); + }); + + $strategy = new AudioStrategy($mock); + + $this->assertNull($strategy->visibility()); + $this->assertEquals('public', $strategy->visibility()); + } + + /** @test */ + public function strategy_merges_chunks_without_transcode() + { + $this->manager->chunksFilesystem()->makeDirectory('chunks'); + + $strategy = new AudioStrategy($this->manager); + $strategy->chunksFolder('resources/mp3'); + $strategy->destination('foo/sample.mp3'); + + $strategy->merge(); + + Storage::assertExists('foo/sample.mp3'); + Storage::assertMissing('chunks/resources/mp3/0_sample.mp3'); + Storage::assertMissing('chunks/resources/mp3/1_sample.mp3'); + Storage::assertMissing('chunks/resources/mp3/2_sample.mp3'); + Storage::assertMissing('chunks/resources/mp3'); + } + + /** @test */ + public function strategy_merges_chunks_with_transcode() + { + $this->manager->chunksFilesystem()->makeDirectory('chunks'); + + $strategy = new AudioStrategy($this->manager); + $strategy->chunksFolder('resources/mp3'); + $strategy->destination('foo/sample.wav'); + + $strategy->merge(); + + Storage::assertExists('foo/sample.wav'); + Storage::assertMissing('chunks/resources/mp3/0_sample.mp3'); + Storage::assertMissing('chunks/resources/mp3/1_sample.mp3'); + Storage::assertMissing('chunks/resources/mp3/2_sample.mp3'); + Storage::assertMissing('chunks/resources/mp3'); + } +} diff --git a/tests/Unit/Merge/Strategies/FlysystemStrategyTest.php b/tests/Unit/Strategies/FlysystemStrategyTest.php similarity index 94% rename from tests/Unit/Merge/Strategies/FlysystemStrategyTest.php rename to tests/Unit/Strategies/FlysystemStrategyTest.php index 2035134..89df140 100644 --- a/tests/Unit/Merge/Strategies/FlysystemStrategyTest.php +++ b/tests/Unit/Strategies/FlysystemStrategyTest.php @@ -1,6 +1,6 @@