ReleaseFileManager::deleteRelease()   F
last analyzed

Complexity

Conditions 15
Paths 1529

Size

Total Lines 64
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 35
dl 0
loc 64
rs 1.7499
c 0
b 0
f 0
cc 15
nc 1529
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace App\Services\AdditionalProcessing;
4
5
use App\Models\MediaInfo as MediaInfoModel;
6
use App\Models\Predb;
7
use App\Models\Release;
8
use App\Models\ReleaseFile;
9
use Blacklight\Releases;
10
use App\Services\AdditionalProcessing\Config\ProcessingConfiguration;
0 ignored issues
show
Bug introduced by
The type App\Services\AdditionalP...ProcessingConfiguration was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use App\Services\AdditionalProcessing\DTO\ReleaseProcessingContext;
12
use Blacklight\ElasticSearchSiteSearch;
13
use Blacklight\ManticoreSearch;
14
use Blacklight\NameFixer;
15
use Blacklight\Nfo;
16
use Blacklight\NZB;
17
use Blacklight\ReleaseExtra;
18
use Blacklight\ReleaseImage;
19
use Illuminate\Contracts\Filesystem\FileNotFoundException;
20
use Illuminate\Support\Carbon;
21
use Illuminate\Support\Facades\File;
22
use Illuminate\Support\Facades\Log;
23
24
/**
25
 * Service for managing release-related database operations.
26
 * Handles file info, release updates, deletions, and search index updates.
27
 */
