Total Complexity | 386 |
Total Lines | 2381 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like ProcessAdditional 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 ProcessAdditional, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
25 | class ProcessAdditional |
||
26 | { |
||
27 | /** |
||
28 | * How many compressed (rar/zip) files to check. |
||
29 | * @int |
||
30 | * @default 20 |
||
31 | */ |
||
32 | public const maxCompressedFilesToCheck = 20; |
||
33 | |||
34 | /** |
||
35 | * @var \Blacklight\db\DB |
||
36 | */ |
||
37 | public $pdo; |
||
38 | |||
39 | /** |
||
40 | * @var bool |
||
41 | */ |
||
42 | protected $_echoDebug; |
||
43 | |||
44 | /** |
||
45 | * Releases to work on. |
||
46 | * @var array |
||
47 | */ |
||
48 | protected $_releases; |
||
49 | |||
50 | /** |
||
51 | * Count of releases to work on. |
||
52 | * @var int |
||
53 | */ |
||
54 | protected $_totalReleases; |
||
55 | |||
56 | /** |
||
57 | * Current release we are working on. |
||
58 | * @var array |
||
59 | */ |
||
60 | protected $_release; |
||
61 | |||
62 | /** |
||
63 | * @var \Blacklight\NZB |
||
64 | */ |
||
65 | protected $_nzb; |
||
66 | |||
67 | /** |
||
68 | * List of files with sizes/etc contained in the NZB. |
||
69 | * @var array |
||
70 | */ |
||
71 | protected $_nzbContents; |
||
72 | |||
73 | /** |
||
74 | * @var \dariusiii\rarinfo\Par2Info |
||
75 | */ |
||
76 | protected $_par2Info; |
||
77 | |||
78 | /** |
||
79 | * @var \dariusiii\rarinfo\ArchiveInfo |
||
80 | */ |
||
81 | protected $_archiveInfo; |
||
82 | |||
83 | /** |
||
84 | * @var array|bool|string |
||
85 | */ |
||
86 | protected $_innerFileBlacklist; |
||
87 | |||
88 | /** |
||
89 | * @var array|bool|int|string |
||
90 | */ |
||
91 | protected $_maxNestedLevels; |
||
92 | |||
93 | /** |
||
94 | * @var array|bool|string |
||
95 | */ |
||
96 | protected $_7zipPath; |
||
97 | |||
98 | /** |
||
99 | * @var array|bool|string |
||
100 | */ |
||
101 | protected $_unrarPath; |
||
102 | |||
103 | /** |
||
104 | * @var string |
||
105 | */ |
||
106 | protected $_killString; |
||
107 | |||
108 | /** |
||
109 | * @var bool|string |
||
110 | */ |
||
111 | protected $_showCLIReleaseID; |
||
112 | |||
113 | /** |
||
114 | * @var int |
||
115 | */ |
||
116 | protected $_queryLimit; |
||
117 | |||
118 | /** |
||
119 | * @var int |
||
120 | */ |
||
121 | protected $_segmentsToDownload; |
||
122 | |||
123 | /** |
||
124 | * @var int |
||
125 | */ |
||
126 | protected $_maximumRarSegments; |
||
127 | |||
128 | /** |
||
129 | * @var int |
||
130 | */ |
||
131 | protected $_maximumRarPasswordChecks; |
||
132 | |||
133 | /** |
||
134 | * @var string |
||
135 | */ |
||
136 | protected $_maxSize; |
||
137 | |||
138 | /** |
||
139 | * @var string |
||
140 | */ |
||
141 | protected $_minSize; |
||
142 | |||
143 | /** |
||
144 | * @var bool |
||
145 | */ |
||
146 | protected $_processThumbnails; |
||
147 | |||
148 | /** |
||
149 | * @var string |
||
150 | */ |
||
151 | protected $_audioSavePath; |
||
152 | |||
153 | /** |
||
154 | * @var string |
||
155 | */ |
||
156 | protected $_supportFileRegex; |
||
157 | |||
158 | /** |
||
159 | * @var bool |
||
160 | */ |
||
161 | protected $_echoCLI; |
||
162 | |||
163 | /** |
||
164 | * @var \Blacklight\NNTP |
||
165 | */ |
||
166 | protected $_nntp; |
||
167 | |||
168 | /** |
||
169 | * @var \Blacklight\Categorize |
||
170 | */ |
||
171 | protected $_categorize; |
||
172 | |||
173 | /** |
||
174 | * @var \Blacklight\NameFixer |
||
175 | */ |
||
176 | protected $_nameFixer; |
||
177 | |||
178 | /** |
||
179 | * @var \Blacklight\ReleaseExtra |
||
180 | */ |
||
181 | protected $_releaseExtra; |
||
182 | |||
183 | /** |
||
184 | * @var \Blacklight\ReleaseImage |
||
185 | */ |
||
186 | protected $_releaseImage; |
||
187 | |||
188 | /** |
||
189 | * @var \Blacklight\Nfo |
||
190 | */ |
||
191 | protected $_nfo; |
||
192 | |||
193 | /** |
||
194 | * @var bool |
||
195 | */ |
||
196 | protected $_extractUsingRarInfo; |
||
197 | |||
198 | /** |
||
199 | * @var bool |
||
200 | */ |
||
201 | protected $_alternateNNTP; |
||
202 | |||
203 | /** |
||
204 | * @var int |
||
205 | */ |
||
206 | protected $_ffMPEGDuration; |
||
207 | |||
208 | /** |
||
209 | * @var bool |
||
210 | */ |
||
211 | protected $_addPAR2Files; |
||
212 | |||
213 | /** |
||
214 | * @var bool |
||
215 | */ |
||
216 | protected $_processVideo; |
||
217 | |||
218 | /** |
||
219 | * @var bool |
||
220 | */ |
||
221 | protected $_processJPGSample; |
||
222 | |||
223 | /** |
||
224 | * @var bool |
||
225 | */ |
||
226 | protected $_processAudioSample; |
||
227 | |||
228 | /** |
||
229 | * @var bool |
||
230 | */ |
||
231 | protected $_processMediaInfo; |
||
232 | |||
233 | /** |
||
234 | * @var bool |
||
235 | */ |
||
236 | protected $_processAudioInfo; |
||
237 | |||
238 | /** |
||
239 | * @var bool |
||
240 | */ |
||
241 | protected $_processPasswords; |
||
242 | |||
243 | /** |
||
244 | * @var string |
||
245 | */ |
||
246 | protected $_audioFileRegex; |
||
247 | |||
248 | /** |
||
249 | * @var string |
||
250 | */ |
||
251 | protected $_ignoreBookRegex; |
||
252 | |||
253 | /** |
||
254 | * @var string |
||
255 | */ |
||
256 | protected $_videoFileRegex; |
||
257 | |||
258 | /** |
||
259 | * Have we created a video file for the current release? |
||
260 | * @var bool |
||
261 | */ |
||
262 | protected $_foundVideo; |
||
263 | |||
264 | /** |
||
265 | * Have we found MediaInfo data for a Video for the current release? |
||
266 | * @var bool |
||
267 | */ |
||
268 | protected $_foundMediaInfo; |
||
269 | |||
270 | /** |
||
271 | * Have we found MediaInfo data for a Audio file for the current release? |
||
272 | * @var bool |
||
273 | */ |
||
274 | protected $_foundAudioInfo; |
||
275 | |||
276 | /** |
||
277 | * Have we created a short Audio file sample for the current release? |
||
278 | * @var bool |
||
279 | */ |
||
280 | protected $_foundAudioSample; |
||
281 | |||
282 | /** |
||
283 | * Extension of the found audio file (MP3/FLAC/etc). |
||
284 | * @var string |
||
285 | */ |
||
286 | protected $_AudioInfoExtension; |
||
287 | |||
288 | /** |
||
289 | * Have we downloaded a JPG file for the current release? |
||
290 | * @var bool |
||
291 | */ |
||
292 | protected $_foundJPGSample; |
||
293 | |||
294 | /** |
||
295 | * Have we created a Video JPG image sample for the current release? |
||
296 | * @var bool |
||
297 | */ |
||
298 | protected $_foundSample; |
||
299 | |||
300 | /** |
||
301 | * Have we found PAR2 info on this release? |
||
302 | * @var bool |
||
303 | */ |
||
304 | protected $_foundPAR2Info; |
||
305 | |||
306 | /** |
||
307 | * Message ID's for found content to download. |
||
308 | * @var array |
||
309 | */ |
||
310 | protected $_sampleMessageIDs; |
||
311 | protected $_JPGMessageIDs; |
||
312 | protected $_MediaInfoMessageIDs; |
||
313 | protected $_AudioInfoMessageIDs; |
||
314 | protected $_RARFileMessageIDs; |
||
315 | |||
316 | /** |
||
317 | * Password status of the current release. |
||
318 | * @var array |
||
319 | */ |
||
320 | protected $_passwordStatus; |
||
321 | |||
322 | /** |
||
323 | * Does the current release have a password? |
||
324 | * @var bool |
||
325 | */ |
||
326 | protected $_releaseHasPassword; |
||
327 | |||
328 | /** |
||
329 | * Does the current release have an NFO file? |
||
330 | * @var bool |
||
331 | */ |
||
332 | protected $_releaseHasNoNFO; |
||
333 | |||
334 | /** |
||
335 | * Name of the current release's usenet group. |
||
336 | * @var string |
||
337 | */ |
||
338 | protected $_releaseGroupName; |
||
339 | |||
340 | /** |
||
341 | * Number of file information added to DB (from rar/zip/par2 contents). |
||
342 | * @var int |
||
343 | */ |
||
344 | protected $_addedFileInfo; |
||
345 | |||
346 | /** |
||
347 | * Number of file information we found from RAR/ZIP. |
||
348 | * (if some of it was already in DB, this count goes up, while the count above does not). |
||
349 | * @var int |
||
350 | */ |
||
351 | protected $_totalFileInfo; |
||
352 | |||
353 | /** |
||
354 | * How many compressed (rar/zip) files have we checked. |
||
355 | * @var int |
||
356 | */ |
||
357 | protected $_compressedFilesChecked; |
||
358 | |||
359 | /** |
||
360 | * Should we download the last rar? |
||
361 | * @var bool |
||
362 | */ |
||
363 | protected $_fetchLastFiles; |
||
364 | |||
365 | /** |
||
366 | * Are we downloading the last rar? |
||
367 | * @var bool |
||
368 | */ |
||
369 | protected $_reverse; |
||
370 | |||
371 | /** |
||
372 | * ProcessAdditional constructor. |
||
373 | * |
||
374 | * @param array $options |
||
375 | * @throws \Exception |
||
376 | */ |
||
377 | public function __construct(array $options = []) |
||
378 | { |
||
379 | $defaults = [ |
||
380 | 'Echo' => false, |
||
381 | 'Categorize' => null, |
||
382 | 'Groups' => null, |
||
383 | 'NameFixer' => null, |
||
384 | 'Nfo' => null, |
||
385 | 'NNTP' => null, |
||
386 | 'NZB' => null, |
||
387 | 'ReleaseExtra' => null, |
||
388 | 'ReleaseImage' => null, |
||
389 | 'Settings' => null, |
||
390 | 'SphinxSearch' => null, |
||
391 | ]; |
||
392 | $options += $defaults; |
||
393 | |||
394 | $this->_echoCLI = ($options['Echo'] && config('nntmux.echocli') && (strtolower(PHP_SAPI) === 'cli')); |
||
395 | |||
396 | $this->pdo = ($options['Settings'] instanceof DB ? $options['Settings'] : new DB()); |
||
397 | $this->_nntp = ($options['NNTP'] instanceof NNTP ? $options['NNTP'] : new NNTP(['Echo' => $this->_echoCLI, 'Settings' => $this->pdo])); |
||
398 | |||
399 | $this->_nzb = ($options['NZB'] instanceof NZB ? $options['NZB'] : new NZB()); |
||
400 | $this->_archiveInfo = new ArchiveInfo(); |
||
401 | $this->_categorize = ($options['Categorize'] instanceof Categorize ? $options['Categorize'] : new Categorize(['Settings' => $this->pdo])); |
||
402 | $this->_nameFixer = ($options['NameFixer'] instanceof NameFixer ? $options['NameFixer'] : new NameFixer(['Echo' =>$this->_echoCLI, 'Groups' => null, 'Settings' => $this->pdo, 'Categorize' => $this->_categorize])); |
||
403 | $this->_releaseExtra = ($options['ReleaseExtra'] instanceof ReleaseExtra ? $options['ReleaseExtra'] : new ReleaseExtra($this->pdo)); |
||
|
|||
404 | $this->_releaseImage = ($options['ReleaseImage'] instanceof ReleaseImage ? $options['ReleaseImage'] : new ReleaseImage()); |
||
405 | $this->_par2Info = new Par2Info(); |
||
406 | $this->_nfo = ($options['Nfo'] instanceof Nfo ? $options['Nfo'] : new Nfo()); |
||
407 | $this->sphinx = ($options['SphinxSearch'] instanceof SphinxSearch ? $options['SphinxSearch'] : new SphinxSearch()); |
||
408 | |||
409 | $this->_innerFileBlacklist = Settings::settingValue('indexer.ppa.innerfileblacklist') === '' ? false : Settings::settingValue('indexer.ppa.innerfileblacklist'); |
||
410 | $this->_maxNestedLevels = (int) Settings::settingValue('..maxnestedlevels') === 0 ? 3 : (int) Settings::settingValue('..maxnestedlevels'); |
||
411 | $this->_extractUsingRarInfo = (int) Settings::settingValue('..extractusingrarinfo') !== 0; |
||
412 | $this->_fetchLastFiles = (int) Settings::settingValue('archive.fetch.end') !== 0; |
||
413 | |||
414 | $this->_7zipPath = false; |
||
415 | $this->_unrarPath = false; |
||
416 | |||
417 | // Pass the binary extractors to ArchiveInfo. |
||
418 | $clients = []; |
||
419 | if (Settings::settingValue('apps..unrarpath') !== '') { |
||
420 | $clients += [ArchiveInfo::TYPE_RAR => Settings::settingValue('apps..unrarpath')]; |
||
421 | $this->_unrarPath = Settings::settingValue('apps..unrarpath'); |
||
422 | } |
||
423 | if (Settings::settingValue('apps..zippath') !== '') { |
||
424 | $clients += [ArchiveInfo::TYPE_ZIP => Settings::settingValue('apps..zippath')]; |
||
425 | $this->_7zipPath = Settings::settingValue('apps..zippath'); |
||
426 | } |
||
427 | $this->_archiveInfo->setExternalClients($clients); |
||
428 | |||
429 | $this->_killString = '"'; |
||
430 | if (Settings::settingValue('apps..timeoutpath') !== '' && (int) Settings::settingValue('..timeoutseconds') > 0) { |
||
431 | $this->_killString = ( |
||
432 | '"'.Settings::settingValue('apps..timeoutpath'). |
||
433 | '" --foreground --signal=KILL '. |
||
434 | Settings::settingValue('..timeoutseconds').' "' |
||
435 | ); |
||
436 | } |
||
437 | |||
438 | $this->_showCLIReleaseID = (PHP_BINARY.' '.__DIR__.DS.'ProcessAdditional.php ReleaseID: '); |
||
439 | |||
440 | // Maximum amount of releases to fetch per run. |
||
441 | $this->_queryLimit = |
||
442 | (Settings::settingValue('..maxaddprocessed') !== '') ? (int) Settings::settingValue('..maxaddprocessed') : 25; |
||
443 | |||
444 | // Maximum message ID's to download per file type in the NZB (video, jpg, etc). |
||
445 | $this->_segmentsToDownload = |
||
446 | (Settings::settingValue('..segmentstodownload') !== '') ? (int) Settings::settingValue('..segmentstodownload') : 2; |
||
447 | |||
448 | // Maximum message ID's to download for a RAR file. |
||
449 | $this->_maximumRarSegments = |
||
450 | (Settings::settingValue('..maxpartsprocessed') !== '') ? (int) Settings::settingValue('..maxpartsprocessed') : 3; |
||
451 | |||
452 | // Maximum RAR files to check for a password before stopping. |
||
453 | $this->_maximumRarPasswordChecks = |
||
454 | (Settings::settingValue('..passchkattempts') !== '') ? (int) Settings::settingValue('..passchkattempts') : 1; |
||
455 | |||
456 | $this->_maximumRarPasswordChecks = ($this->_maximumRarPasswordChecks < 1 ? 1 : $this->_maximumRarPasswordChecks); |
||
457 | |||
458 | // Maximum size of releases in GB. |
||
459 | $this->_maxSize = |
||
460 | (Settings::settingValue('..maxsizetopostprocess') !== '') ? (int) Settings::settingValue('..maxsizetopostprocess') : 100; |
||
461 | $this->_maxSize = ($this->_maxSize > 0 ? ('AND r.size < '.($this->_maxSize * 1073741824)) : ''); |
||
462 | // Minimum size of releases in MB. |
||
463 | $this->_minSize = |
||
464 | (Settings::settingValue('..minsizetopostprocess') !== '') ? (int) Settings::settingValue('..minsizetopostprocess') : 100; |
||
465 | $this->_minSize = ($this->_minSize > 0 ? ('AND r.size > '.($this->_minSize * 1048576)) : ''); |
||
466 | |||
467 | // Use the alternate NNTP provider for downloading Message-ID's ? |
||
468 | $this->_alternateNNTP = (int) Settings::settingValue('..alternate_nntp') === 1; |
||
469 | |||
470 | $this->_ffMPEGDuration = Settings::settingValue('..ffmpeg_duration') !== '' ? (int) Settings::settingValue('..ffmpeg_duration') : 5; |
||
471 | |||
472 | $this->_addPAR2Files = (int) Settings::settingValue('..addpar2') !== 0; |
||
473 | |||
474 | if (! Settings::settingValue('apps..ffmpegpath')) { |
||
475 | $this->_processAudioSample = $this->_processThumbnails = $this->_processVideo = false; |
||
476 | } else { |
||
477 | $this->_processAudioSample = (int) Settings::settingValue('..saveaudiopreview') !== 0; |
||
478 | $this->_processThumbnails = (int) Settings::settingValue('..processthumbnails') !== 0; |
||
479 | $this->_processVideo = (int) Settings::settingValue('..processvideos') !== 0; |
||
480 | } |
||
481 | |||
482 | $this->_processJPGSample = (int) Settings::settingValue('..processjpg') !== 0; |
||
483 | $this->_processMediaInfo = Settings::settingValue('apps..mediainfopath') !== ''; |
||
484 | $this->_processAudioInfo = $this->_processMediaInfo; |
||
485 | $this->_processPasswords = ( |
||
486 | ((int) Settings::settingValue('..checkpasswordedrar') !== 0) && |
||
487 | Settings::settingValue('apps..unrarpath') !== '' |
||
488 | ); |
||
489 | |||
490 | $this->_audioSavePath = NN_COVERS.'audiosample'.DS; |
||
491 | |||
492 | $this->_audioFileRegex = '\.(AAC|AIFF|APE|AC3|ASF|DTS|FLAC|MKA|MKS|MP2|MP3|RA|OGG|OGM|W64|WAV|WMA)'; |
||
493 | $this->_ignoreBookRegex = '/\b(epub|lit|mobi|pdf|sipdf|html)\b.*\.rar(?!.{20,})/i'; |
||
494 | $this->_supportFileRegex = '/\.(vol\d{1,3}\+\d{1,3}|par2|srs|sfv|nzb'; |
||
495 | $this->_videoFileRegex = '\.(AVI|F4V|IFO|M1V|M2V|M4V|MKV|MOV|MP4|MPEG|MPG|MPGV|MPV|OGV|QT|RM|RMVB|TS|VOB|WMV)'; |
||
496 | } |
||
497 | |||
498 | /** |
||
499 | * Clear out the main temp path when done. |
||
500 | */ |
||
501 | public function __destruct() |
||
502 | { |
||
503 | $this->_clearMainTmpPath(); |
||
504 | } |
||
505 | |||
506 | /** |
||
507 | * @param string $groupID |
||
508 | * @param string $guidChar |
||
509 | * @throws \RuntimeException |
||
510 | * @throws \Blacklight\processing\post\ProcessAdditionalException |
||
511 | * @throws \Exception |
||
512 | */ |
||
513 | public function start($groupID = '', $guidChar = '') |
||
526 | } |
||
527 | } |
||
528 | |||
529 | /** |
||
530 | * @var string Main temp path to work on. |
||
531 | */ |
||
532 | protected $_mainTmpPath; |
||
533 | |||
534 | /** |
||
535 | * @var string Temp path for current release. |
||
536 | */ |
||
537 | protected $tmpPath; |
||
538 | |||
539 | /** |
||
540 | * @param $guidChar |
||
541 | * @param string $groupID |
||
542 | * @throws \RuntimeException |
||
543 | * @throws \Exception |
||
544 | * @throws \Blacklight\processing\post\ProcessAdditionalException |
||
545 | */ |
||
546 | protected function _setMainTempPath(&$guidChar, &$groupID = '') |
||
547 | { |
||
548 | // Set up the temporary files folder location. |
||
549 | $this->_mainTmpPath = (string) Settings::settingValue('..tmpunrarpath'); |
||
550 | |||
551 | // Check if it ends with a dir separator. |
||
552 | if (! preg_match('/[\/\\\\]$/', $this->_mainTmpPath)) { |
||
553 | $this->_mainTmpPath .= DS; |
||
554 | } |
||
555 | |||
556 | // If we are doing per group, use the groupID has a inner path, so other scripts don't delete the files we are working on. |
||
557 | if ($groupID !== '') { |
||
558 | $this->_mainTmpPath .= ($groupID.DS); |
||
559 | } elseif ($guidChar !== '') { |
||
560 | $this->_mainTmpPath .= ($guidChar.DS); |
||
561 | } |
||
562 | |||
563 | if (! is_dir($this->_mainTmpPath)) { |
||
564 | $old = umask(0777); |
||
565 | if (! mkdir($this->_mainTmpPath) && ! is_dir($this->_mainTmpPath)) { |
||
566 | throw new \RuntimeException(sprintf('Directory "%s" was not created', $this->_mainTmpPath)); |
||
567 | } |
||
568 | @chmod($this->_mainTmpPath, 0777); |
||
569 | @umask($old); |
||
570 | } |
||
571 | |||
572 | if (! is_dir($this->_mainTmpPath)) { |
||
573 | throw new ProcessAdditionalException('Could not create the tmpunrar folder ('.$this->_mainTmpPath.')'); |
||
574 | } |
||
575 | |||
576 | $this->_clearMainTmpPath(); |
||
577 | |||
578 | $this->tmpPath = $this->_mainTmpPath; |
||
579 | } |
||
580 | |||
581 | /** |
||
582 | * Clear out old folders/files from the main temp folder. |
||
583 | */ |
||
584 | protected function _clearMainTmpPath() |
||
585 | { |
||
586 | if ($this->_mainTmpPath !== '') { |
||
587 | $this->_recursivePathDelete( |
||
588 | $this->_mainTmpPath, |
||
589 | // These are folders we don't want to delete. |
||
590 | [ |
||
591 | // This is the actual temp folder. |
||
592 | $this->_mainTmpPath, |
||
593 | ] |
||
594 | ); |
||
595 | } |
||
596 | } |
||
597 | |||
598 | /** |
||
599 | * Get all releases that need to be processed. |
||
600 | * |
||
601 | * @param int|string $groupID |
||
602 | * @param string $guidChar |
||
603 | * |
||
604 | * @void |
||
605 | */ |
||
606 | protected function _fetchReleases($groupID, &$guidChar) |
||
607 | { |
||
608 | $this->_releases = $this->pdo->query( |
||
609 | sprintf( |
||
610 | ' |
||
611 | SELECT r.id, r.id AS releases_id, r.guid, r.name, r.size, r.groups_id, r.nfostatus, |
||
612 | r.fromname, r.completion, r.categories_id, r.searchname, r.predb_id, |
||
613 | c.disablepreview |
||
614 | FROM releases r |
||
615 | LEFT JOIN categories c ON c.id = r.categories_id |
||
616 | WHERE r.nzbstatus = 1 |
||
617 | %s %s %s %s |
||
618 | AND r.passwordstatus BETWEEN -6 AND -1 |
||
619 | AND r.haspreview = -1 |
||
620 | AND c.disablepreview = 0 |
||
621 | ORDER BY r.passwordstatus ASC, r.postdate DESC |
||
622 | LIMIT %d', |
||
623 | $this->_maxSize, |
||
624 | $this->_minSize, |
||
625 | ($groupID === '' ? '' : 'AND r.groups_id = '.$groupID), |
||
626 | ($guidChar === '' ? '' : 'AND r.leftguid = '.$this->pdo->escapeString($guidChar)), |
||
627 | $this->_queryLimit |
||
628 | ) |
||
629 | ); |
||
630 | |||
631 | if (\is_array($this->_releases)) { |
||
632 | $this->_totalReleases = \count($this->_releases); |
||
633 | } else { |
||
634 | $this->_releases = []; |
||
635 | $this->_totalReleases = 0; |
||
636 | } |
||
637 | } |
||
638 | |||
639 | /** |
||
640 | * Output the description and start time. |
||
641 | * |
||
642 | * @void |
||
643 | */ |
||
644 | protected function _echoDescription() |
||
645 | { |
||
646 | if ($this->_totalReleases > 1 && $this->_echoCLI) { |
||
647 | $this->_echo( |
||
648 | PHP_EOL. |
||
649 | 'Additional post-processing, started at: '. |
||
650 | date('D M d, Y G:i a'). |
||
651 | PHP_EOL. |
||
652 | 'Downloaded: (xB) = yEnc article, f= Failed ;Processing: z = ZIP file, r = RAR file'. |
||
653 | PHP_EOL. |
||
654 | 'Added: s = Sample image, j = JPEG image, A = Audio sample, a = Audio MediaInfo, v = Video sample'. |
||
655 | PHP_EOL. |
||
656 | 'Added: m = Video MediaInfo, n = NFO, ^ = File details from inside the RAR/ZIP', |
||
657 | 'header' |
||
658 | ); |
||
659 | } |
||
660 | } |
||
661 | |||
662 | /** |
||
663 | * Loop through the releases, processing them 1 at a time. |
||
664 | * |
||
665 | * @throws \RuntimeException |
||
666 | * @throws \Exception |
||
667 | */ |
||
668 | protected function _processReleases() |
||
669 | { |
||
670 | foreach ($this->_releases as $this->_release) { |
||
671 | $this->_echo( |
||
672 | PHP_EOL.'['.$this->_release['id'].']['. |
||
673 | $this->_readableBytesString($this->_release['size']).']', |
||
674 | 'primaryOver', |
||
675 | false |
||
676 | ); |
||
677 | |||
678 | cli_set_process_title($this->_showCLIReleaseID.$this->_release['id']); |
||
679 | |||
680 | // Create folder to store temporary files. |
||
681 | if ($this->_createTempFolder() === false) { |
||
682 | continue; |
||
683 | } |
||
684 | |||
685 | // Get NZB contents. |
||
686 | if ($this->_getNZBContents() === false) { |
||
687 | continue; |
||
688 | } |
||
689 | |||
690 | // Reset the current release variables. |
||
691 | $this->_resetReleaseStatus(); |
||
692 | |||
693 | // Go through the files in the NZB, get the amount of book files. |
||
694 | $totalBooks = $this->_processNZBContents(); |
||
695 | |||
696 | // Check if this NZB is a large collection of books. |
||
697 | $bookFlood = false; |
||
698 | if ($totalBooks > 80 && ($totalBooks * 2) >= \count($this->_nzbContents)) { |
||
699 | $bookFlood = true; |
||
700 | } |
||
701 | |||
702 | if ($this->_processPasswords === true || |
||
703 | $this->_processThumbnails === true || |
||
704 | $this->_processMediaInfo === true || |
||
705 | $this->_processAudioInfo === true || |
||
706 | $this->_processVideo === true |
||
707 | ) { |
||
708 | |||
709 | // Process usenet Message-ID downloads. |
||
710 | $this->_processMessageIDDownloads(); |
||
711 | |||
712 | // Process compressed (RAR/ZIP) files inside the NZB. |
||
713 | if ($bookFlood === false && $this->_NZBHasCompressedFile) { |
||
714 | // Download the RARs/ZIPs, extract the files inside them and insert the file info into the DB. |
||
715 | $this->_processNZBCompressedFiles(); |
||
716 | |||
717 | // Download rar/zip in reverse order, to get the last rar or zip file. |
||
718 | if ($this->_fetchLastFiles === true) { |
||
719 | $this->_processNZBCompressedFiles(true); |
||
720 | } |
||
721 | |||
722 | if ($this->_releaseHasPassword === false) { |
||
723 | // Process the extracted files to get video/audio samples/etc. |
||
724 | $this->_processExtractedFiles(); |
||
725 | } |
||
726 | } |
||
727 | } |
||
728 | |||
729 | // Update the release to say we processed it. |
||
730 | $this->_finalizeRelease(); |
||
731 | |||
732 | // Delete all files / folders for this release. |
||
733 | $this->_recursivePathDelete($this->tmpPath); |
||
734 | } |
||
735 | if ($this->_echoCLI) { |
||
736 | echo PHP_EOL; |
||
737 | } |
||
738 | } |
||
739 | |||
740 | /** |
||
741 | * Deletes files and folders recursively. |
||
742 | * |
||
743 | * @param string $path Path to a folder or file. |
||
744 | * @param string[] $ignoredFolders array with paths to folders to ignore. |
||
745 | * |
||
746 | * @void |
||
747 | */ |
||
748 | protected function _recursivePathDelete($path, $ignoredFolders = []) |
||
749 | { |
||
750 | if (is_dir($path)) { |
||
751 | $files = glob(rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.'*'); |
||
752 | |||
753 | foreach ($files as $file) { |
||
754 | $this->_recursivePathDelete($file, $ignoredFolders); |
||
755 | } |
||
756 | |||
757 | if (\in_array($path, $ignoredFolders, false)) { |
||
758 | return; |
||
759 | } |
||
760 | |||
761 | @rmdir($path); |
||
762 | } elseif (is_file($path)) { |
||
763 | @unlink($path); |
||
764 | } |
||
765 | } |
||
766 | |||
767 | /** |
||
768 | * Create a temporary storage folder for the current release. |
||
769 | * |
||
770 | * @return bool |
||
771 | * @throws \RuntimeException |
||
772 | */ |
||
773 | protected function _createTempFolder() |
||
774 | { |
||
775 | // Per release defaults. |
||
776 | $this->tmpPath = $this->_mainTmpPath.$this->_release['guid'].DS; |
||
777 | if (! is_dir($this->tmpPath)) { |
||
778 | $old = umask(0777); |
||
779 | if (! mkdir($this->tmpPath) && ! is_dir($this->tmpPath)) { |
||
780 | throw new \RuntimeException(sprintf('Directory "%s" was not created', $this->tmpPath)); |
||
781 | } |
||
782 | @chmod($this->tmpPath, 0777); |
||
783 | @umask($old); |
||
784 | |||
785 | if (! \is_dir($this->tmpPath)) { |
||
786 | $this->_echo('Unable to create directory: '.$this->tmpPath, 'warning'); |
||
787 | |||
788 | return $this->_decrementPasswordStatus(); |
||
789 | } |
||
790 | } |
||
791 | |||
792 | return true; |
||
793 | } |
||
794 | |||
795 | /** |
||
796 | * Get list of contents inside a release's NZB file. |
||
797 | * |
||
798 | * @return bool |
||
799 | */ |
||
800 | protected function _getNZBContents() |
||
801 | { |
||
802 | $nzbPath = $this->_nzb->NZBPath($this->_release['guid']); |
||
803 | if ($nzbPath === false) { |
||
804 | $this->_echo('NZB not found for GUID: '.$this->_release['guid'], 'warning'); |
||
805 | |||
806 | return $this->_decrementPasswordStatus(); |
||
807 | } |
||
808 | |||
809 | $nzbContents = Utility::unzipGzipFile($nzbPath); |
||
810 | if (! $nzbContents) { |
||
811 | $this->_echo('NZB is empty or broken for GUID: '.$this->_release['guid'], 'warning'); |
||
812 | |||
813 | return $this->_decrementPasswordStatus(); |
||
814 | } |
||
815 | |||
816 | // Get a list of files in the nzb. |
||
817 | $this->_nzbContents = $this->_nzb->nzbFileList($nzbContents, ['no-file-key' => false, 'strip-count' => true]); |
||
818 | if (\count($this->_nzbContents) === 0) { |
||
819 | $this->_echo('NZB is potentially broken for GUID: '.$this->_release['guid'], 'warning'); |
||
820 | |||
821 | return $this->_decrementPasswordStatus(); |
||
822 | } |
||
823 | // Sort keys. |
||
824 | ksort($this->_nzbContents, SORT_NATURAL); |
||
825 | |||
826 | return true; |
||
827 | } |
||
828 | |||
829 | /** |
||
830 | * Decrement password status for the current release. |
||
831 | * |
||
832 | * @param bool $return Return value. |
||
833 | * |
||
834 | * @return bool |
||
835 | */ |
||
836 | protected function _decrementPasswordStatus($return = false) |
||
837 | { |
||
838 | Release::query()->where('id', $this->_release['id'])->decrement('passwordstatus'); |
||
839 | |||
840 | return $return; |
||
841 | } |
||
842 | |||
843 | /** |
||
844 | * Current file we are working on inside a NZB. |
||
845 | * @var array |
||
846 | */ |
||
847 | protected $_currentNZBFile; |
||
848 | |||
849 | /** |
||
850 | * Does the current NZB contain a compressed (RAR/ZIP) file? |
||
851 | * @var bool |
||
852 | */ |
||
853 | protected $_NZBHasCompressedFile; |
||
854 | |||
855 | /** |
||
856 | * Process the files inside the NZB, find Message-ID's to download. |
||
857 | * If we find files with book extensions, return the amount. |
||
858 | * |
||
859 | * @return int |
||
860 | */ |
||
861 | protected function _processNZBContents() |
||
862 | { |
||
863 | $totalBookFiles = 0; |
||
864 | foreach ($this->_nzbContents as $this->_currentNZBFile) { |
||
865 | |||
866 | // Check if it's not a nfo, nzb, par2 etc... |
||
867 | if (preg_match($this->_supportFileRegex.'|nfo\b|inf\b|ofn\b)($|[ ")\]-])(?!.{20,})/i', $this->_currentNZBFile['title'])) { |
||
868 | continue; |
||
869 | } |
||
870 | |||
871 | // Check if it's a rar/zip. |
||
872 | if ($this->_NZBHasCompressedFile === false && |
||
873 | preg_match( |
||
874 | '/\.(part\d+|r\d+|rar|0+|0*10?|zipr\d{2,3}|zipx?)(\s*\.rar)*($|[ ")\]-])|"[a-f0-9]{32}\.[1-9]\d{1,2}".*\(\d+\/\d{2,}\)$/i', |
||
875 | $this->_currentNZBFile['title'] |
||
876 | ) |
||
877 | ) { |
||
878 | $this->_NZBHasCompressedFile = true; |
||
879 | } |
||
880 | |||
881 | // Look for a video sample, make sure it's not an image. |
||
882 | if ($this->_processThumbnails === true && |
||
883 | empty($this->_sampleMessageIDs) && stripos($this->_currentNZBFile['title'], 'sample') !== false && |
||
884 | ! preg_match('/\.jpe?g/i', $this->_currentNZBFile['title']) |
||
885 | ) { |
||
886 | if (isset($this->_currentNZBFile['segments'])) { |
||
887 | // Get the amount of segments for this file. |
||
888 | $segCount = (\count($this->_currentNZBFile['segments']) - 1); |
||
889 | // If it's more than 1 try to get up to the site specified value of segments. |
||
890 | for ($i = 0; $i < $this->_segmentsToDownload; $i++) { |
||
891 | if ($i > $segCount) { |
||
892 | break; |
||
893 | } |
||
894 | $this->_sampleMessageIDs[] = (string) $this->_currentNZBFile['segments'][$i]; |
||
895 | } |
||
896 | } |
||
897 | } |
||
898 | |||
899 | // Look for a JPG picture, make sure it's not a CD cover. |
||
900 | if ($this->_processJPGSample === true && |
||
901 | empty($this->_JPGMessageIDs) && |
||
902 | ! preg_match('/flac|lossless|mp3|music|inner-sanctum|sound/i', $this->_releaseGroupName) && |
||
903 | preg_match('/\.jpe?g[. ")\]]/i', $this->_currentNZBFile['title']) |
||
904 | ) { |
||
905 | if (isset($this->_currentNZBFile['segments'])) { |
||
906 | // Get the amount of segments for this file. |
||
907 | $segCount = (\count($this->_currentNZBFile['segments']) - 1); |
||
908 | // If it's more than 1 try to get up to the site specified value of segments. |
||
909 | for ($i = 0; $i < $this->_segmentsToDownload; $i++) { |
||
910 | if ($i > $segCount) { |
||
911 | break; |
||
912 | } |
||
913 | $this->_JPGMessageIDs[] = (string) $this->_currentNZBFile['segments'][$i]; |
||
914 | } |
||
915 | } |
||
916 | } |
||
917 | |||
918 | // Look for a video file, make sure it's not a sample, for MediaInfo. |
||
919 | if ($this->_processMediaInfo === true && |
||
920 | empty($this->_MediaInfoMessageIDs) && |
||
921 | ! stripos($this->_currentNZBFile['title'], 'sample') !== false && |
||
922 | preg_match('/'.$this->_videoFileRegex.'[. ")\]]/i', $this->_currentNZBFile['title']) |
||
923 | ) { |
||
924 | if (isset($this->_currentNZBFile['segments'][0])) { |
||
925 | $this->_MediaInfoMessageIDs = (string) $this->_currentNZBFile['segments'][0]; |
||
926 | } |
||
927 | } |
||
928 | |||
929 | // Look for a audio file. |
||
930 | if ($this->_processAudioInfo === true && |
||
931 | empty($this->_AudioInfoMessageIDs) && |
||
932 | preg_match('/'.$this->_audioFileRegex.'[. ")\]]/i', $this->_currentNZBFile['title'], $type) |
||
933 | ) { |
||
934 | if (isset($this->_currentNZBFile['segments'])) { |
||
935 | // Get the extension. |
||
936 | $this->_AudioInfoExtension = $type[1]; |
||
937 | $this->_AudioInfoMessageIDs = (string) $this->_currentNZBFile['segments'][0]; |
||
938 | } |
||
939 | } |
||
940 | |||
941 | // Some releases contain many books, increment this to ignore them later. |
||
942 | if (preg_match($this->_ignoreBookRegex, $this->_currentNZBFile['title'])) { |
||
943 | $totalBookFiles++; |
||
944 | } |
||
945 | } |
||
946 | |||
947 | return $totalBookFiles; |
||
948 | } |
||
949 | |||
950 | /** |
||
951 | * List of message-id's we have tried for rar/zip files. |
||
952 | * @var array |
||
953 | */ |
||
954 | protected $_triedCompressedMids = []; |
||
955 | |||
956 | /** |
||
957 | * @param bool $reverse |
||
958 | * @throws \Exception |
||
959 | */ |
||
960 | protected function _processNZBCompressedFiles($reverse = false) |
||
961 | { |
||
962 | $this->_reverse = $reverse; |
||
963 | |||
964 | if ($this->_reverse) { |
||
965 | if (! krsort($this->_nzbContents)) { |
||
966 | return; |
||
967 | } |
||
968 | } else { |
||
969 | $this->_triedCompressedMids = []; |
||
970 | } |
||
971 | |||
972 | $failed = $downloaded = 0; |
||
973 | // Loop through the files, attempt to find if password-ed and files. Starting with what not to process. |
||
974 | foreach ($this->_nzbContents as $nzbFile) { |
||
975 | // TODO change this to max calculated size, as segments vary in size greatly. |
||
976 | if ($downloaded >= $this->_maximumRarSegments) { |
||
977 | break; |
||
978 | } |
||
979 | |||
980 | if ($failed >= $this->_maximumRarPasswordChecks) { |
||
981 | break; |
||
982 | } |
||
983 | |||
984 | if ($this->_releaseHasPassword === true) { |
||
985 | $this->_echo('Skipping processing of rar '.$nzbFile['title'].' it has a password.', 'primaryOver', false); |
||
986 | break; |
||
987 | } |
||
988 | |||
989 | // Probably not a rar/zip. |
||
990 | if (! preg_match( |
||
991 | '/\.(part\d+|r\d+|rar|0+|0*10?|zipr\d{2,3}|zipx?)(\s*\.rar)*($|[ ")\]-])|"[a-f0-9]{32}\.[1-9]\d{1,2}".*\(\d+\/\d{2,}\)$/i', |
||
992 | $nzbFile['title'] |
||
993 | ) |
||
994 | ) { |
||
995 | continue; |
||
996 | } |
||
997 | |||
998 | // Get message-id's for the rar file. |
||
999 | $segCount = (\count($nzbFile['segments']) - 1); |
||
1000 | $mID = []; |
||
1001 | for ($i = 0; $i < $this->_maximumRarSegments; $i++) { |
||
1002 | if ($i > $segCount) { |
||
1003 | break; |
||
1004 | } |
||
1005 | $segment = (string) $nzbFile['segments'][$i]; |
||
1006 | if (! $this->_reverse) { |
||
1007 | $this->_triedCompressedMids[] = $segment; |
||
1008 | } elseif (\in_array($segment, $this->_triedCompressedMids, false)) { |
||
1009 | // We already downloaded this file. |
||
1010 | continue 2; |
||
1011 | } |
||
1012 | $mID[] = $segment; |
||
1013 | } |
||
1014 | // Nothing to download. |
||
1015 | if (empty($mID)) { |
||
1016 | continue; |
||
1017 | } |
||
1018 | |||
1019 | // Download the article(s) from usenet. |
||
1020 | $fetchedBinary = $this->_nntp->getMessages($this->_releaseGroupName, $mID, $this->_alternateNNTP); |
||
1021 | if ($this->_nntp->isError($fetchedBinary)) { |
||
1022 | $fetchedBinary = false; |
||
1023 | } |
||
1024 | |||
1025 | if ($fetchedBinary !== false) { |
||
1026 | |||
1027 | // Echo we downloaded compressed file. |
||
1028 | if ($this->_echoCLI) { |
||
1029 | $this->_echo('(cB)', 'primaryOver', false); |
||
1030 | } |
||
1031 | |||
1032 | $downloaded++; |
||
1033 | |||
1034 | // Process the compressed file. |
||
1035 | $decompressed = $this->_processCompressedData($fetchedBinary); |
||
1036 | |||
1037 | if ($decompressed === true || $this->_releaseHasPassword === true) { |
||
1038 | break; |
||
1039 | } |
||
1040 | } else { |
||
1041 | $failed++; |
||
1042 | if ($this->_echoCLI) { |
||
1043 | $this->_echo('f('.$failed.')', 'warningOver', false); |
||
1044 | } |
||
1045 | } |
||
1046 | } |
||
1047 | } |
||
1048 | |||
1049 | /** |
||
1050 | * Check if the data is a ZIP / RAR file, extract files, get file info. |
||
1051 | * |
||
1052 | * @param string $compressedData |
||
1053 | * |
||
1054 | * @return bool |
||
1055 | * @throws \Exception |
||
1056 | */ |
||
1057 | protected function _processCompressedData(&$compressedData) |
||
1058 | { |
||
1059 | $this->_compressedFilesChecked++; |
||
1060 | // Give the data to archive info so it can check if it's a rar. |
||
1061 | if ($this->_archiveInfo->setData($compressedData, true) === false) { |
||
1062 | $this->_debug('Data is probably not RAR or ZIP.'.PHP_EOL); |
||
1063 | |||
1064 | return false; |
||
1065 | } |
||
1066 | |||
1067 | // Check if there's an error. |
||
1068 | if ($this->_archiveInfo->error !== '') { |
||
1069 | $this->_debug('ArchiveInfo Error: '.$this->_archiveInfo->error); |
||
1070 | |||
1071 | return false; |
||
1072 | } |
||
1073 | |||
1074 | // Get a summary of the compressed file. |
||
1075 | $dataSummary = $this->_archiveInfo->getSummary(true); |
||
1076 | |||
1077 | // Check if the compressed file is encrypted. |
||
1078 | if (! empty($this->_archiveInfo->isEncrypted) || (isset($dataSummary['is_encrypted']) && (int) $dataSummary['is_encrypted'] !== 0)) { |
||
1079 | $this->_debug('ArchiveInfo: Compressed file has a password.'); |
||
1080 | $this->_releaseHasPassword = true; |
||
1081 | $this->_passwordStatus[] = Releases::PASSWD_RAR; |
||
1082 | |||
1083 | return false; |
||
1084 | } |
||
1085 | |||
1086 | switch ($dataSummary['main_type']) { |
||
1087 | case ArchiveInfo::TYPE_RAR: |
||
1088 | if ($this->_echoCLI) { |
||
1089 | $this->_echo('r', 'primaryOver', false); |
||
1090 | } |
||
1091 | |||
1092 | if ($this->_extractUsingRarInfo === false && $this->_unrarPath !== false) { |
||
1093 | $fileName = $this->tmpPath.uniqid('', true).'.rar'; |
||
1094 | file_put_contents($fileName, $compressedData); |
||
1095 | Utility::runCmd( |
||
1096 | $this->_killString.$this->_unrarPath. |
||
1097 | '" e -ai -ep -c- -id -inul -kb -or -p- -r -y "'. |
||
1098 | $fileName.'" "'.$this->tmpPath.'unrar/"' |
||
1099 | ); |
||
1100 | unlink($fileName); |
||
1101 | } |
||
1102 | break; |
||
1103 | case ArchiveInfo::TYPE_ZIP: |
||
1104 | if ($this->_echoCLI) { |
||
1105 | $this->_echo('z', 'primaryOver', false); |
||
1106 | } |
||
1107 | |||
1108 | if ($this->_extractUsingRarInfo === false && $this->_7zipPath !== false) { |
||
1109 | $fileName = $this->tmpPath.uniqid('', true).'.zip'; |
||
1110 | file_put_contents($fileName, $compressedData); |
||
1111 | Utility::runCmd( |
||
1112 | $this->_killString.$this->_7zipPath.'" x "'. |
||
1113 | $fileName.'" -bd -y -o"'.$this->tmpPath.'unzip/"' |
||
1114 | ); |
||
1115 | unlink($fileName); |
||
1116 | } |
||
1117 | break; |
||
1118 | default: |
||
1119 | return false; |
||
1120 | } |
||
1121 | |||
1122 | return $this->_processCompressedFileList(); |
||
1123 | } |
||
1124 | |||
1125 | /** |
||
1126 | * Get a list of all files in the compressed file, add the file info to the DB. |
||
1127 | * |
||
1128 | * @return bool |
||
1129 | * @throws \Exception |
||
1130 | */ |
||
1131 | protected function _processCompressedFileList(): bool |
||
1132 | { |
||
1133 | // Get a list of files inside the Compressed file. |
||
1134 | $files = $this->_archiveInfo->getArchiveFileList(); |
||
1135 | if (! \is_array($files) || \count($files) === 0) { |
||
1136 | return false; |
||
1137 | } |
||
1138 | |||
1139 | // Loop through the files. |
||
1140 | foreach ($files as $file) { |
||
1141 | if ($this->_releaseHasPassword === true) { |
||
1142 | break; |
||
1143 | } |
||
1144 | |||
1145 | if (isset($file['name'])) { |
||
1146 | if (isset($file['error'])) { |
||
1147 | $this->_debug("Error: {$file['error']} (in: {$file['source']})"); |
||
1148 | continue; |
||
1149 | } |
||
1150 | |||
1151 | if (isset($file['pass']) && $file['pass'] === true) { |
||
1152 | $this->_releaseHasPassword = true; |
||
1153 | $this->_passwordStatus[] = Releases::PASSWD_RAR; |
||
1154 | break; |
||
1155 | } |
||
1156 | |||
1157 | if ($this->_innerFileBlacklist !== false && preg_match($this->_innerFileBlacklist, $file['name'])) { |
||
1158 | $this->_releaseHasPassword = true; |
||
1159 | $this->_passwordStatus[] = Releases::PASSWD_POTENTIAL; |
||
1160 | break; |
||
1161 | } |
||
1162 | |||
1163 | $fileName = []; |
||
1164 | if (preg_match('/[^\/\\\\]*\.[a-zA-Z0-9]*$/', $file['name'], $fileName)) { |
||
1165 | $fileName = $fileName[0]; |
||
1166 | } else { |
||
1167 | $fileName = ''; |
||
1168 | } |
||
1169 | |||
1170 | if ($this->_extractUsingRarInfo === true) { |
||
1171 | // Extract files from the rar. |
||
1172 | if (isset($file['compressed']) && (int) $file['compressed'] === 0) { |
||
1173 | @file_put_contents( |
||
1174 | $this->tmpPath.random_int(10, 999999).'_'.$fileName, |
||
1175 | $this->_archiveInfo->getFileData($file['name'], $file['source']) |
||
1176 | ); |
||
1177 | } // If the files are compressed, use a binary extractor. |
||
1178 | else { |
||
1179 | $this->_archiveInfo->extractFile($file['name'], $this->tmpPath.random_int(10, 999999).'_'.$fileName); |
||
1180 | } |
||
1181 | } |
||
1182 | } |
||
1183 | $this->_addFileInfo($file); |
||
1184 | } |
||
1185 | if ($this->_addedFileInfo > 0) { |
||
1186 | $this->sphinx->updateRelease($this->_release['id']); |
||
1187 | } |
||
1188 | |||
1189 | return $this->_totalFileInfo > 0; |
||
1190 | } |
||
1191 | |||
1192 | /** |
||
1193 | * @param $file |
||
1194 | * @throws \Exception |
||
1195 | */ |
||
1196 | protected function _addFileInfo(&$file): void |
||
1197 | { |
||
1198 | // Don't add rar/zip files to the DB. |
||
1199 | if (! isset($file['error']) && isset($file['source']) && |
||
1200 | ! preg_match($this->_supportFileRegex.'|part\d+|r\d{1,3}|zipr\d{2,3}|\d{2,3}|zipx|zip|rar)(\s*\.rar)?$/i', $file['name']) |
||
1201 | ) { |
||
1202 | |||
1203 | // Cache the amount of files we find in the RAR or ZIP, return this to say we did find RAR or ZIP content. |
||
1204 | // This is so we don't download more RAR or ZIP files for no reason. |
||
1205 | $this->_totalFileInfo++; |
||
1206 | |||
1207 | /* Check if we already have the file or not. |
||
1208 | * Also make sure we don't add too many files, some releases have 100's of files, like PS3 releases. |
||
1209 | */ |
||
1210 | if ($this->_addedFileInfo < 11 && ReleaseFile::query()->where(['releases_id' => $this->_release['id'], 'name' => $file['name'], 'size' => $file |
||
1211 | ['size'], ])->first() === null) { |
||
1212 | if (ReleaseFile::addReleaseFiles($this->_release['id'], $file['name'], '', $file['size'], $file['date'], $file['pass'])) { |
||
1213 | $this->_addedFileInfo++; |
||
1214 | |||
1215 | if ($this->_echoCLI) { |
||
1216 | $this->_echo('^', 'primaryOver', false); |
||
1217 | } |
||
1218 | |||
1219 | // Check for "codec spam" |
||
1220 | if (preg_match('/alt\.binaries\.movies($|\.divx$)/', $this->_releaseGroupName) && |
||
1221 | preg_match('/[\/\\\\]Codec[\/\\\\]Setup\.exe/i', $file['name']) |
||
1222 | ) { |
||
1223 | $this->_debug('Codec spam found, setting release to potentially passworded.'.PHP_EOL); |
||
1224 | $this->_releaseHasPassword = true; |
||
1225 | $this->_passwordStatus[] = Releases::PASSWD_POTENTIAL; |
||
1226 | } //Run a PreDB filename check on insert to try and match the release |
||
1227 | elseif (strpos($file['name'], '.') !== 0 && \strlen($file['name']) > 0) { |
||
1228 | $this->_release['filename'] = $file['name']; |
||
1229 | $this->_release['releases_id'] = $this->_release['id']; |
||
1230 | $this->_nameFixer->matchPredbFiles($this->_release, 1, 1, true, 1); |
||
1231 | } |
||
1232 | } |
||
1233 | } |
||
1234 | } |
||
1235 | } |
||
1236 | |||
1237 | /** |
||
1238 | * Go through all the extracted files in the temp folder and process them. |
||
1239 | * |
||
1240 | * @throws \Exception |
||
1241 | */ |
||
1242 | protected function _processExtractedFiles() |
||
1243 | { |
||
1244 | $nestedLevels = 0; |
||
1245 | |||
1246 | // Go through all the files in the temp folder, look for compressed files, extract them and the nested ones. |
||
1247 | while ($nestedLevels < $this->_maxNestedLevels) { |
||
1248 | |||
1249 | // Break out if we checked more than x compressed files. |
||
1250 | if ($this->_compressedFilesChecked >= self::maxCompressedFilesToCheck) { |
||
1251 | break; |
||
1252 | } |
||
1253 | |||
1254 | $foundCompressedFile = false; |
||
1255 | |||
1256 | // Get all the compressed files in the temp folder. |
||
1257 | $files = $this->_getTempDirectoryContents('/.*\.([rz]\d{2,}|rar|zipx?|0{0,2}1)($|[^a-z0-9])/i'); |
||
1258 | |||
1259 | if ($files instanceof \Traversable) { |
||
1260 | foreach ($files as $file) { |
||
1261 | |||
1262 | // Check if the file exists. |
||
1263 | if (is_file($file[0])) { |
||
1264 | $rarData = @file_get_contents($file[0]); |
||
1265 | if ($rarData !== false) { |
||
1266 | $this->_processCompressedData($rarData); |
||
1267 | $foundCompressedFile = true; |
||
1268 | } |
||
1269 | @unlink($file[0]); |
||
1270 | } |
||
1271 | } |
||
1272 | } |
||
1273 | |||
1274 | // If we found no compressed files, break out. |
||
1275 | if ($foundCompressedFile === false) { |
||
1276 | break; |
||
1277 | } |
||
1278 | |||
1279 | $nestedLevels++; |
||
1280 | } |
||
1281 | |||
1282 | $fileType = []; |
||
1283 | |||
1284 | // Get all the remaining files in the temp dir. |
||
1285 | $files = $this->_getTempDirectoryContents(); |
||
1286 | if ($files instanceof \Traversable) { |
||
1287 | foreach ($files as $file) { |
||
1288 | $file = (string) $file; |
||
1289 | |||
1290 | // Skip /. and /.. |
||
1291 | if (preg_match('/[\/\\\\]\.{1,2}$/', $file)) { |
||
1292 | continue; |
||
1293 | } |
||
1294 | |||
1295 | if (is_file($file)) { |
||
1296 | |||
1297 | // Process PAR2 files. |
||
1298 | if ($this->_foundPAR2Info === false && preg_match('/\.par2$/', $file)) { |
||
1299 | $this->_siftPAR2Info($file); |
||
1300 | } // Process NFO files. |
||
1301 | elseif ($this->_releaseHasNoNFO === true && preg_match('/(\.(nfo|inf|ofn)|info\.txt)$/i', $file)) { |
||
1302 | $this->_processNfoFile($file); |
||
1303 | } // Process audio files. |
||
1304 | elseif ( |
||
1305 | ($this->_foundAudioInfo === false || |
||
1306 | $this->_foundAudioSample === false) && |
||
1307 | preg_match('/(.*)'.$this->_audioFileRegex.'$/i', $file, $fileType) |
||
1308 | ) { |
||
1309 | // Try to get audio sample/audio media info. |
||
1310 | @rename($file, $this->tmpPath.'audiofile.'.$fileType[2]); |
||
1311 | $this->_getAudioInfo($this->tmpPath.'audiofile.'.$fileType[2], $fileType[2]); |
||
1312 | @unlink($this->tmpPath.'audiofile.'.$fileType[2]); |
||
1313 | } // Process JPG files. |
||
1314 | elseif ($this->_foundJPGSample === false && preg_match('/\.jpe?g$/i', $file)) { |
||
1315 | $this->_getJPGSample($file); |
||
1316 | @unlink($file); |
||
1317 | } // Video sample // video clip // video media info. |
||
1318 | elseif (($this->_foundSample === false || $this->_foundVideo === false || $this->_foundMediaInfo === false) && |
||
1319 | preg_match('/(.*)'.$this->_videoFileRegex.'$/i', $file) |
||
1320 | ) { |
||
1321 | $this->_processVideoFile($file); |
||
1322 | } |
||
1323 | |||
1324 | // Check file's magic info. |
||
1325 | else { |
||
1326 | $output = Utility::fileInfo($file); |
||
1327 | if (! empty($output)) { |
||
1328 | switch (true) { |
||
1329 | |||
1330 | case $this->_foundJPGSample === false && preg_match('/^JPE?G/i', $output): |
||
1331 | $this->_getJPGSample($file); |
||
1332 | @unlink($file); |
||
1333 | break; |
||
1334 | |||
1335 | case |
||
1336 | ($this->_foundMediaInfo === false || $this->_foundSample === false || $this->_foundVideo === false) |
||
1337 | && preg_match('/Matroska data|MPEG v4|MPEG sequence, v2|\WAVI\W/i', $output): |
||
1338 | $this->_processVideoFile($file); |
||
1339 | break; |
||
1340 | |||
1341 | case |
||
1342 | ($this->_foundAudioSample === false || $this->_foundAudioInfo === false) && |
||
1343 | preg_match('/^FLAC|layer III|Vorbis audio/i', $output, $fileType): |
||
1344 | switch ($fileType[0]) { |
||
1345 | case 'FLAC': |
||
1346 | $fileType = 'FLAC'; |
||
1347 | break; |
||
1348 | case 'layer III': |
||
1349 | $fileType = 'MP3'; |
||
1350 | break; |
||
1351 | case 'Vorbis audio': |
||
1352 | $fileType = 'OGG'; |
||
1353 | break; |
||
1354 | } |
||
1355 | @rename($file, $this->tmpPath.'audiofile.'.$fileType); |
||
1356 | $this->_getAudioInfo($this->tmpPath.'audiofile.'.$fileType, $fileType); |
||
1357 | @unlink($this->tmpPath.'audiofile.'.$fileType); |
||
1358 | break; |
||
1359 | |||
1360 | case $this->_foundPAR2Info === false && stripos($output, 'Parity') === 0: |
||
1361 | $this->_siftPAR2Info($file); |
||
1362 | break; |
||
1363 | } |
||
1364 | } |
||
1365 | } |
||
1366 | } |
||
1367 | } |
||
1368 | } |
||
1369 | } |
||
1370 | |||
1371 | /** |
||
1372 | * Download all binaries from usenet and form samples / get media info / etc from them. |
||
1373 | * |
||
1374 | * @void |
||
1375 | * @throws \Exception |
||
1376 | */ |
||
1377 | protected function _processMessageIDDownloads() |
||
1378 | { |
||
1379 | $this->_processSampleMessageIDs(); |
||
1380 | $this->_processMediaInfoMessageIDs(); |
||
1381 | $this->_processAudioInfoMessageIDs(); |
||
1382 | $this->_processJPGMessageIDs(); |
||
1383 | } |
||
1384 | |||
1385 | /** |
||
1386 | * Download and process binaries for sample videos. |
||
1387 | * |
||
1388 | * @void |
||
1389 | * @throws \Exception |
||
1390 | */ |
||
1391 | protected function _processSampleMessageIDs() |
||
1392 | { |
||
1393 | // Download and process sample image. |
||
1394 | if ($this->_foundSample === false || $this->_foundVideo === false) { |
||
1395 | if (! empty($this->_sampleMessageIDs)) { |
||
1396 | |||
1397 | // Download it from usenet. |
||
1398 | $sampleBinary = $this->_nntp->getMessages($this->_releaseGroupName, $this->_sampleMessageIDs, $this->_alternateNNTP); |
||
1399 | if ($this->_nntp->isError($sampleBinary)) { |
||
1400 | $sampleBinary = false; |
||
1401 | } |
||
1402 | |||
1403 | if ($sampleBinary !== false) { |
||
1404 | if ($this->_echoCLI) { |
||
1405 | $this->_echo('(sB)', 'primaryOver', false); |
||
1406 | } |
||
1407 | |||
1408 | // Check if it's more than 40 bytes. |
||
1409 | if (\strlen($sampleBinary) > 40) { |
||
1410 | $fileLocation = $this->tmpPath.'sample_'.random_int(0, 99999).'.avi'; |
||
1411 | // Try to create the file. |
||
1412 | @file_put_contents($fileLocation, $sampleBinary); |
||
1413 | |||
1414 | // Try to get a sample picture. |
||
1415 | if ($this->_foundSample === false) { |
||
1416 | $this->_foundSample = $this->_getSample($fileLocation); |
||
1417 | } |
||
1418 | |||
1419 | // Try to get a sample video. |
||
1420 | if ($this->_foundVideo === false) { |
||
1421 | $this->_foundVideo = $this->_getVideo($fileLocation); |
||
1422 | } |
||
1423 | |||
1424 | // Try to get media info. Don't get it here if $mediaMsgID is not empty. |
||
1425 | // 2014-06-28 -> Commented out, since the media info of a sample video is not indicative of the actual release.si |
||
1426 | /*if ($this->_foundMediaInfo === false && empty($mediaMsgID)) { |
||
1427 | $this->_foundMediaInfo = $this->_getMediaInfo($fileLocation); |
||
1428 | }*/ |
||
1429 | } |
||
1430 | } elseif ($this->_echoCLI) { |
||
1431 | $this->_echo('f', 'warningOver', false); |
||
1432 | } |
||
1433 | } |
||
1434 | } |
||
1435 | } |
||
1436 | |||
1437 | /** |
||
1438 | * Download and process binaries for media info from videos. |
||
1439 | * |
||
1440 | * @void |
||
1441 | * @throws \Exception |
||
1442 | */ |
||
1443 | protected function _processMediaInfoMessageIDs() |
||
1444 | { |
||
1445 | // Download and process mediainfo. Also try to get a sample if we didn't get one yet. |
||
1446 | if ($this->_foundMediaInfo === false || $this->_foundSample === false || $this->_foundVideo === false) { |
||
1447 | if ($this->_foundMediaInfo === false && ! empty($this->_MediaInfoMessageIDs)) { |
||
1448 | |||
1449 | // Try to download it from usenet. |
||
1450 | $mediaBinary = $this->_nntp->getMessages($this->_releaseGroupName, $this->_MediaInfoMessageIDs, $this->_alternateNNTP); |
||
1451 | if ($this->_nntp->isError($mediaBinary)) { |
||
1452 | // If error set it to false. |
||
1453 | $mediaBinary = false; |
||
1454 | } |
||
1455 | |||
1456 | if ($mediaBinary !== false) { |
||
1457 | if ($this->_echoCLI) { |
||
1458 | $this->_echo('(mB)', 'primaryOver', false); |
||
1459 | } |
||
1460 | |||
1461 | // If it's more than 40 bytes... |
||
1462 | if (\strlen($mediaBinary) > 40) { |
||
1463 | $fileLocation = $this->tmpPath.'media.avi'; |
||
1464 | // Create a file on the disk with it. |
||
1465 | @file_put_contents($fileLocation, $mediaBinary); |
||
1466 | |||
1467 | // Try to get media info. |
||
1468 | if ($this->_foundMediaInfo === false) { |
||
1469 | $this->_foundMediaInfo = $this->_getMediaInfo($fileLocation); |
||
1470 | } |
||
1471 | |||
1472 | // Try to get a sample picture. |
||
1473 | if ($this->_foundSample === false) { |
||
1474 | $this->_foundSample = $this->_getSample($fileLocation); |
||
1475 | } |
||
1476 | |||
1477 | // Try to get a sample video. |
||
1478 | if ($this->_foundVideo === false) { |
||
1479 | $this->_foundVideo = $this->_getVideo($fileLocation); |
||
1480 | } |
||
1481 | } |
||
1482 | } elseif ($this->_echoCLI) { |
||
1483 | $this->_echo('f', 'warningOver', false); |
||
1484 | } |
||
1485 | } |
||
1486 | } |
||
1487 | } |
||
1488 | |||
1489 | /** |
||
1490 | * Download and process binaries for media info from songs. |
||
1491 | * |
||
1492 | * @void |
||
1493 | * @throws \Exception |
||
1494 | */ |
||
1495 | protected function _processAudioInfoMessageIDs() |
||
1496 | { |
||
1497 | // Download audio file, use media info to try to get the artist / album. |
||
1498 | if ($this->_foundAudioInfo === false || $this->_foundAudioSample === false) { |
||
1499 | if (! empty($this->_AudioInfoMessageIDs)) { |
||
1500 | // Try to download it from usenet. |
||
1501 | $audioBinary = $this->_nntp->getMessages($this->_releaseGroupName, $this->_AudioInfoMessageIDs, $this->_alternateNNTP); |
||
1502 | if ($this->_nntp->isError($audioBinary)) { |
||
1503 | $audioBinary = false; |
||
1504 | } |
||
1505 | |||
1506 | if ($audioBinary !== false) { |
||
1507 | if ($this->_echoCLI) { |
||
1508 | $this->_echo('(aB)', 'primaryOver', false); |
||
1509 | } |
||
1510 | |||
1511 | $fileLocation = $this->tmpPath.'audio.'.$this->_AudioInfoExtension; |
||
1512 | // Create a file with it. |
||
1513 | @file_put_contents($fileLocation, $audioBinary); |
||
1514 | |||
1515 | // Try to get media info / sample of the audio file. |
||
1516 | $this->_getAudioInfo($fileLocation, $this->_AudioInfoExtension); |
||
1517 | } elseif ($this->_echoCLI) { |
||
1518 | $this->_echo('f', 'warningOver', false); |
||
1519 | } |
||
1520 | } |
||
1521 | } |
||
1522 | } |
||
1523 | |||
1524 | /** |
||
1525 | * Download and process binaries for JPG pictures. |
||
1526 | * |
||
1527 | * @void |
||
1528 | * @throws \Exception |
||
1529 | */ |
||
1530 | protected function _processJPGMessageIDs() |
||
1531 | { |
||
1532 | // Download JPG file. |
||
1533 | if ($this->_foundJPGSample === false && ! empty($this->_JPGMessageIDs)) { |
||
1534 | |||
1535 | // Try to download it. |
||
1536 | $jpgBinary = $this->_nntp->getMessages($this->_releaseGroupName, $this->_JPGMessageIDs, $this->_alternateNNTP); |
||
1537 | if ($this->_nntp->isError($jpgBinary)) { |
||
1538 | $jpgBinary = false; |
||
1539 | } |
||
1540 | |||
1541 | if ($jpgBinary !== false) { |
||
1542 | if ($this->_echoCLI) { |
||
1543 | $this->_echo('(jB)', 'primaryOver', false); |
||
1544 | } |
||
1545 | |||
1546 | // Try to create a file with it. |
||
1547 | @file_put_contents($this->tmpPath.'samplepicture.jpg', $jpgBinary); |
||
1548 | |||
1549 | // Try to resize and move it. |
||
1550 | $this->_foundJPGSample = ( |
||
1551 | $this->_releaseImage->saveImage( |
||
1552 | $this->_release['guid'].'_thumb', |
||
1553 | $this->tmpPath.'samplepicture.jpg', |
||
1554 | $this->_releaseImage->jpgSavePath, |
||
1555 | 650, |
||
1556 | 650 |
||
1557 | ) === 1 |
||
1558 | ); |
||
1559 | |||
1560 | if ($this->_foundJPGSample !== false) { |
||
1561 | // Update the DB to say we got it. |
||
1562 | Release::query()->where('id', $this->_release['id'])->update(['jpgstatus' => 1]); |
||
1563 | |||
1564 | if ($this->_echoCLI) { |
||
1565 | $this->_echo('j', 'primaryOver', false); |
||
1566 | } |
||
1567 | } |
||
1568 | |||
1569 | @unlink($this->tmpPath.'samplepicture.jpg'); |
||
1570 | } elseif ($this->_echoCLI) { |
||
1571 | $this->_echo('f', 'warningOver', false); |
||
1572 | } |
||
1573 | } |
||
1574 | } |
||
1575 | |||
1576 | /** |
||
1577 | * Update the release to say we processed it. |
||
1578 | */ |
||
1579 | protected function _finalizeRelease() |
||
1580 | { |
||
1581 | $vSQL = $jSQL = ''; |
||
1582 | $iSQL = ', haspreview = 0'; |
||
1583 | |||
1584 | // If samples exist from previous runs, set flags. |
||
1585 | if (is_file($this->_releaseImage->imgSavePath.$this->_release['guid'].'_thumb.jpg')) { |
||
1586 | $iSQL = ', haspreview = 1'; |
||
1587 | } |
||
1588 | |||
1589 | if (is_file($this->_releaseImage->vidSavePath.$this->_release['guid'].'.ogv')) { |
||
1590 | $vSQL = ', videostatus = 1'; |
||
1591 | } |
||
1592 | |||
1593 | if (is_file($this->_releaseImage->jpgSavePath.$this->_release['guid'].'_thumb.jpg')) { |
||
1594 | $jSQL = ', jpgstatus = 1'; |
||
1595 | } |
||
1596 | |||
1597 | // Get the amount of files we found inside the RAR/ZIP files. |
||
1598 | |||
1599 | $releaseFilesCount = ReleaseFile::query()->where('releases_id', $this->_release['id'])->count('releases_id'); |
||
1600 | |||
1601 | if ($releaseFilesCount === null) { |
||
1602 | $releaseFilesCount = 0; |
||
1603 | } |
||
1604 | |||
1605 | $this->_passwordStatus = max($this->_passwordStatus); |
||
1606 | |||
1607 | // Set the release to no password if password processing is off. |
||
1608 | if ($this->_processPasswords === false) { |
||
1609 | $this->_releaseHasPassword = false; |
||
1610 | } |
||
1611 | |||
1612 | // If we failed to get anything from the RAR/ZIPs, decrement the passwordstatus, if the rar/zip has no password. |
||
1613 | if ($this->_releaseHasPassword === false && $this->_NZBHasCompressedFile && $releaseFilesCount === 0) { |
||
1614 | $query = sprintf( |
||
1615 | 'UPDATE releases |
||
1616 | SET passwordstatus = passwordstatus - 1, rarinnerfilecount = %d %s %s %s |
||
1617 | WHERE id = %d', |
||
1618 | $releaseFilesCount, |
||
1619 | $iSQL, |
||
1620 | $vSQL, |
||
1621 | $jSQL, |
||
1622 | $this->_release['id'] |
||
1623 | ); |
||
1624 | } // Else update the release with the password status (if the admin enabled the setting). |
||
1625 | else { |
||
1626 | $query = sprintf( |
||
1627 | 'UPDATE releases |
||
1628 | SET passwordstatus = %d, rarinnerfilecount = %d %s %s %s |
||
1629 | WHERE id = %d', |
||
1630 | ($this->_processPasswords === true ? $this->_passwordStatus : Releases::PASSWD_NONE), |
||
1631 | $releaseFilesCount, |
||
1632 | $iSQL, |
||
1633 | $vSQL, |
||
1634 | $jSQL, |
||
1635 | $this->_release['id'] |
||
1636 | ); |
||
1637 | } |
||
1638 | |||
1639 | $this->pdo->queryExec($query); |
||
1640 | } |
||
1641 | |||
1642 | /** |
||
1643 | * @param string $pattern |
||
1644 | * @param string $path |
||
1645 | * @return bool|\RecursiveIteratorIterator|\RegexIterator |
||
1646 | */ |
||
1647 | protected function _getTempDirectoryContents($pattern = '', $path = '') |
||
1648 | { |
||
1649 | if ($path === '') { |
||
1650 | $path = $this->tmpPath; |
||
1651 | } |
||
1652 | try { |
||
1653 | if ($pattern !== '') { |
||
1654 | return new \RegexIterator( |
||
1655 | new \RecursiveIteratorIterator( |
||
1656 | new \RecursiveDirectoryIterator($path) |
||
1657 | ), |
||
1658 | $pattern, |
||
1659 | \RecursiveRegexIterator::GET_MATCH |
||
1660 | ); |
||
1661 | } else { |
||
1662 | return new \RecursiveIteratorIterator( |
||
1663 | new \RecursiveDirectoryIterator($path) |
||
1664 | ); |
||
1665 | } |
||
1666 | } catch (\Exception $e) { |
||
1667 | $this->_debug('ERROR: Could not open temp dir: '.$e->getMessage().PHP_EOL); |
||
1668 | |||
1669 | return false; |
||
1670 | } |
||
1671 | } |
||
1672 | |||
1673 | /** |
||
1674 | * @param $fileLocation |
||
1675 | * @param $fileExtension |
||
1676 | * @return bool |
||
1677 | * @throws \Exception |
||
1678 | */ |
||
1679 | protected function _getAudioInfo($fileLocation, $fileExtension) |
||
1680 | { |
||
1681 | // Return values. |
||
1682 | $retVal = $audVal = false; |
||
1683 | |||
1684 | // Check if audio sample fetching is on. |
||
1685 | if ($this->_processAudioSample === false) { |
||
1686 | $audVal = true; |
||
1687 | } |
||
1688 | |||
1689 | // Check if media info fetching is on. |
||
1690 | if ($this->_processAudioInfo === false) { |
||
1691 | $retVal = true; |
||
1692 | } |
||
1693 | |||
1694 | // Make sure the category is music or other. |
||
1695 | $rQuery = $this->pdo->queryOneRow( |
||
1696 | sprintf( |
||
1697 | 'SELECT searchname, fromname, categories_id AS id, groups_id FROM releases WHERE proc_pp = 0 AND id = %d', |
||
1698 | $this->_release['id'] |
||
1699 | ) |
||
1700 | ); |
||
1701 | |||
1702 | $musicParent = (string) Category::MUSIC_ROOT; |
||
1703 | if ($rQuery === false || ! preg_match( |
||
1704 | sprintf( |
||
1705 | '/%d\d{3}|%d|%d|%d/', |
||
1706 | $musicParent[0], |
||
1707 | Category::OTHER_MISC, |
||
1708 | Category::MOVIE_OTHER, |
||
1709 | Category::TV_OTHER |
||
1710 | ), |
||
1711 | $rQuery['id'] |
||
1712 | ) |
||
1713 | ) { |
||
1714 | return false; |
||
1715 | } |
||
1716 | |||
1717 | if (is_file($fileLocation)) { |
||
1718 | |||
1719 | // Check if media info is enabled. |
||
1720 | if ($retVal === false) { |
||
1721 | |||
1722 | // Get the media info for the file. |
||
1723 | $xmlArray = Utility::runCmd( |
||
1724 | $this->_killString.Settings::settingValue('apps..mediainfopath').'" --Output=XML "'.$fileLocation.'"' |
||
1725 | ); |
||
1726 | if (\is_array($xmlArray)) { |
||
1727 | |||
1728 | // Convert to array. |
||
1729 | $arrXml = Utility::objectsIntoArray(@simplexml_load_string(implode("\n", $xmlArray))); |
||
1730 | |||
1731 | if (isset($arrXml['File']['track'])) { |
||
1732 | foreach ($arrXml['File']['track'] as $track) { |
||
1733 | if (isset($track['Album']) && isset($track['Performer'])) { |
||
1734 | if (config('nntmux.rename_music_mediainfo') && (int) $this->_release['predb_id'] === 0) { |
||
1735 | // Make the extension upper case. |
||
1736 | $ext = strtoupper($fileExtension); |
||
1737 | |||
1738 | // Form a new search name. |
||
1739 | if (! empty($track['Recorded_date']) && preg_match('/(?:19|20)\d\d/', $track['Recorded_date'], $Year)) { |
||
1740 | $newName = $track['Performer'].' - '.$track['Album'].' ('.$Year[0].') '.$ext; |
||
1741 | } else { |
||
1742 | $newName = $track['Performer'].' - '.$track['Album'].' '.$ext; |
||
1743 | } |
||
1744 | |||
1745 | // Get the category or try to determine it. |
||
1746 | if ($ext === 'MP3') { |
||
1747 | $newCat = Category::MUSIC_MP3; |
||
1748 | } elseif ($ext === 'FLAC') { |
||
1749 | $newCat = Category::MUSIC_LOSSLESS; |
||
1750 | } else { |
||
1751 | $newCat = $this->_categorize->determineCategory($rQuery['groups_id'], $newName, $rQuery['fromname']); |
||
1752 | } |
||
1753 | |||
1754 | $newTitle = $this->pdo->escapeString(substr($newName, 0, 255)); |
||
1755 | // Update the search name. |
||
1756 | $this->pdo->queryExec( |
||
1757 | sprintf( |
||
1758 | ' |
||
1759 | UPDATE releases |
||
1760 | SET searchname = %s, categories_id = %d, iscategorized = 1, isrenamed = 1, proc_pp = 1 |
||
1761 | WHERE id = %d', |
||
1762 | $newTitle, |
||
1763 | $newCat, |
||
1764 | $this->_release['id'] |
||
1765 | ) |
||
1766 | ); |
||
1767 | $this->sphinx->updateRelease($this->_release['id']); |
||
1768 | |||
1769 | // Echo the changed name. |
||
1770 | if ($this->_echoCLI) { |
||
1771 | NameFixer::echoChangedReleaseName( |
||
1772 | [ |
||
1773 | 'new_name' => $newName, |
||
1774 | 'old_name' => $rQuery['searchname'], |
||
1775 | 'new_category' => $newCat, |
||
1776 | 'old_category' => $rQuery['id'], |
||
1777 | 'group' => $rQuery['groups_id'], |
||
1778 | 'releases_id' => $this->_release['id'], |
||
1779 | 'method' => 'ProcessAdditional->_getAudioInfo', |
||
1780 | ] |
||
1781 | ); |
||
1782 | } |
||
1783 | } |
||
1784 | |||
1785 | // Add the media info. |
||
1786 | $this->_releaseExtra->addFromXml($this->_release['id'], $xmlArray); |
||
1787 | |||
1788 | $retVal = true; |
||
1789 | $this->_foundAudioInfo = true; |
||
1790 | if ($this->_echoCLI) { |
||
1791 | $this->_echo('a', 'primaryOver', false); |
||
1792 | } |
||
1793 | break; |
||
1794 | } |
||
1795 | } |
||
1796 | } |
||
1797 | } |
||
1798 | } |
||
1799 | |||
1800 | // Check if creating audio samples is enabled. |
||
1801 | if ($audVal === false) { |
||
1802 | |||
1803 | // File name to store audio file. |
||
1804 | $audioFileName = ($this->_release['guid'].'.ogg'); |
||
1805 | |||
1806 | // Create an audio sample. |
||
1807 | Utility::runCmd( |
||
1808 | $this->_killString. |
||
1809 | Settings::settingValue('apps..ffmpegpath'). |
||
1810 | '" -t 30 -i "'. |
||
1811 | $fileLocation. |
||
1812 | '" -acodec libvorbis -loglevel quiet -y "'. |
||
1813 | $this->tmpPath.$audioFileName. |
||
1814 | '"' |
||
1815 | ); |
||
1816 | |||
1817 | // Check if the new file was created. |
||
1818 | if (is_file($this->tmpPath.$audioFileName)) { |
||
1819 | |||
1820 | // Try to move the temp audio file. |
||
1821 | $renamed = rename($this->tmpPath.$audioFileName, $this->_audioSavePath.$audioFileName); |
||
1822 | |||
1823 | if (! $renamed) { |
||
1824 | // Try to copy it if it fails. |
||
1825 | $copied = copy($this->tmpPath.$audioFileName, $this->_audioSavePath.$audioFileName); |
||
1826 | |||
1827 | // Delete the old file. |
||
1828 | unlink($this->tmpPath.$audioFileName); |
||
1829 | |||
1830 | // If it didn't copy continue. |
||
1831 | if (! $copied) { |
||
1832 | return false; |
||
1833 | } |
||
1834 | } |
||
1835 | |||
1836 | // Try to set the file perms. |
||
1837 | @chmod($this->_audioSavePath.$audioFileName, 0764); |
||
1838 | |||
1839 | // Update DB to said we got a audio sample. |
||
1840 | Release::query()->where('id', $this->_release['id'])->update(['audiostatus' => 1]); |
||
1841 | |||
1842 | $audVal = $this->_foundAudioSample = true; |
||
1843 | |||
1844 | if ($this->_echoCLI) { |
||
1845 | $this->_echo('A', 'primaryOver', false); |
||
1846 | } |
||
1847 | } |
||
1848 | } |
||
1849 | } |
||
1850 | |||
1851 | return $retVal && $audVal; |
||
1852 | } |
||
1853 | |||
1854 | /** |
||
1855 | * Try to get JPG picture, resize it and store it on disk. |
||
1856 | * |
||
1857 | * @param string $fileLocation |
||
1858 | */ |
||
1859 | protected function _getJPGSample($fileLocation) |
||
1860 | { |
||
1861 | // Try to resize/move the image. |
||
1862 | $this->_foundJPGSample = ( |
||
1863 | $this->_releaseImage->saveImage( |
||
1864 | $this->_release['guid'].'_thumb', |
||
1865 | $fileLocation, |
||
1866 | $this->_releaseImage->jpgSavePath, |
||
1867 | 650, |
||
1868 | 650 |
||
1869 | ) === 1 |
||
1870 | ); |
||
1871 | |||
1872 | // If it's successful, tell the DB. |
||
1873 | if ($this->_foundJPGSample !== false) { |
||
1874 | Release::query()->where('id', $this->_release['id'])->update(['jpgstatus' => 1]); |
||
1875 | } |
||
1876 | } |
||
1877 | |||
1878 | /** |
||
1879 | * @param $videoLocation |
||
1880 | * @return string |
||
1881 | * @throws \Exception |
||
1882 | */ |
||
1883 | private function getVideoTime($videoLocation) |
||
1884 | { |
||
1885 | // Attempt to get the file extension as ffmpeg fails on some videos with the wrong extension, avconv however is fine. |
||
1886 | if (preg_match('/(\.[a-zA-Z0-9]+)\s*$/', $videoLocation, $extension)) { |
||
1887 | $extension = $extension[1]; |
||
1888 | } else { |
||
1889 | $extension = '.avi'; |
||
1890 | } |
||
1891 | |||
1892 | $tmpVideo = ($this->tmpPath.uniqid('', true).$extension); |
||
1893 | // Get the real duration of the file. |
||
1894 | $time = Utility::runCmd( |
||
1895 | $this->_killString. |
||
1896 | Settings::settingValue('apps..ffmpegpath'). |
||
1897 | '" -i "'.$videoLocation. |
||
1898 | '" -vcodec copy -y 2>&1 "'. |
||
1899 | $tmpVideo.'"', |
||
1900 | false |
||
1901 | ); |
||
1902 | @unlink($tmpVideo); |
||
1903 | |||
1904 | if (empty($time) || ! preg_match('/time=(\d{1,2}:\d{1,2}:)?(\d{1,2})\.(\d{1,2})\s*bitrate=/i', implode(' ', $time), $numbers)) { |
||
1905 | return ''; |
||
1906 | } else { |
||
1907 | // Reduce the last number by 1, this is to make sure we don't ask avconv/ffmpeg for non existing data. |
||
1908 | if ($numbers[3] > 0) { |
||
1909 | $numbers[3] -= 1; |
||
1910 | } elseif ($numbers[1] > 0) { |
||
1911 | $numbers[2] -= 1; |
||
1912 | $numbers[3] = '99'; |
||
1913 | } |
||
1914 | // Manually pad the numbers in case they are 1 number. to get 02 for example instead of 2. |
||
1915 | return '00:00:'.str_pad($numbers[2], 2, '0', STR_PAD_LEFT).'.'.str_pad($numbers[3], 2, '0', STR_PAD_LEFT); |
||
1916 | } |
||
1917 | } |
||
1918 | |||
1919 | /** |
||
1920 | * @param $fileLocation |
||
1921 | * @return bool |
||
1922 | * @throws \Exception |
||
1923 | */ |
||
1924 | protected function _getSample($fileLocation) |
||
1925 | { |
||
1926 | if (! $this->_processThumbnails) { |
||
1927 | return false; |
||
1928 | } |
||
1929 | |||
1930 | if (is_file($fileLocation)) { |
||
1931 | |||
1932 | // Create path to temp file. |
||
1933 | $fileName = ($this->tmpPath.'zzzz'.random_int(5, 12).random_int(5, 12).'.jpg'); |
||
1934 | |||
1935 | $time = $this->getVideoTime($fileLocation); |
||
1936 | |||
1937 | // Create the image. |
||
1938 | Utility::runCmd( |
||
1939 | $this->_killString. |
||
1940 | Settings::settingValue('apps..ffmpegpath'). |
||
1941 | '" -i "'. |
||
1942 | $fileLocation. |
||
1943 | '" -ss '.($time === '' ? '00:00:03.00' : $time). |
||
1944 | ' -vframes 1 -loglevel quiet -y "'. |
||
1945 | $fileName. |
||
1946 | '"' |
||
1947 | ); |
||
1948 | |||
1949 | // Check if the file exists. |
||
1950 | if (is_file($fileName)) { |
||
1951 | |||
1952 | // Try to resize/move the image. |
||
1953 | $saved = $this->_releaseImage->saveImage( |
||
1954 | $this->_release['guid'].'_thumb', |
||
1955 | $fileName, |
||
1956 | $this->_releaseImage->imgSavePath, |
||
1957 | 800, |
||
1958 | 600 |
||
1959 | ); |
||
1960 | |||
1961 | // Delete the temp file we created. |
||
1962 | @unlink($fileName); |
||
1963 | |||
1964 | // Check if it saved. |
||
1965 | if ($saved === 1) { |
||
1966 | if ($this->_echoCLI) { |
||
1967 | $this->_echo('s', 'primaryOver', false); |
||
1968 | } |
||
1969 | |||
1970 | return true; |
||
1971 | } |
||
1972 | } |
||
1973 | } |
||
1974 | |||
1975 | return false; |
||
1976 | } |
||
1977 | |||
1978 | /** |
||
1979 | * @param $fileLocation |
||
1980 | * @return bool |
||
1981 | * @throws \Exception |
||
1982 | */ |
||
1983 | protected function _getVideo($fileLocation) |
||
1984 | { |
||
1985 | if (! $this->_processVideo) { |
||
1986 | return false; |
||
1987 | } |
||
1988 | |||
1989 | // Try to find an avi file. |
||
1990 | if (is_file($fileLocation)) { |
||
1991 | |||
1992 | // Create a filename to store the temp file. |
||
1993 | $fileName = ($this->tmpPath.'zzzz'.$this->_release['guid'].'.ogv'); |
||
1994 | |||
1995 | $newMethod = false; |
||
1996 | // If wanted sample length is less than 60, try to get sample from the end of the video. |
||
1997 | if ($this->_ffMPEGDuration < 60) { |
||
1998 | // Get the real duration of the file. |
||
1999 | $time = $this->getVideoTime($fileLocation); |
||
2000 | |||
2001 | if ($time !== '' && preg_match('/(\d{2}).(\d{2})/', $time, $numbers)) { |
||
2002 | $newMethod = true; |
||
2003 | |||
2004 | // Get the lowest time we can start making the video at based on how many seconds the admin wants the video to be. |
||
2005 | if ($numbers[1] <= $this->_ffMPEGDuration) { |
||
2006 | // If the clip is shorter than the length we want. |
||
2007 | |||
2008 | // The lowest we want is 0. |
||
2009 | $lowestLength = '00:00:00.00'; |
||
2010 | } else { |
||
2011 | // If the clip is longer than the length we want. |
||
2012 | |||
2013 | // The lowest we want is the the difference between the max video length and our wanted total time. |
||
2014 | $lowestLength = ($numbers[1] - $this->_ffMPEGDuration); |
||
2015 | |||
2016 | // Form the time string. |
||
2017 | $end = '.'.$numbers[2]; |
||
2018 | switch (\strlen($lowestLength)) { |
||
2019 | case 1: |
||
2020 | $lowestLength = ('00:00:0'.(string) $lowestLength.$end); |
||
2021 | break; |
||
2022 | case 2: |
||
2023 | $lowestLength = ('00:00:'.(string) $lowestLength.$end); |
||
2024 | break; |
||
2025 | default: |
||
2026 | $lowestLength = '00:00:60.00'; |
||
2027 | } |
||
2028 | } |
||
2029 | |||
2030 | // Try to get the sample (from the end instead of the start). |
||
2031 | Utility::runCmd( |
||
2032 | $this->_killString. |
||
2033 | Settings::settingValue('apps..ffmpegpath'). |
||
2034 | '" -i "'. |
||
2035 | $fileLocation. |
||
2036 | '" -ss '.$lowestLength. |
||
2037 | ' -t '.$this->_ffMPEGDuration. |
||
2038 | ' -vcodec libtheora -filter:v scale=320:-1 '. |
||
2039 | ' -acodec libvorbis -loglevel quiet -y "'. |
||
2040 | $fileName. |
||
2041 | '"' |
||
2042 | ); |
||
2043 | } |
||
2044 | } |
||
2045 | |||
2046 | if ($newMethod === false) { |
||
2047 | // If longer than 60 or we could not get the video length, run the old way. |
||
2048 | Utility::runCmd( |
||
2049 | $this->_killString. |
||
2050 | Settings::settingValue('apps..ffmpegpath'). |
||
2051 | '" -i "'. |
||
2052 | $fileLocation. |
||
2053 | '" -vcodec libtheora -filter:v scale=320:-1 -t '. |
||
2054 | $this->_ffMPEGDuration. |
||
2055 | ' -acodec libvorbis -loglevel quiet -y "'. |
||
2056 | $fileName. |
||
2057 | '"' |
||
2058 | ); |
||
2059 | } |
||
2060 | |||
2061 | // Until we find the video file. |
||
2062 | if (is_file($fileName)) { |
||
2063 | |||
2064 | // Create a path to where the file should be moved. |
||
2065 | $newFile = ($this->_releaseImage->vidSavePath.$this->_release['guid'].'.ogv'); |
||
2066 | |||
2067 | // Try to move the file to the new path. |
||
2068 | $renamed = @rename($fileName, $newFile); |
||
2069 | |||
2070 | // If we couldn't rename it, try to copy it. |
||
2071 | if (! $renamed) { |
||
2072 | $copied = @copy($fileName, $newFile); |
||
2073 | |||
2074 | // Delete the old file. |
||
2075 | @unlink($fileName); |
||
2076 | |||
2077 | // If it didn't copy, continue. |
||
2078 | if (! $copied) { |
||
2079 | return false; |
||
2080 | } |
||
2081 | } |
||
2082 | |||
2083 | // Change the permissions. |
||
2084 | @chmod($newFile, 0764); |
||
2085 | |||
2086 | // Update query to say we got the video. |
||
2087 | Release::query()->where('guid', $this->_release['guid'])->update(['videostatus' => 1]); |
||
2088 | if ($this->_echoCLI) { |
||
2089 | $this->_echo('v', 'primaryOver', false); |
||
2090 | } |
||
2091 | |||
2092 | return true; |
||
2093 | } |
||
2094 | } |
||
2095 | |||
2096 | return false; |
||
2097 | } |
||
2098 | |||
2099 | /** |
||
2100 | * @param $fileLocation |
||
2101 | * @return bool |
||
2102 | * @throws \Exception |
||
2103 | */ |
||
2104 | protected function _getMediaInfo($fileLocation) |
||
2105 | { |
||
2106 | if (! $this->_processMediaInfo) { |
||
2107 | return false; |
||
2108 | } |
||
2109 | |||
2110 | // Look for the video file. |
||
2111 | if (is_file($fileLocation)) { |
||
2112 | |||
2113 | // Run media info on it. |
||
2114 | $xmlArray = Utility::runCmd( |
||
2115 | $this->_killString.Settings::settingValue('apps..mediainfopath').'" --Output=XML "'.$fileLocation.'"' |
||
2116 | ); |
||
2117 | |||
2118 | // Check if we got it. |
||
2119 | if (\is_array($xmlArray)) { |
||
2120 | |||
2121 | // Convert it to string. |
||
2122 | $xmlArray = implode("\n", $xmlArray); |
||
2123 | |||
2124 | if (! preg_match('/<track type="(Audio|Video)">/i', $xmlArray)) { |
||
2125 | return false; |
||
2126 | } |
||
2127 | |||
2128 | // Insert it into the DB. |
||
2129 | $this->_releaseExtra->addFull($this->_release['id'], $xmlArray); |
||
2130 | $this->_releaseExtra->addFromXml($this->_release['id'], $xmlArray); |
||
2131 | |||
2132 | if ($this->_echoCLI) { |
||
2133 | $this->_echo('m', 'primaryOver', false); |
||
2134 | } |
||
2135 | |||
2136 | return true; |
||
2137 | } |
||
2138 | } |
||
2139 | |||
2140 | return false; |
||
2141 | } |
||
2142 | |||
2143 | /** |
||
2144 | * @param $fileLocation |
||
2145 | * @throws \Exception |
||
2146 | */ |
||
2147 | protected function _siftPAR2Info($fileLocation) |
||
2148 | { |
||
2149 | $this->_par2Info->open($fileLocation); |
||
2150 | |||
2151 | if ($this->_par2Info->error) { |
||
2152 | return; |
||
2153 | } |
||
2154 | |||
2155 | $releaseInfo = $this->pdo->queryOneRow( |
||
2156 | sprintf( |
||
2157 | ' |
||
2158 | SELECT UNIX_TIMESTAMP(postdate) AS postdate, proc_pp |
||
2159 | FROM releases |
||
2160 | WHERE id = %d', |
||
2161 | $this->_release['id'] |
||
2162 | ) |
||
2163 | ); |
||
2164 | |||
2165 | if ($releaseInfo === false) { |
||
2166 | return; |
||
2167 | } |
||
2168 | |||
2169 | // Only get a new name if the category is OTHER. |
||
2170 | $foundName = true; |
||
2171 | if (config('nntmux.rename_par2') && |
||
2172 | $releaseInfo['proc_pp'] === 0 && |
||
2173 | \in_array( |
||
2174 | (int) $this->_release['categories_id'], |
||
2175 | Category::OTHERS_GROUP, |
||
2176 | false |
||
2177 | ) |
||
2178 | ) { |
||
2179 | $foundName = false; |
||
2180 | } |
||
2181 | |||
2182 | $filesAdded = 0; |
||
2183 | |||
2184 | $files = $this->_par2Info->getFileList(); |
||
2185 | foreach ($files as $file) { |
||
2186 | if (! isset($file['name'])) { |
||
2187 | continue; |
||
2188 | } |
||
2189 | |||
2190 | // If we found a name and added 10 files, stop. |
||
2191 | if ($foundName === true && $filesAdded > 10) { |
||
2192 | break; |
||
2193 | } |
||
2194 | |||
2195 | // Add to release files. |
||
2196 | if ($this->_addPAR2Files) { |
||
2197 | if ($filesAdded < 11 && ReleaseFile::query()->where(['releases_id' => $this->_release['id'], 'name' => $file['name']])->first() === null |
||
2198 | ) { |
||
2199 | |||
2200 | // Try to add the files to the DB. |
||
2201 | if (ReleaseFile::addReleaseFiles($this->_release['id'], $file['name'], $file['hash_16K'], $file['size'], $releaseInfo['postdate'], 0)) { |
||
2202 | $filesAdded++; |
||
2203 | } |
||
2204 | } |
||
2205 | } else { |
||
2206 | $filesAdded++; |
||
2207 | } |
||
2208 | |||
2209 | // Try to get a new name. |
||
2210 | if ($foundName === false) { |
||
2211 | $this->_release['textstring'] = $file['name']; |
||
2212 | $this->_release['releases_id'] = $this->_release['id']; |
||
2213 | if ($this->_nameFixer->checkName($this->_release, ($this->_echoCLI ? true : false), 'PAR2, ', 1, 1) === true) { |
||
2214 | $foundName = true; |
||
2215 | } |
||
2216 | } |
||
2217 | } |
||
2218 | // Update the file count with the new file count + old file count. |
||
2219 | Release::query()->where('id', $this->_release['id'])->increment('rarinnerfilecount', $filesAdded); |
||
2220 | $this->_foundPAR2Info = true; |
||
2221 | } |
||
2222 | |||
2223 | /** |
||
2224 | * @param $fileLocation |
||
2225 | * @throws \Exception |
||
2226 | */ |
||
2227 | protected function _processNfoFile($fileLocation) |
||
2234 | } |
||
2235 | } |
||
2236 | } |
||
2237 | } |
||
2238 | |||
2239 | /** |
||
2240 | * @param $fileLocation |
||
2241 | * @throws \Exception |
||
2242 | */ |
||
2243 | protected function _processVideoFile($fileLocation) |
||
2244 | { |
||
2245 | // Try to get a sample with it. |
||
2246 | if ($this->_foundSample === false) { |
||
2247 | $this->_foundSample = $this->_getSample($fileLocation); |
||
2248 | } |
||
2249 | |||
2250 | /* Try to get a video with it. |
||
2251 | * Don't get it here if _sampleMessageIDs is empty |
||
2252 | * or has 1 message-id (Saves downloading another part). |
||
2253 | */ |
||
2254 | if ($this->_foundVideo === false && \count($this->_sampleMessageIDs) < 2) { |
||
2255 | $this->_foundVideo = $this->_getVideo($fileLocation); |
||
2256 | } |
||
2257 | |||
2258 | // Try to get media info with it. |
||
2259 | if ($this->_foundMediaInfo === false) { |
||
2260 | $this->_foundMediaInfo = $this->_getMediaInfo($fileLocation); |
||
2261 | } |
||
2262 | } |
||
2263 | |||
2264 | /** |
||
2265 | * Convert bytes to KB/MB/GB/TB and return in human readable format. |
||
2266 | * |
||
2267 | * @example 240640 would return 235KB |
||
2268 | * |
||
2269 | * @param int $bytes |
||
2270 | * |
||
2271 | * @return string |
||
2272 | */ |
||
2273 | protected function _readableBytesString($bytes) |
||
2274 | { |
||
2275 | $kb = 1024; |
||
2276 | $mb = 1048576; |
||
2277 | $gb = 1073741824; |
||
2278 | $tb = $kb * $gb; |
||
2279 | if ($bytes < $kb) { |
||
2280 | return $bytes.'B'; |
||
2281 | } elseif ($bytes < $mb) { |
||
2282 | return round($bytes / $kb, 1).'KB'; |
||
2283 | } elseif ($bytes < $gb) { |
||
2284 | return round($bytes / $mb, 1).'MB'; |
||
2285 | } elseif ($bytes < $tb) { |
||
2286 | return round($bytes / $gb, 1).'GB'; |
||
2287 | } else { |
||
2288 | return round($bytes / $tb, 1).'TB'; |
||
2289 | } |
||
2290 | } |
||
2291 | |||
2292 | /** |
||
2293 | * Comparison function for uSort, for sorting NZB files. |
||
2294 | * |
||
2295 | * @param array|null|string $a |
||
2296 | * @param array|null|string $b |
||
2297 | * |
||
2298 | * @return int |
||
2299 | */ |
||
2300 | protected function _sortNZB($a, $b) |
||
2301 | { |
||
2302 | $pos = 0; |
||
2303 | $af = $bf = false; |
||
2304 | $a = preg_replace('/\d+[- ._]?(\/|\||[o0]f)[- ._]?\d+?(?![- ._]\d)/i', ' ', $a['title']); |
||
2305 | $b = preg_replace('/\d+[- ._]?(\/|\||[o0]f)[- ._]?\d+?(?![- ._]\d)/i', ' ', $b['title']); |
||
2306 | |||
2307 | if (preg_match('/\.(part\d+|r\d+)(\s*\.rar)*($|[ ")\]-])/i', $a)) { |
||
2308 | $af = true; |
||
2309 | } |
||
2310 | if (preg_match('/\.(part\d+|r\d+)(\s*\.rar)*($|[ ")\]-])/i', $b)) { |
||
2311 | $bf = true; |
||
2312 | } |
||
2313 | |||
2314 | if (! $af && preg_match('/\.rar($|[ ")\]-])/i', $a)) { |
||
2315 | $a = preg_replace('/\.rar(?:$|[ ")\]-])/i', '.*rar', $a); |
||
2316 | $af = true; |
||
2317 | } |
||
2318 | if (! $bf && preg_match('/\.rar($|[ ")\]-])/i', $b)) { |
||
2319 | $b = preg_replace('/\.rar(?:$|[ ")\]-])/i', '.*rar', $b); |
||
2320 | $bf = true; |
||
2321 | } |
||
2322 | |||
2323 | if (! $af && ! $bf) { |
||
2324 | return strnatcasecmp($a, $b); |
||
2325 | } elseif (! $bf) { |
||
2326 | return -1; |
||
2327 | } elseif (! $af) { |
||
2328 | return 1; |
||
2329 | } |
||
2330 | |||
2331 | if ($af && $bf) { |
||
2332 | return strnatcasecmp($a, $b); |
||
2333 | } elseif ($af) { |
||
2334 | return -1; |
||
2335 | } elseif ($bf) { |
||
2336 | return 1; |
||
2337 | } |
||
2338 | |||
2339 | return $pos; |
||
2340 | } |
||
2341 | |||
2342 | /** |
||
2343 | * Reset some variables for the current release. |
||
2344 | */ |
||
2345 | protected function _resetReleaseStatus() |
||
2377 | } |
||
2378 | |||
2379 | /** |
||
2380 | * Echo a string to CLI. |
||
2381 | * |
||
2382 | * @param string $string String to echo. |
||
2383 | * @param string $type Method type. |
||
2384 | * @param bool $newLine Print a new line at the end of the string. |
||
2385 | * |
||
2386 | * @void |
||
2387 | */ |
||
2388 | protected function _echo($string, $type, $newLine = true) |
||
2389 | { |
||
2390 | if ($this->_echoCLI) { |
||
2391 | ColorCLI::doEcho(ColorCLI::$type($string), $newLine); |
||
2392 | } |
||
2393 | } |
||
2394 | |||
2395 | /** |
||
2396 | * Echo a string to CLI. For debugging. |
||
2397 | * |
||
2398 | * @param string $string |
||
2399 | * @param bool $newline |
||
2400 | * |
||
2401 | * @void |
||
2402 | */ |
||
2403 | protected function _debug($string, $newline = true) |
||
2406 | } |
||
2407 | } |
||
2408 |