Passed
Push — master ( 377492...fd7aee )
by Darko
10:20
created

TvProcessor   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 304
Duplicated Lines 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 32
eloc 152
c 4
b 1
f 0
dl 0
loc 304
rs 9.84

12 Methods

Rating   Name   Duplication   Size   Complexity  
A process() 0 11 4
A __construct() 0 4 1
A processParallel() 0 32 4
A displayHeaderParallel() 0 19 3
A displayProviderHeader() 0 14 2
A displaySummary() 0 11 2
A displayHeader() 0 19 3
A displayProviderComplete() 0 13 2
A displaySummaryParallel() 0 12 2
A getProviderPipeline() 0 8 1
A processPipeline() 0 32 4
A hasWorkForProvider() 0 33 4
1
<?php
2
3
namespace App\Services;
4
5
use App\Models\Settings;
6
use Blacklight\ColorCLI;
7
use Blacklight\processing\tv\LocalDB;
8
use Blacklight\processing\tv\TMDB;
9
use Blacklight\processing\tv\TraktTv;
10
use Blacklight\processing\tv\TVDB;
11
use Blacklight\processing\tv\TVMaze;
12
13
class TvProcessor
14
{
15
    // Processing modes
16
    public const MODE_PIPELINE = 'pipeline';  // Sequential processing (efficient, reduces API calls)
17
18
    public const MODE_PARALLEL = 'parallel';  // Parallel processing (faster, more API calls)
19
20
    private bool $echooutput;
21
22
    private ColorCLI $colorCli;
23
24
    private array $stats = [
0 ignored issues
show
introduced by
The private property $stats is not used, and could be removed.
Loading history...
25
        'total' => 0,
26
        'processed' => 0,
27
        'matched' => 0,
28
        'failed' => 0,
29
        'skipped' => 0,
30
        'byProvider' => [
31
            'Local DB' => ['processed' => 0, 'matched' => 0, 'failed' => 0],
32
            'TVDB' => ['processed' => 0, 'matched' => 0, 'failed' => 0],
33
            'TVMaze' => ['processed' => 0, 'matched' => 0, 'failed' => 0],
34
            'TMDB' => ['processed' => 0, 'matched' => 0, 'failed' => 0],
35
            'Trakt' => ['processed' => 0, 'matched' => 0, 'failed' => 0],
36
        ],
37
    ];
38
39
    public function __construct(bool $echooutput)
40
    {
41
        $this->echooutput = $echooutput;
42
        $this->colorCli = new ColorCLI;
43
    }
44
45
    /**
46
     * Process all TV related releases across supported providers.
47
     *
48
     * @param  string  $groupID  Group ID to process
49
     * @param  string  $guidChar  GUID character to process
50
     * @param  int|string|null  $processTV  0/1/2 or '' to read from settings
51
     * @param  string  $mode  Processing mode: 'pipeline' (sequential) or 'parallel' (simultaneous)
52
     */
53
    public function process(string $groupID = '', string $guidChar = '', int|string|null $processTV = '', string $mode = self::MODE_PIPELINE): void
54
    {
55
        $processTV = (is_numeric($processTV) ? $processTV : Settings::settingValue('lookuptv'));
56
        if ($processTV <= 0) {
57
            return;
58
        }
59
60
        if ($mode === self::MODE_PARALLEL) {
61
            $this->processParallel($groupID, $guidChar, $processTV);
62
        } else {
63
            $this->processPipeline($groupID, $guidChar, $processTV);
64
        }
65
    }
66
67
    /**
68
     * Process releases through providers in parallel (all providers process all releases).
69
     * This is faster but uses more API calls. Compatible with Forking class.
70
     */
71
    private function processParallel(string $groupID, string $guidChar, int|string $processTV): void
72
    {
73
        // $this->displayHeaderParallel($guidChar);
74
75
        $providers = $this->getProviderPipeline();
76
        $totalTime = 0;
77
78
        foreach ($providers as $index => $provider) {
79
            // Check if there's any work remaining for this provider
80
            if (! $this->hasWorkForProvider($provider['name'], $groupID, $guidChar, $processTV)) {
81
                if ($this->echooutput) {
82
                    $this->colorCli->primaryOver('  [');
83
                    $this->colorCli->warningOver($index + 1);
84
                    $this->colorCli->primaryOver('/');
85
                    $this->colorCli->warningOver(count($providers));
86
                    $this->colorCli->primaryOver('] ');
87
                    $this->colorCli->alternateOver('→ ');
88
                    $this->colorCli->alternate($provider['name'].' → No work remaining, skipping');
89
                    echo "\n";
90
                }
91
92
                continue;
93
            }
94
95
            $this->displayProviderHeader($provider['name'], $index + 1, count($providers));
96
97
            $startTime = microtime(true);
98
            $provider['instance']->processSite($groupID, $guidChar, $processTV);
99
            $elapsedTime = microtime(true) - $startTime;
100
            $totalTime += $elapsedTime;
101
102
            $this->displayProviderComplete($provider['name'], $elapsedTime);
103
        }
104
105
        // $this->displaySummaryParallel($totalTime);
106
    }
107
108
    /**
109
     * Process releases through providers in pipeline (sequential, each processes failures from previous).
110
     * This is more efficient and reduces API calls significantly.
111
     */
112
    private function processPipeline(string $groupID, string $guidChar, int|string $processTV): void
113
    {
114
        // $this->displayHeader($guidChar);
115
116
        // Pipeline: Process releases through each provider in sequence
117
        // Each provider only processes releases that failed in previous steps
118
        $providers = $this->getProviderPipeline();
119
120
        foreach ($providers as $index => $provider) {
121
            // Check if there's any work remaining for this stage of the pipeline
122
            if (! $this->hasWorkForProvider($provider['name'], $groupID, $guidChar, $processTV)) {
123
                if ($this->echooutput) {
124
                    $this->colorCli->primaryOver('  [');
125
                    $this->colorCli->warningOver($index + 1);
126
                    $this->colorCli->primaryOver('/');
127
                    $this->colorCli->warningOver(count($providers));
128
                    $this->colorCli->primaryOver('] ');
129
                    $this->colorCli->alternateOver('→ ');
130
                    $this->colorCli->alternate($provider['name'].' → No work remaining, skipping');
131
                    echo "\n";
132
                }
133
134
                continue;
135
            }
136
137
            $this->displayProviderHeader($provider['name'], $index + 1, count($providers));
138
139
            $startTime = microtime(true);
140
            $provider['instance']->processSite($groupID, $guidChar, $processTV);
141
            $elapsedTime = microtime(true) - $startTime;
142
143
            $this->displayProviderComplete($provider['name'], $elapsedTime);
144
        }
145
146
        // $this->displaySummary();
147
    }
148
149
    /**
150
     * Get the provider pipeline in order of preference.
151
     */
152
    private function getProviderPipeline(): array
153
    {
154
        return [
155
            ['name' => 'Local DB', 'instance' => new LocalDB, 'status' => 0],
156
            ['name' => 'TVDB', 'instance' => new TVDB, 'status' => 0],
157
            ['name' => 'TVMaze', 'instance' => new TVMaze, 'status' => -1],
158
            ['name' => 'TMDB', 'instance' => new TMDB, 'status' => -2],
159
            ['name' => 'Trakt', 'instance' => new TraktTv, 'status' => -3],
160
        ];
161
    }
162
163
    /**
164
     * Check if there's work remaining for a specific provider in the pipeline.
165
     */
166
    private function hasWorkForProvider(string $providerName, string $groupID, string $guidChar, int|string $processTV): bool
167
    {
168
        $statusMap = [
169
            'Local DB' => 0,   // Process unprocessed releases
170
            'TVDB' => 0,       // Process unprocessed releases (runs in parallel with LocalDB conceptually)
171
            'TVMaze' => -1,    // Process releases not found by TVDB
172
            'TMDB' => -2,      // Process releases not found by TVMaze
173
            'Trakt' => -3,     // Process releases not found by TMDB
174
        ];
175
176
        $status = $statusMap[$providerName] ?? 0;
177
178
        // Build the same query logic as getTvReleases but just check for existence
179
        $query = \App\Models\Release::query()
180
            ->where(['videos_id' => 0, 'tv_episodes_id' => $status])
181
            ->where('size', '>', 1048576)
182
            ->whereBetween('categories_id', [5000, 5999])
183
            ->where('categories_id', '<>', 5070)
184
            ->limit(1);
185
186
        if ($groupID !== '') {
187
            $query->where('groups_id', $groupID);
188
        }
189
190
        if ($guidChar !== '') {
191
            $query->where('leftguid', $guidChar);
192
        }
193
194
        if ($processTV == 2) {
195
            $query->where('isrenamed', '=', 1);
196
        }
197
198
        return $query->exists();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->exists() could return the type Illuminate\Database\Eloq...\Database\Query\Builder which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
199
    }
200
201
    /**
202
     * Display the processing header.
203
     */
204
    private function displayHeader(string $guidChar = ''): void
0 ignored issues
show
Unused Code introduced by
The method displayHeader() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
205
    {
206
        if (! $this->echooutput) {
207
            return;
208
        }
209
210
        echo "\n";
211
        $this->colorCli->headerOver('▶ TV Processing');
212
        if ($guidChar !== '') {
213
            $this->colorCli->primaryOver(' → ');
214
            $this->colorCli->headerOver('PIPELINE Mode');
215
            $this->colorCli->primaryOver(' → ');
216
            $this->colorCli->warningOver('Bucket: ');
217
            $this->colorCli->header(strtoupper($guidChar));
218
        } else {
219
            $this->colorCli->primaryOver(' → ');
220
            $this->colorCli->header('PIPELINE Mode');
221
        }
222
        echo "\n";
223
    }
224
225
    /**
226
     * Display the processing header for parallel mode.
227
     */
228
    private function displayHeaderParallel(string $guidChar = ''): void
0 ignored issues
show
Unused Code introduced by
The method displayHeaderParallel() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
229
    {
230
        if (! $this->echooutput) {
231
            return;
232
        }
233
234
        echo "\n";
235
        $this->colorCli->headerOver('▶ TV Processing');
236
        if ($guidChar !== '') {
237
            $this->colorCli->primaryOver(' → ');
238
            $this->colorCli->headerOver('PARALLEL Mode');
239
            $this->colorCli->primaryOver(' → ');
240
            $this->colorCli->warningOver('Bucket: ');
241
            $this->colorCli->header(strtoupper($guidChar));
242
        } else {
243
            $this->colorCli->primaryOver(' → ');
244
            $this->colorCli->header('PARALLEL Mode');
245
        }
246
        echo "\n";
247
    }
248
249
    /**
250
     * Display provider processing header.
251
     */
252
    private function displayProviderHeader(string $providerName, int $step, int $total): void
253
    {
254
        if (! $this->echooutput) {
255
            return;
256
        }
257
258
        echo "\n";
259
        $this->colorCli->primaryOver('  [');
260
        $this->colorCli->warningOver($step);
261
        $this->colorCli->primaryOver('/');
262
        $this->colorCli->warningOver($total);
263
        $this->colorCli->primaryOver('] ');
264
        $this->colorCli->headerOver('→ ');
265
        $this->colorCli->header($providerName);
266
    }
267
268
    /**
269
     * Display provider completion message.
270
     */
271
    private function displayProviderComplete(string $providerName, float $elapsedTime): void
272
    {
273
        if (! $this->echooutput) {
274
            return;
275
        }
276
277
        echo "\n";
278
        $this->colorCli->primaryOver('  ✓ ');
279
        $this->colorCli->primaryOver($providerName);
280
        $this->colorCli->primaryOver(' → ');
281
        $this->colorCli->alternateOver('Completed in ');
282
        $this->colorCli->warning(sprintf('%.2fs', $elapsedTime));
283
        echo "\n";
284
    }
285
286
    /**
287
     * Display final processing summary.
288
     */
289
    private function displaySummary(): void
0 ignored issues
show
Unused Code introduced by
The method displaySummary() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
290
    {
291
        if (! $this->echooutput) {
292
            return;
293
        }
294
295
        echo "\n";
296
        $this->colorCli->primaryOver('✓ Pipeline Complete');
297
        $this->colorCli->primaryOver(' → ');
298
        $this->colorCli->primary('Local DB → TVDB → TVMaze → TMDB → Trakt');
299
        echo "\n";
300
    }
301
302
    /**
303
     * Display final processing summary for parallel mode.
304
     */
305
    private function displaySummaryParallel(float $totalTime): void
0 ignored issues
show
Unused Code introduced by
The method displaySummaryParallel() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
306
    {
307
        if (! $this->echooutput) {
308
            return;
309
        }
310
311
        echo "\n";
312
        $this->colorCli->primaryOver('✓ Parallel Processing Complete');
313
        $this->colorCli->primaryOver(' → ');
314
        $this->colorCli->warningOver('Total: ');
315
        $this->colorCli->warning(sprintf('%.2fs', $totalTime));
316
        echo "\n";
317
    }
318
}
319