refactor(newsletter): ♻️ Media servers + newsletter refactoring (#4463)

* Abstract media servers content into interfaces

* Media server entities into abstract classes

* Abstract media server content repository

* First pass at newsletter refactoring

* Minor code clean up

* Attempt at abstracting repositories (WIP)

* Fixed cast issue

* Corrected the other properties

* A step towards newsletter refactoring

* Clean up leftovers

* Fix broken episodes db interaction

* Save absolute URL for Plex content

Let's be consistent with Emby and Jellyfin

* Fix broken integration with Plex libraries

* Fix error when multiple media servers configured

* Fix newsletter being sent if no movies or episodes

* Fix broken tests

* Remove unneccesary logs

* Expose stored media server URL

No need to recalculate it
+ Plex URL was broken due to an earlier change

* Remove unused variable

* Remove obsolete tests

URLs are now fetched from database directly

* Retro-compatibility for Plex content URL

Solves URL for media synced before absolute URL was saved in PlexServerContent

* chore: added some obsoletes

* fix: removed the unsub link when not present

Co-authored-by: tidusjar <tidusjar@gmail.com>
pull/4485/head
sephrat 3 years ago committed by GitHub
parent 32ee4e88ec
commit 0ff0a704ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -929,7 +929,7 @@
<e p="EmbyConfiguration.cs" t="Include" /> <e p="EmbyConfiguration.cs" t="Include" />
<e p="EmbyConnectUser.cs" t="Include" /> <e p="EmbyConnectUser.cs" t="Include" />
<e p="EmbyItemContainer.cs" t="Include" /> <e p="EmbyItemContainer.cs" t="Include" />
<e p="EmbyMediaType.cs" t="Include" /> <e p="MediaType.cs" t="Include" />
<e p="EmbyPolicy.cs" t="Include" /> <e p="EmbyPolicy.cs" t="Include" />
<e p="EmbySystemInfo.cs" t="Include" /> <e p="EmbySystemInfo.cs" t="Include" />
<e p="EmbyUser.cs" t="Include" /> <e p="EmbyUser.cs" t="Include" />
@ -1876,7 +1876,7 @@
<e p="PlexAvailabilityChecker.cs" t="Include" /> <e p="PlexAvailabilityChecker.cs" t="Include" />
<e p="PlexContentSync.cs" t="Include" /> <e p="PlexContentSync.cs" t="Include" />
<e p="PlexEpisodeSync.cs" t="Include" /> <e p="PlexEpisodeSync.cs" t="Include" />
<e p="PlexMediaType.cs" t="Include" /> <e p="MediaType.cs" t="Include" />
<e p="PlexRecentlyAddedSync.cs" t="Include" /> <e p="PlexRecentlyAddedSync.cs" t="Include" />
<e p="PlexUserImporter.cs" t="Include" /> <e p="PlexUserImporter.cs" t="Include" />
</e> </e>

@ -1,10 +0,0 @@
namespace Ombi.Api.Emby.Models
{
public enum EmbyMediaType
{
Movie = 0,
Series = 1,
Music = 2,
Episode = 3
}
}

@ -1,10 +0,0 @@
namespace Ombi.Api.Jellyfin.Models
{
public enum JellyfinMediaType
{
Movie = 0,
Series = 1,
Music = 2,
Episode = 3
}
}

@ -182,11 +182,11 @@ namespace Ombi.Core.Tests.Rule.Request
{ {
new PlexServerContent new PlexServerContent
{ {
Type = PlexMediaTypeEntity.Show, Type = MediaType.Series,
TheMovieDbId = "1", TheMovieDbId = "1",
Title = "Test", Title = "Test",
ReleaseYear = "2001", ReleaseYear = "2001",
Episodes = new List<PlexEpisode> Episodes = new List<IMediaServerEpisode>
{ {
new PlexEpisode new PlexEpisode
{ {

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
@ -18,12 +19,14 @@ namespace Ombi.Core.Tests.Rule.Search
public void Setup() public void Setup()
{ {
ContextMock = new Mock<IEmbyContentRepository>(); ContextMock = new Mock<IEmbyContentRepository>();
LoggerMock = new Mock<ILogger<EmbyAvailabilityRule>>();
SettingsMock = new Mock<ISettingsService<EmbySettings>>(); SettingsMock = new Mock<ISettingsService<EmbySettings>>();
Rule = new EmbyAvailabilityRule(ContextMock.Object, SettingsMock.Object); Rule = new EmbyAvailabilityRule(ContextMock.Object, LoggerMock.Object, SettingsMock.Object);
} }
private EmbyAvailabilityRule Rule { get; set; } private EmbyAvailabilityRule Rule { get; set; }
private Mock<IEmbyContentRepository> ContextMock { get; set; } private Mock<IEmbyContentRepository> ContextMock { get; set; }
private Mock<ILogger<EmbyAvailabilityRule>> LoggerMock { get; set; }
private Mock<ISettingsService<EmbySettings>> SettingsMock { get; set; } private Mock<ISettingsService<EmbySettings>> SettingsMock { get; set; }
[Test] [Test]
@ -44,66 +47,6 @@ namespace Ombi.Core.Tests.Rule.Search
Assert.True(search.Available); Assert.True(search.Available);
} }
[Test]
public async Task Movie_Has_Custom_Url_When_Specified_In_Settings()
{
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new EmbySettings
{
Enable = true,
Servers = new List<EmbyServers>
{
new EmbyServers
{
ServerHostname = "http://test.com/",
ServerId = "8"
}
}
});
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new EmbyContent
{
ProviderId = "123",
EmbyId = 1.ToString(),
});
var search = new SearchMovieViewModel()
{
TheMovieDbId = "123",
};
var result = await Rule.Execute(search);
Assert.True(result.Success);
Assert.That(search.EmbyUrl, Is.EqualTo("http://test.com/web/index.html#!/item?id=1&serverId=8"));
}
[Test]
public async Task Movie_Uses_Default_Url_When()
{
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new EmbySettings
{
Enable = true,
Servers = new List<EmbyServers>
{
new EmbyServers
{
ServerHostname = string.Empty,
ServerId = "8"
}
}
});
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new EmbyContent
{
ProviderId = "123",
EmbyId = 1.ToString()
});
var search = new SearchMovieViewModel()
{
TheMovieDbId = "123",
};
var result = await Rule.Execute(search);
Assert.True(result.Success);
Assert.That(search.EmbyUrl, Is.EqualTo("https://app.emby.media/web/index.html#!/item?id=1&serverId=8"));
}
[Test] [Test]
public async Task Movie_ShouldBe_NotAvailable_WhenNotFoundInEmby() public async Task Movie_ShouldBe_NotAvailable_WhenNotFoundInEmby()
{ {

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
@ -18,12 +19,14 @@ namespace Ombi.Core.Tests.Rule.Search
public void Setup() public void Setup()
{ {
ContextMock = new Mock<IJellyfinContentRepository>(); ContextMock = new Mock<IJellyfinContentRepository>();
LoggerMock = new Mock<ILogger<JellyfinAvailabilityRule>>();
SettingsMock = new Mock<ISettingsService<JellyfinSettings>>(); SettingsMock = new Mock<ISettingsService<JellyfinSettings>>();
Rule = new JellyfinAvailabilityRule(ContextMock.Object, SettingsMock.Object); Rule = new JellyfinAvailabilityRule(ContextMock.Object, LoggerMock.Object, SettingsMock.Object);
} }
private JellyfinAvailabilityRule Rule { get; set; } private JellyfinAvailabilityRule Rule { get; set; }
private Mock<IJellyfinContentRepository> ContextMock { get; set; } private Mock<IJellyfinContentRepository> ContextMock { get; set; }
private Mock<ILogger<JellyfinAvailabilityRule>> LoggerMock { get; set; }
private Mock<ISettingsService<JellyfinSettings>> SettingsMock { get; set; } private Mock<ISettingsService<JellyfinSettings>> SettingsMock { get; set; }
[Test] [Test]
@ -44,36 +47,6 @@ namespace Ombi.Core.Tests.Rule.Search
Assert.True(search.Available); Assert.True(search.Available);
} }
[Test]
public async Task Movie_Has_Custom_Url_When_Specified_In_Settings()
{
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings
{
Enable = true,
Servers = new List<JellyfinServers>
{
new JellyfinServers
{
ServerHostname = "http://test.com/",
ServerId = "8"
}
}
});
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new JellyfinContent
{
ProviderId = "123",
JellyfinId = 1.ToString(),
});
var search = new SearchMovieViewModel()
{
TheMovieDbId = "123",
};
var result = await Rule.Execute(search);
Assert.True(result.Success);
Assert.That(search.JellyfinUrl, Is.EqualTo("http://test.com/web/index.html#!/details?id=1&serverId=8"));
}
[Test] [Test]
public async Task Movie_Uses_Default_Url_When() public async Task Movie_Uses_Default_Url_When()
{ {

@ -30,26 +30,26 @@ namespace Ombi.Core.Engine
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(DateTime from, DateTime to) public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(DateTime from, DateTime to)
{ {
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie && x.AddedAt > from && x.AddedAt < to); var plexMovies = _plex.GetAll().Where(x => x.Type == MediaType.Movie && x.AddedAt > from && x.AddedAt < to);
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.Movie && x.AddedAt > from && x.AddedAt < to); var embyMovies = _emby.GetAll().Where(x => x.Type == MediaType.Movie && x.AddedAt > from && x.AddedAt < to);
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == JellyfinMediaType.Movie && x.AddedAt > from && x.AddedAt < to); var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == MediaType.Movie && x.AddedAt > from && x.AddedAt < to);
return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies).Take(30); return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies).Take(30);
} }
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies() public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies()
{ {
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie); var plexMovies = _plex.GetAll().Where(x => x.Type == MediaType.Movie);
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.Movie); var embyMovies = _emby.GetAll().Where(x => x.Type == MediaType.Movie);
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == JellyfinMediaType.Movie); var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == MediaType.Movie);
return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies); return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies);
} }
public IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(DateTime from, DateTime to, bool groupBySeason) public IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(DateTime from, DateTime to, bool groupBySeason)
{ {
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show && x.AddedAt > from && x.AddedAt < to); var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == MediaType.Series && x.AddedAt > from && x.AddedAt < to);
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == EmbyMediaType.Series && x.AddedAt > from && x.AddedAt < to); var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == MediaType.Series && x.AddedAt > from && x.AddedAt < to);
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == JellyfinMediaType.Series && x.AddedAt > from && x.AddedAt < to); var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == MediaType.Series && x.AddedAt > from && x.AddedAt < to);
return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason).Take(30); return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason).Take(30);
} }
@ -57,9 +57,9 @@ namespace Ombi.Core.Engine
public IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(bool groupBySeason) public IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(bool groupBySeason)
{ {
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show); var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == MediaType.Series);
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == EmbyMediaType.Series); var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == MediaType.Series);
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == JellyfinMediaType.Series); var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == MediaType.Series);
return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason); return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason);
} }
@ -76,7 +76,7 @@ namespace Ombi.Core.Engine
{ {
continue; continue;
} }
if (p.Type == PlexMediaTypeEntity.Movie) if (p.Type == MediaType.Movie)
{ {
recentlyAddedLog.Add(new RecentlyAddedLog recentlyAddedLog.Add(new RecentlyAddedLog
{ {
@ -114,7 +114,7 @@ namespace Ombi.Core.Engine
{ {
continue; continue;
} }
if (e.Type == EmbyMediaType.Movie) if (e.Type == MediaType.Movie)
{ {
recentlyAddedLog.Add(new RecentlyAddedLog recentlyAddedLog.Add(new RecentlyAddedLog
{ {
@ -152,7 +152,7 @@ namespace Ombi.Core.Engine
{ {
continue; continue;
} }
if (e.Type == JellyfinMediaType.Movie) if (e.Type == MediaType.Movie)
{ {
recentlyAddedLog.Add(new RecentlyAddedLog recentlyAddedLog.Add(new RecentlyAddedLog
{ {

@ -32,7 +32,7 @@ namespace Ombi.Core.Rule.Rules.Request
{ {
var tvRequest = (ChildRequests) obj; var tvRequest = (ChildRequests) obj;
var tvContent = _plexContent.GetAll().Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show); var tvContent = _plexContent.GetAll().Include(x => x.Episodes).Where(x => x.Type == MediaType.Series);
// We need to do a check on the TVDBId // We need to do a check on the TVDBId
var anyMovieDbMatches = await tvContent.FirstOrDefaultAsync(x => x.TheMovieDbId.Length > 0 && x.TheMovieDbId == tvRequest.Id.ToString()); var anyMovieDbMatches = await tvContent.FirstOrDefaultAsync(x => x.TheMovieDbId.Length > 0 && x.TheMovieDbId == tvRequest.Id.ToString());
if (anyMovieDbMatches == null) if (anyMovieDbMatches == null)

@ -46,10 +46,10 @@ namespace Ombi.Core.Rule.Rules.Search
} }
} }
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<PlexEpisode> allEpisodes, EpisodeRequests episode, public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<IMediaServerEpisode> allEpisodes, EpisodeRequests episode,
SeasonRequests season, PlexServerContent item, bool useTheMovieDb, bool useTvDb, ILogger log) SeasonRequests season, IMediaServerContent item, bool useTheMovieDb, bool useTvDb, ILogger log)
{ {
PlexEpisode epExists = null; IMediaServerEpisode epExists = null;
try try
{ {
@ -79,66 +79,6 @@ namespace Ombi.Core.Rule.Rules.Search
log.LogError(e, "Exception thrown when attempting to check if something is available"); log.LogError(e, "Exception thrown when attempting to check if something is available");
} }
if (epExists != null)
{
episode.Available = true;
}
}
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<EmbyEpisode> allEpisodes, EpisodeRequests episode,
SeasonRequests season, EmbyContent item, bool useTheMovieDb, bool useTvDb)
{
EmbyEpisode epExists = null;
if (useImdb)
{
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.ImdbId == item.ImdbId);
}
if (useTheMovieDb)
{
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.TheMovieDbId == item.TheMovieDbId);
}
if (useTvDb)
{
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.TvDbId == item.TvDbId);
}
if (epExists != null)
{
episode.Available = true;
}
}
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<JellyfinEpisode> allEpisodes, EpisodeRequests episode,
SeasonRequests season, JellyfinContent item, bool useTheMovieDb, bool useTvDb)
{
JellyfinEpisode epExists = null;
if (useImdb)
{
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.ImdbId == item.ImdbId);
}
if (useTheMovieDb)
{
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.TheMovieDbId == item.TheMovieDbId);
}
if (useTvDb)
{
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.TvDbId == item.TvDbId);
}
if (epExists != null) if (epExists != null)
{ {
episode.Available = true; episode.Available = true;

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces; using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Settings; using Ombi.Core.Settings;
@ -13,13 +14,15 @@ namespace Ombi.Core.Rule.Rules.Search
{ {
public class EmbyAvailabilityRule : BaseSearchRule, IRules<SearchViewModel> public class EmbyAvailabilityRule : BaseSearchRule, IRules<SearchViewModel>
{ {
public EmbyAvailabilityRule(IEmbyContentRepository repo, ISettingsService<EmbySettings> s) public EmbyAvailabilityRule(IEmbyContentRepository repo, ILogger<EmbyAvailabilityRule> log, ISettingsService<EmbySettings> s)
{ {
EmbyContentRepository = repo; EmbyContentRepository = repo;
Log = log;
EmbySettings = s; EmbySettings = s;
} }
private IEmbyContentRepository EmbyContentRepository { get; } private IEmbyContentRepository EmbyContentRepository { get; }
private ILogger Log { get; }
private ISettingsService<EmbySettings> EmbySettings { get; } private ISettingsService<EmbySettings> EmbySettings { get; }
public async Task<RuleResult> Execute(SearchViewModel obj) public async Task<RuleResult> Execute(SearchViewModel obj)
@ -64,19 +67,7 @@ namespace Ombi.Core.Rule.Rules.Search
if (item != null) if (item != null)
{ {
obj.Available = true; obj.Available = true;
var s = await EmbySettings.GetSettingsAsync(); obj.EmbyUrl = item.Url;
if (s.Enable)
{
var server = s.Servers.FirstOrDefault();
if ((server?.ServerHostname ?? string.Empty).HasValue())
{
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname);
}
else
{
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, null);
}
}
if (obj.Type == RequestType.TvShow) if (obj.Type == RequestType.TvShow)
{ {
@ -89,7 +80,7 @@ namespace Ombi.Core.Rule.Rules.Search
{ {
foreach (var episode in season.Episodes) foreach (var episode in season.Episodes)
{ {
await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb); await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb, Log);
} }
} }
} }

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces; using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Settings; using Ombi.Core.Settings;
@ -13,13 +14,15 @@ namespace Ombi.Core.Rule.Rules.Search
{ {
public class JellyfinAvailabilityRule : BaseSearchRule, IRules<SearchViewModel> public class JellyfinAvailabilityRule : BaseSearchRule, IRules<SearchViewModel>
{ {
public JellyfinAvailabilityRule(IJellyfinContentRepository repo, ISettingsService<JellyfinSettings> s) public JellyfinAvailabilityRule(IJellyfinContentRepository repo, ILogger<JellyfinAvailabilityRule> log, ISettingsService<JellyfinSettings> s)
{ {
JellyfinContentRepository = repo; JellyfinContentRepository = repo;
Log = log;
JellyfinSettings = s; JellyfinSettings = s;
} }
private IJellyfinContentRepository JellyfinContentRepository { get; } private IJellyfinContentRepository JellyfinContentRepository { get; }
private ILogger Log { get; }
private ISettingsService<JellyfinSettings> JellyfinSettings { get; } private ISettingsService<JellyfinSettings> JellyfinSettings { get; }
public async Task<RuleResult> Execute(SearchViewModel obj) public async Task<RuleResult> Execute(SearchViewModel obj)
@ -78,20 +81,7 @@ namespace Ombi.Core.Rule.Rules.Search
useTheMovieDb = true; useTheMovieDb = true;
} }
obj.Available = true; obj.Available = true;
var s = await JellyfinSettings.GetSettingsAsync(); obj.JellyfinUrl = item.Url;
if (s.Enable)
{
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
if ((server?.ServerHostname ?? string.Empty).HasValue())
{
obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, server?.ServerId, server?.ServerHostname);
}
else
{
var firstServer = s.Servers?.FirstOrDefault();
obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, firstServer.ServerId, firstServer.FullUri);
}
}
if (obj.Type == RequestType.TvShow) if (obj.Type == RequestType.TvShow)
{ {
@ -104,7 +94,7 @@ namespace Ombi.Core.Rule.Rules.Search
{ {
foreach (var episode in season.Episodes) foreach (var episode in season.Episodes)
{ {
await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb); await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb, Log);
} }
} }
} }

@ -33,7 +33,7 @@ namespace Ombi.Core.Rule.Rules.Search
var useId = false; var useId = false;
var useTvDb = false; var useTvDb = false;
PlexMediaTypeEntity type = ConvertType(obj.Type); MediaType type = ConvertType(obj.Type);
if (obj.ImdbId.HasValue()) if (obj.ImdbId.HasValue())
{ {
@ -90,9 +90,17 @@ namespace Ombi.Core.Rule.Rules.Search
useTheMovieDb = true; useTheMovieDb = true;
} }
obj.Available = true; obj.Available = true;
obj.PlexUrl = PlexHelper.BuildPlexMediaUrl(item.Url, host); if (item.Url.StartsWith("http"))
{
obj.PlexUrl = item.Url;
}
else
{
// legacy content
obj.PlexUrl = PlexHelper.BuildPlexMediaUrl(item.Url, host);
}
obj.Quality = item.Quality; obj.Quality = item.Quality;
if (obj.Type == RequestType.TvShow) if (obj.Type == RequestType.TvShow)
{ {
var search = (SearchTvShowViewModel)obj; var search = (SearchTvShowViewModel)obj;
@ -115,12 +123,12 @@ namespace Ombi.Core.Rule.Rules.Search
return Success(); return Success();
} }
private PlexMediaTypeEntity ConvertType(RequestType type) => private MediaType ConvertType(RequestType type) =>
type switch type switch
{ {
RequestType.Movie => PlexMediaTypeEntity.Movie, RequestType.Movie => MediaType.Movie,
RequestType.TvShow => PlexMediaTypeEntity.Show, RequestType.TvShow => MediaType.Series,
_ => PlexMediaTypeEntity.Movie, _ => MediaType.Movie,
}; };
} }
} }

@ -104,11 +104,11 @@ namespace Ombi.Helpers
return new ProviderId(); return new ProviderId();
} }
public static string GetPlexMediaUrl(string machineId, int mediaId) public static string GetPlexMediaUrl(string machineId, int mediaId, string plexHost)
{ {
var url = var url =
$"web/#!/server/{machineId}/details?key=%2flibrary%2Fmetadata%2F{mediaId}"; $"web/#!/server/{machineId}/details?key=%2flibrary%2Fmetadata%2F{mediaId}";
return url; return BuildPlexMediaUrl(url, plexHost);
} }
public static string BuildPlexMediaUrl(string savedUrl, string plexHost) public static string BuildPlexMediaUrl(string savedUrl, string plexHost)

@ -30,6 +30,7 @@ namespace Ombi.Notifications.Templates
private const string TableLocation = "{@RECENTLYADDED}"; private const string TableLocation = "{@RECENTLYADDED}";
private const string IntroText = "{@INTRO}"; private const string IntroText = "{@INTRO}";
private const string Unsubscribe = "{@UNSUBSCRIBE}"; private const string Unsubscribe = "{@UNSUBSCRIBE}";
private const string UnsubscribeText = "{@UNSUBSCRIBETEXT}";
public string LoadTemplate(string subject, string intro, string tableHtml, string logo, string unsubscribeLink) public string LoadTemplate(string subject, string intro, string tableHtml, string logo, string unsubscribeLink)
@ -41,6 +42,7 @@ namespace Ombi.Notifications.Templates
sb.Replace(DateKey, DateTime.Now.ToString("f")); sb.Replace(DateKey, DateTime.Now.ToString("f"));
sb.Replace(Logo, string.IsNullOrEmpty(logo) ? OmbiLogo : logo); sb.Replace(Logo, string.IsNullOrEmpty(logo) ? OmbiLogo : logo);
sb.Replace(Unsubscribe, string.IsNullOrEmpty(unsubscribeLink) ? string.Empty : unsubscribeLink); sb.Replace(Unsubscribe, string.IsNullOrEmpty(unsubscribeLink) ? string.Empty : unsubscribeLink);
sb.Replace(UnsubscribeText, string.IsNullOrEmpty(unsubscribeLink) ? string.Empty : "Unsubscrible");
return sb.ToString(); return sb.ToString();
} }

@ -453,7 +453,7 @@
<tbody> <tbody>
<tr> <tr>
<td class="content-block powered-by" valign="top" align="center" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;"> <td class="content-block powered-by" valign="top" align="center" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;">
<a href="{@UNSUBSCRIBE}" style="font-weight: 400; font-size: 12px; text-align: center; color: #ff761b;">Unsubscribe</a> <a href="{@UNSUBSCRIBE}" style="font-weight: 400; font-size: 12px; text-align: center; color: #ff761b;">{@UNSUBSCRIBETEXT}</a>
</td> </td>
</tr> </tr>
<tr> <tr>

@ -124,7 +124,7 @@ namespace Ombi.Schedule.Jobs.Emby
var tvDbId = child.ParentRequest.TvDbId; var tvDbId = child.ParentRequest.TvDbId;
var imdbId = child.ParentRequest.ImdbId; var imdbId = child.ParentRequest.ImdbId;
IQueryable<EmbyEpisode> seriesEpisodes = null; IQueryable<IMediaServerEpisode> seriesEpisodes = null;
if (useImdb) if (useImdb)
{ {
seriesEpisodes = embyEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); seriesEpisodes = embyEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString());

@ -16,7 +16,7 @@ using Ombi.Schedule.Jobs.Ombi;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Quartz; using Quartz;
using EmbyMediaType = Ombi.Store.Entities.EmbyMediaType; using MediaType = Ombi.Store.Entities.MediaType;
namespace Ombi.Schedule.Jobs.Emby namespace Ombi.Schedule.Jobs.Emby
{ {
@ -165,10 +165,10 @@ namespace Ombi.Schedule.Jobs.Emby
ImdbId = tvShow.ProviderIds?.Imdb, ImdbId = tvShow.ProviderIds?.Imdb,
TheMovieDbId = tvShow.ProviderIds?.Tmdb, TheMovieDbId = tvShow.ProviderIds?.Tmdb,
Title = tvShow.Name, Title = tvShow.Name,
Type = EmbyMediaType.Series, Type = MediaType.Series,
EmbyId = tvShow.Id, EmbyId = tvShow.Id,
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname), Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname),
AddedAt = DateTime.UtcNow AddedAt = DateTime.UtcNow,
}); });
} }
else else
@ -255,10 +255,10 @@ namespace Ombi.Schedule.Jobs.Emby
ImdbId = movieInfo.ProviderIds.Imdb, ImdbId = movieInfo.ProviderIds.Imdb,
TheMovieDbId = movieInfo.ProviderIds?.Tmdb, TheMovieDbId = movieInfo.ProviderIds?.Tmdb,
Title = movieInfo.Name, Title = movieInfo.Name,
Type = EmbyMediaType.Movie, Type = MediaType.Movie,
EmbyId = movieInfo.Id, EmbyId = movieInfo.Id,
Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname), Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname),
AddedAt = DateTime.UtcNow, AddedAt = DateTime.UtcNow
}); });
} }
else else

