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 |