Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(plex-watchlist): Lookup the ID from different sources when Plex d… #4843

Merged
merged 1 commit into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 197 additions & 9 deletions src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using NUnit.Framework;
using Ombi.Api.Plex;
using Ombi.Api.Plex.Models;
using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Models.Requests;
Expand Down Expand Up @@ -55,7 +57,7 @@ public async Task TerminatesWhenPlexIsNotEnabled()
[Test]
public async Task TerminatesWhenWatchlistIsNotEnabled()
{

_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = false });
await _subject.Execute(null);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
Expand Down Expand Up @@ -145,7 +147,7 @@ public async Task FailedWatchListUser_OldToken_ShouldSkip()
[Test]
public async Task NoPlexUsersWithToken()
{

_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
var um = MockHelper.MockUserManager(new List<OmbiUser>
{
Expand All @@ -170,7 +172,7 @@ public async Task NoPlexUsersWithToken()
[Test]
public async Task MultipleUsers()
{

_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
var um = MockHelper.MockUserManager(new List<OmbiUser>
{
Expand All @@ -194,7 +196,7 @@ public async Task MultipleUsers()
[Test]
public async Task MovieRequestFromWatchList_NoGuid()
{

_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{
Expand Down Expand Up @@ -245,7 +247,7 @@ public async Task MovieRequestFromWatchList_NoGuid()
[Test]
public async Task TvRequestFromWatchList_NoGuid()
{

_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{
Expand Down Expand Up @@ -295,7 +297,7 @@ public async Task TvRequestFromWatchList_NoGuid()
[Test]
public async Task MovieRequestFromWatchList_AlreadyRequested()
{

_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{
Expand Down Expand Up @@ -394,7 +396,7 @@ public async Task TvRequestFromWatchList_AlreadyRequested()
[Test]
public async Task MovieRequestFromWatchList_NoTmdbGuid()
{

_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{
Expand Down Expand Up @@ -433,6 +435,7 @@ public async Task MovieRequestFromWatchList_NoTmdbGuid()
});
_mocker.Setup<IMovieRequestEngine, Task<RequestEngineResult>>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()))
.ReturnsAsync(new RequestEngineResult { RequestId = 1 });

await _subject.Execute(_context.Object);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
_mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
Expand All @@ -441,10 +444,195 @@ public async Task MovieRequestFromWatchList_NoTmdbGuid()
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Never);
}

[Test]
public async Task MovieRequestFromWatchList_NoTmdbGuid_LookupFromTdb()
{

_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{
MediaContainer = new PlexWatchlist
{
Metadata = new List<Metadata>
{
new Metadata
{
type = "movie",
ratingKey = "abc"
}
}
}
});
_mocker.Setup<IPlexApi, Task<PlexWatchlistMetadataContainer>>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new PlexWatchlistMetadataContainer
{
MediaContainer = new PlexWatchlistMetadata
{
Metadata = new WatchlistMetadata[]
{
new WatchlistMetadata
{
Guid = new List<PlexGuids>
{
new PlexGuids
{
Id = "imdb://123"
}
}
}
}

}
});
_mocker.Setup<IMovieRequestEngine, Task<RequestEngineResult>>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()))
.ReturnsAsync(new RequestEngineResult { RequestId = 1 });
_mocker.Setup<IMovieDbApi, Task<FindResult>>(x => x.Find("123", ExternalSource.imdb_id)).ReturnsAsync(new FindResult
{
movie_results = new Movie_Results[]
{
new Movie_Results
{
id = 333
}
}
});

await _subject.Execute(_context.Object);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.Is<MovieRequestViewModel>(x => x.TheMovieDbId == 333)), Times.Once);
_mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
_mocker.Verify<IMovieRequestEngine>(x => x.SetUser(It.Is<OmbiUser>(x => x.Id == "abc")), Times.Once);
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Once);
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Once);
_mocker.Verify<IMovieDbApi>(x => x.Find("123", ExternalSource.imdb_id), Times.Once);
}


