diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index c3828eae3..5664d276f 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -188,7 +188,7 @@ namespace Ombi.Core.Engine (await tvBuilder .GetShowInfo(tv.TheMovieDbId, tv.languageCode)) .CreateTvList(tv) - .CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id); + .CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id, tv.Source); await tvBuilder.BuildEpisodes(tv); diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs index 6843332c0..4053109da 100644 --- a/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs @@ -53,7 +53,7 @@ namespace Ombi.Core.Helpers return this; } - public TvShowRequestBuilderV2 CreateChild(TvRequestViewModelV2 model, string userId) + public TvShowRequestBuilderV2 CreateChild(TvRequestViewModelV2 model, string userId, RequestSource source) { var animationGenre = TheMovieDbRecord.genres?.Any(s => s.name.Equals("Animation", StringComparison.InvariantCultureIgnoreCase)) ?? false; var animeKeyword = TheMovieDbRecord.Keywords?.KeywordsValue?.Any(s => s.Name.Equals("Anime", StringComparison.InvariantCultureIgnoreCase)) ?? false; @@ -68,7 +68,8 @@ namespace Ombi.Core.Helpers Title = TheMovieDbRecord.name, ReleaseYear = FirstAir, RequestedByAlias = model.RequestedByAlias, - SeriesType = animationGenre && animeKeyword ? SeriesType.Anime : SeriesType.Standard + SeriesType = animationGenre && animeKeyword ? SeriesType.Anime : SeriesType.Standard, + Source = source }; return this; diff --git a/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs b/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs index a9742fb32..e1df9553f 100644 --- a/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs +++ b/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Ombi.Store.Entities.Requests; namespace Ombi.Core.Models.Requests { @@ -7,5 +8,6 @@ namespace Ombi.Core.Models.Requests { public int TheMovieDbId { get; set; } public string languageCode { get; set; } = "en"; + public RequestSource Source { get; set; } = RequestSource.Ombi; } } \ No newline at end of file diff --git a/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs b/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs index 7f3e0ccbd..b4759e280 100644 --- a/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs +++ b/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs @@ -136,6 +136,54 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Once); } + + [Test] + public async Task TvRequestFromWatchList_NoGuid() + { + _mocker.Setup, Task>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true }); + _mocker.Setup>(x => x.GetWatchlist(It.IsAny(), It.IsAny())).ReturnsAsync(new PlexWatchlistContainer + { + MediaContainer = new PlexWatchlist + { + Metadata = new List + { + new Metadata + { + type = "show", + ratingKey = "abc" + } + } + } + }); + _mocker.Setup>(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny())) + .ReturnsAsync(new PlexWatchlistMetadataContainer + { + MediaContainer = new PlexWatchlistMetadata + { + Metadata = new WatchlistMetadata[] + { + new WatchlistMetadata + { + Guid = new List + { + new PlexGuids + { + Id = "tmdb://123" + } + } + } + } + + } + }); + _mocker.Setup>(x => x.RequestTvShow(It.IsAny())) + .ReturnsAsync(new RequestEngineResult { RequestId = 1 }); + await _subject.Execute(_context.Object); + _mocker.Verify(x => x.RequestTvShow(It.Is(x => x.TheMovieDbId == 123)), Times.Once); + _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); + _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Once); + } + [Test] public async Task MovieRequestFromWatchList_AlreadyRequested() { @@ -183,6 +231,53 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Once); } + [Test] + public async Task TvRequestFromWatchList_AlreadyRequested() + { + _mocker.Setup, Task>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true }); + _mocker.Setup>(x => x.GetWatchlist(It.IsAny(), It.IsAny())).ReturnsAsync(new PlexWatchlistContainer + { + MediaContainer = new PlexWatchlist + { + Metadata = new List + { + new Metadata + { + type = "show", + ratingKey = "abc" + } + } + } + }); + _mocker.Setup>(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny())) + .ReturnsAsync(new PlexWatchlistMetadataContainer + { + MediaContainer = new PlexWatchlistMetadata + { + Metadata = new WatchlistMetadata[] + { + new WatchlistMetadata + { + Guid = new List + { + new PlexGuids + { + Id = "tmdb://123" + } + } + } + } + + } + }); + _mocker.Setup>(x => x.RequestTvShow(It.IsAny())) + .ReturnsAsync(new RequestEngineResult { ErrorCode = ErrorCode.AlreadyRequested, ErrorMessage = "Requested" }); + await _subject.Execute(_context.Object); + _mocker.Verify(x => x.RequestTvShow(It.Is(x => x.TheMovieDbId == 123)), Times.Once); + _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); + _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Once); + } + [Test] public async Task MovieRequestFromWatchList_NoTmdbGuid() { @@ -229,5 +324,52 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Never); } + + [Test] + public async Task TvRequestFromWatchList_NoTmdbGuid() + { + _mocker.Setup, Task>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true }); + _mocker.Setup>(x => x.GetWatchlist(It.IsAny(), It.IsAny())).ReturnsAsync(new PlexWatchlistContainer + { + MediaContainer = new PlexWatchlist + { + Metadata = new List + { + new Metadata + { + type = "movie", + ratingKey = "abc" + } + } + } + }); + _mocker.Setup>(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny())) + .ReturnsAsync(new PlexWatchlistMetadataContainer + { + MediaContainer = new PlexWatchlistMetadata + { + Metadata = new WatchlistMetadata[] + { + new WatchlistMetadata + { + Guid = new List + { + new PlexGuids + { + Id = "imdb://123" + } + } + } + } + + } + }); + _mocker.Setup>(x => x.RequestTvShow(It.IsAny())) + .ReturnsAsync(new RequestEngineResult { RequestId = 1 }); + await _subject.Execute(_context.Object); + _mocker.Verify(x => x.RequestTvShow(It.IsAny()), Times.Never); + _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); + _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Never); + } } } diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs index b07e95da0..10e34bf20 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs @@ -4,6 +4,7 @@ using Ombi.Api.Plex.Models; using Ombi.Core.Authentication; using Ombi.Core.Engine; using Ombi.Core.Engine.Interfaces; +using Ombi.Core.Models.Requests; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; @@ -23,16 +24,18 @@ namespace Ombi.Schedule.Jobs.Plex private readonly ISettingsService _settings; private readonly OmbiUserManager _ombiUserManager; private readonly IMovieRequestEngine _movieRequestEngine; + private readonly ITvRequestEngine _tvRequestEngine; private readonly ILogger _logger; public PlexWatchlistImport(IPlexApi plexApi, ISettingsService settings, OmbiUserManager ombiUserManager, - IMovieRequestEngine movieRequestEngine, + IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, ILogger logger) { _plexApi = plexApi; _settings = settings; _ombiUserManager = ombiUserManager; _movieRequestEngine = movieRequestEngine; + _tvRequestEngine = tvRequestEngine; _logger = logger; } @@ -56,29 +59,48 @@ namespace Ombi.Schedule.Jobs.Plex var items = watchlist.MediaContainer.Metadata; foreach (var item in items) { + var providerIds = await GetProviderIds(user.MediaServerToken, item, context?.CancellationToken ?? CancellationToken.None); + if (!providerIds.TheMovieDb.HasValue()) + { + // We need a MovieDbId to support this; + return; + } switch (item.type) { case "show": - await ProcessShow(item); + await ProcessShow(int.Parse(providerIds.TheMovieDb), user, context?.CancellationToken ?? CancellationToken.None); break; case "movie": - await ProcessMovie(user.MediaServerToken, item, user, context?.CancellationToken ?? CancellationToken.None); + await ProcessMovie(int.Parse(providerIds.TheMovieDb), user, context?.CancellationToken ?? CancellationToken.None); break; } } } } - private async Task ProcessMovie(string authToken, Metadata movie, OmbiUser user, CancellationToken cancellationToken) + private async Task ProcessMovie(int theMovieDbId, OmbiUser user, CancellationToken cancellationToken) { - var providerIds = await GetProviderIds(authToken, movie, cancellationToken); - if (!providerIds.TheMovieDb.HasValue()) - { - // We need a MovieDbId to support this; - return; - } _movieRequestEngine.SetUser(user); - var response = await _movieRequestEngine.RequestMovie(new() { TheMovieDbId = int.Parse(providerIds.TheMovieDb), Source = RequestSource.PlexWatchlist}); + var response = await _movieRequestEngine.RequestMovie(new() { TheMovieDbId = theMovieDbId, Source = RequestSource.PlexWatchlist}); + if (response.IsError) + { + if (response.ErrorCode == ErrorCode.AlreadyRequested) + { + return; + } + _logger.LogInformation($"Error adding title from PlexWatchlist for user '{user.UserName}'. Message: '{response.ErrorMessage}'"); + } + else + { + _logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}"); + } + } + + + private async Task ProcessShow(int theMovieDbId, OmbiUser user, CancellationToken cancellationToken) + { + _tvRequestEngine.SetUser(user); + var response = await _tvRequestEngine.RequestTvShow(new TvRequestViewModelV2 { RequestAll = true, TheMovieDbId = theMovieDbId, Source = RequestSource.PlexWatchlist }); if (response.IsError) { if (response.ErrorCode == ErrorCode.AlreadyRequested) @@ -122,11 +144,6 @@ namespace Ombi.Schedule.Jobs.Plex return providerIds; } - private async Task ProcessShow(Metadata metadata) - { - - } - public void Dispose() { } } } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html index 4098bb748..8356aecb2 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html @@ -9,6 +9,7 @@ user: request.requestedUser.userAlias, date: request.requestedDate | amLocal | amUserLocale | amDateFormat: 'LL' } }} - {{request.deniedReason}} + {{'MediaDetails.RequestSource' | translate }} {{RequestSource[request.source]}} diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts index 2de85814c..e397e60ea 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from "@angular/core"; -import { IChildRequests, RequestType } from "../../../../../interfaces"; +import { IChildRequests, RequestSource, RequestType } from "../../../../../interfaces"; import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component"; import { MatDialog } from "@angular/material/dialog"; @@ -17,6 +17,8 @@ export class TvRequestsPanelComponent { @Input() public isAdmin: boolean; @Input() public manageOwnRequests: boolean; + public RequestSource = RequestSource; + public displayedColumns: string[] = ['number', 'title', 'airDate', 'status']; constructor(private requestService: RequestService,