NameFixingService::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 15
dl 0
loc 27
rs 9.7666
c 1
b 0
f 0
cc 1
nc 1
nop 9

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Services\NameFixing;
6
7
use App\Models\Category;
8
use App\Models\Release;
9
use App\Services\NameFixing\Contracts\NameSourceFixerInterface;
10
use App\Services\NameFixing\DTO\NameFixResult;
11
use App\Services\NameFixing\Extractors\NfoNameExtractor;
12
use App\Services\NameFixing\Extractors\FileNameExtractor;
13
use App\Services\Search\ElasticSearchService;
14
use App\Services\Search\ManticoreSearchService;
15
use Blacklight\ColorCLI;
16
use Illuminate\Support\Arr;
17
18
/**
19
 * Main service for name fixing operations.
20
 *
21
 * Orchestrates the various name fixing sources (NFO, Files, CRC, SRR, etc.)
22
 * and handles the overall processing flow.
23
 */
24
class NameFixingService
25
{
26
    // Constants for name fixing status
27
    public const PROC_NFO_NONE = 0;
28
    public const PROC_NFO_DONE = 1;
29
    public const PROC_FILES_NONE = 0;
30
    public const PROC_FILES_DONE = 1;
31
    public const PROC_PAR2_NONE = 0;
32
    public const PROC_PAR2_DONE = 1;
33
    public const PROC_UID_NONE = 0;
34
    public const PROC_UID_DONE = 1;
35
    public const PROC_HASH16K_NONE = 0;
36
    public const PROC_HASH16K_DONE = 1;
37
    public const PROC_SRR_NONE = 0;
38
    public const PROC_SRR_DONE = 1;
39
    public const PROC_CRC_NONE = 0;
40
    public const PROC_CRC_DONE = 1;
41
42
    // Constants for overall rename status
43
    public const IS_RENAMED_NONE = 0;
44
    public const IS_RENAMED_DONE = 1;
45
46
    protected ReleaseUpdateService $updateService;
47
    protected NameCheckerService $checkerService;
48
    protected NfoNameExtractor $nfoExtractor;
49
    protected FileNameExtractor $fileExtractor;
50
    protected FileNameCleaner $fileNameCleaner;
51
    protected FilePrioritizer $filePrioritizer;
52
    protected ManticoreSearchService $manticore;
53
    protected ElasticSearchService $elasticsearch;
54
    protected ColorCLI $colorCLI;
55
    protected bool $echoOutput;
56
57
    protected string $othercats;
58
    protected string $timeother;
59
    protected string $timeall;
60
    protected string $fullother;
61
    protected string $fullall;
62
63
    protected int $_totalReleases = 0;
64
65
    public function __construct(
66
        ?ReleaseUpdateService $updateService = null,
67
        ?NameCheckerService $checkerService = null,
68
        ?NfoNameExtractor $nfoExtractor = null,
69
        ?FileNameExtractor $fileExtractor = null,
70
        ?FileNameCleaner $fileNameCleaner = null,
71
        ?FilePrioritizer $filePrioritizer = null,
72
        ?ManticoreSearchService $manticore = null,
73
        ?ElasticSearchService $elasticsearch = null,
74
        ?ColorCLI $colorCLI = null
75
    ) {
76
        $this->updateService = $updateService ?? new ReleaseUpdateService();
77
        $this->checkerService = $checkerService ?? new NameCheckerService();
78
        $this->nfoExtractor = $nfoExtractor ?? new NfoNameExtractor();
79
        $this->fileExtractor = $fileExtractor ?? new FileNameExtractor();
80
        $this->fileNameCleaner = $fileNameCleaner ?? new FileNameCleaner();
81
        $this->filePrioritizer = $filePrioritizer ?? new FilePrioritizer();
82
        $this->manticore = $manticore ?? app(ManticoreSearchService::class);
83
        $this->elasticsearch = $elasticsearch ?? app(ElasticSearchService::class);
84
        $this->colorCLI = $colorCLI ?? new ColorCLI();
85
        $this->echoOutput = config('nntmux.echocli');
86
87
        $this->othercats = implode(',', Category::OTHERS_GROUP);
88
        $this->timeother = sprintf(' AND rel.adddate > (NOW() - INTERVAL 6 HOUR) AND rel.categories_id IN (%s) GROUP BY rel.id ORDER BY postdate DESC', $this->othercats);
89
        $this->timeall = ' AND rel.adddate > (NOW() - INTERVAL 6 HOUR) GROUP BY rel.id ORDER BY postdate DESC';
90
        $this->fullother = sprintf(' AND rel.categories_id IN (%s) GROUP BY rel.id', $this->othercats);
91
        $this->fullall = '';
92
    }
93
94
    /**
95
     * Fix names using NFO content.
96
     */
97
    public function fixNamesWithNfo(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
98
    {
99
        $this->echoStartMessage($time, '.nfo files');
100
        $type = 'NFO, ';
101
102
        $preId = false;
103
        if ($cats === 3) {
104
            $query = sprintf(
105
                'SELECT rel.id AS releases_id, rel.fromname
106
                FROM releases rel
107
                INNER JOIN release_nfos nfo ON (nfo.releases_id = rel.id)
108
                WHERE rel.predb_id = 0'
109
            );
110
            $cats = 2;
111
            $preId = true;
112
        } else {
113
            $query = sprintf(
114
                'SELECT rel.id AS releases_id, rel.fromname
115
                FROM releases rel
116
                INNER JOIN release_nfos nfo ON (nfo.releases_id = rel.id)
117
                WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
118
                AND rel.predb_id = 0
119
                AND rel.proc_nfo = %d',
120
                self::IS_RENAMED_NONE,
121
                Category::OTHER_MISC,
122
                Category::OTHER_HASHED,
123
                self::PROC_NFO_NONE
124
            );
125
        }
126
127
        $releases = $this->getReleases($time, $cats, $query);
128
        $total = $releases->count();
129
130
        if ($total > 0) {
131
            $this->_totalReleases = $total;
132
            $this->colorCLI->info(number_format($total) . ' releases to process.');
133
134
            foreach ($releases as $rel) {
135
                $releaseRow = Release::fromQuery(
136
                    sprintf(
137
                        'SELECT nfo.releases_id AS nfoid, rel.groups_id, rel.fromname, rel.categories_id, rel.name, rel.searchname,
138
                            UNCOMPRESS(nfo) AS textstring, rel.id AS releases_id
139
                        FROM releases rel
140
                        INNER JOIN release_nfos nfo ON (nfo.releases_id = rel.id)
141
                        WHERE rel.id = %d LIMIT 1',
142
                        $rel->releases_id
143
                    )
144
                );
145
146
                $this->updateService->incrementChecked();
147
148
                // Ignore encrypted NFOs
149
                if (preg_match('/^=newz\[NZB\]=\w+/', $releaseRow[0]->textstring)) {
150
                    $this->updateService->updateSingleColumn('proc_nfo', self::PROC_NFO_DONE, $rel->releases_id);
151
                    continue;
152
                }
153
154
                $this->updateService->reset();
155
156
                // Try NFO extraction
157
                $nfoResult = $this->nfoExtractor->extractFromNfo($releaseRow[0]->textstring);
158
                if ($nfoResult !== null) {
159
                    $this->updateService->updateRelease(
160
                        $releaseRow[0],
161
                        $nfoResult->newName,
162
                        'nfoCheck: ' . $nfoResult->method,
163
                        $echo,
164
                        $type,
165
                        $nameStatus,
166
                        $show
167
                    );
168
                }
169
170
                // If NFO extraction didn't work, try pattern checkers
171
                if (!$this->updateService->matched) {
172
                    $this->checkWithPatternMatchers($releaseRow[0], $echo, $type, $nameStatus, $show, $preId);
173
                }
174
175
                $this->echoRenamed($show);
176
            }
177
            $this->echoFoundCount($echo, ' NFO\'s');
178
        } else {
179
            $this->colorCLI->info('Nothing to fix.');
180
        }
181
    }
182
183
    /**
184
     * Fix names using file names.
185
     */
186
    public function fixNamesWithFiles(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
187
    {
188
        $this->echoStartMessage($time, 'file names');
189
        $type = 'Filenames, ';
190
191
        $preId = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $preId is dead and can be removed.
Loading history...
192
        if ($cats === 3) {
193
            $query = sprintf(
194
                'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
195
                    rf.releases_id AS fileid, rel.id AS releases_id
196
                FROM releases rel
197
                INNER JOIN release_files rf ON rf.releases_id = rel.id
198
                WHERE predb_id = 0'
199
            );
200
            $cats = 2;
201
            $preId = true;
202
        } else {
203
            $query = sprintf(
204
                'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
205
                    rf.releases_id AS fileid, rel.id AS releases_id
206
                FROM releases rel
207
                INNER JOIN release_files rf ON rf.releases_id = rel.id
208
                WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
209
                AND rel.predb_id = 0
210
                AND proc_files = %d',
211
                self::IS_RENAMED_NONE,
212
                Category::OTHER_MISC,
213
                Category::OTHER_HASHED,
214
                self::PROC_FILES_NONE
215
            );
216
        }
217
218
        $releases = $this->getReleases($time, $cats, $query);
219
        $total = $releases->count();
220
221
        if ($total > 0) {
222
            $this->_totalReleases = $total;
223
            $this->colorCLI->info(number_format($total) . ' file names to process.');
224
225
            // Group files by release
226
            $releaseFiles = [];
227
            foreach ($releases as $release) {
228
                $releaseId = $release->releases_id;
229
                if (!isset($releaseFiles[$releaseId])) {
230
                    $releaseFiles[$releaseId] = [
231
                        'release' => $release,
232
                        'files' => [],
233
                    ];
234
                }
235
                $releaseFiles[$releaseId]['files'][] = $release->textstring;
236
            }
237
238
            foreach ($releaseFiles as $releaseId => $data) {
239
                $this->updateService->reset();
240
                $this->updateService->incrementChecked();
241
242
                // Prioritize files for matching
243
                $prioritizedFiles = $this->filePrioritizer->prioritizeForMatching($data['files']);
244
245
                foreach ($prioritizedFiles as $filename) {
246
                    $release = clone $data['release'];
247
                    $release->textstring = $filename;
248
249
                    // Try file name extraction
250
                    $fileResult = $this->fileExtractor->extractFromFile($filename);
251
                    if ($fileResult !== null) {
252
                        $this->updateService->updateRelease(
253
                            $release,
254
                            $fileResult->newName,
255
                            'fileCheck: ' . $fileResult->method,
256
                            $echo,
257
                            $type,
258
                            $nameStatus,
259
                            $show
260
                        );
261
                    }
262
263
                    // If not matched, try PreDB search
264
                    if (!$this->updateService->matched) {
265
                        $this->preDbFileCheck($release, $echo, $type, $nameStatus, $show);
266
                    }
267
268
                    if (!$this->updateService->matched) {
269
                        $this->preDbTitleCheck($release, $echo, $type, $nameStatus, $show);
270
                    }
271
272
                    if ($this->updateService->matched) {
273
                        break;
274
                    }
275
                }
276
277
                $this->echoRenamed($show);
278
            }
279
280
            $this->echoFoundCount($echo, ' files');
281
        } else {
282
            $this->colorCLI->info('Nothing to fix.');
283
        }
284
    }
285
286
    /**
287
     * Fix names using SRR files.
288
     */
289
    public function fixNamesWithSrr(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
290
    {
291
        $this->echoStartMessage($time, 'SRR file names');
292
        $type = 'SRR, ';
293
294
        if ($cats === 3) {
295
            $query = sprintf(
296
                'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
297
                    rf.releases_id AS fileid, rel.id AS releases_id
298
                FROM releases rel
299
                INNER JOIN release_files rf ON (rf.releases_id = rel.id)
300
                WHERE predb_id = 0
301
                AND (rf.name LIKE %s OR rf.name LIKE %s)',
302
                escapeString('%.srr'),
303
                escapeString('%.srs')
304
            );
305
            $cats = 2;
306
        } else {
307
            $query = sprintf(
308
                'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
309
                    rf.releases_id AS fileid, rel.id AS releases_id
310
                FROM releases rel
311
                INNER JOIN release_files rf ON (rf.releases_id = rel.id)
312
                WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
313
                AND rel.predb_id = 0
314
                AND (rf.name LIKE %s OR rf.name LIKE %s)
315
                AND rel.proc_srr = %d',
316
                self::IS_RENAMED_NONE,
317
                Category::OTHER_MISC,
318
                Category::OTHER_HASHED,
319
                escapeString('%.srr'),
320
                escapeString('%.srs'),
321
                self::PROC_SRR_NONE
322
            );
323
        }
324
325
        $releases = $this->getReleases($time, $cats, $query);
326
        $total = $releases->count();
327
328
        if ($total > 0) {
329
            $this->_totalReleases = $total;
330
            $this->colorCLI->info(number_format($total) . ' srr file extensions to process.');
331
332
            foreach ($releases as $release) {
333
                $this->updateService->reset();
334
                $this->updateService->incrementChecked();
335
336
                $this->srrNameCheck($release, $echo, $type, $nameStatus, $show);
337
                $this->echoRenamed($show);
338
            }
339
340
            $this->echoFoundCount($echo, ' files');
341
        } else {
342
            $this->colorCLI->info('Nothing to fix.');
343
        }
344
    }
345
346
    /**
347
     * Fix names using CRC32 hashes.
348
     */
349
    public function fixNamesWithCrc(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
350
    {
351
        $this->echoStartMessage($time, 'CRC32');
352
        $type = 'CRC32, ';
353
354
        $preId = false;
355
        if ($cats === 3) {
356
            $query = sprintf(
357
                'SELECT rf.crc32 AS textstring, rf.name AS filename, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id, rel.size as relsize,
358
                    rf.releases_id AS fileid, rel.id AS releases_id
359
                FROM releases rel
360
                INNER JOIN release_files rf ON rf.releases_id = rel.id
361
                WHERE predb_id = 0
362
                AND rf.crc32 != \'\'
363
                AND rf.crc32 IS NOT NULL'
364
            );
365
            $cats = 2;
366
            $preId = true;
367
        } else {
368
            $query = sprintf(
369
                'SELECT rf.crc32 AS textstring, rf.name AS filename, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id, rel.size as relsize,
370
                    rf.releases_id AS fileid, rel.id AS releases_id
371
                FROM releases rel
372
                INNER JOIN release_files rf ON rf.releases_id = rel.id
373
                WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
374
                AND rel.predb_id = 0
375
                AND rel.proc_crc32 = %d
376
                AND rf.crc32 != \'\'
377
                AND rf.crc32 IS NOT NULL',
378
                self::IS_RENAMED_NONE,
379
                Category::OTHER_MISC,
380
                Category::OTHER_HASHED,
381
                self::PROC_CRC_NONE
382
            );
383
        }
384
385
        $releases = $this->getReleases($time, $cats, $query);
386
        $total = $releases->count();
387
388
        if ($total > 0) {
389
            $this->_totalReleases = $total;
390
            $this->colorCLI->info(number_format($total) . ' CRC32\'s to process.');
391
392
            // Group by release
393
            $releasesCrc = [];
394
            foreach ($releases as $release) {
395
                $releaseId = $release->releases_id;
396
                if (!isset($releasesCrc[$releaseId])) {
397
                    $releasesCrc[$releaseId] = [
398
                        'release' => $release,
399
                        'crcs' => [],
400
                    ];
401
                }
402
                if (!empty($release->textstring)) {
403
                    $priority = $this->filePrioritizer->getCrcPriority($release->filename ?? '');
404
                    $releasesCrc[$releaseId]['crcs'][$priority][] = $release->textstring;
405
                }
406
            }
407
408
            foreach ($releasesCrc as $releaseId => $data) {
409
                $this->updateService->reset();
410
                $this->updateService->incrementChecked();
411
412
                ksort($data['crcs']);
413
                foreach ($data['crcs'] as $crcs) {
414
                    foreach ($crcs as $crc) {
415
                        $release = clone $data['release'];
416
                        $release->textstring = $crc;
417
418
                        $this->crcCheck($release, $echo, $type, $nameStatus, $show, $preId);
419
420
                        if ($this->updateService->matched) {
421
                            break 2;
422
                        }
423
                    }
424
                }
425
426
                $this->echoRenamed($show);
427
            }
428
429
            $this->echoFoundCount($echo, ' crc32\'s');
430
        } else {
431
            $this->colorCLI->info('Nothing to fix.');
432
        }
433
    }
434
435
    /**
436
     * Fix names using Media info unique IDs.
437
     */
438
    public function fixNamesWithMedia(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
439
    {
440
        $type = 'UID, ';
441
        $this->echoStartMessage($time, 'mediainfo Unique_IDs');
442
443
        if ($cats === 3) {
444
            $query = sprintf(
445
                'SELECT rel.id AS releases_id, rel.size AS relsize, rel.groups_id, rel.fromname, rel.categories_id,
446
                    rel.name, rel.name AS textstring, rel.predb_id, rel.searchname,
447
                    ru.unique_id AS uid
448
                FROM releases rel
449
                LEFT JOIN media_infos ru ON ru.releases_id = rel.id
450
                WHERE ru.releases_id IS NOT NULL
451
                AND rel.predb_id = 0'
452
            );
453
            $cats = 2;
454
        } else {
455
            $query = sprintf(
456
                'SELECT rel.id AS releases_id, rel.size AS relsize, rel.groups_id, rel.fromname, rel.categories_id,
457
                    rel.name, rel.name AS textstring, rel.predb_id, rel.searchname,
458
                    ru.unique_id AS uid
459
                FROM releases rel
460
                LEFT JOIN media_infos ru ON ru.releases_id = rel.id
461
                WHERE ru.releases_id IS NOT NULL
462
                AND (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
463
                AND rel.predb_id = 0
464
                AND rel.proc_uid = %d',
465
                self::IS_RENAMED_NONE,
466
                Category::OTHER_MISC,
467
                Category::OTHER_HASHED,
468
                self::PROC_UID_NONE
469
            );
470
        }
471
472
        $releases = $this->getReleases($time, $cats, $query);
473
        $total = $releases->count();
474
475
        if ($total > 0) {
476
            $this->_totalReleases = $total;
477
            $this->colorCLI->info(number_format($total) . ' unique ids to process.');
478
479
            foreach ($releases as $rel) {
480
                $this->updateService->reset();
481
                $this->updateService->incrementChecked();
482
                $this->uidCheck($rel, $echo, $type, $nameStatus, $show);
483
                $this->echoRenamed($show);
484
            }
485
486
            $this->echoFoundCount($echo, ' UID\'s');
487
        } else {
488
            $this->colorCLI->info('Nothing to fix.');
489
        }
490
    }
491
492
    /**
493
     * Fix names using PAR2 hash_16K.
494
     */
495
    public function fixNamesWithParHash(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
496
    {
497
        $type = 'PAR2 hash, ';
498
        $this->echoStartMessage($time, 'PAR2 hash_16K');
499
500
        if ($cats === 3) {
501
            $query = sprintf(
502
                'SELECT rel.id AS releases_id, rel.size AS relsize, rel.groups_id, rel.fromname, rel.categories_id,
503
                    rel.name, rel.name AS textstring, rel.predb_id, rel.searchname,
504
                    IFNULL(ph.hash, \'\') AS hash
505
                FROM releases rel
506
                LEFT JOIN par_hashes ph ON ph.releases_id = rel.id
507
                WHERE ph.hash != \'\'
508
                AND rel.predb_id = 0'
509
            );
510
            $cats = 2;
511
        } else {
512
            $query = sprintf(
513
                'SELECT rel.id AS releases_id, rel.size AS relsize, rel.groups_id, rel.fromname, rel.categories_id,
514
                    rel.name, rel.name AS textstring, rel.predb_id, rel.searchname,
515
                    IFNULL(ph.hash, \'\') AS hash
516
                FROM releases rel
517
                LEFT JOIN par_hashes ph ON ph.releases_id = rel.id
518
                WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
519
                AND rel.predb_id = 0
520
                AND ph.hash != \'\'
521
                AND rel.proc_hash16k = %d',
522
                self::IS_RENAMED_NONE,
523
                Category::OTHER_MISC,
524
                Category::OTHER_HASHED,
525
                self::PROC_HASH16K_NONE
526
            );
527
        }
528
529
        $releases = $this->getReleases($time, $cats, $query);
530
        $total = $releases->count();
531
532
        if ($total > 0) {
533
            $this->_totalReleases = $total;
534
            $this->colorCLI->info(number_format($total) . ' hash_16K to process.');
535
536
            foreach ($releases as $rel) {
537
                $this->updateService->reset();
538
                $this->updateService->incrementChecked();
539
                $this->hashCheck($rel, $echo, $type, $nameStatus, $show);
540
                $this->echoRenamed($show);
541
            }
542
543
            $this->echoFoundCount($echo, ' hashes');
544
        } else {
545
            $this->colorCLI->info('Nothing to fix.');
546
        }
547
    }
548
549
    /**
550
     * Check with pattern matchers (TV, Movie, Game, App).
551
     */
552
    protected function checkWithPatternMatchers(object $release, bool $echo, string $type, bool $nameStatus, bool $show, bool $preId): void
553
    {
554
        // Check for PreDB match first
555
        $preDbMatch = $this->updateService->checkPreDbMatch($release, $release->textstring);
556
        if ($preDbMatch !== null) {
557
            $this->updateService->updateRelease(
558
                $release,
559
                $preDbMatch['title'],
560
                'preDB: Match',
561
                $echo,
562
                $type,
563
                $nameStatus,
564
                $show,
565
                $preDbMatch['id']
566
            );
567
            return;
568
        }
569
570
        if ($preId) {
571
            return;
572
        }
573
574
        // Try pattern checkers
575
        $result = $this->checkerService->check($release, $release->textstring);
576
        if ($result !== null) {
577
            $this->updateService->updateRelease(
578
                $release,
579
                $result->newName,
580
                $result->getFormattedMethod(),
581
                $echo,
582
                $type,
583
                $nameStatus,
584
                $show
585
            );
586
        }
587
    }
588
589
    /**
590
     * Check SRR file for release name.
591
     */
592
    protected function srrNameCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
593
    {
594
        $extractedName = null;
595
596
        if (preg_match('/^(.+)\.srr$/i', $release->textstring, $hit)) {
597
            $extractedName = $hit[1];
598
        } elseif (preg_match('/^(.+)\.srs$/i', $release->textstring, $hit)) {
599
            $extractedName = $hit[1];
600
        }
601
602
        if ($extractedName !== null) {
603
            if (preg_match('/[\\\\\/]([^\\\\\/]+)$/', $extractedName, $pathMatch)) {
604
                $extractedName = $pathMatch[1];
605
            }
606
607
            if (preg_match(ReleaseUpdateService::PREDB_REGEX, $extractedName)) {
608
                $this->updateService->updateRelease(
609
                    $release,
610
                    $extractedName,
611
                    'fileCheck: SRR extension',
612
                    $echo,
613
                    $type,
614
                    $nameStatus,
615
                    $show
616
                );
617
                return true;
618
            }
619
        }
620
621
        $this->updateService->updateSingleColumn('proc_srr', self::PROC_SRR_DONE, $release->releases_id);
622
        return false;
623
    }
624
625
    /**
626
     * Check CRC32 for matches.
627
     */
628
    protected function crcCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show, bool $preId): bool
0 ignored issues
show
Unused Code introduced by
The parameter $preId is not used and could be removed. ( Ignorable by Annotation )

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

628
    protected function crcCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show, /** @scrutinizer ignore-unused */ bool $preId): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
629
    {
630
        if ($release->textstring === '') {
631
            $this->updateService->updateSingleColumn('proc_crc32', self::PROC_CRC_DONE, $release->releases_id);
632
            return false;
633
        }
634
635
        $result = Release::fromQuery(
636
            sprintf(
637
                'SELECT rf.crc32, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id, rel.size as relsize, rel.predb_id as predb_id,
638
                    rf.releases_id AS fileid, rel.id AS releases_id
639
                FROM releases rel
640
                LEFT JOIN release_files rf ON rf.releases_id = rel.id
641
                WHERE rel.predb_id > 0
642
                AND rf.crc32 = %s',
643
                escapeString($release->textstring)
644
            )
645
        );
646
647
        foreach ($result as $res) {
648
            $floor = round(($res->relsize - $release->relsize) / $res->relsize * 100, 1);
649
            if ($floor >= -5 && $floor <= 5) {
650
                $this->updateService->updateRelease(
651
                    $release,
652
                    $res->searchname,
653
                    'crcCheck: CRC32',
654
                    $echo,
655
                    $type,
656
                    $nameStatus,
657
                    $show,
658
                    $res->predb_id
659
                );
660
                return true;
661
            }
662
        }
663
664
        $this->updateService->updateSingleColumn('proc_crc32', self::PROC_CRC_DONE, $release->releases_id);
665
        return false;
666
    }
667
668
    /**
669
     * Check UID for matches.
670
     */
671
    protected function uidCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
672
    {
673
        if (empty($release->uid)) {
674
            $this->updateService->updateSingleColumn('proc_uid', self::PROC_UID_DONE, $release->releases_id);
675
            return false;
676
        }
677
678
        $result = Release::fromQuery(sprintf(
679
            'SELECT r.id AS releases_id, r.size AS relsize, r.name AS textstring, r.searchname, r.fromname, r.predb_id
680
            FROM releases r
681
            LEFT JOIN media_infos ru ON ru.releases_id = r.id
682
            WHERE ru.releases_id IS NOT NULL
683
            AND ru.unique_id = %s
684
            AND ru.releases_id != %d
685
            AND (r.predb_id > 0 OR r.anidbid > 0 OR r.fromname = %s)',
686
            escapeString($release->uid),
687
            $release->releases_id,
688
            escapeString('[email protected] (EF)')
689
        ));
690
691
        foreach ($result as $res) {
692
            $floor = round(($res->relsize - $release->relsize) / $res->relsize * 100, 1);
693
            if ($floor >= -10 && $floor <= 10) {
694
                $this->updateService->updateRelease(
695
                    $release,
696
                    $res->searchname,
697
                    'uidCheck: Unique_ID',
698
                    $echo,
699
                    $type,
700
                    $nameStatus,
701
                    $show,
702
                    $res->predb_id
703
                );
704
                return true;
705
            }
706
        }
707
708
        $this->updateService->updateSingleColumn('proc_uid', self::PROC_UID_DONE, $release->releases_id);
709
        return false;
710
    }
711
712
    /**
713
     * Check PAR2 hash for matches.
714
     */
715
    protected function hashCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
716
    {
717
        $result = Release::fromQuery(sprintf(
718
            'SELECT r.id AS releases_id, r.size AS relsize, r.name AS textstring, r.searchname, r.fromname, r.predb_id
719
            FROM releases r
720
            LEFT JOIN par_hashes ph ON ph.releases_id = r.id
721
            WHERE ph.hash = %s
722
            AND ph.releases_id != %d
723
            AND (r.predb_id > 0 OR r.anidbid > 0)',
724
            escapeString($release->hash),
725
            $release->releases_id
726
        ));
727
728
        foreach ($result as $res) {
729
            $floor = round(($res->relsize - $release->relsize) / $res->relsize * 100, 1);
730
            if ($floor >= -5 && $floor <= 5) {
731
                $this->updateService->updateRelease(
732
                    $release,
733
                    $res->searchname,
734
                    'hashCheck: PAR2 hash_16K',
735
                    $echo,
736
                    $type,
737
                    $nameStatus,
738
                    $show,
739
                    $res->predb_id
740
                );
741
                return true;
742
            }
743
        }
744
745
        $this->updateService->updateSingleColumn('proc_hash16k', self::PROC_HASH16K_DONE, $release->releases_id);
746
        return false;
747
    }
748
749
    /**
750
     * Check PreDB for filename matches.
751
     */
752
    protected function preDbFileCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
753
    {
754
        $fileName = $this->fileNameCleaner->cleanForMatching($release->textstring);
755
756
        if (empty($fileName)) {
757
            return false;
758
        }
759
760
        if (config('nntmux.elasticsearch_enabled') === true) {
761
            $results = $this->elasticsearch->searchPreDb($fileName);
762
            foreach ($results as $hit) {
763
                if (!empty($hit)) {
764
                    $this->updateService->updateRelease(
765
                        $release,
766
                        $hit['title'],
767
                        'PreDb: Filename match',
768
                        $echo,
769
                        $type,
770
                        $nameStatus,
771
                        $show,
772
                        $hit['id']
773
                    );
774
                    return true;
775
                }
776
            }
777
        } else {
778
            $predbSearch = Arr::get($this->manticore->searchIndexes('predb_rt', $fileName, ['filename', 'title']), 'data');
779
            if (!empty($predbSearch)) {
780
                foreach ($predbSearch as $hit) {
781
                    if (!empty($hit)) {
782
                        $this->updateService->updateRelease(
783
                            $release,
784
                            $hit['title'],
785
                            'PreDb: Filename match',
786
                            $echo,
787
                            $type,
788
                            $nameStatus,
789
                            $show
790
                        );
791
                        return true;
792
                    }
793
                }
794
            }
795
        }
796
797
        return false;
798
    }
799
800
    /**
801
     * Check PreDB for title matches.
802
     */
803
    protected function preDbTitleCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
804
    {
805
        $fileName = $this->fileNameCleaner->cleanForMatching($release->textstring);
806
807
        if (empty($fileName)) {
808
            return false;
809
        }
810
811
        if (config('nntmux.elasticsearch_enabled') === true) {
812
            $results = $this->elasticsearch->searchPreDb($fileName);
813
            foreach ($results as $hit) {
814
                if (!empty($hit)) {
815
                    $this->updateService->updateRelease(
816
                        $release,
817
                        $hit['title'],
818
                        'PreDb: Title match',
819
                        $echo,
820
                        $type,
821
                        $nameStatus,
822
                        $show,
823
                        $hit['id']
824
                    );
825
                    return true;
826
                }
827
            }
828
        } else {
829
            $results = Arr::get($this->manticore->searchIndexes('predb_rt', $fileName, ['title']), 'data');
830
            if (!empty($results)) {
831
                foreach ($results as $hit) {
832
                    if (!empty($hit)) {
833
                        $this->updateService->updateRelease(
834
                            $release,
835
                            $hit['title'],
836
                            'PreDb: Title match',
837
                            $echo,
838
                            $type,
839
                            $nameStatus,
840
                            $show
841
                        );
842
                        return true;
843
                    }
844
                }
845
            }
846
        }
847
848
        return false;
849
    }
850
851
    /**
852
     * Get releases based on time and category parameters.
853
     */
854
    protected function getReleases(int $time, int $cats, string $query, int $limit = 0): \Illuminate\Database\Eloquent\Collection|bool
855
    {
856
        $releases = false;
857
        $queryLimit = ($limit === 0) ? '' : ' LIMIT ' . $limit;
858
859
        if ($time === 1 && $cats === 1) {
860
            $releases = Release::fromQuery($query . $this->timeother . $queryLimit);
861
        }
862
        if ($time === 1 && $cats === 2) {
863
            $releases = Release::fromQuery($query . $this->timeall . $queryLimit);
864
        }
865
        if ($time === 2 && $cats === 1) {
866
            $releases = Release::fromQuery($query . $this->fullother . $queryLimit);
867
        }
868
        if ($time === 2 && $cats === 2) {
869
            $releases = Release::fromQuery($query . $this->fullall . $queryLimit);
870
        }
871
872
        return $releases;
873
    }
874
875
    /**
876
     * Echo start message.
877
     */
878
    protected function echoStartMessage(int $time, string $type): void
879
    {
880
        $this->colorCLI->info(
881
            sprintf(
882
                'Fixing search names %s using %s.',
883
                ($time === 1 ? 'in the past 6 hours' : 'since the beginning'),
884
                $type
885
            )
886
        );
887
    }
888
889
    /**
890
     * Echo found count.
891
     */
892
    protected function echoFoundCount(bool $echo, string $type): void
893
    {
894
        $stats = $this->updateService->getStats();
895
        if ($echo === true) {
896
            $this->colorCLI->info(
897
                PHP_EOL .
898
                number_format($stats['fixed']) .
899
                ' releases have had their names changed out of: ' .
900
                number_format($stats['checked']) .
901
                $type . '.'
902
            );
903
        } else {
904
            $this->colorCLI->info(
905
                PHP_EOL .
906
                number_format($stats['fixed']) .
907
                ' releases could have their names changed. ' .
908
                number_format($stats['checked']) .
909
                $type . ' were checked.'
910
            );
911
        }
912
    }
913
914
    /**
915
     * Echo renamed progress.
916
     */
917
    protected function echoRenamed(bool $show): void
918
    {
919
        $stats = $this->updateService->getStats();
920
921
        // Show milestone message every 500 releases
922
        if ($stats['checked'] % 500 === 0 && $stats['checked'] > 0) {
923
            $this->colorCLI->alternate(PHP_EOL . number_format($stats['checked']) . ' files processed.' . PHP_EOL);
924
        }
925
926
        // Show active counter on the same line (overwrites previous)
927
        if ($show === true) {
928
            $percent = $this->_totalReleases > 0
929
                ? round(($stats['checked'] / $this->_totalReleases) * 100, 1)
930
                : 0;
931
932
            // Use carriage return to overwrite the same line
933
            echo "\rRenamed: " . number_format($stats['fixed']) .
934
                 ' | Processed: ' . number_format($stats['checked']) .
935
                 '/' . number_format($this->_totalReleases) .
936
                 ' (' . $percent . '%)    ';
937
        }
938
    }
939
940
    /**
941
     * Get the update service.
942
     */
943
    public function getUpdateService(): ReleaseUpdateService
944
    {
945
        return $this->updateService;
946
    }
947
948
    /**
949
     * Get the checker service.
950
     */
951
    public function getCheckerService(): NameCheckerService
952
    {
953
        return $this->checkerService;
954
    }
955
956
    /**
957
     * Fix names using PAR2 files (requires NNTP connection).
958
     */
959
    public function fixNamesWithPar2(int $time, bool $echo, int $cats, bool $nameStatus, bool $show, \Blacklight\NNTP $nntp): void
0 ignored issues
show
Unused Code introduced by
The parameter $nntp is not used and could be removed. ( Ignorable by Annotation )

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

959
    public function fixNamesWithPar2(int $time, bool $echo, int $cats, bool $nameStatus, bool $show, /** @scrutinizer ignore-unused */ \Blacklight\NNTP $nntp): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
960
    {
961
        $this->echoStartMessage($time, 'par2 files');
962
963
        if ($cats === 3) {
964
            $query = sprintf(
965
                'SELECT rel.id AS releases_id, rel.guid, rel.groups_id, rel.fromname
966
                FROM releases rel
967
                WHERE rel.predb_id = 0'
968
            );
969
            $cats = 2;
970
        } else {
971
            $query = sprintf(
972
                'SELECT rel.id AS releases_id, rel.guid, rel.groups_id, rel.fromname
973
                FROM releases rel
974
                WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
975
                AND rel.predb_id = 0
976
                AND rel.proc_par2 = %d',
977
                self::IS_RENAMED_NONE,
978
                Category::OTHER_MISC,
979
                Category::OTHER_HASHED,
980
                self::PROC_PAR2_NONE
981
            );
982
        }
983
984
        $releases = $this->getReleases($time, $cats, $query);
985
        $total = $releases ? $releases->count() : 0;
986
987
        if ($total > 0) {
988
            $this->_totalReleases = $total;
989
            $this->colorCLI->info(number_format($total) . ' releases to process.');
990
            $nzbContents = new \Blacklight\NZBContents();
991
992
            foreach ($releases as $release) {
993
                if ($nzbContents->checkPAR2($release->guid, $release->releases_id, $release->groups_id, (int) $nameStatus, (int) $show)) {
994
                    $this->updateService->fixed++;
995
                }
996
997
                $this->updateService->incrementChecked();
998
                $this->echoRenamed($show);
999
            }
1000
            $this->echoFoundCount($echo, ' files');
1001
        } else {
1002
            $this->colorCLI->info('Nothing to fix.');
1003
        }
1004
    }
1005
1006
    /**
1007
     * Fix XXX release names using specific file names.
1008
     */
1009
    public function fixXXXNamesWithFiles(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
1010
    {
1011
        $this->echoStartMessage($time, 'file names');
1012
        $type = 'Filenames, ';
1013
1014
        if ($cats === 3) {
1015
            $query = sprintf(
1016
                'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
1017
                    rf.releases_id AS fileid, rel.id AS releases_id
1018
                FROM releases rel
1019
                INNER JOIN release_files rf ON rf.releases_id = rel.id
1020
                WHERE predb_id = 0'
1021
            );
1022
            $cats = 2;
1023
        } else {
1024
            $query = sprintf(
1025
                'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
1026
                    rf.releases_id AS fileid, rel.id AS releases_id
1027
                FROM releases rel
1028
                INNER JOIN release_files rf ON rf.releases_id = rel.id
1029
                WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
1030
                AND rel.predb_id = 0
1031
                AND rf.name LIKE %s',
1032
                self::IS_RENAMED_NONE,
1033
                Category::OTHER_MISC,
1034
                Category::OTHER_HASHED,
1035
                escapeString('%SDPORN%')
1036
            );
1037
        }
1038
1039
        $releases = $this->getReleases($time, $cats, $query);
1040
        $total = $releases ? $releases->count() : 0;
1041
1042
        if ($total > 0) {
1043
            $this->_totalReleases = $total;
1044
            $this->colorCLI->info(number_format($total) . ' xxx file names to process.');
1045
1046
            foreach ($releases as $release) {
1047
                $this->updateService->reset();
1048
                $this->xxxNameCheck($release, $echo, $type, $nameStatus, $show);
1049
                $this->updateService->incrementChecked();
1050
                $this->echoRenamed($show);
1051
            }
1052
            $this->echoFoundCount($echo, ' files');
1053
        } else {
1054
            $this->colorCLI->info('Nothing to fix.');
1055
        }
1056
    }
1057
1058
    /**
1059
     * Fix release names using mediainfo movie_name.
1060
     */
1061
    public function fixNamesWithMediaMovieName(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
1062
    {
1063
        $type = 'Mediainfo, ';
1064
        $this->echoStartMessage($time, 'Mediainfo movie_name');
1065
1066
        if ($cats === 3) {
1067
            $query = sprintf(
1068
                'SELECT rel.id AS releases_id, rel.name, rel.name AS textstring, rel.predb_id, rel.searchname, rel.fromname, rel.groups_id, rel.categories_id, rel.id AS releases_id, rf.movie_name as movie_name
1069
                FROM releases rel
1070
                INNER JOIN media_infos rf ON rf.releases_id = rel.id
1071
                WHERE rel.predb_id = 0'
1072
            );
1073
            $cats = 2;
1074
        } else {
1075
            $query = sprintf(
1076
                'SELECT rel.id AS releases_id, rel.name, rel.name AS textstring, rel.predb_id, rel.searchname, rel.fromname, rel.groups_id, rel.categories_id, rel.id AS releases_id, rf.movie_name as movie_name, rf.file_name as file_name
1077
                FROM releases rel
1078
                INNER JOIN media_infos rf ON rf.releases_id = rel.id
1079
                WHERE rel.isrenamed = %d
1080
                AND rel.predb_id = 0',
1081
                self::IS_RENAMED_NONE
1082
            );
1083
            if ($cats === 2) {
1084
                $query .= PHP_EOL . 'AND rel.categories_id IN (' . Category::OTHER_MISC . ',' . Category::OTHER_HASHED . ')';
1085
            }
1086
        }
1087
1088
        $releases = $this->getReleases($time, $cats, $query);
1089
        $total = $releases ? $releases->count() : 0;
1090
1091
        if ($total > 0) {
1092
            $this->_totalReleases = $total;
1093
            $this->colorCLI->info(number_format($total) . ' mediainfo movie names to process.');
1094
1095
            foreach ($releases as $rel) {
1096
                $this->updateService->incrementChecked();
1097
                $this->updateService->reset();
1098
                $this->mediaMovieNameCheck($rel, $echo, $type, $nameStatus, $show);
1099
                $this->echoRenamed($show);
1100
            }
1101
            $this->echoFoundCount($echo, ' MediaInfo\'s');
1102
        } else {
1103
            $this->colorCLI->info('Nothing to fix.');
1104
        }
1105
    }
1106
1107
    /**
1108
     * Check for XXX release name.
1109
     */
1110
    protected function xxxNameCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
1111
    {
1112
        if (preg_match('/^.+?SDPORN/i', $release->textstring, $hit)) {
1113
            $this->updateService->updateRelease($release, $hit[0], 'fileCheck: XXX SDPORN', $echo, $type, $nameStatus, $show);
1114
            return true;
1115
        }
1116
1117
        $this->updateService->updateSingleColumn('proc_files', self::PROC_FILES_DONE, $release->releases_id);
1118
        return false;
1119
    }
1120
1121
    /**
1122
     * Check mediainfo movie_name for release name.
1123
     */
1124
    protected function mediaMovieNameCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
1125
    {
1126
        $newName = '';
1127
1128
        if (!empty($release->movie_name)) {
1129
            if (preg_match(ReleaseUpdateService::PREDB_REGEX, $release->movie_name, $hit)) {
1130
                $newName = $hit[1];
1131
            } elseif (preg_match('/(.+),(\sRMZ\.cr)?$/i', $release->movie_name, $hit)) {
1132
                $newName = $hit[1];
1133
            } else {
1134
                $newName = $release->movie_name;
1135
            }
1136
        }
1137
1138
        if ($newName !== '') {
1139
            $this->updateService->updateRelease($release, $newName, 'MediaInfo: Movie Name', $echo, $type, $nameStatus, $show, $release->predb_id ?? 0);
1140
            return true;
1141
        }
1142
1143
        $this->updateService->updateSingleColumn('proc_uid', self::PROC_UID_DONE, $release->releases_id);
1144
        return false;
1145
    }
1146
1147
    /**
1148
     * Check the array using regex for a clean name.
1149
     *
1150
     * @throws \Exception
1151
     */
1152
    public function checkName(object $release, bool $echo, string $type, bool $nameStatus, bool $show, bool $preId = false): bool
1153
    {
1154
        // Check PreDB first
1155
        $preDbMatch = $this->updateService->checkPreDbMatch($release, $release->textstring);
1156
        if ($preDbMatch !== null) {
1157
            $this->updateService->updateRelease($release, $preDbMatch['title'], 'preDB: Match', $echo, $type, $nameStatus, $show, $preDbMatch['id']);
1158
            return true;
1159
        }
1160
1161
        if ($preId) {
1162
            return $this->updateService->matched;
1163
        }
1164
1165
        // Route to appropriate checker based on type
1166
        switch ($type) {
1167
            case 'PAR2, ':
1168
                $result = $this->fileExtractor->extractFromFile($release->textstring);
1169
                if ($result !== null) {
1170
                    $this->updateService->updateRelease($release, $result->newName, 'fileCheck: ' . $result->method, $echo, $type, $nameStatus, $show);
1171
                }
1172
                break;
1173
1174
            case 'NFO, ':
1175
                $result = $this->nfoExtractor->extractFromNfo($release->textstring);
1176
                if ($result !== null) {
1177
                    $this->updateService->updateRelease($release, $result->newName, 'nfoCheck: ' . $result->method, $echo, $type, $nameStatus, $show);
1178
                }
1179
                break;
1180
1181
            case 'Filenames, ':
1182
                // Try PreDB file check
1183
                if (!$this->updateService->matched) {
1184
                    $this->preDbFileCheck($release, $echo, $type, $nameStatus, $show);
1185
                }
1186
                // Try PreDB title check
1187
                if (!$this->updateService->matched) {
1188
                    $this->preDbTitleCheck($release, $echo, $type, $nameStatus, $show);
1189
                }
1190
                // Try file name extraction
1191
                if (!$this->updateService->matched) {
1192
                    $result = $this->fileExtractor->extractFromFile($release->textstring);
1193
                    if ($result !== null) {
1194
                        $this->updateService->updateRelease($release, $result->newName, 'fileCheck: ' . $result->method, $echo, $type, $nameStatus, $show);
1195
                    }
1196
                }
1197
                break;
1198
1199
            default:
1200
                // Use pattern checker service
1201
                $result = $this->checkerService->check($release, $release->textstring);
1202
                if ($result !== null) {
1203
                    $this->updateService->updateRelease($release, $result->newName, $result->getFormattedMethod(), $echo, $type, $nameStatus, $show);
1204
                }
1205
        }
1206
1207
        // Update processing flags if not matched
1208
        if ($nameStatus === true && !$this->updateService->matched) {
1209
            $this->updateProcessingFlags($type, $release->releases_id);
1210
        }
1211
1212
        return $this->updateService->matched;
1213
    }
1214
1215
    /**
1216
     * Update processing flags based on type.
1217
     */
1218
    protected function updateProcessingFlags(string $type, int $releaseId): void
1219
    {
1220
        switch ($type) {
1221
            case 'NFO, ':
1222
                $this->updateService->updateSingleColumn('proc_nfo', self::PROC_NFO_DONE, $releaseId);
1223
                break;
1224
            case 'Filenames, ':
1225
                $this->updateService->updateSingleColumn('proc_files', self::PROC_FILES_DONE, $releaseId);
1226
                break;
1227
            case 'PAR2, ':
1228
                $this->updateService->updateSingleColumn('proc_par2', self::PROC_PAR2_DONE, $releaseId);
1229
                break;
1230
            case 'PAR2 hash, ':
1231
                $this->updateService->updateSingleColumn('proc_hash16k', self::PROC_HASH16K_DONE, $releaseId);
1232
                break;
1233
            case 'SRR, ':
1234
                $this->updateService->updateSingleColumn('proc_srr', self::PROC_SRR_DONE, $releaseId);
1235
                break;
1236
            case 'UID, ':
1237
            case 'Mediainfo, ':
1238
                $this->updateService->updateSingleColumn('proc_uid', self::PROC_UID_DONE, $releaseId);
1239
                break;
1240
            case 'CRC32, ':
1241
                $this->updateService->updateSingleColumn('proc_crc32', self::PROC_CRC_DONE, $releaseId);
1242
                break;
1243
        }
1244
    }
1245
1246
    /**
1247
     * Match a release filename to a PreDB filename or title.
1248
     *
1249
     * @throws \Exception
1250
     */
1251
    public function matchPreDbFiles(object $release, bool $echo, bool $nameStatus, bool $show): int
1252
    {
1253
        $matching = 0;
1254
1255
        $files = explode('||', $release->filename ?? '');
1256
        $prioritizedFiles = $this->filePrioritizer->prioritizeForPreDb($files);
1257
1258
        foreach ($prioritizedFiles as $fileName) {
1259
            $cleanedFileName = $this->fileNameCleaner->cleanForMatching($fileName);
1260
1261
            if (empty($cleanedFileName) || strlen($cleanedFileName) < 8) {
1262
                continue;
1263
            }
1264
1265
            $preMatch = $this->preMatch($cleanedFileName);
1266
            if ($preMatch[0] === true) {
1267
                if (config('nntmux.elasticsearch_enabled') === true) {
1268
                    $results = $this->elasticsearch->searchPreDb($preMatch[1]);
1269
                } else {
1270
                    $results = Arr::get($this->manticore->searchIndexes('predb_rt', $preMatch[1], ['filename', 'title']), 'data');
1271
                }
1272
1273
                if (!empty($results)) {
1274
                    foreach ($results as $result) {
1275
                        if (!empty($result)) {
1276
                            $preFtMatch = $this->preMatch($result['filename'] ?? '');
1277
                            if ($preFtMatch[0] === true) {
1278
                                if ($result['title'] !== $release->searchname) {
1279
                                    $this->updateService->updateRelease($release, $result['title'], 'file matched source: ' . $result['source'], $echo, 'PreDB file match, ', $nameStatus, $show);
1280
                                } else {
1281
                                    $this->updateService->updateSingleColumn('predb_id', $result['id'], $release->releases_id);
1282
                                }
1283
                                $matching++;
1284
                                return $matching;
1285
                            }
1286
                        }
1287
                    }
1288
                }
1289
            }
1290
        }
1291
1292
        return $matching;
1293
    }
1294
1295
    /**
1296
     * Pre-match check for filename patterns.
1297
     */
1298
    protected function preMatch(string $fileName): array
1299
    {
1300
        $result = preg_match('/(\d{2}\.\d{2}\.\d{2})+([\w\-.]+[\w]$)/i', $fileName, $hit);
1301
        return [$result === 1, $hit[0] ?? ''];
1302
    }
1303
1304
    /**
1305
     * Check if a release name looks like a season pack.
1306
     */
1307
    public function isSeasonPack(string $name): bool
1308
    {
1309
        // Season pack pattern: S01 without E01
1310
        return (bool) preg_match('/S\d{1,2}(?!E\d)/i', $name);
1311
    }
1312
1313
    /**
1314
     * Reset the update service state.
1315
     */
1316
    public function reset(): void
1317
    {
1318
        $this->updateService->reset();
1319
    }
1320
1321
    /**
1322
     * Retrieves releases and their file names to attempt PreDB matches.
1323
     *
1324
     * @throws \Exception
1325
     */
1326
    public function getPreFileNames(array $args = []): void
1327
    {
1328
        $show = isset($args[2]) && $args[2] === 'show';
1329
1330
        if (isset($args[1]) && is_numeric($args[1])) {
1331
            $limit = 'LIMIT ' . $args[1];
1332
            $orderBy = 'ORDER BY r.id DESC';
1333
        } else {
1334
            $orderBy = 'ORDER BY r.id ASC';
1335
            $limit = 'LIMIT 1000000';
1336
        }
1337
1338
        $this->colorCLI->info(PHP_EOL . 'Match PreFiles ' . ($args[1] ?? 'all') . ' Started at ' . now());
1339
        $this->colorCLI->info('Matching predb filename to cleaned release_files.name.');
1340
1341
        $counter = $counted = 0;
1342
        $timeStart = now();
1343
1344
        $query = Release::fromQuery(
1345
            sprintf(
1346
                "SELECT r.id AS releases_id, r.name, r.searchname,
1347
                    r.fromname, r.groups_id, r.categories_id,
1348
                    GROUP_CONCAT(rf.name ORDER BY LENGTH(rf.name) DESC SEPARATOR '||') AS filename
1349
                FROM releases r
1350
                INNER JOIN release_files rf ON r.id = rf.releases_id
1351
                WHERE rf.name IS NOT NULL
1352
                AND r.predb_id = 0
1353
                AND r.categories_id IN (%s)
1354
                AND r.isrenamed = 0
1355
                GROUP BY r.id
1356
                %s %s",
1357
                implode(',', Category::OTHERS_GROUP),
1358
                $orderBy,
1359
                $limit
1360
            )
1361
        );
1362
1363
        if ($query->isNotEmpty()) {
1364
            $total = $query->count();
1365
1366
            if ($total > 0) {
1367
                $this->colorCLI->info(PHP_EOL . number_format($total) . ' releases to process.');
1368
1369
                foreach ($query as $row) {
1370
                    $success = $this->matchPreDbFiles($row, true, true, $show);
1371
                    if ($success === 1) {
1372
                        $counted++;
1373
                    }
1374
                    if ($show === false) {
1375
                        $this->colorCLI->info('Renamed Releases: [' . number_format($counted) . '] ' . (new ColorCLI())->percentString(++$counter, $total));
1376
                    }
1377
                }
1378
                $this->colorCLI->info(PHP_EOL . 'Renamed ' . number_format($counted) . ' releases in ' . now()->diffInSeconds($timeStart, true) . ' seconds.');
1379
            } else {
1380
                $this->colorCLI->info('Nothing to do.');
1381
            }
1382
        }
1383
    }
1384
}
1385