28
class ReleaseFileManager
29
{
30
    private ?ManticoreSearch $manticore = null;
31
    private ?ElasticSearchSiteSearch $elasticsearch = null;
32
33
    public function __construct(
34
        private readonly ProcessingConfiguration $config,
35
        private readonly ReleaseExtra $releaseExtra,
36
        private readonly ReleaseImage $releaseImage,
37
        private readonly Nfo $nfo,
38
        private readonly NZB $nzb,
39
        private readonly NameFixer $nameFixer
40
    ) {}
41
42
    /**
43
     * Add file information to the database.
44
     * @throws \Exception
45
     */
46
    public function addFileInfo(
47
        array $file,
48
        ReleaseProcessingContext $context,
49
        string $supportFileRegex
50
    ): bool {
51
        if (isset($file['error'])) {
52
            if ($this->config->debugMode) {
53
                Log::debug("Error: {$file['error']} (in: {$file['source']})");
54
            }
55
            return false;
56
        }
57
58
        if (! isset($file['name'])) {
59
            return false;
60
        }
61
62
        // Check for password
63
        if (isset($file['pass']) && $file['pass'] === true) {
64
            $context->releaseHasPassword = true;
65
            $context->passwordStatus = Releases::PASSWD_RAR;
66
            return false;
67
        }
68
69
        // Check inner file blacklist
70
        if ($this->config->innerFileBlacklist !== false
71
            && preg_match($this->config->innerFileBlacklist, $file['name'])
72
        ) {
73
            $context->releaseHasPassword = true;
74
            $context->passwordStatus = Releases::PASSWD_RAR;
75
            return false;
76
        }
77
78
        // Skip support files
79
        if (preg_match(
80
            '/(?:'.$supportFileRegex.'|part\d+|[rz]\d{1,3}|zipr\d{2,3}|\d{2,3}|zipx?|zip|rar|7z|gz|bz2|xz)(\s*\.rar)?$/i',
81
            $file['name']
82
        )) {
83
            return false;
84
        }
85
86
        // Increment total file info count
87
        $context->totalFileInfo++;
88
89
        // Don't add too many files
90
        if ($context->addedFileInfo >= 11) {
91
            return false;
92
        }
93
94
        // Check if a file already exists
95
        $exists = ReleaseFile::query()
96
            ->where([
97
                'releases_id' => $context->release->id,
98
                'name' => $file['name'],
99
                'size' => $file['size'],
100
            ])
101
            ->first();
102
103
        if ($exists !== null) {
104
            return false;
105
        }
106
107
        // Add the file
108
        $added = ReleaseFile::addReleaseFiles(
109
            $context->release->id,
110
            $file['name'],
111
            $file['size'],
112
            $file['date'],
113
            $file['pass'] ?? 0,
114
            '',
115
            $file['crc32'] ?? ''
116
        );
117
118
        if (! empty($added)) {
119
            $context->addedFileInfo++;
120
121
            // Check for codec spam
122
            if (preg_match('#(?:^|[/\\\\])Codec[/\\\\]Setup\.exe$#i', $file['name'])) {
123
                if ($this->config->debugMode) {
124
                    Log::debug('Codec spam found, setting release to potentially passworded.');
125
                }
126
                $context->releaseHasPassword = true;
127
                $context->passwordStatus = Releases::PASSWD_RAR;
128
            } elseif ($file['name'] !== '' && ! str_starts_with($file['name'], '.')) {
129
                // Run PreDB filename check
130
                $context->release['filename'] = $file['name'];
131
                $context->release['releases_id'] = $context->release->id;
132
                $this->nameFixer->matchPreDbFiles($context->release, 1, 1, true);
133
            }
134
135
            return true;
136
        }
137
138
        return false;
139
    }
140
141
    /**
142
     * Update search indexes after adding file info.
143
     */
144
    public function updateSearchIndex(int $releaseId): void
145
    {
146
        if ($this->config->elasticsearchEnabled) {
147
            $this->elasticsearch()->updateRelease($releaseId);
148
        } else {
149
            $this->manticore()->updateRelease($releaseId);
150
        }
151
    }
152
153
    /**
154
     * Finalize release processing with status updates.
155
     */
156
    public function finalizeRelease(ReleaseProcessingContext $context, bool $processPasswords): void
157
    {
158
        $updateRows = ['haspreview' => 0];
159
160
        // Check for existing samples
161
        if (File::isFile($this->releaseImage->imgSavePath.$context->release->guid.'_thumb.jpg')) {
0 ignored issues
show
Bug introduced by
The property guid does not seem to exist on App\Models\Release. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
162
            $updateRows = ['haspreview' => 1];
163
        }
164
165
        if (File::isFile($this->releaseImage->vidSavePath.$context->release->guid.'.ogv')) {
166
            $updateRows['videostatus'] = 1;
167
        }
168
169
        if (File::isFile($this->releaseImage->jpgSavePath.$context->release->guid.'_thumb.jpg')) {
170
            $updateRows['jpgstatus'] = 1;
171
        }
172
173
        // Get file count
174
        $releaseFilesCount = ReleaseFile::whereReleasesId($context->release->id)->count('releases_id') ?? 0;
175
176
        $passwordStatus = max([$context->passwordStatus]);
177
178
        // Set to no password if processing is off
179
        if (! $processPasswords) {
180
            $context->releaseHasPassword = false;
181
        }
182
183
        // Update based on conditions
184
        if (! $context->releaseHasPassword && $context->nzbHasCompressedFile && $releaseFilesCount === 0) {
185
            Release::query()->where('id', $context->release->id)->update($updateRows);
186
        } else {
187
            $updateRows['passwordstatus'] = $processPasswords ? $passwordStatus : Releases::PASSWD_NONE;
188
            $updateRows['rarinnerfilecount'] = $releaseFilesCount;
189
            Release::query()->where('id', $context->release->id)->update($updateRows);
190
        }
191
    }
192
193
    /**
194
     * Delete a broken release completely.
195
     */
196
    public function deleteRelease(Release $release): void
197
    {
198
        try {
199
            if (empty($release->id)) {
200
                return;
201
            }
202
203
            $id = (int) $release->id;
204
            $guid = $release->guid ?? '';
0 ignored issues
show
Bug introduced by
The property guid does not seem to exist on App\Models\Release. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
205
206
            // Delete NZB file
207
            try {
208
                $nzbPath = $this->nzb->NZBPath($guid);
209
                if ($nzbPath && File::exists($nzbPath)) {
210
                    File::delete($nzbPath);
211
                }
212
            } catch (\Throwable) {
213
                // Ignore
214
            }
215
216
            // Delete preview assets
217
            try {
218
                $files = [
219
                    $this->releaseImage->imgSavePath.$guid.'_thumb.jpg',
220
                    $this->releaseImage->jpgSavePath.$guid.'_thumb.jpg',
221
                    $this->releaseImage->vidSavePath.$guid.'.ogv',
222
                ];
223
                foreach ($files as $file) {
224
                    if ($file && File::exists($file)) {
225
                        File::delete($file);
226
                    }
227
                }
228
            } catch (\Throwable) {
229
                // Ignore
230
            }
231
232
            // Delete related database rows
233
            try {
234
                ReleaseFile::where('releases_id', $id)->delete();
235
            } catch (\Throwable) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
236
            }
237
238
            try {
239
                MediaInfoModel::where('releases_id', $id)->delete();
240
            } catch (\Throwable) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
241
            }
242
243
            // Delete from search index
244
            try {
245
                if ($this->config->elasticsearchEnabled) {
246
                    $this->elasticsearch()->deleteRelease($id);
247
                } else {
248
                    $this->manticore()->deleteRelease(['i' => $id, 'g' => $guid]);
249
                }
250
            } catch (\Throwable) {
251
                // Ignore
252
            }
253
254
            // Delete release row
255
            try {
256
                Release::where('id', $id)->delete();
257
            } catch (\Throwable) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
258
            }