@ -151,7 +151,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin
var tvDbId = child.ParentRequest.TvDbId; var tvDbId = child.ParentRequest.TvDbId;
var imdbId = child.ParentRequest.ImdbId; var imdbId = child.ParentRequest.ImdbId;
IQueryable<JellyfinEpisode> seriesEpisodes = null; IQueryable<IMediaServerEpisode> seriesEpisodes = null;
if (useImdb) if (useImdb)
{ {
seriesEpisodes = jellyfinEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); seriesEpisodes = jellyfinEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString());

@ -14,7 +14,7 @@ using Ombi.Schedule.Jobs.Ombi;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Quartz; using Quartz;
using JellyfinMediaType = Ombi.Store.Entities.JellyfinMediaType; using MediaType = Ombi.Store.Entities.MediaType;
namespace Ombi.Schedule.Jobs.Jellyfin namespace Ombi.Schedule.Jobs.Jellyfin
{ {
@ -143,7 +143,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin
ImdbId = tvShow.ProviderIds?.Imdb, ImdbId = tvShow.ProviderIds?.Imdb,
TheMovieDbId = tvShow.ProviderIds?.Tmdb, TheMovieDbId = tvShow.ProviderIds?.Tmdb,
Title = tvShow.Name, Title = tvShow.Name,
Type = JellyfinMediaType.Series, Type = MediaType.Series,
JellyfinId = tvShow.Id, JellyfinId = tvShow.Id,
Url = JellyfinHelper.GetJellyfinMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname), Url = JellyfinHelper.GetJellyfinMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname),
AddedAt = DateTime.UtcNow AddedAt = DateTime.UtcNow
@ -223,10 +223,10 @@ namespace Ombi.Schedule.Jobs.Jellyfin
ImdbId = movieInfo.ProviderIds.Imdb, ImdbId = movieInfo.ProviderIds.Imdb,
TheMovieDbId = movieInfo.ProviderIds?.Tmdb, TheMovieDbId = movieInfo.ProviderIds?.Tmdb,
Title = movieInfo.Name, Title = movieInfo.Name,
Type = JellyfinMediaType.Movie, Type = MediaType.Movie,
JellyfinId = movieInfo.Id, JellyfinId = movieInfo.Id,
Url = JellyfinHelper.GetJellyfinMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname), Url = JellyfinHelper.GetJellyfinMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname),
AddedAt = DateTime.UtcNow, AddedAt = DateTime.UtcNow
}); });
} }
else else

