mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-10 15:32:37 -07:00
feat(emby): Show watched status for TV requests
* feat(emby): Show watched status for TV requests * Consider only requested episodes in played progress * Clarify tv watched progress tooltip * Fix unrespected code guidelines
This commit is contained in:
parent
151efe19d0
commit
1f37de0888
24 changed files with 1623 additions and 41 deletions
|
@ -254,18 +254,30 @@ namespace Ombi.Api.Emby
|
||||||
req.AddHeader("Device", "Ombi");
|
req.AddHeader("Device", "Ombi");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EmbyItemContainer<EmbyMovie>> GetMoviesPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri)
|
public async Task<EmbyItemContainer<EmbyMovie>> GetMoviesPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) =>
|
||||||
{
|
await GetPlayed<EmbyMovie>("Movie", apiKey, userId, baseUri, startIndex, count, parentIdFilder, "ProviderIds");
|
||||||
return await GetPlayed<EmbyMovie>("Movie", apiKey, userId, baseUri, startIndex, count, parentIdFilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<EmbyItemContainer<T>> GetPlayed<T>(string type, string apiKey, string userId, string baseUri, int startIndex, int count, string parentIdFilder = default)
|
public async Task<EmbyItemContainer<EmbyEpisodes>> GetTvPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) =>
|
||||||
|
await GetPlayed<EmbyEpisodes>("Episode", apiKey, userId, baseUri, startIndex, count, parentIdFilder);
|
||||||
|
|
||||||
|
private async Task<EmbyItemContainer<T>> GetPlayed<T>(
|
||||||
|
string type,
|
||||||
|
string apiKey,
|
||||||
|
string userId,
|
||||||
|
string baseUri,
|
||||||
|
int startIndex,
|
||||||
|
int count,
|
||||||
|
string parentIdFilder = default,
|
||||||
|
string fields = default)
|
||||||
{
|
{
|
||||||
var request = new Request($"emby/items", baseUri, HttpMethod.Get);
|
var request = new Request($"emby/items", baseUri, HttpMethod.Get);
|
||||||
|
|
||||||
request.AddQueryString("Recursive", true.ToString());
|
request.AddQueryString("Recursive", true.ToString());
|
||||||
request.AddQueryString("IncludeItemTypes", type);
|
request.AddQueryString("IncludeItemTypes", type);
|
||||||
request.AddQueryString("Fields", "ProviderIds");
|
if (!string.IsNullOrEmpty(fields))
|
||||||
|
{
|
||||||
|
request.AddQueryString("Fields", fields);
|
||||||
|
}
|
||||||
request.AddQueryString("UserId", userId);
|
request.AddQueryString("UserId", userId);
|
||||||
request.AddQueryString("isPlayed", true.ToString());
|
request.AddQueryString("isPlayed", true.ToString());
|
||||||
|
|
||||||
|
|
|
@ -34,5 +34,6 @@ namespace Ombi.Api.Emby
|
||||||
Task<EmbyItemContainer<EmbySeries>> RecentlyAddedShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
|
Task<EmbyItemContainer<EmbySeries>> RecentlyAddedShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
|
||||||
|
|
||||||
Task<EmbyItemContainer<EmbyMovie>> GetMoviesPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
|
Task<EmbyItemContainer<EmbyMovie>> GetMoviesPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
|
||||||
|
Task<EmbyItemContainer<EmbyEpisodes>> GetTvPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -35,7 +35,8 @@ namespace Ombi.Core.Engine
|
||||||
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, ICurrentUser user,
|
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, ICurrentUser user,
|
||||||
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, ILogger<TvRequestEngine> logger,
|
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, ILogger<TvRequestEngine> logger,
|
||||||
ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
|
ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
|
||||||
IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService) : base(user, requestService, rule, manager, cache, settings, sub)
|
IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService,
|
||||||
|
IUserPlayedEpisodeRepository userPlayedEpisodeRepository) : base(user, requestService, rule, manager, cache, settings, sub)
|
||||||
{
|
{
|
||||||
TvApi = tvApi;
|
TvApi = tvApi;
|
||||||
MovieDbApi = movApi;
|
MovieDbApi = movApi;
|
||||||
|
@ -44,6 +45,7 @@ namespace Ombi.Core.Engine
|
||||||
TvSender = sender;
|
TvSender = sender;
|
||||||
_requestLog = rl;
|
_requestLog = rl;
|
||||||
_mediaCacheService = mediaCacheService;
|
_mediaCacheService = mediaCacheService;
|
||||||
|
_userPlayedEpisodeRepository = userPlayedEpisodeRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
private INotificationHelper NotificationHelper { get; }
|
private INotificationHelper NotificationHelper { get; }
|
||||||
|
@ -54,6 +56,7 @@ namespace Ombi.Core.Engine
|
||||||
private readonly ILogger<TvRequestEngine> _logger;
|
private readonly ILogger<TvRequestEngine> _logger;
|
||||||
private readonly IRepository<RequestLog> _requestLog;
|
private readonly IRepository<RequestLog> _requestLog;
|
||||||
private readonly IMediaCacheService _mediaCacheService;
|
private readonly IMediaCacheService _mediaCacheService;
|
||||||
|
private readonly IUserPlayedEpisodeRepository _userPlayedEpisodeRepository;
|
||||||
|
|
||||||
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
|
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
|
||||||
{
|
{
|
||||||
|
@ -292,7 +295,7 @@ namespace Ombi.Core.Engine
|
||||||
.Skip(position).Take(count).ToListAsync();
|
.Skip(position).Take(count).ToListAsync();
|
||||||
|
|
||||||
}
|
}
|
||||||
await CheckForSubscription(shouldHide, allRequests);
|
await FillAdditionalFields(shouldHide, allRequests);
|
||||||
|
|
||||||
return new RequestsViewModel<TvRequests>
|
return new RequestsViewModel<TvRequests>
|
||||||
{
|
{
|
||||||
|
@ -328,7 +331,7 @@ namespace Ombi.Core.Engine
|
||||||
return new RequestsViewModel<TvRequests>();
|
return new RequestsViewModel<TvRequests>();
|
||||||
}
|
}
|
||||||
|
|
||||||
await CheckForSubscription(shouldHide, allRequests);
|
await FillAdditionalFields(shouldHide, allRequests);
|
||||||
|
|
||||||
return new RequestsViewModel<TvRequests>
|
return new RequestsViewModel<TvRequests>
|
||||||
{
|
{
|
||||||
|
@ -351,7 +354,7 @@ namespace Ombi.Core.Engine
|
||||||
allRequests = await TvRepository.Get().ToListAsync();
|
allRequests = await TvRepository.Get().ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
await CheckForSubscription(shouldHide, allRequests);
|
await FillAdditionalFields(shouldHide, allRequests);
|
||||||
return allRequests;
|
return allRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,7 +399,7 @@ namespace Ombi.Core.Engine
|
||||||
? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
|
? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
|
||||||
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
|
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
|
||||||
|
|
||||||
await CheckForSubscription(shouldHide, allRequests);
|
await FillAdditionalFields(shouldHide, allRequests);
|
||||||
|
|
||||||
// Make sure we do not show duplicate child requests
|
// Make sure we do not show duplicate child requests
|
||||||
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
|
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
|
||||||
|
@ -469,7 +472,7 @@ namespace Ombi.Core.Engine
|
||||||
? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
|
? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
|
||||||
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
|
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
|
||||||
|
|
||||||
await CheckForSubscription(shouldHide, allRequests);
|
await FillAdditionalFields(shouldHide, allRequests);
|
||||||
|
|
||||||
// Make sure we do not show duplicate child requests
|
// Make sure we do not show duplicate child requests
|
||||||
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
|
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
|
||||||
|
@ -523,7 +526,7 @@ namespace Ombi.Core.Engine
|
||||||
allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase)
|
allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase)
|
||||||
? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
|
? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
|
||||||
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
|
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
|
||||||
await CheckForSubscription(shouldHide, allRequests);
|
await FillAdditionalFields(shouldHide, allRequests);
|
||||||
|
|
||||||
// Make sure we do not show duplicate child requests
|
// Make sure we do not show duplicate child requests
|
||||||
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
|
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
|
||||||
|
@ -551,7 +554,7 @@ namespace Ombi.Core.Engine
|
||||||
allRequests = await TvRepository.GetLite().ToListAsync();
|
allRequests = await TvRepository.GetLite().ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
await CheckForSubscription(shouldHide, allRequests);
|
await FillAdditionalFields(shouldHide, allRequests);
|
||||||
return allRequests;
|
return allRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,7 +573,7 @@ namespace Ombi.Core.Engine
|
||||||
request = await TvRepository.Get().Where(x => x.Id == requestId).FirstOrDefaultAsync();
|
request = await TvRepository.Get().Where(x => x.Id == requestId).FirstOrDefaultAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
await CheckForSubscription(shouldHide, new List<TvRequests>{request});
|
await FillAdditionalFields(shouldHide, new List<TvRequests>{request});
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,7 +627,7 @@ namespace Ombi.Core.Engine
|
||||||
allRequests = await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync();
|
allRequests = await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
await CheckForSubscription(shouldHide, allRequests);
|
await FillAdditionalFields(shouldHide, allRequests);
|
||||||
|
|
||||||
return allRequests;
|
return allRequests;
|
||||||
}
|
}
|
||||||
|
@ -643,7 +646,7 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
||||||
|
|
||||||
await CheckForSubscription(shouldHide, results);
|
await FillAdditionalFields(shouldHide, results);
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,14 +867,20 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckForSubscription(HideResult shouldHide, List<TvRequests> x)
|
private async Task FillAdditionalFields(HideResult shouldHide, List<TvRequests> x)
|
||||||
{
|
{
|
||||||
foreach (var tvRequest in x)
|
foreach (var tvRequest in x)
|
||||||
{
|
{
|
||||||
await CheckForSubscription(shouldHide, tvRequest.ChildRequests);
|
await FillAdditionalFields(shouldHide, tvRequest.ChildRequests);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task FillAdditionalFields(HideResult shouldHide, List<ChildRequests> childRequests)
|
||||||
|
{
|
||||||
|
await CheckForSubscription(shouldHide, childRequests);
|
||||||
|
CheckForPlayed(shouldHide, childRequests);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task CheckForSubscription(HideResult shouldHide, List<ChildRequests> childRequests)
|
private async Task CheckForSubscription(HideResult shouldHide, List<ChildRequests> childRequests)
|
||||||
{
|
{
|
||||||
var sub = _subscriptionRepository.GetAll();
|
var sub = _subscriptionRepository.GetAll();
|
||||||
|
@ -896,6 +905,52 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class EpisodeKey
|
||||||
|
{
|
||||||
|
public int SeasonNumber;
|
||||||
|
public int EpisodeNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckForPlayed(HideResult shouldHide, List<ChildRequests> childRequests)
|
||||||
|
{
|
||||||
|
var theMovieDbIds = childRequests.Select(x => x.Id);
|
||||||
|
foreach (var request in childRequests)
|
||||||
|
{
|
||||||
|
var requestedEpisodes = GetEpisodesKeys(request);
|
||||||
|
|
||||||
|
var playedEpisodes = _userPlayedEpisodeRepository
|
||||||
|
.GetAll()
|
||||||
|
.Where(x => x.TheMovieDbId == request.Id && x.UserId == request.RequestedUserId)
|
||||||
|
.AsEnumerable()
|
||||||
|
.Join(requestedEpisodes,
|
||||||
|
played => new { played.SeasonNumber, played.EpisodeNumber },
|
||||||
|
requested => new { requested.SeasonNumber, requested.EpisodeNumber },
|
||||||
|
(played, requested) => new { played });
|
||||||
|
|
||||||
|
var playedCount = playedEpisodes.Count();
|
||||||
|
var toWatchCount = requestedEpisodes.Count();
|
||||||
|
request.RequestedUserPlayedProgress = 100 * playedCount / toWatchCount;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<EpisodeKey> GetEpisodesKeys(ChildRequests request)
|
||||||
|
{
|
||||||
|
List<EpisodeKey> result = new List<EpisodeKey>();
|
||||||
|
foreach(var season in request.SeasonRequests)
|
||||||
|
{
|
||||||
|
foreach(var episode in season.Episodes)
|
||||||
|
{
|
||||||
|
result.Add(new EpisodeKey
|
||||||
|
{
|
||||||
|
SeasonNumber = season.SeasonNumber,
|
||||||
|
EpisodeNumber = episode.EpisodeNumber
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf, int rootFolder, int qualityProfile)
|
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf, int rootFolder, int qualityProfile)
|
||||||
{
|
{
|
||||||
// Add the child
|
// Add the child
|
||||||
|
|
|
@ -198,6 +198,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddScoped<IJellyfinContentRepository, JellyfinContentRepository>();
|
services.AddScoped<IJellyfinContentRepository, JellyfinContentRepository>();
|
||||||
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||||
services.AddScoped<IUserPlayedMovieRepository, UserPlayedMovieRepository>();
|
services.AddScoped<IUserPlayedMovieRepository, UserPlayedMovieRepository>();
|
||||||
|
services.AddScoped<IUserPlayedEpisodeRepository, UserPlayedEpisodeRepository>();
|
||||||
|
|
||||||
services.AddScoped<ITvRequestRepository, TvRequestRepository>();
|
services.AddScoped<ITvRequestRepository, TvRequestRepository>();
|
||||||
services.AddScoped<IMovieRequestRepository, MovieRequestRepository>();
|
services.AddScoped<IMovieRequestRepository, MovieRequestRepository>();
|
||||||
|
|
|
@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Ombi.Api.Emby;
|
using Ombi.Api.Emby;
|
||||||
using Ombi.Api.Emby.Models;
|
using Ombi.Api.Emby.Models;
|
||||||
|
using Ombi.Api.Emby.Models.Media.Tv;
|
||||||
using Ombi.Api.Emby.Models.Movie;
|
using Ombi.Api.Emby.Models.Movie;
|
||||||
using Ombi.Core.Authentication;
|
using Ombi.Core.Authentication;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
|
@ -18,20 +19,34 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
{
|
{
|
||||||
public class EmbyPlayedSync : EmbyLibrarySync, IEmbyPlayedSync
|
public class EmbyPlayedSync : EmbyLibrarySync, IEmbyPlayedSync
|
||||||
{
|
{
|
||||||
public EmbyPlayedSync(ISettingsService<EmbySettings> settings, IEmbyApiFactory api, ILogger<EmbyContentSync> logger,
|
public EmbyPlayedSync(
|
||||||
IUserPlayedMovieRepository repo, INotificationHubService notification, OmbiUserManager user) : base(settings, api, logger, notification)
|
ISettingsService<EmbySettings> settings,
|
||||||
|
IEmbyApiFactory api,
|
||||||
|
ILogger<EmbyContentSync> logger,
|
||||||
|
IUserPlayedMovieRepository movieRepo,
|
||||||
|
IUserPlayedEpisodeRepository episodeRepo,
|
||||||
|
IEmbyContentRepository contentRepo,
|
||||||
|
INotificationHubService notification,
|
||||||
|
OmbiUserManager user) : base(settings, api, logger, notification)
|
||||||
{
|
{
|
||||||
_userManager = user;
|
_userManager = user;
|
||||||
_repo = repo;
|
_movieRepo = movieRepo;
|
||||||
|
_contentRepo = contentRepo;
|
||||||
|
_episodeRepo = episodeRepo;
|
||||||
}
|
}
|
||||||
private OmbiUserManager _userManager { get; }
|
private OmbiUserManager _userManager { get; }
|
||||||
|
|
||||||
private readonly IUserPlayedMovieRepository _repo;
|
private readonly IUserPlayedMovieRepository _movieRepo;
|
||||||
|
private readonly IUserPlayedEpisodeRepository _episodeRepo;
|
||||||
|
private readonly IEmbyContentRepository _contentRepo;
|
||||||
|
|
||||||
protected override Task ProcessTv(EmbyServers server, string parentId = default)
|
protected async override Task ProcessTv(EmbyServers server, string parentId = default)
|
||||||
{
|
{
|
||||||
// TODO
|
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser || x.UserType == UserType.EmbyConnectUser).ToListAsync();
|
||||||
return Task.CompletedTask;
|
foreach (var user in allUsers)
|
||||||
|
{
|
||||||
|
await ProcessTvUser(server, user, parentId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override Task ProcessMovies(EmbyServers server, string parentId = default)
|
protected async override Task ProcessMovies(EmbyServers server, string parentId = default)
|
||||||
|
@ -65,7 +80,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
var totalCount = movies.TotalRecordCount;
|
var totalCount = movies.TotalRecordCount;
|
||||||
var processed = 0;
|
var processed = 0;
|
||||||
var mediaToAdd = new HashSet<UserPlayedMovie>();
|
var mediaToAdd = new HashSet<UserPlayedMovie>();
|
||||||
|
|
||||||
while (processed < totalCount)
|
while (processed < totalCount)
|
||||||
{
|
{
|
||||||
foreach (var movie in movies.Items)
|
foreach (var movie in movies.Items)
|
||||||
|
@ -80,7 +95,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
{
|
{
|
||||||
movies = await Api.GetMoviesPlayed(server.ApiKey, parentId, processed, AmountToTake, user.ProviderUserId, server.FullUri);
|
movies = await Api.GetMoviesPlayed(server.ApiKey, parentId, processed, AmountToTake, user.ProviderUserId, server.FullUri);
|
||||||
}
|
}
|
||||||
await _repo.AddRange(mediaToAdd);
|
await _movieRepo.AddRange(mediaToAdd);
|
||||||
mediaToAdd.Clear();
|
mediaToAdd.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,13 +113,117 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
UserId = user.Id
|
UserId = user.Id
|
||||||
};
|
};
|
||||||
// Check if it exists
|
// Check if it exists
|
||||||
var existingMovie = await _repo.Get(userPlayedMovie.TheMovieDbId, userPlayedMovie.UserId);
|
var existingMovie = await _movieRepo.Get(userPlayedMovie.TheMovieDbId, userPlayedMovie.UserId);
|
||||||
var alreadyGoingToAdd = content.Any(x => x.TheMovieDbId == userPlayedMovie.TheMovieDbId && x.UserId == userPlayedMovie.UserId);
|
var alreadyGoingToAdd = content.Any(x => x.TheMovieDbId == userPlayedMovie.TheMovieDbId && x.UserId == userPlayedMovie.UserId);
|
||||||
if (existingMovie == null && !alreadyGoingToAdd)
|
if (existingMovie == null && !alreadyGoingToAdd)
|
||||||
{
|
{
|
||||||
content.Add(userPlayedMovie);
|
content.Add(userPlayedMovie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ProcessTvUser(EmbyServers server, OmbiUser user, string parentId = default)
|
||||||
|
{
|
||||||
|
EmbyItemContainer<EmbyEpisodes> episodes;
|
||||||
|
if (recentlyAdded)
|
||||||
|
{
|
||||||
|
var recentlyAddedAmountToTake = 10; // to be adjusted?
|
||||||
|
episodes = await Api.GetTvPlayed(server.ApiKey, parentId, 0, recentlyAddedAmountToTake, user.ProviderUserId, server.FullUri);
|
||||||
|
// Setting this so we don't attempt to grab more than we need
|
||||||
|
if (episodes.TotalRecordCount > recentlyAddedAmountToTake)
|
||||||
|
{
|
||||||
|
episodes.TotalRecordCount = recentlyAddedAmountToTake;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
episodes = await Api.GetTvPlayed(server.ApiKey, parentId, 0, AmountToTake, user.ProviderUserId, server.FullUri);
|
||||||
|
}
|
||||||
|
var totalCount = episodes.TotalRecordCount;
|
||||||
|
var processed = 0;
|
||||||
|
var mediaToAdd = new HashSet<UserPlayedEpisode>();
|
||||||
|
|
||||||
|
while (processed < totalCount)
|
||||||
|
{
|
||||||
|
foreach (var episode in episodes.Items)
|
||||||
|
{
|
||||||
|
await ProcessTv(episode, user, mediaToAdd, server);
|
||||||
|
processed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next batch
|
||||||
|
// Recently Added should never be checked as the TotalRecords should equal the amount to take
|
||||||
|
if (!recentlyAdded)
|
||||||
|
{
|
||||||
|
episodes = await Api.GetTvPlayed(server.ApiKey, parentId, processed, AmountToTake, user.ProviderUserId, server.FullUri);
|
||||||
|
}
|
||||||
|
await _episodeRepo.AddRange(mediaToAdd);
|
||||||
|
mediaToAdd.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task ProcessTv(EmbyEpisodes episode, OmbiUser user, ICollection<UserPlayedEpisode> content, EmbyServers server)
|
||||||
|
{
|
||||||
|
|
||||||
|
var parent = await _contentRepo.GetByEmbyId(episode.SeriesId);
|
||||||
|
if (parent == null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this",
|
||||||
|
episode.Name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (parent.TheMovieDbId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
_logger.LogWarning($"Episode {episode.Name} is not linked to a TMDB series. Skipping.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await AddToContent(content, new UserPlayedEpisode()
|
||||||
|
{
|
||||||
|
TheMovieDbId = int.Parse(parent.TheMovieDbId),
|
||||||
|
SeasonNumber = episode.ParentIndexNumber,
|
||||||
|
EpisodeNumber = episode.IndexNumber,
|
||||||
|
UserId = user.Id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (episode.IndexNumberEnd.HasValue && episode.IndexNumberEnd.Value != episode.IndexNumber)
|
||||||
|
{
|
||||||
|
int episodeNumber = episode.IndexNumber;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
_logger.LogDebug($"Multiple-episode file detected. Adding episode ${episodeNumber}");
|
||||||
|
episodeNumber++;
|
||||||
|
|
||||||
|
await AddToContent(content, new UserPlayedEpisode()
|
||||||
|
{
|
||||||
|
TheMovieDbId = int.Parse(parent.TheMovieDbId),
|
||||||
|
SeasonNumber = episode.ParentIndexNumber,
|
||||||
|
EpisodeNumber = episodeNumber,
|
||||||
|
UserId = user.Id
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
} while (episodeNumber < episode.IndexNumberEnd.Value);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddToContent(ICollection<UserPlayedEpisode> content, UserPlayedEpisode episode)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Check if it exists
|
||||||
|
var existingEpisode = await _episodeRepo.Get(episode.TheMovieDbId, episode.SeasonNumber, episode.EpisodeNumber, episode.UserId);
|
||||||
|
var alreadyGoingToAdd = content.Any(x =>
|
||||||
|
x.TheMovieDbId == episode.TheMovieDbId
|
||||||
|
&& x.SeasonNumber == episode.SeasonNumber
|
||||||
|
&& x.EpisodeNumber == episode.EpisodeNumber
|
||||||
|
&& x.UserId == episode.UserId);
|
||||||
|
if (existingEpisode == null && !alreadyGoingToAdd)
|
||||||
|
{
|
||||||
|
content.Add(episode);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
IPlexContentRepository plexRepo,
|
IPlexContentRepository plexRepo,
|
||||||
IEmbyContentRepository embyRepo,
|
IEmbyContentRepository embyRepo,
|
||||||
IJellyfinContentRepository jellyfinRepo,
|
IJellyfinContentRepository jellyfinRepo,
|
||||||
IUserPlayedMovieRepository userPlayedRepo,
|
IUserPlayedMovieRepository userPlayedMovieRepo,
|
||||||
|
IUserPlayedEpisodeRepository userPlayedEpisodeRepo,
|
||||||
ISettingsService<EmbySettings> embySettings,
|
ISettingsService<EmbySettings> embySettings,
|
||||||
ISettingsService<JellyfinSettings> jellyfinSettings)
|
ISettingsService<JellyfinSettings> jellyfinSettings)
|
||||||
{
|
{
|
||||||
|
@ -30,7 +31,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
_plexRepo = plexRepo;
|
_plexRepo = plexRepo;
|
||||||
_embyRepo = embyRepo;
|
_embyRepo = embyRepo;
|
||||||
_jellyfinRepo = jellyfinRepo;
|
_jellyfinRepo = jellyfinRepo;
|
||||||
_userPlayedRepo = userPlayedRepo;
|
_userPlayedMovieRepo = userPlayedMovieRepo;
|
||||||
|
_userPlayedEpisodeRepo = userPlayedEpisodeRepo;
|
||||||
_embySettings = embySettings;
|
_embySettings = embySettings;
|
||||||
_jellyfinSettings = jellyfinSettings;
|
_jellyfinSettings = jellyfinSettings;
|
||||||
_plexSettings.ClearCache();
|
_plexSettings.ClearCache();
|
||||||
|
@ -41,7 +43,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
private readonly IPlexContentRepository _plexRepo;
|
private readonly IPlexContentRepository _plexRepo;
|
||||||
private readonly IEmbyContentRepository _embyRepo;
|
private readonly IEmbyContentRepository _embyRepo;
|
||||||
private readonly IJellyfinContentRepository _jellyfinRepo;
|
private readonly IJellyfinContentRepository _jellyfinRepo;
|
||||||
private readonly IUserPlayedMovieRepository _userPlayedRepo;
|
private readonly IUserPlayedMovieRepository _userPlayedMovieRepo;
|
||||||
|
private readonly IUserPlayedEpisodeRepository _userPlayedEpisodeRepo;
|
||||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||||
|
|
||||||
|
@ -66,7 +69,10 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const string movieSql = "DELETE FROM UserPlayedMovie";
|
const string movieSql = "DELETE FROM UserPlayedMovie";
|
||||||
await _userPlayedRepo.ExecuteSql(movieSql);
|
await _userPlayedMovieRepo.ExecuteSql(movieSql);
|
||||||
|
|
||||||
|
const string episodeSql = "DELETE FROM UserPlayedEpisode";
|
||||||
|
await _userPlayedEpisodeRepo.ExecuteSql(episodeSql);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace Ombi.Store.Context
|
||||||
public DbSet<SickRageCache> SickRageCache { get; set; }
|
public DbSet<SickRageCache> SickRageCache { get; set; }
|
||||||
public DbSet<SickRageEpisodeCache> SickRageEpisodeCache { get; set; }
|
public DbSet<SickRageEpisodeCache> SickRageEpisodeCache { get; set; }
|
||||||
public DbSet<UserPlayedMovie> UserPlayedMovie { get; set; }
|
public DbSet<UserPlayedMovie> UserPlayedMovie { get; set; }
|
||||||
|
public DbSet<UserPlayedEpisode> UserPlayedEpisode { get; set; }
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder builder)
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,6 +59,9 @@ namespace Ombi.Store.Entities.Requests
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public int RequestedUserPlayedProgress { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SeriesType
|
public enum SeriesType
|
||||||
|
|
10
src/Ombi.Store/Entities/UserPlayedEpisode.cs
Normal file
10
src/Ombi.Store/Entities/UserPlayedEpisode.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ombi.Store.Entities
|
||||||
|
{
|
||||||
|
public class UserPlayedEpisode : Entity
|
||||||
|
{
|
||||||
|
public int TheMovieDbId { get; set; }
|
||||||
|
public int SeasonNumber { get; set; }
|
||||||
|
public int EpisodeNumber { get; set; }
|
||||||
|
public string UserId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
589
src/Ombi.Store/Migrations/ExternalMySql/20230515182204_MovieEpisodePlayed.Designer.cs
generated
Normal file
589
src/Ombi.Store/Migrations/ExternalMySql/20230515182204_MovieEpisodePlayed.Designer.cs
generated
Normal file
|
@ -0,0 +1,589 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Ombi.Store.Context.MySql;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations.ExternalMySql
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ExternalMySqlContext))]
|
||||||
|
[Migration("20230515182204_MovieEpisodePlayed")]
|
||||||
|
partial class MovieEpisodePlayed
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "6.0.9")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("CouchPotatoCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<bool>("Has4K")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Quality")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("EmbyContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ParentId")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ParentId");
|
||||||
|
|
||||||
|
b.ToTable("EmbyEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<bool>("Has4K")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("JellyfinId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Quality")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("JellyfinContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("JellyfinId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ParentId")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ParentId");
|
||||||
|
|
||||||
|
b.ToTable("JellyfinEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("ArtistId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ForeignAlbumId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("Monitored")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<decimal>("PercentOfTracks")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ReleaseDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("TrackCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LidarrAlbumCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("ArtistId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ArtistName")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ForeignArtistId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("Monitored")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LidarrArtistCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("GrandparentKey")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ParentKey")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GrandparentKey");
|
||||||
|
|
||||||
|
b.ToTable("PlexEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ParentKey")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("PlexContentId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int?>("PlexServerContentId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("SeasonKey")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PlexServerContentId");
|
||||||
|
|
||||||
|
b.ToTable("PlexSeasonsContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<bool>("Has4K")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("Quality")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ReleaseYear")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int?>("RequestId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PlexServerContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("TmdbId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PlexWatchlistHistory");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<bool>("Has4K")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("HasFile")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("HasRegular")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("RadarrCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SickRageCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SickRageEpisodeCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SonarrCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<bool>("HasFile")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<int>("MovieDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SonarrEpisodeCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("UserPlayedEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("UserPlayedMovie");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||||
|
.WithMany("Episodes")
|
||||||
|
.HasForeignKey("ParentId")
|
||||||
|
.HasPrincipalKey("EmbyId");
|
||||||
|
|
||||||
|
b.Navigation("Series");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
|
||||||
|
.WithMany("Episodes")
|
||||||
|
.HasForeignKey("ParentId")
|
||||||
|
.HasPrincipalKey("JellyfinId");
|
||||||
|
|
||||||
|
b.Navigation("Series");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
|
||||||
|
.WithMany("Episodes")
|
||||||
|
.HasForeignKey("GrandparentKey")
|
||||||
|
.HasPrincipalKey("Key");
|
||||||
|
|
||||||
|
b.Navigation("Series");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
|
||||||
|
.WithMany("Seasons")
|
||||||
|
.HasForeignKey("PlexServerContentId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Episodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Episodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Episodes");
|
||||||
|
|
||||||
|
b.Navigation("Seasons");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations.ExternalMySql
|
||||||
|
{
|
||||||
|
public partial class MovieEpisodePlayed : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserPlayedEpisode",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||||
|
TheMovieDbId = table.Column<int>(type: "int", nullable: false),
|
||||||
|
SeasonNumber = table.Column<int>(type: "int", nullable: false),
|
||||||
|
EpisodeNumber = table.Column<int>(type: "int", nullable: false),
|
||||||
|
UserId = table.Column<string>(type: "longtext", nullable: true)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserPlayedEpisode", x => x.Id);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserPlayedEpisode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -488,6 +488,29 @@ namespace Ombi.Store.Migrations.ExternalMySql
|
||||||
b.ToTable("SonarrEpisodeCache");
|
b.ToTable("SonarrEpisodeCache");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("UserPlayedEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
|
|
587
src/Ombi.Store/Migrations/ExternalSqlite/20230515161757_EpisodeUserPlayed.Designer.cs
generated
Normal file
587
src/Ombi.Store/Migrations/ExternalSqlite/20230515161757_EpisodeUserPlayed.Designer.cs
generated
Normal file
|
@ -0,0 +1,587 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Ombi.Store.Context.Sqlite;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations.ExternalSqlite
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ExternalSqliteContext))]
|
||||||
|
[Migration("20230515161757_EpisodeUserPlayed")]
|
||||||
|
partial class EpisodeUserPlayed
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "6.0.9");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("CouchPotatoCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Has4K")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Quality")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("EmbyContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ParentId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ParentId");
|
||||||
|
|
||||||
|
b.ToTable("EmbyEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Has4K")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("JellyfinId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Quality")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("JellyfinContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("JellyfinId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ParentId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ParentId");
|
||||||
|
|
||||||
|
b.ToTable("JellyfinEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("ArtistId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ForeignAlbumId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Monitored")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<decimal>("PercentOfTracks")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ReleaseDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("TrackCount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LidarrAlbumCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ArtistId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ArtistName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ForeignArtistId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Monitored")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LidarrArtistCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("GrandparentKey")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ParentKey")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GrandparentKey");
|
||||||
|
|
||||||
|
b.ToTable("PlexEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ParentKey")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PlexContentId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("PlexServerContentId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("SeasonKey")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PlexServerContentId");
|
||||||
|
|
||||||
|
b.ToTable("PlexSeasonsContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Has4K")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Quality")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ReleaseYear")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("RequestId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PlexServerContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("TmdbId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PlexWatchlistHistory");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("Has4K")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("HasFile")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("HasRegular")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("RadarrCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SickRageCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SickRageEpisodeCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SonarrCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("HasFile")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("MovieDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SonarrEpisodeCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("UserPlayedEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("UserPlayedMovie");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||||
|
.WithMany("Episodes")
|
||||||
|
.HasForeignKey("ParentId")
|
||||||
|
.HasPrincipalKey("EmbyId");
|
||||||
|
|
||||||
|
b.Navigation("Series");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
|
||||||
|
.WithMany("Episodes")
|
||||||
|
.HasForeignKey("ParentId")
|
||||||
|
.HasPrincipalKey("JellyfinId");
|
||||||
|
|
||||||
|
b.Navigation("Series");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
|
||||||
|
.WithMany("Episodes")
|
||||||
|
.HasForeignKey("GrandparentKey")
|
||||||
|
.HasPrincipalKey("Key");
|
||||||
|
|
||||||
|
b.Navigation("Series");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
|
||||||
|
.WithMany("Seasons")
|
||||||
|
.HasForeignKey("PlexServerContentId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Episodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Episodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Episodes");
|
||||||
|
|
||||||
|
b.Navigation("Seasons");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations.ExternalSqlite
|
||||||
|
{
|
||||||
|
public partial class EpisodeUserPlayed : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserPlayedEpisode",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
TheMovieDbId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
SeasonNumber = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
EpisodeNumber = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
UserId = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserPlayedEpisode", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserPlayedEpisode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -486,6 +486,29 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
||||||
b.ToTable("SonarrEpisodeCache");
|
b.ToTable("SonarrEpisodeCache");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("UserPlayedEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
|
|
13
src/Ombi.Store/Repository/IUserPlayedEpisodeRepository.cs
Normal file
13
src/Ombi.Store/Repository/IUserPlayedEpisodeRepository.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Repository
|
||||||
|
{
|
||||||
|
public interface IUserPlayedEpisodeRepository : IExternalRepository<UserPlayedEpisode>
|
||||||
|
{
|
||||||
|
Task<UserPlayedEpisode> Get(int theMovieDbId, int seasonNumber, int episodeNumber, string userId);
|
||||||
|
}
|
||||||
|
}
|
26
src/Ombi.Store/Repository/UserPlayedEpisodeRepository.cs
Normal file
26
src/Ombi.Store/Repository/UserPlayedEpisodeRepository.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Query;
|
||||||
|
using Ombi.Store.Context;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Repository
|
||||||
|
{
|
||||||
|
public class UserPlayedEpisodeRepository : ExternalRepository<UserPlayedEpisode>, IUserPlayedEpisodeRepository
|
||||||
|
{
|
||||||
|
protected ExternalContext Db { get; }
|
||||||
|
public UserPlayedEpisodeRepository(ExternalContext db) : base(db)
|
||||||
|
{
|
||||||
|
Db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserPlayedEpisode> Get(int theMovieDbId, int seasonNumber, int episodeNumber, string userId)
|
||||||
|
{
|
||||||
|
return await Db.UserPlayedEpisode.FirstOrDefaultAsync(x => x.TheMovieDbId == theMovieDbId && x.SeasonNumber == seasonNumber && x.EpisodeNumber == episodeNumber && x.UserId == userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,6 +46,7 @@ import { MatNativeDateModule } from '@angular/material/core';
|
||||||
import { MatPaginatorI18n } from "./localization/MatPaginatorI18n";
|
import { MatPaginatorI18n } from "./localization/MatPaginatorI18n";
|
||||||
import { MatPaginatorIntl } from "@angular/material/paginator";
|
import { MatPaginatorIntl } from "@angular/material/paginator";
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||||
import { MatSlideToggleModule } from "@angular/material/slide-toggle";
|
import { MatSlideToggleModule } from "@angular/material/slide-toggle";
|
||||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
|
@ -150,6 +151,7 @@ export function JwtTokenGetter() {
|
||||||
OverlayModule,
|
OverlayModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
|
MatProgressBarModule,
|
||||||
JwtModule.forRoot({
|
JwtModule.forRoot({
|
||||||
config: {
|
config: {
|
||||||
tokenGetter: JwtTokenGetter,
|
tokenGetter: JwtTokenGetter,
|
||||||
|
|
|
@ -132,6 +132,7 @@ export interface ITvRequests {
|
||||||
background: any;
|
background: any;
|
||||||
totalSeasons: number;
|
totalSeasons: number;
|
||||||
tvDbId: number; // NO LONGER USED
|
tvDbId: number; // NO LONGER USED
|
||||||
|
requestedUserPlayedProgress: number;
|
||||||
|
|
||||||
open: boolean; // THIS IS FOR THE UI
|
open: boolean; // THIS IS FOR THE UI
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,19 @@
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="watchedByRequestedUser">
|
||||||
|
<th
|
||||||
|
mat-header-cell
|
||||||
|
*matHeaderCellDef
|
||||||
|
disableClear
|
||||||
|
matTooltip="{{ 'Requests.WatchedProgressTooltip' | translate}}">
|
||||||
|
{{ 'Requests.Watched' | translate}}
|
||||||
|
</th>
|
||||||
|
<td mat-cell id="requestedUserPlayedProgress{{element.id}}" *matCellDef="let element">
|
||||||
|
<mat-progress-bar mode="determinate" value="{{element.requestedUserPlayedProgress}}" class="played-progress"></mat-progress-bar>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
@import "./styles/variables.scss";
|
||||||
|
|
||||||
|
.played-progress {
|
||||||
|
width: 5rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import { Observable, merge, of as observableOf } from 'rxjs';
|
||||||
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
|
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { AuthService } from "../../../auth/auth.service";
|
import { AuthService } from "../../../auth/auth.service";
|
||||||
|
import { FeaturesFacade } from "../../../state/features/features.facade";
|
||||||
import { MatPaginator } from "@angular/material/paginator";
|
import { MatPaginator } from "@angular/material/paginator";
|
||||||
import { MatSort } from "@angular/material/sort";
|
import { MatSort } from "@angular/material/sort";
|
||||||
import { RequestFilterType } from "../../models/RequestFilterType";
|
import { RequestFilterType } from "../../models/RequestFilterType";
|
||||||
|
@ -13,15 +14,16 @@ import { StorageService } from "../../../shared/storage/storage-service";
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./tv-grid.component.html",
|
templateUrl: "./tv-grid.component.html",
|
||||||
selector: "tv-grid",
|
selector: "tv-grid",
|
||||||
styleUrls: ["../requests-list.component.scss"]
|
styleUrls: ["../requests-list.component.scss", "tv-grid.component.scss"]
|
||||||
})
|
})
|
||||||
export class TvGridComponent implements OnInit, AfterViewInit {
|
export class TvGridComponent implements OnInit, AfterViewInit {
|
||||||
public dataSource: IChildRequests[] = [];
|
public dataSource: IChildRequests[] = [];
|
||||||
public resultsLength: number;
|
public resultsLength: number;
|
||||||
public isLoadingResults = true;
|
public isLoadingResults = true;
|
||||||
public displayedColumns: string[] = ['series', 'requestedBy', 'status', 'requestStatus', 'requestedDate','actions'];
|
public displayedColumns: string[] = ['series', 'requestedBy', 'status', 'requestStatus', 'requestedDate'];
|
||||||
public gridCount: string = "15";
|
public gridCount: string = "15";
|
||||||
public isAdmin: boolean;
|
public isAdmin: boolean;
|
||||||
|
public isPlayedSyncEnabled = false;
|
||||||
public defaultSort: string = "requestedDate";
|
public defaultSort: string = "requestedDate";
|
||||||
public defaultOrder: string = "desc";
|
public defaultOrder: string = "desc";
|
||||||
public currentFilter: RequestFilterType = RequestFilterType.All;
|
public currentFilter: RequestFilterType = RequestFilterType.All;
|
||||||
|
@ -40,12 +42,17 @@ export class TvGridComponent implements OnInit, AfterViewInit {
|
||||||
@ViewChild(MatSort) sort: MatSort;
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
|
||||||
constructor(private requestService: RequestServiceV2, private auth: AuthService,
|
constructor(private requestService: RequestServiceV2, private auth: AuthService,
|
||||||
private ref: ChangeDetectorRef, private storageService: StorageService) {
|
private ref: ChangeDetectorRef, private storageService: StorageService,
|
||||||
|
private featureFacade: FeaturesFacade) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||||
|
this.isPlayedSyncEnabled = this.featureFacade.isPlayedSyncEnabled();
|
||||||
|
|
||||||
|
this.addDynamicColumns();
|
||||||
|
|
||||||
const defaultCount = this.storageService.get(this.storageKeyGridCount);
|
const defaultCount = this.storageService.get(this.storageKeyGridCount);
|
||||||
const defaultSort = this.storageService.get(this.storageKey);
|
const defaultSort = this.storageService.get(this.storageKey);
|
||||||
const defaultOrder = this.storageService.get(this.storageKeyOrder);
|
const defaultOrder = this.storageService.get(this.storageKeyOrder);
|
||||||
|
@ -64,9 +71,18 @@ export class TvGridComponent implements OnInit, AfterViewInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addDynamicColumns() {
|
||||||
|
if (this.isPlayedSyncEnabled) {
|
||||||
|
this.displayedColumns.push('watchedByRequestedUser');
|
||||||
|
}
|
||||||
|
|
||||||
|
// always put the actions column at the end
|
||||||
|
this.displayedColumns.push('actions');
|
||||||
|
}
|
||||||
|
|
||||||
public async ngAfterViewInit() {
|
public async ngAfterViewInit() {
|
||||||
|
|
||||||
this.storageService.save(this.storageKeyGridCount, this.gridCount);
|
this.storageService.save(this.storageKeyGridCount, this.gridCount);
|
||||||
this.storageService.save(this.storageKeyCurrentFilter, (+this.currentFilter).toString());
|
this.storageService.save(this.storageKeyCurrentFilter, (+this.currentFilter).toString());
|
||||||
this.paginator.showFirstLastButtons = true;
|
this.paginator.showFirstLastButtons = true;
|
||||||
|
|
||||||
|
@ -78,7 +94,7 @@ export class TvGridComponent implements OnInit, AfterViewInit {
|
||||||
startWith({}),
|
startWith({}),
|
||||||
switchMap((value: any) => {
|
switchMap((value: any) => {
|
||||||
this.isLoadingResults = true;
|
this.isLoadingResults = true;
|
||||||
|
|
||||||
if (value.active || value.direction) {
|
if (value.active || value.direction) {
|
||||||
this.storageService.save(this.storageKey, value.active);
|
this.storageService.save(this.storageKey, value.active);
|
||||||
this.storageService.save(this.storageKeyOrder, value.direction);
|
this.storageService.save(this.storageKeyOrder, value.direction);
|
||||||
|
@ -103,7 +119,7 @@ export class TvGridComponent implements OnInit, AfterViewInit {
|
||||||
const filter = () => { this.dataSource = this.dataSource.filter((req) => {
|
const filter = () => { this.dataSource = this.dataSource.filter((req) => {
|
||||||
return req.id !== request.id;
|
return req.id !== request.id;
|
||||||
})};
|
})};
|
||||||
|
|
||||||
const onChange = () => {
|
const onChange = () => {
|
||||||
this.ref.detectChanges();
|
this.ref.detectChanges();
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {MatMenuModule} from '@angular/material/menu';
|
||||||
import { MatNativeDateModule } from '@angular/material/core';
|
import { MatNativeDateModule } from '@angular/material/core';
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||||
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
|
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
|
||||||
|
import { MatProgressBarModule } from "@angular/material/progress-bar";
|
||||||
import {MatRadioModule} from '@angular/material/radio';
|
import {MatRadioModule} from '@angular/material/radio';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||||
|
@ -66,6 +67,7 @@ import { WatchProvidersSelectComponent } from "./components/watch-providers-sele
|
||||||
MomentModule,
|
MomentModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
|
MatProgressBarModule,
|
||||||
MatAutocompleteModule,
|
MatAutocompleteModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatTabsModule,
|
MatTabsModule,
|
||||||
|
@ -99,6 +101,7 @@ import { WatchProvidersSelectComponent } from "./components/watch-providers-sele
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
SidebarModule,
|
SidebarModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
|
MatProgressBarModule,
|
||||||
IssuesReportComponent,
|
IssuesReportComponent,
|
||||||
EpisodeRequestComponent,
|
EpisodeRequestComponent,
|
||||||
AdminRequestDialogComponent,
|
AdminRequestDialogComponent,
|
||||||
|
|
|
@ -161,6 +161,7 @@
|
||||||
"RequestStatus": "Request status",
|
"RequestStatus": "Request status",
|
||||||
"Watched": "Watched",
|
"Watched": "Watched",
|
||||||
"WatchedTooltip": "The user who made the request has watched it",
|
"WatchedTooltip": "The user who made the request has watched it",
|
||||||
|
"WatchedProgressTooltip": "Shows how much the user who made the request has watched it",
|
||||||
"WatchedByUsersCount": "{{count}} users have watched this.",
|
"WatchedByUsersCount": "{{count}} users have watched this.",
|
||||||
"Denied": " Denied:",
|
"Denied": " Denied:",
|
||||||
"TheatricalRelease": "Theatrical Release: {{date}}",
|
"TheatricalRelease": "Theatrical Release: {{date}}",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue