Skip to content

Commit 20f3aca

Browse files
authored
Fix movie wrongly shown as available when a series shares its TMDB ID (#5432)
TheMovieDb uses separate ID namespaces for movies and TV shows, so the same id can refer to both a movie and a series. The Emby/Jellyfin availability lookups matched content purely by provider id without checking the media type, so a movie was marked as available (and linked to "Watch on ...") whenever a series with the same TMDB id existed on the server. Plex already disambiguated by media type, which is why it was unaffected. Only accept media server content whose type matches the item being searched for, both in the shared search availability rule and in the background Emby/Jellyfin availability checkers. Fixes #5430
1 parent 6ebfab4 commit 20f3aca

4 files changed

Lines changed: 43 additions & 12 deletions

File tree

src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,5 +117,25 @@ public async Task Movie_ShouldBe_NotAvailable_WhenNotFoundInJellyfin()
117117
Assert.True(result.Success);
118118
Assert.False(search.Available);
119119
}
120+
121+
[Test]
122+
public async Task Movie_ShouldNotBe_Available_WhenOnlyASeriesSharesTheMovieDbId()
123+
{
124+
// TheMovieDb has separate ID namespaces for movies and TV, so a series can share the same id.
125+
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new JellyfinContent
126+
{
127+
TheMovieDbId = "87428",
128+
Quality = "1080",
129+
Type = MediaType.Series
130+
});
131+
var search = new SearchMovieViewModel()
132+
{
133+
TheMovieDbId = "87428",
134+
};
135+
var result = await Rule.Execute(search);
136+
137+
Assert.True(result.Success);
138+
Assert.False(search.Available);
139+
}
120140
}
121141
}

src/Ombi.Core/Rule/Rules/Search/MediaServerAvailabilityRule.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,19 @@ protected static async Task<ContentLookupResult> FindContentByProviderIds(
166166
var result = new ContentLookupResult();
167167
IMediaServerContent item = null;
168168

169+
// TheMovieDb (and potentially other providers) use separate ID namespaces for
170+
// movies and TV shows, so the same ID can refer to both a movie and a series.
171+
// Only accept content whose type matches the thing we are searching for, otherwise
172+
// a movie can be wrongly marked as available because a series shares its ID (and vice versa).
173+
var expectedType = obj is SearchMovieViewModel ? MediaType.Movie : MediaType.Series;
174+
bool Matches(IMediaServerContent content) => content != null && content.Type == expectedType;
175+
169176
if (obj.ImdbId.HasValue())
170177
{
171-
item = await getByImdbId(obj.ImdbId);
172-
if (item != null)
178+
var match = await getByImdbId(obj.ImdbId);
179+
if (Matches(match))
173180
{
181+
item = match;
174182
result.UseImdb = true;
175183
}
176184
}
@@ -179,28 +187,31 @@ protected static async Task<ContentLookupResult> FindContentByProviderIds(
179187
{
180188
if (lookupById && obj.Id > 0)
181189
{
182-
item = await getByTheMovieDbId(obj.Id.ToString());
183-
if (item != null)
190+
var match = await getByTheMovieDbId(obj.Id.ToString());
191+
if (Matches(match))
184192
{
193+
item = match;
185194
obj.TheMovieDbId = obj.Id.ToString();
186195
result.UseTheMovieDb = true;
187196
}
188197
}
189198

190199
if (item == null && obj.TheMovieDbId.HasValue())
191200
{
192-
item = await getByTheMovieDbId(obj.TheMovieDbId);
193-
if (item != null)
201+
var match = await getByTheMovieDbId(obj.TheMovieDbId);
202+
if (Matches(match))
194203
{
204+
item = match;
195205
result.UseTheMovieDb = true;
196206
}
197207
}
198208

199209
if (item == null && obj.TheTvDbId.HasValue())
200210
{
201-
item = await getByTvDbId(obj.TheTvDbId);
202-
if (item != null)
211+
var match = await getByTvDbId(obj.TheTvDbId);
212+
if (Matches(match))
203213
{
214+
item = match;
204215
result.UseTvDb = true;
205216
}
206217
}

src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ private async Task ProcessMovies()
7070
embyContent = await _repo.GetByImdbId(movie.ImdbId);
7171
}
7272

73-
if (embyContent == null)
73+
if (embyContent == null || embyContent.Type != MediaType.Movie)
7474
{
75-
// We don't have this yet
75+
// We don't have this yet (a series may share the same TheMovieDbId, so ensure it's a movie)
7676
continue;
7777
}
7878

src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ private async Task ProcessMovies()
9797
jellyfinContent = await _repo.GetByImdbId(movie.ImdbId);
9898
}
9999

100-
if (jellyfinContent == null)
100+
if (jellyfinContent == null || jellyfinContent.Type != MediaType.Movie)
101101
{
102-
// We don't have this yet
102+
// We don't have this yet (a series may share the same TheMovieDbId, so ensure it's a movie)
103103
continue;
104104
}
105105

0 commit comments

Comments
 (0)