Availability Checker #1464 #865

pull/1510/head
Jamie.Rees 7 years ago
parent 9ed6fd09a4
commit 385d206287

@ -12,9 +12,9 @@ namespace Ombi.Api.Plex
Task<PlexServer> GetServer(string authToken);
Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost);
Task<PlexContainer> GetLibrary(string authToken, string plexFullHost, string libraryId);
Task<PlexMetadata> GetEpisodeMetaData(string authToken, string host, string ratingKey);
Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, string itemId);
Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, string ratingKey);
Task<PlexMetadata> GetEpisodeMetaData(string authToken, string host, int ratingKey);
Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId);
Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey);
Task<PlexContainer> GetAllEpisodes(string authToken, string host, string section, int start, int retCount);
}
}

@ -2,7 +2,7 @@ namespace Ombi.Api.Plex.Models
{
public class Metadata
{
public string ratingKey { get; set; }
public int ratingKey { get; set; }
public string key { get; set; }
public string studio { get; set; }
public string type { get; set; }
@ -28,8 +28,8 @@ namespace Ombi.Api.Plex.Models
public Genre[] Genre { get; set; }
public Role[] Role { get; set; }
public string primaryExtraKey { get; set; }
public string parentRatingKey { get; set; }
public string grandparentRatingKey { get; set; }
public int parentRatingKey { get; set; }
public int grandparentRatingKey { get; set; }
public string guid { get; set; }
public int librarySectionID { get; set; }
public string librarySectionKey { get; set; }

@ -85,21 +85,21 @@ namespace Ombi.Api.Plex
/// <param name="plexFullHost"></param>
/// <param name="ratingKey"></param>
/// <returns></returns>
public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, string ratingKey)
public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey)
{
var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken);
return await Api.Request<PlexMetadata>(request);
}
public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, string itemId)
public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId)
{
var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken);
return await Api.Request<PlexMetadata>(request);
}
public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, string ratingKey)
public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey)
{
var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken);

@ -123,6 +123,7 @@ namespace Ombi.DependencyInjection
{
services.AddTransient<IPlexContentCacher, PlexContentCacher>();
services.AddTransient<IPlexEpisodeCacher, PlexEpisodeCacher>();
services.AddTransient<IPlexAvailabilityChecker, PlexAvailabilityChecker>();
services.AddTransient<IJobSetup, JobSetup>();
services.AddTransient<IRadarrCacher, RadarrCacher>();
services.AddTransient<IOmbiAutomaticUpdater, OmbiAutomaticUpdater>();

@ -91,7 +91,7 @@ namespace Ombi.Helpers
return 0;
}
public static string GetPlexMediaUrl(string machineId, string mediaId)
public static string GetPlexMediaUrl(string machineId, int mediaId)
{
var url =
$"https://app.plex.tv/web/app#!/server/{machineId}/details?key=library%2Fmetadata%2F{mediaId}";

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Ombi.Schedule.Jobs.Plex
{
public interface IPlexAvailabilityChecker
{
Task Start();
}
}

@ -0,0 +1,84 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests;
namespace Ombi.Schedule.Jobs.Plex
{
public class PlexAvailabilityChecker : IPlexAvailabilityChecker
{
public PlexAvailabilityChecker(IPlexContentRepository repo, ITvRequestRepository tvRequest, IMovieRequestRepository movies)
{
_tvRepo = tvRequest;
_repo = repo;
_movieRepo = movies;
}
private readonly ITvRequestRepository _tvRepo;
private readonly IMovieRequestRepository _movieRepo;
private readonly IPlexContentRepository _repo;
public async Task Start()
{
await ProcessMovies();
await ProcessTv();
}
private async Task ProcessTv()
{
var tv = _tvRepo.GetChild();
var plexEpisodes = _repo.GetAllEpisodes().Include(x => x.Series);
foreach (var child in tv)
{
var tvDbId = child.ParentRequest.TvDbId;
var seriesEpisodes = plexEpisodes.Where(x => x.Series.ProviderId == tvDbId.ToString());
foreach (var season in child.SeasonRequests)
{
foreach (var episode in season.Episodes)
{
var foundEp = await seriesEpisodes.FirstOrDefaultAsync(
x => x.EpisodeNumber == episode.EpisodeNumber &&
x.SeasonNumber == episode.Season.SeasonNumber);
if (foundEp != null)
{
episode.Available = true;
}
}
}
// Check to see if all of the episodes in all seasons are available for this request
var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available));
if (allAvailable)
{
// We have fulfulled this request!
child.Available = true;
}
}
await _tvRepo.Save();
}
private async Task ProcessMovies()
{
// Get all non available
var movies = _movieRepo.Get().Where(x => !x.Available);
foreach (var movie in movies)
{
var plexContent = await _repo.Get(movie.ImdbId);
if (plexContent == null)
{
// We don't yet have this
continue;
}
movie.Available = true;
}
await _movieRepo.Save();
}
}
}