259
        } catch (\Throwable) {
260
            // Last resort: swallow any exception
261
        }
262
    }
263
264
    /**
265
     * Process PAR2 file for file info and release name matching.
266
     */
267
    public function processPar2File(
268
        string $fileLocation,
269
        ReleaseProcessingContext $context,
270
        \dariusiii\rarinfo\Par2Info $par2Info
271
    ): bool {
272
        $par2Info->open($fileLocation);
273
274
        if ($par2Info->error) {
275
            return false;
276
        }
277
278
        $releaseInfo = Release::query()
279
            ->where('id', $context->release->id)
280
            ->select(['postdate', 'proc_pp'])
281
            ->first();
282
283
        if ($releaseInfo === null) {
284
            return false;
285
        }
286
287
        $postDate = Carbon::createFromFormat('Y-m-d H:i:s', $releaseInfo->postdate)->getTimestamp();
0 ignored issues
show
Bug introduced by
The property postdate does not seem to exist on App\Models\Release. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
288
289
        // Only get new name if category is OTHER
290
        $foundName = true;
291
        if ((int) $releaseInfo->proc_pp === 0 && $this->config->renamePar2
0 ignored issues
show
Bug introduced by
The property proc_pp does not seem to exist on App\Models\Release. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
292
            && in_array((int) $context->release->categories_id, \App\Models\Category::OTHERS_GROUP, false)
0 ignored issues
show
Bug introduced by
The property categories_id does not exist on App\Models\Release. Did you mean category_ids?
Loading history...
293
        ) {
294
            $foundName = false;
295
        }
296
297
        $filesAdded = 0;
298
299
        foreach ($par2Info->getFileList() as $file) {
300
            if (! isset($file['name'])) {
301
                continue;
302
            }
303
304
            if ($foundName && $filesAdded > 10) {
305
                break;
306
            }
307
308
            // Add to release files
309
            if ($this->config->addPAR2Files) {
310
                if ($filesAdded < 11
311
                    && ReleaseFile::query()
312
                        ->where(['releases_id' => $context->release->id, 'name' => $file['name']])
313
                        ->first() === null
314
                ) {
315
                    if (ReleaseFile::addReleaseFiles(
316
                        $context->release->id,
317
                        $file['name'],
318
                        $file['size'],
319
                        $postDate,
320
                        0,
321
                        $file['hash_16K']
322
                    )) {
323
                        $filesAdded++;
324
                    }
325
                }
326
            } else {
327
                $filesAdded++;
328
            }
329
330
            // Try to get a new name
331
            if (! $foundName) {
332
                $context->release->textstring = $file['name'];
0 ignored issues
show
Bug introduced by
The property textstring does not seem to exist on App\Models\Release. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
333
                $context->release->releases_id = $context->release->id;
0 ignored issues
show
Bug introduced by
The property releases_id does not seem to exist on App\Models\Release. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
334
                if ($this->nameFixer->checkName($context->release, $this->config->echoCLI, 'PAR2, ', 1, 1)) {
335
                    $foundName = true;
336
                }
337
            }
338
        }
339
340
        // Update file count
341
        Release::query()->where('id', $context->release->id)->increment('rarinnerfilecount', $filesAdded);
342
        $context->foundPAR2Info = true;
343
344
        return true;
345
    }
