TmuxMonitorService::setKillswitches()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 10
rs 10
cc 3
nc 4
nop 0
1
<?php
2
3
namespace App\Services\Tmux;
4
5
use App\Models\Category;
6
use App\Models\Collection;
7
use App\Models\Release;
8
use App\Models\Settings;
9
use Illuminate\Support\Facades\DB;
10
11
/**
12
 * Service for monitoring tmux operations and collecting statistics
13
 */
14
class TmuxMonitorService
15
{
16
    protected Tmux $tmux;
17
18
    protected array $runVar = [];
19
20
    protected int $iterations = 1;
21
22
    protected bool $shouldContinue = true;
23
24
    public function __construct()
25
    {
26
        $this->tmux = new Tmux;
27
    }
28
29
    /**
30
     * Initialize monitor with default values
31
     */
32
    public function initializeMonitor(): array
33
    {
34
        $this->runVar['paths']['misc'] = base_path().'/misc/';
35
        $this->runVar['paths']['cli'] = base_path().'/cli/';
36
37
        $this->runVar['constants'] = $this->tmux->getConstantSettings();
38
        $this->runVar['settings'] = $this->tmux->getMonitorSettings();
39
        $this->runVar['connections'] = $this->tmux->getConnectionsInfo($this->runVar['constants']);
40
41
        // Initialize timers
42
        $this->runVar['timers'] = $this->initializeTimers();
43
44
        // Initialize counts
45
        $this->runVar['counts'] = [
46
            'iterations' => 1,
47
            'now' => [],
48
            'start' => [],
49
            'diff' => [],
50
            'percent' => [],
51
        ];
52
53
        // Parse fix_crap setting into an array
54
        $fixCrapSetting = $this->runVar['settings']['fix_crap'] ?? '';
55
        $fixCrapTypes = ! empty($fixCrapSetting)
56
            ? (is_array($fixCrapSetting) ? $fixCrapSetting : explode(',', $fixCrapSetting))
57
            : [];
58
        $fixCrapTypes = array_filter($fixCrapTypes);
59
60
        $this->runVar['modsettings'] = [
61
            'fix_crap' => $fixCrapTypes,
62
            'fc' => [
63
                'num' => 0,
64
                'max' => count($fixCrapTypes),
65
                'time' => 'full',
66
                'firstrun' => true,
67
            ],
68
        ];
69
70
        return $this->runVar;
71
    }
72
73
    /**
74
     * Initialize all timers
75
     */
76
    protected function initializeTimers(): array
77
    {
78
        $now = time();
79
80
        return [
81
            'timer1' => $now,
82
            'timer2' => $now,
83
            'timer3' => $now,
84
            'timer4' => $now,
85
            'timer5' => $now,
86
            'query' => [
87
                'tmux_time' => 0,
88
                'split_time' => 0,
89
                'init_time' => 0,
90
                'proc1_time' => 0,
91
                'proc2_time' => 0,
92
                'proc3_time' => 0,
93
                'split1_time' => 0,
94
                'init1_time' => 0,
95
                'proc11_time' => 0,
96
                'proc21_time' => 0,
97
                'proc31_time' => 0,
98
                'tpg_time' => 0,
99
                'tpg1_time' => 0,
100
            ],
101
            'newOld' => [
102
                'newestrelname' => '',
103
                'oldestcollection' => 0,
104
                'newestpre' => 0,
105
                'newestrelease' => 0,
106
            ],
107
        ];
108
    }
109
110
    /**
111
     * Collect current statistics
112
     */
113
    public function collectStatistics(): array
114
    {
115
        // Refresh settings periodically
116
        $monitorDelay = (int) ($this->runVar['settings']['monitor'] ?? 60);
117
        $timeSinceLastRefresh = time() - ($this->runVar['timers']['timer2'] ?? 0);
118
119
        if ($this->iterations === 1 || $timeSinceLastRefresh >= $monitorDelay) {
120
            $this->refreshStatistics();
121
            $this->runVar['timers']['timer2'] = time();
122
        }
123
124
        // Update connection counts
125
        $this->updateConnectionCounts();
126
127
        // Set killswitches
128
        $this->setKillswitches();
129
130
        return $this->runVar;
131
    }
132
133
    /**
134
     * Refresh all statistics from database
135
     */
136
    protected function refreshStatistics(): void
137
    {
138
        $timer = time();
139
140
        // Refresh settings
141
        $this->runVar['settings'] = $this->tmux->getMonitorSettings();
142
        $this->runVar['timers']['query']['tmux_time'] = time() - $timer;
143
144
        // Get category counts
145
        $this->getCategoryCounts();
146
147
        // Get process counts
148
        $this->getProcessCounts();
149
150
        // Get table counts
151
        $this->getTableCounts();
152
153
        // Calculate diffs and percentages
154
        $this->calculateStatistics();
155
    }
156
157
    /**
158
     * Get counts by category
159
     */
160
    protected function getCategoryCounts(): void
161
    {
162
        $timer = time();
163
164
        $this->runVar['counts']['now']['tv'] = Release::query()
165
            ->whereBetween('categories_id', [Category::TV_ROOT, Category::TV_OTHER])
166
            ->count('id');
167
168
        $this->runVar['counts']['now']['movies'] = Release::query()
169
            ->whereBetween('categories_id', [Category::MOVIE_ROOT, Category::MOVIE_OTHER])
170
            ->count('id');
171
172
        $this->runVar['counts']['now']['audio'] = Release::query()
173
            ->whereBetween('categories_id', [Category::MUSIC_ROOT, Category::MUSIC_OTHER])
174
            ->count('id');
175
176
        $this->runVar['counts']['now']['books'] = Release::query()
177
            ->whereBetween('categories_id', [Category::BOOKS_ROOT, Category::BOOKS_UNKNOWN])
178
            ->count('id');
179
180
        $this->runVar['counts']['now']['console'] = Release::query()
181
            ->whereBetween('categories_id', [Category::GAME_ROOT, Category::GAME_OTHER])
182
            ->count('id');
183
184
        $this->runVar['counts']['now']['pc'] = Release::query()
185
            ->whereBetween('categories_id', [Category::PC_ROOT, Category::PC_PHONE_ANDROID])
186
            ->count('id');
187
188
        $this->runVar['counts']['now']['xxx'] = Release::query()
189
            ->whereBetween('categories_id', [Category::XXX_ROOT, Category::XXX_OTHER])
190
            ->count('id');
191
192
        $this->runVar['counts']['now']['misc'] = Release::query()
193
            ->whereBetween('categories_id', [Category::OTHER_ROOT, Category::OTHER_HASHED])
194
            ->count('id');
195
196
        $this->runVar['timers']['query']['init_time'] = time() - $timer;
197
    }
198
199
    /**
200
     * Get process-related counts
201
     */
202
    protected function getProcessCounts(): void
203
    {
204
        $timer = time();
205
206
        try {
207
            $bookReqIds = $this->runVar['settings']['book_reqids'] ?? Category::BOOKS_ROOT;
208
            $dbName = config('nntmux.db_name');
209
210
            $proc1Query = $this->tmux->proc_query(1, $bookReqIds, $dbName);
211
            $proc1Result = DB::selectOne($proc1Query);
0 ignored issues
show
Bug introduced by
It seems like $proc1Query can also be of type false; however, parameter $query of Illuminate\Support\Facades\DB::selectOne() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

211
            $proc1Result = DB::selectOne(/** @scrutinizer ignore-type */ $proc1Query);
Loading history...
212
213
            if ($proc1Result) {
214
                foreach ((array) $proc1Result as $key => $value) {
215
                    $this->runVar['counts']['now'][$key] = $value;
216
                }
217
            }
218
219
            $this->runVar['timers']['query']['proc1_time'] = time() - $timer;
220
221
            // Process 2
222
            $timer2 = time();
223
            $maxSize = $this->runVar['settings']['maxsize_pp'] ?? '';
224
            $minSize = $this->runVar['settings']['minsize_pp'] ?? '';
225
226
            $proc2Query = $this->tmux->proc_query(2, $bookReqIds, $dbName, $maxSize, $minSize);
227
            $proc2Result = DB::selectOne($proc2Query);
228
229
            if ($proc2Result) {
230
                foreach ((array) $proc2Result as $key => $value) {
231
                    $this->runVar['counts']['now'][$key] = $value;
232
                }
233
            }
234
235
            $this->runVar['timers']['query']['proc2_time'] = time() - $timer2;
236
237
        } catch (\Exception $e) {
238
            logger()->error('Error collecting process counts: '.$e->getMessage());
239
        }
240
    }
241
242
    /**
243
     * Get table row counts
244
     */
245
    protected function getTableCounts(): void
246
    {
247
        $timer = time();
248
249
        try {
250
            $this->runVar['counts']['now']['collections_table'] = Collection::query()->count();
251
252
            // Get binaries/parts counts from information_schema or use approximation
253
            $dbName = config('nntmux.db_name');
254
            $tables = $this->tmux->cbpmTableQuery();
255
256
            $this->runVar['counts']['now']['binaries_table'] = 0;
257
            $this->runVar['counts']['now']['parts_table'] = 0;
258
            $this->runVar['counts']['now']['missed_parts_table'] = 0;
259
260
            foreach ($tables as $table) {
261
                $tableName = $table->name;
262
                $count = $this->getTableRowCount($tableName);
263
264
                if (str_contains($tableName, 'binaries')) {
265
                    $this->runVar['counts']['now']['binaries_table'] += $count;
266
                } elseif (str_contains($tableName, 'missed_parts')) {
267
                    $this->runVar['counts']['now']['missed_parts_table'] += $count;
268
                } elseif (str_contains($tableName, 'parts')) {
269
                    $this->runVar['counts']['now']['parts_table'] += $count;
270
                }
271
            }
272
273
            $this->runVar['timers']['query']['tpg_time'] = time() - $timer;
274
275
            // Get additional table counts (query 4)
276
            $timer4 = time();
0 ignored issues
show
Unused Code introduced by
The assignment to $timer4 is dead and can be removed.
Loading history...
277
            $bookReqIds = $this->runVar['settings']['book_reqids'] ?? Category::BOOKS_ROOT;
278
            $proc4Query = $this->tmux->proc_query(4, $bookReqIds, $dbName);
279
            $proc4Result = DB::selectOne($proc4Query);
0 ignored issues
show
Bug introduced by
It seems like $proc4Query can also be of type false; however, parameter $query of Illuminate\Support\Facades\DB::selectOne() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

279
            $proc4Result = DB::selectOne(/** @scrutinizer ignore-type */ $proc4Query);
Loading history...
280
281
            if ($proc4Result) {
282
                foreach ((array) $proc4Result as $key => $value) {
283
                    $this->runVar['counts']['now'][$key] = $value;
284
                }
285
            }
286
287
            // Get newest/oldest data (query 6)
288
            $timer6 = time();
0 ignored issues
show
Unused Code introduced by
The assignment to $timer6 is dead and can be removed.
Loading history...
289
            $proc6Query = $this->tmux->proc_query(6, $bookReqIds, $dbName);
290
            $proc6Result = DB::selectOne($proc6Query);
291
292
            if ($proc6Result) {
293
                foreach ((array) $proc6Result as $key => $value) {
294
                    $this->runVar['timers']['newOld'][$key] = $value;
295
                }
296
            }
297
298
        } catch (\Exception $e) {
299
            logger()->error('Error collecting table counts: '.$e->getMessage());
300
        }
301
    }
302
303
    /**
304
     * Get row count for a table
305
     */
306
    protected function getTableRowCount(string $tableName): int
307
    {
308
        try {
309
            $result = DB::selectOne(
310
                'SELECT TABLE_ROWS AS count FROM information_schema.TABLES
311
                 WHERE TABLE_NAME = ? AND TABLE_SCHEMA = DATABASE()',
312
                [$tableName]
313
            );
314
315
            return (int) ($result->count ?? 0);
316
        } catch (\Exception $e) {
317
            return 0;
318
        }
319
    }
320
321
    /**
322
     * Calculate statistics (diffs, percentages, totals)
323
     */
324
    protected function calculateStatistics(): void
325
    {
326
        // Calculate total work
327
        $this->runVar['counts']['now']['total_work'] = 0;
328
329
        foreach ($this->runVar['counts']['now'] as $key => $value) {
330
            if (str_starts_with($key, 'process')) {
331
                $this->runVar['counts']['now']['total_work'] += $value;
332
            }
333
        }
334
335
        // Set initial start values on first iteration
336
        if ($this->iterations === 1) {
337
            $this->runVar['counts']['start'] = $this->runVar['counts']['now'];
338
        }
339
340
        // Calculate diffs
341
        foreach ($this->runVar['counts']['now'] as $key => $value) {
342
            $startValue = $this->runVar['counts']['start'][$key] ?? 0;
343
            $this->runVar['counts']['diff'][$key] = number_format($value - $startValue);
344
        }
345
346
        // Calculate percentages for category counts (as % of total categorized releases)
347
        $categoryKeys = ['tv', 'movies', 'audio', 'books', 'console', 'pc', 'xxx', 'misc'];
348
349
        // Sum all category counts to get the true total
350
        $totalCategorized = 0;
351
        foreach ($categoryKeys as $key) {
352
            $totalCategorized += $this->runVar['counts']['now'][$key] ?? 0;
353
        }
354
355
        foreach ($categoryKeys as $key) {
356
            $value = $this->runVar['counts']['now'][$key] ?? 0;
357
            $this->runVar['counts']['percent'][$key] = $totalCategorized > 0
358
                ? sprintf('%02d', floor(($value / $totalCategorized) * 100))
359
                : 0;
360
        }
361
362
        // Calculate percentages for PP Lists (matched / total for each type)
363
        // NFO: nfo / (nfo + processnfo) * 100
364
        $nfoMatched = $this->runVar['counts']['now']['nfo'] ?? 0;
365
        $nfoUnmatched = $this->runVar['counts']['now']['processnfo'] ?? 0;
366
        $nfoTotal = $nfoMatched + $nfoUnmatched;
367
        $this->runVar['counts']['percent']['nfo'] = $nfoTotal > 0
368
            ? sprintf('%02d', floor(($nfoMatched / $nfoTotal) * 100))
369
            : 0;
370
371
        // PreDB: predb_matched / predb * 100
372
        $predbMatched = $this->runVar['counts']['now']['predb_matched'] ?? 0;
373
        $predbTotal = $this->runVar['counts']['now']['predb'] ?? 1;
374
        $this->runVar['counts']['percent']['predb_matched'] = $predbTotal > 0
375
            ? sprintf('%02d', floor(($predbMatched / $predbTotal) * 100))
376
            : 0;
377
378
        // Renames: renamed / (renamed + processrenames) * 100
379
        $renamed = $this->runVar['counts']['now']['renamed'] ?? 0;
380
        $processrenames = $this->runVar['counts']['now']['processrenames'] ?? 0;
381
        $renameTotal = $renamed + $processrenames;
382
        $this->runVar['counts']['percent']['renamed'] = $renameTotal > 0
383
            ? sprintf('%02d', floor(($renamed / $renameTotal) * 100))
384
            : 0;
385
    }
386
387
    /**
388
     * Update connection counts
389
     */
390
    protected function updateConnectionCounts(): void
391
    {
392
        $this->runVar['conncounts'] = $this->tmux->getUSPConnections(
393
            'primary',
394
            $this->runVar['connections']
395
        );
396
397
        if ((int) ($this->runVar['constants']['alternate_nntp'] ?? 0) === 1) {
398
            $alternateConns = $this->tmux->getUSPConnections(
399
                'alternate',
400
                $this->runVar['connections']
401
            );
402
            $this->runVar['conncounts'] = array_merge($this->runVar['conncounts'], $alternateConns);
403
        }
404
    }
405
406
    /**
407
     * Set killswitches based on limits
408
     */
409
    protected function setKillswitches(): void
410
    {
411
        $ppKillLimit = (int) ($this->runVar['settings']['postprocess_kill'] ?? 0);
412
        $collKillLimit = (int) ($this->runVar['settings']['collections_kill'] ?? 0);
413
414
        $totalWork = (int) ($this->runVar['counts']['now']['total_work'] ?? 0);
415
        $collections = (int) ($this->runVar['counts']['now']['collections_table'] ?? 0);
416
417
        $this->runVar['killswitch']['pp'] = ($ppKillLimit > 0 && $ppKillLimit < $totalWork);
418
        $this->runVar['killswitch']['coll'] = ($collKillLimit > 0 && $collKillLimit < $collections);
419
    }
420
421
    /**
422
     * Update the monitor display
423
     */
424
    public function updateDisplay(): void
425
    {
426
        // This would output to terminal - implementation depends on TmuxOutput
427
        // For now, we'll just log basic info
428
        if ($this->iterations % 10 === 0) {
429
            logger()->info('Tmux Monitor', [
430
                'iteration' => $this->iterations,
431
                'total_work' => $this->runVar['counts']['now']['total_work'] ?? 0,
432
                'collections' => $this->runVar['counts']['now']['collections_table'] ?? 0,
433
            ]);
434
        }
435
    }
436
437
    /**
438
     * Increment iteration counter
439
     */
440
    public function incrementIteration(): void
441
    {
442
        $this->iterations++;
443
        $this->runVar['counts']['iterations'] = $this->iterations;
444
    }
445
446
    /**
447
     * Check if monitoring should continue
448
     */
449
    public function shouldContinue(): bool
450
    {
451
        $exitFlag = (int) Settings::settingValue('exit');
452
453
        if ($exitFlag === 0) {
454
            return true;
455
        }
456
457
        $this->shouldContinue = false;
458
459
        return false;
460
    }
461
462
    /**
463
     * Get current run variables
464
     */
465
    public function getRunVar(): array
466
    {
467
        return $this->runVar;
468
    }
469
}
470