Update browse and API queries

This commit is contained in:
DariusIII
2026-02-11 23:14:34 +01:00
parent 1da018627e
commit 865a151eae
14 changed files with 641 additions and 710 deletions
+91 -73
View File
@@ -178,11 +178,10 @@ class GamesService
*
* @param array<string, mixed> $excludedCats
* @param array<string, mixed> $orderBy
* @return array<string, mixed>
*
* @throws \Exception
*/
public function getGamesRange(mixed $page, mixed $cat, mixed $start, mixed $num, array|string $orderBy = '', string $maxAge = '', array $excludedCats = []): array
public function getGamesRange(mixed $page, mixed $cat, mixed $start, mixed $num, array|string $orderBy = '', string $maxAge = '', array $excludedCats = []): mixed
{
$page = max(1, $page);
$start = max(0, $start);
@@ -192,91 +191,110 @@ class GamesService
if (count($cat) > 0 && $cat[0] !== -1) {
$catsrch = Category::getCategorySearch($cat);
}
$whereAge = '';
if ($maxAge > 0) {
$maxAge = sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge);
$whereAge = sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge);
}
$exccatlist = '';
if (count($excludedCats) > 0) {
$exccatlist = ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')';
}
$order = $this->getGamesOrder($orderBy);
$gamesSql =
"SELECT SQL_CALC_FOUND_ROWS gi.id, GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id FROM gamesinfo gi LEFT JOIN releases r ON gi.id = r.gamesinfo_id WHERE gi.title != '' AND gi.cover = 1 AND r.passwordstatus "
.app(\App\Services\Releases\ReleaseBrowseService::class)->showPasswords().
$browseBy.
$catsrch.
$maxAge.
$exccatlist.
' GROUP BY gi.id ORDER BY '.($order[0]).' '.($order[1]). // @phpstan-ignore offsetAccess.notFound
($start === false ? '' : ' LIMIT '.$num.' OFFSET '.$start);
$expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
$gamesCache = Cache::get(md5($gamesSql.$page));
if ($gamesCache !== null) {
$games = $gamesCache;
} else {
$data = DB::select($gamesSql);
$games = ['total' => DB::select('SELECT FOUND_ROWS() AS total'), 'result' => $data];
Cache::put(md5($gamesSql.$page), $games, $expiresAt);
$showPasswords = app(\App\Services\Releases\ReleaseBrowseService::class)->showPasswords();
$baseWhere = "gi.title != '' AND gi.cover = 1 "
."AND r.passwordstatus {$showPasswords} "
.$browseBy.' '
.$catsrch.' '
.$whereAge
.$exccatlist;
$cacheKey = md5('games_range_'.$baseWhere.$order[0].$order[1].$start.$num.$page); // @phpstan-ignore offsetAccess.notFound
$cached = Cache::get($cacheKey);
if ($cached !== null) {
return $cached;
}
$gameIDs = $releaseIDs = [];
if (is_array($games['result'])) {
foreach ($games['result'] as $game => $id) {
$gameIDs[] = $id->id;
$releaseIDs[] = $id->grp_release_id;
}
// Step 1: Count total distinct games matching filters
$countSql = 'SELECT COUNT(DISTINCT gi.id) AS total '
.'FROM gamesinfo gi '
.'INNER JOIN releases r ON gi.id = r.gamesinfo_id '
.'WHERE '.$baseWhere;
$totalResult = DB::select($countSql);
$totalCount = $totalResult[0]->total ?? 0;
if ($totalCount === 0) {
return collect();
}
$returnSql = sprintf(
"SELECT
GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id,
GROUP_CONCAT(r.rarinnerfilecount ORDER BY r.postdate DESC SEPARATOR ',') AS grp_rarinnerfilecount,
GROUP_CONCAT(r.haspreview ORDER BY r.postdate DESC SEPARATOR ',') AS grp_haspreview,
GROUP_CONCAT(r.passwordstatus ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_password,
GROUP_CONCAT(r.guid ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_guid,
GROUP_CONCAT(rn.releases_id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_nfoid,
GROUP_CONCAT(g.name ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_grpname,
GROUP_CONCAT(r.searchname ORDER BY r.postdate DESC SEPARATOR '#') AS grp_release_name,
GROUP_CONCAT(r.postdate ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_postdate,
GROUP_CONCAT(r.adddate ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_adddate,
GROUP_CONCAT(r.size ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_size,
GROUP_CONCAT(r.totalpart ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_totalparts,
GROUP_CONCAT(r.comments ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_comments,
GROUP_CONCAT(r.grabs ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_grabs,
GROUP_CONCAT(df.failed ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_failed,
GROUP_CONCAT(cp.title, ' > ', c.title ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_catname,
gi.*, YEAR(gi.releasedate) as year, r.gamesinfo_id, rn.releases_id AS nfoid, g.name AS group_name
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
LEFT OUTER JOIN categories c ON c.id = r.categories_id
LEFT OUTER JOIN root_categories cp ON cp.id = c.root_categories_id
INNER JOIN gamesinfo gi ON gi.id = r.gamesinfo_id
WHERE gi.id IN (%s)
AND r.id IN (%s)
%s
GROUP BY gi.id
ORDER BY %s %s",
(! empty($gameIDs) ? implode(',', $gameIDs) : -1),
(! empty($releaseIDs) ? implode(',', $releaseIDs) : -1),
$catsrch,
$order[0], // @phpstan-ignore offsetAccess.notFound
$order[1] // @phpstan-ignore offsetAccess.notFound
);
// Step 2: Get paginated games entity list with only needed columns
$gamesSql = 'SELECT gi.id, gi.title, gi.cover, gi.publisher, gi.releasedate, gi.review, gi.url, '
.'YEAR(gi.releasedate) AS year, '
.'MAX(r.postdate) AS latest_postdate, '
.'COUNT(r.id) AS total_releases '
.'FROM gamesinfo gi '
.'INNER JOIN releases r ON gi.id = r.gamesinfo_id '
.'WHERE '.$baseWhere.' '
.'GROUP BY gi.id, gi.title, gi.cover, gi.publisher, gi.releasedate, gi.review, gi.url '
."ORDER BY {$order[0]} {$order[1]} " // @phpstan-ignore offsetAccess.notFound
."LIMIT {$num} OFFSET {$start}";
$return = Cache::get(md5($returnSql.$page));
if ($return !== null) {
return $return;
}
$return = DB::select($returnSql);
if (count($return) > 0) {
$return[0]->_totalcount = $games['total'][0]->total ?? 0;
}
Cache::put(md5($returnSql.$page), $return, $expiresAt);
$games = GamesInfo::fromQuery($gamesSql);
return $return;
if ($games->isEmpty()) {
return collect();
}
// Build list of game IDs for release query
$gameIds = $games->pluck('id')->toArray();
$inGameIds = implode(',', array_map('intval', $gameIds));
// Step 3: Get top 2 releases per game using ROW_NUMBER()
$releasesSql = 'SELECT ranked.id, ranked.gamesinfo_id, ranked.guid, ranked.searchname, '
.'ranked.size, ranked.postdate, ranked.adddate, ranked.haspreview, ranked.grabs, '
.'ranked.comments, ranked.totalpart, ranked.group_name, ranked.nfoid, ranked.failed_count '
.'FROM ( '
.'SELECT r.id, r.gamesinfo_id, r.guid, r.searchname, r.size, r.postdate, r.adddate, '
.'r.haspreview, r.grabs, r.comments, r.totalpart, g.name AS group_name, '
.'rn.releases_id AS nfoid, df.failed AS failed_count, '
.'ROW_NUMBER() OVER (PARTITION BY r.gamesinfo_id ORDER BY r.postdate DESC) AS rn '
.'FROM releases r '
.'LEFT JOIN usenet_groups g ON g.id = r.groups_id '
.'LEFT JOIN release_nfos rn ON rn.releases_id = r.id '
.'LEFT JOIN dnzb_failures df ON df.release_id = r.id '
."WHERE r.gamesinfo_id IN ({$inGameIds}) "
."AND r.passwordstatus {$showPasswords} "
.$catsrch.' '
.$whereAge
.$exccatlist
.') ranked '
.'WHERE ranked.rn <= 2 '
.'ORDER BY ranked.gamesinfo_id, ranked.postdate DESC';
$releases = DB::select($releasesSql);
// Group releases by gamesinfo_id for fast lookup
$releasesByGame = [];
foreach ($releases as $release) {
$releasesByGame[$release->gamesinfo_id][] = $release;
}
// Attach releases to each game entity
foreach ($games as $game) {
$game->releases = $releasesByGame[$game->id] ?? []; // @phpstan-ignore assign.propertyReadOnly
}
// Set total count on first item
if ($games->isNotEmpty()) {
$games[0]->_totalcount = $totalCount; // @phpstan-ignore property.notFound
}
Cache::put($cacheKey, $games, $expiresAt);
return $games;
}
/**