346
347
    /**
348
     * Process NFO file with enhanced detection capabilities.
349
     *
350
     * Supports multiple NFO naming conventions:
351
     * - Standard: .nfo, .diz, .info
352
     * - Alternative: file_id.diz, readme.txt, info.txt
353
     * - Scene-style: 00-groupname.nfo, groupname-releasename.nfo
354
     */
355
    public function processNfoFile(
356
        string $fileLocation,
357
        ReleaseProcessingContext $context,
358
        \Blacklight\NNTP $nntp
359
    ): bool {
360
        try {
361
            $data = File::get($fileLocation);
362
363
            // Try to detect and convert encoding
364
            $data = $this->normalizeNfoEncoding($data);
365
366
            if ($this->nfo->isNFO($data, $context->release->guid)
0 ignored issues
show
Bug introduced by
The property guid does not seem to exist on App\Models\Release. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
367
                && $this->nfo->addAlternateNfo($data, $context->release, $nntp)
368
            ) {
369
                $context->releaseHasNoNFO = false;
370
                return true;
371
            }
372
        } catch (FileNotFoundException $e) {
373
            Log::warning("Could not read potential NFO file: {$fileLocation} - {$e->getMessage()}");
374
        }
375
376
        return false;
377
    }
378
379
    /**
380
     * Check if a filename looks like an NFO file.
381
     *
382
     * @param string $filename The filename to check.
383
     * @return bool True if the filename matches NFO patterns.
384
     */
385
    public function isNfoFilename(string $filename): bool
386
    {
387
        // Standard NFO extensions
388
        if (preg_match('/\.(?:nfo|diz|info?)$/i', $filename)) {
389
            return true;
390
        }
391
392
        // Alternative NFO filenames
393
        $nfoPatterns = [
394
            '/^(?:file[_-]?id|readme|release|info(?:rmation)?|about|notes?)\.(?:txt|diz)$/i',
395
            '/^00-[a-z0-9_-]+\.nfo$/i',           // Scene: 00-group.nfo
396
            '/^0+-[a-z0-9_-]+\.nfo$/i',           // Scene variations
397
            '/^[a-z0-9_-]+-[a-z0-9_.-]+\.nfo$/i', // Scene: group-release.nfo
398
            '/info\.txt$/i',                      // info.txt (common alternative)
399
        ];
400
401
        $basename = basename($filename);
402
        foreach ($nfoPatterns as $pattern) {
403
            if (preg_match($pattern, $basename)) {
404
                return true;
405
            }
406
        }
407
408
        return false;
409
    }
410
411
    /**
412
     * Normalize NFO encoding to UTF-8.
413
     *
414
     * NFO files often use CP437 (DOS) encoding for ASCII art.
415
     * This method attempts to detect and convert various encodings.
416
     *
417
     * @param string $data Raw NFO data.
418
     * @return string UTF-8 encoded NFO data.
419
     */
420
    protected function normalizeNfoEncoding(string $data): string
