Make imdbid string everywhere

This commit is contained in:
DariusIII
2026-04-09 17:16:17 +02:00
parent 4995988d18
commit 1e8e607148
37 changed files with 223 additions and 189 deletions
@@ -76,7 +76,7 @@ class CreateManticoreIndexes extends Command
'filename' => ['type' => 'text'],
'categories_id' => ['type' => 'integer'],
// External media IDs for efficient searching
'imdbid' => ['type' => 'integer'],
'imdbid' => ['type' => 'string'],
'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'],
'tvdb' => ['type' => 'integer'],
@@ -103,7 +103,7 @@ class CreateManticoreIndexes extends Command
'min_infix_len' => 2,
],
'columns' => [
'imdbid' => ['type' => 'integer'],
'imdbid' => ['type' => 'string'],
'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'],
'title' => ['type' => 'text'],
@@ -126,7 +126,7 @@ class CreateManticoreIndexes extends Command
'trakt' => ['type' => 'integer'],
'tvmaze' => ['type' => 'integer'],
'tvrage' => ['type' => 'integer'],
'imdb' => ['type' => 'integer'],
'imdb' => ['type' => 'string'],
'tmdb' => ['type' => 'integer'],
'started' => ['type' => 'text'],
'type' => ['type' => 'integer'],
+4 -4
View File
@@ -81,7 +81,7 @@ class CreateMediaIndexes extends Command
'min_infix_len' => 2,
],
'columns' => [
'imdbid' => ['type' => 'integer'],
'imdbid' => ['type' => 'string'],
'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'],
'title' => ['type' => 'text'],
@@ -104,7 +104,7 @@ class CreateMediaIndexes extends Command
'trakt' => ['type' => 'integer'],
'tvmaze' => ['type' => 'integer'],
'tvrage' => ['type' => 'integer'],
'imdb' => ['type' => 'integer'],
'imdb' => ['type' => 'string'],
'tmdb' => ['type' => 'integer'],
'started' => ['type' => 'text'],
'type' => ['type' => 'integer'],
@@ -228,7 +228,7 @@ class CreateMediaIndexes extends Command
$moviesMapping = [
'mappings' => [
'properties' => [
'imdbid' => ['type' => 'integer'],
'imdbid' => ['type' => 'keyword'],
'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'],
'title' => ['type' => 'text', 'analyzer' => 'standard'],
@@ -255,7 +255,7 @@ class CreateMediaIndexes extends Command
'trakt' => ['type' => 'integer'],
'tvmaze' => ['type' => 'integer'],
'tvrage' => ['type' => 'integer'],
'imdb' => ['type' => 'integer'],
'imdb' => ['type' => 'keyword'],
'tmdb' => ['type' => 'integer'],
'started' => ['type' => 'keyword'],
'type' => ['type' => 'integer'],
@@ -202,7 +202,7 @@ class NntmuxPopulateSearchIndexes extends Command
'filename' => '',
'videos_id' => (int) ($item->videos_id ?? 0),
'movieinfo_id' => (int) ($item->movieinfo_id ?? 0),
'imdbid' => (int) ($item->imdbid ?? 0),
'imdbid' => (string) ($item->imdbid ?? ''),
'tmdbid' => 0,
'traktid' => 0,
'tvdb' => 0,
@@ -287,7 +287,7 @@ class NntmuxPopulateSearchIndexes extends Command
function ($item) {
return [
'id' => $item->id,
'imdbid' => (int) ($item->imdbid ?? 0),
'imdbid' => (string) ($item->imdbid ?? ''),
'tmdbid' => (int) ($item->tmdbid ?? 0),
'traktid' => (int) ($item->traktid ?? 0),
'title' => (string) ($item->title ?? ''),
@@ -345,7 +345,7 @@ class NntmuxPopulateSearchIndexes extends Command
'trakt' => (int) ($item->trakt ?? 0),
'tvmaze' => (int) ($item->tvmaze ?? 0),
'tvrage' => (int) ($item->tvrage ?? 0),
'imdb' => (int) ($item->imdb ?? 0),
'imdb' => (string) ($item->imdb ?? ''),
'tmdb' => (int) ($item->tmdb ?? 0),
'started' => (string) ($item->started ?? ''),
'type' => (int) ($item->type ?? 0),
+2 -2
View File
@@ -194,7 +194,7 @@ class NntmuxResetDb extends Command
'type' => 'long',
'index' => false,
],
'imdbid' => ['type' => 'integer'],
'imdbid' => ['type' => 'keyword'],
'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'],
'title' => [
@@ -247,7 +247,7 @@ class NntmuxResetDb extends Command
'trakt' => ['type' => 'integer'],
'tvmaze' => ['type' => 'integer'],
'tvrage' => ['type' => 'integer'],
'imdb' => ['type' => 'integer'],
'imdb' => ['type' => 'keyword'],
'tmdb' => ['type' => 'integer'],
'started' => ['type' => 'text'],
'type' => ['type' => 'integer'],
@@ -427,7 +427,7 @@ class UpdateReleasesIndexSchema extends Command
private function prepareMediaData(mixed $release): array
{
return [
'imdbid' => (int) ($release->imdbid ?: 0),
'imdbid' => (string) ($release->imdbid ?: ''),
'tmdbid' => (int) ($release->tmdbid ?: ($release->video_tmdb ?: 0)),
'traktid' => (int) ($release->traktid ?: ($release->video_trakt ?: 0)),
'tvdb' => (int) ($release->tvdb ?: 0),
@@ -42,7 +42,7 @@ class UpdateReleasesIndexSchemaES extends Command
* @var array<string, mixed>
*/
private array $mediaFields = [
'imdbid' => ['type' => 'integer'],
'imdbid' => ['type' => 'keyword'],
'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'],
'tvdb' => ['type' => 'integer'],
@@ -384,7 +384,7 @@ class UpdateReleasesIndexSchemaES extends Command
'format' => 'yyyy-MM-dd HH:mm:ss',
],
// New media-related fields
'imdbid' => ['type' => 'integer'],
'imdbid' => ['type' => 'keyword'],
'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'],
'tvdb' => ['type' => 'integer'],
@@ -597,7 +597,7 @@ class UpdateReleasesIndexSchemaES extends Command
private function prepareMediaData(mixed $release): array
{
return [
'imdbid' => (int) ($release->imdbid ?: 0),
'imdbid' => (string) ($release->imdbid ?: ''),
'tmdbid' => (int) ($release->tmdbid ?: ($release->video_tmdb ?: 0)),
'traktid' => (int) ($release->traktid ?: ($release->video_trakt ?: 0)),
'tvdb' => (int) ($release->tvdb ?: 0),
+20 -3
View File
@@ -217,10 +217,27 @@ if (! function_exists('runCmd')) {
}
}
if (! function_exists('imdb_id_is_valid')) {
/**
* Check whether an IMDB ID string is a valid, non-zero numeric identifier.
* IMDB IDs are stored as plain digit strings (e.g. "0137523", "1375666", "14688458")
* exactly as provided by upstream sources no padding, no trimming.
*/
function imdb_id_is_valid(int|string|null $id): bool
{
if ($id === null || $id === '' || $id === 0) {
return false;
}
$s = preg_replace('/\D/', '', trim((string) $id));
return $s !== '' && $s !== '0' && ((int) $s) !== 0;
}
}
if (! function_exists('imdb_id_pad')) {
/**
* Normalize IMDB ID to 8 zero-padded digits for storage in releases / join consistency.
* Enables index use on releases.imdbid; call when writing to releases.imdbid.
* @deprecated Use imdb_id_is_valid() instead. This function exists only for backward
* compatibility during the transition period and will be removed.
*/
function imdb_id_pad(int|string|null $id): string
{
@@ -467,7 +484,7 @@ if (! function_exists('getReleaseCover')) {
$gamesinfo_id = $getValue($release, 'gamesinfo_id');
$anidbid = $getValue($release, 'anidbid');
if (! empty($imdbid) && $imdbid > 0) {
if (! empty($imdbid) && imdb_id_is_valid($imdbid)) {
$coverType = 'movies';
$coverId = (string) $imdbid;
} elseif (! empty($videos_id) && $videos_id > 0) {
+3 -1
View File
@@ -260,7 +260,9 @@ class ApiController extends BasePageController
$maxAge = $this->maxAge($request);
UserRequest::addApiRequest($uid, $request->getRequestUri());
$imdbId = $request->has('imdbid') && $request->filled('imdbid') ? (int) $request->input('imdbid') : -1;
$imdbId = $request->has('imdbid') && $request->filled('imdbid')
? (string) Str::replace('tt', '', (string) $request->input('imdbid'))
: '';
$tmdbId = $request->has('tmdbid') && $request->filled('tmdbid') ? (int) $request->input('tmdbid') : -1;
$traktId = $request->has('traktid') && $request->filled('traktid') ? (int) $request->input('traktid') : -1;
+2 -1
View File
@@ -24,6 +24,7 @@ use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class ApiV2Controller extends BasePageController
{
@@ -133,7 +134,7 @@ class ApiV2Controller extends BasePageController
event(new UserAccessedApi($user, $request->ip()));
// Get request parameters efficiently
$imdbId = (int) $request->input('imdbid', -1);
$imdbId = (string) Str::replace('tt', '', (string) $request->input('imdbid', ''));
$tmdbId = (int) $request->input('tmdbid', -1);
$traktId = (int) $request->input('traktid', -1);
$minSize = max(0, (int) $request->input('minsize', 0));
+2 -1
View File
@@ -173,7 +173,8 @@ class RSS extends ApiController
DB::raw('COUNT(DISTINCT ud.id) as total_downloads'),
])
->where('m.title', '!=', '')
->where('m.imdbid', '!=', '0000000')
->whereNotNull('m.imdbid')
->where('m.imdbid', '!=', '')
->where('ud.timestamp', '>=', $fortyEightHoursAgo)
->groupBy('m.imdbid')
->havingRaw('COUNT(DISTINCT ud.id) > 0')
+4 -4
View File
@@ -240,7 +240,7 @@ class XML_Response
$attrs = array_merge($attrs, $this->buildTvAttrArray());
}
if (isset($this->release->imdbid) && $this->release->imdbid > 0) {
if (isset($this->release->imdbid) && imdb_id_is_valid($this->release->imdbid)) {
$attrs['imdb'] = $this->release->imdbid;
}
if (isset($this->release->anidbid) && $this->release->anidbid > 0) {
@@ -302,7 +302,7 @@ class XML_Response
if (isset($this->release->tvmaze) && $this->release->tvmaze > 0) {
$attrs['tvmazeid'] = $this->release->tvmaze;
}
if (isset($this->release->imdb) && $this->release->imdb > 0) {
if (isset($this->release->imdb) && imdb_id_is_valid($this->release->imdb)) {
$attrs['imdbid'] = $this->release->imdb;
}
if (isset($this->release->tmdb) && $this->release->tmdb > 0) {
@@ -645,7 +645,7 @@ class XML_Response
$this->setTvAttr();
}
if (isset($this->release->imdbid) && $this->release->imdbid > 0) {
if (isset($this->release->imdbid) && imdb_id_is_valid($this->release->imdbid)) {
$this->writeZedAttr('imdb', $this->release->imdbid);
}
if (isset($this->release->anidbid) && $this->release->anidbid > 0) {
@@ -706,7 +706,7 @@ class XML_Response
if (isset($this->release->tvmaze) && $this->release->tvmaze > 0) {
$this->writeZedAttr('tvmazeid', $this->release->tvmaze);
}
if (isset($this->release->imdb) && $this->release->imdb > 0) {
if (isset($this->release->imdb) && imdb_id_is_valid($this->release->imdb)) {
$this->writeZedAttr('imdbid', $this->release->imdb);
}
if (isset($this->release->tmdb) && $this->release->tmdb > 0) {
+1 -1
View File
@@ -85,7 +85,7 @@ class DetailsController extends BasePageController
}
$mov = '';
if (imdb_id_pad($data['imdbid']) !== '00000000') {
if (imdb_id_is_valid($data['imdbid'])) {
$mov = $this->movieService->getMovieInfo($data['imdbid']);
if (! empty($mov['title'])) {
$mov['title'] = str_replace(['/', '\\'], '', $mov['title']);
+1 -1
View File
@@ -305,7 +305,7 @@ class GetNzbController extends BasePageController
];
// Add optional metadata headers
if (! empty($releaseData->imdbid) && $releaseData->imdbid > 0) {
if (! empty($releaseData->imdbid) && imdb_id_is_valid($releaseData->imdbid)) {
$headers['X-DNZB-MoreInfo'] = "http://www.imdb.com/title/tt{$releaseData->imdbid}";
} elseif (! empty($releaseData->tvdb) && $releaseData->tvdb > 0) {
$headers['X-DNZB-MoreInfo'] = "http://www.thetvdb.com/?tab=series&id={$releaseData->tvdb}";
+2 -1
View File
@@ -229,7 +229,8 @@ class MovieController extends BasePageController
DB::raw('COUNT(DISTINCT r.id) as release_count'),
])
->where('m.title', '!=', '')
->where('m.imdbid', '!=', '0000000')
->whereNotNull('m.imdbid')
->where('m.imdbid', '!=', '')
->where('ud.timestamp', '>=', $fortyEightHoursAgo)
->groupBy('m.imdbid', 'm.title', 'm.year', 'm.rating', 'm.plot', 'm.genre', 'm.cover', 'm.tmdbid', 'm.traktid')
->havingRaw('COUNT(DISTINCT ud.id) > 0')
+1 -1
View File
@@ -13,7 +13,7 @@ use Illuminate\Support\Facades\Cache;
* App\Models\MovieInfo.
*
* @property int $id
* @property int $imdbid
* @property string $imdbid
* @property int $tmdbid
* @property string $title
* @property string $tagline
+1 -1
View File
@@ -13,7 +13,7 @@ use Illuminate\Database\Eloquent\Model;
*
* @property int $id
* @property int $users_id
* @property int|null $imdbid
* @property string|null $imdbid
* @property string|null $categories List of categories for user movies
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
+1 -1
View File
@@ -20,7 +20,7 @@ use Illuminate\Support\Facades\DB;
* @property string $countries_id Two character country code (FK to countries table).
* @property string $started Date (UTC) of production's first airing.
* @property int $anidb ID number for anidb site
* @property int $imdb ID number for IMDB site (without the 'tt' prefix).
* @property string $imdb ID number for IMDB site (without the 'tt' prefix).
* @property int $tmdb ID number for TMDB site.
* @property int $trakt ID number for TraktTV site.
* @property int $tvdb ID number for TVDB site
+1 -1
View File
@@ -49,7 +49,7 @@ class MovieInfoObserver
try {
Search::insertMovie([
'id' => $movie->id,
'imdbid' => $movie->imdbid ?? 0,
'imdbid' => $movie->imdbid ?? '',
'tmdbid' => $movie->tmdbid ?? 0,
'traktid' => $movie->traktid ?? 0,
'title' => $movie->title ?? '',
+10 -15
View File
@@ -63,7 +63,7 @@ class MovieBrowseService
$whereAge = $maxAge > 0 ? 'AND r.postdate > NOW() - INTERVAL '.$maxAge.' DAY ' : '';
$browseBy = $this->getBrowseBy();
$baseWhere = "m.title != '' AND m.imdbid NOT IN ('0000000', '00000000') "
$baseWhere = "m.title != '' AND m.imdbid IS NOT NULL AND m.imdbid != '' "
."AND r.passwordstatus {$this->showPasswords} "
.$browseBy.' '
.$catFilter
@@ -85,11 +85,9 @@ class MovieBrowseService
$totalCount = Cache::get($countCacheKey);
if ($totalCount === null) {
// releases.imdbid is stored 8-char normalized (imdb_id_pad); only pad movieinfo
// so the index on r.imdbid can be used.
$countSql = 'SELECT COUNT(DISTINCT m.imdbid) AS total '
.'FROM movieinfo m '
.'INNER JOIN releases r ON r.imdbid = LPAD(TRIM(m.imdbid), 8, \'0\') '
.'INNER JOIN releases r ON r.imdbid = m.imdbid '
.'WHERE '.$baseWhere;
$totalResult = DB::select($countSql);
@@ -125,7 +123,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(TRIM(m.imdbid), 8, \'0\') '
.'INNER JOIN releases r ON r.imdbid = m.imdbid '
.'WHERE '.$baseWhere.' '
.'GROUP BY m.imdbid'.$innerExtraGroupBy.' '
."ORDER BY {$innerOrderBy} {$order[1]} "
@@ -144,11 +142,9 @@ class MovieBrowseService
$movieImdbIds = $movies->pluck('imdbid')->toArray();
// Step 3: Get top 2 releases per movie using UNION ALL with LIMIT 2 per imdbid.
// r.imdbid is stored 8-char normalized so index can be used.
$unionParts = [];
foreach ($movieImdbIds as $id) {
$paddedId = str_pad((string) (int) $id, 8, '0', STR_PAD_LEFT);
$quotedId = "'".$paddedId."'";
$quotedId = escapeString((string) $id);
$unionParts[] = '(SELECT r.id, r.imdbid, r.guid, r.searchname, '
.'r.size, r.postdate, r.adddate, r.haspreview '
.'FROM releases r '
@@ -163,16 +159,15 @@ class MovieBrowseService
$releasesSql = implode(' UNION ALL ', $unionParts);
$releases = DB::select($releasesSql);
// Group by imdbid (already 8-char in DB)
// Group by imdbid
$releasesByMovie = [];
foreach ($releases as $release) {
$releasesByMovie[$release->imdbid][] = $release;
}
// Attach releases to each movie (movie imdbid may be 7/8 digit; normalize for lookup)
// Attach releases to each movie
foreach ($movies as $movie) {
$normMovieId = str_pad((string) (int) $movie->imdbid, 8, '0', STR_PAD_LEFT);
$movie->releases = $releasesByMovie[$normMovieId] ?? []; // @phpstan-ignore assign.propertyReadOnly
$movie->releases = $releasesByMovie[$movie->imdbid] ?? []; // @phpstan-ignore assign.propertyReadOnly
}
// Set total count on first item (matches existing pattern used by controllers)
@@ -194,11 +189,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)."'";
$quotedId = escapeString($imdbid);
$sql = 'SELECT r.id, r.guid, r.searchname, r.size, r.postdate, r.adddate, r.haspreview '
.'FROM releases r '
.'WHERE r.imdbid = '.$paddedId.' '
.'WHERE r.imdbid = '.$quotedId.' '
."AND r.passwordstatus {$this->showPasswords} "
.$whereExcluded.' '
.'ORDER BY r.postdate DESC';
@@ -256,7 +251,7 @@ class MovieBrowseService
$bbv .= '.';
}
if ($bb === 'imdb') {
$browseBy .= sprintf(' AND m.imdbid = %d', $bbv);
$browseBy .= ' AND m.imdbid = '.escapeString($bbv);
} else {
$browseBy .= ' AND m.'.$bb.' '.'LIKE '.escapeString('%'.$bbv.'%');
}
+32 -68
View File
@@ -108,19 +108,12 @@ class MovieService
*/
public function getMovieInfo(int|string|null $imdbId): ?MovieInfo
{
$lookupIds = $this->movieInfoLookupIds($imdbId);
if ($lookupIds === []) {
$sanitized = $this->sanitizeImdbId($imdbId);
if ($sanitized === null) {
return null;
}
foreach ($lookupIds as $lookupId) {
$movie = MovieInfo::query()->where('imdbid', $lookupId)->first();
if ($movie !== null) {
return $movie;
}
}
return null;
return MovieInfo::query()->where('imdbid', $sanitized)->first();
}
/**
@@ -131,33 +124,26 @@ class MovieService
*/
public function getTrailer(int|string|null $imdbId): string|false
{
$lookupIds = $this->movieInfoLookupIds($imdbId);
if ($lookupIds === []) {
$sanitized = $this->sanitizeImdbId($imdbId);
if ($sanitized === null) {
return false;
}
foreach ($lookupIds as $lookupId) {
$trailer = MovieInfo::query()->where('imdbid', $lookupId)->where('trailer', '<>', '')->first(['trailer']);
if ($trailer !== null) {
return $trailer['trailer'];
}
}
$canonicalImdbId = $this->canonicalImdbId($imdbId);
if ($canonicalImdbId === null) {
return false;
$trailer = MovieInfo::query()->where('imdbid', $sanitized)->where('trailer', '<>', '')->first(['trailer']);
if ($trailer !== null) {
return $trailer['trailer'];
}
if ($this->traktcheck !== null) {
$data = $this->traktTv->client->getMovieSummary('tt'.$canonicalImdbId, 'full');
$data = $this->traktTv->client->getMovieSummary('tt'.$sanitized, 'full');
if (($data !== false) && ! empty($data['trailer'])) {
return $data['trailer'];
}
}
$trailer = imdb_trailers($canonicalImdbId);
$trailer = imdb_trailers($sanitized);
if ($trailer) {
MovieInfo::query()->whereIn('imdbid', $lookupIds)->update(['trailer' => $trailer]);
MovieInfo::query()->where('imdbid', $sanitized)->update(['trailer' => $trailer]);
return $trailer;
}
@@ -165,41 +151,10 @@ class MovieService
return false;
}
private function movieInfoLookupIds(int|string|null $imdbId): array
{
$canonicalImdbId = $this->canonicalImdbId($imdbId);
if ($canonicalImdbId === null) {
return [];
}
$lookupIds = [$canonicalImdbId];
$rawImdbId = $this->sanitizeImdbId($imdbId);
if ($rawImdbId !== null && ! in_array($rawImdbId, $lookupIds, true)) {
$lookupIds[] = $rawImdbId;
}
$paddedImdbId = imdb_id_pad($canonicalImdbId);
if ($paddedImdbId !== '00000000' && ! in_array($paddedImdbId, $lookupIds, true)) {
$lookupIds[] = $paddedImdbId;
}
return $lookupIds;
}
private function canonicalImdbId(int|string|null $imdbId): ?string
{
$sanitizedImdbId = $this->sanitizeImdbId($imdbId);
if ($sanitizedImdbId === null) {
return null;
}
if (strlen($sanitizedImdbId) === 8 && str_starts_with($sanitizedImdbId, '0')) {
return substr($sanitizedImdbId, 1);
}
return $sanitizedImdbId;
}
/**
* Sanitize an IMDB ID to its raw numeric string, preserving leading zeros.
* Returns null if the ID is empty or all zeros.
*/
private function sanitizeImdbId(int|string|null $imdbId): ?string
{
if ($imdbId === null || $imdbId === '') {
@@ -207,7 +162,7 @@ class MovieService
}
$sanitizedImdbId = preg_replace('/\D/', '', trim((string) $imdbId));
if ($sanitizedImdbId === null || $sanitizedImdbId === '' || imdb_id_pad($sanitizedImdbId) === '00000000') {
if ($sanitizedImdbId === null || $sanitizedImdbId === '' || ! imdb_id_is_valid($sanitizedImdbId)) {
return null;
}
@@ -1013,12 +968,12 @@ class MovieService
public function doMovieUpdate(string $buffer, string $service, int $id, int $processImdb = 1): string|false
{
$existingImdbId = Release::query()->where('id', $id)->value('imdbid');
if ($existingImdbId !== null && $existingImdbId !== '' && $existingImdbId !== '0000000' && $existingImdbId !== '00000000') {
if ($existingImdbId !== null && imdb_id_is_valid($existingImdbId)) {
return $existingImdbId;
}
$imdbId = false;
if (preg_match('/(?:imdb.*?)?(?:tt|Title\?)(?P<imdbid>\d{5,8})/i', $buffer, $hits)) {
if (preg_match('/(?:imdb.*?)?(?:tt|Title\?)(?P<imdbid>\d{5,})/i', $buffer, $hits)) {
$imdbId = $hits['imdbid'];
}
@@ -1032,7 +987,7 @@ class MovieService
$movieInfoId = MovieInfo::query()->where('imdbid', $imdbId)->first(['id']);
Release::query()->where('id', $id)->update([
'imdbid' => imdb_id_pad($imdbId),
'imdbid' => $imdbId,
'movieinfo_id' => $movieInfoId !== null ? $movieInfoId['id'] : null,
]);
@@ -1047,7 +1002,7 @@ class MovieService
$info = $this->updateMovieInfo($imdbId);
if ($info === false) {
Release::query()->where('id', $id)->update(['imdbid' => imdb_id_pad('0000000')]);
Release::query()->where('id', $id)->update(['imdbid' => '']);
} elseif ($info === true) {
$freshMovieInfo = MovieInfo::query()->where('imdbid', $imdbId)->first(['id']);
@@ -1084,7 +1039,10 @@ class MovieService
$query = Release::query()
->select(['searchname', 'id'])
->whereBetween('categories_id', [Category::MOVIE_ROOT, Category::MOVIE_OTHER])
->whereNull('imdbid');
->where(function ($query): void {
$query->whereNull('imdbid')
->orWhereIn('imdbid', ['', '0', '0000000', '00000000']);
});
if ($groupID !== '') {
$query->where('groups_id', $groupID);
@@ -1135,7 +1093,13 @@ class MovieService
continue;
} else {
$releaseCheck = Release::query()->where('id', $arr['id'])->whereNotNull('imdbid')->exists();
$releaseCheck = Release::query()
->where('id', $arr['id'])
->where(function ($query): void {
$query->whereNotNull('imdbid')
->whereNotIn('imdbid', ['', '0', '0000000', '00000000']);
})
->exists();
if ($releaseCheck) {
if ($this->echooutput) {
cli()->info('Release already has IMDB ID, skipping');
@@ -1162,7 +1126,7 @@ class MovieService
}
foreach (array_chunk($failedIDs, 100) as $chunk) {
Release::query()->whereIn('id', $chunk)->update(['imdbid' => imdb_id_pad('0000000')]);
Release::query()->whereIn('id', $chunk)->update(['imdbid' => '']);
}
}
}
+2 -2
View File
@@ -208,7 +208,7 @@ class NfoService
}
// IMDB (movies and TV shows)
if (preg_match('/imdb\.com\/title\/(tt\d{7,8})/i', $str, $hits)) {
if (preg_match('/imdb\.com\/title\/(tt\d{7,})/i', $str, $hits)) {
return ['showid' => trim($hits[1]), 'site' => 'imdb'];
}
@@ -1174,7 +1174,7 @@ class NfoService
$ids = [];
// IMDB
if (preg_match_all('/imdb\.com\/title\/(tt\d{7,8})/i', $nfoContent, $matches)) {
if (preg_match_all('/imdb\.com\/title\/(tt\d{7,})/i', $nfoContent, $matches)) {
foreach ($matches[1] as $id) {
$ids[] = ['id' => $id, 'source' => 'imdb'];
}
+1 -1
View File
@@ -841,7 +841,7 @@ class ReleaseRemoverService
FROM releases r
LEFT JOIN release_files rf ON r.id = rf.releases_id
WHERE r.categories_id IN ({$categories})
AND (r.imdbid NOT IN ('0000000', 0) OR r.categories_id BETWEEN ".Category::XXX_ROOT.' AND '.Category::XXX_OTHER.")
AND ((r.imdbid IS NOT NULL AND r.imdbid NOT IN ('', '0')) OR r.categories_id BETWEEN ".Category::XXX_ROOT.' AND '.Category::XXX_OTHER.")
AND r.nfostatus = 1
AND r.haspreview = 0
AND r.jpgstatus = 0
+16 -8
View File
@@ -297,7 +297,11 @@ class ReleaseSearchService
$externalIds = [];
if (! empty($siteIdArr)) {
foreach ($siteIdArr as $column => $id) {
if ($id > 0 && $column !== 'id') {
$hasValue = $column === 'imdb'
? ($id !== null && $id !== '' && imdb_id_is_valid((string) $id))
: ($id > 0);
if ($hasValue && $column !== 'id') {
// Map column names to search index field names
$fieldName = match ($column) {
'tvdb' => 'tvdb',
@@ -309,7 +313,7 @@ class ReleaseSearchService
default => null,
};
if ($fieldName) {
$externalIds[$fieldName] = (int) $id;
$externalIds[$fieldName] = $column === 'imdb' ? (string) $id : (int) $id;
}
}
}
@@ -548,7 +552,11 @@ class ReleaseSearchService
// OPTIMIZATION: Try to find releases using search index external IDs first
$externalIds = [];
foreach ($siteIdArr as $column => $Id) {
if ($Id > 0 && $column !== 'id') {
$hasValue = $column === 'imdb'
? ($Id !== null && $Id !== '' && imdb_id_is_valid((string) $Id))
: ($Id > 0);
if ($hasValue && $column !== 'id') {
$fieldName = match ($column) {
'tvdb' => 'tvdb',
'trakt' => 'traktid',
@@ -559,7 +567,7 @@ class ReleaseSearchService
default => null,
};
if ($fieldName) {
$externalIds[$fieldName] = (int) $Id;
$externalIds[$fieldName] = $column === 'imdb' ? (string) $Id : (int) $Id;
}
}
}
@@ -759,7 +767,7 @@ class ReleaseSearchService
* @param array<string, mixed> $excludedCategories
* @return Collection|mixed
*/
public function moviesSearch(int $imDbId = -1, int $tmDbId = -1, int $traktId = -1, int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed
public function moviesSearch(string $imDbId = '', int $tmDbId = -1, int $traktId = -1, int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed
{
$searchLimit = $this->determineSearchCandidateLimit($offset, $limit);
$searchResult = [];
@@ -767,7 +775,7 @@ class ReleaseSearchService
// OPTIMIZATION: If we have external IDs, use the search index to find releases directly
// This avoids expensive database JOINs by using indexed external ID fields in releases_rt
$externalIds = [];
if ($imDbId !== -1 && $imDbId > 0) {
if ($imDbId !== '' && imdb_id_is_valid($imDbId)) {
$externalIds['imdbid'] = $imDbId;
}
if ($tmDbId !== -1 && $tmDbId > 0) {
@@ -822,8 +830,8 @@ class ReleaseSearchService
$needsMovieJoin = false;
if (empty($searchResult) && ! empty($externalIds)) {
$needsMovieJoin = true;
if ($imDbId !== -1 && $imDbId > 0) {
$conditions[] = sprintf('r.imdbid = %d', $imDbId);
if ($imDbId !== '' && imdb_id_is_valid($imDbId)) {
$conditions[] = sprintf('r.imdbid = %s', escapeString($imDbId));
}
if ($tmDbId !== -1 && $tmDbId > 0) {
$conditions[] = sprintf('m.tmdbid = %d', $tmDbId);
+2 -2
View File
@@ -131,7 +131,7 @@ class PostProcessRunner extends BaseRunner
SELECT id
FROM releases
WHERE categories_id BETWEEN 2000 AND 2999
AND imdbid IS NULL
AND (imdbid IS NULL OR imdbid IN (\'\', \'0\', \'0000000\', \'00000000\'))
'.$condLookup.' '.$condRenamedOnly.'
LIMIT 1';
if (count(DB::select($checkSql)) === 0) {
@@ -145,7 +145,7 @@ class PostProcessRunner extends BaseRunner
SELECT DISTINCT LEFT(leftguid, 1) AS id, '.$renamedFlag.' AS renamed
FROM releases
WHERE categories_id BETWEEN 2000 AND 2999
AND imdbid IS NULL
AND (imdbid IS NULL OR imdbid IN (\'\', \'0\', \'0000000\', \'00000000\'))
'.$condLookup.' '.$condRenamedOnly.'
LIMIT 16';
$queue = DB::select($sql);
@@ -1059,6 +1059,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'releases.searchname',
'releases.fromname',
'releases.categories_id',
'releases.imdbid',
DB::raw('IFNULL(GROUP_CONCAT(rf.name SEPARATOR " "),"") filename'),
])
->groupBy('releases.id')
@@ -1080,6 +1081,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'plainsearchname' => $searchNameDotless,
'fromname' => $release->fromname,
'categories_id' => $release->categories_id,
'imdbid' => (string) ($release->imdbid ?? ''),
'filename' => $release->filename, // @phpstan-ignore property.notFound
],
'doc_as_upsert' => true,
@@ -1681,6 +1683,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'plainsearchname' => $searchNameDotless,
'fromname' => $parameters['fromname'] ?? '',
'categories_id' => $parameters['categories_id'] ?? 0,
'imdbid' => (string) ($parameters['imdbid'] ?? ''),
'filename' => $parameters['filename'] ?? '',
'add_date' => now()->format('Y-m-d H:i:s'),
'post_date' => $parameters['postdate'] ?? now()->format('Y-m-d H:i:s'),
@@ -1849,7 +1852,7 @@ class ElasticSearchDriver implements SearchDriverInterface
$document = [
'id' => $parameters['id'],
'imdbid' => (int) ($parameters['imdbid'] ?? 0),
'imdbid' => (string) ($parameters['imdbid'] ?? ''),
'tmdbid' => (int) ($parameters['tmdbid'] ?? 0),
'traktid' => (int) ($parameters['traktid'] ?? 0),
'title' => (string) ($parameters['title'] ?? ''),
@@ -1971,7 +1974,7 @@ class ElasticSearchDriver implements SearchDriverInterface
$params['body'][] = [
'id' => $movie['id'],
'imdbid' => (int) ($movie['imdbid'] ?? 0),
'imdbid' => (string) ($movie['imdbid'] ?? ''),
'tmdbid' => (int) ($movie['tmdbid'] ?? 0),
'traktid' => (int) ($movie['traktid'] ?? 0),
'title' => (string) ($movie['title'] ?? ''),
@@ -2091,7 +2094,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'body' => [
'query' => [
'term' => [
$field => (int) $value,
$field => $field === 'imdbid' ? (string) $value : (int) $value,
],
],
'size' => 1,
@@ -2141,7 +2144,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'trakt' => (int) ($parameters['trakt'] ?? 0),
'tvmaze' => (int) ($parameters['tvmaze'] ?? 0),
'tvrage' => (int) ($parameters['tvrage'] ?? 0),
'imdb' => (int) ($parameters['imdb'] ?? 0),
'imdb' => (string) ($parameters['imdb'] ?? ''),
'tmdb' => (int) ($parameters['tmdb'] ?? 0),
'started' => (string) ($parameters['started'] ?? ''),
'type' => (int) ($parameters['type'] ?? 0),
@@ -2262,7 +2265,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'trakt' => (int) ($tvShow['trakt'] ?? 0),
'tvmaze' => (int) ($tvShow['tvmaze'] ?? 0),
'tvrage' => (int) ($tvShow['tvrage'] ?? 0),
'imdb' => (int) ($tvShow['imdb'] ?? 0),
'imdb' => (string) ($tvShow['imdb'] ?? ''),
'tmdb' => (int) ($tvShow['tmdb'] ?? 0),
'started' => (string) ($tvShow['started'] ?? ''),
'type' => (int) ($tvShow['type'] ?? 0),
@@ -2429,7 +2432,7 @@ class ElasticSearchDriver implements SearchDriverInterface
$shouldClauses = [];
foreach ($externalIds as $field => $value) {
if (! empty($value) && in_array($field, ['imdbid', 'tmdbid', 'traktid', 'tvdb', 'tvmaze', 'tvrage'])) {
$shouldClauses[] = ['term' => [$field => (int) $value]];
$shouldClauses[] = ['term' => [$field => $field === 'imdbid' ? (string) $value : (int) $value]];
}
}
@@ -165,7 +165,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'categories_id' => (int) ($parameters['categories_id'] ?? 0),
'filename' => $parameters['filename'] ?? '',
// External media IDs for efficient searching
'imdbid' => (int) ($parameters['imdbid'] ?? 0),
'imdbid' => (string) ($parameters['imdbid'] ?? ''),
'tmdbid' => (int) ($parameters['tmdbid'] ?? 0),
'traktid' => (int) ($parameters['traktid'] ?? 0),
'tvdb' => (int) ($parameters['tvdb'] ?? 0),
@@ -304,7 +304,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'fromname' => (string) ($release['fromname'] ?? ''),
'categories_id' => (int) ($release['categories_id'] ?? 0),
'filename' => (string) ($release['filename'] ?? ''),
'imdbid' => (int) ($release['imdbid'] ?? 0),
'imdbid' => (string) ($release['imdbid'] ?? ''),
'tmdbid' => (int) ($release['tmdbid'] ?? 0),
'traktid' => (int) ($release['traktid'] ?? 0),
'tvdb' => (int) ($release['tvdb'] ?? 0),
@@ -714,7 +714,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'filename' => ['type' => 'text'],
'categories_id' => ['type' => 'integer'],
// External media IDs for efficient searching
'imdbid' => ['type' => 'integer'],
'imdbid' => ['type' => 'string'],
'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'],
'tvdb' => ['type' => 'integer'],
@@ -751,7 +751,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'min_infix_len' => 2,
],
'columns' => [
'imdbid' => ['type' => 'integer'],
'imdbid' => ['type' => 'string'],
'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'],
'title' => ['type' => 'text'],
@@ -779,7 +779,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'trakt' => ['type' => 'integer'],
'tvmaze' => ['type' => 'integer'],
'tvrage' => ['type' => 'integer'],
'imdb' => ['type' => 'integer'],
'imdb' => ['type' => 'string'],
'tmdb' => ['type' => 'integer'],
'started' => ['type' => 'text'],
'type' => ['type' => 'integer'],
@@ -1611,7 +1611,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
try {
$document = [
'imdbid' => (int) ($parameters['imdbid'] ?? 0),
'imdbid' => (string) ($parameters['imdbid'] ?? ''),
'tmdbid' => (int) ($parameters['tmdbid'] ?? 0),
'traktid' => (int) ($parameters['traktid'] ?? 0),
'title' => (string) ($parameters['title'] ?? ''),
@@ -1713,7 +1713,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
$documents[] = [
'id' => $movie['id'],
'imdbid' => (int) ($movie['imdbid'] ?? 0),
'imdbid' => (string) ($movie['imdbid'] ?? ''),
'tmdbid' => (int) ($movie['tmdbid'] ?? 0),
'traktid' => (int) ($movie['traktid'] ?? 0),
'title' => (string) ($movie['title'] ?? ''),
@@ -1818,7 +1818,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'trakt' => (int) ($parameters['trakt'] ?? 0),
'tvmaze' => (int) ($parameters['tvmaze'] ?? 0),
'tvrage' => (int) ($parameters['tvrage'] ?? 0),
'imdb' => (int) ($parameters['imdb'] ?? 0),
'imdb' => (string) ($parameters['imdb'] ?? ''),
'tmdb' => (int) ($parameters['tmdb'] ?? 0),
'started' => (string) ($parameters['started'] ?? ''),
'type' => (int) ($parameters['type'] ?? 0),
@@ -1919,7 +1919,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'trakt' => (int) ($tvShow['trakt'] ?? 0),
'tvmaze' => (int) ($tvShow['tvmaze'] ?? 0),
'tvrage' => (int) ($tvShow['tvrage'] ?? 0),
'imdb' => (int) ($tvShow['imdb'] ?? 0),
'imdb' => (string) ($tvShow['imdb'] ?? ''),
'tmdb' => (int) ($tvShow['tmdb'] ?? 0),
'started' => (string) ($tvShow['started'] ?? ''),
'type' => (int) ($tvShow['type'] ?? 0),
@@ -2026,7 +2026,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
// Add filters for each external ID provided
foreach ($externalIds as $field => $value) {
if (! empty($value) && in_array($field, ['imdbid', 'tmdbid', 'traktid', 'tvdb', 'tvmaze', 'tvrage'])) {
$query->filter($field, '=', (int) $value);
$query->filter($field, '=', $field === 'imdbid' ? (string) $value : (int) $value);
}
}
+11 -12
View File
@@ -409,12 +409,11 @@ class TmdbClient
}
// Try to find the show by IMDB ID
$imdbId = $ids['imdb'] ?? 0;
$imdbId = $ids['imdb'] ?? '';
if (! empty($imdbId)) {
// Format IMDB ID with tt prefix if it's numeric
$imdbFormatted = is_numeric($imdbId)
? 'tt'.str_pad((string) $imdbId, 8, '0', STR_PAD_LEFT)
: (string) $imdbId;
$imdbFormatted = str_starts_with((string) $imdbId, 'tt')
? (string) $imdbId
: 'tt'.(string) $imdbId;
$show = $this->findTvByExternalId($imdbFormatted, 'imdb_id');
if ($show !== null && isset($show['id'])) {
@@ -452,9 +451,9 @@ class TmdbClient
} elseif ($source === 'tvdb' && is_numeric($id) && (int) $id > 0) {
$show = $this->findTvByExternalId((string) $id, 'tvdb_id');
} elseif ($source === 'imdb') {
$imdbFormatted = is_numeric($id)
? 'tt'.str_pad((string) $id, 8, '0', STR_PAD_LEFT)
: (string) $id;
$imdbFormatted = str_starts_with((string) $id, 'tt')
? (string) $id
: 'tt'.(string) $id;
$show = $this->findTvByExternalId($imdbFormatted, 'imdb_id');
}
@@ -471,11 +470,11 @@ class TmdbClient
$externalIds = self::getArray($show, 'external_ids');
// Parse IMDB ID to numeric
$imdbId = 0;
// Parse IMDB ID to raw numeric string
$imdbId = '';
if (! empty($externalIds['imdb_id'])) {
preg_match('/tt(?P<imdbid>\d{6,8})$/i', $externalIds['imdb_id'], $imdb);
$imdbId = (int) ($imdb['imdbid'] ?? 0);
preg_match('/tt(?P<imdbid>\d{6,})$/i', (string) $externalIds['imdb_id'], $imdb);
$imdbId = (string) ($imdb['imdbid'] ?? '');
}
return [
+1 -1
View File
@@ -307,7 +307,7 @@ class Tmux
SELECT
SUM(IF(categories_id BETWEEN %d AND %d AND categories_id != %d AND videos_id = 0 AND tv_episodes_id BETWEEN -3 AND 0 AND size > 1048576,1,0)) AS processtv,
SUM(IF(categories_id = %d AND anidbid IS NULL,1,0)) AS processanime,
SUM(IF(categories_id BETWEEN %d AND %d AND imdbid IS NULL,1,0)) AS processmovies,
SUM(IF(categories_id BETWEEN %d AND %d AND (imdbid IS NULL OR imdbid IN (\'\', \'0\', \'0000000\', \'00000000\')),1,0)) AS processmovies,
SUM(IF(categories_id IN (%d, %d, %d) AND musicinfo_id IS NULL,1,0)) AS processmusic,
SUM(IF(categories_id BETWEEN %d AND %d AND consoleinfo_id IS NULL,1,0)) AS processconsole,
SUM(IF(categories_id IN (%s) AND bookinfo_id IS NULL,1,0)) AS processbooks,
@@ -384,13 +384,13 @@ class TmdbProvider extends AbstractTvProvider
->first(['tmdb', 'tvdb', 'imdb']);
if ($result === null) {
return ['tmdb' => 0, 'tvdb' => 0, 'imdb' => 0];
return ['tmdb' => 0, 'tvdb' => 0, 'imdb' => ''];
}
return [
'tmdb' => (int) ($result->tmdb ?? 0),
'tvdb' => (int) ($result->tvdb ?? 0),
'imdb' => (int) ($result->imdb ?? 0),
'imdb' => (string) ($result->imdb ?? ''),
];
}
@@ -495,11 +495,11 @@ class TmdbProvider extends AbstractTvProvider
? 'https://image.tmdb.org/t/p/w500'.$posterPath
: '';
$imdbId = 0;
$imdbId = '';
$externalIds = TmdbClient::getArray($show, 'external_ids');
if (! empty($externalIds['imdb_id'])) {
preg_match('/tt(?P<imdbid>\d{6,8})$/i', (string) $externalIds['imdb_id'], $imdb);
$imdbId = $imdb['imdbid'] ?? 0;
preg_match('/tt(?P<imdbid>\d{6,})$/i', (string) $externalIds['imdb_id'], $imdb);
$imdbId = (string) ($imdb['imdbid'] ?? '');
}
$originCountry = TmdbClient::getArray($show, 'origin_country');
@@ -558,7 +558,7 @@ class TmdbProvider extends AbstractTvProvider
}
// Try IMDB ID
if (! empty($imdbId) && $imdbId > 0) {
if (! empty($imdbId) && imdb_id_is_valid($imdbId)) {
$ids = $traktService->lookupShowIds($imdbId, 'imdb');
if ($ids !== null && ! empty($ids['trakt'])) {
return (int) $ids['trakt'];
@@ -254,14 +254,14 @@ class TraktProvider extends AbstractTvProvider
->first(['trakt', 'tmdb', 'tvdb', 'imdb']);
if ($result === null) {
return ['trakt' => 0, 'tmdb' => 0, 'tvdb' => 0, 'imdb' => 0];
return ['trakt' => 0, 'tmdb' => 0, 'tvdb' => 0, 'imdb' => ''];
}
return [
'trakt' => (int) ($result->trakt ?? 0),
'tmdb' => (int) ($result->tmdb ?? 0),
'tvdb' => (int) ($result->tvdb ?? 0),
'imdb' => (int) ($result->imdb ?? 0),
'imdb' => (string) ($result->imdb ?? ''),
];
}
@@ -387,12 +387,12 @@ class TraktProvider extends AbstractTvProvider
*/
public function formatShowInfo(mixed $show): array
{
preg_match('/tt(?P<imdbid>\d{6,8})$/i', (string) ($show['ids']['imdb'] ?? ''), $imdb);
preg_match('/tt(?P<imdbid>\d{6,})$/i', (string) ($show['ids']['imdb'] ?? ''), $imdb);
$this->posterUrl = $show['images']['poster']['thumb'] ?? '';
$this->fanartUrl = $show['images']['fanart']['thumb'] ?? '';
$this->localizedTZ = $show['airs']['timezone'] ?? '';
$imdbId = $imdb['imdbid'] ?? 0;
$imdbId = (string) ($imdb['imdbid'] ?? '');
$tvdbId = $show['ids']['tvdb'] ?? 0;
// Look up TVMaze ID using TVDB or IMDB
@@ -438,8 +438,8 @@ class TraktProvider extends AbstractTvProvider
}
// Try IMDB ID as fallback
if (! empty($imdbId) && $imdbId > 0) {
$imdbFormatted = 'tt'.str_pad((string) $imdbId, 8, '0', STR_PAD_LEFT);
if (! empty($imdbId) && imdb_id_is_valid($imdbId)) {
$imdbFormatted = 'tt'.(string) $imdbId;
$result = $tvmazeClient->getShowBySiteID('imdb', $imdbFormatted);
if ($result !== null && isset($result->id)) {
return (int) $result->id;
@@ -444,12 +444,12 @@ class TvMazeProvider extends AbstractTvProvider
$this->posterUrl = (string) ($show->mediumImage ?? '');
$tvdbId = (int) ($show->externalIDs['thetvdb'] ?? 0);
$imdbId = 0;
$imdbId = '';
// Extract IMDB ID if available
if (! empty($show->externalIDs['imdb'])) {
preg_match('/tt(?P<imdbid>\d{6,9})$/i', (string) $show->externalIDs['imdb'], $imdb);
$imdbId = (int) ($imdb['imdbid'] ?? 0);
preg_match('/tt(?P<imdbid>\d{6,})$/i', (string) $show->externalIDs['imdb'], $imdb);
$imdbId = (string) ($imdb['imdbid'] ?? '');
}
// Look up TMDB and Trakt IDs using available external IDs
@@ -497,7 +497,7 @@ class TvMazeProvider extends AbstractTvProvider
}
}
// Try IMDB ID if TMDB not found
if ($result['tmdb'] === 0 && ! empty($imdbId) && $imdbId > 0) {
if ($result['tmdb'] === 0 && ! empty($imdbId) && imdb_id_is_valid($imdbId)) {
$tmdbIds = $tmdbClient->lookupTvShowIds($imdbId, 'imdb');
if ($tmdbIds !== null) {
$result['tmdb'] = $tmdbIds['tmdb'] ?? 0;
@@ -403,12 +403,12 @@ class TvdbProvider extends AbstractTvProvider
}
}
$imdbId = 0;
$imdbId = '';
$imdbIdObj = null;
try {
$imdbIdObj = $this->client->series()->extended((int) $show->tvdb_id);
preg_match('/tt(?P<imdbid>\d{6,9})$/i', (string) ($imdbIdObj->getIMDBId() ?? ''), $imdb);
$imdbId = $imdb['imdbid'] ?? 0;
preg_match('/tt(?P<imdbid>\d{6,})$/i', (string) ($imdbIdObj->getIMDBId() ?? ''), $imdb);
$imdbId = (string) ($imdb['imdbid'] ?? '');
} catch (ResourceNotFoundException $e) {
cli()->error('Show ImdbId not found on TVDB');
} catch (\Exception) {
@@ -476,7 +476,7 @@ class TvdbProvider extends AbstractTvProvider
}
// Try IMDB ID as fallback
if (! empty($imdbId) && $imdbId > 0) {
if (! empty($imdbId) && imdb_id_is_valid($imdbId)) {
$traktIds = $traktService->lookupShowIds($imdbId, 'imdb');
if ($traktIds !== null && ! empty($traktIds['trakt'])) {
$result['trakt'] = (int) $traktIds['trakt'];
+1 -1
View File
@@ -141,6 +141,6 @@ class ApiTransformer extends TransformerAbstract
*/
protected function nullIfZero($value)
{
return ($value !== null && $value !== 0) ? $value : $this->null();
return ($value !== null && $value !== 0 && $value !== '' && $value !== '0') ? $value : $this->null();
}
}
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Normalize legacy zero-padded IMDb IDs while preserving legitimate leading-zero IDs.
*
* Historically some paths stored IMDb IDs with imdb_id_pad(), which converted 7-digit
* IDs like 0137523 into 8-digit padded values. Real 8+ digit IMDb IDs are never expected
* to start with 0, so values matching /^0\d{7}$/ can safely drop a single leading zero.
*
* Sentinel values like 0000000 / 00000000 become empty strings to preserve the existing
* "attempted but unresolved" behavior in string-based code paths.
*/
public function up(): void
{
$this->normalizeImdbColumn('releases', 'imdbid');
$this->normalizeImdbColumn('movieinfo', 'imdbid');
$this->normalizeImdbColumn('user_movies', 'imdbid');
$this->normalizeImdbColumn('videos', 'imdb');
}
public function down(): void
{
// No-op: this migration removes lossy zero-padding and cannot be safely reversed.
}
private function normalizeImdbColumn(string $table, string $column): void
{
DB::table($table)
->whereIn($column, ['0000000', '00000000', '0'])
->update([$column => '']);
DB::table($table)
->whereRaw(sprintf("`%s` REGEXP '^0[0-9]{7}$'", $column))
->update([$column => DB::raw(sprintf('SUBSTRING(`%s`, 2)', $column))]);
}
};
+1 -1
View File
@@ -248,7 +248,7 @@
<a href="#" class="add-to-cart px-2 py-1 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-500 transition text-sm" data-guid="{{ $result->guid }}" title="Add to Cart">
<i class="icon_cart fa fa-shopping-basket"></i>
</a>
@if(!empty($result->imdbid) && $result->imdbid != '0' && $result->imdbid != 0 && $result->imdbid != '0000000')
@if(!empty($result->imdbid) && imdb_id_is_valid($result->imdbid))
<a href="{{ url('/mymovies?id=add&imdb=' . $result->imdbid) }}"
class="px-2 py-1 bg-purple-600 dark:bg-purple-700 text-white rounded-lg hover:bg-purple-700 dark:hover:bg-purple-800 transition text-sm"
title="Add to My Movies">
+2 -2
View File
@@ -325,7 +325,7 @@
<a href="#" class="add-to-cart px-2 py-1 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition text-sm" data-guid="{{ $result->guid }}" title="Add to Cart">
<i class="icon_cart fa fa-shopping-basket"></i>
</a>
@if(!empty($result->imdbid) && $result->imdbid != '0' && $result->imdbid != 0 && $result->imdbid != '0000000')
@if(!empty($result->imdbid) && imdb_id_is_valid($result->imdbid))
<a href="{{ url('/mymovies?id=add&imdb=' . $result->imdbid) }}"
class="px-2 py-1 bg-purple-600 dark:bg-purple-700 text-white rounded-lg hover:bg-purple-700 dark:hover:bg-purple-800 transition text-sm"
title="Add to My Movies">
@@ -371,7 +371,7 @@
<a href="#" class="add-to-cart px-2 py-1 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition text-sm" data-guid="{{ $result->guid }}" title="Add to Cart">
<i class="icon_cart fa fa-shopping-basket"></i>
</a>
@if(!empty($result->imdbid) && $result->imdbid != '0' && $result->imdbid != 0 && $result->imdbid != '0000000')
@if(!empty($result->imdbid) && imdb_id_is_valid($result->imdbid))
<a href="{{ url('/mymovies?id=add&imdb=' . $result->imdbid) }}"
class="px-2 py-1 bg-purple-600 dark:bg-purple-700 text-white rounded-lg hover:bg-purple-700 dark:hover:bg-purple-800 transition text-sm"
title="Add to My Movies">
+7 -7
View File
@@ -90,7 +90,7 @@ class MovieServiceTest extends ImdbScraperTestCase
}
#[Test]
public function it_finds_movie_info_for_padded_release_imdb_ids(): void
public function it_finds_movie_info_for_imdb_ids_with_meaningful_leading_zeroes(): void
{
Cache::flush();
@@ -98,20 +98,20 @@ class MovieServiceTest extends ImdbScraperTestCase
$service->echooutput = false;
$service->update([
'imdbid' => '8169446',
'imdbid' => '0137523',
'title' => 'Example Movie',
'year' => '2024',
]);
$movie = $service->getMovieInfo('08169446');
$movie = $service->getMovieInfo('0137523');
$this->assertNotNull($movie);
$this->assertSame('8169446', $movie->imdbid);
$this->assertSame('0137523', $movie->imdbid);
$this->assertSame('Example Movie', $movie->title);
}
#[Test]
public function it_returns_existing_trailer_for_padded_release_imdb_ids(): void
public function it_returns_existing_trailer_for_imdb_ids_with_meaningful_leading_zeroes(): void
{
Cache::flush();
@@ -119,13 +119,13 @@ class MovieServiceTest extends ImdbScraperTestCase
$service->echooutput = false;
$service->update([
'imdbid' => '8169446',
'imdbid' => '0137523',
'title' => 'Example Movie',
'year' => '2024',
'trailer' => 'https://example.test/embed/trailer',
]);
$this->assertSame('https://example.test/embed/trailer', $service->getTrailer('08169446'));
$this->assertSame('https://example.test/embed/trailer', $service->getTrailer('0137523'));
}
/**