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'], 'filename' => ['type' => 'text'],
'categories_id' => ['type' => 'integer'], 'categories_id' => ['type' => 'integer'],
// External media IDs for efficient searching // External media IDs for efficient searching
'imdbid' => ['type' => 'integer'], 'imdbid' => ['type' => 'string'],
'tmdbid' => ['type' => 'integer'], 'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'], 'traktid' => ['type' => 'integer'],
'tvdb' => ['type' => 'integer'], 'tvdb' => ['type' => 'integer'],
@@ -103,7 +103,7 @@ class CreateManticoreIndexes extends Command
'min_infix_len' => 2, 'min_infix_len' => 2,
], ],
'columns' => [ 'columns' => [
'imdbid' => ['type' => 'integer'], 'imdbid' => ['type' => 'string'],
'tmdbid' => ['type' => 'integer'], 'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'], 'traktid' => ['type' => 'integer'],
'title' => ['type' => 'text'], 'title' => ['type' => 'text'],
@@ -126,7 +126,7 @@ class CreateManticoreIndexes extends Command
'trakt' => ['type' => 'integer'], 'trakt' => ['type' => 'integer'],
'tvmaze' => ['type' => 'integer'], 'tvmaze' => ['type' => 'integer'],
'tvrage' => ['type' => 'integer'], 'tvrage' => ['type' => 'integer'],
'imdb' => ['type' => 'integer'], 'imdb' => ['type' => 'string'],
'tmdb' => ['type' => 'integer'], 'tmdb' => ['type' => 'integer'],
'started' => ['type' => 'text'], 'started' => ['type' => 'text'],
'type' => ['type' => 'integer'], 'type' => ['type' => 'integer'],
+4 -4
View File
@@ -81,7 +81,7 @@ class CreateMediaIndexes extends Command
'min_infix_len' => 2, 'min_infix_len' => 2,
], ],
'columns' => [ 'columns' => [
'imdbid' => ['type' => 'integer'], 'imdbid' => ['type' => 'string'],
'tmdbid' => ['type' => 'integer'], 'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'], 'traktid' => ['type' => 'integer'],
'title' => ['type' => 'text'], 'title' => ['type' => 'text'],
@@ -104,7 +104,7 @@ class CreateMediaIndexes extends Command
'trakt' => ['type' => 'integer'], 'trakt' => ['type' => 'integer'],
'tvmaze' => ['type' => 'integer'], 'tvmaze' => ['type' => 'integer'],
'tvrage' => ['type' => 'integer'], 'tvrage' => ['type' => 'integer'],
'imdb' => ['type' => 'integer'], 'imdb' => ['type' => 'string'],
'tmdb' => ['type' => 'integer'], 'tmdb' => ['type' => 'integer'],
'started' => ['type' => 'text'], 'started' => ['type' => 'text'],
'type' => ['type' => 'integer'], 'type' => ['type' => 'integer'],
@@ -228,7 +228,7 @@ class CreateMediaIndexes extends Command
$moviesMapping = [ $moviesMapping = [
'mappings' => [ 'mappings' => [
'properties' => [ 'properties' => [
'imdbid' => ['type' => 'integer'], 'imdbid' => ['type' => 'keyword'],
'tmdbid' => ['type' => 'integer'], 'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'], 'traktid' => ['type' => 'integer'],
'title' => ['type' => 'text', 'analyzer' => 'standard'], 'title' => ['type' => 'text', 'analyzer' => 'standard'],
@@ -255,7 +255,7 @@ class CreateMediaIndexes extends Command
'trakt' => ['type' => 'integer'], 'trakt' => ['type' => 'integer'],
'tvmaze' => ['type' => 'integer'], 'tvmaze' => ['type' => 'integer'],
'tvrage' => ['type' => 'integer'], 'tvrage' => ['type' => 'integer'],
'imdb' => ['type' => 'integer'], 'imdb' => ['type' => 'keyword'],
'tmdb' => ['type' => 'integer'], 'tmdb' => ['type' => 'integer'],
'started' => ['type' => 'keyword'], 'started' => ['type' => 'keyword'],
'type' => ['type' => 'integer'], 'type' => ['type' => 'integer'],
@@ -202,7 +202,7 @@ class NntmuxPopulateSearchIndexes extends Command
'filename' => '', 'filename' => '',
'videos_id' => (int) ($item->videos_id ?? 0), 'videos_id' => (int) ($item->videos_id ?? 0),
'movieinfo_id' => (int) ($item->movieinfo_id ?? 0), 'movieinfo_id' => (int) ($item->movieinfo_id ?? 0),
'imdbid' => (int) ($item->imdbid ?? 0), 'imdbid' => (string) ($item->imdbid ?? ''),
'tmdbid' => 0, 'tmdbid' => 0,
'traktid' => 0, 'traktid' => 0,
'tvdb' => 0, 'tvdb' => 0,
@@ -287,7 +287,7 @@ class NntmuxPopulateSearchIndexes extends Command
function ($item) { function ($item) {
return [ return [
'id' => $item->id, 'id' => $item->id,
'imdbid' => (int) ($item->imdbid ?? 0), 'imdbid' => (string) ($item->imdbid ?? ''),
'tmdbid' => (int) ($item->tmdbid ?? 0), 'tmdbid' => (int) ($item->tmdbid ?? 0),
'traktid' => (int) ($item->traktid ?? 0), 'traktid' => (int) ($item->traktid ?? 0),
'title' => (string) ($item->title ?? ''), 'title' => (string) ($item->title ?? ''),
@@ -345,7 +345,7 @@ class NntmuxPopulateSearchIndexes extends Command
'trakt' => (int) ($item->trakt ?? 0), 'trakt' => (int) ($item->trakt ?? 0),
'tvmaze' => (int) ($item->tvmaze ?? 0), 'tvmaze' => (int) ($item->tvmaze ?? 0),
'tvrage' => (int) ($item->tvrage ?? 0), 'tvrage' => (int) ($item->tvrage ?? 0),
'imdb' => (int) ($item->imdb ?? 0), 'imdb' => (string) ($item->imdb ?? ''),
'tmdb' => (int) ($item->tmdb ?? 0), 'tmdb' => (int) ($item->tmdb ?? 0),
'started' => (string) ($item->started ?? ''), 'started' => (string) ($item->started ?? ''),
'type' => (int) ($item->type ?? 0), 'type' => (int) ($item->type ?? 0),
+2 -2
View File
@@ -194,7 +194,7 @@ class NntmuxResetDb extends Command
'type' => 'long', 'type' => 'long',
'index' => false, 'index' => false,
], ],
'imdbid' => ['type' => 'integer'], 'imdbid' => ['type' => 'keyword'],
'tmdbid' => ['type' => 'integer'], 'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'], 'traktid' => ['type' => 'integer'],
'title' => [ 'title' => [
@@ -247,7 +247,7 @@ class NntmuxResetDb extends Command
'trakt' => ['type' => 'integer'], 'trakt' => ['type' => 'integer'],
'tvmaze' => ['type' => 'integer'], 'tvmaze' => ['type' => 'integer'],
'tvrage' => ['type' => 'integer'], 'tvrage' => ['type' => 'integer'],
'imdb' => ['type' => 'integer'], 'imdb' => ['type' => 'keyword'],
'tmdb' => ['type' => 'integer'], 'tmdb' => ['type' => 'integer'],
'started' => ['type' => 'text'], 'started' => ['type' => 'text'],
'type' => ['type' => 'integer'], 'type' => ['type' => 'integer'],
@@ -427,7 +427,7 @@ class UpdateReleasesIndexSchema extends Command
private function prepareMediaData(mixed $release): array private function prepareMediaData(mixed $release): array
{ {
return [ return [
'imdbid' => (int) ($release->imdbid ?: 0), 'imdbid' => (string) ($release->imdbid ?: ''),
'tmdbid' => (int) ($release->tmdbid ?: ($release->video_tmdb ?: 0)), 'tmdbid' => (int) ($release->tmdbid ?: ($release->video_tmdb ?: 0)),
'traktid' => (int) ($release->traktid ?: ($release->video_trakt ?: 0)), 'traktid' => (int) ($release->traktid ?: ($release->video_trakt ?: 0)),
'tvdb' => (int) ($release->tvdb ?: 0), 'tvdb' => (int) ($release->tvdb ?: 0),
@@ -42,7 +42,7 @@ class UpdateReleasesIndexSchemaES extends Command
* @var array<string, mixed> * @var array<string, mixed>
*/ */
private array $mediaFields = [ private array $mediaFields = [
'imdbid' => ['type' => 'integer'], 'imdbid' => ['type' => 'keyword'],
'tmdbid' => ['type' => 'integer'], 'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'], 'traktid' => ['type' => 'integer'],
'tvdb' => ['type' => 'integer'], 'tvdb' => ['type' => 'integer'],
@@ -384,7 +384,7 @@ class UpdateReleasesIndexSchemaES extends Command
'format' => 'yyyy-MM-dd HH:mm:ss', 'format' => 'yyyy-MM-dd HH:mm:ss',
], ],
// New media-related fields // New media-related fields
'imdbid' => ['type' => 'integer'], 'imdbid' => ['type' => 'keyword'],
'tmdbid' => ['type' => 'integer'], 'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'], 'traktid' => ['type' => 'integer'],
'tvdb' => ['type' => 'integer'], 'tvdb' => ['type' => 'integer'],
@@ -597,7 +597,7 @@ class UpdateReleasesIndexSchemaES extends Command
private function prepareMediaData(mixed $release): array private function prepareMediaData(mixed $release): array
{ {
return [ return [
'imdbid' => (int) ($release->imdbid ?: 0), 'imdbid' => (string) ($release->imdbid ?: ''),
'tmdbid' => (int) ($release->tmdbid ?: ($release->video_tmdb ?: 0)), 'tmdbid' => (int) ($release->tmdbid ?: ($release->video_tmdb ?: 0)),
'traktid' => (int) ($release->traktid ?: ($release->video_trakt ?: 0)), 'traktid' => (int) ($release->traktid ?: ($release->video_trakt ?: 0)),
'tvdb' => (int) ($release->tvdb ?: 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')) { if (! function_exists('imdb_id_pad')) {
/** /**
* Normalize IMDB ID to 8 zero-padded digits for storage in releases / join consistency. * @deprecated Use imdb_id_is_valid() instead. This function exists only for backward
* Enables index use on releases.imdbid; call when writing to releases.imdbid. * compatibility during the transition period and will be removed.
*/ */
function imdb_id_pad(int|string|null $id): string function imdb_id_pad(int|string|null $id): string
{ {
@@ -467,7 +484,7 @@ if (! function_exists('getReleaseCover')) {
$gamesinfo_id = $getValue($release, 'gamesinfo_id'); $gamesinfo_id = $getValue($release, 'gamesinfo_id');
$anidbid = $getValue($release, 'anidbid'); $anidbid = $getValue($release, 'anidbid');
if (! empty($imdbid) && $imdbid > 0) { if (! empty($imdbid) && imdb_id_is_valid($imdbid)) {
$coverType = 'movies'; $coverType = 'movies';
$coverId = (string) $imdbid; $coverId = (string) $imdbid;
} elseif (! empty($videos_id) && $videos_id > 0) { } elseif (! empty($videos_id) && $videos_id > 0) {
+3 -1
View File
@@ -260,7 +260,9 @@ class ApiController extends BasePageController
$maxAge = $this->maxAge($request); $maxAge = $this->maxAge($request);
UserRequest::addApiRequest($uid, $request->getRequestUri()); 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; $tmdbId = $request->has('tmdbid') && $request->filled('tmdbid') ? (int) $request->input('tmdbid') : -1;
$traktId = $request->has('traktid') && $request->filled('traktid') ? (int) $request->input('traktid') : -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\Routing\Redirector;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class ApiV2Controller extends BasePageController class ApiV2Controller extends BasePageController
{ {
@@ -133,7 +134,7 @@ class ApiV2Controller extends BasePageController
event(new UserAccessedApi($user, $request->ip())); event(new UserAccessedApi($user, $request->ip()));
// Get request parameters efficiently // 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); $tmdbId = (int) $request->input('tmdbid', -1);
$traktId = (int) $request->input('traktid', -1); $traktId = (int) $request->input('traktid', -1);
$minSize = max(0, (int) $request->input('minsize', 0)); $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'), DB::raw('COUNT(DISTINCT ud.id) as total_downloads'),
]) ])
->where('m.title', '!=', '') ->where('m.title', '!=', '')
->where('m.imdbid', '!=', '0000000') ->whereNotNull('m.imdbid')
->where('m.imdbid', '!=', '')
->where('ud.timestamp', '>=', $fortyEightHoursAgo) ->where('ud.timestamp', '>=', $fortyEightHoursAgo)
->groupBy('m.imdbid') ->groupBy('m.imdbid')
->havingRaw('COUNT(DISTINCT ud.id) > 0') ->havingRaw('COUNT(DISTINCT ud.id) > 0')
+4 -4
View File
@@ -240,7 +240,7 @@ class XML_Response
$attrs = array_merge($attrs, $this->buildTvAttrArray()); $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; $attrs['imdb'] = $this->release->imdbid;
} }
if (isset($this->release->anidbid) && $this->release->anidbid > 0) { 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) { if (isset($this->release->tvmaze) && $this->release->tvmaze > 0) {
$attrs['tvmazeid'] = $this->release->tvmaze; $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; $attrs['imdbid'] = $this->release->imdb;
} }
if (isset($this->release->tmdb) && $this->release->tmdb > 0) { if (isset($this->release->tmdb) && $this->release->tmdb > 0) {
@@ -645,7 +645,7 @@ class XML_Response
$this->setTvAttr(); $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); $this->writeZedAttr('imdb', $this->release->imdbid);
} }
if (isset($this->release->anidbid) && $this->release->anidbid > 0) { 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) { if (isset($this->release->tvmaze) && $this->release->tvmaze > 0) {
$this->writeZedAttr('tvmazeid', $this->release->tvmaze); $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); $this->writeZedAttr('imdbid', $this->release->imdb);
} }
if (isset($this->release->tmdb) && $this->release->tmdb > 0) { if (isset($this->release->tmdb) && $this->release->tmdb > 0) {
+1 -1
View File
@@ -85,7 +85,7 @@ class DetailsController extends BasePageController
} }
$mov = ''; $mov = '';
if (imdb_id_pad($data['imdbid']) !== '00000000') { if (imdb_id_is_valid($data['imdbid'])) {
$mov = $this->movieService->getMovieInfo($data['imdbid']); $mov = $this->movieService->getMovieInfo($data['imdbid']);
if (! empty($mov['title'])) { if (! empty($mov['title'])) {
$mov['title'] = str_replace(['/', '\\'], '', $mov['title']); $mov['title'] = str_replace(['/', '\\'], '', $mov['title']);
+1 -1
View File
@@ -305,7 +305,7 @@ class GetNzbController extends BasePageController
]; ];
// Add optional metadata headers // 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}"; $headers['X-DNZB-MoreInfo'] = "http://www.imdb.com/title/tt{$releaseData->imdbid}";
} elseif (! empty($releaseData->tvdb) && $releaseData->tvdb > 0) { } elseif (! empty($releaseData->tvdb) && $releaseData->tvdb > 0) {
$headers['X-DNZB-MoreInfo'] = "http://www.thetvdb.com/?tab=series&id={$releaseData->tvdb}"; $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'), DB::raw('COUNT(DISTINCT r.id) as release_count'),
]) ])
->where('m.title', '!=', '') ->where('m.title', '!=', '')
->where('m.imdbid', '!=', '0000000') ->whereNotNull('m.imdbid')
->where('m.imdbid', '!=', '')
->where('ud.timestamp', '>=', $fortyEightHoursAgo) ->where('ud.timestamp', '>=', $fortyEightHoursAgo)
->groupBy('m.imdbid', 'm.title', 'm.year', 'm.rating', 'm.plot', 'm.genre', 'm.cover', 'm.tmdbid', 'm.traktid') ->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') ->havingRaw('COUNT(DISTINCT ud.id) > 0')
+1 -1
View File
@@ -13,7 +13,7 @@ use Illuminate\Support\Facades\Cache;
* App\Models\MovieInfo. * App\Models\MovieInfo.
* *
* @property int $id * @property int $id
* @property int $imdbid * @property string $imdbid
* @property int $tmdbid * @property int $tmdbid
* @property string $title * @property string $title
* @property string $tagline * @property string $tagline
+1 -1
View File
@@ -13,7 +13,7 @@ use Illuminate\Database\Eloquent\Model;
* *
* @property int $id * @property int $id
* @property int $users_id * @property int $users_id
* @property int|null $imdbid * @property string|null $imdbid
* @property string|null $categories List of categories for user movies * @property string|null $categories List of categories for user movies
* @property Carbon|null $created_at * @property Carbon|null $created_at
* @property Carbon|null $updated_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 $countries_id Two character country code (FK to countries table).
* @property string $started Date (UTC) of production's first airing. * @property string $started Date (UTC) of production's first airing.
* @property int $anidb ID number for anidb site * @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 $tmdb ID number for TMDB site.
* @property int $trakt ID number for TraktTV site. * @property int $trakt ID number for TraktTV site.
* @property int $tvdb ID number for TVDB site * @property int $tvdb ID number for TVDB site
+1 -1
View File
@@ -49,7 +49,7 @@ class MovieInfoObserver
try { try {
Search::insertMovie([ Search::insertMovie([
'id' => $movie->id, 'id' => $movie->id,
'imdbid' => $movie->imdbid ?? 0, 'imdbid' => $movie->imdbid ?? '',
'tmdbid' => $movie->tmdbid ?? 0, 'tmdbid' => $movie->tmdbid ?? 0,
'traktid' => $movie->traktid ?? 0, 'traktid' => $movie->traktid ?? 0,
'title' => $movie->title ?? '', 'title' => $movie->title ?? '',
+10 -15
View File
@@ -63,7 +63,7 @@ class MovieBrowseService
$whereAge = $maxAge > 0 ? 'AND r.postdate > NOW() - INTERVAL '.$maxAge.' DAY ' : ''; $whereAge = $maxAge > 0 ? 'AND r.postdate > NOW() - INTERVAL '.$maxAge.' DAY ' : '';
$browseBy = $this->getBrowseBy(); $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} " ."AND r.passwordstatus {$this->showPasswords} "
.$browseBy.' ' .$browseBy.' '
.$catFilter .$catFilter
@@ -85,11 +85,9 @@ class MovieBrowseService
$totalCount = Cache::get($countCacheKey); $totalCount = Cache::get($countCacheKey);
if ($totalCount === null) { 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 ' $countSql = 'SELECT COUNT(DISTINCT m.imdbid) AS total '
.'FROM movieinfo m ' .'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; .'WHERE '.$baseWhere;
$totalResult = DB::select($countSql); $totalResult = DB::select($countSql);
@@ -125,7 +123,7 @@ class MovieBrowseService
.'FROM (' .'FROM ('
.'SELECT m.imdbid, MAX(r.postdate) AS latest_postdate, COUNT(r.id) AS total_releases ' .'SELECT m.imdbid, MAX(r.postdate) AS latest_postdate, COUNT(r.id) AS total_releases '
.'FROM movieinfo m ' .'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.' ' .'WHERE '.$baseWhere.' '
.'GROUP BY m.imdbid'.$innerExtraGroupBy.' ' .'GROUP BY m.imdbid'.$innerExtraGroupBy.' '
."ORDER BY {$innerOrderBy} {$order[1]} " ."ORDER BY {$innerOrderBy} {$order[1]} "
@@ -144,11 +142,9 @@ class MovieBrowseService
$movieImdbIds = $movies->pluck('imdbid')->toArray(); $movieImdbIds = $movies->pluck('imdbid')->toArray();
// Step 3: Get top 2 releases per movie using UNION ALL with LIMIT 2 per imdbid. // 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 = []; $unionParts = [];
foreach ($movieImdbIds as $id) { foreach ($movieImdbIds as $id) {
$paddedId = str_pad((string) (int) $id, 8, '0', STR_PAD_LEFT); $quotedId = escapeString((string) $id);
$quotedId = "'".$paddedId."'";
$unionParts[] = '(SELECT r.id, r.imdbid, r.guid, r.searchname, ' $unionParts[] = '(SELECT r.id, r.imdbid, r.guid, r.searchname, '
.'r.size, r.postdate, r.adddate, r.haspreview ' .'r.size, r.postdate, r.adddate, r.haspreview '
.'FROM releases r ' .'FROM releases r '
@@ -163,16 +159,15 @@ class MovieBrowseService
$releasesSql = implode(' UNION ALL ', $unionParts); $releasesSql = implode(' UNION ALL ', $unionParts);
$releases = DB::select($releasesSql); $releases = DB::select($releasesSql);
// Group by imdbid (already 8-char in DB) // Group by imdbid
$releasesByMovie = []; $releasesByMovie = [];
foreach ($releases as $release) { foreach ($releases as $release) {
$releasesByMovie[$release->imdbid][] = $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) { foreach ($movies as $movie) {
$normMovieId = str_pad((string) (int) $movie->imdbid, 8, '0', STR_PAD_LEFT); $movie->releases = $releasesByMovie[$movie->imdbid] ?? []; // @phpstan-ignore assign.propertyReadOnly
$movie->releases = $releasesByMovie[$normMovieId] ?? []; // @phpstan-ignore assign.propertyReadOnly
} }
// Set total count on first item (matches existing pattern used by controllers) // 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 public function getMovieReleases(string $imdbid, array $excludedCats = []): array
{ {
$whereExcluded = count($excludedCats) > 0 ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''; $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 ' $sql = 'SELECT r.id, r.guid, r.searchname, r.size, r.postdate, r.adddate, r.haspreview '
.'FROM releases r ' .'FROM releases r '
.'WHERE r.imdbid = '.$paddedId.' ' .'WHERE r.imdbid = '.$quotedId.' '
."AND r.passwordstatus {$this->showPasswords} " ."AND r.passwordstatus {$this->showPasswords} "
.$whereExcluded.' ' .$whereExcluded.' '
.'ORDER BY r.postdate DESC'; .'ORDER BY r.postdate DESC';
@@ -256,7 +251,7 @@ class MovieBrowseService
$bbv .= '.'; $bbv .= '.';
} }
if ($bb === 'imdb') { if ($bb === 'imdb') {
$browseBy .= sprintf(' AND m.imdbid = %d', $bbv); $browseBy .= ' AND m.imdbid = '.escapeString($bbv);
} else { } else {
$browseBy .= ' AND m.'.$bb.' '.'LIKE '.escapeString('%'.$bbv.'%'); $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 public function getMovieInfo(int|string|null $imdbId): ?MovieInfo
{ {
$lookupIds = $this->movieInfoLookupIds($imdbId); $sanitized = $this->sanitizeImdbId($imdbId);
if ($lookupIds === []) { if ($sanitized === null) {
return null; return null;
} }
foreach ($lookupIds as $lookupId) { return MovieInfo::query()->where('imdbid', $sanitized)->first();
$movie = MovieInfo::query()->where('imdbid', $lookupId)->first();
if ($movie !== null) {
return $movie;
}
}
return null;
} }
/** /**
@@ -131,33 +124,26 @@ class MovieService
*/ */
public function getTrailer(int|string|null $imdbId): string|false public function getTrailer(int|string|null $imdbId): string|false
{ {
$lookupIds = $this->movieInfoLookupIds($imdbId); $sanitized = $this->sanitizeImdbId($imdbId);
if ($lookupIds === []) { if ($sanitized === null) {
return false; return false;
} }
foreach ($lookupIds as $lookupId) { $trailer = MovieInfo::query()->where('imdbid', $sanitized)->where('trailer', '<>', '')->first(['trailer']);
$trailer = MovieInfo::query()->where('imdbid', $lookupId)->where('trailer', '<>', '')->first(['trailer']); if ($trailer !== null) {
if ($trailer !== null) { return $trailer['trailer'];
return $trailer['trailer'];
}
}
$canonicalImdbId = $this->canonicalImdbId($imdbId);
if ($canonicalImdbId === null) {
return false;
} }
if ($this->traktcheck !== null) { 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'])) { if (($data !== false) && ! empty($data['trailer'])) {
return $data['trailer']; return $data['trailer'];
} }
} }
$trailer = imdb_trailers($canonicalImdbId); $trailer = imdb_trailers($sanitized);
if ($trailer) { if ($trailer) {
MovieInfo::query()->whereIn('imdbid', $lookupIds)->update(['trailer' => $trailer]); MovieInfo::query()->where('imdbid', $sanitized)->update(['trailer' => $trailer]);
return $trailer; return $trailer;
} }
@@ -165,41 +151,10 @@ class MovieService
return false; return false;
} }
private function movieInfoLookupIds(int|string|null $imdbId): array /**
{ * Sanitize an IMDB ID to its raw numeric string, preserving leading zeros.
$canonicalImdbId = $this->canonicalImdbId($imdbId); * Returns null if the ID is empty or all zeros.
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;
}
private function sanitizeImdbId(int|string|null $imdbId): ?string private function sanitizeImdbId(int|string|null $imdbId): ?string
{ {
if ($imdbId === null || $imdbId === '') { if ($imdbId === null || $imdbId === '') {
@@ -207,7 +162,7 @@ class MovieService
} }
$sanitizedImdbId = preg_replace('/\D/', '', trim((string) $imdbId)); $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; return null;
} }
@@ -1013,12 +968,12 @@ class MovieService
public function doMovieUpdate(string $buffer, string $service, int $id, int $processImdb = 1): string|false public function doMovieUpdate(string $buffer, string $service, int $id, int $processImdb = 1): string|false
{ {
$existingImdbId = Release::query()->where('id', $id)->value('imdbid'); $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; return $existingImdbId;
} }
$imdbId = false; $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']; $imdbId = $hits['imdbid'];
} }
@@ -1032,7 +987,7 @@ class MovieService
$movieInfoId = MovieInfo::query()->where('imdbid', $imdbId)->first(['id']); $movieInfoId = MovieInfo::query()->where('imdbid', $imdbId)->first(['id']);
Release::query()->where('id', $id)->update([ Release::query()->where('id', $id)->update([
'imdbid' => imdb_id_pad($imdbId), 'imdbid' => $imdbId,
'movieinfo_id' => $movieInfoId !== null ? $movieInfoId['id'] : null, 'movieinfo_id' => $movieInfoId !== null ? $movieInfoId['id'] : null,
]); ]);
@@ -1047,7 +1002,7 @@ class MovieService
$info = $this->updateMovieInfo($imdbId); $info = $this->updateMovieInfo($imdbId);
if ($info === false) { if ($info === false) {
Release::query()->where('id', $id)->update(['imdbid' => imdb_id_pad('0000000')]); Release::query()->where('id', $id)->update(['imdbid' => '']);
} elseif ($info === true) { } elseif ($info === true) {
$freshMovieInfo = MovieInfo::query()->where('imdbid', $imdbId)->first(['id']); $freshMovieInfo = MovieInfo::query()->where('imdbid', $imdbId)->first(['id']);
@@ -1084,7 +1039,10 @@ class MovieService
$query = Release::query() $query = Release::query()
->select(['searchname', 'id']) ->select(['searchname', 'id'])
->whereBetween('categories_id', [Category::MOVIE_ROOT, Category::MOVIE_OTHER]) ->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 !== '') { if ($groupID !== '') {
$query->where('groups_id', $groupID); $query->where('groups_id', $groupID);
@@ -1135,7 +1093,13 @@ class MovieService
continue; continue;
} else { } 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 ($releaseCheck) {
if ($this->echooutput) { if ($this->echooutput) {
cli()->info('Release already has IMDB ID, skipping'); cli()->info('Release already has IMDB ID, skipping');
@@ -1162,7 +1126,7 @@ class MovieService
} }
foreach (array_chunk($failedIDs, 100) as $chunk) { 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) // 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']; return ['showid' => trim($hits[1]), 'site' => 'imdb'];
} }
@@ -1174,7 +1174,7 @@ class NfoService
$ids = []; $ids = [];
// IMDB // 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) { foreach ($matches[1] as $id) {
$ids[] = ['id' => $id, 'source' => 'imdb']; $ids[] = ['id' => $id, 'source' => 'imdb'];
} }
+1 -1
View File
@@ -841,7 +841,7 @@ class ReleaseRemoverService
FROM releases r FROM releases r
LEFT JOIN release_files rf ON r.id = rf.releases_id LEFT JOIN release_files rf ON r.id = rf.releases_id
WHERE r.categories_id IN ({$categories}) 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.nfostatus = 1
AND r.haspreview = 0 AND r.haspreview = 0
AND r.jpgstatus = 0 AND r.jpgstatus = 0
+16 -8
View File
@@ -297,7 +297,11 @@ class ReleaseSearchService
$externalIds = []; $externalIds = [];
if (! empty($siteIdArr)) { if (! empty($siteIdArr)) {
foreach ($siteIdArr as $column => $id) { 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 // Map column names to search index field names
$fieldName = match ($column) { $fieldName = match ($column) {
'tvdb' => 'tvdb', 'tvdb' => 'tvdb',
@@ -309,7 +313,7 @@ class ReleaseSearchService
default => null, default => null,
}; };
if ($fieldName) { 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 // OPTIMIZATION: Try to find releases using search index external IDs first
$externalIds = []; $externalIds = [];
foreach ($siteIdArr as $column => $Id) { 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) { $fieldName = match ($column) {
'tvdb' => 'tvdb', 'tvdb' => 'tvdb',
'trakt' => 'traktid', 'trakt' => 'traktid',
@@ -559,7 +567,7 @@ class ReleaseSearchService
default => null, default => null,
}; };
if ($fieldName) { 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 * @param array<string, mixed> $excludedCategories
* @return Collection|mixed * @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); $searchLimit = $this->determineSearchCandidateLimit($offset, $limit);
$searchResult = []; $searchResult = [];
@@ -767,7 +775,7 @@ class ReleaseSearchService
// OPTIMIZATION: If we have external IDs, use the search index to find releases directly // 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 // This avoids expensive database JOINs by using indexed external ID fields in releases_rt
$externalIds = []; $externalIds = [];
if ($imDbId !== -1 && $imDbId > 0) { if ($imDbId !== '' && imdb_id_is_valid($imDbId)) {
$externalIds['imdbid'] = $imDbId; $externalIds['imdbid'] = $imDbId;
} }
if ($tmDbId !== -1 && $tmDbId > 0) { if ($tmDbId !== -1 && $tmDbId > 0) {
@@ -822,8 +830,8 @@ class ReleaseSearchService
$needsMovieJoin = false; $needsMovieJoin = false;
if (empty($searchResult) && ! empty($externalIds)) { if (empty($searchResult) && ! empty($externalIds)) {
$needsMovieJoin = true; $needsMovieJoin = true;
if ($imDbId !== -1 && $imDbId > 0) { if ($imDbId !== '' && imdb_id_is_valid($imDbId)) {
$conditions[] = sprintf('r.imdbid = %d', $imDbId); $conditions[] = sprintf('r.imdbid = %s', escapeString($imDbId));
} }
if ($tmDbId !== -1 && $tmDbId > 0) { if ($tmDbId !== -1 && $tmDbId > 0) {
$conditions[] = sprintf('m.tmdbid = %d', $tmDbId); $conditions[] = sprintf('m.tmdbid = %d', $tmDbId);
+2 -2
View File
@@ -131,7 +131,7 @@ class PostProcessRunner extends BaseRunner
SELECT id SELECT id
FROM releases FROM releases
WHERE categories_id BETWEEN 2000 AND 2999 WHERE categories_id BETWEEN 2000 AND 2999
AND imdbid IS NULL AND (imdbid IS NULL OR imdbid IN (\'\', \'0\', \'0000000\', \'00000000\'))
'.$condLookup.' '.$condRenamedOnly.' '.$condLookup.' '.$condRenamedOnly.'
LIMIT 1'; LIMIT 1';
if (count(DB::select($checkSql)) === 0) { if (count(DB::select($checkSql)) === 0) {
@@ -145,7 +145,7 @@ class PostProcessRunner extends BaseRunner
SELECT DISTINCT LEFT(leftguid, 1) AS id, '.$renamedFlag.' AS renamed SELECT DISTINCT LEFT(leftguid, 1) AS id, '.$renamedFlag.' AS renamed
FROM releases FROM releases
WHERE categories_id BETWEEN 2000 AND 2999 WHERE categories_id BETWEEN 2000 AND 2999
AND imdbid IS NULL AND (imdbid IS NULL OR imdbid IN (\'\', \'0\', \'0000000\', \'00000000\'))
'.$condLookup.' '.$condRenamedOnly.' '.$condLookup.' '.$condRenamedOnly.'
LIMIT 16'; LIMIT 16';
$queue = DB::select($sql); $queue = DB::select($sql);
@@ -1059,6 +1059,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'releases.searchname', 'releases.searchname',
'releases.fromname', 'releases.fromname',
'releases.categories_id', 'releases.categories_id',
'releases.imdbid',
DB::raw('IFNULL(GROUP_CONCAT(rf.name SEPARATOR " "),"") filename'), DB::raw('IFNULL(GROUP_CONCAT(rf.name SEPARATOR " "),"") filename'),
]) ])
->groupBy('releases.id') ->groupBy('releases.id')
@@ -1080,6 +1081,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'plainsearchname' => $searchNameDotless, 'plainsearchname' => $searchNameDotless,
'fromname' => $release->fromname, 'fromname' => $release->fromname,
'categories_id' => $release->categories_id, 'categories_id' => $release->categories_id,
'imdbid' => (string) ($release->imdbid ?? ''),
'filename' => $release->filename, // @phpstan-ignore property.notFound 'filename' => $release->filename, // @phpstan-ignore property.notFound
], ],
'doc_as_upsert' => true, 'doc_as_upsert' => true,
@@ -1681,6 +1683,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'plainsearchname' => $searchNameDotless, 'plainsearchname' => $searchNameDotless,
'fromname' => $parameters['fromname'] ?? '', 'fromname' => $parameters['fromname'] ?? '',
'categories_id' => $parameters['categories_id'] ?? 0, 'categories_id' => $parameters['categories_id'] ?? 0,
'imdbid' => (string) ($parameters['imdbid'] ?? ''),
'filename' => $parameters['filename'] ?? '', 'filename' => $parameters['filename'] ?? '',
'add_date' => now()->format('Y-m-d H:i:s'), 'add_date' => now()->format('Y-m-d H:i:s'),
'post_date' => $parameters['postdate'] ?? 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 = [ $document = [
'id' => $parameters['id'], 'id' => $parameters['id'],
'imdbid' => (int) ($parameters['imdbid'] ?? 0), 'imdbid' => (string) ($parameters['imdbid'] ?? ''),
'tmdbid' => (int) ($parameters['tmdbid'] ?? 0), 'tmdbid' => (int) ($parameters['tmdbid'] ?? 0),
'traktid' => (int) ($parameters['traktid'] ?? 0), 'traktid' => (int) ($parameters['traktid'] ?? 0),
'title' => (string) ($parameters['title'] ?? ''), 'title' => (string) ($parameters['title'] ?? ''),
@@ -1971,7 +1974,7 @@ class ElasticSearchDriver implements SearchDriverInterface
$params['body'][] = [ $params['body'][] = [
'id' => $movie['id'], 'id' => $movie['id'],
'imdbid' => (int) ($movie['imdbid'] ?? 0), 'imdbid' => (string) ($movie['imdbid'] ?? ''),
'tmdbid' => (int) ($movie['tmdbid'] ?? 0), 'tmdbid' => (int) ($movie['tmdbid'] ?? 0),
'traktid' => (int) ($movie['traktid'] ?? 0), 'traktid' => (int) ($movie['traktid'] ?? 0),
'title' => (string) ($movie['title'] ?? ''), 'title' => (string) ($movie['title'] ?? ''),
@@ -2091,7 +2094,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'body' => [ 'body' => [
'query' => [ 'query' => [
'term' => [ 'term' => [
$field => (int) $value, $field => $field === 'imdbid' ? (string) $value : (int) $value,
], ],
], ],
'size' => 1, 'size' => 1,
@@ -2141,7 +2144,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'trakt' => (int) ($parameters['trakt'] ?? 0), 'trakt' => (int) ($parameters['trakt'] ?? 0),
'tvmaze' => (int) ($parameters['tvmaze'] ?? 0), 'tvmaze' => (int) ($parameters['tvmaze'] ?? 0),
'tvrage' => (int) ($parameters['tvrage'] ?? 0), 'tvrage' => (int) ($parameters['tvrage'] ?? 0),
'imdb' => (int) ($parameters['imdb'] ?? 0), 'imdb' => (string) ($parameters['imdb'] ?? ''),
'tmdb' => (int) ($parameters['tmdb'] ?? 0), 'tmdb' => (int) ($parameters['tmdb'] ?? 0),
'started' => (string) ($parameters['started'] ?? ''), 'started' => (string) ($parameters['started'] ?? ''),
'type' => (int) ($parameters['type'] ?? 0), 'type' => (int) ($parameters['type'] ?? 0),
@@ -2262,7 +2265,7 @@ class ElasticSearchDriver implements SearchDriverInterface
'trakt' => (int) ($tvShow['trakt'] ?? 0), 'trakt' => (int) ($tvShow['trakt'] ?? 0),
'tvmaze' => (int) ($tvShow['tvmaze'] ?? 0), 'tvmaze' => (int) ($tvShow['tvmaze'] ?? 0),
'tvrage' => (int) ($tvShow['tvrage'] ?? 0), 'tvrage' => (int) ($tvShow['tvrage'] ?? 0),
'imdb' => (int) ($tvShow['imdb'] ?? 0), 'imdb' => (string) ($tvShow['imdb'] ?? ''),
'tmdb' => (int) ($tvShow['tmdb'] ?? 0), 'tmdb' => (int) ($tvShow['tmdb'] ?? 0),
'started' => (string) ($tvShow['started'] ?? ''), 'started' => (string) ($tvShow['started'] ?? ''),
'type' => (int) ($tvShow['type'] ?? 0), 'type' => (int) ($tvShow['type'] ?? 0),
@@ -2429,7 +2432,7 @@ class ElasticSearchDriver implements SearchDriverInterface
$shouldClauses = []; $shouldClauses = [];
foreach ($externalIds as $field => $value) { foreach ($externalIds as $field => $value) {
if (! empty($value) && in_array($field, ['imdbid', 'tmdbid', 'traktid', 'tvdb', 'tvmaze', 'tvrage'])) { 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), 'categories_id' => (int) ($parameters['categories_id'] ?? 0),
'filename' => $parameters['filename'] ?? '', 'filename' => $parameters['filename'] ?? '',
// External media IDs for efficient searching // External media IDs for efficient searching
'imdbid' => (int) ($parameters['imdbid'] ?? 0), 'imdbid' => (string) ($parameters['imdbid'] ?? ''),
'tmdbid' => (int) ($parameters['tmdbid'] ?? 0), 'tmdbid' => (int) ($parameters['tmdbid'] ?? 0),
'traktid' => (int) ($parameters['traktid'] ?? 0), 'traktid' => (int) ($parameters['traktid'] ?? 0),
'tvdb' => (int) ($parameters['tvdb'] ?? 0), 'tvdb' => (int) ($parameters['tvdb'] ?? 0),
@@ -304,7 +304,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'fromname' => (string) ($release['fromname'] ?? ''), 'fromname' => (string) ($release['fromname'] ?? ''),
'categories_id' => (int) ($release['categories_id'] ?? 0), 'categories_id' => (int) ($release['categories_id'] ?? 0),
'filename' => (string) ($release['filename'] ?? ''), 'filename' => (string) ($release['filename'] ?? ''),
'imdbid' => (int) ($release['imdbid'] ?? 0), 'imdbid' => (string) ($release['imdbid'] ?? ''),
'tmdbid' => (int) ($release['tmdbid'] ?? 0), 'tmdbid' => (int) ($release['tmdbid'] ?? 0),
'traktid' => (int) ($release['traktid'] ?? 0), 'traktid' => (int) ($release['traktid'] ?? 0),
'tvdb' => (int) ($release['tvdb'] ?? 0), 'tvdb' => (int) ($release['tvdb'] ?? 0),
@@ -714,7 +714,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'filename' => ['type' => 'text'], 'filename' => ['type' => 'text'],
'categories_id' => ['type' => 'integer'], 'categories_id' => ['type' => 'integer'],
// External media IDs for efficient searching // External media IDs for efficient searching
'imdbid' => ['type' => 'integer'], 'imdbid' => ['type' => 'string'],
'tmdbid' => ['type' => 'integer'], 'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'], 'traktid' => ['type' => 'integer'],
'tvdb' => ['type' => 'integer'], 'tvdb' => ['type' => 'integer'],
@@ -751,7 +751,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'min_infix_len' => 2, 'min_infix_len' => 2,
], ],
'columns' => [ 'columns' => [
'imdbid' => ['type' => 'integer'], 'imdbid' => ['type' => 'string'],
'tmdbid' => ['type' => 'integer'], 'tmdbid' => ['type' => 'integer'],
'traktid' => ['type' => 'integer'], 'traktid' => ['type' => 'integer'],
'title' => ['type' => 'text'], 'title' => ['type' => 'text'],
@@ -779,7 +779,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'trakt' => ['type' => 'integer'], 'trakt' => ['type' => 'integer'],
'tvmaze' => ['type' => 'integer'], 'tvmaze' => ['type' => 'integer'],
'tvrage' => ['type' => 'integer'], 'tvrage' => ['type' => 'integer'],
'imdb' => ['type' => 'integer'], 'imdb' => ['type' => 'string'],
'tmdb' => ['type' => 'integer'], 'tmdb' => ['type' => 'integer'],
'started' => ['type' => 'text'], 'started' => ['type' => 'text'],
'type' => ['type' => 'integer'], 'type' => ['type' => 'integer'],
@@ -1611,7 +1611,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
try { try {
$document = [ $document = [
'imdbid' => (int) ($parameters['imdbid'] ?? 0), 'imdbid' => (string) ($parameters['imdbid'] ?? ''),
'tmdbid' => (int) ($parameters['tmdbid'] ?? 0), 'tmdbid' => (int) ($parameters['tmdbid'] ?? 0),
'traktid' => (int) ($parameters['traktid'] ?? 0), 'traktid' => (int) ($parameters['traktid'] ?? 0),
'title' => (string) ($parameters['title'] ?? ''), 'title' => (string) ($parameters['title'] ?? ''),
@@ -1713,7 +1713,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
$documents[] = [ $documents[] = [
'id' => $movie['id'], 'id' => $movie['id'],
'imdbid' => (int) ($movie['imdbid'] ?? 0), 'imdbid' => (string) ($movie['imdbid'] ?? ''),
'tmdbid' => (int) ($movie['tmdbid'] ?? 0), 'tmdbid' => (int) ($movie['tmdbid'] ?? 0),
'traktid' => (int) ($movie['traktid'] ?? 0), 'traktid' => (int) ($movie['traktid'] ?? 0),
'title' => (string) ($movie['title'] ?? ''), 'title' => (string) ($movie['title'] ?? ''),
@@ -1818,7 +1818,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'trakt' => (int) ($parameters['trakt'] ?? 0), 'trakt' => (int) ($parameters['trakt'] ?? 0),
'tvmaze' => (int) ($parameters['tvmaze'] ?? 0), 'tvmaze' => (int) ($parameters['tvmaze'] ?? 0),
'tvrage' => (int) ($parameters['tvrage'] ?? 0), 'tvrage' => (int) ($parameters['tvrage'] ?? 0),
'imdb' => (int) ($parameters['imdb'] ?? 0), 'imdb' => (string) ($parameters['imdb'] ?? ''),
'tmdb' => (int) ($parameters['tmdb'] ?? 0), 'tmdb' => (int) ($parameters['tmdb'] ?? 0),
'started' => (string) ($parameters['started'] ?? ''), 'started' => (string) ($parameters['started'] ?? ''),
'type' => (int) ($parameters['type'] ?? 0), 'type' => (int) ($parameters['type'] ?? 0),
@@ -1919,7 +1919,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
'trakt' => (int) ($tvShow['trakt'] ?? 0), 'trakt' => (int) ($tvShow['trakt'] ?? 0),
'tvmaze' => (int) ($tvShow['tvmaze'] ?? 0), 'tvmaze' => (int) ($tvShow['tvmaze'] ?? 0),
'tvrage' => (int) ($tvShow['tvrage'] ?? 0), 'tvrage' => (int) ($tvShow['tvrage'] ?? 0),
'imdb' => (int) ($tvShow['imdb'] ?? 0), 'imdb' => (string) ($tvShow['imdb'] ?? ''),
'tmdb' => (int) ($tvShow['tmdb'] ?? 0), 'tmdb' => (int) ($tvShow['tmdb'] ?? 0),
'started' => (string) ($tvShow['started'] ?? ''), 'started' => (string) ($tvShow['started'] ?? ''),
'type' => (int) ($tvShow['type'] ?? 0), 'type' => (int) ($tvShow['type'] ?? 0),
@@ -2026,7 +2026,7 @@ class ManticoreSearchDriver implements SearchDriverInterface
// Add filters for each external ID provided // Add filters for each external ID provided
foreach ($externalIds as $field => $value) { foreach ($externalIds as $field => $value) {
if (! empty($value) && in_array($field, ['imdbid', 'tmdbid', 'traktid', 'tvdb', 'tvmaze', 'tvrage'])) { 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 // Try to find the show by IMDB ID
$imdbId = $ids['imdb'] ?? 0; $imdbId = $ids['imdb'] ?? '';
if (! empty($imdbId)) { if (! empty($imdbId)) {
// Format IMDB ID with tt prefix if it's numeric $imdbFormatted = str_starts_with((string) $imdbId, 'tt')
$imdbFormatted = is_numeric($imdbId) ? (string) $imdbId
? 'tt'.str_pad((string) $imdbId, 8, '0', STR_PAD_LEFT) : 'tt'.(string) $imdbId;
: (string) $imdbId;
$show = $this->findTvByExternalId($imdbFormatted, 'imdb_id'); $show = $this->findTvByExternalId($imdbFormatted, 'imdb_id');
if ($show !== null && isset($show['id'])) { if ($show !== null && isset($show['id'])) {
@@ -452,9 +451,9 @@ class TmdbClient
} elseif ($source === 'tvdb' && is_numeric($id) && (int) $id > 0) { } elseif ($source === 'tvdb' && is_numeric($id) && (int) $id > 0) {
$show = $this->findTvByExternalId((string) $id, 'tvdb_id'); $show = $this->findTvByExternalId((string) $id, 'tvdb_id');
} elseif ($source === 'imdb') { } elseif ($source === 'imdb') {
$imdbFormatted = is_numeric($id) $imdbFormatted = str_starts_with((string) $id, 'tt')
? 'tt'.str_pad((string) $id, 8, '0', STR_PAD_LEFT) ? (string) $id
: (string) $id; : 'tt'.(string) $id;
$show = $this->findTvByExternalId($imdbFormatted, 'imdb_id'); $show = $this->findTvByExternalId($imdbFormatted, 'imdb_id');
} }
@@ -471,11 +470,11 @@ class TmdbClient
$externalIds = self::getArray($show, 'external_ids'); $externalIds = self::getArray($show, 'external_ids');
// Parse IMDB ID to numeric // Parse IMDB ID to raw numeric string
$imdbId = 0; $imdbId = '';
if (! empty($externalIds['imdb_id'])) { if (! empty($externalIds['imdb_id'])) {
preg_match('/tt(?P<imdbid>\d{6,8})$/i', $externalIds['imdb_id'], $imdb); preg_match('/tt(?P<imdbid>\d{6,})$/i', (string) $externalIds['imdb_id'], $imdb);
$imdbId = (int) ($imdb['imdbid'] ?? 0); $imdbId = (string) ($imdb['imdbid'] ?? '');
} }
return [ return [
+1 -1
View File
@@ -307,7 +307,7 @@ class Tmux
SELECT 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 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 = %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 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 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, 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']); ->first(['tmdb', 'tvdb', 'imdb']);
if ($result === null) { if ($result === null) {
return ['tmdb' => 0, 'tvdb' => 0, 'imdb' => 0]; return ['tmdb' => 0, 'tvdb' => 0, 'imdb' => ''];
} }
return [ return [
'tmdb' => (int) ($result->tmdb ?? 0), 'tmdb' => (int) ($result->tmdb ?? 0),
'tvdb' => (int) ($result->tvdb ?? 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 ? 'https://image.tmdb.org/t/p/w500'.$posterPath
: ''; : '';
$imdbId = 0; $imdbId = '';
$externalIds = TmdbClient::getArray($show, 'external_ids'); $externalIds = TmdbClient::getArray($show, 'external_ids');
if (! empty($externalIds['imdb_id'])) { if (! empty($externalIds['imdb_id'])) {
preg_match('/tt(?P<imdbid>\d{6,8})$/i', (string) $externalIds['imdb_id'], $imdb); preg_match('/tt(?P<imdbid>\d{6,})$/i', (string) $externalIds['imdb_id'], $imdb);
$imdbId = $imdb['imdbid'] ?? 0; $imdbId = (string) ($imdb['imdbid'] ?? '');
} }
$originCountry = TmdbClient::getArray($show, 'origin_country'); $originCountry = TmdbClient::getArray($show, 'origin_country');
@@ -558,7 +558,7 @@ class TmdbProvider extends AbstractTvProvider
} }
// Try IMDB ID // Try IMDB ID
if (! empty($imdbId) && $imdbId > 0) { if (! empty($imdbId) && imdb_id_is_valid($imdbId)) {
$ids = $traktService->lookupShowIds($imdbId, 'imdb'); $ids = $traktService->lookupShowIds($imdbId, 'imdb');
if ($ids !== null && ! empty($ids['trakt'])) { if ($ids !== null && ! empty($ids['trakt'])) {
return (int) $ids['trakt']; return (int) $ids['trakt'];
@@ -254,14 +254,14 @@ class TraktProvider extends AbstractTvProvider
->first(['trakt', 'tmdb', 'tvdb', 'imdb']); ->first(['trakt', 'tmdb', 'tvdb', 'imdb']);
if ($result === null) { if ($result === null) {
return ['trakt' => 0, 'tmdb' => 0, 'tvdb' => 0, 'imdb' => 0]; return ['trakt' => 0, 'tmdb' => 0, 'tvdb' => 0, 'imdb' => ''];
} }
return [ return [
'trakt' => (int) ($result->trakt ?? 0), 'trakt' => (int) ($result->trakt ?? 0),
'tmdb' => (int) ($result->tmdb ?? 0), 'tmdb' => (int) ($result->tmdb ?? 0),
'tvdb' => (int) ($result->tvdb ?? 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 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->posterUrl = $show['images']['poster']['thumb'] ?? '';
$this->fanartUrl = $show['images']['fanart']['thumb'] ?? ''; $this->fanartUrl = $show['images']['fanart']['thumb'] ?? '';
$this->localizedTZ = $show['airs']['timezone'] ?? ''; $this->localizedTZ = $show['airs']['timezone'] ?? '';
$imdbId = $imdb['imdbid'] ?? 0; $imdbId = (string) ($imdb['imdbid'] ?? '');
$tvdbId = $show['ids']['tvdb'] ?? 0; $tvdbId = $show['ids']['tvdb'] ?? 0;
// Look up TVMaze ID using TVDB or IMDB // Look up TVMaze ID using TVDB or IMDB
@@ -438,8 +438,8 @@ class TraktProvider extends AbstractTvProvider
} }
// Try IMDB ID as fallback // Try IMDB ID as fallback
if (! empty($imdbId) && $imdbId > 0) { if (! empty($imdbId) && imdb_id_is_valid($imdbId)) {
$imdbFormatted = 'tt'.str_pad((string) $imdbId, 8, '0', STR_PAD_LEFT); $imdbFormatted = 'tt'.(string) $imdbId;
$result = $tvmazeClient->getShowBySiteID('imdb', $imdbFormatted); $result = $tvmazeClient->getShowBySiteID('imdb', $imdbFormatted);
if ($result !== null && isset($result->id)) { if ($result !== null && isset($result->id)) {
return (int) $result->id; return (int) $result->id;
@@ -444,12 +444,12 @@ class TvMazeProvider extends AbstractTvProvider
$this->posterUrl = (string) ($show->mediumImage ?? ''); $this->posterUrl = (string) ($show->mediumImage ?? '');
$tvdbId = (int) ($show->externalIDs['thetvdb'] ?? 0); $tvdbId = (int) ($show->externalIDs['thetvdb'] ?? 0);
$imdbId = 0; $imdbId = '';
// Extract IMDB ID if available // Extract IMDB ID if available
if (! empty($show->externalIDs['imdb'])) { if (! empty($show->externalIDs['imdb'])) {
preg_match('/tt(?P<imdbid>\d{6,9})$/i', (string) $show->externalIDs['imdb'], $imdb); preg_match('/tt(?P<imdbid>\d{6,})$/i', (string) $show->externalIDs['imdb'], $imdb);
$imdbId = (int) ($imdb['imdbid'] ?? 0); $imdbId = (string) ($imdb['imdbid'] ?? '');
} }
// Look up TMDB and Trakt IDs using available external IDs // 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 // 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'); $tmdbIds = $tmdbClient->lookupTvShowIds($imdbId, 'imdb');
if ($tmdbIds !== null) { if ($tmdbIds !== null) {
$result['tmdb'] = $tmdbIds['tmdb'] ?? 0; $result['tmdb'] = $tmdbIds['tmdb'] ?? 0;
@@ -403,12 +403,12 @@ class TvdbProvider extends AbstractTvProvider
} }
} }
$imdbId = 0; $imdbId = '';
$imdbIdObj = null; $imdbIdObj = null;
try { try {
$imdbIdObj = $this->client->series()->extended((int) $show->tvdb_id); $imdbIdObj = $this->client->series()->extended((int) $show->tvdb_id);
preg_match('/tt(?P<imdbid>\d{6,9})$/i', (string) ($imdbIdObj->getIMDBId() ?? ''), $imdb); preg_match('/tt(?P<imdbid>\d{6,})$/i', (string) ($imdbIdObj->getIMDBId() ?? ''), $imdb);
$imdbId = $imdb['imdbid'] ?? 0; $imdbId = (string) ($imdb['imdbid'] ?? '');
} catch (ResourceNotFoundException $e) { } catch (ResourceNotFoundException $e) {
cli()->error('Show ImdbId not found on TVDB'); cli()->error('Show ImdbId not found on TVDB');
} catch (\Exception) { } catch (\Exception) {
@@ -476,7 +476,7 @@ class TvdbProvider extends AbstractTvProvider
} }
// Try IMDB ID as fallback // Try IMDB ID as fallback
if (! empty($imdbId) && $imdbId > 0) { if (! empty($imdbId) && imdb_id_is_valid($imdbId)) {
$traktIds = $traktService->lookupShowIds($imdbId, 'imdb'); $traktIds = $traktService->lookupShowIds($imdbId, 'imdb');
if ($traktIds !== null && ! empty($traktIds['trakt'])) { if ($traktIds !== null && ! empty($traktIds['trakt'])) {
$result['trakt'] = (int) $traktIds['trakt']; $result['trakt'] = (int) $traktIds['trakt'];
+1 -1
View File
@@ -141,6 +141,6 @@ class ApiTransformer extends TransformerAbstract
*/ */
protected function nullIfZero($value) 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"> <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> <i class="icon_cart fa fa-shopping-basket"></i>
</a> </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) }}" <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" 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"> 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"> <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> <i class="icon_cart fa fa-shopping-basket"></i>
</a> </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) }}" <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" 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"> 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"> <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> <i class="icon_cart fa fa-shopping-basket"></i>
</a> </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) }}" <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" 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"> title="Add to My Movies">
+7 -7
View File
@@ -90,7 +90,7 @@ class MovieServiceTest extends ImdbScraperTestCase
} }
#[Test] #[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(); Cache::flush();
@@ -98,20 +98,20 @@ class MovieServiceTest extends ImdbScraperTestCase
$service->echooutput = false; $service->echooutput = false;
$service->update([ $service->update([
'imdbid' => '8169446', 'imdbid' => '0137523',
'title' => 'Example Movie', 'title' => 'Example Movie',
'year' => '2024', 'year' => '2024',
]); ]);
$movie = $service->getMovieInfo('08169446'); $movie = $service->getMovieInfo('0137523');
$this->assertNotNull($movie); $this->assertNotNull($movie);
$this->assertSame('8169446', $movie->imdbid); $this->assertSame('0137523', $movie->imdbid);
$this->assertSame('Example Movie', $movie->title); $this->assertSame('Example Movie', $movie->title);
} }
#[Test] #[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(); Cache::flush();
@@ -119,13 +119,13 @@ class MovieServiceTest extends ImdbScraperTestCase
$service->echooutput = false; $service->echooutput = false;
$service->update([ $service->update([
'imdbid' => '8169446', 'imdbid' => '0137523',
'title' => 'Example Movie', 'title' => 'Example Movie',
'year' => '2024', 'year' => '2024',
'trailer' => 'https://example.test/embed/trailer', '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'));
} }
/** /**