Completed
Branch dev (4bcb34)
by Darko
13:52
created

ProcessAdditional::_processAudioInfoMessageIDs()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 13
nc 10
nop 0
dl 0
loc 24
rs 5.7377
c 0
b 0
f 0
1
<?php
2
3
namespace Blacklight\processing\post;
4
5
use Blacklight\Nfo;
6
use Blacklight\NZB;
7
use Blacklight\NNTP;
8
use App\Models\Group;
9
use Blacklight\db\DB;
10
use App\Models\Release;
11
use App\Models\Category;
12
use App\Models\Settings;
13
use Blacklight\ColorCLI;
14
use Blacklight\Releases;
15
use Blacklight\NameFixer;
16
use Blacklight\Categorize;
17
use App\Models\ReleaseFile;
18
use Blacklight\ReleaseExtra;
19
use Blacklight\ReleaseImage;
20
use Blacklight\SphinxSearch;
21
use Blacklight\utility\Utility;
22
use dariusiii\rarinfo\Par2Info;
23
use dariusiii\rarinfo\ArchiveInfo;
24
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));
0 ignored issues
show
Bug introduced by
It seems like $this->pdo can also be of type Blacklight\db\DB; however, parameter $settings of Blacklight\ReleaseExtra::__construct() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

403
        $this->_releaseExtra = ($options['ReleaseExtra'] instanceof ReleaseExtra ? $options['ReleaseExtra'] : new ReleaseExtra(/** @scrutinizer ignore-type */ $this->pdo));
Loading history...
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());
0 ignored issues
show
Bug Best Practice introduced by
The property sphinx does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
408
409
        $this->_innerFileBlacklist = Settings::settingValue('indexer.ppa.innerfileblacklist') === '' ? false : Settings::settingValue('indexer.ppa.innerfileblacklist');
0 ignored issues
show
Bug introduced by
'indexer.ppa.innerfileblacklist' of type string is incompatible with the type boolean|array expected by parameter $setting of App\Models\Settings::settingValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

409
        $this->_innerFileBlacklist = Settings::settingValue(/** @scrutinizer ignore-type */ 'indexer.ppa.innerfileblacklist') === '' ? false : Settings::settingValue('indexer.ppa.innerfileblacklist');