[Test]
public async Task TvRequestFromWatchList_NoTmdbGuid_LookupFromTdb()
{
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, MonitorAll = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{
MediaContainer = new PlexWatchlist
{
Metadata = new List<Metadata>
{
new Metadata
{
type = "show",
ratingKey = "abc"
}
}
}
});
_mocker.Setup<IPlexApi, Task<PlexWatchlistMetadataContainer>>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new PlexWatchlistMetadataContainer
{
MediaContainer = new PlexWatchlistMetadata
{
Metadata = new WatchlistMetadata[]
{
new WatchlistMetadata
{
Guid = new List<PlexGuids>
{
new PlexGuids
{
Id = "imdbid://123"
}
}
}
}

}
});

_mocker.Setup<IMovieDbApi, Task<FindResult>>(x => x.Find("123", ExternalSource.imdb_id)).ReturnsAsync(new FindResult
{
tv_results = new TvResults[]
{
new TvResults
{
id = 333
}
}
});
_mocker.Setup<ITvRequestEngine, Task<RequestEngineResult>>(x => x.RequestTvShow(It.IsAny<TvRequestViewModelV2>()))
.ReturnsAsync(new RequestEngineResult { RequestId = 1 });
await _subject.Execute(_context.Object);
_mocker.Verify<ITvRequestEngine>(x => x.RequestTvShow(It.Is<TvRequestViewModelV2>(x => x.TheMovieDbId == 333 && x.LatestSeason == false && x.RequestAll == true)), Times.Once);
_mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
_mocker.Verify<ITvRequestEngine>(x => x.SetUser(It.Is<OmbiUser>(x => x.Id == "abc")), Times.Once);
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Once);
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Once);
_mocker.Verify<IMovieDbApi>(x => x.Find("123", ExternalSource.imdb_id), Times.Once);
}

[Test]
public async Task TvRequestFromWatchList_NoTmdbGuid_LookupFromTdb_ViaTvDb()
{
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, MonitorAll = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{
MediaContainer = new PlexWatchlist
{
Metadata = new List<Metadata>
{
new Metadata
{
type = "show",
ratingKey = "abc"
}
}
}
});
_mocker.Setup<IPlexApi, Task<PlexWatchlistMetadataContainer>>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new PlexWatchlistMetadataContainer
{
MediaContainer = new PlexWatchlistMetadata
{
Metadata = new WatchlistMetadata[]
{
new WatchlistMetadata
{
Guid = new List<PlexGuids>
{
new PlexGuids
{
Id = "thetvdb://123"
}
}
}
}

}
});

_mocker.Setup<IMovieDbApi, Task<FindResult>>(x => x.Find("123", ExternalSource.tvdb_id)).ReturnsAsync(new FindResult
{
tv_results = new TvResults[]
{
new TvResults
{
id = 333
}
}
});
_mocker.Setup<ITvRequestEngine, Task<RequestEngineResult>>(x => x.RequestTvShow(It.IsAny<TvRequestViewModelV2>()))
.ReturnsAsync(new RequestEngineResult { RequestId = 1 });
await _subject.Execute(_context.Object);
_mocker.Verify<ITvRequestEngine>(x => x.RequestTvShow(It.Is<TvRequestViewModelV2>(x => x.TheMovieDbId == 333 && x.LatestSeason == false && x.RequestAll == true)), Times.Once);
_mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
_mocker.Verify<ITvRequestEngine>(x => x.SetUser(It.Is<OmbiUser>(x => x.Id == "abc")), Times.Once);
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Once);
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Once);
_mocker.Verify<IMovieDbApi>(x => x.Find("123", ExternalSource.tvdb_id), Times.Once);
}

