Files

560 lines
18 KiB
PHP
Raw Permalink Normal View History

2025-04-09 23:03:16 +03:00
<?php
namespace App\Services;
use App\Models\Game;
use App\Models\Genre;
use App\Models\Platform;
use App\Models\Artwork;
use App\Models\Screenshot;
use App\Models\Video;
use Illuminate\Support\Facades\Log;
use MarcReichel\IGDBLaravel\Models\Collection;
use MarcReichel\IGDBLaravel\Models\Game as IgdbGame;
use MarcReichel\IGDBLaravel\Models\Cover;
use MarcReichel\IGDBLaravel\Models\Genre as IgdbGenre;
use MarcReichel\IGDBLaravel\Models\Platform as IgdbPlatform;
class IgdbService
{
public function get(
int $limit,
$startDate = null,
$endDate = null,
) {
2025-04-09 23:03:16 +03:00
try {
$existingIds = $this->getExistingIds();
$queryBuilder = IgdbGame::select([
'id',
'name',
'summary',
2025-04-12 17:18:33 +03:00
'cover',
2025-04-09 23:03:16 +03:00
'slug',
'genres',
'platforms',
'first_release_date',
'rating',
'rating_count'
])
2025-04-12 17:18:33 +03:00
// ->where('rating_count', '>', 0)
// ->where('category', '=', 0)
;
2025-04-09 23:03:16 +03:00
if ($startDate) {
$queryBuilder->where('first_release_date', '>=', $startDate);
}
if ($endDate) {
$queryBuilder->where('first_release_date', '<=', $endDate);
}
2025-04-09 23:03:16 +03:00
if (!empty($existingIds)) {
2025-04-12 17:18:33 +03:00
// Convert array of quoted strings to array of integers
// $existingIds = array_map(function($id) {
// return (int)str_replace('"', '', $id);
2025-04-12 17:18:33 +03:00
// }, $existingIds);
$queryBuilder->whereNotIn('id', $existingIds);
2025-04-09 23:03:16 +03:00
}
$a = $queryBuilder
2025-04-12 17:18:33 +03:00
->orderBy('first_release_date', 'asc')
->limit($limit)
2025-04-09 23:03:16 +03:00
->get();
}catch (\Throwable $e) {
dd(
$e->getMessage(),
$e->getTrace()
);
}
return $a;
}
private function getExistingIds(): ?array
{
$existingGames = Game::get();
if (empty($existingGames)) {
return null;
}
$existingIds = [];
/** @var Game $existingGame */
foreach ($existingGames as $existingGame) {
2025-04-12 17:18:33 +03:00
$existingIds[] = (string)$existingGame->igdb_id;
2025-04-09 23:03:16 +03:00
}
2025-04-12 17:18:33 +03:00
return $existingIds;
// return array_map(function($id) {
// return '"'.$id.'"';
// }, $existingIds);
2025-04-09 23:03:16 +03:00
}
public function searchGames(string $query, int $limit = 10, ?int $startTimestamp = null, ?int $endTimestamp = null): array
{
try {
$queryBuilder = IgdbGame::select([
'id',
'name',
'summary',
'cover',
'slug',
'genres',
'platforms',
'first_release_date',
'rating',
'rating_count'
])
->where('rating_count', '>', 0)
->where('category', '=', 0) // Only main games, no DLCs or expansions
->limit($limit)
->orderBy('rating', 'desc');
if ($startTimestamp && $endTimestamp) {
$queryBuilder->where('first_release_date', '>=', $startTimestamp)
->where('first_release_date', '<=', $endTimestamp);
}
if ($query) {
$queryBuilder->search($query);
}
return $queryBuilder->get()->toArray();
} catch (\Exception $e) {
Log::error('Failed to search games', [
'query' => $query,
'error' => $e->getMessage()
]);
return [];
}
}
public function getGameDetails(int $igdbId): ?array
{
try {
$game = IgdbGame::select([
'id',
'name',
'summary',
'storyline',
'cover.url',
'slug',
'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'
])
->where('id', $igdbId)
->first();
if (!$game) {
return null;
}
return $game->toArray();
} catch (\Exception $e) {
Log::error('Failed to fetch game details from IGDB', [
'igdb_id' => $igdbId,
'error' => $e->getMessage()
]);
return null;
}
}
public function getGameCover(int $coverId): ?string
{
try {
return Cover::select(['url'])
->where('id', $coverId)
->first()
?->url;
} catch (\Exception $e) {
Log::error('Failed to get game cover', [
'id' => $coverId,
'error' => $e->getMessage()
]);
return null;
}
}
public function getGenreDetails(int $genreId): ?array
{
try {
return IgdbGenre::select(['id', 'name', 'slug'])
->where('id', $genreId)
->first()
?->toArray();
} catch (\Exception $e) {
Log::error('Failed to get genre details', [
'id' => $genreId,
'error' => $e->getMessage()
]);
return null;
}
}
public function getPlatformDetails(int $platformId): ?array
{
try {
return IgdbPlatform::select(['id', 'name', 'slug', 'abbreviation'])
->where('id', $platformId)
->first()
?->toArray();
} catch (\Exception $e) {
Log::error('Failed to get platform details', [
'id' => $platformId,
'error' => $e->getMessage()
]);
return null;
}
}
public function processGameData(IgdbGame $gameData): Game
{
// Process platforms
$platforms = [];
if (isset($gameData->platforms) && is_array($gameData->platforms)) {
2025-04-10 00:44:55 +03:00
$platformData = IgdbPlatform::select(['id', 'name', 'slug', 'abbreviation'])
->whereIn('id', $gameData->platforms)
->get();
foreach ($platformData as $platformDetails) {
$platform = Platform::updateOrCreate(
['igdb_id' => $platformDetails->id],
[
'name' => $platformDetails->name,
'slug' => $platformDetails->slug,
'abbreviation' => $platformDetails->abbreviation,
]
);
$platforms[] = $platform->id;
2025-04-09 23:03:16 +03:00
}
}
2025-04-10 00:44:55 +03:00
2025-04-12 17:18:33 +03:00
// Process genres
$genres = [];
if (isset($gameData->genres) && is_array($gameData->genres)) {
$genreData = IgdbGenre::select(['id', 'name', 'slug'])
->whereIn('id', $gameData->genres)
->get();
foreach ($genreData as $genreDetails) {
$genre = Genre::updateOrCreate(
['igdb_id' => $genreDetails->id],
[
'name' => $genreDetails->name,
'slug' => $genreDetails->slug,
]
);
$genres[] = $genre->id;
}
}
$cover = Cover::where('game', '=', $gameData->id)->first();
$coverUrl = null;
if ($cover) {
2026-02-28 17:21:59 +02:00
// Convert cover to high resolution
$coverUrl = $this->convertToHighResolutionUrl($cover->url, 'cover');
2025-04-12 17:18:33 +03:00
}
2025-04-09 23:03:16 +03:00
// Create or update the game
$game = Game::updateOrCreate(
['igdb_id' => $gameData->id],
[
'name' => $gameData->name,
'summary' => $gameData->summary ?? null,
2025-04-12 17:18:33 +03:00
'cover_url' => $coverUrl,
2025-04-09 23:03:16 +03:00
'slug' => $gameData->slug,
'first_release_date' => $gameData->first_release_date ?? null,
'rating' => $gameData->rating ?? null,
'rating_count' => $gameData->rating_count ?? null,
]
);
2025-04-12 17:18:33 +03:00
// Sync platforms and genres
if (!empty($platforms)) {
$game->platforms()->sync($platforms);
}
if (!empty($genres)) {
$game->genres()->sync($genres);
}
2025-04-09 23:03:16 +03:00
// Process artworks
2025-04-10 00:44:55 +03:00
try {
$artworks = \MarcReichel\IGDBLaravel\Models\Artwork::select([
'id',
'url',
2025-04-12 17:18:33 +03:00
'width',
2025-04-10 00:44:55 +03:00
'height',
'image_id'
])
->where('game', '=', $gameData->id)
->get();
foreach ($artworks as $artworkData) {
2026-02-28 17:21:59 +02:00
// Convert URL to high resolution
$artworkUrl = $this->convertToHighResolutionUrl($artworkData->url, 'artwork');
2025-04-09 23:03:16 +03:00
Artwork::updateOrCreate(
2025-04-10 00:44:55 +03:00
['igdb_id' => $artworkData->id],
2025-04-09 23:03:16 +03:00
[
'game_id' => $game->id,
2026-02-28 17:21:59 +02:00
'url' => $artworkUrl,
2025-04-10 00:44:55 +03:00
'width' => $artworkData->width ?? null,
'height' => $artworkData->height ?? null,
'image_id' => $artworkData->image_id ?? null,
2025-04-09 23:03:16 +03:00
]
);
}
2025-04-10 00:44:55 +03:00
} catch (\Exception $e) {
Log::error('Failed to get artworks for game', [
'game_id' => $game->id,
'error' => $e->getMessage()
]);
2025-04-09 23:03:16 +03:00
}
2025-04-10 00:44:55 +03:00
2025-04-09 23:03:16 +03:00
// Process screenshots
2025-04-10 00:44:55 +03:00
try {
$screenshots = \MarcReichel\IGDBLaravel\Models\Screenshot::select([
'id',
'url',
'width',
'height',
'image_id'
])
->where('game', '=', $gameData->id)
->get();
foreach ($screenshots as $screenshotData) {
2026-02-28 17:21:59 +02:00
// Convert URL to high resolution
$screenshotUrl = $this->convertToHighResolutionUrl($screenshotData->url, 'screenshot');
2025-04-09 23:03:16 +03:00
Screenshot::updateOrCreate(
2025-04-10 00:44:55 +03:00
['igdb_id' => $screenshotData->id],
2025-04-09 23:03:16 +03:00
[
'game_id' => $game->id,
2026-02-28 17:21:59 +02:00
'url' => $screenshotUrl,
2025-04-10 00:44:55 +03:00
'width' => $screenshotData->width ?? null,
'height' => $screenshotData->height ?? null,
'image_id' => $screenshotData->image_id ?? null,
2025-04-09 23:03:16 +03:00
]
);
}
2025-04-10 00:44:55 +03:00
} catch (\Exception $e) {
Log::error('Failed to get screenshots for game', [
'game_id' => $game->id,
'error' => $e->getMessage()
]);
2025-04-09 23:03:16 +03:00
}
// Process videos
2025-04-10 00:44:55 +03:00
try {
$videos = \MarcReichel\IGDBLaravel\Models\GameVideo::select([
'id',
2025-04-12 17:18:33 +03:00
'name',
2025-04-10 00:44:55 +03:00
'video_id',
'url',
'checksum'
])
->where('game', '=', $gameData->id)
->get();
foreach ($videos as $videoData) {
2025-04-09 23:03:16 +03:00
Video::updateOrCreate(
2025-04-10 00:44:55 +03:00
['igdb_id' => $videoData->id],
2025-04-09 23:03:16 +03:00
[
'game_id' => $game->id,
2025-04-10 00:44:55 +03:00
'name' => $videoData->name,
'video_id' => $videoData->video_id,
'url' => $videoData->url,
'checksum' => $videoData->checksum ?? null,
2025-04-09 23:03:16 +03:00
]
);
}
2025-04-10 00:44:55 +03:00
} catch (\Exception $e) {
Log::error('Failed to get videos for game', [
'game_id' => $game->id,
'error' => $e->getMessage()
]);
2025-04-09 23:03:16 +03:00
}
// Process relationships
$this->processGameRelationships($game, $gameData);
return $game;
}
private function processGameRelationships(Game $game,IgdbGame $gameData): void
{
// Ensure $gameData is an IgdbGame instance
if (!($gameData instanceof \MarcReichel\IGDBLaravel\Models\Game)) {
throw new \InvalidArgumentException('Game data must be an instance of IgdbGame');
}
2025-04-12 17:18:33 +03:00
// Initialize relationships array
$relationships = [];
// Define relationship mappings and check each one
$relationshipMappings = [
'bundles',
'collections',
'dlcs',
'expanded_games',
'franchises',
'ports',
'remakes',
'similar_games'
2025-04-09 23:03:16 +03:00
];
2025-04-12 17:18:33 +03:00
// Only query IGDB for relationships that have data
foreach ($relationshipMappings as $relationship) {
if (!empty($gameData->$relationship)) {
$relationships[$relationship] = IgdbGame::select(['id'])
->where('id', 'in', $gameData->$relationship)
->get()
->pluck('id')
->toArray();
} else {
$relationships[$relationship] = [];
}
}
// Process and sync each relationship
foreach ($relationships as $igdbKey => $relatedIds) {
// Convert IGDB relationship key to Laravel relationship method
$relationshipMethod = str_replace('_', '', $igdbKey); // Remove underscores
2025-04-09 23:03:16 +03:00
$relatedGames = [];
2025-04-12 17:18:33 +03:00
if (!empty($relatedIds)) {
2025-04-09 23:03:16 +03:00
$relatedGames = Game::whereIn('igdb_id', $relatedIds)->pluck('id')->toArray();
}
2025-04-12 17:18:33 +03:00
// Only sync if the relationship method exists
if (method_exists($game, $relationshipMethod)) {
Log::info("Syncing relationship '$relationshipMethod' for game {$game->id}");
$game->$relationshipMethod()->sync($relatedGames);
} else {
Log::warning("Relationship method '$relationshipMethod' does not exist on Game model", [
'game_id' => $game->id,
'igdb_key' => $igdbKey
]);
}
2025-04-09 23:03:16 +03:00
}
}
2025-04-10 00:44:55 +03:00
public function processGenreData(array $genreData): void
{
try {
Genre::updateOrCreate(
['igdb_id' => $genreData['id']],
[
'name' => $genreData['name'],
'slug' => $genreData['slug'],
]
);
} catch (\Exception $e) {
Log::error('Failed to process genre data', [
'genre_id' => $genreData['id'],
'error' => $e->getMessage()
]);
throw $e;
}
}
public function processPlatformData(array $platformData): void
{
try {
Platform::updateOrCreate(
['igdb_id' => $platformData['id']],
[
'name' => $platformData['name'],
'slug' => $platformData['slug'],
'abbreviation' => $platformData['abbreviation'] ?? null,
]
);
} catch (\Exception $e) {
Log::error('Failed to process platform data', [
'platform_id' => $platformData['id'],
'error' => $e->getMessage()
]);
throw $e;
}
}
2026-02-28 17:21:59 +02:00
/**
* Convert IGDB image URL to high resolution
*
* IGDB Image Size Reference:
* - t_thumb: 90x90 (thumb)
* - t_cover_small: 90x128 (cover small)
* - t_screenshot_med: 569x320 (screenshot medium)
* - t_screenshot_big: 889x500 (screenshot big)
* - t_screenshot_huge: 1280x720 (screenshot huge)
* - t_720p: 1280x720 (720p)
* - t_1080p: 1920x1080 (1080p - highest quality)
* - t_cover_big: 264x374 (cover big)
* - t_logo_med: 284x160 (logo medium)
* - t_original: Original size (uncompressed, may be very large)
*
* @param string|null $url The IGDB image URL
* @param string $type The type of image (screenshot, artwork, cover)
* @return string|null The high resolution URL
*/
private function convertToHighResolutionUrl(?string $url, string $type = 'screenshot'): ?string
{
if (!$url) {
return null;
}
// Ensure URL has https protocol
if (strpos($url, '//') === 0) {
$url = 'https:' . $url;
} elseif (strpos($url, 'http://') === 0) {
$url = str_replace('http://', 'https://', $url);
}
// Define size mappings based on image type
$sizeMap = [
'screenshot' => 't_1080p', // 1920x1080 - best for screenshots
'artwork' => 't_1080p', // 1920x1080 - best for artwork
'cover' => 't_cover_big', // 264x374 - good balance for covers
];
$targetSize = $sizeMap[$type] ?? 't_1080p';
// Replace any existing size identifier with high resolution
$url = preg_replace('/t_[a-z_0-9]+/', $targetSize, $url);
$url = str_replace('t_thumb', 't_original', $url);
return $url;
}
2025-04-09 23:03:16 +03:00
}