diff --git a/composer.json b/composer.json index de36a06..dbff965 100644 --- a/composer.json +++ b/composer.json @@ -14,16 +14,16 @@ "php": ">=7.4.0", "ext-json": "*", "symfony/filesystem": "^4.4|^5.1|^6.0|^7.0", - "symfony/polyfill-php80": "^1.26", + "symfony/polyfill-php80": "^1.28", "symfony/process": "^4.4|^5.1|^6.0|^7.0" }, "require-dev": { "mikey179/vfsstream": "^1.6.11", - "php-cs-fixer/shim": "^3.40", - "phpstan/phpstan": "^1.10.45", + "php-cs-fixer/shim": "^3.49", + "phpstan/phpstan": "^1.10.57", "phpstan/phpstan-phpunit": "^1.3.15", - "phpstan/phpstan-strict-rules": "^1.5", - "symfony/phpunit-bridge": "^6.3.2" + "phpstan/phpstan-strict-rules": "^1.5.2", + "symfony/phpunit-bridge": "^6.4.3" }, "autoload": { "psr-4": { "YoutubeDl\\": "src/" } diff --git a/src/Options.php b/src/Options.php index a4cecaa..e15faf9 100644 --- a/src/Options.php +++ b/src/Options.php @@ -173,6 +173,16 @@ class Options // Video Format Options private ?string $format = null; + /** + * @var list + */ + private array $formatSort = []; + private ?bool $formatSortForce = null; + private ?bool $videoMultistreams = null; + private ?bool $audioMultistreams = null; + private ?bool $preferFreeFormats = null; + private ?bool $checkFormats = null; + private ?bool $checkAllFormats = null; private bool $youtubeSkipDashManifest = false; private ?string $mergeOutputFormat = null; @@ -1202,6 +1212,92 @@ public function format(?string $format): self return $new; } + /** + * Sort the formats by the fields given. + * + * @see https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#sorting-formats + * + * @param list $formatSort + */ + public function formatSort(array $formatSort): self + { + $new = clone $this; + $new->formatSort = $formatSort; + + return $new; + } + + /** + * Force user specified sort order to have precedence over all fields. + * + * @see https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#sorting-formats + */ + public function formatSortForce(?bool $formatSortForce): self + { + $new = clone $this; + $new->formatSortForce = $formatSortForce; + + return $new; + } + + /** + * Allow multiple video streams to be merged into a single file. + */ + public function videoMultistreams(?bool $videoMultistreams): self + { + $new = clone $this; + $new->videoMultistreams = $videoMultistreams; + + return $new; + } + + /** + * Allow multiple audio streams to be merged into a single file. + */ + public function audioMultistreams(?bool $audioMultistreams): self + { + $new = clone $this; + $new->audioMultistreams = $audioMultistreams; + + return $new; + } + + /** + * Prefer video formats with free containers over non-free ones of same + * quality. Use with `Options::formatSort('ext')` option to strictly prefer + * free containers irrespective of quality. + */ + public function preferFreeFormats(?bool $preferFreeFormats): self + { + $new = clone $this; + $new->preferFreeFormats = $preferFreeFormats; + + return $new; + } + + /** + * Make sure formats are selected only from those that are actually + * downloadable. + */ + public function checkFormats(?bool $checkFormats): self + { + $new = clone $this; + $new->checkFormats = $checkFormats; + + return $new; + } + + /** + * Check all formats for whether they are actually downloadable. + */ + public function checkAllFormats(?bool $checkAllFormats): self + { + $new = clone $this; + $new->checkAllFormats = $checkAllFormats; + + return $new; + } + /** * Do not download the DASH manifests and related data on YouTube videos. */ @@ -1657,6 +1753,13 @@ public function toArray(): array 'max-sleep-interval' => $this->maxSleepInterval, // Video Format Options 'format' => $this->format, + 'format-sort' => $this->formatSort, + 'format-sort-force' => $this->formatSortForce, + 'video-multistreams' => $this->videoMultistreams, + 'audio-multistreams' => $this->audioMultistreams, + 'prefer-free-formats' => $this->preferFreeFormats, + 'check-formats' => $this->checkFormats, + 'check-all-formats' => $this->checkAllFormats, 'youtube-skip-dash-manifest' => $this->youtubeSkipDashManifest, 'merge-output-format' => $this->mergeOutputFormat, // Subtitle Options diff --git a/src/Process/ArgvBuilder.php b/src/Process/ArgvBuilder.php index e2218f1..e064092 100644 --- a/src/Process/ArgvBuilder.php +++ b/src/Process/ArgvBuilder.php @@ -29,7 +29,7 @@ public static function build(Options $options): array foreach ($value as $url) { $cmd[] = $url; } - } elseif ($option === 'playlist-items' || $option === 'sub-lang') { + } elseif ($option === 'playlist-items' || $option === 'sub-lang' || $option === 'format-sort') { if (count($value) > 0) { $cmd[] = sprintf('--%s=%s', $option, implode(',', $value)); } diff --git a/src/Process/DefaultProcessBuilder.php b/src/Process/DefaultProcessBuilder.php index 3c9b18d..1cfee73 100644 --- a/src/Process/DefaultProcessBuilder.php +++ b/src/Process/DefaultProcessBuilder.php @@ -14,7 +14,7 @@ final class DefaultProcessBuilder implements ProcessBuilderInterface { private ExecutableFinder $executableFinder; - public function __construct(ExecutableFinder $executableFinder = null) + public function __construct(?ExecutableFinder $executableFinder = null) { $this->executableFinder = $executableFinder ?? new ExecutableFinder(); } diff --git a/src/YoutubeDl.php b/src/YoutubeDl.php index 5bd0d81..8089cbf 100644 --- a/src/YoutubeDl.php +++ b/src/YoutubeDl.php @@ -62,7 +62,7 @@ class YoutubeDl */ private $debug; - public function __construct(ProcessBuilderInterface $processBuilder = null, MetadataReaderInterface $metadataReader = null, Filesystem $filesystem = null) + public function __construct(?ProcessBuilderInterface $processBuilder = null, ?MetadataReaderInterface $metadataReader = null, ?Filesystem $filesystem = null) { $this->processBuilder = $processBuilder ?? new DefaultProcessBuilder(); $this->metadataReader = $metadataReader ?? new DefaultMetadataReader(); diff --git a/tests/StaticProcess.php b/tests/StaticProcess.php index 0df4d64..6179d7a 100644 --- a/tests/StaticProcess.php +++ b/tests/StaticProcess.php @@ -43,7 +43,7 @@ public function writeMetadata(array $writeMetadata): void /** * @param array $env */ - public function start(callable $callback = null, array $env = []): void + public function start(?callable $callback = null, array $env = []): void { if (!is_callable($callback) || $this->outputFile === null) { return; @@ -66,7 +66,7 @@ public function start(callable $callback = null, array $env = []): void } } - public function wait(callable $callback = null): int + public function wait(?callable $callback = null): int { return 0; }