@ -5,7 +5,8 @@ namespace Ombi.Schedule.Jobs.Ombi
{ {
public abstract class HtmlTemplateGenerator public abstract class HtmlTemplateGenerator
{ {
protected virtual void AddBackgroundInsideTable(StringBuilder sb, string url) protected StringBuilder sb;
protected virtual void AddBackgroundInsideTable(string url)
{ {
sb.Append("<td align=\"center\" valign=\"top\" width=\"500\" height=\"252\" class=\"media-card\" style=\"font-size: 14px; font-family: 'Open Sans', Helvetica, Arial, sans-serif; vertical-align: top; padding: 3px; width: 500px; min-width: 500px; max-width: 500px; height: 252px; max-height: 252px; \">"); sb.Append("<td align=\"center\" valign=\"top\" width=\"500\" height=\"252\" class=\"media-card\" style=\"font-size: 14px; font-family: 'Open Sans', Helvetica, Arial, sans-serif; vertical-align: top; padding: 3px; width: 500px; min-width: 500px; max-width: 500px; height: 252px; max-height: 252px; \">");
sb.AppendFormat("<table class=\"card-bg\" width=\"500\" height=\"252\" background=\"url(0)\" bgcolor=\"#1f1f1f\" style=\"background-image: url(0); border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 500px; background-color: #1f1f1f; background-position: center; background-size: cover; background-repeat: no-repeat; background-clip: padding-box; border: 2px solid rgba(255, 118, 27, .4); height: 252px; max-height: 252px; \">", url); sb.AppendFormat("<table class=\"card-bg\" width=\"500\" height=\"252\" background=\"url(0)\" bgcolor=\"#1f1f1f\" style=\"background-image: url(0); border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 500px; background-color: #1f1f1f; background-position: center; background-size: cover; background-repeat: no-repeat; background-clip: padding-box; border: 2px solid rgba(255, 118, 27, .4); height: 252px; max-height: 252px; \">", url);
@ -14,14 +15,14 @@ namespace Ombi.Schedule.Jobs.Ombi
sb.Append("<table class=\"bg-tint\" width=\"100%\" bgcolor=\"rgba(0, 0, 0, .6)\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: rgba(0, 0, 0, .6); \">"); sb.Append("<table class=\"bg-tint\" width=\"100%\" bgcolor=\"rgba(0, 0, 0, .6)\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: rgba(0, 0, 0, .6); \">");
} }
protected virtual void AddPosterInsideTable(StringBuilder sb, string url) protected virtual void AddPosterInsideTable(string url)
{ {
sb.Append("<tr>"); sb.Append("<tr>");
sb.Append("<td class=\"poster-container\" width=\"150\" height=\"225\" valign=\"top\" style=\"ont-family: sans-serif; font-size: 14px; vertical-align: top; width: 150px; min-width: 150px; height: 225px; max-height: 225px; min-height: 225px; \">"); sb.Append("<td class=\"poster-container\" width=\"150\" height=\"225\" valign=\"top\" style=\"ont-family: sans-serif; font-size: 14px; vertical-align: top; width: 150px; min-width: 150px; height: 225px; max-height: 225px; min-height: 225px; \">");
sb.AppendFormat("<table class=\"poster-img\" width=\"100%\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">", url); sb.AppendFormat("<table class=\"poster-img\" width=\"100%\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">", url);
} }
protected virtual void AddMediaServerUrl(StringBuilder sb, string mediaurl, string url) protected virtual void AddMediaServerUrl(string mediaurl, string url)
{ {
if (url.HasValue()) if (url.HasValue())
{ {
@ -41,14 +42,14 @@ namespace Ombi.Schedule.Jobs.Ombi
sb.Append("</td>"); sb.Append("</td>");
} }
protected virtual void AddInfoTable(StringBuilder sb) protected virtual void AddInfoTable()
{ {
sb.Append( sb.Append(
"<td class=\"movie-info\" height=\"227\" valign=\"top\" align=\"left\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; padding-left: 4px; text-align: left; height: 227px; \">"); "<td class=\"movie-info\" height=\"227\" valign=\"top\" align=\"left\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; padding-left: 4px; text-align: left; height: 227px; \">");
sb.Append("<table style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; height: 100%; \">"); sb.Append("<table style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; height: 100%; \">");
} }
protected virtual void AddTitle(StringBuilder sb, string url, string title) protected virtual void AddTitle( string url, string title)
{ {
sb.Append("<tr class=\"title\" valign=\"top\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 22px;line-height: 24px;vertical-align: top;max-width: 320px;display: block;height: 50px;min-height: 50px;max-height: 50px; \">"); sb.Append("<tr class=\"title\" valign=\"top\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 22px;line-height: 24px;vertical-align: top;max-width: 320px;display: block;height: 50px;min-height: 50px;max-height: 50px; \">");
sb.Append("<td>"); sb.Append("<td>");
@ -59,7 +60,7 @@ namespace Ombi.Schedule.Jobs.Ombi
sb.Append("</tr>"); sb.Append("</tr>");
} }
protected virtual void AddParagraph(StringBuilder sb, string text) protected virtual void AddParagraph(string text)
{ {
sb.Append("<tr class=\"description\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;height: 130px;max-height: 130px;max-width: 320px;overflow: hidden;text-overflow: ellipsis;display: block;font-size: 14px !important;text-align: justify;\" valign=\"top\">"); sb.Append("<tr class=\"description\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;height: 130px;max-height: 130px;max-width: 320px;overflow: hidden;text-overflow: ellipsis;display: block;font-size: 14px !important;text-align: justify;\" valign=\"top\">");
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">"); sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");
@ -68,7 +69,7 @@ namespace Ombi.Schedule.Jobs.Ombi
sb.Append("</tr>"); sb.Append("</tr>");
} }
protected virtual void AddTvParagraph(StringBuilder sb, string episodes, string summary) protected virtual void AddTvParagraph(string episodes, string summary)
{ {
sb.Append("<tr class=\"description\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;height: 130px;max-height: 130px;max-width: 320px;overflow: hidden;text-overflow: ellipsis;display: block;font-size: 14px !important;text-align: justify;\" valign=\"top\">"); sb.Append("<tr class=\"description\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;height: 130px;max-height: 130px;max-width: 320px;overflow: hidden;text-overflow: ellipsis;display: block;font-size: 14px !important;text-align: justify;\" valign=\"top\">");
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">"); sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");
@ -78,7 +79,7 @@ namespace Ombi.Schedule.Jobs.Ombi
sb.Append("</tr>"); sb.Append("</tr>");
} }
protected virtual void AddGenres(StringBuilder sb, string text) protected virtual void AddGenres(string text)
{ {
sb.Append("<tr class=\"meta\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; max-width: 300px; min-width: 300px; padding: 3px 7px; margin-top: 10px; line-height: 1; text-align: left; white-space: nowrap; vertical-align: middle; background-color: rgba(255, 118, 27, 0.5); color: #fff; border-radius: 2px; overflow: hidden; display: block; font-size: 0.9rem;\" align=\"left\" valign=\"middle\" bgcolor=\"rgba(255, 118, 27, 0.5)\">"); sb.Append("<tr class=\"meta\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; max-width: 300px; min-width: 300px; padding: 3px 7px; margin-top: 10px; line-height: 1; text-align: left; white-space: nowrap; vertical-align: middle; background-color: rgba(255, 118, 27, 0.5); color: #fff; border-radius: 2px; overflow: hidden; display: block; font-size: 0.9rem;\" align=\"left\" valign=\"middle\" bgcolor=\"rgba(255, 118, 27, 0.5)\">");
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">"); sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");

File diff suppressed because it is too large Load Diff

@ -116,12 +116,12 @@ namespace Ombi.Schedule.Jobs.Ombi
{ {
// Ensure we check that we have not linked this item to a request // Ensure we check that we have not linked this item to a request
var allMovies = await _plexRepo.GetAll().Where(x => var allMovies = await _plexRepo.GetAll().Where(x =>
x.Type == PlexMediaTypeEntity.Movie && x.RequestId == null && (x.TheMovieDbId == null || x.ImdbId == null)).ToListAsync(); x.Type == MediaType.Movie && x.RequestId == null && (x.TheMovieDbId == null || x.ImdbId == null)).ToListAsync();
await StartPlexMovies(allMovies); await StartPlexMovies(allMovies);
// Now Tv // Now Tv
var allTv = await _plexRepo.GetAll().Where(x => var allTv = await _plexRepo.GetAll().Where(x =>
x.Type == PlexMediaTypeEntity.Show && x.RequestId == null && (x.TheMovieDbId == null || x.ImdbId == null || x.TvDbId == null)).ToListAsync(); x.Type == MediaType.Series && x.RequestId == null && (x.TheMovieDbId == null || x.ImdbId == null || x.TvDbId == null)).ToListAsync();
await StartPlexTv(allTv); await StartPlexTv(allTv);
} }
@ -178,7 +178,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private async Task StartEmbyTv() private async Task StartEmbyTv()
{ {
var allTv = await _embyRepo.GetAll().Where(x => var allTv = await _embyRepo.GetAll().Where(x =>
x.Type == EmbyMediaType.Series && (x.TheMovieDbId == null || x.ImdbId == null || x.TvDbId == null)).ToListAsync(); x.Type == MediaType.Series && (x.TheMovieDbId == null || x.ImdbId == null || x.TvDbId == null)).ToListAsync();
foreach (var show in allTv) foreach (var show in allTv)
{ {
@ -213,7 +213,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private async Task StartJellyfinTv() private async Task StartJellyfinTv()
{ {
var allTv = await _jellyfinRepo.GetAll().Where(x => var allTv = await _jellyfinRepo.GetAll().Where(x =>
x.Type == JellyfinMediaType.Series && (x.TheMovieDbId == null || x.ImdbId == null || x.TvDbId == null)).ToListAsync(); x.Type == MediaType.Series && (x.TheMovieDbId == null || x.ImdbId == null || x.TvDbId == null)).ToListAsync();
foreach (var show in allTv) foreach (var show in allTv)
{ {
@ -278,7 +278,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private async Task StartEmbyMovies(EmbySettings settings) private async Task StartEmbyMovies(EmbySettings settings)
{ {
var allMovies = await _embyRepo.GetAll().Where(x => var allMovies = await _embyRepo.GetAll().Where(x =>
x.Type == EmbyMediaType.Movie && (x.TheMovieDbId == null || x.ImdbId == null)).ToListAsync(); x.Type == MediaType.Movie && (x.TheMovieDbId == null || x.ImdbId == null)).ToListAsync();
foreach (var movie in allMovies) foreach (var movie in allMovies)
{ {
movie.ImdbId.HasValue(); movie.ImdbId.HasValue();
@ -333,7 +333,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private async Task StartJellyfinMovies(JellyfinSettings settings) private async Task StartJellyfinMovies(JellyfinSettings settings)
{ {
var allMovies = await _jellyfinRepo.GetAll().Where(x => var allMovies = await _jellyfinRepo.GetAll().Where(x =>
x.Type == JellyfinMediaType.Movie && (x.TheMovieDbId == null || x.ImdbId == null)).ToListAsync(); x.Type == MediaType.Movie && (x.TheMovieDbId == null || x.ImdbId == null)).ToListAsync();
foreach (var movie in allMovies) foreach (var movie in allMovies)
{ {
movie.ImdbId.HasValue(); movie.ImdbId.HasValue();

@ -86,7 +86,7 @@ namespace Ombi.Schedule.Jobs.Plex
var tvDbId = child.ParentRequest.TvDbId; var tvDbId = child.ParentRequest.TvDbId;
var imdbId = child.ParentRequest.ImdbId; var imdbId = child.ParentRequest.ImdbId;
IQueryable<PlexEpisode> seriesEpisodes = null; IQueryable<IMediaServerEpisode> seriesEpisodes = null;
if (useImdb) if (useImdb)
{ {
seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString());
@ -105,8 +105,7 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
// Let's try and match the series by name // Let's try and match the series by name
seriesEpisodes = plexEpisodes.Where(x => seriesEpisodes = plexEpisodes.Where(x =>
x.Series.Title == child.Title && x.Series.Title == child.Title);
x.Series.ReleaseYear == child.ParentRequest.ReleaseDate.Year.ToString());
} }

@ -226,7 +226,7 @@ namespace Ombi.Schedule.Jobs.Plex
await Repo.SaveChangesAsync(); await Repo.SaveChangesAsync();
if (content.Metadata != null) if (content.Metadata != null)
{ {
var episodesAdded = await EpisodeSync.ProcessEpsiodes(content.Metadata, allEps); var episodesAdded = await EpisodeSync.ProcessEpsiodes(content.Metadata, (IQueryable<PlexEpisode>)allEps);
episodesProcessed.AddRange(episodesAdded.Select(x => x.Id)); episodesProcessed.AddRange(episodesAdded.Select(x => x.Id));
} }
} }
@ -301,7 +301,7 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
var existing = await Repo.GetFirstContentByCustom(x => x.Title == movie.title var existing = await Repo.GetFirstContentByCustom(x => x.Title == movie.title
&& x.ReleaseYear == movie.year.ToString() && x.ReleaseYear == movie.year.ToString()
&& x.Type == PlexMediaTypeEntity.Movie); && x.Type == MediaType.Movie);
// The rating key keeps changing // The rating key keeps changing
//var existing = await Repo.GetByKey(movie.ratingKey); //var existing = await Repo.GetByKey(movie.ratingKey);
if (existing != null) if (existing != null)
@ -349,9 +349,9 @@ namespace Ombi.Schedule.Jobs.Plex
AddedAt = DateTime.Now, AddedAt = DateTime.Now,
Key = movie.ratingKey, Key = movie.ratingKey,
ReleaseYear = movie.year.ToString(), ReleaseYear = movie.year.ToString(),
Type = PlexMediaTypeEntity.Movie, Type = MediaType.Movie,
Title = movie.title, Title = movie.title,
Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, movie.ratingKey), Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, movie.ratingKey, servers.ServerHostname),
Seasons = new List<PlexSeasonsContent>(), Seasons = new List<PlexSeasonsContent>(),
Quality = movie.Media?.FirstOrDefault()?.videoResolution ?? string.Empty Quality = movie.Media?.FirstOrDefault()?.videoResolution ?? string.Empty
}; };
@ -411,7 +411,7 @@ namespace Ombi.Schedule.Jobs.Plex
// Let's try and match // Let's try and match
var existingContent = await Repo.GetFirstContentByCustom(x => x.Title == show.title var existingContent = await Repo.GetFirstContentByCustom(x => x.Title == show.title
&& x.ReleaseYear == show.year.ToString() && x.ReleaseYear == show.year.ToString()
&& x.Type == PlexMediaTypeEntity.Show); && x.Type == MediaType.Series);
// Just double check the rating key, since this is our unique constraint // Just double check the rating key, since this is our unique constraint
var existingKey = await Repo.GetByKey(show.ratingKey); var existingKey = await Repo.GetByKey(show.ratingKey);
@ -463,7 +463,7 @@ namespace Ombi.Schedule.Jobs.Plex
Repo.DeleteWithoutSave(existingContent); Repo.DeleteWithoutSave(existingContent);
// Because we have changed the rating key, we need to change all children too // Because we have changed the rating key, we need to change all children too
var episodeToChange = Repo.GetAllEpisodes().Where(x => x.GrandparentKey == oldKey); var episodeToChange = Repo.GetAllEpisodes().Cast<PlexEpisode>().Where(x => x.GrandparentKey == oldKey);
if (episodeToChange.Any()) if (episodeToChange.Any())
{ {
foreach (var e in episodeToChange) foreach (var e in episodeToChange)
@ -553,9 +553,9 @@ namespace Ombi.Schedule.Jobs.Plex
AddedAt = DateTime.Now, AddedAt = DateTime.Now,
Key = show.ratingKey, Key = show.ratingKey,
ReleaseYear = show.year.ToString(), ReleaseYear = show.year.ToString(),
Type = PlexMediaTypeEntity.Show, Type = MediaType.Series,
Title = show.title, Title = show.title,
Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, show.ratingKey), Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, show.ratingKey, servers.ServerHostname),
Seasons = new List<PlexSeasonsContent>() Seasons = new List<PlexSeasonsContent>()
}; };
await GetProviderIds(showMetadata, item); await GetProviderIds(showMetadata, item);
@ -567,19 +567,19 @@ namespace Ombi.Schedule.Jobs.Plex
if (item.ImdbId.HasValue()) if (item.ImdbId.HasValue())
{ {
existingImdb = await Repo.GetAll().AnyAsync(x => existingImdb = await Repo.GetAll().AnyAsync(x =>
x.ImdbId == item.ImdbId && x.Type == PlexMediaTypeEntity.Show); x.ImdbId == item.ImdbId && x.Type == MediaType.Series);
} }
if (item.TheMovieDbId.HasValue()) if (item.TheMovieDbId.HasValue())
{ {
existingMovieDbId = await Repo.GetAll().AnyAsync(x => existingMovieDbId = await Repo.GetAll().AnyAsync(x =>
x.TheMovieDbId == item.TheMovieDbId && x.Type == PlexMediaTypeEntity.Show); x.TheMovieDbId == item.TheMovieDbId && x.Type == MediaType.Series);
} }
if (item.TvDbId.HasValue()) if (item.TvDbId.HasValue())
{ {
existingTvDbId = await Repo.GetAll().AnyAsync(x => existingTvDbId = await Repo.GetAll().AnyAsync(x =>
x.TvDbId == item.TvDbId && x.Type == PlexMediaTypeEntity.Show); x.TvDbId == item.TvDbId && x.Type == MediaType.Series);
} }
if (existingImdb || existingTvDbId || existingMovieDbId) if (existingImdb || existingTvDbId || existingMovieDbId)

@ -113,7 +113,7 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
var currentPosition = 0; var currentPosition = 0;
var resultCount = settings.EpisodeBatchSize == 0 ? 150 : settings.EpisodeBatchSize; var resultCount = settings.EpisodeBatchSize == 0 ? 150 : settings.EpisodeBatchSize;
var currentEpisodes = _repo.GetAllEpisodes(); var currentEpisodes = _repo.GetAllEpisodes().Cast<PlexEpisode>();
var episodes = await _api.GetAllEpisodes(settings.PlexAuthToken, settings.FullUri, section.key, currentPosition, resultCount); var episodes = await _api.GetAllEpisodes(settings.PlexAuthToken, settings.FullUri, section.key, currentPosition, resultCount);
_log.LogInformation(LoggingEvents.PlexEpisodeCacher, $"Total Epsiodes found for {episodes.MediaContainer.librarySectionTitle} = {episodes.MediaContainer.totalSize}"); _log.LogInformation(LoggingEvents.PlexEpisodeCacher, $"Total Epsiodes found for {episodes.MediaContainer.librarySectionTitle} = {episodes.MediaContainer.totalSize}");

@ -1,4 +1,5 @@
using System.IO; using System.Collections.Generic;
using System.IO;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Store.Entities; using Ombi.Store.Entities;
@ -42,20 +43,20 @@ namespace Ombi.Store.Context
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
builder.Entity<PlexServerContent>().HasMany(x => x.Episodes) builder.Entity<PlexServerContent>().HasMany(x => (ICollection<PlexEpisode>) x.Episodes)
.WithOne(x => x.Series) .WithOne(x => (PlexServerContent) x.Series)
.HasPrincipalKey(x => x.Key) .HasPrincipalKey(x => x.Key)
.HasForeignKey(x => x.GrandparentKey); .HasForeignKey(x => x.GrandparentKey);
builder.Entity<EmbyEpisode>() builder.Entity<EmbyEpisode>()
.HasOne(p => p.Series) .HasOne(p => (EmbyContent) p.Series)
.WithMany(b => b.Episodes) .WithMany(b => (ICollection<EmbyEpisode>) b.Episodes)
.HasPrincipalKey(x => x.EmbyId) .HasPrincipalKey(x => x.EmbyId)
.HasForeignKey(p => p.ParentId); .HasForeignKey(p => p.ParentId);
builder.Entity<JellyfinEpisode>() builder.Entity<JellyfinEpisode>()
.HasOne(p => p.Series) .HasOne(p => (JellyfinContent) p.Series)
.WithMany(b => b.Episodes) .WithMany(b => (ICollection<JellyfinEpisode>) b.Episodes)
.HasPrincipalKey(x => x.JellyfinId) .HasPrincipalKey(x => x.JellyfinId)
.HasForeignKey(p => p.ParentId); .HasForeignKey(p => p.ParentId);

@ -32,40 +32,13 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Ombi.Store.Entities namespace Ombi.Store.Entities
{ {
[Table("EmbyContent")] [Table("EmbyContent")]
public class EmbyContent : Entity public class EmbyContent : MediaServerContent
{ {
public string Title { get; set; }
/// <summary> [Obsolete("Cannot delete due to DB migration issues with SQLite")]
/// OBSOLETE, Cannot delete due to DB migration issues with SQLite
/// </summary>
public string ProviderId { get; set; } public string ProviderId { get; set; }
public string EmbyId { get; set; } public string EmbyId { get; set; }
public EmbyMediaType Type { get; set; } public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Emby;
public DateTime AddedAt { get; set; }
public string ImdbId { get; set; }
public string TheMovieDbId { get; set; }
public string TvDbId { get; set; }
public string Url { get; set; }
public ICollection<EmbyEpisode> Episodes { get; set; }
[NotMapped]
public bool HasImdb => !string.IsNullOrEmpty(ImdbId);
[NotMapped]
public bool HasTvDb => !string.IsNullOrEmpty(TvDbId);
[NotMapped]
public bool HasTheMovieDb => !string.IsNullOrEmpty(TheMovieDbId);
} }
public enum EmbyMediaType
{
Movie = 0,
Series = 1,
Music = 2
}
} }

@ -26,18 +26,16 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore.Metadata; using System.Linq;
namespace Ombi.Store.Entities namespace Ombi.Store.Entities
{ {
[Table("EmbyEpisode")] [Table("EmbyEpisode")]
public class EmbyEpisode : Entity public class EmbyEpisode : MediaServerEpisode
{ {
public string Title { get; set; }
public string EmbyId { get; set; } public string EmbyId { get; set; }
public int EpisodeNumber { get; set; }
public int SeasonNumber { get; set; }
public string ParentId { get; set; } public string ParentId { get; set; }
/// <summary> /// <summary>
/// NOT USED /// NOT USED
@ -47,7 +45,23 @@ namespace Ombi.Store.Entities
public string TvDbId { get; set; } public string TvDbId { get; set; }
public string ImdbId { get; set; } public string ImdbId { get; set; }
public string TheMovieDbId { get; set; } public string TheMovieDbId { get; set; }
[NotMapped]
public EmbyContent EmbySeries
{
get => (EmbyContent)Series;
set => Series = value;
}
public override IMediaServerContent SeriesIsIn(ICollection<IMediaServerContent> content)
{
return content.OfType<EmbyContent>().FirstOrDefault(
x => x.EmbyId == this.EmbySeries.EmbyId);
}
public override bool IsIn(IMediaServerContent content)
{
return content.Episodes.Cast<EmbyEpisode>().Any(x => x.EmbyId == this.EmbyId);
}
public EmbyContent Series { get; set; }
} }
} }

@ -2,7 +2,7 @@
namespace Ombi.Store.Entities namespace Ombi.Store.Entities
{ {
public abstract class Entity public abstract class Entity: IEntity
{ {
[Key] [Key]
public int Id { get; set; } public int Id { get; set; }

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Ombi.Store.Entities
{
public interface IEntity
{
[Key]
public int Id { get; set; }
}
}

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Ombi.Store.Entities
{
public interface IMediaServerContent: IEntity
{
public string Title { get; set; }
public string ImdbId { get; set; }
public string TvDbId { get; set; }
public string TheMovieDbId { get; set; }
public MediaType Type { get; set; }
public RecentlyAddedType RecentlyAddedType{ get; }
public string Url { get; set; }
public ICollection<IMediaServerEpisode> Episodes { get; set; }
public DateTime AddedAt { get; set; }
[NotMapped]
public bool HasImdb => !string.IsNullOrEmpty(ImdbId);
[NotMapped]
public bool HasTvDb => !string.IsNullOrEmpty(TvDbId);
[NotMapped]
public bool HasTheMovieDb => !string.IsNullOrEmpty(TheMovieDbId);
}
public interface IMediaServerEpisode
{
public int EpisodeNumber { get; set; }
public int SeasonNumber { get; set; }
public string Title { get; set; }
/// <summary>
/// The Season key
/// </summary>
/// <value>
/// The parent key.
/// </value>
public IMediaServerContent Series { get; set; }
public IMediaServerContent SeriesIsIn(ICollection<IMediaServerContent> content);
public bool IsIn(IMediaServerContent content);
}
public enum MediaType
{
Movie = 0,
Series = 1,
Music = 2,
Episode = 3
}
}

@ -32,40 +32,14 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Ombi.Store.Entities namespace Ombi.Store.Entities
{ {
[Table("JellyfinContent")] [Table("JellyfinContent")]
public class JellyfinContent : Entity public class JellyfinContent : MediaServerContent
{ {
public string Title { get; set; }
/// <summary>
/// OBSOLETE, Cannot delete due to DB migration issues with SQLite [Obsolete("Cannot delete due to DB migration issues with SQLite")]
/// </summary>
public string ProviderId { get; set; } public string ProviderId { get; set; }
public string JellyfinId { get; set; } public string JellyfinId { get; set; }
public JellyfinMediaType Type { get; set; } public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Jellyfin;
public DateTime AddedAt { get; set; }
public string ImdbId { get; set; }
public string TheMovieDbId { get; set; }
public string TvDbId { get; set; }
public string Url { get; set; }
public ICollection<JellyfinEpisode> Episodes { get; set; }
[NotMapped]
public bool HasImdb => !string.IsNullOrEmpty(ImdbId);
[NotMapped]
public bool HasTvDb => !string.IsNullOrEmpty(TvDbId);
[NotMapped]
public bool HasTheMovieDb => !string.IsNullOrEmpty(TheMovieDbId);
} }
public enum JellyfinMediaType
{
Movie = 0,
Series = 1,
Music = 2
}
} }

@ -26,18 +26,17 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
namespace Ombi.Store.Entities namespace Ombi.Store.Entities
{ {
[Table("JellyfinEpisode")] [Table("JellyfinEpisode")]
public class JellyfinEpisode : Entity public class JellyfinEpisode : MediaServerEpisode
{ {
public string Title { get; set; }
public string JellyfinId { get; set; } public string JellyfinId { get; set; }
public int EpisodeNumber { get; set; }
public int SeasonNumber { get; set; }
public string ParentId { get; set; } public string ParentId { get; set; }
/// <summary> /// <summary>
/// NOT USED /// NOT USED
@ -47,7 +46,22 @@ namespace Ombi.Store.Entities
public string TvDbId { get; set; } public string TvDbId { get; set; }
public string ImdbId { get; set; } public string ImdbId { get; set; }
public string TheMovieDbId { get; set; } public string TheMovieDbId { get; set; }
[NotMapped]
public JellyfinContent Series { get; set; } public JellyfinContent JellyfinSeries
{
get => (JellyfinContent)Series;
set => Series = value;
}
public override IMediaServerContent SeriesIsIn(ICollection<IMediaServerContent> content)
{
return content.OfType<JellyfinContent>().FirstOrDefault(
x => x.JellyfinId == this.JellyfinSeries.JellyfinId);
}
public override bool IsIn(IMediaServerContent content)
{
return content.Episodes.Cast<JellyfinEpisode>().Any(x => x.JellyfinId == this.JellyfinId);
}
} }
} }

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations.Schema;
using Ombi.Store.Repository;
namespace Ombi.Store.Entities
{
public abstract class MediaServerContent: Entity, IMediaServerContent
{
public string Title { get; set; }
public string ImdbId { get; set; }
public string TvDbId { get; set; }
public string TheMovieDbId { get; set; }
public MediaType Type { get; set; }
public string Url { get; set; }
public ICollection<IMediaServerEpisode> Episodes { get; set; }
public DateTime AddedAt { get; set; }
[NotMapped]
public bool HasImdb => !string.IsNullOrEmpty(ImdbId);
[NotMapped]
public bool HasTvDb => !string.IsNullOrEmpty(TvDbId);
[NotMapped]
public bool HasTheMovieDb => !string.IsNullOrEmpty(TheMovieDbId);
[NotMapped]
public abstract RecentlyAddedType RecentlyAddedType { get; }
}
public abstract class MediaServerEpisode: Entity, IMediaServerEpisode
{
public int EpisodeNumber { get; set; }
public int SeasonNumber { get; set; }
public string Title { get; set; }
public IMediaServerContent Series { get; set; }
public abstract IMediaServerContent SeriesIsIn(ICollection<IMediaServerContent> content);
public abstract bool IsIn(IMediaServerContent content);
}
}

@ -1,30 +1,38 @@
using System.ComponentModel.DataAnnotations.Schema; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace Ombi.Store.Entities namespace Ombi.Store.Entities
{ {
[Table("PlexEpisode")] [Table("PlexEpisode")]
public class PlexEpisode : Entity public class PlexEpisode : MediaServerEpisode
{ {
public int EpisodeNumber { get; set; }
public int SeasonNumber { get; set; }
public int Key { get; set; } // RatingKey public int Key { get; set; } // RatingKey
public string Title { get; set; }
/// <summary>
/// The Season key
/// </summary>
/// <value> /// <value>
/// The parent key. /// The parent key.
/// </value> /// </value>
public int ParentKey { get; set; } public int ParentKey { get; set; }
/// <summary>
/// The Series key
/// </summary>
/// <value> /// <value>
/// The grandparent key. /// The grandparent key.
/// </value> /// </value>
public int GrandparentKey { get; set; } public int GrandparentKey { get; set; }
[NotMapped]
public PlexServerContent PlexSeries
{
get => (PlexServerContent)Series;
set => Series = value;
}
public override IMediaServerContent SeriesIsIn(ICollection<IMediaServerContent> content)
{
return content.OfType<PlexServerContent>().FirstOrDefault(
x => x.Key == this.PlexSeries.Key);
}
public override bool IsIn(IMediaServerContent content)
{
return content.Episodes.Cast<PlexEpisode>().Any(x => x.Key == this.Key);
}
public PlexServerContent Series { get; set; }
} }
} }

@ -32,37 +32,20 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Ombi.Store.Entities namespace Ombi.Store.Entities
{ {
[Table("PlexServerContent")] [Table("PlexServerContent")]
public class PlexServerContent : Entity public class PlexServerContent : MediaServerContent
{ {
public string Title { get; set; }
public string ReleaseYear { get; set; } public string ReleaseYear { get; set; }
public string ImdbId { get; set; }
public string TvDbId { get; set; }
public string TheMovieDbId { get; set; }
public PlexMediaTypeEntity Type { get; set; }
public string Url { get; set; }
public ICollection<PlexEpisode> Episodes { get; set; }
public ICollection<PlexSeasonsContent> Seasons { get; set; } public ICollection<PlexSeasonsContent> Seasons { get; set; }
/// <summary> /// <summary>
/// Plex's internal ID for this item /// Plex's internal ID for this item
/// </summary> /// </summary>
public int Key { get; set; } public int Key { get; set; }
public DateTime AddedAt { get; set; }
public string Quality { get; set; } public string Quality { get; set; }
public int? RequestId { get; set; } public int? RequestId { get; set; }
[NotMapped] public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Plex;
public bool HasImdb => !string.IsNullOrEmpty(ImdbId);
[NotMapped]
public bool HasTvDb => !string.IsNullOrEmpty(TvDbId);
[NotMapped]
public bool HasTheMovieDb => !string.IsNullOrEmpty(TheMovieDbId);
} }
[Table("PlexSeasonsContent")] [Table("PlexSeasonsContent")]
@ -73,10 +56,4 @@ namespace Ombi.Store.Entities
public int SeasonKey { get; set; } public int SeasonKey { get; set; }
public int ParentKey { get; set; } public int ParentKey { get; set; }
} }
public enum PlexMediaTypeEntity
{
Movie = 0,
Show = 1
}
} }

@ -35,17 +35,13 @@ using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public class EmbyContentRepository : ExternalRepository<EmbyContent>, IEmbyContentRepository public class EmbyContentRepository : MediaServerContentRepository<EmbyContent>, IEmbyContentRepository
{ {
public EmbyContentRepository(ExternalContext db):base(db) public EmbyContentRepository(ExternalContext db):base(db)
{ {
Db = db;
} }
private ExternalContext Db { get; }
public async Task<EmbyContent> GetByImdbId(string imdbid) public async Task<EmbyContent> GetByImdbId(string imdbid)
{ {
return await Db.EmbyContent.FirstOrDefaultAsync(x => x.ImdbId == imdbid); return await Db.EmbyContent.FirstOrDefaultAsync(x => x.ImdbId == imdbid);
@ -69,20 +65,20 @@ namespace Ombi.Store.Repository
return await Db.EmbyContent./*Include(x => x.Seasons).*/FirstOrDefaultAsync(x => x.EmbyId == embyId); return await Db.EmbyContent./*Include(x => x.Seasons).*/FirstOrDefaultAsync(x => x.EmbyId == embyId);
} }
public async Task Update(EmbyContent existingContent) public override async Task Update(IMediaServerContent existingContent)
{ {
Db.EmbyContent.Update(existingContent); Db.EmbyContent.Update((EmbyContent)existingContent);
await InternalSaveChanges(); await InternalSaveChanges();
} }
public IQueryable<EmbyEpisode> GetAllEpisodes() public override IQueryable<IMediaServerEpisode> GetAllEpisodes()
{ {
return Db.EmbyEpisode.AsQueryable(); return Db.EmbyEpisode.AsQueryable();
} }
public async Task<EmbyEpisode> Add(EmbyEpisode content) public override async Task<IMediaServerEpisode> Add(IMediaServerEpisode content)
{ {
await Db.EmbyEpisode.AddAsync(content); await Db.EmbyEpisode.AddAsync((EmbyEpisode)content);
await InternalSaveChanges(); await InternalSaveChanges();
return content; return content;
} }
@ -91,16 +87,17 @@ namespace Ombi.Store.Repository
return await Db.EmbyEpisode.FirstOrDefaultAsync(x => x.EmbyId == key); return await Db.EmbyEpisode.FirstOrDefaultAsync(x => x.EmbyId == key);
} }
public async Task AddRange(IEnumerable<EmbyEpisode> content) public override async Task AddRange(IEnumerable<IMediaServerEpisode> content)
{ {
Db.EmbyEpisode.AddRange(content); Db.EmbyEpisode.AddRange((IEnumerable<EmbyEpisode>)content);
await InternalSaveChanges(); await InternalSaveChanges();
} }
public void UpdateWithoutSave(EmbyContent existingContent) public override void UpdateWithoutSave(IMediaServerContent existingContent)
{ {
Db.EmbyContent.Update(existingContent); Db.EmbyContent.Update((EmbyContent)existingContent);
} }
public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Emby;
} }
} }

@ -6,19 +6,16 @@ using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public interface IEmbyContentRepository : IRepository<EmbyContent> public interface IEmbyContentRepository : IMediaServerContentRepository<EmbyContent>
{ {
Task<EmbyContent> GetByEmbyId(string embyId);
Task<EmbyEpisode> GetEpisodeByEmbyId(string key);
// TODO: merge these with IJellyfinContentRepository
IQueryable<EmbyContent> Get(); IQueryable<EmbyContent> Get();
Task<EmbyContent> GetByTheMovieDbId(string mov); Task<EmbyContent> GetByTheMovieDbId(string mov);
Task<EmbyContent> GetByTvDbId(string tv); Task<EmbyContent> GetByTvDbId(string tv);
Task<EmbyContent> GetByImdbId(string imdbid); Task<EmbyContent> GetByImdbId(string imdbid);
Task<EmbyContent> GetByEmbyId(string embyId);
Task Update(EmbyContent existingContent);
IQueryable<EmbyEpisode> GetAllEpisodes();
Task<EmbyEpisode> Add(EmbyEpisode content);
Task<EmbyEpisode> GetEpisodeByEmbyId(string key);
Task AddRange(IEnumerable<EmbyEpisode> content);
void UpdateWithoutSave(EmbyContent existingContent);
} }
} }

@ -9,7 +9,7 @@ using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public interface IExternalRepository<T> where T : Entity public interface IExternalRepository<T> where T : IEntity
{ {
Task<T> Find(object key); Task<T> Find(object key);
IQueryable<T> GetAll(); IQueryable<T> GetAll();
@ -25,6 +25,5 @@ namespace Ombi.Store.Repository
where TEntity : class; where TEntity : class;
Task ExecuteSql(string sql); Task ExecuteSql(string sql);
DbSet<T> _db { get; }
} }
} }

@ -6,19 +6,16 @@ using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public interface IJellyfinContentRepository : IRepository<JellyfinContent> public interface IJellyfinContentRepository : IMediaServerContentRepository<JellyfinContent>
{ {
Task<JellyfinContent> GetByJellyfinId(string jellyfinId);
Task<JellyfinEpisode> GetEpisodeByJellyfinId(string key);
// TODO: merge these with IEmbyContentRepository
IQueryable<JellyfinContent> Get(); IQueryable<JellyfinContent> Get();
Task<JellyfinContent> GetByTheMovieDbId(string mov); Task<JellyfinContent> GetByTheMovieDbId(string mov);
Task<JellyfinContent> GetByTvDbId(string tv); Task<JellyfinContent> GetByTvDbId(string tv);
Task<JellyfinContent> GetByImdbId(string imdbid); Task<JellyfinContent> GetByImdbId(string imdbid);
Task<JellyfinContent> GetByJellyfinId(string jellyfinId);
Task Update(JellyfinContent existingContent);
IQueryable<JellyfinEpisode> GetAllEpisodes();
Task<JellyfinEpisode> Add(JellyfinEpisode content);
Task<JellyfinEpisode> GetEpisodeByJellyfinId(string key);
Task AddRange(IEnumerable<JellyfinEpisode> content);
void UpdateWithoutSave(JellyfinContent existingContent);
} }
} }

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ombi.Store.Entities;
namespace Ombi.Store.Repository
{
public interface IMediaServerContentRepository<Content> : IExternalRepository<Content>
where Content : IMediaServerContent
{
RecentlyAddedType RecentlyAddedType{ get; }
Task Update(IMediaServerContent existingContent);
IQueryable<IMediaServerEpisode> GetAllEpisodes();
Task<IMediaServerEpisode> Add(IMediaServerEpisode content);
Task AddRange(IEnumerable<IMediaServerEpisode> content);
void UpdateWithoutSave(IMediaServerContent existingContent);
}
}

@ -8,23 +8,18 @@ using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public interface IPlexContentRepository : IExternalRepository<PlexServerContent> public interface IPlexContentRepository : IMediaServerContentRepository<PlexServerContent>
{ {
Task<bool> ContentExists(string providerId); Task<bool> ContentExists(string providerId);
Task<PlexServerContent> Get(string providerId, ProviderType type); Task<PlexServerContent> Get(string providerId, ProviderType type);
Task<PlexServerContent> GetByType(string providerId, ProviderType type, PlexMediaTypeEntity plexType); Task<PlexServerContent> GetByType(string providerId, ProviderType type, MediaType mediaType);
Task<PlexServerContent> GetByKey(int key); Task<PlexServerContent> GetByKey(int key);
Task Update(PlexServerContent existingContent);
IQueryable<PlexEpisode> GetAllEpisodes();
Task<PlexEpisode> Add(PlexEpisode content);
Task<PlexEpisode> GetEpisodeByKey(int key); Task<PlexEpisode> GetEpisodeByKey(int key);
Task AddRange(IEnumerable<PlexEpisode> content);
IEnumerable<PlexServerContent> GetWhereContentByCustom(Expression<Func<PlexServerContent, bool>> predicate); IEnumerable<PlexServerContent> GetWhereContentByCustom(Expression<Func<PlexServerContent, bool>> predicate);
Task<PlexServerContent> GetFirstContentByCustom(Expression<Func<PlexServerContent, bool>> predicate); Task<PlexServerContent> GetFirstContentByCustom(Expression<Func<PlexServerContent, bool>> predicate);
Task DeleteEpisode(PlexEpisode content); Task DeleteEpisode(PlexEpisode content);
void DeleteWithoutSave(PlexServerContent content); void DeleteWithoutSave(PlexServerContent content);
void DeleteWithoutSave(PlexEpisode content); void DeleteWithoutSave(PlexEpisode content);
Task UpdateRange(IEnumerable<PlexServerContent> existingContent); Task UpdateRange(IEnumerable<PlexServerContent> existingContent);
void UpdateWithoutSave(PlexServerContent existingContent);
} }
} }

@ -10,7 +10,7 @@ using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public interface IRepository<T> where T : Entity public interface IRepository<T> where T : IEntity
{ {
Task<T> Find(object key); Task<T> Find(object key);
Task<T> Find(object key, CancellationToken cancellationToken); Task<T> Find(object key, CancellationToken cancellationToken);
@ -27,6 +27,5 @@ namespace Ombi.Store.Repository
where TEntity : class; where TEntity : class;
Task ExecuteSql(string sql); Task ExecuteSql(string sql);
DbSet<T> _db { get; }
} }
} }

@ -35,17 +35,13 @@ using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public class JellyfinContentRepository : ExternalRepository<JellyfinContent>, IJellyfinContentRepository public class JellyfinContentRepository : MediaServerContentRepository<JellyfinContent>, IJellyfinContentRepository
{ {
public JellyfinContentRepository(ExternalContext db):base(db) public JellyfinContentRepository(ExternalContext db):base(db)
{ {
Db = db;
} }
private ExternalContext Db { get; }
public async Task<JellyfinContent> GetByImdbId(string imdbid) public async Task<JellyfinContent> GetByImdbId(string imdbid)
{ {
return await Db.JellyfinContent.FirstOrDefaultAsync(x => x.ImdbId == imdbid); return await Db.JellyfinContent.FirstOrDefaultAsync(x => x.ImdbId == imdbid);
@ -69,20 +65,20 @@ namespace Ombi.Store.Repository
return await Db.JellyfinContent./*Include(x => x.Seasons).*/FirstOrDefaultAsync(x => x.JellyfinId == jellyfinId); return await Db.JellyfinContent./*Include(x => x.Seasons).*/FirstOrDefaultAsync(x => x.JellyfinId == jellyfinId);
} }
public async Task Update(JellyfinContent existingContent) public override async Task Update(IMediaServerContent existingContent)
{ {
Db.JellyfinContent.Update(existingContent); Db.JellyfinContent.Update((JellyfinContent)existingContent);
await InternalSaveChanges(); await InternalSaveChanges();
} }
public IQueryable<JellyfinEpisode> GetAllEpisodes() public override IQueryable<IMediaServerEpisode> GetAllEpisodes()
{ {
return Db.JellyfinEpisode.AsQueryable(); return Db.JellyfinEpisode.AsQueryable();
} }
public async Task<JellyfinEpisode> Add(JellyfinEpisode content) public override async Task<IMediaServerEpisode> Add(IMediaServerEpisode content)
{ {
await Db.JellyfinEpisode.AddAsync(content); await Db.JellyfinEpisode.AddAsync((JellyfinEpisode)content);
await InternalSaveChanges(); await InternalSaveChanges();
return content; return content;
} }
@ -91,16 +87,17 @@ namespace Ombi.Store.Repository
return await Db.JellyfinEpisode.FirstOrDefaultAsync(x => x.JellyfinId == key); return await Db.JellyfinEpisode.FirstOrDefaultAsync(x => x.JellyfinId == key);
} }
public async Task AddRange(IEnumerable<JellyfinEpisode> content) public override async Task AddRange(IEnumerable<IMediaServerEpisode> content)
{ {
Db.JellyfinEpisode.AddRange(content); Db.JellyfinEpisode.AddRange((IEnumerable<JellyfinEpisode>)content);
await InternalSaveChanges(); await InternalSaveChanges();
} }
public void UpdateWithoutSave(JellyfinContent existingContent) public override void UpdateWithoutSave(IMediaServerContent existingContent)
{ {
Db.JellyfinContent.Update(existingContent); Db.JellyfinContent.Update((JellyfinContent)existingContent);
} }
public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Jellyfin;
} }
} }

