|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace App\Console\Commands; |
|
4
|
|
|
|
|
5
|
|
|
use App\Models\Release; |
|
6
|
|
|
use Illuminate\Console\Command; |
|
7
|
|
|
use Illuminate\Support\Facades\Artisan; |
|
8
|
|
|
|
|
9
|
|
|
class FetchSamples extends Command |
|
10
|
|
|
{ |
|
11
|
|
|
protected $signature = 'releases:fetch-samples |
|
12
|
|
|
{--category= : Category id or comma-separated list of category ids (required)} |
|
13
|
|
|
{--limit=0 : Max number of releases to process (0 = all)} |
|
14
|
|
|
{--chunk=500 : Chunk size when iterating releases} |
|
15
|
|
|
{--dry-run : Show how many and which GUIDs would be processed without running} |
|
16
|
|
|
{--show-output : Display output from each releases:additional invocation}'; |
|
17
|
|
|
|
|
18
|
|
|
protected $description = 'Fetch/generate samples by running additional postprocessing (with reset) for releases in the supplied category / categories having jpgstatus = 0.'; |
|
19
|
|
|
|
|
20
|
|
|
public function handle(): int |
|
21
|
|
|
{ |
|
22
|
|
|
$limit = (int) $this->option('limit'); |
|
23
|
|
|
$chunkSize = (int) $this->option('chunk'); |
|
24
|
|
|
$dryRun = (bool) $this->option('dry-run'); |
|
25
|
|
|
$showOutput = (bool) $this->option('show-output'); |
|
26
|
|
|
$categoryOpt = $this->option('category'); |
|
27
|
|
|
|
|
28
|
|
|
// Validate the required category option. |
|
29
|
|
|
if ($categoryOpt === null || trim((string) $categoryOpt) === '') { |
|
30
|
|
|
$this->info('Category option is empty. Provide --category with one or more numeric category IDs. Command will not run.'); |
|
31
|
|
|
|
|
32
|
|
|
return self::SUCCESS; |
|
33
|
|
|
} |
|
34
|
|
|
|
|
35
|
|
|
// Parse categories: allow comma or whitespace separated values. |
|
36
|
|
|
$catIds = collect(preg_split('/[\s,]+/', trim((string) $categoryOpt), -1, PREG_SPLIT_NO_EMPTY)) |
|
|
|
|
|
|
37
|
|
|
->map(fn ($v) => trim($v)) |
|
38
|
|
|
->filter(fn ($v) => ctype_digit($v)) |
|
39
|
|
|
->map(fn ($v) => (int) $v) |
|
40
|
|
|
->unique() |
|
41
|
|
|
->values(); |
|
42
|
|
|
|
|
43
|
|
|
if ($catIds->isEmpty()) { |
|
44
|
|
|
$this->info('Category option provided but no valid numeric IDs parsed. Command will not run.'); |
|
45
|
|
|
|
|
46
|
|
|
return self::SUCCESS; |
|
47
|
|
|
} |
|
48
|
|
|
|
|
49
|
|
|
if ($limit < 0) { |
|
50
|
|
|
$this->error('Limit must be >= 0'); |
|
51
|
|
|
|
|
52
|
|
|
return self::FAILURE; |
|
53
|
|
|
} |
|
54
|
|
|
if ($chunkSize < 1) { |
|
55
|
|
|
$this->error('Chunk size must be >= 1'); |
|
56
|
|
|
|
|
57
|
|
|
return self::FAILURE; |
|
58
|
|
|
} |
|
59
|
|
|
|
|
60
|
|
|
// Build base query now |
|
61
|
|
|
$baseQuery = Release::query() |
|
62
|
|
|
->whereIn('categories_id', $catIds->all()) |
|
63
|
|
|
->where('jpgstatus', 0) |
|
64
|
|
|
->orderBy('id', 'desc'); |
|
|
|
|
|
|
65
|
|
|
|
|
66
|
|
|
$totalAll = $baseQuery->count(); |
|
67
|
|
|
if ($totalAll === 0) { |
|
68
|
|
|
$this->info('No matching releases found (categories_id IN ['.implode(',', $catIds->all()).'] AND jpgstatus = 0).'); |
|
69
|
|
|
|
|
70
|
|
|
return self::SUCCESS; |
|
71
|
|
|
} |
|
72
|
|
|
|
|
73
|
|
|
$effectiveTotal = $limit > 0 ? min($limit, $totalAll) : $totalAll; |
|
74
|
|
|
$this->info('Categories: ['.implode(',', $catIds->all()).']'); |
|
75
|
|
|
$this->info("Found {$totalAll} matching release(s). Processing {$effectiveTotal}.".($dryRun ? ' (dry-run)' : '')); |
|
76
|
|
|
|
|
77
|
|
|
if ($dryRun) { |
|
78
|
|
|
$previewQuery = clone $baseQuery; |
|
79
|
|
|
if ($limit > 0) { |
|
80
|
|
|
$previewQuery->limit($limit); |
|
81
|
|
|
} |
|
82
|
|
|
$previewGuids = $previewQuery->pluck('guid'); |
|
83
|
|
|
$this->line('Dry run: GUIDs to process (with --reset):'); |
|
84
|
|
|
foreach ($previewGuids as $g) { |
|
85
|
|
|
$this->line($g); |
|
86
|
|
|
} |
|
87
|
|
|
$this->info('Dry run complete.'); |
|
88
|
|
|
|
|
89
|
|
|
return self::SUCCESS; |
|
90
|
|
|
} |
|
91
|
|
|
|
|
92
|
|
|
$processed = 0; |
|
93
|
|
|
$failed = 0; |
|
94
|
|
|
$remaining = $effectiveTotal; |
|
95
|
|
|
|
|
96
|
|
|
$bar = $this->output->createProgressBar($effectiveTotal); |
|
|
|
|
|
|
97
|
|
|
$bar->start(); |
|
98
|
|
|
|
|
99
|
|
|
$query = clone $baseQuery; |
|
100
|
|
|
|
|
101
|
|
|
$query->chunkById($chunkSize, function ($releases) use (&$processed, &$failed, &$remaining, $bar, $showOutput) { |
|
102
|
|
|
foreach ($releases as $release) { |
|
103
|
|
|
if ($remaining <= 0) { |
|
104
|
|
|
return false; // stop chunking |
|
105
|
|
|
} |
|
106
|
|
|
|
|
107
|
|
|
$guid = $release->guid; |
|
108
|
|
|
try { |
|
109
|
|
|
// Call the existing single-release additional processing command with this GUID. |
|
110
|
|
|
$exitCode = Artisan::call('releases:additional', [ |
|
111
|
|
|
'guid' => $guid, // pass GUID explicitly |
|
112
|
|
|
'--reset' => true, |
|
113
|
|
|
]); |
|
114
|
|
|
$subOutput = trim(Artisan::output()); |
|
115
|
|
|
|
|
116
|
|
|
if ($exitCode === 0) { |
|
117
|
|
|
$processed++; |
|
118
|
|
|
if ($showOutput && $subOutput !== '') { |
|
119
|
|
|
$this->getOutput()->writeln("\n<info>{$guid}</info> -> {$subOutput}"); |
|
120
|
|
|
} |
|
121
|
|
|
} else { |
|
122
|
|
|
$failed++; |
|
123
|
|
|
$this->getOutput()->writeln("\n<error>Non-zero exit code ({$exitCode}) for GUID {$guid}</error>".($showOutput && $subOutput !== '' ? "\n Output: {$subOutput}" : '')); |
|
124
|
|
|
} |
|
125
|
|
|
} catch (\Throwable $e) { |
|
126
|
|
|
$failed++; |
|
127
|
|
|
$this->getOutput()->writeln("\n<error>Error processing GUID {$guid}: {$e->getMessage()}</error>"); |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
$remaining--; |
|
131
|
|
|
$bar->advance(); |
|
132
|
|
|
|
|
133
|
|
|
if ($remaining <= 0) { |
|
134
|
|
|
break; // exit foreach to stop further work |
|
135
|
|
|
} |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
if ($remaining <= 0) { |
|
139
|
|
|
return false; // signal chunkById to stop |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
return true; // continue chunking |
|
143
|
|
|
}, 'id'); |
|
144
|
|
|
|
|
145
|
|
|
$bar->finish(); |
|
146
|
|
|
$this->newLine(); |
|
147
|
|
|
$this->info("Processing complete. Success: {$processed}, Failed: {$failed}."); |
|
148
|
|
|
|
|
149
|
|
|
return $failed === 0 ? self::SUCCESS : self::FAILURE; |
|
150
|
|
|
} |
|
151
|
|
|
} |
|
152
|
|
|
|