421
    {
422
        // Check for UTF-8 BOM and remove it
423
        if (str_starts_with($data, "\xEF\xBB\xBF")) {
424
            $data = substr($data, 3);
425
        }
426
427
        // Check for UTF-16 BOM
428
        if (str_starts_with($data, "\xFF\xFE")) {
429
            // UTF-16 LE
430
            $data = mb_convert_encoding(substr($data, 2), 'UTF-8', 'UTF-16LE');
431
        } elseif (str_starts_with($data, "\xFE\xFF")) {
432
            // UTF-16 BE
433
            $data = mb_convert_encoding(substr($data, 2), 'UTF-8', 'UTF-16BE');
434
        }
435
436
        // If already valid UTF-8, return as-is
437
        if (mb_check_encoding($data, 'UTF-8')) {
438
            return $data;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $data could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
439
        }
440
441
        // Try CP437 (DOS encoding - common for scene NFOs with ASCII art)
442
        // Use the utility function if available
443
        if (class_exists('\Blacklight\utility\Utility') && method_exists('\Blacklight\utility\Utility', 'cp437toUTF')) {
444
            return \Blacklight\utility\Utility::cp437toUTF($data);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type array; however, parameter $string of Blacklight\utility\Utility::cp437toUTF() does only seem to accept string, 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

444
            return \Blacklight\utility\Utility::cp437toUTF(/** @scrutinizer ignore-type */ $data);
Loading history...
445
        }
446
447
        // Fallback: try ISO-8859-1 (Latin-1)
448
        $converted = @mb_convert_encoding($data, 'UTF-8', 'ISO-8859-1');
449
        if ($converted !== false) {
450
            return $converted;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $converted could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
451
        }
452
453
        // Last resort: force UTF-8 with error handling
454
        return mb_convert_encoding($data, 'UTF-8', 'UTF-8');
0 ignored issues
show
Bug Best Practice introduced by
The expression return mb_convert_encodi...data, 'UTF-8', 'UTF-8') could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
455
    }
456
457
    /**
458
     * Handle release name extraction from RAR file content.
459
     */
460
    public function processReleaseNameFromRar(
461
        array $dataSummary,
462
        ReleaseProcessingContext $context
463
    ): void {
464
        $fileData = $dataSummary['file_list'] ?? [];
465
        if (empty($fileData)) {
466
            return;
467
        }
468
469
        $rarFileName = array_column($fileData, 'name');
470
        if (empty($rarFileName[0])) {
471
            return;
472
        }
473
474
        $extractedName = $this->extractReleaseNameFromFile($rarFileName[0]);
475
476
        if ($extractedName !== null) {
477
            $preCheck = Predb::whereTitle($extractedName)->first();
478
            $context->release->preid = $preCheck !== null ? $preCheck->value('id') : 0;
0 ignored issues
show
Bug introduced by
The property preid does not exist on App\Models\Release. Did you mean predb?
Loading history...
479
            $candidate = $preCheck->title ?? $extractedName;
480
            $candidate = $this->normalizeCandidateTitle($candidate);
481
482
            if ($this->isPlausibleReleaseTitle($candidate)) {
483
                (new NameFixer())->updateRelease(
484
                    $context->release,
485
                    $candidate,
486
                    'RarInfo FileName Match',
487
                    true,
488
                    'Filenames, ',
489
                    true,
490
                    true,
491
                    $context->release->preid
492
                );
493
            } elseif ($this->config->debugMode) {
494
                Log::debug('RarInfo: Ignored low-quality candidate "'.$candidate.'" from inner file name.');
495
            }
496
        } elseif (! empty($dataSummary['archives'][$rarFileName[0]]['file_list'])) {
497
            // Try nested archive
498
            $archiveData = $dataSummary['archives'][$rarFileName[0]]['file_list'];
499
            $archiveFileName = array_column($archiveData, 'name');
500
            $extractedName = $this->extractReleaseNameFromFile($archiveFileName[0] ?? '');
501
502
            if ($extractedName !== null) {
503
                $preCheck = Predb::whereTitle($extractedName)->first();
504
                $context->release->preid = $preCheck !== null ? $preCheck->value('id') : 0;
505
                $candidate = $preCheck->title ?? $extractedName;
506
                $candidate = $this->normalizeCandidateTitle($candidate);
507
508
                if ($this->isPlausibleReleaseTitle($candidate)) {
509
                    (new NameFixer())->updateRelease(
510
                        $context->release,
511
                        $candidate,
512
                        'RarInfo FileName Match',
513
                        true,
514
                        'Filenames, ',
515
                        true,
516
                        true,
517
                        $context->release->preid
518
                    );
519
                }
520
            }
521
        }
522
    }
523
524
    /**
525
     * Extract the release name from a filename.
526
     */
527
    private function extractReleaseNameFromFile(string $filename): ?string
