Added the watchlist import for movies

pull/4576/head
tidusjar 3 years ago
parent df59b46a78
commit f41eea89a0

@ -10,6 +10,7 @@ using Ombi.Core.Authentication;
using Ombi.Core.Engine.V2;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository.Requests;
using Ombi.Core.Helpers;
namespace Ombi.Core.Tests.Engine
{
@ -25,7 +26,7 @@ namespace Ombi.Core.Tests.Engine
{
MovieRepo = new Mock<IMovieRequestRepository>();
TvRepo = new Mock<ITvRequestRepository>();
var principle = new Mock<IPrincipal>();
var principle = new Mock<ICurrentUser>();
var identity = new Mock<IIdentity>();
identity.Setup(x => x.Name).Returns("UnitTest");
principle.Setup(x => x.Identity).Returns(identity.Object);

@ -4,6 +4,7 @@ using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
@ -35,12 +36,17 @@ namespace Ombi.Core.Tests.Engine
var identity = new Mock<IIdentity>();
identity.Setup(x => x.Name).Returns("Test");
principle.Setup(x => x.Identity).Returns(identity.Object);
var currentUser = new Mock<ICurrentUser>();
currentUser.Setup(x => x.Identity).Returns(identity.Object);
currentUser.Setup(x => x.Username).Returns("Test");
currentUser.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { NormalizedUserName = "TEST", Id = "a" });
_repoMock = new Mock<IMovieRequestRepository>();
var requestServiceMock = new Mock<IRequestServiceMain>();
requestServiceMock.Setup(x => x.MovieRequestService).Returns(_repoMock.Object);
_mocker.Use(principle.Object);
_mocker.Use(currentUser.Object);
_mocker.Use(userManager.Object);
_mocker.Use(requestServiceMock);

@ -4,6 +4,7 @@ using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Core.Services;
using Ombi.Helpers;
@ -36,6 +37,13 @@ namespace Ombi.Core.Tests.Engine
var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("Test");
principleMock.SetupGet(x => x.Identity).Returns(identityMock.Object);
var currentUser = new Mock<ICurrentUser>();
currentUser.Setup(x => x.Identity).Returns(identityMock.Object);
currentUser.Setup(x => x.Username).Returns("Test");
currentUser.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "Test", NormalizedUserName = "TEST", Id = "a" });
_mocker.Use(currentUser.Object);
_mocker.Use(principleMock.Object);
_subject = _mocker.CreateInstance<RequestLimitService>();

@ -4,6 +4,7 @@ using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Core.Services;
using Ombi.Helpers;
@ -36,7 +37,12 @@ namespace Ombi.Core.Tests.Engine
var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("Test");
principleMock.SetupGet(x => x.Identity).Returns(identityMock.Object);
var currentUser = new Mock<ICurrentUser>();
currentUser.Setup(x => x.Identity).Returns(identityMock.Object);
currentUser.Setup(x => x.Username).Returns("Test");
currentUser.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "Test", NormalizedUserName = "TEST", Id = "a" });
_mocker.Use(principleMock.Object);
_mocker.Use(currentUser.Object);
_subject = _mocker.CreateInstance<RequestLimitService>();
}

@ -4,6 +4,7 @@ using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Core.Services;
using Ombi.Helpers;
@ -33,7 +34,12 @@ namespace Ombi.Core.Tests.Engine
var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("Test");
principleMock.SetupGet(x => x.Identity).Returns(identityMock.Object);
var currentUser = new Mock<ICurrentUser>();
currentUser.Setup(x => x.Identity).Returns(identityMock.Object);
currentUser.Setup(x => x.Username).Returns("Test");
currentUser.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "Test", NormalizedUserName = "TEST", Id = "a" });
_mocker.Use(principleMock.Object);
_mocker.Use(currentUser.Object);
_subject = _mocker.CreateInstance<RequestLimitService>();
}

@ -7,6 +7,7 @@ using Moq;
using NUnit.Framework;
using Ombi.Api.TheMovieDb;
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Services;
@ -33,7 +34,7 @@ namespace Ombi.Core.Tests.Engine.V2
var requestService = new Mock<IRequestServiceMain>();
_movieRequestRepository = new Mock<IMovieRequestRepository>();
requestService.Setup(x => x.MovieRequestService).Returns(_movieRequestRepository.Object);
var user = new Mock<IPrincipal>();
var user = new Mock<ICurrentUser>();
var notificationHelper = new Mock<INotificationHelper>();
var rules = new Mock<IRuleEvaluator>();
var movieSender = new Mock<IMovieSender>();

