├── .gitignore ├── .github ├── FUNDING.yml └── workflows │ └── laravel.yml ├── src ├── MediaValidatorServiceProvider.php ├── Traits │ └── MediaFile.php └── MediaValidator.php ├── LICENSE ├── composer.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: minuteoflaravel 2 | -------------------------------------------------------------------------------- /src/MediaValidatorServiceProvider.php: -------------------------------------------------------------------------------- 1 | ffprobe = FFProbe::create([ 15 | 'ffprobe.binaries' => [ 16 | 'ffprobe', 17 | '/usr/bin/ffprobe', 18 | '/usr/local/bin/ffprobe', 19 | 'avprobe', 20 | '/usr/bin/avprobe', 21 | '/usr/local/bin/avprobe', 22 | ], 23 | ]); 24 | } 25 | 26 | private function isAudio($file) 27 | { 28 | return $this->mediaHasAudio($file) && !$this->mediaHasVideo($file); 29 | } 30 | 31 | private function isVideo($file) 32 | { 33 | return $this->mediaHasAudio($file) && $this->mediaHasVideo($file); 34 | } 35 | 36 | private function mediaHasAudio($file) { 37 | try { 38 | $stream = $this->ffprobe 39 | ->streams($file) 40 | ->audios() 41 | ->first(); 42 | } catch (\Throwable $e) { 43 | return false; 44 | } 45 | 46 | if ($stream === null) { 47 | return false; 48 | } 49 | 50 | $this->setAudioStream($stream); 51 | 52 | return true; 53 | } 54 | 55 | private function mediaHasVideo($file) { 56 | try { 57 | $stream = $this->ffprobe 58 | ->streams($file) 59 | ->videos() 60 | ->first(); 61 | } catch (\Throwable $e) { 62 | return false; 63 | } 64 | 65 | if ($stream === null) { 66 | return false; 67 | } 68 | 69 | $this->setVideoStream($stream); 70 | 71 | return true; 72 | } 73 | 74 | public function setAudioStream($stream) { 75 | $this->audioStream = $stream; 76 | } 77 | 78 | public function getAudioStream() { 79 | return $this->audioStream; 80 | } 81 | 82 | public function setVideoStream($stream) { 83 | $this->videoStream = $stream; 84 | } 85 | 86 | public function getVideoStream() { 87 | return $this->videoStream; 88 | } 89 | 90 | private function getMediaDuration($file) { 91 | if ($this->isAudio($file)) { 92 | return (int) $this->getAudioStream()->get('duration'); 93 | } 94 | 95 | if ($this->isVideo($file)) { 96 | return (int) $this->getVideoStream()->get('duration'); 97 | } 98 | 99 | return false; 100 | } 101 | 102 | private function getMediaDimensions($file) { 103 | if (!$this->isVideo($file)) return false; 104 | 105 | return [ 106 | 'width' => $this->getVideoStream()->get('width'), 107 | 'height' => $this->getVideoStream()->get('height'), 108 | ]; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Audio & Video Validator 2 | This package adds validators for audio and video files to your Laravel project. 3 | 4 | ## Installation 5 | To use this package you should intall ffmpeg multimedia framework: 6 | 7 | - On Debian/Ubuntu, run ```sudo apt install ffmpeg``` 8 | - On macOS with Homebrew: ```brew install ffmpeg``` 9 | 10 | After that install the package via composer: 11 | 12 | ```bash 13 | composer require minuteoflaravel/laravel-audio-video-validator 14 | ``` 15 | 16 | ## Validators 17 | 18 | Package adds these validators: 19 | - audio 20 | - video 21 | - codec 22 | - duration 23 | - duration_max 24 | - duration_min 25 | - video_width 26 | - video_height 27 | - video_max_width 28 | - video_max_height 29 | - video_min_width 30 | - video_min_height 31 | 32 | ## Custom error messages 33 | 34 | If you need to add your custom translatable error messages then just add them as always to resources/lang/en/validation.php file: 35 | 36 | ```php 37 | 'audio' => 'The :attribute must be a audio.', 38 | 'video' => 'The :attribute must be a video.', 39 | 'codec' => 'The :attribute codec must be one of these: :codec', 40 | 'duration' => 'The :attribute must be :duration seconds duration.', 41 | 'duration_max' => 'The :attribute duration must be less than :duration_max seconds.', 42 | 'duration_min' => 'The :attribute duration must be greater than :duration_min seconds.', 43 | 'video_width' => 'The :attribute width must be :video_width.', 44 | 'video_height' => 'The :attribute height must be :video_height.', 45 | 'video_max_width' => 'The :attribute width must be less than :video_max_width.', 46 | 'video_min_width' => 'The :attribute width must be greater than :video_min_width.', 47 | 'video_min_height' => 'The :attribute height must be greater than :video_min_height.', 48 | ``` 49 | 50 | ## Some examples 51 | 52 | To check if file is audio file and audio duration is 60 seconds: 53 | 54 | ```php 55 | $request->validate([ 56 | 'audio' => 'audio|duration:60', 57 | ]); 58 | ``` 59 | 60 | To check if file is audio file and audio duration is between 30 and 300 seconds: 61 | 62 | ```php 63 | $request->validate([ 64 | 'audio' => 'audio|duration_min:30|duration_max:300', 65 | ]); 66 | ``` 67 | 68 | To check if file is video file and video duration is between 30 and 300 seconds: 69 | 70 | ```php 71 | $request->validate([ 72 | 'video' => 'video|duration_min:30|duration_max:300', 73 | ]); 74 | ``` 75 | 76 | To check if file is video file and video dimensions are 1000x640: 77 | 78 | ```php 79 | $request->validate([ 80 | 'video' => 'video|video_width:1000|video_height:640', 81 | ]); 82 | ``` 83 | 84 | To check if file is video file and video dimensions greater than 1000x640: 85 | 86 | ```php 87 | $request->validate([ 88 | 'video' => 'video|video_min_width:1000|video_min_height:640', 89 | ]); 90 | ``` 91 | 92 | To check if file is audio file and codec is mp3 or pcm_s16le(wav): 93 | 94 | ```php 95 | $request->validate([ 96 | 'audio' => 'audio|codec:mp3,pcm_s16le', 97 | ]); 98 | ``` 99 | 100 | To check if file is video file and codec is h264(mp4): 101 | 102 | ```php 103 | $request->validate([ 104 | 'video' => 'video|codec:h264', 105 | ]); 106 | ``` 107 | 108 | 109 | ## License 110 | 111 | The MIT License (MIT). Please see [License File](LICENSE) for more information. 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/MediaValidator.php: -------------------------------------------------------------------------------- 1 | createFFProbe(); 16 | } 17 | 18 | public static function boot() { 19 | self::addValidationRules(); 20 | self::replaceMessages(); 21 | } 22 | 23 | public static function addValidationRules() { 24 | Validator::extend( 25 | 'audio', 26 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateAudio', 27 | 'The :attribute must be a audio.', 28 | ); 29 | 30 | Validator::extend( 31 | 'video', 32 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateVideo', 33 | 'The :attribute must be a video.', 34 | ); 35 | 36 | Validator::extend( 37 | 'codec', 38 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateCodec', 39 | 'The :attribute codec must be one of these: :codec', 40 | ); 41 | 42 | Validator::extend( 43 | 'duration', 44 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateDuration', 45 | 'The :attribute must be :duration seconds duration.' 46 | ); 47 | 48 | Validator::extend( 49 | 'duration_max', 50 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateDurationMax', 51 | 'The :attribute duration must be less than :duration_max seconds.' 52 | ); 53 | 54 | Validator::extend( 55 | 'duration_min', 56 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateDurationMin', 57 | 'The :attribute duration must be greater than :duration_min seconds.' 58 | ); 59 | 60 | Validator::extend( 61 | 'video_width', 62 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateVideoWidth', 63 | 'The :attribute width must be :video_width.' 64 | ); 65 | 66 | Validator::extend( 67 | 'video_height', 68 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateVideoHeight', 69 | 'The :attribute height must be :video_height.' 70 | ); 71 | 72 | Validator::extend( 73 | 'video_max_width', 74 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateVideoMaxWidth', 75 | 'The :attribute width must be less than :video_max_width.' 76 | ); 77 | 78 | Validator::extend( 79 | 'video_max_height', 80 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateVideoMaxHeight', 81 | 'The :attribute height must be than :video_max_height.' 82 | ); 83 | 84 | Validator::extend( 85 | 'video_min_width', 86 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateVideoMinWidth', 87 | 'The :attribute width must be greater than :video_min_width.' 88 | ); 89 | 90 | Validator::extend( 91 | 'video_min_height', 92 | 'MinuteOfLaravel\MediaValidator\MediaValidator@validateVideoMinHeight', 93 | 'The :attribute height must be greater than :video_min_height.' 94 | ); 95 | } 96 | 97 | public static function replaceMessages() { 98 | Validator::replacer('codec', function ($message, $attribute, $rule, $parameters) { 99 | return str_replace(':' . $rule, implode($parameters), $message); 100 | }); 101 | 102 | Validator::replacer('duration', function ($message, $attribute, $rule, $parameters) { 103 | return str_replace(':' . $rule, $parameters[0], $message); 104 | }); 105 | 106 | Validator::replacer('duration_max', function ($message, $attribute, $rule, $parameters) { 107 | return str_replace(':' . $rule, $parameters[0], $message); 108 | }); 109 | 110 | Validator::replacer('duration_min', function ($message, $attribute, $rule, $parameters) { 111 | return str_replace(':' . $rule, $parameters[0], $message); 112 | }); 113 | 114 | Validator::replacer('video_width', function ($message, $attribute, $rule, $parameters) { 115 | return str_replace(':' . $rule, $parameters[0], $message); 116 | }); 117 | 118 | Validator::replacer('video_height', function ($message, $attribute, $rule, $parameters) { 119 | return str_replace(':' . $rule, $parameters[0], $message); 120 | }); 121 | 122 | Validator::replacer('video_max_width', function ($message, $attribute, $rule, $parameters) { 123 | return str_replace(':' . $rule, $parameters[0], $message); 124 | }); 125 | 126 | Validator::replacer('video_max_height', function ($message, $attribute, $rule, $parameters) { 127 | return str_replace(':' . $rule, $parameters[0], $message); 128 | }); 129 | 130 | Validator::replacer('video_min_width', function ($message, $attribute, $rule, $parameters) { 131 | return str_replace(':' . $rule, $parameters[0], $message); 132 | }); 133 | 134 | Validator::replacer('video_min_height', function ($message, $attribute, $rule, $parameters) { 135 | return str_replace(':' . $rule, $parameters[0], $message); 136 | }); 137 | } 138 | 139 | public function validateAudio(string $attribute, $value): bool 140 | { 141 | return $this->isAudio($value); 142 | } 143 | 144 | public function validateVideo(string $attribute, $value): bool 145 | { 146 | return $this->isVideo($value); 147 | } 148 | 149 | 150 | public function validateCodec(string $attribute, $value, array $parameters): bool 151 | { 152 | $this->requireParameterCount(1, $parameters, 'codec'); 153 | 154 | if ($this->isAudio($value)) { 155 | $codecName = $this->getAudioStream()->get('codec_name'); 156 | 157 | return in_array($codecName, $parameters); 158 | } 159 | 160 | if ($this->isVideo($value)) { 161 | $codecName = $this->getVideoStream()->get('codec_name'); 162 | 163 | return in_array($codecName, $parameters); 164 | } 165 | 166 | return true; 167 | } 168 | 169 | public function validateDuration(string $attribute, $value, array $parameters): bool 170 | { 171 | if (!$this->isAudio($value) && !$this->isVideo($value)) return true; 172 | 173 | $this->requireParameterCount(1, $parameters, 'duration'); 174 | $duration = $this->getMediaDuration($value); 175 | 176 | if (!$duration) return false; 177 | 178 | return $duration == $parameters[0]; 179 | } 180 | 181 | public function validateDurationMax(string $attribute, $value, array $parameters): bool 182 | { 183 | if (!$this->isAudio($value) && !$this->isVideo($value)) return true; 184 | 185 | $this->requireParameterCount(1, $parameters, 'duration_max'); 186 | $duration = $this->getMediaDuration($value); 187 | 188 | if (!$duration) return false; 189 | 190 | return $duration <= $parameters[0]; 191 | } 192 | 193 | public function validateDurationMin(string $attribute, $value, array $parameters): bool 194 | { 195 | if (!$this->isAudio($value) && !$this->isVideo($value)) return true; 196 | 197 | $this->requireParameterCount(1, $parameters, 'duration_min'); 198 | $duration = $this->getMediaDuration($value); 199 | 200 | if (!$duration) return false; 201 | 202 | return $duration >= $parameters[0]; 203 | } 204 | 205 | public function validateVideoWidth(string $attribute, $value, array $parameters): bool 206 | { 207 | if (!$this->isVideo($value) ) return true; 208 | 209 | $this->requireParameterCount(1, $parameters, 'video_width'); 210 | $dimensions = $this->getMediaDimensions($value); 211 | 212 | if (!$dimensions) return false; 213 | 214 | return $dimensions['width'] == $parameters[0]; 215 | } 216 | 217 | public function validateVideoHeight(string $attribute, $value, array $parameters): bool 218 | { 219 | if (!$this->isVideo($value) ) return true; 220 | 221 | $this->requireParameterCount(1, $parameters, 'video_height'); 222 | $dimensions = $this->getMediaDimensions($value); 223 | 224 | if (!$dimensions) return false; 225 | 226 | return $dimensions['height'] == $parameters[0]; 227 | } 228 | 229 | public function validateVideoMaxWidth(string $attribute, $value, array $parameters): bool 230 | { 231 | if (!$this->isVideo($value) ) return true; 232 | 233 | $this->requireParameterCount(1, $parameters, 'video_max_width'); 234 | $dimensions = $this->getMediaDimensions($value); 235 | 236 | if (!$dimensions) return false; 237 | 238 | return $dimensions['width'] <= $parameters[0]; 239 | } 240 | 241 | public function validateVideoMaxHeight(string $attribute, $value, array $parameters): bool 242 | { 243 | if (!$this->isVideo($value) ) return true; 244 | 245 | $this->requireParameterCount(1, $parameters, 'video_max_height'); 246 | $dimensions = $this->getMediaDimensions($value); 247 | 248 | if (!$dimensions) return false; 249 | 250 | return $dimensions['height'] <= $parameters[0]; 251 | } 252 | 253 | public function validateVideoMinWidth(string $attribute, $value, array $parameters): bool 254 | { 255 | if (!$this->isVideo($value) ) return true; 256 | 257 | $this->requireParameterCount(1, $parameters, 'video_min_width'); 258 | $dimensions = $this->getMediaDimensions($value); 259 | 260 | if (!$dimensions) return false; 261 | 262 | return $dimensions['width'] >= $parameters[0]; 263 | } 264 | 265 | public function validateVideoMinHeight(string $attribute, $value, array $parameters): bool 266 | { 267 | if (!$this->isVideo($value) ) return true; 268 | 269 | $this->requireParameterCount(1, $parameters, 'video_min_height'); 270 | $dimensions = $this->getMediaDimensions($value); 271 | 272 | if (!$dimensions) return false; 273 | 274 | return $dimensions['height'] >= $parameters[0]; 275 | } 276 | 277 | } 278 | --------------------------------------------------------------------------------