@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ombi.Store.Context;
using Ombi.Store.Entities;
namespace Ombi.Store.Repository
{
public abstract class MediaServerContentRepository<T> : ExternalRepository<T>, IMediaServerContentRepository<T> where T : MediaServerContent
{
protected ExternalContext Db { get; }
public abstract RecentlyAddedType RecentlyAddedType { get; }
public MediaServerContentRepository(ExternalContext db) : base(db)
{
Db = db;
}
public abstract Task Update(IMediaServerContent existingContent);
public abstract IQueryable<IMediaServerEpisode> GetAllEpisodes();
public abstract Task<IMediaServerEpisode> Add(IMediaServerEpisode content);
public abstract Task AddRange(IEnumerable<IMediaServerEpisode> content);
public abstract void UpdateWithoutSave(IMediaServerContent existingContent);
}
}

@ -37,17 +37,13 @@ using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public class PlexServerContentRepository : ExternalRepository<PlexServerContent>, IPlexContentRepository public class PlexServerContentRepository : MediaServerContentRepository<PlexServerContent>, IPlexContentRepository
{ {
public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Plex;
public PlexServerContentRepository(ExternalContext db) : base(db) public PlexServerContentRepository(ExternalContext db) : base(db)
{ {
Db = db;
} }
private ExternalContext Db { get; }
public async Task<bool> ContentExists(string providerId) public async Task<bool> ContentExists(string providerId)
{ {
var any = await Db.PlexServerContent.AnyAsync(x => x.ImdbId == providerId); var any = await Db.PlexServerContent.AnyAsync(x => x.ImdbId == providerId);
@ -79,16 +75,16 @@ namespace Ombi.Store.Repository
return null; return null;
} }
public async Task<PlexServerContent> GetByType(string providerId, ProviderType type, PlexMediaTypeEntity plexType) public async Task<PlexServerContent> GetByType(string providerId, ProviderType type, MediaType mediaType)
{ {
switch (type) switch (type)
{ {
case ProviderType.ImdbId: case ProviderType.ImdbId:
return await Db.PlexServerContent.FirstOrDefaultAsync(x => x.ImdbId == providerId && x.Type == plexType); return await Db.PlexServerContent.FirstOrDefaultAsync(x => x.ImdbId == providerId && x.Type == mediaType);
case ProviderType.TheMovieDbId: case ProviderType.TheMovieDbId:
return await Db.PlexServerContent.FirstOrDefaultAsync(x => x.TheMovieDbId == providerId && x.Type == plexType); return await Db.PlexServerContent.FirstOrDefaultAsync(x => x.TheMovieDbId == providerId && x.Type == mediaType);
case ProviderType.TvDbId: case ProviderType.TvDbId:
return await Db.PlexServerContent.FirstOrDefaultAsync(x => x.TvDbId == providerId && x.Type == plexType); return await Db.PlexServerContent.FirstOrDefaultAsync(x => x.TvDbId == providerId && x.Type == mediaType);
default: default:
break; break;
} }
@ -114,14 +110,14 @@ namespace Ombi.Store.Repository
.FirstOrDefaultAsync(predicate); .FirstOrDefaultAsync(predicate);
} }
public async Task Update(PlexServerContent existingContent) public override async Task Update(IMediaServerContent existingContent)
{ {
Db.PlexServerContent.Update(existingContent); Db.PlexServerContent.Update((PlexServerContent)existingContent);
await InternalSaveChanges(); await InternalSaveChanges();
} }
public void UpdateWithoutSave(PlexServerContent existingContent) public override void UpdateWithoutSave(IMediaServerContent existingContent)
{ {
Db.PlexServerContent.Update(existingContent); Db.PlexServerContent.Update((PlexServerContent)existingContent);
} }
public async Task UpdateRange(IEnumerable<PlexServerContent> existingContent) public async Task UpdateRange(IEnumerable<PlexServerContent> existingContent)
@ -130,7 +126,7 @@ namespace Ombi.Store.Repository
await InternalSaveChanges(); await InternalSaveChanges();
} }
public IQueryable<PlexEpisode> GetAllEpisodes() public override IQueryable<IMediaServerEpisode> GetAllEpisodes()
{ {
return Db.PlexEpisode.Include(x => x.Series).AsQueryable(); return Db.PlexEpisode.Include(x => x.Series).AsQueryable();
} }
@ -145,9 +141,9 @@ namespace Ombi.Store.Repository
Db.PlexEpisode.Remove(content); Db.PlexEpisode.Remove(content);
} }
public async Task<PlexEpisode> Add(PlexEpisode content) public override async Task<IMediaServerEpisode> Add(IMediaServerEpisode content)
{ {
await Db.PlexEpisode.AddAsync(content); await Db.PlexEpisode.AddAsync((PlexEpisode)content);
await InternalSaveChanges(); await InternalSaveChanges();
return content; return content;
} }
@ -162,10 +158,11 @@ namespace Ombi.Store.Repository
{ {
return await Db.PlexEpisode.FirstOrDefaultAsync(x => x.Key == key); return await Db.PlexEpisode.FirstOrDefaultAsync(x => x.Key == key);
} }
public async Task AddRange(IEnumerable<PlexEpisode> content) public override async Task AddRange(IEnumerable<IMediaServerEpisode> content)
{ {
Db.PlexEpisode.AddRange(content); Db.PlexEpisode.AddRange((IEnumerable<PlexEpisode>)content);
await InternalSaveChanges(); await InternalSaveChanges();
} }
} }
} }
Loading…
Cancel
Save