diff --git a/Blacklight/Console.php b/Blacklight/Console.php index 0e2f4df3a..c80e98c08 100755 --- a/Blacklight/Console.php +++ b/Blacklight/Console.php @@ -16,6 +16,8 @@ use MarcReichel\IGDBLaravel\Models\Platform; /** * Class Console. + * + * @deprecated Use \App\Services\ConsoleService instead. This class is kept for backwards compatibility. */ class Console { diff --git a/app/Http/Controllers/Admin/AdminConsoleController.php b/app/Http/Controllers/Admin/AdminConsoleController.php index be10cfb13..9c8153df9 100644 --- a/app/Http/Controllers/Admin/AdminConsoleController.php +++ b/app/Http/Controllers/Admin/AdminConsoleController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\BasePageController; -use Blacklight\Console; +use App\Services\ConsoleService; use Blacklight\Genres; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -12,6 +12,14 @@ use Illuminate\View\View; class AdminConsoleController extends BasePageController { + protected ConsoleService $consoleService; + + public function __construct(ConsoleService $consoleService) + { + parent::__construct(); + $this->consoleService = $consoleService; + } + /** * Display a listing of console games */ @@ -32,7 +40,6 @@ class AdminConsoleController extends BasePageController public function edit(Request $request): View|RedirectResponse { $this->setAdminPrefs(); - $console = new Console(['Settings' => null]); $gen = new Genres; $meta_title = $title = 'Console Edit'; @@ -41,7 +48,7 @@ class AdminConsoleController extends BasePageController if ($request->has('id')) { $id = $request->input('id'); - $con = $console->getConsoleInfo($id); + $con = $this->consoleService->getConsoleInfo($id); if (! $con) { abort(404); @@ -65,7 +72,7 @@ class AdminConsoleController extends BasePageController ? $con['releasedate'] : Carbon::parse($request->input('releasedate'))->timestamp; - $console->update( + $this->consoleService->update( $id, $request->input('title'), $request->input('asin'), diff --git a/app/Http/Controllers/ConsoleController.php b/app/Http/Controllers/ConsoleController.php index 2d0a8e3cd..d0f1109c5 100644 --- a/app/Http/Controllers/ConsoleController.php +++ b/app/Http/Controllers/ConsoleController.php @@ -3,13 +3,21 @@ namespace App\Http\Controllers; use App\Models\Category; -use Blacklight\Console; +use App\Services\ConsoleService; use Blacklight\Genres; use Illuminate\Http\Request; use Illuminate\Support\Arr; class ConsoleController extends BasePageController { + protected ConsoleService $consoleService; + + public function __construct(ConsoleService $consoleService) + { + parent::__construct(); + $this->consoleService = $consoleService; + } + /** * @throws \Exception */ @@ -18,7 +26,6 @@ class ConsoleController extends BasePageController if ($id === 'WiiVare') { $id = 'WiiVareVC'; } - $console = new Console; $gen = new Genres; $concats = Category::getChildren(Category::GAME_ROOT); @@ -42,13 +49,13 @@ class ConsoleController extends BasePageController $catarray = []; $catarray[] = $category; - $ordering = $console->getConsoleOrdering(); + $ordering = $this->consoleService->getConsoleOrdering(); $orderby = $request->has('ob') && \in_array($request->input('ob'), $ordering, false) ? $request->input('ob') : ''; $page = $request->has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1; $offset = ($page - 1) * config('nntmux.items_per_cover_page'); $consoles = []; - $rslt = $console->getConsoleRange($page, $catarray, $offset, config('nntmux.items_per_cover_page'), $orderby, $this->userdata->categoryexclusions); + $rslt = $this->consoleService->getConsoleRange($page, $catarray, $offset, config('nntmux.items_per_cover_page'), $orderby, $this->userdata->categoryexclusions); $results = $this->paginate($rslt ?? [], $rslt[0]->_totalcount ?? 0, config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); $maxwords = 50; diff --git a/app/Http/Controllers/DetailsController.php b/app/Http/Controllers/DetailsController.php index e5850c50c..23da6328a 100644 --- a/app/Http/Controllers/DetailsController.php +++ b/app/Http/Controllers/DetailsController.php @@ -19,7 +19,7 @@ use App\Services\BookService; use App\Services\AnidbService; use App\Services\XxxBrowseService; use App\Services\GamesService; -use Blacklight\Console; +use App\Services\ConsoleService; use Blacklight\Music; use App\Services\ReleaseExtraService; use Illuminate\Http\Request; @@ -133,7 +133,7 @@ class DetailsController extends BasePageController $con = ''; if ($data['consoleinfo_id'] !== '') { - $con = (new Console)->getConsoleInfo($data['consoleinfo_id']); + $con = (new ConsoleService)->getConsoleInfo($data['consoleinfo_id']); } $AniDBAPIArray = ''; diff --git a/app/Models/ConsoleInfo.php b/app/Models/ConsoleInfo.php index 8face73e3..c04abe639 100644 --- a/app/Models/ConsoleInfo.php +++ b/app/Models/ConsoleInfo.php @@ -3,6 +3,8 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; use Laravel\Scout\Searchable; /** @@ -75,4 +77,68 @@ class ConsoleInfo extends Model 'platform' => $this->platform, ]; } + + // ======================================== + // Relationships + // ======================================== + + /** + * Get the genre for the console info. + */ + public function genre(): BelongsTo + { + return $this->belongsTo(Genre::class, 'genres_id'); + } + + /** + * Get the releases for the console info. + */ + public function releases(): HasMany + { + return $this->hasMany(Release::class, 'consoleinfo_id'); + } + + // ======================================== + // Query Scopes + // ======================================== + + /** + * Scope a query to only include consoles with covers. + */ + public function scopeWithCover($query) + { + return $query->where('cover', 1); + } + + /** + * Scope a query to only include consoles for a specific platform. + */ + public function scopeForPlatform($query, string $platform) + { + return $query->where('platform', $platform); + } + + // ======================================== + // Static Helper Methods + // ======================================== + + /** + * Get console info by ID with genre. + */ + public static function getWithGenre(int $id): ?self + { + return static::query() + ->where('consoleinfo.id', $id) + ->select('consoleinfo.*', 'genres.title as genres') + ->leftJoin('genres', 'genres.id', '=', 'consoleinfo.genres_id') + ->first(); + } + + /** + * Find by ASIN. + */ + public static function findByAsin(string $asin): ?self + { + return static::where('asin', $asin)->first(); + } } diff --git a/app/Services/ConsoleService.php b/app/Services/ConsoleService.php new file mode 100644 index 000000000..c8c2830e2 --- /dev/null +++ b/app/Services/ConsoleService.php @@ -0,0 +1,807 @@ +echoOutput = config('nntmux.echocli'); + $this->colorCli = new ColorCLI; + $this->imageService = $imageService ?? new ReleaseImageService; + + $this->pubkey = Settings::settingValue('amazonpubkey'); + $this->privkey = Settings::settingValue('amazonprivkey'); + $this->asstag = Settings::settingValue('amazonassociatetag'); + $this->gameQty = (Settings::settingValue('maxgamesprocessed') !== '') ? (int) Settings::settingValue('maxgamesprocessed') : 150; + $this->sleepTime = (Settings::settingValue('amazonsleep') !== '') ? (int) Settings::settingValue('amazonsleep') : 1000; + $this->imgSavePath = config('nntmux_settings.covers_path') . '/console/'; + $this->renamed = (int) Settings::settingValue('lookupgames') === 2; + + $this->failCache = []; + } + + // ======================================== + // Console Info Retrieval Methods + // ======================================== + + /** + * Get console info by ID. + */ + public function getConsoleInfo(int $id): ?Model + { + return ConsoleInfo::query() + ->where('consoleinfo.id', $id) + ->select('consoleinfo.*', 'genres.title as genres') + ->leftJoin('genres', 'genres.id', '=', 'consoleinfo.genres_id') + ->first(); + } + + /** + * Get console info by name using full-text search. + */ + public function getConsoleInfoByName(string $title, string $platform): Model|false + { + $searchWords = ''; + + $title = preg_replace('/( - | -|\(.+\)|\(|\))/', ' ', $title); + $title = preg_replace('/[^\w ]+/', '', $title); + $title = trim(trim(preg_replace('/\s\s+/i', ' ', $title))); + + foreach (explode(' ', $title) as $word) { + $word = trim(rtrim(trim($word), '-')); + if ($word !== '' && $word !== '-') { + $word = '+' . $word; + $searchWords .= sprintf('%s ', $word); + } + } + $searchWords = trim($searchWords . '+' . $platform); + + return ConsoleInfo::search($searchWords)->first() ?? false; + } + + // ======================================== + // Browse/Range Methods + // ======================================== + + /** + * Get console games range with pagination. + * + * @throws \Exception + */ + public function getConsoleRange(int $page, array $cat, int $start, int $num, string $orderBy, array $excludedCats = []): array + { + $page = max(1, $page); + $start = max(0, $start); + + $browseBy = $this->getBrowseBy(); + $catsrch = ''; + if (\count($cat) > 0 && (int) $cat[0] !== -1) { + $catsrch = Category::getCategorySearch($cat); + } + $exccatlist = ''; + if (\count($excludedCats) > 0) { + $exccatlist = ' AND r.categories_id NOT IN (' . implode(',', $excludedCats) . ')'; + } + $order = $this->getConsoleOrder($orderBy); + $calcSql = sprintf( + " + SELECT SQL_CALC_FOUND_ROWS + con.id, + GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id + FROM consoleinfo con + LEFT JOIN releases r ON con.id = r.consoleinfo_id + WHERE con.title != '' + AND con.cover = 1 + AND r.passwordstatus %s + %s %s %s + GROUP BY con.id + ORDER BY %s %s %s", + app(Releases\ReleaseBrowseService::class)->showPasswords(), + $browseBy, + $catsrch, + $exccatlist, + $order[0], + $order[1], + ($start === false ? '' : ' LIMIT ' . $num . ' OFFSET ' . $start) + ); + + $cached = Cache::get(md5($calcSql . $page)); + if ($cached !== null) { + $consoles = $cached; + } else { + $data = DB::select($calcSql); + $consoles = ['total' => DB::select('SELECT FOUND_ROWS() AS total'), 'result' => $data]; + $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium')); + Cache::put(md5($calcSql . $page), $consoles, $expiresAt); + } + + $consoleIDs = $releaseIDs = false; + if (\is_array($consoles['result'])) { + foreach ($consoles['result'] as $console => $id) { + $consoleIDs = [$id->id]; + $releaseIDs = [$id->grp_release_id]; + } + } + + $sql = sprintf( + ' + SELECT + r.id, r.rarinnerfilecount, r.grabs, r.comments, r.totalpart, r.size, r.postdate, r.searchname, r.haspreview, r.passwordstatus, r.guid, rn.releases_id, g.name AS group_name, df.failed AS failed, + con.*, + r.consoleinfo_id, + g.name AS group_name, + genres.title AS genre, + rn.releases_id AS nfoid + FROM releases r + LEFT OUTER JOIN usenet_groups g ON g.id = r.groups_id + LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id + LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id + INNER JOIN consoleinfo con ON con.id = r.consoleinfo_id + INNER JOIN genres ON con.genres_id = genres.id + WHERE con.id IN (%s) + AND r.id IN (%s) + %s + GROUP BY con.id + ORDER BY %s %s', + (\is_array($consoleIDs) ? implode(',', $consoleIDs) : -1), + (\is_array($releaseIDs) ? implode(',', $releaseIDs) : -1), + $catsrch, + $order[0], + $order[1] + ); + + $return = Cache::get(md5($sql . $page)); + if ($return !== null) { + return $return; + } + + $return = DB::select($sql); + if (\count($return) > 0) { + $return[0]->_totalcount = $consoles['total'][0]->total ?? 0; + } + + $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long')); + Cache::put(md5($sql . $page), $return, $expiresAt); + + return $return; + } + + /** + * Get console order array. + */ + public function getConsoleOrder(string $orderBy): array + { + $order = ($orderBy === '') ? 'r.postdate' : $orderBy; + $orderArr = explode('_', $order); + + $orderfield = match ($orderArr[0]) { + 'title' => 'con.title', + 'platform' => 'con.platform', + 'releasedate' => 'con.releasedate', + 'genre' => 'con.genres_id', + 'size' => 'r.size', + 'files' => 'r.totalpart', + 'stats' => 'r.grabs', + default => 'r.postdate', + }; + + $ordersort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc'; + + return [$orderfield, $ordersort]; + } + + /** + * Get console ordering options. + */ + public function getConsoleOrdering(): array + { + return [ + 'title_asc', 'title_desc', + 'posted_asc', 'posted_desc', + 'size_asc', 'size_desc', + 'files_asc', 'files_desc', + 'stats_asc', 'stats_desc', + 'platform_asc', 'platform_desc', + 'releasedate_asc', 'releasedate_desc', + 'genre_asc', 'genre_desc', + ]; + } + + /** + * Get browse by options. + */ + public function getBrowseByOptions(): array + { + return ['platform' => 'platform', 'title' => 'title', 'genre' => 'genres_id']; + } + + /** + * Get browse by SQL clause. + */ + public function getBrowseBy(): string + { + $browseBy = ' '; + foreach ($this->getBrowseByOptions() as $bbk => $bbv) { + if (! empty($_REQUEST[$bbk])) { + $bbs = stripslashes($_REQUEST[$bbk]); + $browseBy .= ' AND con.' . $bbv . ' LIKE ' . escapeString('%' . $bbs . '%'); + } + } + + return $browseBy; + } + + // ======================================== + // Update Methods + // ======================================== + + /** + * Update console info record. + */ + public function update( + int $id, + string $title, + ?string $asin, + ?string $url, + ?int $salesrank, + ?string $platform, + ?string $publisher, + ?string $releasedate, + ?string $esrb, + int $cover, + ?int $genresId, + string $review = 'review' + ): void { + $releasedate = $releasedate !== '' ? $releasedate : null; + $review = $review === 'review' ? $review : substr($review, 0, 3000); + + ConsoleInfo::query() + ->where('id', $id) + ->update([ + 'title' => $title, + 'asin' => $asin, + 'url' => $url, + 'salesrank' => $salesrank, + 'platform' => $platform, + 'publisher' => $publisher, + 'releasedate' => $releasedate, + 'esrb' => $esrb, + 'cover' => $cover, + 'genres_id' => $genresId, + 'review' => $review, + ]); + } + + // ======================================== + // IGDB Integration Methods + // ======================================== + + /** + * Update console info from IGDB. + * + * @return int|mixed + * + * @throws \Exception + */ + public function updateConsoleInfo(array $gameInfo): int + { + $consoleId = self::CONS_NTFND; + + $igdb = $this->fetchIGDBProperties($gameInfo['title'], $gameInfo['node']); + if ($igdb !== false) { + if ($igdb['coverurl'] !== '') { + $igdb['cover'] = 1; + } else { + $igdb['cover'] = 0; + } + + $consoleId = $this->updateConsoleTable($igdb); + + if ($this->echoOutput && $consoleId !== -2) { + $this->colorCli->header('Added/updated game: ') . + $this->colorCli->alternateOver(' Title: ') . + $this->colorCli->primary($igdb['title']) . + $this->colorCli->alternateOver(' Platform: ') . + $this->colorCli->primary($igdb['platform']) . + $this->colorCli->alternateOver(' Genre: ') . + $this->colorCli->primary($igdb['consolegenre']); + } + } + + return $consoleId; + } + + /** + * Fetch IGDB properties for a game. + * + * @throws \Exception + */ + public function fetchIGDBProperties(string $gameInfo, string $gamePlatform): bool|array|\StdClass + { + $bestMatch = false; + + $gamePlatform = $this->replacePlatform($gamePlatform); + + if (config('config.credentials.client_id') !== '' && config('config.credentials.client_secret') !== '') { + try { + $result = Game::where('name', $gameInfo)->get(); + if (! empty($result)) { + $bestMatchPct = 0; + foreach ($result as $res) { + similar_text(strtolower($gameInfo), strtolower($res->name), $percent); + if ($percent >= 90 && $percent > $bestMatchPct) { + $bestMatch = $res->id; + $bestMatchPct = $percent; + } + } + + if ($bestMatch !== false) { + $game = Game::with([ + 'cover' => ['url'], + 'screenshots' => ['url'], + 'involved_companies' => ['company', 'publisher'], + 'themes', + ])->where('id', $bestMatch)->first(); + + $publishers = []; + if (! empty($game->involved_companies)) { + foreach ($game->involved_companies as $publisher) { + if ($publisher['publisher'] === true) { + $company = Company::find($publisher['company']); + $publishers[] = $company['name']; + } + } + } + + $genres = []; + if (! empty($game->themes)) { + foreach ($game->themes as $theme) { + $genres[] = $theme['name']; + } + } + + $genreKey = $this->getGenreKey(implode(',', $genres)); + + $platform = ''; + if (! empty($game->platforms)) { + foreach ($game->platforms as $platforms) { + $percentCurrent = 0; + $gamePlatforms = Platform::where('id', $platforms)->get(); + foreach ($gamePlatforms as $gamePlat) { + similar_text($gamePlat['name'], $gamePlatform, $percent); + if ($percent >= 85 && $percent > $percentCurrent) { + $percentCurrent = $percent; + $platform = $gamePlat['name']; + break; + } + } + } + } + + return [ + 'title' => $game->name, + 'asin' => $game->id, + 'review' => $game->summary ?? '', + 'coverurl' => ! empty($game->cover->url) ? 'https:' . $game->cover->url : '', + 'releasedate' => ! empty($game->first_release_date) ? $game->first_release_date->format('Y-m-d') : now()->format('Y-m-d'), + 'esrb' => ! empty($game->aggregated_rating) ? round($game->aggregated_rating) . '%' : 'Not Rated', + 'url' => $game->url ?? '', + 'publisher' => ! empty($publishers) ? implode(',', $publishers) : 'Unknown', + 'platform' => $platform ?? '', + 'consolegenre' => ! empty($genres) ? implode(',', $genres) : 'Unknown', + 'consolegenreid' => $genreKey ?? '', + 'salesrank' => '', + ]; + } + + $this->colorCli->notice('IGDB returned no valid results'); + + return false; + } + + $this->colorCli->notice('IGDB found no valid results'); + + return false; + } catch (ClientException $e) { + if ($e->getCode() === 429) { + $this->igdbSleep = now()->endOfMonth(); + } + } catch (\Exception $e) { + $this->colorCli->error('Error fetching IGDB properties: ' . $e->getMessage()); + + return false; + } + } + + return false; + } + + // ======================================== + // Release Processing Methods + // ======================================== + + /** + * Process console releases. + * + * @throws \Exception + */ + public function processConsoleReleases(): void + { + $query = Release::query() + ->select(['searchname', 'id']) + ->whereBetween('categories_id', [Category::GAME_ROOT, Category::GAME_OTHER]) + ->whereNull('consoleinfo_id'); + + if ($this->renamed === true) { + $query->where('isrenamed', '=', 1); + } + + $res = $query->limit($this->gameQty)->orderBy('postdate')->get(); + + $releaseCount = $res->count(); + if ($res instanceof \Traversable && $releaseCount > 0) { + if ($this->echoOutput) { + $this->colorCli->header('Processing ' . $releaseCount . ' console release(s).'); + } + + foreach ($res as $arr) { + $startTime = now()->timestamp; + $usedAmazon = false; + $gameId = self::CONS_NTFND; + $gameInfo = $this->parseTitle($arr['searchname']); + + if ($gameInfo !== false) { + if ($this->echoOutput) { + $this->colorCli->info('Looking up: ' . $gameInfo['title'] . ' (' . $gameInfo['platform'] . ')'); + } + + // Check for existing console entry. + $gameCheck = $this->getConsoleInfoByName($gameInfo['title'], $gameInfo['platform']); + + if ($gameCheck === false && \in_array($gameInfo['title'] . $gameInfo['platform'], $this->failCache, false)) { + // Lookup recently failed, no point trying again + if ($this->echoOutput) { + $this->colorCli->info('Cached previous failure. Skipping.'); + } + $gameId = -2; + } elseif ($gameCheck === false) { + $gameId = $this->updateConsoleInfo($gameInfo); + $usedAmazon = true; + if ($gameId === null) { + $gameId = -2; + $this->failCache[] = $gameInfo['title'] . $gameInfo['platform']; + } + } else { + if ($this->echoOutput) { + $this->colorCli->headerOver('Found Local: ') . + $this->colorCli->primary("{$gameInfo['title']} - {$gameInfo['platform']}"); + } + $gameId = $gameCheck['id'] ?? -2; + } + } elseif ($this->echoOutput) { + echo '.'; + } + + // Update release. + Release::query()->where('id', $arr['id'])->update(['consoleinfo_id' => $gameId]); + + // Sleep to not flood amazon. + $diff = floor((now()->timestamp - $startTime) * 1000000); + if ($this->sleepTime * 1000 - $diff > 0 && $usedAmazon === true) { + usleep($this->sleepTime * 1000 - $diff); + } + } + } elseif ($this->echoOutput) { + $this->colorCli->header('No console releases to process.'); + } + } + + // ======================================== + // Title Parsing Methods + // ======================================== + + /** + * Parse release title for game info. + * + * @return array|false + */ + public function parseTitle(string $releaseName): array|false + { + $releaseName = preg_replace('/\sMulti\d?\s/i', '', $releaseName); + $result = []; + + // Get name of the game from name of release. + if (preg_match('/^(.+((abgx360EFNet|EFNet\sFULL|FULL\sabgxEFNet|abgx\sFULL|abgxbox360EFNet)\s|illuminatenboard\sorg|Place2(hom|us)e.net|united-forums? co uk|\(\d+\)))?(?P.*?)[\.\-_ ](v\.?\d\.\d|PAL|NTSC|EUR|USA|JP|ASIA|JAP|JPN|AUS|MULTI(\.?\d{1,2})?|PATCHED|FULLDVD|DVD5|DVD9|DVDRIP|PROPER|REPACK|RETAIL|DEMO|DISTRIBUTION|REGIONFREE|[\. ]RF[\. ]?|READ\.?NFO|NFOFIX|PSX(2PSP)?|PS[2-4]|PSP|PSVITA|WIIU|WII|X\-?BOX|XBLA|X360|3DS|NDS|N64|NGC)/i', $releaseName, $hits)) { + $title = $hits['title']; + + // Replace dots, underscores, or brackets with spaces. + $result['title'] = str_replace(['.', '_', '%20', '[', ']'], ' ', $title); + $result['title'] = str_replace([' RF ', '.RF.', '-RF-', '_RF_'], ' ', $result['title']); + // Remove format tags from release title for match + $result['title'] = trim(preg_replace('/PAL|MULTI(\d)?|NTSC-?J?|\(JAPAN\)/i', '', $result['title'])); + // Remove disc tags from release title for match + $result['title'] = trim(preg_replace('/Dis[ck] \d.*$/i', '', $result['title'])); + + // Needed to add code to handle DLC Properly. + if (stripos('dlc', $result['title']) !== false) { + $result['dlc'] = '1'; + if (stripos('Rock Band Network', $result['title']) !== false) { + $result['title'] = 'Rock Band'; + } elseif (strpos('-', $result['title']) !== false) { + $dlc = explode('-', $result['title']); + $result['title'] = $dlc[0]; + } elseif (preg_match('/(.*? .*?) /i', $result['title'], $dlc)) { + $result['title'] = $dlc[0]; + } + } + } else { + $title = ''; + } + + // Get the platform of the release. + if (preg_match('/[\.\-_ ](?P<platform>XBLA|WiiWARE|N64|SNES|NES|PS[2-4]|PS 3|PSX(2PSP)?|PSP|WIIU|WII|XBOX360|XBOXONE|X\-?BOX|X360|3DS|NDS|N?GC)/i', $releaseName, $hits)) { + $platform = $hits['platform']; + + if (preg_match('/^N?GC$/i', $platform)) { + $platform = 'NGC'; + } + + if (stripos('PSX2PSP', $platform) === 0) { + $platform = 'PSX'; + } + + if (! empty($title) && stripos('XBLA', $platform) === 0 && stripos('dlc', $title) !== false) { + $platform = 'XBOX360'; + } + + $browseNode = $this->getBrowseNode($platform); + $result['platform'] = $platform; + $result['node'] = $browseNode; + } + + $result['release'] = $releaseName; + array_map('trim', $result); + + return (isset($result['title'], $result['platform']) && ! empty($result['title'])) ? $result : false; + } + + // ======================================== + // Platform/Node Methods + // ======================================== + + /** + * Replace platform name to Amazon equivalent. + */ + public function replacePlatform(string $platform): string + { + return match (strtoupper($platform)) { + 'X360', 'XBOX360' => 'Xbox 360', + 'XBOXONE', 'XBOX ONE' => 'Xbox One', + 'DSI', 'NDS' => 'Nintendo DS', + '3DS' => 'Nintendo 3DS', + 'PS2' => 'PlayStation2', + 'PS3' => 'PlayStation 3', + 'PS4' => 'PlayStation 4', + 'PSP' => 'Sony PSP', + 'PSVITA' => 'PlayStation Vita', + 'PSX', 'PSX2PSP' => 'PlayStation', + 'WIIU' => 'Nintendo Wii U', + 'WII' => 'Nintendo Wii', + 'NGC' => 'GameCube', + 'N64' => 'Nintendo 64', + 'NES' => 'Nintendo NES', + 'SUPER NINTENDO', 'NINTENDO SUPER NES', 'SNES' => 'SNES', + default => $platform, + }; + } + + /** + * Get Amazon browse node ID for platform. + */ + public function getBrowseNode(string $platform): string + { + return match ($platform) { + 'PS2' => '301712', + 'PS3' => '14210751', + 'PS4' => '6427814011', + 'PSP' => '11075221', + 'PSVITA' => '3010556011', + 'PSX' => '294940', + 'WII', 'Wii' => '14218901', + 'WIIU', 'WiiU' => '3075112011', + 'XBOX360', 'X360' => '14220161', + 'XBOXONE' => '6469269011', + 'XBOX', 'X-BOX' => '537504', + 'NDS' => '11075831', + '3DS' => '2622269011', + 'GC', 'NGC' => '541022', + 'N64' => '229763', + 'SNES' => '294945', + 'NES' => '566458', + default => '468642', + }; + } + + // ======================================== + // Genre Methods + // ======================================== + + /** + * Match browse node to genre name. + * + * @return bool|string + */ + public function matchBrowseNode(string $nodeName): bool|string + { + $str = match ($nodeName) { + 'Action_shooter', 'Action_Games', 'Action_games' => 'Action', + 'Action/Adventure', 'Action\\Adventure', 'Adventure_games' => 'Adventure', + 'Boxing_games', 'Sports_games' => 'Sports', + 'Fantasy_action_games' => 'Fantasy', + 'Fighting_action_games' => 'Fighting', + 'Flying_simulation_games' => 'Flying', + 'Horror_action_games' => 'Horror', + 'Kids & Family' => 'Family', + 'Role_playing_games' => 'Role-Playing', + 'Shooter_action_games' => 'Shooter', + 'Singing_games' => 'Music', + 'Action', 'Adventure', 'Arcade', 'Board Games', 'Cards', 'Casino', + 'Collections', 'Family', 'Fantasy', 'Fighting', 'Flying', 'Horror', + 'Music', 'Puzzle', 'Racing', 'Rhythm', 'Role-Playing', 'Simulation', + 'Shooter', 'Shooting', 'Sports', 'Strategy', 'Trivia' => $nodeName, + default => '', + }; + + return ($str !== '') ? $str : false; + } + + // ======================================== + // Protected Helper Methods + // ======================================== + + /** + * Update or create console info in the database. + * + * @return int|mixed + */ + protected function updateConsoleTable(array $con = []): int + { + $check = ConsoleInfo::query()->where('asin', $con['asin'])->first(); + + if ($check === null) { + $consoleId = ConsoleInfo::query() + ->insertGetId([ + 'title' => $con['title'], + 'asin' => $con['asin'], + 'url' => $con['url'], + 'salesrank' => $con['salesrank'], + 'platform' => $con['platform'], + 'publisher' => $con['publisher'], + 'genres_id' => (int) $con['consolegenreid'] === -1 ? null : $con['consolegenreid'], + 'esrb' => $con['esrb'], + 'releasedate' => $con['releasedate'] !== '' ? $con['releasedate'] : null, + 'review' => substr($con['review'], 0, 3000), + 'created_at' => now(), + 'updated_at' => now(), + ]); + + if ($con['cover'] === 1) { + $con['cover'] = $this->imageService->saveImage((string) $consoleId, $con['coverurl'], $this->imgSavePath, 250, 250); + } + } else { + $consoleId = $check['id']; + + if ($con['cover'] === 1) { + $con['cover'] = $this->imageService->saveImage((string) $consoleId, $con['coverurl'], $this->imgSavePath, 250, 250); + } + + $this->update( + $consoleId, + $con['title'], + $con['asin'], + $con['url'], + $con['salesrank'], + $con['platform'], + $con['publisher'], + $con['releasedate'] ?? null, + $con['esrb'], + $con['cover'], + $con['consolegenreid'], + $con['review'] ?? null + ); + } + + return $consoleId; + } + + /** + * Get or create genre key. + * + * @return false|int|string + * + * @throws \Exception + */ + protected function getGenreKey(string $genreName): false|int|string + { + $genreassoc = $this->loadGenres(); + + if (\in_array(strtolower($genreName), $genreassoc, false)) { + $genreKey = array_search(strtolower($genreName), $genreassoc, false); + } else { + $genreKey = Genre::query()->insertGetId(['title' => $genreName, 'type' => Genres::CONSOLE_TYPE]); + } + + return $genreKey; + } + + /** + * Load genres from database. + * + * @throws \Exception + */ + protected function loadGenres(): array + { + $gen = new Genres(['Settings' => null]); + + return $gen->loadGenres(Genres::CONSOLE_TYPE); + } +} + diff --git a/app/Services/ConsolesProcessor.php b/app/Services/ConsolesProcessor.php index 7b8d52978..e4e1b99b2 100644 --- a/app/Services/ConsolesProcessor.php +++ b/app/Services/ConsolesProcessor.php @@ -3,7 +3,6 @@ namespace App\Services; use App\Models\Settings; -use Blacklight\Console; class ConsolesProcessor { @@ -17,7 +16,7 @@ class ConsolesProcessor public function process(): void { if ((int) Settings::settingValue('lookupgames') !== 0) { - (new Console)->processConsoleReleases(); + (new ConsoleService)->processConsoleReleases(); } } } diff --git a/misc/testing/PostProc/getConsole.php b/misc/testing/PostProc/getConsole.php index 9d9e865ac..943a9b639 100644 --- a/misc/testing/PostProc/getConsole.php +++ b/misc/testing/PostProc/getConsole.php @@ -5,12 +5,12 @@ require_once dirname(__DIR__, 3).DIRECTORY_SEPARATOR.'bootstrap/autoload.php'; use App\Models\Category; +use App\Services\ConsoleService; use Blacklight\ColorCLI; -use Blacklight\Console; use Illuminate\Support\Facades\DB; $pdo = DB::connection()->getPdo(); -$console = new Console(['Echo' => true]); +$console = new ConsoleService; $colorCli = new ColorCLI; $res = $pdo->query(