|
| 1 | +<?php |
| 2 | + |
| 3 | +/* |
| 4 | + * This file is part of the Symfony package. |
| 5 | + * |
| 6 | + * (c) Fabien Potencier <fabien@symfony.com> |
| 7 | + * |
| 8 | + * For the full copyright and license information, please view the LICENSE |
| 9 | + * file that was distributed with this source code. |
| 10 | + */ |
| 11 | + |
| 12 | +namespace Symfony\Component\Validator\Constraints; |
| 13 | + |
| 14 | +use Symfony\Component\Process\ExecutableFinder; |
| 15 | +use Symfony\Component\Process\Process; |
| 16 | +use Symfony\Component\Validator\Attribute\HasNamedArguments; |
| 17 | +use Symfony\Component\Validator\Exception\LogicException; |
| 18 | + |
| 19 | +/** |
| 20 | + * @author Kev <https://github.com/symfonyaml> |
| 21 | + * @author Nicolas Grekas <p@tchwork.com> |
| 22 | + */ |
| 23 | +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] |
| 24 | +class Video extends File |
| 25 | +{ |
| 26 | + public const SIZE_NOT_DETECTED_ERROR = '5dab98df-43c8-481b-94f9-46a3c958285c'; |
| 27 | + public const TOO_WIDE_ERROR = '9e18d6a4-aeda-4644-be8e-9e29dbfd6c4a'; |
| 28 | + public const TOO_NARROW_ERROR = 'b267f54b-d994-46d4-9ca6-338fc4f7962f'; |
| 29 | + public const TOO_HIGH_ERROR = '44f4c411-0199-48c2-b597-df1f5944ccde'; |
| 30 | + public const TOO_LOW_ERROR = '0b6bc3ce-df90-40f9-90aa-5bbb840cb481'; |
| 31 | + public const TOO_FEW_PIXEL_ERROR = '510ddf98-2eda-436e-be7e-b6f107bc0e22'; |
| 32 | + public const TOO_MANY_PIXEL_ERROR = 'ff0a8ee8-951d-4c97-afe2-03c0d61a2a02'; |
| 33 | + public const RATIO_TOO_BIG_ERROR = '5e6b9c21-d4d8-444d-9f4c-e3ff1e25a9a6'; |
| 34 | + public const RATIO_TOO_SMALL_ERROR = '26985857-7447-49dc-b271-1477a76cc63c'; |
| 35 | + public const SQUARE_NOT_ALLOWED_ERROR = '18500335-b868-4056-b2a2-aa2aeeb0cbdf'; |
| 36 | + public const LANDSCAPE_NOT_ALLOWED_ERROR = 'cbf38fbc-04c0-457a-8c29-a6f3080e415a'; |
| 37 | + public const PORTRAIT_NOT_ALLOWED_ERROR = '6c3e34a8-94d5-4434-9f20-fb9c0f3ab531'; |
| 38 | + public const CORRUPTED_VIDEO_ERROR = '591b9c4d-d357-425f-8672-6b187816550e'; |
| 39 | + public const MULTIPLE_VIDEO_STREAMS_ERROR = '2d1b2b2e-3f37-4fdd-9a2a-8b6b77b2a6a3'; |
| 40 | + public const UNSUPPORTED_VIDEO_CODEC_ERROR = 'a9f2f6f7-2b5a-4f3c-b746-d3e2e9d1b2a1'; |
| 41 | + public const UNSUPPORTED_VIDEO_CONTAINER_ERROR = 'b7c9d2a4-5e1f-4aa0-8f9d-1c3e2b4a6d7e'; |
| 42 | + |
| 43 | + // Include the mapping from the base class |
| 44 | + |
| 45 | + protected const ERROR_NAMES = [ |
| 46 | + self::NOT_FOUND_ERROR => 'NOT_FOUND_ERROR', |
| 47 | + self::NOT_READABLE_ERROR => 'NOT_READABLE_ERROR', |
| 48 | + self::EMPTY_ERROR => 'EMPTY_ERROR', |
| 49 | + self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR', |
| 50 | + self::INVALID_MIME_TYPE_ERROR => 'INVALID_MIME_TYPE_ERROR', |
| 51 | + self::FILENAME_TOO_LONG => 'FILENAME_TOO_LONG', |
| 52 | + self::SIZE_NOT_DETECTED_ERROR => 'SIZE_NOT_DETECTED_ERROR', |
| 53 | + self::TOO_WIDE_ERROR => 'TOO_WIDE_ERROR', |
| 54 | + self::TOO_NARROW_ERROR => 'TOO_NARROW_ERROR', |
| 55 | + self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', |
| 56 | + self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', |
| 57 | + self::TOO_FEW_PIXEL_ERROR => 'TOO_FEW_PIXEL_ERROR', |
| 58 | + self::TOO_MANY_PIXEL_ERROR => 'TOO_MANY_PIXEL_ERROR', |
| 59 | + self::RATIO_TOO_BIG_ERROR => 'RATIO_TOO_BIG_ERROR', |
| 60 | + self::RATIO_TOO_SMALL_ERROR => 'RATIO_TOO_SMALL_ERROR', |
| 61 | + self::SQUARE_NOT_ALLOWED_ERROR => 'SQUARE_NOT_ALLOWED_ERROR', |
| 62 | + self::LANDSCAPE_NOT_ALLOWED_ERROR => 'LANDSCAPE_NOT_ALLOWED_ERROR', |
| 63 | + self::PORTRAIT_NOT_ALLOWED_ERROR => 'PORTRAIT_NOT_ALLOWED_ERROR', |
| 64 | + self::CORRUPTED_VIDEO_ERROR => 'CORRUPTED_VIDEO_ERROR', |
| 65 | + self::MULTIPLE_VIDEO_STREAMS_ERROR => 'MULTIPLE_VIDEO_STREAMS_ERROR', |
| 66 | + self::UNSUPPORTED_VIDEO_CODEC_ERROR => 'UNSUPPORTED_VIDEO_CODEC_ERROR', |
| 67 | + self::UNSUPPORTED_VIDEO_CONTAINER_ERROR => 'UNSUPPORTED_VIDEO_CONTAINER_ERROR', |
| 68 | + ]; |
| 69 | + |
| 70 | + public array|string $mimeTypes = 'video/*'; |
| 71 | + public ?int $minWidth = null; |
| 72 | + public ?int $maxWidth = null; |
| 73 | + public ?int $maxHeight = null; |
| 74 | + public ?int $minHeight = null; |
| 75 | + public int|float|null $maxRatio = null; |
| 76 | + public int|float|null $minRatio = null; |
| 77 | + public int|float|null $minPixels = null; |
| 78 | + public int|float|null $maxPixels = null; |
| 79 | + public ?bool $allowSquare = true; |
| 80 | + public ?bool $allowLandscape = true; |
| 81 | + public ?bool $allowPortrait = true; |
| 82 | + public array $allowedCodecs = ['h264', 'hevc', 'h265', 'vp9', 'av1', 'mpeg4', 'mpeg2video']; |
| 83 | + public array $allowedContainers = ['mp4', 'mov', 'mkv', 'webm', 'avi']; |
| 84 | + |
| 85 | + // The constant for a wrong MIME type is taken from the parent class. |
| 86 | + public string $mimeTypesMessage = 'This file is not a valid video.'; |
| 87 | + public string $sizeNotDetectedMessage = 'The size of the video could not be detected.'; |
| 88 | + public string $maxWidthMessage = 'The video width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.'; |
| 89 | + public string $minWidthMessage = 'The video width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.'; |
| 90 | + public string $maxHeightMessage = 'The video height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.'; |
| 91 | + public string $minHeightMessage = 'The video height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.'; |
| 92 | + public string $minPixelsMessage = 'The video has too few pixels ({{ pixels }} pixels). Minimum amount expected is {{ min_pixels }} pixels.'; |
| 93 | + public string $maxPixelsMessage = 'The video has too many pixels ({{ pixels }} pixels). Maximum amount expected is {{ max_pixels }} pixels.'; |
| 94 | + public string $maxRatioMessage = 'The video ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.'; |
| 95 | + public string $minRatioMessage = 'The video ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.'; |
| 96 | + public string $allowSquareMessage = 'The video is square ({{ width }}x{{ height }}px). Square videos are not allowed.'; |
| 97 | + public string $allowLandscapeMessage = 'The video is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented videos are not allowed.'; |
| 98 | + public string $allowPortraitMessage = 'The video is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented videos are not allowed.'; |
| 99 | + public string $corruptedMessage = 'The video file is corrupted.'; |
| 100 | + public string $multipleVideoStreamsMessage = 'The video contains multiple streams. Only one stream is allowed.'; |
| 101 | + public string $unsupportedCodecMessage = 'Unsupported video codec "{{ codec }}".'; |
| 102 | + public string $unsupportedContainerMessage = 'Unsupported video container "{{ container }}".'; |
| 103 | + |
| 104 | + /** |
| 105 | + * @param positive-int|string|null $maxSize The max size of the underlying file |
| 106 | + * @param bool|null $binaryFormat Pass true to use binary-prefixed units (KiB, MiB, etc.) or false to use SI-prefixed units (kB, MB) in displayed messages. Pass null to guess the format from the maxSize option. (defaults to null) |
| 107 | + * @param non-empty-string[]|null $mimeTypes Acceptable media types |
| 108 | + * @param positive-int|null $filenameMaxLength Maximum length of the file name |
| 109 | + * @param string|null $disallowEmptyMessage Enable empty upload validation with this message in case of error |
| 110 | + * @param string|null $uploadIniSizeErrorMessage Message if the file size exceeds the max size configured in php.ini |
| 111 | + * @param string|null $uploadFormSizeErrorMessage Message if the file size exceeds the max size configured in the HTML input field |
| 112 | + * @param string|null $uploadPartialErrorMessage Message if the file is only partially uploaded |
| 113 | + * @param string|null $uploadNoTmpDirErrorMessage Message if there is no upload_tmp_dir in php.ini |
| 114 | + * @param string|null $uploadCantWriteErrorMessage Message if the uploaded file can not be stored in the temporary directory |
| 115 | + * @param string|null $uploadErrorMessage Message if an unknown error occurred on upload |
| 116 | + * @param string[]|null $groups |
| 117 | + * @param int<0, int>|null $minWidth Minimum video width |
| 118 | + * @param positive-int|null $maxWidth Maximum video width |
| 119 | + * @param positive-int|null $maxHeight Maximum video height |
| 120 | + * @param int<0, int>|null $minHeight Minimum video weight |
| 121 | + * @param positive-int|float|null $maxRatio Maximum video ratio |
| 122 | + * @param int<0, max>|float|null $minRatio Minimum video ratio |
| 123 | + * @param int<0, max>|float|null $minPixels Minimum amount of pixels |
| 124 | + * @param positive-int|float|null $maxPixels Maximum amount of pixels |
| 125 | + * @param bool|null $allowSquare Whether to allow a square video (defaults to true) |
| 126 | + * @param bool|null $allowLandscape Whether to allow a landscape video (defaults to true) |
| 127 | + * @param bool|null $allowPortrait Whether to allow a portrait video (defaults to true) |
| 128 | + * @param string|null $sizeNotDetectedMessage Message if the system can not determine video size and there is a size constraint to validate |
| 129 | + * @param string[]|null $allowedCodecs Allowed codec names |
| 130 | + * @param string[]|null $allowedContainers Allowed container names |
| 131 | + * |
| 132 | + * @see https://www.iana.org/assignments/media-types/media-types.xhtml Existing media types |
| 133 | + */ |
| 134 | + #[HasNamedArguments] |
| 135 | + public function __construct( |
| 136 | + int|string|null $maxSize = null, |
| 137 | + ?bool $binaryFormat = null, |
| 138 | + array|string|null $mimeTypes = null, |
| 139 | + ?int $filenameMaxLength = null, |
| 140 | + ?int $minWidth = null, |
| 141 | + ?int $maxWidth = null, |
| 142 | + ?int $maxHeight = null, |
| 143 | + ?int $minHeight = null, |
| 144 | + int|float|null $maxRatio = null, |
| 145 | + int|float|null $minRatio = null, |
| 146 | + int|float|null $minPixels = null, |
| 147 | + int|float|null $maxPixels = null, |
| 148 | + ?bool $allowSquare = null, |
| 149 | + ?bool $allowLandscape = null, |
| 150 | + ?bool $allowPortrait = null, |
| 151 | + ?array $allowedCodecs = null, |
| 152 | + ?array $allowedContainers = null, |
| 153 | + ?string $notFoundMessage = null, |
| 154 | + ?string $notReadableMessage = null, |
| 155 | + ?string $maxSizeMessage = null, |
| 156 | + ?string $mimeTypesMessage = null, |
| 157 | + ?string $disallowEmptyMessage = null, |
| 158 | + ?string $filenameTooLongMessage = null, |
| 159 | + ?string $uploadIniSizeErrorMessage = null, |
| 160 | + ?string $uploadFormSizeErrorMessage = null, |
| 161 | + ?string $uploadPartialErrorMessage = null, |
| 162 | + ?string $uploadNoFileErrorMessage = null, |
| 163 | + ?string $uploadNoTmpDirErrorMessage = null, |
| 164 | + ?string $uploadCantWriteErrorMessage = null, |
| 165 | + ?string $uploadExtensionErrorMessage = null, |
| 166 | + ?string $uploadErrorMessage = null, |
| 167 | + ?string $sizeNotDetectedMessage = null, |
| 168 | + ?string $maxWidthMessage = null, |
| 169 | + ?string $minWidthMessage = null, |
| 170 | + ?string $maxHeightMessage = null, |
| 171 | + ?string $minHeightMessage = null, |
| 172 | + ?string $minPixelsMessage = null, |
| 173 | + ?string $maxPixelsMessage = null, |
| 174 | + ?string $maxRatioMessage = null, |
| 175 | + ?string $minRatioMessage = null, |
| 176 | + ?string $allowSquareMessage = null, |
| 177 | + ?string $allowLandscapeMessage = null, |
| 178 | + ?string $allowPortraitMessage = null, |
| 179 | + ?string $corruptedMessage = null, |
| 180 | + ?string $multipleVideoStreamsMessage = null, |
| 181 | + ?string $unsupportedCodecMessage = null, |
| 182 | + ?string $unsupportedContainerMessage = null, |
| 183 | + ?array $groups = null, |
| 184 | + mixed $payload = null, |
| 185 | + ) { |
| 186 | + static $hasFfprobe; |
| 187 | + if (!$hasFfprobe) { |
| 188 | + if (!class_exists(Process::class)) { |
| 189 | + throw new LogicException('The Process component is required to use the Video constraint. Try running "composer require symfony/process".'); |
| 190 | + } |
| 191 | + if (!$hasFfprobe ??= (new ExecutableFinder())->find('ffprobe')) { |
| 192 | + throw new LogicException('The ffprobe binary is required to use the Video constraint.'); |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | + parent::__construct( |
| 197 | + null, |
| 198 | + $maxSize, |
| 199 | + $binaryFormat, |
| 200 | + $mimeTypes, |
| 201 | + $filenameMaxLength, |
| 202 | + $notFoundMessage, |
| 203 | + $notReadableMessage, |
| 204 | + $maxSizeMessage, |
| 205 | + $mimeTypesMessage, |
| 206 | + $disallowEmptyMessage, |
| 207 | + $filenameTooLongMessage, |
| 208 | + $uploadIniSizeErrorMessage, |
| 209 | + $uploadFormSizeErrorMessage, |
| 210 | + $uploadPartialErrorMessage, |
| 211 | + $uploadNoFileErrorMessage, |
| 212 | + $uploadNoTmpDirErrorMessage, |
| 213 | + $uploadCantWriteErrorMessage, |
| 214 | + $uploadExtensionErrorMessage, |
| 215 | + $uploadErrorMessage, |
| 216 | + $groups, |
| 217 | + $payload |
| 218 | + ); |
| 219 | + |
| 220 | + $this->minWidth = $minWidth ?? $this->minWidth; |
| 221 | + $this->maxWidth = $maxWidth ?? $this->maxWidth; |
| 222 | + $this->maxHeight = $maxHeight ?? $this->maxHeight; |
| 223 | + $this->minHeight = $minHeight ?? $this->minHeight; |
| 224 | + $this->maxRatio = $maxRatio ?? $this->maxRatio; |
| 225 | + $this->minRatio = $minRatio ?? $this->minRatio; |
| 226 | + $this->minPixels = $minPixels ?? $this->minPixels; |
| 227 | + $this->maxPixels = $maxPixels ?? $this->maxPixels; |
| 228 | + $this->allowSquare = $allowSquare ?? $this->allowSquare; |
| 229 | + $this->allowLandscape = $allowLandscape ?? $this->allowLandscape; |
| 230 | + $this->allowPortrait = $allowPortrait ?? $this->allowPortrait; |
| 231 | + $this->allowedCodecs = $allowedCodecs ?? $this->allowedCodecs; |
| 232 | + $this->allowedContainers = $allowedContainers ?? $this->allowedContainers; |
| 233 | + $this->sizeNotDetectedMessage = $sizeNotDetectedMessage ?? $this->sizeNotDetectedMessage; |
| 234 | + $this->maxWidthMessage = $maxWidthMessage ?? $this->maxWidthMessage; |
| 235 | + $this->minWidthMessage = $minWidthMessage ?? $this->minWidthMessage; |
| 236 | + $this->maxHeightMessage = $maxHeightMessage ?? $this->maxHeightMessage; |
| 237 | + $this->minHeightMessage = $minHeightMessage ?? $this->minHeightMessage; |
| 238 | + $this->minPixelsMessage = $minPixelsMessage ?? $this->minPixelsMessage; |
| 239 | + $this->maxPixelsMessage = $maxPixelsMessage ?? $this->maxPixelsMessage; |
| 240 | + $this->maxRatioMessage = $maxRatioMessage ?? $this->maxRatioMessage; |
| 241 | + $this->minRatioMessage = $minRatioMessage ?? $this->minRatioMessage; |
| 242 | + $this->allowSquareMessage = $allowSquareMessage ?? $this->allowSquareMessage; |
| 243 | + $this->allowLandscapeMessage = $allowLandscapeMessage ?? $this->allowLandscapeMessage; |
| 244 | + $this->allowPortraitMessage = $allowPortraitMessage ?? $this->allowPortraitMessage; |
| 245 | + $this->corruptedMessage = $corruptedMessage ?? $this->corruptedMessage; |
| 246 | + $this->multipleVideoStreamsMessage = $multipleVideoStreamsMessage ?? $this->multipleVideoStreamsMessage; |
| 247 | + $this->unsupportedCodecMessage = $unsupportedCodecMessage ?? $this->unsupportedCodecMessage; |
| 248 | + $this->unsupportedContainerMessage = $unsupportedContainerMessage ?? $this->unsupportedContainerMessage; |
| 249 | + |
| 250 | + if (!\in_array('video/*', (array) $this->mimeTypes, true) && null === $mimeTypesMessage) { |
| 251 | + $this->mimeTypesMessage = 'The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.'; |
| 252 | + } |
| 253 | + } |
| 254 | +} |
0 commit comments