528
    {
529
        $basename = basename($filename);
530
        $cleaned = preg_replace(
531
            '/\.(mkv|avi|mp4|m4v|mpg|mpeg|wmv|flv|mov|ts|vob|iso|divx|par2?|nfo|sfv|nzb|rar|zip|r\d{2,3}|pkg|exe|msi)$/i',
532
            '',
533
            $basename
534
        );
535
536
        if (preg_match('/^(.+[-.][A-Za-z0-9_]{2,})$/i', $cleaned, $match)) {
537
            return ucwords($match[1], '.-_ ');
538
        }
539
540
        if (preg_match(NameFixer::PREDB_REGEX, $cleaned, $hit)) {
541
            return ucwords($hit[0], '.');
542
        }
543
544
        return null;
545
    }
546
547
    /**
548
     * Normalize a candidate title.
549
     */
550
    private function normalizeCandidateTitle(string $title): string
551
    {
552
        $t = trim($title);
553
        $t = preg_replace('/\.(mkv|avi|mp4|m4v|mpg|mpeg|wmv|flv|mov|ts|vob|iso|divx)$/i', '', $t) ?? $t;
554
        $t = preg_replace('/\.(par2?|nfo|sfv|nzb|rar|zip|r\d{2,3}|pkg|exe|msi|jpe?g|png|gif|bmp)$/i', '', $t) ?? $t;
555
        $t = preg_replace('/[.\-_ ](?:part|vol|r)\d+(?:\+\d+)?$/i', '', $t) ?? $t;
556
        $t = preg_replace('/[\s_]+/', ' ', $t) ?? $t;
557
        return trim($t, " .-_\t\r\n");
558
    }
559
560
    /**
561
     * Check if a title is plausible for release naming.
562
     */
563
    private function isPlausibleReleaseTitle(string $title): bool
564
    {
565
        $t = trim($title);
566
        if ($t === '' || strlen($t) < 12) {
567
            return false;
568
        }
569
570
        $wordCount = preg_match_all('/[A-Za-z0-9]{3,}/', $t);
571
        if ($wordCount < 2) {
572
            return false;
573
        }
574
575
        if (preg_match('/(?:^|[.\-_ ])(?:part|vol|r)\d+(?:\+\d+)?$/i', $t)) {
576
            return false;
577
        }
578
579
        if (preg_match('/^(setup|install|installer|patch|update|crack|keygen)\d*[\s._-]/i', $t)) {
580
            return false;
581
        }
582
583
        $hasGroupSuffix = (bool) preg_match('/[-.][A-Za-z0-9]{2,}$/', $t);
584
        $hasYear = (bool) preg_match('/\b(19|20)\d{2}\b/', $t);
585
        $hasQuality = (bool) preg_match('/\b(480p|720p|1080p|2160p|4k|webrip|web[ .-]?dl|bluray|bdrip|dvdrip|hdtv|hdrip|xvid|x264|x265|hevc|h\.?264|ts|cam|r5|proper|repack)\b/i', $t);
586
        $hasTV = (bool) preg_match('/\bS\d{1,2}[Eex]\d{1,3}\b/i', $t);
587
        $hasXXX = (bool) preg_match('/\bXXX\b/i', $t);
588
589
        return $hasGroupSuffix || ($hasTV && $hasQuality) || ($hasYear && ($hasQuality || $hasTV)) || $hasXXX;
590
    }
591
592
    private function manticore(): ManticoreSearch
593
    {
594
        if ($this->manticore === null) {
595
            $this->manticore = new ManticoreSearch();
596
        }
597
        return $this->manticore;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->manticore could return the type null which is incompatible with the type-hinted return Blacklight\ManticoreSearch. Consider adding an additional type-check to rule them out.
Loading history...
598
    }
599
600
    private function elasticsearch(): ElasticSearchSiteSearch
601
    {
602
        if ($this->elasticsearch === null) {
603
            $this->elasticsearch = new ElasticSearchSiteSearch();
604
        }
605
        return $this->elasticsearch;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->elasticsearch could return the type null which is incompatible with the type-hinted return Blacklight\ElasticSearchSiteSearch. Consider adding an additional type-check to rule them out.
Loading history...
606
    }
607
}
608
609