Loading history...
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: ');
0 ignored issues
show
Bug introduced by
The constant Blacklight\processing\post\DS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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 = '')
514
    {
515
        $this->_setMainTempPath($guidChar, $groupID);
516
517
        // Fetch all the releases to work on.
518
        $this->_fetchReleases($groupID, $guidChar);
519
520
        // Check if we have releases to work on.
521
        if ($this->_totalReleases > 0) {
522
            // Echo start time and process description.
523
            $this->_echoDescription();
524
525
            $this->_processReleases();
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');
0 ignored issues
show
Bug introduced by
'..tmpunrarpath' of type string is incompatible with the type boolean|array expected by parameter $setting of App\Models\Settings::settingValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

549
        $this->_mainTmpPath = (string) Settings::settingValue(/** @scrutinizer ignore-type */ '..tmpunrarpath');
Loading history...
550
551
        // Check if it ends with a dir separator.
552
        if (! preg_match('/[\/\\\\]$/', $this->_mainTmpPath)) {
553
            $this->_mainTmpPath .= DS;
0 ignored issues
show
Bug introduced by
The constant Blacklight\processing\post\DS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

568
            /** @scrutinizer ignore-unhandled */ @chmod($this->_mainTmpPath, 0777);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
569
            @umask($old);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for umask(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

569
            /** @scrutinizer ignore-unhandled */ @umask($old);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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)) {
0 ignored issues
show
introduced by
The condition is_array($this->_releases) is always true.
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rmdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

761
            /** @scrutinizer ignore-unhandled */ @rmdir($path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
762
        } elseif (is_file($path)) {
763
            @unlink($path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

763
            /** @scrutinizer ignore-unhandled */ @unlink($path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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;
0 ignored issues
show
Bug introduced by
The constant Blacklight\processing\post\DS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

782
            /** @scrutinizer ignore-unhandled */ @chmod($this->tmpPath, 0777);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
783
            @umask($old);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for umask(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

783
            /** @scrutinizer ignore-unhandled */ @umask($old);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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) {
0 ignored issues
show
introduced by
The condition $nzbContents is always false.
Loading history...
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.
0 ignored issues
show
Bug introduced by
Are you sure $this->_unrarPath of type string|true|array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1096
                        $this->_killString./** @scrutinizer ignore-type */ $this->_unrarPath.
Loading history...
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 "'.
0 ignored issues
show
Bug introduced by
Are you sure $this->_7zipPath of type string|true|array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1112
                        $this->_killString./** @scrutinizer ignore-type */ $this->_7zipPath.'" x "'.
Loading history...
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) {
0 ignored issues
show
introduced by
The condition is_array($files) is always true.
Loading history...
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(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1173
                        /** @scrutinizer ignore-unhandled */ @file_put_contents(

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type integer expected by parameter $show of Blacklight\NameFixer::matchPredbFiles(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1230
                        $this->_nameFixer->matchPredbFiles($this->_release, 1, 1, /** @scrutinizer ignore-type */ true, 1);
Loading history...
Unused Code introduced by
The call to Blacklight\NameFixer::matchPredbFiles() has too many arguments starting with 1. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1230
                        $this->_nameFixer->/** @scrutinizer ignore-call */ 
1231
                                           matchPredbFiles($this->_release, 1, 1, true, 1);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
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]);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1269
                        /** @scrutinizer ignore-unhandled */ @unlink($file[0]);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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]);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1310
                        /** @scrutinizer ignore-unhandled */ @rename($file, $this->tmpPath.'audiofile.'.$fileType[2]);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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
0 ignored issues
show
Coding Style introduced by
As per coding-style, case should be followed by a single space.

As per the PSR-2 coding standard, there must be a space after the case keyword, instead of the test immediately following it.

switch (true) {
    case!isset($a):  //wrong
        doSomething();
        break;
    case !isset($b):  //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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
0 ignored issues
show
Coding Style introduced by
As per coding-style, case should be followed by a single space.

As per the PSR-2 coding standard, there must be a space after the case keyword, instead of the test immediately following it.

switch (true) {
    case!isset($a):  //wrong
        doSomething();
        break;
    case !isset($b):  //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1412
                        /** @scrutinizer ignore-unhandled */ @file_put_contents($fileLocation, $sampleBinary);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1465
                        /** @scrutinizer ignore-unhandled */ @file_put_contents($fileLocation, $mediaBinary);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1466
1467
                        // Try to get media info.
1468
                        if ($this->_foundMediaInfo === false) {
0 ignored issues
show
introduced by
The condition $this->_foundMediaInfo === false is always true.
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1513
                    /** @scrutinizer ignore-unhandled */ @file_put_contents($fileLocation, $audioBinary);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for file_put_contents(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1547
                /** @scrutinizer ignore-unhandled */ @file_put_contents($this->tmpPath.'samplepicture.jpg', $jpgBinary);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1569
                /** @scrutinizer ignore-unhandled */ @unlink($this->tmpPath.'samplepicture.jpg');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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.'"'
0 ignored issues
show
Bug introduced by
'apps..mediainfopath' of type string is incompatible with the type boolean|array expected by parameter $setting of App\Models\Settings::settingValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1724
                    $this->_killString.Settings::settingValue(/** @scrutinizer ignore-type */ 'apps..mediainfopath').'" --Output=XML "'.$fileLocation.'"'
Loading history...
1725
                );
1726
                if (\is_array($xmlArray)) {
0 ignored issues
show
introduced by
The condition is_array($xmlArray) is always true.
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1837
                    /** @scrutinizer ignore-unhandled */ @chmod($this->_audioSavePath.$audioFileName, 0764);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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').
0 ignored issues
show
Bug introduced by
'apps..ffmpegpath' of type string is incompatible with the type boolean|array expected by parameter $setting of App\Models\Settings::settingValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1896
            Settings::settingValue(/** @scrutinizer ignore-type */ 'apps..ffmpegpath').
Loading history...
1897
            '" -i "'.$videoLocation.
1898
            '" -vcodec copy -y 2>&1 "'.
1899
            $tmpVideo.'"',
1900
            false
1901
        );
1902
        @unlink($tmpVideo);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1902
        /** @scrutinizer ignore-unhandled */ @unlink($tmpVideo);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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').
0 ignored issues
show
Bug introduced by
'apps..ffmpegpath' of type string is incompatible with the type boolean|array expected by parameter $setting of App\Models\Settings::settingValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1940
                Settings::settingValue(/** @scrutinizer ignore-type */ 'apps..ffmpegpath').
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1962
                /** @scrutinizer ignore-unhandled */ @unlink($fileName);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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').
0 ignored issues
show
Bug introduced by
'apps..ffmpegpath' of type string is incompatible with the type boolean|array expected by parameter $setting of App\Models\Settings::settingValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2033
                        Settings::settingValue(/** @scrutinizer ignore-type */ 'apps..ffmpegpath').
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

2075
                    /** @scrutinizer ignore-unhandled */ @unlink($fileName);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

2084
                /** @scrutinizer ignore-unhandled */ @chmod($newFile, 0764);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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.'"'
0 ignored issues
show
Bug introduced by
'apps..mediainfopath' of type string is incompatible with the type boolean|array expected by parameter $setting of App\Models\Settings::settingValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2115
                $this->_killString.Settings::settingValue(/** @scrutinizer ignore-type */ 'apps..mediainfopath').'" --Output=XML "'.$fileLocation.'"'
Loading history...
2116
            );
2117
2118
            // Check if we got it.
2119
            if (\is_array($xmlArray)) {
0 ignored issues
show
introduced by
The condition is_array($xmlArray) is always true.
Loading history...
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)
2228
    {
2229
        $data = @file_get_contents($fileLocation);
2230
        if ($data !== false) {
0 ignored issues
show
introduced by
The condition $data !== false is always true.
Loading history...
2231
            if ($this->_nfo->isNFO($data, $this->_release['guid']) === true) {
2232
                if ($this->_nfo->addAlternateNfo($data, $this->_release, $this->_nntp) === true) {
2233
                    $this->_releaseHasNoNFO = false;
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) {
0 ignored issues
show
introduced by
The condition $bf is always true.
Loading history...
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()
2346
    {
2347
        // Only process for samples, previews and images if not disabled.
2348
        $this->_foundVideo = $this->_processVideo ? false : true;
2349
        $this->_foundMediaInfo = $this->_processMediaInfo ? false : true;
2350
        $this->_foundAudioInfo = $this->_processAudioInfo ? false : true;
2351
        $this->_foundAudioSample = $this->_processAudioSample ? false : true;
2352
        $this->_foundJPGSample = $this->_processJPGSample ? false : true;
2353
        $this->_foundSample = $this->_processThumbnails ? false : true;
2354
        $this->_foundSample = (int) $this->_release['disablepreview'] === 1;
2355
        $this->_foundPAR2Info = false;
2356
2357
        $this->_passwordStatus = [Releases::PASSWD_NONE];
2358
        $this->_releaseHasPassword = false;
2359
2360
        $this->_releaseGroupName = Group::getNameByID($this->_release['groups_id']);
2361
2362
        $this->_releaseHasNoNFO = false;
2363
        // Make sure we don't already have an nfo.
2364
        if ((int) $this->_release['nfostatus'] !== 1) {
2365
            $this->_releaseHasNoNFO = true;
2366
        }
2367
2368
        $this->_NZBHasCompressedFile = false;
2369
2370
        $this->_sampleMessageIDs = $this->_JPGMessageIDs = $this->_MediaInfoMessageIDs = [];
2371
        $this->_AudioInfoMessageIDs = $this->_RARFileMessageIDs = [];
2372
        $this->_AudioInfoExtension = '';
2373
2374
        $this->_addedFileInfo = 0;
2375
        $this->_totalFileInfo = 0;
2376
        $this->_compressedFilesChecked = 0;
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)
2404
    {
2405
        $this->_echo('DEBUG: '.$string, 'debug', $newline);
2406
    }
2407
}
2408