| Total Complexity | 69 |
| Total Lines | 305 |
| Duplicated Lines | 0 % |
| Changes | 2 | ||
| Bugs | 1 | Features | 0 |
Complex classes like MediaProcessingService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use MediaProcessingService, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 23 | class MediaProcessingService |
||
| 24 | { |
||
| 25 | public function __construct( |
||
| 26 | private readonly FFMpeg $ffmpeg, |
||
| 27 | private readonly FFProbe $ffprobe, |
||
| 28 | private readonly MediaInfo $mediaInfo, |
||
| 29 | private readonly ReleaseImage $releaseImage, |
||
| 30 | private readonly ReleaseExtra $releaseExtra, |
||
| 31 | private readonly ManticoreSearch $manticore, |
||
| 32 | private readonly ElasticSearchSiteSearch $elasticsearch, |
||
| 33 | private readonly Categorize $categorize, |
||
| 34 | ) {} |
||
| 35 | |||
| 36 | public function getVideoTime(string $videoLocation): string |
||
| 37 | { |
||
| 38 | $time = null; |
||
| 39 | try { |
||
| 40 | if ($this->ffprobe->isValid($videoLocation)) { |
||
| 41 | $val = $this->ffprobe->format($videoLocation)->get('duration'); |
||
| 42 | if (is_string($val) || is_numeric($val)) { |
||
| 43 | $time = (string) $val; |
||
| 44 | } |
||
| 45 | } |
||
| 46 | } catch (\Throwable $e) { |
||
| 47 | if (config('app.debug') === true) { |
||
| 48 | Log::debug($e->getMessage()); |
||
| 49 | } |
||
| 50 | } |
||
| 51 | |||
| 52 | if (empty($time)) { |
||
| 53 | return ''; |
||
| 54 | } |
||
| 55 | |||
| 56 | // Case 1: matches ffmpeg log style `time=.. bitrate=` (optionally with hours) |
||
| 57 | if (preg_match('/time=(\d{1,2}:\d{1,2}:)?(\d{1,2})\.(\d{1,2})\s*bitrate=/i', $time, $numbers)) { |
||
| 58 | if ($numbers[3] > 0) { |
||
| 59 | $numbers[3]--; |
||
| 60 | } elseif (! empty($numbers[1])) { |
||
| 61 | $numbers[2]--; |
||
| 62 | $numbers[3] = '99'; |
||
| 63 | } |
||
| 64 | |||
| 65 | return '00:00:'.str_pad((string) $numbers[2], 2, '0', STR_PAD_LEFT).'.'.str_pad((string) $numbers[3], 2, '0', STR_PAD_LEFT); |
||
| 66 | } |
||
| 67 | |||
| 68 | // Case 1b: matches `time=MM:SS.xx` (without trailing bitrate) |
||
| 69 | if (preg_match('/time=(\d{2}):(\d{2})\.(\d{2})/i', $time, $m)) { |
||
| 70 | $sec = (int) $m[2]; |
||
| 71 | $hund = (int) $m[3]; |
||
| 72 | if ($hund > 0) { |
||
| 73 | $hund--; |
||
| 74 | } else { |
||
| 75 | if ($sec > 0) { |
||
| 76 | $sec--; |
||
| 77 | $hund = 99; |
||
| 78 | } |
||
| 79 | } |
||
| 80 | |||
| 81 | return '00:00:'.str_pad((string) $sec, 2, '0', STR_PAD_LEFT).'.'.str_pad((string) $hund, 2, '0', STR_PAD_LEFT); |
||
| 82 | } |
||
| 83 | |||
| 84 | // Case 2: numeric seconds |
||
| 85 | if (is_numeric($time)) { |
||
| 86 | $seconds = (float) $time; |
||
| 87 | if ($seconds <= 0) { |
||
| 88 | return ''; |
||
| 89 | } |
||
| 90 | $seconds = max(0.0, $seconds - 0.01); |
||
| 91 | $whole = (int) floor($seconds); |
||
| 92 | $hund = (int) round(($seconds - $whole) * 100); |
||
| 93 | $hund = min($hund, 99); |
||
| 94 | |||
| 95 | return '00:00:'.str_pad((string) $whole, 2, '0', STR_PAD_LEFT).'.'.str_pad((string) $hund, 2, '0', STR_PAD_LEFT); |
||
| 96 | } |
||
| 97 | |||
| 98 | return ''; |
||
| 99 | } |
||
| 100 | |||
| 101 | public function saveJPGSample(string $guid, string $fileLocation): bool |
||
| 102 | { |
||
| 103 | $saved = $this->releaseImage->saveImage( |
||
| 104 | $guid.'_thumb', |
||
| 105 | $fileLocation, |
||
| 106 | $this->releaseImage->jpgSavePath, |
||
| 107 | 650, |
||
| 108 | 650 |
||
| 109 | ) === 1; |
||
| 110 | if ($saved) { |
||
| 111 | Release::query()->where('guid', $guid)->update(['jpgstatus' => 1]); |
||
| 112 | } |
||
| 113 | |||
| 114 | return $saved; |
||
| 115 | } |
||
| 116 | |||
| 117 | public function createSampleImage(string $guid, string $fileLocation, string $tmpPath, bool $enabled, int $width = 800, int $height = 600): bool |
||
| 118 | { |
||
| 119 | if (! $enabled) { |
||
| 120 | return false; |
||
| 121 | } |
||
| 122 | if (! File::isFile($fileLocation)) { |
||
| 123 | return false; |
||
| 124 | } |
||
| 125 | $fileName = ($tmpPath.'zzzz'.random_int(5, 12).random_int(5, 12).'.jpg'); |
||
| 126 | $time = $this->getVideoTime($fileLocation); |
||
| 127 | if ($this->ffprobe->isValid($fileLocation)) { |
||
| 128 | try { |
||
| 129 | $this->ffmpeg->open($fileLocation) |
||
| 130 | ->frame(TimeCode::fromString($time === '' ? '00:00:03:00' : $time)) |
||
|
|
|||
| 131 | ->save($fileName); |
||
| 132 | } catch (\Throwable $e) { |
||
| 133 | if (config('app.debug') === true) { |
||
| 134 | Log::error($e->getMessage()); |
||
| 135 | } |
||
| 136 | } |
||
| 137 | } |
||
| 138 | if (! File::isFile($fileName)) { |
||
| 139 | return false; |
||
| 140 | } |
||
| 141 | $saved = $this->releaseImage->saveImage( |
||
| 142 | $guid.'_thumb', |
||
| 143 | $fileName, |
||
| 144 | $this->releaseImage->imgSavePath, |
||
| 145 | $width, |
||
| 146 | $height |
||
| 147 | ); |
||
| 148 | File::delete($fileName); |
||
| 149 | if ($saved === 1) { |
||
| 150 | return true; |
||
| 151 | } |
||
| 152 | |||
| 153 | return false; |
||
| 154 | } |
||
| 155 | |||
| 156 | public function createVideoSample(string $guid, string $fileLocation, string $tmpPath, bool $enabled, int $durationSeconds): bool |
||
| 157 | { |
||
| 158 | if (! $enabled) { |
||
| 159 | return false; |
||
| 160 | } |
||
| 161 | if (! File::isFile($fileLocation)) { |
||
| 162 | return false; |
||
| 163 | } |
||
| 164 | $fileName = ($tmpPath.'zzzz'.$guid.'.ogv'); |
||
| 165 | $newMethod = false; |
||
| 166 | if ($durationSeconds < 60) { |
||
| 167 | $time = $this->getVideoTime($fileLocation); |
||
| 168 | if ($time !== '' && preg_match('/(\d{2}).(\d{2})/', $time, $numbers)) { |
||
| 169 | $newMethod = true; |
||
| 170 | if ($numbers[1] <= $durationSeconds) { |
||
| 171 | $lowestLength = '00:00:00.00'; |
||
| 172 | } else { |
||
| 173 | $lowestLength = ($numbers[1] - $durationSeconds); |
||
| 174 | $end = '.'.$numbers[2]; |
||
| 175 | $lowestLength = match (strlen((string) $lowestLength)) { |
||
| 176 | 1 => ('00:00:0'.$lowestLength.$end), |
||
| 177 | 2 => ('00:00:'.$lowestLength.$end), |
||
| 178 | default => '00:00:60.00', |
||
| 179 | }; |
||
| 180 | } |
||
| 181 | if ($this->ffprobe->isValid($fileLocation)) { |
||
| 182 | try { |
||
| 183 | $video = $this->ffmpeg->open($fileLocation); |
||
| 184 | $videoSample = $video->clip(TimeCode::fromString($lowestLength), TimeCode::fromSeconds($durationSeconds)); |
||
| 185 | $format = new Ogg; |
||
| 186 | $format->setAudioCodec('libvorbis'); |
||
| 187 | $videoSample->filters()->resize(new Dimension(320, -1), ResizeFilter::RESIZEMODE_SCALE_HEIGHT); |
||
| 188 | $videoSample->save($format, $fileName); |
||
| 189 | } catch (\Throwable $e) { |
||
| 190 | if (config('app.debug') === true) { |
||
| 191 | Log::error($e->getMessage()); |
||
| 192 | } |
||
| 193 | } |
||
| 194 | } |
||
| 195 | } |
||
| 196 | } |
||
| 197 | if (! $newMethod && $this->ffprobe->isValid($fileLocation)) { |
||
| 198 | try { |
||
| 199 | $video = $this->ffmpeg->open($fileLocation); |
||
| 200 | $videoSample = $video->clip(TimeCode::fromSeconds(0), TimeCode::fromSeconds($durationSeconds)); |
||
| 201 | $format = new Ogg; |
||
| 202 | $format->setAudioCodec('libvorbis'); |
||
| 203 | $videoSample->filters()->resize(new Dimension(320, -1), ResizeFilter::RESIZEMODE_SCALE_HEIGHT); |
||
| 204 | $videoSample->save($format, $fileName); |
||
| 205 | } catch (\Throwable $e) { |
||
| 206 | if (config('app.debug') === true) { |
||
| 207 | Log::error($e->getMessage()); |
||
| 208 | } |
||
| 209 | } |
||
| 210 | } |
||
| 211 | if (! File::isFile($fileName)) { |
||
| 212 | return false; |
||
| 213 | } |
||
| 214 | $newFile = ($this->releaseImage->vidSavePath.$guid.'.ogv'); |
||
| 215 | if (! @File::move($fileName, $newFile)) { |
||
| 216 | $copied = @File::copy($fileName, $newFile); |
||
| 217 | File::delete($fileName); |
||
| 218 | if (! $copied) { |
||
| 219 | return false; |
||
| 220 | } |
||
| 221 | } |
||
| 222 | @chmod($newFile, 0764); |
||
| 223 | Release::query()->where('guid', $guid)->update(['videostatus' => 1]); |
||
| 224 | |||
| 225 | return true; |
||
| 226 | } |
||
| 227 | |||
| 228 | public function addVideoMediaInfo(int $releaseId, string $fileLocation): bool |
||
| 243 | } |
||
| 244 | } |
||
| 245 | |||
| 246 | public function addAudioInfoAndSample( |
||
| 247 | Release $release, |
||
| 328 | } |
||
| 329 | } |
||
| 330 |