@ -107,10 +107,10 @@ namespace Ombi.Schedule.Jobs.Plex
{
seasonsContent.Add(new PlexSeasonsContent
{
ParentKey = int.Parse(season.parentRatingKey),
SeasonKey = int.Parse(season.ratingKey),
ParentKey = season.parentRatingKey,
SeasonKey = season.ratingKey,
SeasonNumber = season.index,
PlexContentId = int.Parse(show.ratingKey)
PlexContentId = show.ratingKey
});
}
@ -197,8 +197,7 @@ namespace Ombi.Schedule.Jobs.Plex
if (contentToAdd.Any())
{
contentToAdd.ForEach(async x => await Repo.Add(x));
await Repo.AddRange(contentToAdd);
}
}

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Hangfire;
using Hangfire.Common;
using Microsoft.Extensions.Logging;
using Ombi.Api.Plex;
@ -19,19 +20,21 @@ namespace Ombi.Schedule.Jobs.Plex
public class PlexEpisodeCacher : IPlexEpisodeCacher
{
public PlexEpisodeCacher(ISettingsService<PlexSettings> s, ILogger<PlexEpisodeCacher> log, IPlexApi plexApi,
IPlexContentRepository repo)
IPlexContentRepository repo, IPlexAvailabilityChecker a)
{
_settings = s;
_log = log;
_api = plexApi;
_repo = repo;
_availabilityChecker = a;
}
private readonly ISettingsService<PlexSettings> _settings;
private readonly ILogger<PlexEpisodeCacher> _log;
private readonly IPlexApi _api;
private readonly IPlexContentRepository _repo;
private readonly IPlexAvailabilityChecker _availabilityChecker;
public async Task Start()
{
try
@ -46,6 +49,7 @@ namespace Ombi.Schedule.Jobs.Plex
{
await Cache(server);
BackgroundJob.Enqueue(() => _availabilityChecker.Start());
}
}
catch (Exception e)

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@ -29,38 +30,45 @@ namespace Ombi.Schedule.Jobs.Radarr
public async Task CacheContent()
{
var settings = RadarrSettings.GetSettings();
if (settings.Enabled)
try
{
try
var settings = RadarrSettings.GetSettings();
if (settings.Enabled)
{
var movies = await RadarrApi.GetMovies(settings.ApiKey, settings.FullUri);
if (movies != null)
try
{
// Let's remove the old cached data
await _ctx.Database.ExecuteSqlCommandAsync("TRUNCATE TABLE RadarrCache");
var movieIds = new List<RadarrCache>();
foreach (var m in movies)
var movies = await RadarrApi.GetMovies(settings.ApiKey, settings.FullUri);
if (movies != null)
{
if (m.tmdbId > 0)
{
movieIds.Add(new RadarrCache{TheMovieDbId = m.tmdbId});
}
else
// Let's remove the old cached data
await _ctx.Database.ExecuteSqlCommandAsync("TRUNCATE TABLE RadarrCache");
var movieIds = new List<RadarrCache>();
foreach (var m in movies)
{
Log.Error("TMDBId is not > 0 for movie {0}", m.title);
if (m.tmdbId > 0)
{
movieIds.Add(new RadarrCache { TheMovieDbId = m.tmdbId });
}
else
{
Log.Error("TMDBId is not > 0 for movie {0}", m.title);
}
}
}
await _ctx.RadarrCache.AddRangeAsync(movieIds);
await _ctx.RadarrCache.AddRangeAsync(movieIds);
await _ctx.SaveChangesAsync();
await _ctx.SaveChangesAsync();
}
}
catch (System.Exception ex)
{
Logger.LogError(LoggingEvents.Cacher, ex, "Failed caching queued items from Radarr");
}
}
catch (System.Exception ex)
{
Logger.LogError(LoggingEvents.Cacher, ex, "Failed caching queued items from Radarr");
}
}
catch (Exception e)
{
Logger.LogInformation(LoggingEvents.RadarrCacher, "Radarr is not setup, cannot cache episodes");
}
}