@ -23,6 +23,7 @@ using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Ombi.Test.Common;
using Artist = Hqub.MusicBrainz.API.Entities.Artist;
using Ombi.Core.Helpers;
namespace Ombi.Core.Tests.Engine.V2
{
@ -45,7 +46,7 @@ namespace Ombi.Core.Tests.Engine.V2
.ForEach(b => F.Behaviors.Remove(b));
F.Behaviors.Add(new OmitOnRecursionBehavior());
var principle = new Mock<IPrincipal>();
var principle = new Mock<ICurrentUser>();
var requestService = new Mock<IRequestServiceMain>();
var ruleEval = new Mock<IRuleEvaluator>();
var um = MockHelper.MockUserManager(new List<OmbiUser>());

@ -9,6 +9,7 @@ using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Settings;
@ -32,8 +33,9 @@ namespace Ombi.Core.Tests.Engine
TvRequestEngine = new Mock<ITvRequestEngine>();
MovieRequestEngine = new Mock<IMovieRequestEngine>();
MovieRequestEngine = new Mock<IMovieRequestEngine>();
User = new Mock<IPrincipal>();
User.Setup(x => x.Identity.Name).Returns("abc");
User = new Mock<ICurrentUser>();
User.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "abc", NormalizedUserName = "ABC", Id = "abc" });
UserManager = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { Id = "abc", UserName = "abc", NormalizedUserName = "ABC" } });
Rule = new Mock<IRuleEvaluator>();
Engine = new VoteEngine(VoteRepository.Object, User.Object, UserManager.Object, Rule.Object, VoteSettings.Object, MusicRequestEngine.Object,
@ -48,7 +50,7 @@ namespace Ombi.Core.Tests.Engine
public Fixture F { get; set; }
public VoteEngine Engine { get; set; }
public Mock<IPrincipal> User { get; set; }
public Mock<ICurrentUser> User { get; set; }
public Mock<OmbiUserManager> UserManager { get; set; }
public Mock<IRuleEvaluator> Rule { get; set; }
public Mock<IRepository<Votes>> VoteRepository { get; set; }

@ -11,6 +11,7 @@ using System.Collections.Generic;
using Ombi.Store.Entities;
using System;
using Ombi.Core.Services;
using Ombi.Core.Helpers;
namespace Ombi.Core.Tests.Rule.Request
{
@ -27,17 +28,18 @@ namespace Ombi.Core.Tests.Rule.Request
public void Setup()
{
PrincipalMock = new Mock<IPrincipal>();
PrincipalMock.Setup(x => x.Identity.Name).Returns("abc");
FeatureService = new Mock<IFeatureService>();
PrincipalMock = new Mock<ICurrentUser>();
PrincipalMock.Setup(x => x.Username).Returns("abc");
PrincipalMock.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "abc", NormalizedUserName = "ABC", Id = "a" });
UserManager = MockHelper.MockUserManager(_users);
Rule = new AutoApproveRule(PrincipalMock.Object, UserManager.Object, FeatureService.Object);
}
private AutoApproveRule Rule { get; set; }
private Mock<IPrincipal> PrincipalMock { get; set; }
private Mock<ICurrentUser> PrincipalMock { get; set; }
private Mock<OmbiUserManager> UserManager { get; set; }
private Mock<IFeatureService> FeatureService { get; set; }
@ -99,7 +101,8 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnSuccess_WhenSystemUserAndRequestTV()
{
PrincipalMock.Setup(x => x.Identity.Name).Returns("sys");
PrincipalMock.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "sys", NormalizedUserName = "SYS", Id = "a" });
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveTv)).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.TvShow };
var result = await Rule.Execute(request);

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Rule.Rules;
using Ombi.Core.Rule.Rules.Request;
using Ombi.Helpers;
@ -26,8 +27,9 @@ namespace Ombi.Core.Tests.Rule.Request
public void Setup()
{
PrincipalMock = new Mock<IPrincipal>();
PrincipalMock.Setup(x => x.Identity.Name).Returns("abc");
PrincipalMock = new Mock<ICurrentUser>();
PrincipalMock.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "abc", NormalizedUserName = "ABC", Id = "a" });
UserManager = MockHelper.MockUserManager(_users);
Rule = new CanRequestRule(PrincipalMock.Object, UserManager.Object);
@ -35,7 +37,7 @@ namespace Ombi.Core.Tests.Rule.Request
private CanRequestRule Rule { get; set; }
private Mock<IPrincipal> PrincipalMock { get; set; }
private Mock<ICurrentUser> PrincipalMock { get; set; }
private Mock<OmbiUserManager> UserManager { get; set; }
[Test]
@ -107,7 +109,8 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnSuccess_WhenRequestingMovieWithSystemRole()
{
PrincipalMock.Setup(x => x.Identity.Name).Returns("sys");
PrincipalMock.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "sys", NormalizedUserName = "SYS", Id = "a" });
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Admin)).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);

@ -118,14 +118,23 @@ namespace Ombi.Core.Authentication
var plexAccount = await _plexApi.GetAccount(plexToken);
// Check for a ombi user
if (plexAccount?.user != null)
if (plexAccount?.user == null)
{
return null;
}
var potentialOmbiUser = await Users.FirstOrDefaultAsync(x =>
x.ProviderUserId == plexAccount.user.id);
return potentialOmbiUser;
// Update ombi user with the token
if (potentialOmbiUser != null)
{
potentialOmbiUser.MediaServerToken = plexAccount.user.authentication_token;
await UpdateAsync(potentialOmbiUser);
}
return null;
return potentialOmbiUser;
}
@ -142,6 +151,10 @@ namespace Ombi.Core.Authentication
var result = await _plexApi.SignIn(new UserRequest { password = password, login = login });
if (result.user?.authentication_token != null)
{
// Update ombi user with the token
user.MediaServerToken = result.user?.authentication_token;
await UpdateAsync(user);
return true;
}
return false;

