Passed
Push — master ( ef50ac...ef616b )
by Darko
06:25
created

Forking::processStartWork()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 0
1
<?php
2
3
namespace Blacklight\libraries;
4
5
use App\Models\Settings;
6
use App\Models\UsenetGroup;
7
use Blacklight\ColorCLI;
8
use Blacklight\Nfo;
9
use Blacklight\NNTP;
10
use Blacklight\NZB;
11
use Blacklight\processing\PostProcess;
12
use Illuminate\Support\Carbon;
13
use Illuminate\Support\Facades\DB;
14
use Illuminate\Support\Facades\Log;
15
use Opis\Closure\SerializableClosure;
16
use Spatie\Async\Output\SerializableException;
17
use Spatie\Async\Pool;
18
use Symfony\Component\Process\Process;
19
20
/**
21
 * Class Forking.
22
 *
23
 * This forks various scripts.
24
 *
25
 * For example, you get all the ID's of the active groups in the groups table, you then iterate over them and spawn
26
 * processes of misc/update_binaries.php passing the group ID's.
27
 */
28
class Forking
29
{
30
    public ColorCLI $colorCli;
31
32
    /**
33
     * @var int The type of output
34
     */
35
    protected int $outputType;
36
37
    /**
38
     * Path to do not run folder.
39
     */
40
    private string $dnr_path;
41
42
    /**
43
     * Work to work on.
44
     */
45
    private array $work = [];
46
47
    /**
48
     * How much work do we have to do?
49
     */
50
    public int $_workCount = 0;
51
52
    /**
53
     * The type of work we want to work on.
54
     */
55
    private string $workType = '';
56
57
    /**
58
     * List of passed in options for the current work type.
59
     */
60
    private array $workTypeOptions = [];
61
62
    /**
63
     * Max amount of child processes to do work at a time.
64
     */
65
    private int $maxProcesses = 1;
66
67
    /**
68
     * Group used for safe backfill.
69
     */
70
    private string $safeBackfillGroup = '';
71
72
    protected int $maxSize;
73
74
    protected int $minSize;
75
76
    protected int $maxRetries;
77
78
    protected int $dummy;
79
80
    private bool $processAdditional = false; // Should we process additional?
81
82
    private bool $processNFO = false; // Should we process NFOs?
83
84
    private bool $processMovies = false; // Should we process Movies?
85
86
    private bool $processTV = false; // Should we process TV?
87
88
    /**
89
     * Setup required parent / self vars.
90
     *
91
     * @throws \Exception
92
     */
93
    public function __construct()
94
    {
95
        SerializableClosure::removeSecurityProvider();
96
        $this->colorCli = new ColorCLI();
97
98
        $this->dnr_path = PHP_BINARY.' misc/update/multiprocessing/.do_not_run/switch.php "php  ';
99
100
        $this->maxSize = (int) Settings::settingValue('..maxsizetoprocessnfo');
101
        $this->minSize = (int) Settings::settingValue('..minsizetoprocessnfo');
102
        $this->maxRetries = (int) Settings::settingValue('..maxnforetries') >= 0 ? -((int) Settings::settingValue('..maxnforetries') + 1) : Nfo::NFO_UNPROC;
103
        $this->maxRetries = max($this->maxRetries, -8);
104
    }
105
106
    /**
107
     * Setup the class to work on a type of work, then process the work.
108
     * Valid work types:.
109
     *
110
     * @param  string  $type  The type of multiProcessing to do : backfill, binaries, releases, postprocess
111
     * @param  array  $options  Array containing arguments for the type of work.
112
     *
113
     * @throws \Exception
114
     */
115
    public function processWorkType(string $type, array $options = []): void
116
    {
117
        // Set/reset some variables.
118
        $startTime = now()->timestamp;
119
        $this->workType = $type;
120
        $this->workTypeOptions = $options;
121
        $this->processAdditional = $this->processNFO = $this->processTV = $this->processMovies = $this->ppRenamedOnly = false;
122
        $this->work = [];
123
124
        // Process extra work that should not be forked and done before forking.
125
        $this->processStartWork();
126
127
        // Get work to fork.
128
        $this->getWork();
129
130
        // Process extra work that should not be forked and done after.
131
        $this->processEndWork();
132
133
        if (config('nntmux.echocli')) {
134
            $this->colorCli->header(
135
                'Multi-processing for '.$this->workType.' finished in '.(now()->timestamp - $startTime).
136
                ' seconds at '.now()->toRfc2822String().'.'.PHP_EOL
137
            );
138
        }
139
    }
140
141
    /**
142
     * Only post process renamed movie / tv releases?
143
     */
144
    private bool $ppRenamedOnly;
145
146
    /**
147
     * Get work for our workers to work on, set the max child processes here.
148
     *
149
     * @throws \Exception
150
     */
151
    private function getWork(): void
152
    {
153
        $this->maxProcesses = 0;
154
155
        switch ($this->workType) {
156
            case 'backfill':
157
                $this->backfill();
158
                break;
159
160
            case 'binaries':
161
                $this->binaries();
162
                break;
163
164
            case 'fixRelNames_standard':
165
            case 'fixRelNames_predbft':
166
                $this->fixRelNames();
167
                break;
168
169
            case 'releases':
170
                $this->releases();
171
                break;
172
173
            case 'postProcess_ama':
174
                $this->processSingle();
175
                break;
176
177
            case 'postProcess_add':
178
                $this->postProcessAdd();
179
                break;
180
181
            case 'postProcess_mov':
182
                $this->ppRenamedOnly = (isset($this->workTypeOptions[0]) && $this->workTypeOptions[0] === true);
183
                $this->postProcessMov();
184
                break;
185
186
            case 'postProcess_nfo':
187
                $this->postProcessNfo();
188
                break;
189
190
            case 'postProcess_sha':
191
                $this->processSharing();
192
                break;
193
194
            case 'postProcess_tv':
195
                $this->ppRenamedOnly = (isset($this->workTypeOptions[0]) && $this->workTypeOptions[0] === true);
196
                $this->postProcessTv();
197
                break;
198
199
            case 'safe_backfill':
200
                $this->safeBackfill();
201
                break;
202
203
            case 'safe_binaries':
204
                $this->safeBinaries();
205
                break;
206
207
            case 'update_per_group':
208
                $this->updatePerGroup();
209
                break;
210
        }
211
    }
212
213
    /**
214
     * Process work if we have any.
215
     */
216
    private function processWork(): void
217
    {
218
        $this->_workCount = \count($this->work);
219
        if ($this->_workCount > 0 && config('nntmux.echocli') === true) {
220
            $this->colorCli->header(
221
                'Multi-processing started at '.now()->toRfc2822String().' for '.$this->workType.' with '.$this->_workCount.
222
                ' job(s) to do using a max of '.$this->maxProcesses.' child process(es).'
223
            );
224
        }
225
        if (empty($this->_workCount) && config('nntmux.echocli') === true) {
226
            $this->colorCli->header('No work to do!');
227
        }
228
    }
229
230
    /**
231
     * Process any work that does not need to be forked, but needs to run at the start.
232
     */
233
    private function processStartWork(): void
234
    {
235
        switch ($this->workType) {
236
            case 'safe_backfill':
237
            case 'safe_binaries':
238
                $this->_executeCommand(PHP_BINARY.' misc/update/tmux/bin/update_groups.php');
239
                break;
240
        }
241
    }
242
243
    /**
244
     * Process any work that does not need to be forked, but needs to run at the end.
245
     */
246
    private function processEndWork(): void
247
    {
248
        switch ($this->workType) {
249
            case 'update_per_group':
250
            case 'releases':
251
252
                $this->_executeCommand($this->dnr_path.'releases  '.\count($this->work).'_"');
253
254
                break;
255
        }
256
    }
257
258
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
259
    //////////////////////////////////////// All backFill code here ////////////////////////////////////////////////////
260
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
261
262
    private function backfill(): void
263
    {
264
        // The option for backFill is for doing up to x articles. Else it's done by date.
265
        $this->work = DB::select(
266
            sprintf(
267
                'SELECT name %s FROM usenet_groups WHERE backfill = 1',
268
                ($this->workTypeOptions[0] === false ? '' : (', '.$this->workTypeOptions[0].' AS max'))
269
            )
270
        );
271
272
        $pool = Pool::create()->concurrency($this->maxProcesses)->timeout(config('nntmux.multiprocessing_max_child_time'));
273
        $this->processWork();
274
        $maxWork = \count($this->work);
275
        foreach ($this->work as $group) {
276
            $pool->add(function () use ($group) {
277
                return $this->_executeCommand(PHP_BINARY.' misc/update/backfill.php '.$group->name.(isset($group->max) ? (' '.$group->max) : ''));
278
            }, 2000000)->then(function ($output) use ($group, $maxWork) {
279
                echo $output;
280
                $this->colorCli->primary('Task #'.$maxWork.' Backfilled group '.$group->name);
281
            })->catch(function (\Throwable $exception) {
282
                echo $exception->getMessage();
283
            })->catch(static function (SerializableException $serializableException) {
0 ignored issues
show
Unused Code introduced by
The parameter $serializableException 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

283
            })->catch(static function (/** @scrutinizer ignore-unused */ SerializableException $serializableException) {

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...
284
                //we do nothing here just catch the error and move on
285
            });
286
            $maxWork--;
287
        }
288
        $pool->wait();
289
    }
290
291
    private function safeBackfill(): void
292
    {
293
        $backfill_qty = (int) Settings::settingValue('site.tmux.backfill_qty');
294
        $backfill_order = (int) Settings::settingValue('site.tmux.backfill_order');
295
        $backfill_days = (int) Settings::settingValue('site.tmux.backfill_days');
296
        $maxMessages = (int) Settings::settingValue('..maxmssgs');
297
        $threads = (int) Settings::settingValue('..backfillthreads');
298
299
        $orderby = 'ORDER BY a.last_record ASC';
300
        switch ($backfill_order) {
301
            case 1:
302
                $orderby = 'ORDER BY first_record_postdate DESC';
303
                break;
304
305
            case 2:
306
                $orderby = 'ORDER BY first_record_postdate ASC';
307
                break;
308
309
            case 3:
310
                $orderby = 'ORDER BY name ASC';
311
                break;
312
313
            case 4:
314
                $orderby = 'ORDER BY name DESC';
315
                break;
316
317
            case 5:
318
                $orderby = 'ORDER BY a.last_record DESC';
319
                break;
320
        }
321
322
        $backfilldays = '';
323
        if ($backfill_days === 1) {
324
            $backfilldays = 'g.backfill_target';
325
        } elseif ($backfill_days === 2) {
326
            $backfilldays = now()->diffInDays(Carbon::createFromFormat('Y-m-d', Settings::settingValue('..safebackfilldate')));
327
        }
328
329
        $data = DB::select(
330
            sprintf(
331
                'SELECT g.name,
332
				g.first_record AS our_first,
333
				MAX(a.first_record) AS their_first,
334
				MAX(a.last_record) AS their_last
335
				FROM usenet_groups g
336
				INNER JOIN short_groups a ON g.name = a.name
337
				WHERE g.first_record IS NOT NULL
338
				AND g.first_record_postdate IS NOT NULL
339
				AND g.backfill = 1
340
				AND (NOW() - INTERVAL %s DAY ) < g.first_record_postdate
341
				GROUP BY a.name, a.last_record, g.name, g.first_record
342
				%s LIMIT 1',
343
                $backfilldays,
344
                $orderby
345
            )
346
        );
347
348
        $count = 0;
349
        if (! empty($data) && isset($data[0]->name)) {
350
            $this->safeBackfillGroup = $data[0]->name;
351
352
            $count = ($data[0]->our_first - $data[0]->their_first);
353
        }
354
355
        if ($count > 0) {
356
            if ($count > ($backfill_qty * $threads)) {
357
                $getEach = ceil(($backfill_qty * $threads) / $maxMessages);
358
            } else {
359
                $getEach = $count / $maxMessages;
360
            }
361
362
            $queues = [];
363
            for ($i = 0; $i <= $getEach - 1; $i++) {
364
                $queues[$i] = sprintf('get_range  backfill  %s  %s  %s  %s', $this->safeBackfillGroup, $data[0]->our_first - $i * $maxMessages - $maxMessages, $data[0]->our_first - $i * $maxMessages - 1, $i + 1);
365
            }
366
367
            $pool = Pool::create()->concurrency($threads)->timeout(config('nntmux.multiprocessing_max_child_time'));
368
369
            $this->processWork();
370
            foreach ($queues as $queue) {
371
                $pool->add(function () use ($queue) {
372
                    return $this->_executeCommand($this->dnr_path.$queue.'"');
373
                }, 2000000)->then(function ($output) {
374
                    echo $output;
375
                    $this->colorCli->primary('Backfilled group '.$this->safeBackfillGroup);
376
                })->catch(function (\Throwable $exception) {
377
                    echo $exception->getMessage();
378
                })->catch(static function (SerializableException $serializableException) {
0 ignored issues
show
Unused Code introduced by
The parameter $serializableException 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

378
                })->catch(static function (/** @scrutinizer ignore-unused */ SerializableException $serializableException) {

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...
379
                    //we do nothing here just catch the error and move on
380
                });
381
            }
382
            $pool->wait();
383
        } else {
384
            if (config('nntmux.echocli')) {
385
                $this->colorCli->primary('No backfill needed for group '.$this->safeBackfillGroup);
386
            }
387
        }
388
    }
389
390
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
391
    //////////////////////////////////////// All binaries code here ////////////////////////////////////////////////////
392
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
393
394
    private function binaries(): void
395
    {
396
        $this->work = DB::select(
397
            sprintf(
398
                'SELECT name, %d AS max FROM usenet_groups WHERE active = 1',
399
                $this->workTypeOptions[0]
400
            )
401
        );
402
403
        $this->maxProcesses = (int) Settings::settingValue('..binarythreads');
404
405
        $pool = Pool::create()->concurrency($this->maxProcesses)->timeout(config('nntmux.multiprocessing_max_child_time'));
406
407
        $maxWork = \count($this->work);
408
409
        $this->processWork();
410
        foreach ($this->work as $group) {
411
            $pool->add(function () use ($group) {
412
                return $this->_executeCommand(PHP_BINARY.' misc/update/update_binaries.php '.$group->name.' '.$group->max);
413
            }, 2000000)->then(function ($output) use ($group, $maxWork) {
414
                echo $output;
415
                $this->colorCli->primary('Task #'.$maxWork.' Updated group '.$group->name);
416
            })->catch(function (\Throwable $exception) {
417
                echo $exception->getMessage();
418
            })->catch(static function (SerializableException $serializableException) {
0 ignored issues
show
Unused Code introduced by
The parameter $serializableException 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

418
            })->catch(static function (/** @scrutinizer ignore-unused */ SerializableException $serializableException) {

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...
419
                //we do nothing here just catch the error and move on
420
            });
421
            $maxWork--;
422
        }
423
424
        $pool->wait();
425
    }
426
427
    /**
428
     * @throws \Exception
429
     */
430
    private function safeBinaries(): void
431
    {
432
        $maxHeaders = (int) Settings::settingValue('..max_headers_iteration') ?: 1000000;
433
        $maxMessages = (int) Settings::settingValue('..maxmssgs');
434
        $this->maxProcesses = (int) Settings::settingValue('..binarythreads');
435
436
        $this->work = DB::select(
437
            '
438
			SELECT g.name AS groupname, g.last_record AS our_last,
439
				a.last_record AS their_last
440
			FROM usenet_groups g
441
			INNER JOIN short_groups a ON g.active = 1 AND g.name = a.name
442
			ORDER BY a.last_record DESC'
443
        );
444
445
        if (! empty($this->work)) {
446
            $i = 1;
447
            $queues = [];
448
            foreach ($this->work as $group) {
449
                if ((int) $group->our_last === 0) {
450
                    $queues[$i] = sprintf('update_group_headers  %s', $group->groupname);
451
                    $i++;
452
                } else {
453
                    //only process if more than 20k headers available and skip the first 20k
454
                    $count = $group->their_last - $group->our_last - 20000;
455
                    if ($count <= $maxMessages * 2) {
456
                        $queues[$i] = sprintf('update_group_headers  %s', $group->groupname);
457
                        $i++;
458
                    } else {
459
                        $queues[$i] = sprintf('part_repair  %s', $group->groupname);
460
                        $i++;
461
                        $getEach = floor(min($count, $maxHeaders) / $maxMessages);
462
                        $remaining = min($count, $maxHeaders) - $getEach * $maxMessages;
463
                        for ($j = 0; $j < $getEach; $j++) {
464
                            $queues[$i] = sprintf('get_range  binaries  %s  %s  %s  %s', $group->groupname, $group->our_last + $j * $maxMessages + 1, $group->our_last + $j * $maxMessages + $maxMessages, $i);
465
                            $i++;
466
                        }
467
                        //add remainder to queue
468
                        $queues[$i] = sprintf('get_range  binaries  %s  %s  %s  %s', $group->groupname, $group->our_last + ($j + 1) * $maxMessages + 1, $group->our_last + ($j + 1) * $maxMessages + $remaining + 1, $i);
469
                        $i++;
470
                    }
471
                }
472
            }
473
            $pool = Pool::create()->concurrency($this->maxProcesses)->timeout(config('nntmux.multiprocessing_max_child_time'));
474
475
            $this->processWork();
476
            foreach ($queues as $queue) {
477
                preg_match('/alt\..+/i', $queue, $hit);
478
                $pool->add(function () use ($queue) {
479
                    return $this->_executeCommand($this->dnr_path.$queue.'"');
480
                }, 2000000)->then(function ($output) use ($hit) {
481
                    if (! empty($hit)) {
482
                        echo $output;
483
                        $this->colorCli->primary('Updated group '.$hit[0]);
484
                    }
485
                })->catch(function (\Throwable $exception) {
486
                    echo $exception->getMessage();
487
                })->catch(static function (SerializableException $serializableException) {
0 ignored issues
show
Unused Code introduced by
The parameter $serializableException 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

487
                })->catch(static function (/** @scrutinizer ignore-unused */ SerializableException $serializableException) {

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...
488
                    //we do nothing here just catch the error and move on
489
                });
490
            }
491
492
            $pool->wait();
493
        }
494
    }
495
496
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
497
    //////////////////////////////////// All fix release names code here ///////////////////////////////////////////////
498
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
499
500
    private function fixRelNames(): void
501
    {
502
        $this->maxProcesses = (int) Settings::settingValue('..fixnamethreads');
503
        $maxPerRun = (int) Settings::settingValue('..fixnamesperrun');
504
505
        if ($this->maxProcesses > 16) {
506
            $this->maxProcesses = 16;
507
        } elseif ($this->maxProcesses === 0) {
508
            $this->maxProcesses = 1;
509
        }
510
511
        $leftGuids = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
512
513
        // Prevent PreDB FT from always running
514
        if ($this->workTypeOptions[0] === 'predbft') {
515
            $preCount = DB::select(
516
                sprintf(
517
                    "
518
					SELECT COUNT(p.id) AS num
519
					FROM predb p
520
					WHERE LENGTH(p.title) >= 15
521
					AND p.title NOT REGEXP '[\"\<\> ]'
522
					AND p.searched = 0
523
					AND p.predate < (NOW() - INTERVAL 1 DAY)"
524
                )
525
            );
526
            if ($preCount[0]->num > 0) {
527
                $leftGuids = \array_slice($leftGuids, 0, (int) ceil($preCount[0]->num / $maxPerRun));
528
            } else {
529
                $leftGuids = [];
530
            }
531
        }
532
533
        $count = 0;
534
        $queues = [];
535
        foreach ($leftGuids as $leftGuid) {
536
            $count++;
537
            if ($maxPerRun > 0) {
538
                $queues[$count] = sprintf('%s %s %s %s', $this->workTypeOptions[0], $leftGuid, $maxPerRun, $count);
539
            }
540
        }
541
542
        $this->work = $queues;
543
544
        $pool = Pool::create()->concurrency($this->maxProcesses)->timeout(config('nntmux.multiprocessing_max_child_time'));
545
546
        $maxWork = \count($queues);
547
548
        $this->processWork();
549
        foreach ($this->work as $queue) {
550
            $pool->add(function () use ($queue) {
551
                return $this->_executeCommand(PHP_BINARY.' misc/update/tmux/bin/groupfixrelnames.php "'.$queue.'"'.' true');
552
            }, 2000000)->then(function ($output) use ($maxWork) {
553
                echo $output;
554
                $this->colorCli->primary('Task #'.$maxWork.' Finished fixing releases names');
555
            })->catch(function (\Throwable $exception) {
556
                echo $exception->getMessage();
557
            })->catch(static function (SerializableException $serializableException) {
0 ignored issues
show
Unused Code introduced by
The parameter $serializableException 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

557
            })->catch(static function (/** @scrutinizer ignore-unused */ SerializableException $serializableException) {

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...
558
                //we do nothing here just catch the error and move on
559
            });
560
            $maxWork--;
561
        }
562
        $pool->wait();
563
    }
564
565
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
566
    //////////////////////////////////////// All releases code here ////////////////////////////////////////////////////
567
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
568
569
    private function releases(): void
570
    {
571
        $work = DB::select('SELECT id, name FROM usenet_groups WHERE (active = 1 OR backfill = 1)');
572
        $this->maxProcesses = (int) Settings::settingValue('..releasethreads');
573
574
        $uGroups = [];
575
        foreach ($work as $group) {
576
            try {
577
                $query = DB::select(sprintf('SELECT id FROM collections WHERE groups_id = %d LIMIT 1', $group->id));
578
                if (! empty($query)) {
579
                    $uGroups[] = ['id' => $group->id, 'name' => $group->name];
580
                }
581
            } catch (\PDOException $e) {
582
                if (config('app.debug') === true) {
583
                    Log::debug($e->getMessage());
584
                }
585
            }
586
        }
587
588
        $maxWork = \count($uGroups);
589
590
        $this->work = $uGroups;
591
592
        $pool = Pool::create()->concurrency($this->maxProcesses)->timeout(config('nntmux.multiprocessing_max_child_time'));
593
594
        $this->processWork();
595
        foreach ($uGroups as $group) {
596
            $pool->add(function () use ($group) {
597
                return $this->_executeCommand($this->dnr_path.'releases  '.$group['id'].'"');
598
            }, 2000000)->then(function ($output) use ($maxWork) {
599
                echo $output;
600
                $this->colorCli->primary('Task #'.$maxWork.' Finished performing release processing');
601
            })->catch(function (\Throwable $exception) {
602
                echo $exception->getMessage();
603
            })->catch(static function (SerializableException $serializableException) {
0 ignored issues
show
Unused Code introduced by
The parameter $serializableException 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

603
            })->catch(static function (/** @scrutinizer ignore-unused */ SerializableException $serializableException) {

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...
604
                //we do nothing here just catch the error and move on
605
            });
606
            $maxWork--;
607
        }
608
609
        $pool->wait();
610
    }
611
612
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
613
    /////////////////////////////////////// All post process code here /////////////////////////////////////////////////
614
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
615
616
    /**
617
     * Only 1 exit method is used for post process, since they are all similar.
618
     */
619
    public function postProcess(array $releases, int $maxProcess): void
620
    {
621
        $type = $desc = '';
622
        if ($this->processAdditional) {
623
            $type = 'additional true ';
624
            $desc = 'additional postprocessing';
625
        } elseif ($this->processNFO) {
626
            $type = 'nfo true ';
627
            $desc = 'nfo postprocessing';
628
        } elseif ($this->processMovies) {
629
            $type = 'movies true ';
630
            $desc = 'movies postprocessing';
631
        } elseif ($this->processTV) {
632
            $type = 'tv true ';
633
            $desc = 'tv postprocessing';
634
        }
635
        $pool = Pool::create()->concurrency($maxProcess)->timeout(config('nntmux.multiprocessing_max_child_time'));
636
        $count = \count($releases);
637
        $this->processWork();
638
        foreach ($releases as $release) {
639
            if ($type !== '') {
640
                $pool->add(function () use ($release, $type) {
641
                    return $this->_executeCommand(PHP_BINARY.' misc/update/postprocess.php '.$type.$release->id);
642
                }, 2000000)->then(function ($output) use ($desc, $count) {
643
                    echo $output;
644
                    $this->colorCli->primary('Finished task #'.$count.' for '.$desc);
645
                })->catch(function (\Throwable $exception) {
646
                    echo $exception->getMessage();
647
                })->catch(static function (SerializableException $serializableException) {
0 ignored issues
show
Unused Code introduced by
The parameter $serializableException 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

647
                })->catch(static function (/** @scrutinizer ignore-unused */ SerializableException $serializableException) {

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...
648
                    //we do nothing here just catch the error and move on
649
                })->timeout(function () use ($count) {
650
                    $this->colorCli->notice('Task #'.$count.': Timeout occurred.');
651
                });
652
                $count--;
653
            }
654
        }
655
        $pool->wait();
656
    }
657
658
    /**
659
     * @throws \Exception
660
     */
661
    private function postProcessAdd(): void
662
    {
663
        $ppAddMinSize = Settings::settingValue('..minsizetopostprocess') !== '' ? (int) Settings::settingValue('..minsizetopostprocess') : 1;
0 ignored issues
show
introduced by
The condition App\Models\Settings::set...etopostprocess') !== '' is always true.
Loading history...
664
        $ppAddMinSize = ($ppAddMinSize > 0 ? ('AND r.size > '.($ppAddMinSize * 1048576)) : '');
665
        $ppAddMaxSize = (Settings::settingValue('..maxsizetopostprocess') !== '') ? (int) Settings::settingValue('..maxsizetopostprocess') : 100;
0 ignored issues
show
introduced by
The condition App\Models\Settings::set...etopostprocess') !== '' is always true.
Loading history...
666
        $ppAddMaxSize = ($ppAddMaxSize > 0 ? ('AND r.size < '.($ppAddMaxSize * 1073741824)) : '');
667
        $this->maxProcesses = 1;
668
        $ppQueue = DB::select(
669
            sprintf(
670
                '
671
					SELECT r.leftguid AS id
672
					FROM releases r
673
					LEFT JOIN categories c ON c.id = r.categories_id
674
					WHERE r.nzbstatus = %d
675
					AND r.passwordstatus = -1
676
					AND r.haspreview = -1
677
					AND c.disablepreview = 0
678
					%s %s
679
					GROUP BY r.leftguid
680
					LIMIT 16',
681
                NZB::NZB_ADDED,
682
                $ppAddMaxSize,
683
                $ppAddMinSize
684
            )
685
        );
686
        if (\count($ppQueue) > 0) {
687
            $this->processAdditional = true;
688
            $this->work = $ppQueue;
689
            $this->maxProcesses = (int) Settings::settingValue('..postthreads');
690
        }
691
692
        $this->postProcess($this->work, $this->maxProcesses);
693
    }
694
695
    private string $nfoQueryString = '';
696
697
    /**
698
     * Check if we should process NFO's.
699
     *
700
     *
701
     * @throws \Exception
702
     */
703
    private function checkProcessNfo(): bool
704
    {
705
        if ((int) Settings::settingValue('..lookupnfo') === 1) {
706
            $this->nfoQueryString = Nfo::NfoQueryString();
707
708
            return DB::select(sprintf('SELECT r.id FROM releases r WHERE 1=1 %s LIMIT 1', $this->nfoQueryString)) > 0;
709
        }
710
711
        return false;
712
    }
713
714
    /**
715
     * @throws \Exception
716
     */
717
    private function postProcessNfo(): void
718
    {
719
        $this->maxProcesses = 1;
720
        if ($this->checkProcessNfo()) {
721
            $this->processNFO = true;
722
            $this->work = DB::select(
723
                sprintf(
724
                    '
725
					SELECT r.leftguid AS id
726
					FROM releases r
727
					WHERE 1=1 %s
728
					GROUP BY r.leftguid
729
					LIMIT 16',
730
                    $this->nfoQueryString
731
                )
732
            );
733
            $this->maxProcesses = (int) Settings::settingValue('..nfothreads');
734
        }
735
736
        $this->postProcess($this->work, $this->maxProcesses);
737
    }
738
739
    /**
740
     * @throws \Exception
741
     */
742
    private function checkProcessMovies(): bool
743
    {
744
        if (Settings::settingValue('..lookupimdb') > 0) {
745
            return DB::select(sprintf('
746
						SELECT id
747
						FROM releases
748
						WHERE categories_id BETWEEN 2000 AND 2999
749
						AND nzbstatus = %d
750
						AND imdbid IS NULL
751
						%s %s
752
						LIMIT 1', NZB::NZB_ADDED, ((int) Settings::settingValue('..lookupimdb') === 2 ? 'AND isrenamed = 1' : ''), ($this->ppRenamedOnly ? 'AND isrenamed = 1' : ''))) > 0;
753
        }
754
755
        return false;
756
    }
757
758
    /**
759
     * @throws \Exception
760
     */
761
    private function postProcessMov(): void
762
    {
763
        $this->maxProcesses = 1;
764
        if ($this->checkProcessMovies()) {
765
            $this->processMovies = true;
766
            $this->work = DB::select(
767
                sprintf(
768
                    '
769
					SELECT leftguid AS id, %d AS renamed
770
					FROM releases
771
					WHERE categories_id BETWEEN 2000 AND 2999
772
					AND nzbstatus = %d
773
					AND imdbid IS NULL
774
					%s %s
775
					GROUP BY leftguid
776
					LIMIT 16',
777
                    ($this->ppRenamedOnly ? 2 : 1),
778
                    NZB::NZB_ADDED,
779
                    ((int) Settings::settingValue('..lookupimdb') === 2 ? 'AND isrenamed = 1' : ''),
780
                    ($this->ppRenamedOnly ? 'AND isrenamed = 1' : '')
781
                )
782
            );
783
            $this->maxProcesses = (int) Settings::settingValue('..postthreadsnon');
784
        }
785
786
        $this->postProcess($this->work, $this->maxProcesses);
787
    }
788
789
    /**
790
     * Check if we should process TV's.
791
     *
792
     *
793
     * @throws \Exception
794
     */
795
    private function checkProcessTV(): bool
796
    {
797
        if ((int) Settings::settingValue('..lookuptvrage') > 0) {
798
            return DB::select(sprintf('
799
						SELECT id
800
						FROM releases
801
						WHERE categories_id BETWEEN 5000 AND 5999
802
						AND nzbstatus = %d
803
						AND size > 1048576
804
						AND tv_episodes_id BETWEEN -2 AND 0
805
						%s %s
806
						', NZB::NZB_ADDED, (int) Settings::settingValue('..lookuptvrage') === 2 ? 'AND isrenamed = 1' : '', $this->ppRenamedOnly ? 'AND isrenamed = 1' : '')) > 0;
807
        }
808
809
        return false;
810
    }
811
812
    /**
813
     * @throws \Exception
814
     */
815
    private function postProcessTv(): void
816
    {
817
        $this->maxProcesses = 1;
818
        if ($this->checkProcessTV()) {
819
            $this->processTV = true;
820
            $this->work = DB::select(
821
                sprintf(
822
                    '
823
					SELECT leftguid AS id, %d AS renamed
824
					FROM releases
825
					WHERE categories_id BETWEEN 5000 AND 5999
826
					AND nzbstatus = %d
827
					AND tv_episodes_id BETWEEN -2 AND 0
828
					AND size > 1048576
829
					%s %s
830
					GROUP BY leftguid
831
					LIMIT 16',
832
                    ($this->ppRenamedOnly ? 2 : 1),
833
                    NZB::NZB_ADDED,
834
                    (int) Settings::settingValue('..lookuptvrage') === 2 ? 'AND isrenamed = 1' : '',
835
                    ($this->ppRenamedOnly ? 'AND isrenamed = 1' : '')
836
                )
837
            );
838
            $this->maxProcesses = (int) Settings::settingValue('..postthreadsnon');
839
        }
840
841
        $this->postProcess($this->work, $this->maxProcesses);
842
    }
843
844
    /**
845
     * Process sharing.
846
     *
847
     *
848
     * @throws \Exception
849
     */
850
    private function processSharing(): bool
851
    {
852
        $sharing = DB::select('SELECT enabled FROM sharing');
853
        if ($sharing > 0 && (int) $sharing[0]->enabled === 1) {
854
            $nntp = new NNTP();
855
            if ((int) (Settings::settingValue('..alternate_nntp') === 1 ? $nntp->doConnect(true, true) : $nntp->doConnect()) === true) {
0 ignored issues
show
introduced by
The condition (int)App\Models\Settings...p->doConnect() === true is always false.
Loading history...
introduced by
The condition App\Models\Settings::set....alternate_nntp') === 1 is always false.
Loading history...
856
                (new PostProcess(['ColorCLI' => $this->colorCli]))->processSharing($nntp);
857
            }
858
859
            return true;
860
        }
861
862
        return false;
863
    }
864
865
    /**
866
     * Process all that require a single thread.
867
     *
868
     * @throws \Exception
869
     */
870
    private function processSingle(): void
871
    {
872
        $postProcess = new PostProcess(['ColorCLI' => $this->colorCli]);
0 ignored issues
show
Unused Code introduced by
The call to Blacklight\processing\PostProcess::__construct() has too many arguments starting with array('ColorCLI' => $this->colorCli). ( Ignorable by Annotation )

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

872
        $postProcess = /** @scrutinizer ignore-call */ new PostProcess(['ColorCLI' => $this->colorCli]);

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...
873
        //$postProcess->processAnime();
874
        $postProcess->processBooks();
875
        $postProcess->processConsoles();
876
        $postProcess->processGames();
877
        $postProcess->processMusic();
878
        $postProcess->processXXX();
879
    }
880
881
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
882
    ///////////////////////////////// All "update_per_Group" code goes here ////////////////////////////////////////////
883
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
884
885
    /**
886
     * @throws \Exception
887
     */
888
    private function updatePerGroup(): void
889
    {
890
        $this->work = DB::select('SELECT id , name FROM usenet_groups WHERE (active = 1 OR backfill = 1)');
891
892
        $maxProcess = (int) Settings::settingValue('..releasethreads');
893
894
        $pool = Pool::create()->concurrency($maxProcess)->timeout(config('nntmux.multiprocessing_max_child_time'));
895
        $this->processWork();
896
        foreach ($this->work as $group) {
897
            $pool->add(function () use ($group) {
898
                return $this->_executeCommand($this->dnr_path.'update_per_group  '.$group->id.'"');
899
            }, 2000000)->then(function ($output) use ($group) {
900
                echo $output;
901
                $name = UsenetGroup::getNameByID($group->id);
902
                $this->colorCli->primary('Finished updating binaries, processing releases and additional postprocessing for group:'.$name);
903
            })->catch(function (\Throwable $exception) {
904
                echo $exception->getMessage();
905
            })->catch(static function (SerializableException $serializableException) {
906
                echo $serializableException->asThrowable()->getMessage();
907
            });
908
        }
909
910
        $pool->wait();
911
    }
912
913
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
914
    //////////////////////////////////////////// Various methods ///////////////////////////////////////////////////////
915
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
916
917
    protected function _executeCommand(string $command): string
918
    {
919
        $process = Process::fromShellCommandline($command);
920
        $process->setTimeout(1800);
921
        $process->run(function ($type, $buffer) {
922
            if (Process::ERR === $type) {
923
                echo $buffer;
924
            }
925
        });
926
927
        return $process->getOutput();
928
    }
929
930
    /**
931
     * Echo a message to CLI.
932
     */
933
    public function logger(string $message): void
934
    {
935
        if (config('nntmux.echocli')) {
936
            echo $message.PHP_EOL;
937
        }
938
    }
939
940
    /**
941
     * This method is executed whenever a child is finished doing work.
942
     *
943
     * @param  string  $pid  The PID numbers.
944
     */
945
    public function exit(string $pid): void
946
    {
947
        if (config('nntmux.echocli')) {
948
            $this->colorCli->header(
949
                'Process ID #'.$pid.' has completed.'.PHP_EOL.
950
                'There are '.($this->maxProcesses - 1).' process(es) still active with '.
951
                (--$this->_workCount).' job(s) left in the queue.',
952
                true
953
            );
954
        }
955
    }
956
}
957