@ -45,6 +45,16 @@ namespace Ombi.Store.Context
optionsBuilder.UseSqlite("Data Source=Ombi.db");
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<PlexEpisode>()
.HasOne(p => p.Series)
.WithMany(b => b.Episodes)
.HasPrincipalKey(x => x.Key)
.HasForeignKey(p => p.GrandparentKey);
base.OnModelCreating(builder);
}
public void Seed()
{

@ -45,11 +45,13 @@ namespace Ombi.Store.Entities
/// Only used for TV Shows
/// </summary>
public virtual ICollection<PlexSeasonsContent> Seasons { get; set; }
public ICollection<PlexEpisode> Episodes { get; set; }
/// <summary>
/// Plex's internal ID for this item
/// </summary>
public string Key { get; set; }
public int Key { get; set; }
public DateTime AddedAt { get; set; }
}

@ -7,21 +7,24 @@ namespace Ombi.Store.Entities
{
public int EpisodeNumber { get; set; }
public int SeasonNumber { get; set; }
public string Key { get; set; } // RatingKey
public int Key { get; set; } // RatingKey
public string Title { get; set; }
/// <summary>
/// The Show key
/// The Season key
/// </summary>
/// <value>
/// The parent key.
/// </value>
public string ParentKey { get; set; }
public int ParentKey { get; set; }
/// <summary>
/// The Series key
/// </summary>
/// <value>
/// The grandparent key.
/// </value>
public string GrandparentKey { get; set; }
public int GrandparentKey { get; set; }
public PlexContent Series { get; set; }
}
}

@ -10,7 +10,7 @@ using Ombi.Helpers;
namespace Ombi.Store.Migrations
{
[DbContext(typeof(OmbiContext))]
[Migration("20170823144220_Inital")]
[Migration("20170824133349_Inital")]
partial class Inital
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -254,7 +254,7 @@ namespace Ombi.Store.Migrations
b.Property<DateTime>("AddedAt");
b.Property<string>("Key");
b.Property<int>("Key");
b.Property<string>("ProviderId");
@ -278,11 +278,11 @@ namespace Ombi.Store.Migrations
b.Property<int>("EpisodeNumber");
b.Property<string>("GrandparentKey");
b.Property<int>("GrandparentKey");
b.Property<string>("Key");
b.Property<int>("Key");
b.Property<string>("ParentKey");
b.Property<int>("ParentKey");
b.Property<int>("SeasonNumber");
@ -290,6 +290,8 @@ namespace Ombi.Store.Migrations
b.HasKey("Id");
b.HasIndex("GrandparentKey");
b.ToTable("PlexEpisode");
});
@ -568,6 +570,15 @@ namespace Ombi.Store.Migrations
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.PlexContent", "Series")
.WithMany("Episodes")
.HasForeignKey("GrandparentKey")
.HasPrincipalKey("Key")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexContent")

