Update imdbid handling

This commit is contained in:
DariusIII
2026-02-15 08:48:51 +01:00
parent 623cb24820
commit dd5af00a9c
7 changed files with 37 additions and 25 deletions
+1 -1
View File
@@ -453,7 +453,7 @@ if (! function_exists('getReleaseCover')) {
if (! empty($imdbid) && $imdbid > 0) {
$coverType = 'movies';
$coverId = str_pad($imdbid, 7, '0', STR_PAD_LEFT);
$coverId = str_pad($imdbid, 8, '0', STR_PAD_LEFT);
} elseif (! empty($musicinfo_id)) {
$coverType = 'music';
$coverId = $musicinfo_id;
+28 -16
View File
@@ -82,11 +82,11 @@ class MovieBrowseService
$totalCount = Cache::get($countCacheKey);
if ($totalCount === null) {
// Use LPAD to convert movieinfo.imdbid (int) to the zero-padded varchar format
// stored in releases.imdbid, so the index on r.imdbid can be used for the join.
// Normalize both sides so we match whether imdbid is stored with or without
// leading zeros (e.g. "0099348" vs "99348") in movieinfo or releases.
$countSql = 'SELECT COUNT(DISTINCT m.imdbid) AS total '
.'FROM movieinfo m '
.'INNER JOIN releases r ON r.imdbid = LPAD(m.imdbid, 7, \'0\') '
.'INNER JOIN releases r ON LPAD(TRIM(r.imdbid), 8, \'0\') = LPAD(TRIM(m.imdbid), 8, \'0\') '
.'WHERE '.$baseWhere;
$totalResult = DB::select($countSql);
@@ -122,7 +122,7 @@ class MovieBrowseService
.'FROM ('
.'SELECT m.imdbid, MAX(r.postdate) AS latest_postdate, COUNT(r.id) AS total_releases '
.'FROM movieinfo m '
.'INNER JOIN releases r ON r.imdbid = LPAD(m.imdbid, 7, \'0\') '
.'INNER JOIN releases r ON LPAD(TRIM(r.imdbid), 8, \'0\') = LPAD(TRIM(m.imdbid), 8, \'0\') '
.'WHERE '.$baseWhere.' '
.'GROUP BY m.imdbid'.$innerExtraGroupBy.' '
."ORDER BY {$innerOrderBy} {$order[1]} " // @phpstan-ignore offsetAccess.notFound
@@ -141,18 +141,16 @@ class MovieBrowseService
$movieImdbIds = $movies->pluck('imdbid')->toArray();
// Step 3: Get top 2 releases per movie using UNION ALL with LIMIT 2 per imdbid.
// Each subquery does an index lookup on imdbid and stops after 2 rows,
// avoiding the full-table-scan + ROW_NUMBER() materialization of millions of rows.
// IMDB IDs are quoted as strings with leading-zero padding to match the
// varchar format stored in releases.imdbid (e.g. '0099348'), preventing
// implicit type casts that would bypass index usage.
// Match releases by normalized 8-char imdbid so we find them whether stored
// with or without leading zeros (IMDb IDs are 78 digits).
$unionParts = [];
foreach ($movieImdbIds as $id) {
$quotedId = "'".str_pad((string) intval($id), 7, '0', STR_PAD_LEFT)."'";
$paddedId = str_pad((string) (int) $id, 8, '0', STR_PAD_LEFT);
$quotedId = "'".$paddedId."'";
$unionParts[] = '(SELECT r.id, r.imdbid, r.guid, r.searchname, '
.'r.size, r.postdate, r.adddate, r.haspreview '
.'FROM releases r '
."WHERE r.imdbid = {$quotedId} "
.'WHERE LPAD(TRIM(r.imdbid), 8, \'0\') = '.$quotedId.' '
."AND r.passwordstatus {$this->showPasswords} "
.$catFilter
.$whereExcluded
@@ -163,15 +161,17 @@ class MovieBrowseService
$releasesSql = implode(' UNION ALL ', $unionParts);
$releases = DB::select($releasesSql);
// Group releases by imdbid for fast lookup
// Group releases by normalized imdbid so lookup works regardless of stored format
$releasesByMovie = [];
foreach ($releases as $release) {
$releasesByMovie[$release->imdbid][] = $release;
$normId = str_pad((string) (int) $release->imdbid, 8, '0', STR_PAD_LEFT);
$releasesByMovie[$normId][] = $release;
}
// Attach releases to each movie object
foreach ($movies as $movie) {
$movie->releases = $releasesByMovie[$movie->imdbid] ?? []; // @phpstan-ignore assign.propertyReadOnly
$normMovieId = str_pad((string) (int) $movie->imdbid, 8, '0', STR_PAD_LEFT);
$movie->releases = $releasesByMovie[$normMovieId] ?? []; // @phpstan-ignore assign.propertyReadOnly
}
// Set total count on first item (matches existing pattern used by controllers)
@@ -193,10 +193,11 @@ class MovieBrowseService
public function getMovieReleases(string $imdbid, array $excludedCats = []): array
{
$whereExcluded = count($excludedCats) > 0 ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : '';
$paddedId = "'".str_pad((string) (int) $imdbid, 8, '0', STR_PAD_LEFT)."'";
$sql = 'SELECT r.id, r.guid, r.searchname, r.size, r.postdate, r.adddate, r.haspreview '
.'FROM releases r '
."WHERE r.imdbid = '".str_pad((string) intval($imdbid), 7, '0', STR_PAD_LEFT)."' "
.'WHERE LPAD(TRIM(r.imdbid), 8, \'0\') = '.$paddedId.' '
."AND r.passwordstatus {$this->showPasswords} "
.$whereExcluded.' '
.'ORDER BY r.postdate DESC';
@@ -238,7 +239,18 @@ class MovieBrowseService
$browseByArr = ['title', 'director', 'actors', 'genre', 'rating', 'year', 'imdb'];
foreach ($browseByArr as $bb) {
if (request()->has($bb) && ! empty(request()->input($bb))) {
$bbv = stripslashes(request()->input($bb));
$bbv = request()->input($bb);
if (is_array($bbv)) {
continue;
}
$bbv = stripslashes((string) $bbv);
if ($bb === 'year') {
if (preg_match('/^(19|20)\d{2}$/', $bbv)) {
$browseBy .= ' AND m.year = '.escapeString($bbv);
}
continue;
}
if ($bb === 'rating') {
$bbv .= '.';
}
+3 -3
View File
@@ -356,7 +356,7 @@ class TmdbClient
if (! empty($imdbId)) {
// Format IMDB ID with tt prefix if it's numeric
$imdbFormatted = is_numeric($imdbId)
? 'tt'.str_pad((string) $imdbId, 7, '0', STR_PAD_LEFT)
? 'tt'.str_pad((string) $imdbId, 8, '0', STR_PAD_LEFT)
: (string) $imdbId;
$show = $this->findTvByExternalId($imdbFormatted, 'imdb_id');
@@ -396,7 +396,7 @@ class TmdbClient
$show = $this->findTvByExternalId((string) $id, 'tvdb_id');
} elseif ($source === 'imdb') {
$imdbFormatted = is_numeric($id)
? 'tt'.str_pad((string) $id, 7, '0', STR_PAD_LEFT)
? 'tt'.str_pad((string) $id, 8, '0', STR_PAD_LEFT)
: (string) $id;
$show = $this->findTvByExternalId($imdbFormatted, 'imdb_id');
}
@@ -417,7 +417,7 @@ class TmdbClient
// Parse IMDB ID to numeric
$imdbId = 0;
if (! empty($externalIds['imdb_id'])) {
preg_match('/tt(?P<imdbid>\d{6,7})$/i', $externalIds['imdb_id'], $imdb);
preg_match('/tt(?P<imdbid>\d{6,8})$/i', $externalIds['imdb_id'], $imdb);
$imdbId = (int) ($imdb['imdbid'] ?? 0);
}
+1 -1
View File
@@ -233,7 +233,7 @@ class TraktService
}
return match ($idType) {
'imdb' => is_numeric($id) ? 'tt'.str_pad((string) $id, 7, '0', STR_PAD_LEFT) : (string) $id,
'imdb' => is_numeric($id) ? 'tt'.str_pad((string) $id, 8, '0', STR_PAD_LEFT) : (string) $id,
'trakt', 'tmdb', 'tvdb' => (string) $id,
};
}
@@ -494,7 +494,7 @@ class TmdbProvider extends AbstractTvProvider
$imdbId = 0;
$externalIds = TmdbClient::getArray($show, 'external_ids');
if (! empty($externalIds['imdb_id'])) {
preg_match('/tt(?P<imdbid>\d{6,7})$/i', $externalIds['imdb_id'], $imdb);
preg_match('/tt(?P<imdbid>\d{6,8})$/i', $externalIds['imdb_id'], $imdb);
$imdbId = $imdb['imdbid'] ?? 0;
}
@@ -383,7 +383,7 @@ class TraktProvider extends AbstractTvProvider
*/
public function formatShowInfo(mixed $show): array
{
preg_match('/tt(?P<imdbid>\d{6,7})$/i', $show['ids']['imdb'], $imdb);
preg_match('/tt(?P<imdbid>\d{6,8})$/i', $show['ids']['imdb'], $imdb);
$this->posterUrl = $show['images']['poster']['thumb'] ?? '';
$this->fanartUrl = $show['images']['fanart']['thumb'] ?? '';
$this->localizedTZ = $show['airs']['timezone'] ?? '';
@@ -435,7 +435,7 @@ class TraktProvider extends AbstractTvProvider
// Try IMDB ID as fallback
if (! empty($imdbId) && $imdbId > 0) {
$imdbFormatted = 'tt'.str_pad((string) $imdbId, 7, '0', STR_PAD_LEFT);
$imdbFormatted = 'tt'.str_pad((string) $imdbId, 8, '0', STR_PAD_LEFT);
$result = $tvmazeClient->getShowBySiteID('imdb', $imdbFormatted);
if ($result !== null && isset($result->id)) {
return (int) $result->id;
+1 -1
View File
@@ -176,7 +176,7 @@
@if(!empty($show['imdb']) && $show['imdb'] > 0)
<a class="px-4 py-2 bg-yellow-100 text-yellow-800 rounded-lg hover:bg-yellow-200 inline-flex items-center text-sm font-medium transition"
target="_blank"
href="{{ $site['dereferrer_link'] }}https://www.imdb.com/title/tt{{ str_pad($show['imdb'], 7, '0', STR_PAD_LEFT) }}"
href="{{ $site['dereferrer_link'] }}https://www.imdb.com/title/tt{{ str_pad($show['imdb'], 8, '0', STR_PAD_LEFT) }}"
title="View at IMDb">
<i class="fa fa-film mr-2"></i> IMDb
</a>