2025-04-10 00:44:55 +03:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
|
|
|
|
|
|
use App\Services\IgdbAuthService;
|
|
|
|
|
use Illuminate\Console\Command;
|
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
|
use Illuminate\Support\Facades\Log;
|
2025-04-12 17:18:33 +03:00
|
|
|
use JetBrains\PhpStorm\NoReturn;
|
2025-04-10 00:44:55 +03:00
|
|
|
use ZipArchive;
|
|
|
|
|
|
|
|
|
|
class FetchIgdbDumps extends Command
|
|
|
|
|
{
|
|
|
|
|
protected $signature = 'igdb:fetch-dumps
|
|
|
|
|
{--type=games : Type of dump to fetch (games, genres, platforms)}
|
|
|
|
|
{--limit=1000 : Number of items to fetch per request}
|
|
|
|
|
{--offset=0 : Starting offset for pagination}
|
|
|
|
|
{--output=storage/dumps : Output directory for the dumps}';
|
|
|
|
|
|
|
|
|
|
protected $description = 'Fetch data dumps from IGDB API';
|
|
|
|
|
|
|
|
|
|
private const ENDPOINTS = [
|
|
|
|
|
'games' => 'https://api.igdb.com/v4/games',
|
|
|
|
|
'genres' => 'https://api.igdb.com/v4/genres',
|
|
|
|
|
'platforms' => 'https://api.igdb.com/v4/platforms',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
private const FIELDS = [
|
|
|
|
|
'games' => 'id,name,summary,cover.url,slug,genres,platforms,first_release_date,rating,rating_count,category,status,url,version_title,artworks.url,artworks.width,artworks.height,artworks.image_id,screenshots.url,screenshots.width,screenshots.height,screenshots.image_id,videos.name,videos.video_id,videos.url,videos.checksum,bundles.id,collections.id,dlcs.id,expanded_games.id,franchises.id,ports.id,remakes.id,similar_games.id',
|
|
|
|
|
'genres' => 'id,name,slug',
|
|
|
|
|
'platforms' => 'id,name,slug,abbreviation',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public function handle(IgdbAuthService $authService): int
|
|
|
|
|
{
|
|
|
|
|
$type = $this->option('type');
|
|
|
|
|
$limit = (int) $this->option('limit');
|
|
|
|
|
$offset = (int) $this->option('offset');
|
|
|
|
|
$outputDir = $this->option('output');
|
|
|
|
|
|
|
|
|
|
if (!isset(self::ENDPOINTS[$type])) {
|
|
|
|
|
$this->error("Invalid type: {$type}");
|
|
|
|
|
return Command::FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_dir($outputDir)) {
|
|
|
|
|
if (!mkdir($outputDir, 0755, true)) {
|
|
|
|
|
$this->error("Failed to create output directory: {$outputDir}");
|
|
|
|
|
return Command::FAILURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->info("Fetching {$type} dumps from IGDB API");
|
|
|
|
|
$this->info("Output directory: {$outputDir}");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$this->fetchDumps($type, $limit, $offset, $outputDir, $authService);
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
$this->error("Error fetching dumps: {$e->getMessage()}");
|
|
|
|
|
Log::error('Error fetching IGDB dumps', [
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
'trace' => $e->getTraceAsString()
|
|
|
|
|
]);
|
|
|
|
|
return Command::FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->info('Dump fetching completed successfully');
|
|
|
|
|
return Command::SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-12 17:18:33 +03:00
|
|
|
#[NoReturn]
|
2025-04-10 00:44:55 +03:00
|
|
|
private function fetchDumps(string $type, int $limit, int $offset, string $outputDir, IgdbAuthService $authService): void
|
|
|
|
|
{
|
|
|
|
|
$endpoint = self::ENDPOINTS[$type];
|
|
|
|
|
$fields = self::FIELDS[$type];
|
|
|
|
|
$totalFetched = 0;
|
|
|
|
|
$fileCount = 1;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
$this->info("Fetching batch {$fileCount} (offset: {$offset})");
|
|
|
|
|
|
|
|
|
|
$accessToken = $authService->getAccessToken();
|
|
|
|
|
|
|
|
|
|
$response = Http::withHeaders([
|
2025-04-12 17:18:33 +03:00
|
|
|
'Client-ID' => config('igdb.credentials.client_id'),
|
2025-04-10 00:44:55 +03:00
|
|
|
'Authorization' => 'Bearer ' . $accessToken,
|
|
|
|
|
])->post($endpoint, [
|
|
|
|
|
'fields' => $fields,
|
|
|
|
|
'limit' => $limit,
|
|
|
|
|
'offset' => $offset,
|
2025-04-12 17:18:33 +03:00
|
|
|
// 'where' => 'rating_count > 0',
|
2025-04-10 00:44:55 +03:00
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if (!$response->successful()) {
|
|
|
|
|
throw new \Exception("API request failed: " . $response->body());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $response->json();
|
|
|
|
|
$count = count($data);
|
|
|
|
|
|
|
|
|
|
if ($count === 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$filename = "{$type}_{$fileCount}.json";
|
|
|
|
|
$filepath = "{$outputDir}/{$filename}";
|
|
|
|
|
|
|
|
|
|
if (!file_put_contents($filepath, json_encode($data, JSON_PRETTY_PRINT))) {
|
|
|
|
|
throw new \Exception("Failed to write to file: {$filepath}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->info("Saved {$count} items to {$filename}");
|
|
|
|
|
$totalFetched += $count;
|
|
|
|
|
$offset += $limit;
|
|
|
|
|
$fileCount++;
|
|
|
|
|
|
|
|
|
|
// Sleep to avoid rate limiting
|
|
|
|
|
sleep(1);
|
|
|
|
|
} while ($count === $limit);
|
|
|
|
|
|
|
|
|
|
$this->info("Total items fetched: {$totalFetched}");
|
|
|
|
|
}
|
|
|
|
|
}
|