@ -132,7 +132,7 @@ namespace Ombi.Store.Migrations
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
AddedAt = table.Column<DateTime>(nullable: false),
Key = table.Column<string>(nullable: true),
Key = table.Column<int>(nullable: false),
ProviderId = table.Column<string>(nullable: true),
ReleaseYear = table.Column<string>(nullable: true),
Title = table.Column<string>(nullable: true),
@ -142,24 +142,7 @@ namespace Ombi.Store.Migrations
constraints: table =>
{
table.PrimaryKey("PK_PlexContent", x => x.Id);
});
migrationBuilder.CreateTable(
name: "PlexEpisode",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
EpisodeNumber = table.Column<int>(nullable: false),
GrandparentKey = table.Column<string>(nullable: true),
Key = table.Column<string>(nullable: true),
ParentKey = table.Column<string>(nullable: true),
SeasonNumber = table.Column<int>(nullable: false),
Title = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PlexEpisode", x => x.Id);
table.UniqueConstraint("AK_PlexContent_Key", x => x.Key);
});
migrationBuilder.CreateTable(
@ -334,6 +317,30 @@ namespace Ombi.Store.Migrations
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "PlexEpisode",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
EpisodeNumber = table.Column<int>(nullable: false),
GrandparentKey = table.Column<int>(nullable: false),
Key = table.Column<int>(nullable: false),
ParentKey = table.Column<int>(nullable: false),
SeasonNumber = table.Column<int>(nullable: false),
Title = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PlexEpisode", x => x.Id);
table.ForeignKey(
name: "FK_PlexEpisode_PlexContent_GrandparentKey",
column: x => x.GrandparentKey,
principalTable: "PlexContent",
principalColumn: "Key",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PlexSeasonsContent",
columns: table => new
@ -529,6 +536,11 @@ namespace Ombi.Store.Migrations
column: "NormalizedUserName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PlexEpisode_GrandparentKey",
table: "PlexEpisode",
column: "GrandparentKey");
migrationBuilder.CreateIndex(
name: "IX_PlexSeasonsContent_PlexContentId",
table: "PlexSeasonsContent",

@ -253,7 +253,7 @@ namespace Ombi.Store.Migrations
b.Property<DateTime>("AddedAt");
b.Property<string>("Key");
b.Property<int>("Key");
b.Property<string>("ProviderId");
@ -277,11 +277,11 @@ namespace Ombi.Store.Migrations
b.Property<int>("EpisodeNumber");
b.Property<string>("GrandparentKey");
b.Property<int>("GrandparentKey");
b.Property<string>("Key");
b.Property<int>("Key");
b.Property<string>("ParentKey");
b.Property<int>("ParentKey");
b.Property<int>("SeasonNumber");
@ -289,6 +289,8 @@ namespace Ombi.Store.Migrations
b.HasKey("Id");
b.HasIndex("GrandparentKey");
b.ToTable("PlexEpisode");
});
@ -567,6 +569,15 @@ namespace Ombi.Store.Migrations
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.PlexContent", "Series")
.WithMany("Episodes")
.HasForeignKey("GrandparentKey")
.HasPrincipalKey("Key")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexContent")

@ -12,11 +12,12 @@ namespace Ombi.Store.Repository
Task<bool> ContentExists(string providerId);
Task<IEnumerable<PlexContent>> GetAll();
Task<PlexContent> Get(string providerId);
Task<PlexContent> GetByKey(string key);
Task<PlexContent> GetByKey(int key);
Task Update(PlexContent existingContent);
IQueryable<PlexEpisode> GetAllEpisodes();
Task<PlexEpisode> Add(PlexEpisode content);
Task<PlexEpisode> GetEpisodeByKey(string key);
Task<PlexEpisode> GetEpisodeByKey(int key);
Task AddRange(IEnumerable<PlexEpisode> content);
IQueryable<PlexContent> Get();
}
}

@ -72,7 +72,12 @@ namespace Ombi.Store.Repository
return await Db.PlexContent.FirstOrDefaultAsync(x => x.ProviderId == providerId);
}
public async Task<PlexContent> GetByKey(string key)
public IQueryable<PlexContent> Get()
{
return Db.PlexContent.AsQueryable();
}
public async Task<PlexContent> GetByKey(int key)
{
return await Db.PlexContent.Include(x => x.Seasons).FirstOrDefaultAsync(x => x.Key == key);
}
@ -94,7 +99,7 @@ namespace Ombi.Store.Repository
await Db.SaveChangesAsync();
return content;
}
public async Task<PlexEpisode> GetEpisodeByKey(string key)
public async Task<PlexEpisode> GetEpisodeByKey(int key)
{
return await Db.PlexEpisode.FirstOrDefaultAsync(x => x.Key == key);
}

@ -11,5 +11,6 @@ namespace Ombi.Store.Repository
IQueryable<MovieRequests> Get();
Task<MovieRequests> GetRequest(int theMovieDbId);
Task Update(MovieRequests request);
Task Save();
}
}

@ -15,5 +15,6 @@ namespace Ombi.Store.Repository.Requests
Task Update(TvRequests request);
Task UpdateChild(ChildRequests request);
IQueryable<ChildRequests> GetChild();
Task Save();
}
}

@ -1,4 +1,5 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Store.Context;
@ -46,5 +47,10 @@ namespace Ombi.Store.Repository.Requests
{
await Db.SaveChangesAsync();
}
}
public async Task Save()
{
await Db.SaveChangesAsync();
}
}
}

@ -45,6 +45,11 @@ namespace Ombi.Store.Repository.Requests
.AsQueryable();
}
public async Task Save()
{
await Db.SaveChangesAsync();
}
public async Task<TvRequests> Add(TvRequests request)
{
await Db.TvRequests.AddAsync(request);

Loading…
Cancel
Save