[Test]
public async Task TvRequestFromWatchList_NoTmdbGuid()
{

_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{
Expand Down Expand Up @@ -494,7 +682,7 @@ public async Task TvRequestFromWatchList_NoTmdbGuid()
[Test]
public async Task MovieRequestFromWatchList_AlreadyImported()
{

_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{
Expand Down
60 changes: 55 additions & 5 deletions src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using Microsoft.Extensions.Logging;
using Ombi.Api.Plex;
using Ombi.Api.Plex.Models;
using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
Expand All @@ -14,6 +16,7 @@
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
using Quartz;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -30,13 +33,15 @@ public class PlexWatchlistImport : IPlexWatchlistImport
private readonly IMovieRequestEngine _movieRequestEngine;
private readonly ITvRequestEngine _tvRequestEngine;
private readonly INotificationHubService _notificationHubService;
private readonly ILogger _logger;
private readonly Microsoft.Extensions.Logging.ILogger _logger;
private readonly IExternalRepository<PlexWatchlistHistory> _watchlistRepo;
private readonly IRepository<PlexWatchlistUserError> _userError;
private readonly IMovieDbApi _movieDbApi;

public PlexWatchlistImport(IPlexApi plexApi, ISettingsService<PlexSettings> settings, OmbiUserManager ombiUserManager,
IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, INotificationHubService notificationHubService,
ILogger<PlexWatchlistImport> logger, IExternalRepository<PlexWatchlistHistory> watchlistRepo, IRepository<PlexWatchlistUserError> userError)
ILogger<PlexWatchlistImport> logger, IExternalRepository<PlexWatchlistHistory> watchlistRepo, IRepository<PlexWatchlistUserError> userError,
IMovieDbApi movieDbApi)
{
_plexApi = plexApi;
_settings = settings;
Expand All @@ -47,6 +52,7 @@ public PlexWatchlistImport(IPlexApi plexApi, ISettingsService<PlexSettings> sett
_logger = logger;
_watchlistRepo = watchlistRepo;
_userError = userError;
_movieDbApi = movieDbApi;
}

public async Task Execute(IJobExecutionContext context)
Expand Down Expand Up @@ -109,9 +115,16 @@ await _userError.Add(new PlexWatchlistUserError
var providerIds = await GetProviderIds(user.MediaServerToken, item, context?.CancellationToken ?? CancellationToken.None);
if (!providerIds.TheMovieDb.HasValue())
{
_logger.LogWarning($"No TheMovieDb Id found for {item.title}, could not import via Plex WatchList");
// We need a MovieDbId to support this;
continue;
// Try and use another Id to figure out TheMovieDB
var movieDbId = await FindTmdbIdFromAlternateSources(providerIds, item.type);
if (string.IsNullOrEmpty(movieDbId))
{
_logger.LogWarning($"No TheMovieDb Id found for {item.title} for user {user.UserName}, could not import via Plex WatchList");
// We need a MovieDbId to support this;
continue;
}

providerIds.TheMovieDb = movieDbId;
}

// Check to see if we have already imported this item
Expand Down Expand Up @@ -143,6 +156,43 @@ await _userError.Add(new PlexWatchlistUserError
await NotifyClient("Finished Watchlist Import");
}

private async Task<string> FindTmdbIdFromAlternateSources(ProviderId providerId, string type)
{
FindResult result = null;
var hasResult = false;
var movie = type == "movie";
if (!string.IsNullOrEmpty(providerId.TheTvDb))
{
result = await _movieDbApi.Find(providerId.TheTvDb, ExternalSource.tvdb_id);
hasResult = result?.tv_results?.Length > 0;
}
if (!string.IsNullOrEmpty(providerId.ImdbId) && !hasResult)
{
result = await _movieDbApi.Find(providerId.ImdbId, ExternalSource.imdb_id);
if (movie)
{
hasResult = result?.movie_results?.Length > 0;
}
else
{
hasResult = result?.tv_results?.Length > 0;
}
}
if (hasResult)
{
if (movie)
{
return result.movie_results?[0]?.id.ToString() ?? string.Empty;
}
else
{

return result.tv_results?[0]?.id.ToString() ?? string.Empty;
}
}
return string.Empty;
}

private async Task ProcessMovie(int theMovieDbId, OmbiUser user)
{
_movieRequestEngine.SetUser(user);
Expand Down