@ -26,7 +26,7 @@ namespace Ombi.Core.Engine
private Dictionary<int, MovieRequests> _dbMovies;
private Dictionary<int, TvRequests> _dbTv;
protected BaseMediaEngine(IPrincipal identity, IRequestServiceMain requestService,
protected BaseMediaEngine(ICurrentUser identity, IRequestServiceMain requestService,
IRuleEvaluator rules, OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub) : base(identity, um, rules)
{
RequestService = requestService;

@ -11,6 +11,7 @@ using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Config;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
@ -24,7 +25,7 @@ namespace Ombi.Core.Engine.Demo
{
public class DemoMovieSearchEngine : MovieSearchEngine, IDemoMovieSearchEngine
{
public DemoMovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
public DemoMovieSearchEngine(ICurrentUser identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
ILogger<MovieSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s,
IRepository<RequestSubscription> sub, IOptions<DemoLists> lists)
: base(identity, service, movApi, mapper, logger, r, um, mem, s, sub)

@ -4,6 +4,7 @@ using Ombi.Api.Trakt;
using Ombi.Api.TvMaze;
using Ombi.Config;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
@ -24,7 +25,7 @@ namespace Ombi.Core.Engine.Demo
public class DemoTvSearchEngine : TvSearchEngine, IDemoTvSearchEngine
{
public DemoTvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
public DemoTvSearchEngine(ICurrentUser identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache,
ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IOptions<DemoLists> lists, IImageService imageService,
ISettingsService<CustomizationSettings> custom)

@ -1,42 +1,35 @@
using System;
using Ombi.Core.Rule;
using System.Collections.Generic;
using System.Security.Principal;
using System.Threading.Tasks;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Entities;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Helpers;
using Ombi.Core.Helpers;
namespace Ombi.Core.Engine.Interfaces
{
public abstract class BaseEngine
{
protected BaseEngine(IPrincipal user, OmbiUserManager um, IRuleEvaluator rules)
protected BaseEngine(ICurrentUser user, OmbiUserManager um, IRuleEvaluator rules)
{
UserPrinciple = user;
CurrentUser = user;
Rules = rules;
UserManager = um;
}
protected IPrincipal UserPrinciple { get; }
protected ICurrentUser CurrentUser { get; }
protected IRuleEvaluator Rules { get; }
protected OmbiUserManager UserManager { get; }
protected string Username => UserPrinciple.Identity.Name;
protected string Username => CurrentUser.Username;
protected Task<OmbiUser> GetUser() => CurrentUser.GetUser();
private OmbiUser _user;
protected async Task<OmbiUser> GetUser()
{
if(!Username.HasValue())
{
return null;
}
var username = Username.ToUpper();
return _user ??= await UserManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username);
}
/// <summary>
/// Only used for background tasks
/// </summary>
public void SetUser(OmbiUser user) => CurrentUser.SetUser(user);
protected async Task<string> UserAlias()
{

@ -25,5 +25,6 @@ namespace Ombi.Core.Engine.Interfaces
Task UnSubscribeRequest(int requestId, RequestType type);
Task SubscribeToRequest(int requestId, RequestType type);
Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken);
void SetUser(OmbiUser user);
}
}

@ -23,12 +23,13 @@ using Ombi.Store.Repository;
using Ombi.Core.Models;
using System.Threading;
using Ombi.Core.Services;
using Ombi.Core.Helpers;
namespace Ombi.Core.Engine
{
public class MovieRequestEngine : BaseMediaEngine, IMovieRequestEngine
{
public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user,
public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, ICurrentUser user,
INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log,
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService,

@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging;
using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
@ -22,7 +23,7 @@ namespace Ombi.Core.Engine
{
public class MovieSearchEngine : BaseMediaEngine, IMovieEngine
{
public MovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
public MovieSearchEngine(ICurrentUser identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
ILogger<MovieSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub)
: base(identity, service, r, um, mem, s, sub)
{

@ -24,12 +24,13 @@ using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
using System.ComponentModel;
using Ombi.Core.Helpers;
namespace Ombi.Core.Engine
{
public class MusicRequestEngine : BaseMediaEngine, IMusicRequestEngine
{
public MusicRequestEngine(IRequestServiceMain requestService, IPrincipal user,
public MusicRequestEngine(IRequestServiceMain requestService, ICurrentUser user,
INotificationHelper helper, IRuleEvaluator r, ILogger<MusicRequestEngine> log,
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, ILidarrApi lidarr,

@ -27,7 +27,7 @@ namespace Ombi.Core.Engine
{
public class MusicSearchEngine : BaseMediaEngine, IMusicSearchEngine
{
public MusicSearchEngine(IPrincipal identity, IRequestServiceMain service, ILidarrApi lidarrApi, IMapper mapper,
public MusicSearchEngine(ICurrentUser identity, IRequestServiceMain service, ILidarrApi lidarrApi, IMapper mapper,
ILogger<MusicSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub,
ISettingsService<LidarrSettings> lidarrSettings)
: base(identity, service, r, um, mem, s, sub)

@ -32,7 +32,7 @@ namespace Ombi.Core.Engine
{
public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine
{
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user,
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, ICurrentUser user,
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, ILogger<TvRequestEngine> logger,
ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService) : base(user, requestService, rule, manager, cache, settings, sub)

@ -23,6 +23,7 @@ using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using System.Threading;
using TraktSharp.Entities;
using Ombi.Core.Helpers;
namespace Ombi.Core.Engine
{
@ -32,7 +33,7 @@ namespace Ombi.Core.Engine
private readonly IImageService _imageService;
private readonly IMovieDbApi _theMovieDbApi;
public TvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
public TvSearchEngine(ICurrentUser identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ISettingsService<CustomizationSettings> customizationSettings,
ICacheService memCache, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IImageService imageService,
IMovieDbApi theMovieDbApi)

@ -5,6 +5,7 @@ using System.Security.Principal;
using System.Threading.Tasks;
using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Search.V2;
using Ombi.Core.Rule.Interfaces;
using Ombi.Store.Entities;
@ -17,7 +18,7 @@ namespace Ombi.Core.Engine.V2
{
public DateTime DaysAgo => DateTime.Now.AddDays(-90);
public DateTime DaysAhead => DateTime.Now.AddDays(90);
public CalendarEngine(IPrincipal user, OmbiUserManager um, IRuleEvaluator rules, IMovieRequestRepository movieRepo,
public CalendarEngine(ICurrentUser user, OmbiUserManager um, IRuleEvaluator rules, IMovieRequestRepository movieRepo,
ITvRequestRepository tvRequestRepo) : base(user, um, rules)
{
_movieRepo = movieRepo;

@ -5,6 +5,7 @@ using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Core.Models.Search.V2;
@ -28,7 +29,7 @@ namespace Ombi.Core.Engine.V2
{
public class MovieSearchEngineV2 : BaseMediaEngine, IMovieEngineV2
{
public MovieSearchEngineV2(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
public MovieSearchEngineV2(ICurrentUser identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
ILogger<MovieSearchEngineV2> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub,
ISettingsService<CustomizationSettings> customizationSettings, IMovieRequestEngine movieRequestEngine, IHttpClientFactory httpClientFactory)
: base(identity, service, r, um, mem, s, sub)

@ -7,6 +7,7 @@ using Ombi.Api.MusicBrainz;
using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search.V2;
using Ombi.Core.Rule.Interfaces;
@ -25,7 +26,7 @@ namespace Ombi.Core.Engine.V2
{
public class MultiSearchEngine : BaseMediaEngine, IMultiSearchEngine
{
public MultiSearchEngine(IPrincipal identity, IRequestServiceMain requestService, IRuleEvaluator rules,
public MultiSearchEngine(ICurrentUser identity, IRequestServiceMain requestService, IRuleEvaluator rules,
OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub,
IMovieDbApi movieDbApi, ISettingsService<LidarrSettings> lidarrSettings, IMusicBrainzApi musicApi)
: base(identity, requestService, rules, um, cache, ombiSettings, sub)

@ -11,6 +11,7 @@ using Ombi.Api.Lidarr.Models;
using Ombi.Api.MusicBrainz;
using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search.V2.Music;
using Ombi.Core.Rule.Interfaces;
@ -31,7 +32,7 @@ namespace Ombi.Core.Engine.V2
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
private readonly ILidarrApi _lidarrApi;
public MusicSearchEngineV2(IPrincipal identity, IRequestServiceMain requestService, IRuleEvaluator rules,
public MusicSearchEngineV2(ICurrentUser identity, IRequestServiceMain requestService, IRuleEvaluator rules,
OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings,
IRepository<RequestSubscription> sub, IMusicBrainzApi musicBrainzApi, ISettingsService<LidarrSettings> lidarrSettings,
ILidarrApi lidarrApi)

@ -25,6 +25,7 @@ using Ombi.Api.TheMovieDb.Models;
using System.Diagnostics;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Models.UI;
using Ombi.Core.Helpers;
namespace Ombi.Core.Engine.V2
{
@ -37,7 +38,7 @@ namespace Ombi.Core.Engine.V2
private readonly ISettingsService<CustomizationSettings> _customization;
private readonly ITvRequestEngine _requestEngine;
public TvSearchEngineV2(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
public TvSearchEngineV2(ICurrentUser identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache, ISettingsService<OmbiSettings> s,
IRepository<RequestSubscription> sub, IMovieDbApi movieApi, ISettingsService<CustomizationSettings> customization, ITvRequestEngine requestEngine)
: base(identity, service, r, um, memCache, s, sub)

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Core.Models.UI;
using Ombi.Core.Rule.Interfaces;
@ -20,7 +21,7 @@ namespace Ombi.Core.Engine
{
public class VoteEngine : BaseEngine, IVoteEngine
{
public VoteEngine(IRepository<Votes> votes, IPrincipal user, OmbiUserManager um, IRuleEvaluator r, ISettingsService<VoteSettings> voteSettings,
public VoteEngine(IRepository<Votes> votes, ICurrentUser user, OmbiUserManager um, IRuleEvaluator r, ISettingsService<VoteSettings> voteSettings,
IMusicRequestEngine musicRequestEngine, ITvRequestEngine tvRequestEngine, IMovieRequestEngine movieRequestEngine) : base(user, um, r)
{
_voteRepository = votes;

@ -0,0 +1,47 @@
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Helpers;
using Ombi.Store.Entities;
using System.Security.Principal;
using System.Threading.Tasks;
namespace Ombi.Core.Helpers
{
public class CurrentUser : ICurrentUser
{
private readonly IPrincipal _principle;
private readonly OmbiUserManager _userManager;
private OmbiUser _user;
public IIdentity Identity { get; set; }
public CurrentUser(IPrincipal principle, OmbiUserManager userManager)
{
_principle = principle;
_userManager = userManager;
Identity = _principle?.Identity;
}
public void SetUser(OmbiUser user)
{
_user = user;
}
public string Username => Identity.Name;
public async Task<OmbiUser> GetUser()
{
if (!Username.HasValue() && _user == null)
{
return null;
}
if (_user != null)
{
return _user;
}
var username = Username.ToUpper();
return _user ??= await _userManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username);
}
}
}

@ -0,0 +1,15 @@
using Ombi.Store.Entities;
using System.Security.Principal;
using System.Threading.Tasks;
namespace Ombi.Core.Helpers
{
public interface ICurrentUser
{
string Username { get; }
Task<OmbiUser> GetUser();
void SetUser(OmbiUser user);
IIdentity Identity { get; set; }
}
}

@ -1,31 +1,5 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2018 Jamie Rees
// File: MovieRequestViewModel.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using Newtonsoft.Json;
using Newtonsoft.Json;
using Ombi.Store.Entities.Requests;
namespace Ombi.Core.Models.Requests
{
@ -41,5 +15,11 @@ namespace Ombi.Core.Models.Requests
/// </summary>
[JsonIgnore]
public string RequestedByAlias { get; set; }
/// <summary>
/// Only set via list imports
/// </summary>
[JsonIgnore]
public RequestSource Source { get; set; } = RequestSource.Ombi;
}
}

@ -3,6 +3,7 @@ using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Services;
@ -15,20 +16,21 @@ namespace Ombi.Core.Rule.Rules.Request
{
public class AutoApproveRule : BaseRequestRule, IRules<BaseRequest>
{
public AutoApproveRule(IPrincipal principal, OmbiUserManager um, IFeatureService featureService)
public AutoApproveRule(ICurrentUser principal, OmbiUserManager um, IFeatureService featureService)
{
User = principal;
_manager = um;
_featureService = featureService;
}
private IPrincipal User { get; }
private ICurrentUser User { get; }
private readonly OmbiUserManager _manager;
private readonly IFeatureService _featureService;
public async Task<RuleResult> Execute(BaseRequest obj)
{
var username = User.Identity.Name.ToUpper();
var currentUser = await User.GetUser();
var username = currentUser.UserName.ToUpper();
var user = await _manager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username);
if (await _manager.IsInRoleAsync(user, OmbiRoles.Admin) || user.IsSystemUser)
{

@ -10,23 +10,25 @@ using Ombi.Core.Engine;
using Ombi.Core.Rule.Interfaces;
using Ombi.Helpers;
using Ombi.Store.Entities.Requests;
using Ombi.Core.Helpers;
namespace Ombi.Core.Rule.Rules.Request
{
public class CanRequestRule : BaseRequestRule, IRules<BaseRequest>
{
public CanRequestRule(IPrincipal principal, OmbiUserManager manager)
public CanRequestRule(ICurrentUser principal, OmbiUserManager manager)
{
User = principal;
_manager = manager;
}
private IPrincipal User { get; }
private ICurrentUser User { get; }
private readonly OmbiUserManager _manager;
public async Task<RuleResult> Execute(BaseRequest obj)
{
var username = User.Identity.Name.ToUpper();
var currentUser = await User.GetUser();
var username = currentUser.UserName.ToUpper();
var user = await _manager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username);
if (await _manager.IsInRoleAsync(user, OmbiRoles.Admin) || user.IsSystemUser)
return Success();

@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Helpers;
using Ombi.Store.Entities;
@ -20,11 +21,11 @@ namespace Ombi.Core.Services
}
public class RequestLimitService : IRequestLimitService
{
private readonly IPrincipal _user;
private readonly ICurrentUser _user;
private readonly OmbiUserManager _userManager;
private readonly IRepository<RequestLog> _requestLog;
public RequestLimitService(IPrincipal user, OmbiUserManager userManager, IRepository<RequestLog> rl)
public RequestLimitService(ICurrentUser user, OmbiUserManager userManager, IRepository<RequestLog> rl)
{
_user = user;
_userManager = userManager;
@ -141,7 +142,8 @@ namespace Ombi.Core.Services
private async Task<OmbiUser> GetUser()
{
var username = _user.Identity.Name.ToUpper();
var currentUser = await _user.GetUser();
var username = currentUser.UserName.ToUpper();
return await _userManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username);
}

@ -70,6 +70,7 @@ using Ombi.Api.RottenTomatoes;
using System.Net.Http;
using Microsoft.Extensions.Logging;
using Ombi.Core.Services;
using Ombi.Core.Helpers;
namespace Ombi.DependencyInjection
{
@ -124,6 +125,8 @@ namespace Ombi.DependencyInjection
var runtimeVersion = AssemblyHelper.GetRuntimeVersion();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IPrincipal>(sp => sp.GetService<IHttpContextAccessor>().HttpContext.User);
// HttpContext User is null for background jobs
services.AddScoped<ICurrentUser, CurrentUser>(sp => new CurrentUser(sp.GetService<IHttpContextAccessor>()?.HttpContext?.User ?? null, sp.GetService<OmbiUserManager>()));
services.AddHttpClient("OmbiClient", client =>
{
client.DefaultRequestHeaders.Add("User-Agent", $"Ombi/{runtimeVersion} (https://ombi.io/)");

@ -3,18 +3,16 @@ using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Api.Plex;
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.Schedule.Jobs.Plex;
using Ombi.Store.Entities;
using Ombi.Test.Common;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -42,16 +40,194 @@ namespace Ombi.Schedule.Tests
[Test]
public async Task TerminatesWhenPlexIsNotEnabled()
{
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = false });
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = false, EnableWatchlistImport = true });
await _subject.Execute(null);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
_mocker.Verify<IPlexApi>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
}
[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);
_mocker.Verify<IPlexApi>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
}
[Test]
public async Task EmptyWatchList()
{
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlist>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlist());
_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());
await _subject.Execute(_context.Object);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
_mocker.Verify<IPlexApi>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
}
[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>
{
new OmbiUser { Id = "abc", UserType = UserType.EmbyUser, MediaServerToken = "abc", UserName = "abc", NormalizedUserName = "ABC" },
new OmbiUser { Id = "abc", UserType = UserType.LocalUser, MediaServerToken = "abc", UserName = "abc", NormalizedUserName = "ABC" },
new OmbiUser { Id = "abc", UserType = UserType.SystemUser, MediaServerToken = "abc", UserName = "abc", NormalizedUserName = "ABC" },
new OmbiUser { Id = "abc", UserType = UserType.JellyfinUser, MediaServerToken = "abc", UserName = "abc", NormalizedUserName = "ABC" },
new OmbiUser { Id = "abc", UserType = UserType.EmbyConnectUser, MediaServerToken = "abc", UserName = "abc", NormalizedUserName = "ABC" },
new OmbiUser { Id = "abc", UserType = UserType.PlexUser, UserName = "abc", NormalizedUserName = "ABC" },
});
_mocker.Use(um);
_subject = _mocker.CreateInstance<PlexWatchlistImport>();
await _subject.Execute(_context.Object);
_mocker.Verify<IPlexApi>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
}
[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
{
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 = "tmdb://123"
}
}
}
}
}
});
_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.Is<MovieRequestViewModel>(x => x.TheMovieDbId == 123)), 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);
}
[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
{
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 = "tmdb://123"
}
}
}
}
}
});
_mocker.Setup<IMovieRequestEngine, Task<RequestEngineResult>>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()))
.ReturnsAsync(new RequestEngineResult { ErrorCode = ErrorCode.AlreadyRequested, ErrorMessage = "Requested" });
await _subject.Execute(_context.Object);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.Is<MovieRequestViewModel>(x => x.TheMovieDbId == 123)), 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);
}
[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
{
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 });
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);
_mocker.Verify<IMovieRequestEngine>(x => x.SetUser(It.Is<OmbiUser>(x => x.Id == "abc")), Times.Never);
}
}
}

@ -1,17 +1,17 @@
using Ombi.Api.Plex;
using Microsoft.Extensions.Logging;
using Ombi.Api.Plex;
using Ombi.Api.Plex.Models;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using Ombi.Helpers;
using Ombi.Store.Entities;
using Ombi.Store.Repository.Requests;
using Ombi.Store.Entities.Requests;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -22,34 +22,32 @@ namespace Ombi.Schedule.Jobs.Plex
private readonly IPlexApi _plexApi;
private readonly ISettingsService<PlexSettings> _settings;
private readonly OmbiUserManager _ombiUserManager;
private readonly IMovieRequestRepository _movieRequestRepository;
private readonly ITvRequestRepository _tvRequestRepository;
private readonly IMovieRequestEngine _movieRequestEngine;
private readonly ILogger _logger;
public PlexWatchlistImport(IPlexApi plexApi, ISettingsService<PlexSettings> settings, OmbiUserManager ombiUserManager,
IMovieRequestRepository movieRequestRepository, ITvRequestRepository tvRequestRepository, IMovieRequestEngine movieRequestEngine)
IMovieRequestEngine movieRequestEngine,
ILogger<PlexWatchlistImport> logger)
{
_plexApi = plexApi;
_settings = settings;
_ombiUserManager = ombiUserManager;
_movieRequestRepository = movieRequestRepository;
_tvRequestRepository = tvRequestRepository;
_movieRequestEngine = movieRequestEngine;
_logger = logger;
}
public async Task Execute(IJobExecutionContext context)
{
var settings = await _settings.GetSettingsAsync();
if (!settings.Enable)
if (!settings.Enable || !settings.EnableWatchlistImport)
{
return;
}
var plexUsersWithTokens = _ombiUserManager.Users.Where(x => x.UserType == UserType.PlexUser && x.MediaServerToken != null).ToList();
//foreach (var user in plexUsersWithTokens)
//{
var watchlist = await _plexApi.GetWatchlist(token, context?.CancellationToken ?? CancellationToken.None);
foreach (var user in plexUsersWithTokens)
{
var watchlist = await _plexApi.GetWatchlist(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None);
if (watchlist == null || !(watchlist.MediaContainer?.Metadata?.Any() ?? false))
{
return;
@ -64,27 +62,38 @@ namespace Ombi.Schedule.Jobs.Plex
await ProcessShow(item);
break;
case "movie":
await ProcessMovie(token, item, null, context?.CancellationToken ?? CancellationToken.None);
await ProcessMovie(user.MediaServerToken, item, user, context?.CancellationToken ?? CancellationToken.None);
break;
}
}
//}
}
}
private async Task ProcessMovie(string authToken, Metadata movie, PlexServers servers, CancellationToken cancellationToken)
private async Task ProcessMovie(string authToken, Metadata movie, OmbiUser user, CancellationToken cancellationToken)
{
var providerIds = await GetProviderIds(authToken, movie, servers, cancellationToken);
var providerIds = await GetProviderIds(authToken, movie, cancellationToken);
if (!providerIds.TheMovieDb.HasValue())
{
// We need a MovieDbId to support this;
return;
}
//_movieRequestEngine.RequestMovie(new() { TheMovieDbId = });
_movieRequestEngine.SetUser(user);
var response = await _movieRequestEngine.RequestMovie(new() { TheMovieDbId = int.Parse(providerIds.TheMovieDb), 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<ProviderId> GetProviderIds(string authToken, Metadata movie, PlexServers servers, CancellationToken cancellationToken)
private async Task<ProviderId> GetProviderIds(string authToken, Metadata movie, CancellationToken cancellationToken)
{
var guids = new List<string>();
if (!movie.Guid.Any())

@ -91,6 +91,7 @@ namespace Ombi.Schedule
await OmbiQuartz.Instance.AddJob<IPlexUserImporter>(nameof(IPlexUserImporter), "Plex", JobSettingsHelper.UserImporter(s));
await OmbiQuartz.Instance.AddJob<IPlexEpisodeSync>(nameof(IPlexEpisodeSync), "Plex", null);
await OmbiQuartz.Instance.AddJob<IPlexAvailabilityChecker>(nameof(IPlexAvailabilityChecker), "Plex", null);
await OmbiQuartz.Instance.AddJob<IPlexWatchlistImport>(nameof(IPlexWatchlistImport), "Plex", JobSettingsHelper.PlexWatchlistImport(s));
}
private static async Task AddEmby(JobSettings s)

@ -7,6 +7,7 @@ namespace Ombi.Core.Settings.Models.External
public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings
{
public bool Enable { get; set; }
public bool EnableWatchlistImport { get; set; }
/// <summary>
/// This is the ClientId for OAuth
/// </summary>

@ -19,5 +19,6 @@
public string RetryRequests { get; set; }
public string MediaDatabaseRefresh { get; set; }
public string AutoDeleteRequests { get; set; }
public string PlexWatchlistImport { get; set; }
}
}

@ -55,6 +55,11 @@ namespace Ombi.Settings.Settings.Models
return ValidateCron(Get(s.UserImporter, Cron.Daily()));
}
public static string PlexWatchlistImport(JobSettings s)
{
return ValidateCron(Get(s.PlexWatchlistImport, Cron.Daily()));
}
public static string Newsletter(JobSettings s)
{
return ValidateCron(Get(s.Newsletter, Cron.Weekly(Helpers.DayOfWeek.Friday, 12)));

@ -22,6 +22,8 @@ namespace Ombi.Store.Entities.Requests
[ForeignKey(nameof(RequestedUserId))]
public OmbiUser RequestedUser { get; set; }
public RequestSource Source { get; set; } = RequestSource.Ombi;
[NotMapped]
public virtual bool CanApprove => !Approved && !Available;

@ -0,0 +1,8 @@
namespace Ombi.Store.Entities.Requests
{
public enum RequestSource
{
Ombi = 0,
PlexWatchlist = 1
}
}

@ -0,0 +1,48 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Ombi.Store.Migrations.OmbiSqlite
{
public partial class RequestSource : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "Source",
table: "MovieRequests",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "Source",
table: "ChildRequests",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "Source",
table: "AlbumRequests",
type: "INTEGER",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Source",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "Source",
table: "ChildRequests");
migrationBuilder.DropColumn(
name: "Source",
table: "AlbumRequests");
}
}
}

@ -469,6 +469,9 @@ namespace Ombi.Store.Migrations.OmbiSqlite
b.Property<string>("RequestedUserId")
.HasColumnType("TEXT");
b.Property<int>("Source")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.HasColumnType("TEXT");
@ -527,6 +530,9 @@ namespace Ombi.Store.Migrations.OmbiSqlite
b.Property<int>("SeriesType")
.HasColumnType("INTEGER");
b.Property<int>("Source")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.HasColumnType("TEXT");
@ -729,6 +735,9 @@ namespace Ombi.Store.Migrations.OmbiSqlite
b.Property<int>("RootPathOverride")
.HasColumnType("INTEGER");
b.Property<int>("Source")
.HasColumnType("INTEGER");
b.Property<string>("Status")
.HasColumnType("TEXT");

@ -112,6 +112,7 @@ export interface IPublicInfo {
export interface IPlexSettings extends ISettings {
enable: boolean;
enableWatchlistImport: boolean;
servers: IPlexServer[];
}
@ -219,6 +220,7 @@ export interface IJobSettings {
mediaDatabaseRefresh: string;
autoDeleteRequests: string;
embyRecentlyAddedSync: string;
plexWatchlistImport: string;
}
export interface IIssueSettings extends ISettings {

@ -27,6 +27,10 @@ export class JobService extends ServiceHelpers {
return this.http.post<boolean>(`${this.url}plexUserImporter/`, {headers: this.headers});
}
public runPlexWatchlistImport(): Observable<boolean> {
return this.http.post<boolean>(`${this.url}plexwatchlist/`, {headers: this.headers});
}
public runEmbyImporter(): Observable<boolean> {
return this.http.post<boolean>(`${this.url}embyUserImporter/`, {headers: this.headers});
}

@ -77,6 +77,13 @@
<small *ngIf="form.get('plexRecentlyAddedSync').hasError('required')" class="error-text">The Plex Sync is required</small></mat-form-field>
<button mat-raised-button type="button" class="btn btn-sm btn-primary-outline cronbtn" (click)="testCron(form.get('plexRecentlyAddedSync')?.value)">Test</button>
</div>
<div class="form-group cronBox">
<mat-form-field appearance="outline" floatLabel=always>
<mat-label for="plexWatchlistImport" class="control-mat-label">Plex Watchlist Import</mat-label>
<input type="text" matInput [ngClass]="{'form-error': form.get('plexWatchlistImport').hasError('required')}" id="plexWatchlistImport" name="plexWatchlistImport" formControlName="plexWatchlistImport">
<small *ngIf="form.get('plexWatchlistImport').hasError('required')" class="error-text">The Plex Watchlist Import is required</small></mat-form-field>
<button mat-raised-button type="button" class="btn btn-sm btn-primary-outline cronbtn" (click)="testCron(form.get('plexWatchlistImport')?.value)">Test</button>
</div>
<div class="form-group cronBox">
<mat-form-field appearance="outline" floatLabel=always>

@ -37,6 +37,7 @@ export class JobsComponent implements OnInit {
mediaDatabaseRefresh: [x.mediaDatabaseRefresh, Validators.required],
autoDeleteRequests: [x.autoDeleteRequests, Validators.required],
embyRecentlyAddedSync: [x.embyRecentlyAddedSync, Validators.required],
plexWatchlistImport: [x.plexWatchlistImport, Validators.required],
});
});
}

@ -9,6 +9,12 @@
<mat-slide-toggle [(ngModel)]="settings.enable" [checked]="settings.enable">Enable
</mat-slide-toggle>
</div>
<div class="md-form-field">
<mat-slide-toggle [(ngModel)]="settings.enableWatchlistImport" [checked]="settings.enableWatchlistImport">Enable User Watchlist Requests
</mat-slide-toggle>
<p>When a Plex User adds something to their watchlist in Plex, it will turn up in Ombi as a Request if enabled. This <b>only</b> applies to users that are logging in with their Plex Account</p>
<p>Request limits if set are all still applied etc.</p>
</div>
<div class="md-form-field">
<mat-slide-toggle [(ngModel)]="advanced">Advanced</mat-slide-toggle>
</div>
@ -183,6 +189,12 @@
Clear Data And Resync
</button>
</div>
<div class="form-group">
<button mat-raised-button (click)="runWatchlistImport()" type="button" id="watchlistImport"
class="mat-focus-indicator mat-stroked-button mat-button-base">
Run Watchlist Import
</button>
</div>
</div>
</div>
</mat-tab>

@ -172,6 +172,14 @@ export class PlexComponent implements OnInit, OnDestroy {
});
}
public runWatchlistImport(): void {
this.jobService.runPlexWatchlistImport().subscribe(x => {
if (x) {
this.notificationService.success("Triggered the Watchlist Import");
}
});
}
public ngOnDestroy() {
this.subscriptions.next();
this.subscriptions.complete();

@ -91,6 +91,17 @@ namespace Ombi.Controllers.V1
return true;
}
/// <summary>
/// Runs the Plex Watchlist Importer
/// </summary>
/// <returns></returns>
[HttpPost("plexwatchlist")]
public async Task<bool> PlexWatchlistImport()
{
await OmbiQuartz.TriggerJob(nameof(IPlexWatchlistImport), "Plex");
return true;
}
/// <summary>
/// Runs the Emby User importer
/// </summary>

@ -626,6 +626,7 @@ namespace Ombi.Controllers.V1
j.MediaDatabaseRefresh = j.MediaDatabaseRefresh.HasValue() ? j.MediaDatabaseRefresh : JobSettingsHelper.MediaDatabaseRefresh(j);
j.AutoDeleteRequests = j.AutoDeleteRequests.HasValue() ? j.AutoDeleteRequests : JobSettingsHelper.AutoDeleteRequests(j);
j.EmbyRecentlyAddedSync = j.EmbyRecentlyAddedSync.HasValue() ? j.EmbyRecentlyAddedSync : JobSettingsHelper.EmbyRecentlyAddedSync(j);
j.PlexWatchlistImport = j.PlexWatchlistImport.HasValue() ? j.PlexWatchlistImport : JobSettingsHelper.PlexWatchlistImport(j);
return j;
}

@ -6,19 +6,16 @@ using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Ombi.Core.Authentication;
using Ombi.Helpers;
using Ombi.Models;
using Ombi.Models.External;
using Ombi.Models.Identity;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Ombi.Core.Settings;
using Ombi.Settings.Settings.Models;
using Ombi.Schedule.Jobs.Plex;
namespace Ombi.Controllers.V1
{
@ -27,26 +24,21 @@ namespace Ombi.Controllers.V1
[ApiController]
public class TokenController : ControllerBase
{
public TokenController(OmbiUserManager um, IOptions<TokenAuthentication> ta, ITokenRepository token,
IPlexOAuthManager oAuthManager, ILogger<TokenController> logger, ISettingsService<AuthenticationSettings> auth,
IPlexWatchlistImport import)
public TokenController(OmbiUserManager um, ITokenRepository token,
IPlexOAuthManager oAuthManager, ILogger<TokenController> logger, ISettingsService<AuthenticationSettings> auth)
{
_userManager = um;
_tokenAuthenticationOptions = ta.Value;
_token = token;
_plexOAuthManager = oAuthManager;
_log = logger;
_authSettings = auth;
_import = import;
}
private readonly TokenAuthentication _tokenAuthenticationOptions;
private readonly ITokenRepository _token;
private readonly OmbiUserManager _userManager;
private readonly IPlexOAuthManager _plexOAuthManager;
private readonly ILogger<TokenController> _log;
private readonly ISettingsService<AuthenticationSettings> _authSettings;
private readonly IPlexWatchlistImport _import;
/// <summary>
/// Gets the token.
@ -57,7 +49,6 @@ namespace Ombi.Controllers.V1
[ProducesResponseType(401)]
public async Task<IActionResult> GetToken([FromBody] UserAuthModel model)
{
await _import.Execute(null);
if (!model.UsePlexOAuth)
{
var user = await _userManager.FindByNameAsync(model.Username);
@ -122,6 +113,7 @@ namespace Ombi.Controllers.V1
{
return Unauthorized();
}
return await CreateToken(true, user);
}

Loading…
Cancel
Save