mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 04:49:33 -07:00
merge
This commit is contained in:
commit
c4f22031d3
134 changed files with 9282 additions and 14930 deletions
|
@ -1,6 +1,6 @@
|
|||
# Changelog
|
||||
|
||||
## (unreleased)
|
||||
## v3.0.3477 (2018-07-18)
|
||||
|
||||
### **New Features**
|
||||
|
||||
|
|
25
build.cake
25
build.cake
|
@ -1,10 +1,10 @@
|
|||
|
||||
#tool "nuget:?package=GitVersion.CommandLine"
|
||||
#addin "Cake.Gulp"
|
||||
#addin "nuget:?package=Cake.Npm&version=0.13.0"
|
||||
#addin "SharpZipLib"
|
||||
#addin nuget:?package=Cake.Compression&version=0.1.4
|
||||
#addin "Cake.Incubator"
|
||||
#addin "Cake.Yarn"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// ARGUMENTS
|
||||
|
@ -122,36 +122,19 @@ Task("SetVersionInfo")
|
|||
|
||||
Task("NPM")
|
||||
.Does(() => {
|
||||
var settings = new NpmInstallSettings {
|
||||
LogLevel = NpmLogLevel.Silent,
|
||||
WorkingDirectory = webProjDir,
|
||||
Production = true
|
||||
};
|
||||
|
||||
NpmInstall(settings);
|
||||
Yarn.FromPath(webProjDir).Install();
|
||||
});
|
||||
|
||||
Task("Gulp Publish")
|
||||
.IsDependentOn("NPM")
|
||||
.Does(() => {
|
||||
|
||||
var runScriptSettings = new NpmRunScriptSettings {
|
||||
ScriptName="publish",
|
||||
WorkingDirectory = webProjDir,
|
||||
};
|
||||
|
||||
NpmRunScript(runScriptSettings);
|
||||
Yarn.FromPath(webProjDir).RunScript("publish");
|
||||
});
|
||||
|
||||
Task("TSLint")
|
||||
.Does(() =>
|
||||
{
|
||||
var settings = new NpmRunScriptSettings {
|
||||
WorkingDirectory = webProjDir,
|
||||
ScriptName = "lint"
|
||||
};
|
||||
|
||||
NpmRunScript(settings);
|
||||
Yarn.FromPath(webProjDir).RunScript("lint");
|
||||
});
|
||||
|
||||
Task("PrePublish")
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.4.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -6,6 +6,30 @@ namespace Ombi.Api.Sonarr.Models
|
|||
{
|
||||
public class Episode
|
||||
{
|
||||
public Episode()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Episode(Episode ep)
|
||||
{
|
||||
seriesId = ep.seriesId;
|
||||
episodeFileId = ep.episodeFileId;
|
||||
seasonNumber = ep.seasonNumber;
|
||||
episodeNumber = ep.episodeNumber;
|
||||
title = ep.title;
|
||||
airDate = ep.airDate;
|
||||
airDateUtc = ep.airDateUtc;
|
||||
overview = ep.overview;
|
||||
hasFile = ep.hasFile;
|
||||
monitored = ep.monitored;
|
||||
unverifiedSceneNumbering = ep.unverifiedSceneNumbering;
|
||||
id = ep.id;
|
||||
absoluteEpisodeNumber = ep.absoluteEpisodeNumber;
|
||||
sceneAbsoluteEpisodeNumber = ep.sceneAbsoluteEpisodeNumber;
|
||||
sceneEpisodeNumber = ep.sceneEpisodeNumber;
|
||||
sceneSeasonNumber = ep.sceneSeasonNumber;
|
||||
}
|
||||
public int seriesId { get; set; }
|
||||
public int episodeFileId { get; set; }
|
||||
public int seasonNumber { get; set; }
|
||||
|
@ -27,6 +51,24 @@ namespace Ombi.Api.Sonarr.Models
|
|||
|
||||
public class Episodefile
|
||||
{
|
||||
public Episodefile()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Episodefile(Episodefile e)
|
||||
{
|
||||
seriesId = e.seriesId;
|
||||
seasonNumber = e.seasonNumber;
|
||||
relativePath = e.relativePath;
|
||||
path = e.path;
|
||||
size = e.size;
|
||||
dateAdded = e.dateAdded;
|
||||
sceneName = e.sceneName;
|
||||
quality = new EpisodeQuality(e.quality);
|
||||
qualityCutoffNotMet = e.qualityCutoffNotMet;
|
||||
id = e.id;
|
||||
}
|
||||
public int seriesId { get; set; }
|
||||
public int seasonNumber { get; set; }
|
||||
public string relativePath { get; set; }
|
||||
|
@ -41,12 +83,32 @@ namespace Ombi.Api.Sonarr.Models
|
|||
|
||||
public class EpisodeQuality
|
||||
{
|
||||
public EpisodeQuality()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EpisodeQuality(EpisodeQuality e)
|
||||
{
|
||||
quality = new Quality(e.quality);
|
||||
revision = new Revision(e.revision);
|
||||
}
|
||||
public Quality quality { get; set; }
|
||||
public Revision revision { get; set; }
|
||||
}
|
||||
|
||||
public class Revision
|
||||
{
|
||||
public Revision()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Revision(Revision r)
|
||||
{
|
||||
version = r.version;
|
||||
real = r.real;
|
||||
}
|
||||
public int version { get; set; }
|
||||
public int real { get; set; }
|
||||
}
|
||||
|
|
|
@ -2,6 +2,16 @@ namespace Ombi.Api.Sonarr.Models
|
|||
{
|
||||
public class Quality
|
||||
{
|
||||
public Quality()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Quality(Quality q)
|
||||
{
|
||||
id = q.id;
|
||||
name = q.name;
|
||||
}
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
|
|
@ -29,7 +29,10 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
{
|
||||
ProviderId = "123"
|
||||
});
|
||||
var search = new SearchMovieViewModel();
|
||||
var search = new SearchMovieViewModel()
|
||||
{
|
||||
TheMovieDbId = "123",
|
||||
};
|
||||
var result = await Rule.Execute(search);
|
||||
|
||||
Assert.True(result.Success);
|
||||
|
|
|
@ -15,13 +15,13 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
Task<RequestEngineResult> DenyChildRequest(int requestId);
|
||||
Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type);
|
||||
Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
|
||||
Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search);
|
||||
Task<TvRequests> UpdateTvRequest(TvRequests request);
|
||||
Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position);
|
||||
Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId);
|
||||
Task<ChildRequests> UpdateChildRequest(ChildRequests request);
|
||||
Task RemoveTvChild(int requestId);
|
||||
Task<RequestEngineResult> ApproveChildRequest(int id);
|
||||
Task<IEnumerable<TvRequests>> GetRequestsLite();
|
||||
Task UpdateQualityProfile(int requestId, int profileId);
|
||||
Task UpdateRootPath(int requestId, int rootPath);
|
||||
}
|
||||
}
|
|
@ -7,16 +7,10 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
public interface ITvSearchEngine
|
||||
{
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm);
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> SearchTreeNode(string searchTerm);
|
||||
Task<TreeNode<SearchTvShowViewModel>> GetShowInformationTreeNode(int tvdbid);
|
||||
Task<SearchTvShowViewModel> GetShowInformation(int tvdbid);
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> PopularTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Popular();
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> AnticipatedTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Anticipated();
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatchesTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> MostWatches();
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> TrendingTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Trending();
|
||||
}
|
||||
}
|
|
@ -452,6 +452,7 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
|
||||
request.Available = true;
|
||||
request.MarkedAsAvailable = DateTime.Now;
|
||||
NotificationHelper.Notify(request, NotificationType.RequestAvailable);
|
||||
await MovieRepository.Update(request);
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
||||
public class TreeNode<T>
|
||||
{
|
||||
public string Label { get; set; }
|
||||
public T Data { get; set; }
|
||||
public List<TreeNode<T>> Children { get; set; }
|
||||
public bool Leaf { get; set; }
|
||||
public bool Expanded { get; set; }
|
||||
}
|
||||
|
||||
public class TreeNode<T,U>
|
||||
{
|
||||
public string Label { get; set; }
|
||||
public T Data { get; set; }
|
||||
public List<TreeNode<U>> Children { get; set; }
|
||||
public bool Leaf { get; set; }
|
||||
public bool Expanded { get; set; }
|
||||
}
|
||||
}
|
|
@ -171,24 +171,30 @@ namespace Ombi.Core.Engine
|
|||
public async Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<TvRequests> allRequests;
|
||||
List<TvRequests> allRequests = null;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = await TvRepository.GetLite(shouldHide.UserId)
|
||||
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
var tv = TvRepository.GetLite(shouldHide.UserId);
|
||||
if (tv.Any() && tv.Select(x => x.ChildRequests).Any())
|
||||
{
|
||||
allRequests = await tv.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)).Skip(position).Take(count).ToListAsync();
|
||||
}
|
||||
|
||||
// Filter out children
|
||||
|
||||
FilterChildren(allRequests, shouldHide);
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests = await TvRepository.GetLite()
|
||||
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
var tv = TvRepository.GetLite();
|
||||
if (tv.Any() && tv.Select(x => x.ChildRequests).Any())
|
||||
{
|
||||
allRequests = await tv.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)).Skip(position).Take(count).ToListAsync();
|
||||
}
|
||||
}
|
||||
if (allRequests == null)
|
||||
{
|
||||
return new RequestsViewModel<TvRequests>();
|
||||
}
|
||||
|
||||
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
|
||||
return new RequestsViewModel<TvRequests>
|
||||
|
@ -196,38 +202,6 @@ namespace Ombi.Core.Engine
|
|||
Collection = allRequests
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<TvRequests> allRequests;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = await TvRepository.Get(shouldHide.UserId)
|
||||
.Include(x => x.ChildRequests)
|
||||
.ThenInclude(x => x.SeasonRequests)
|
||||
.ThenInclude(x => x.Episodes)
|
||||
.Where(x => x.ChildRequests.Any())
|
||||
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
|
||||
FilterChildren(allRequests, shouldHide);
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests = await TvRepository.Get()
|
||||
.Include(x => x.ChildRequests)
|
||||
.ThenInclude(x => x.SeasonRequests)
|
||||
.ThenInclude(x => x.Episodes)
|
||||
.Where(x => x.ChildRequests.Any())
|
||||
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
}
|
||||
|
||||
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
return ParseIntoTreeNode(allRequests);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TvRequests>> GetRequests()
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
|
@ -288,6 +262,10 @@ namespace Ombi.Core.Engine
|
|||
|
||||
private static void FilterChildren(IEnumerable<TvRequests> allRequests, HideResult shouldHide)
|
||||
{
|
||||
if (allRequests == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Filter out children
|
||||
foreach (var t in allRequests)
|
||||
{
|
||||
|
@ -350,21 +328,22 @@ namespace Ombi.Core.Engine
|
|||
return results;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search)
|
||||
public async Task UpdateRootPath(int requestId, int rootPath)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
IQueryable<TvRequests> allRequests;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = TvRepository.Get(shouldHide.UserId);
|
||||
var allRequests = TvRepository.Get();
|
||||
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
results.RootFolder = rootPath;
|
||||
|
||||
await TvRepository.Update(results);
|
||||
}
|
||||
else
|
||||
|
||||
public async Task UpdateQualityProfile(int requestId, int profileId)
|
||||
{
|
||||
allRequests = TvRepository.Get();
|
||||
}
|
||||
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
||||
results.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
return ParseIntoTreeNode(results);
|
||||
var allRequests = TvRepository.Get();
|
||||
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
results.QualityOverride = profileId;
|
||||
|
||||
await TvRepository.Update(results);
|
||||
}
|
||||
|
||||
public async Task<TvRequests> UpdateTvRequest(TvRequests request)
|
||||
|
@ -516,6 +495,7 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
request.Available = true;
|
||||
request.MarkedAsAvailable = DateTime.Now;
|
||||
foreach (var season in request.SeasonRequests)
|
||||
{
|
||||
foreach (var e in season.Episodes)
|
||||
|
@ -585,28 +565,6 @@ namespace Ombi.Core.Engine
|
|||
return await AfterRequest(model.ChildRequests.FirstOrDefault());
|
||||
}
|
||||
|
||||
private static List<TreeNode<TvRequests, List<ChildRequests>>> ParseIntoTreeNode(IEnumerable<TvRequests> result)
|
||||
{
|
||||
var node = new List<TreeNode<TvRequests, List<ChildRequests>>>();
|
||||
|
||||
foreach (var value in result)
|
||||
{
|
||||
node.Add(new TreeNode<TvRequests, List<ChildRequests>>
|
||||
{
|
||||
Data = value,
|
||||
Children = new List<TreeNode<List<ChildRequests>>>
|
||||
{
|
||||
new TreeNode<List<ChildRequests>>
|
||||
{
|
||||
Data = SortEpisodes(value.ChildRequests),
|
||||
Leaf = true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private static List<ChildRequests> SortEpisodes(List<ChildRequests> items)
|
||||
{
|
||||
foreach (var value in items)
|
||||
|
|
|
@ -59,11 +59,6 @@ namespace Ombi.Core.Engine
|
|||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> SearchTreeNode(string searchTerm)
|
||||
{
|
||||
var result = await Search(searchTerm);
|
||||
return result.Select(ParseIntoTreeNode).ToList();
|
||||
}
|
||||
public async Task<SearchTvShowViewModel> GetShowInformation(int tvdbid)
|
||||
{
|
||||
var show = await TvMazeApi.ShowLookupByTheTvDbId(tvdbid);
|
||||
|
@ -116,19 +111,6 @@ namespace Ombi.Core.Engine
|
|||
return await ProcessResult(mapped);
|
||||
}
|
||||
|
||||
public async Task<TreeNode<SearchTvShowViewModel>> GetShowInformationTreeNode(int tvdbid)
|
||||
{
|
||||
var result = await GetShowInformation(tvdbid);
|
||||
return ParseIntoTreeNode(result);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> PopularTree()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
return processed.Select(ParseIntoTreeNode).ToList();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> Popular()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
|
||||
|
@ -136,12 +118,6 @@ namespace Ombi.Core.Engine
|
|||
return processed;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> AnticipatedTree()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
return processed.Select(ParseIntoTreeNode).ToList();
|
||||
}
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> Anticipated()
|
||||
{
|
||||
|
||||
|
@ -150,12 +126,6 @@ namespace Ombi.Core.Engine
|
|||
return processed;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatchesTree()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
return processed.Select(ParseIntoTreeNode).ToList();
|
||||
}
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> MostWatches()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
|
||||
|
@ -163,13 +133,6 @@ namespace Ombi.Core.Engine
|
|||
return processed;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> TrendingTree()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
return processed.Select(ParseIntoTreeNode).ToList();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> Trending()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
|
||||
|
@ -177,22 +140,6 @@ namespace Ombi.Core.Engine
|
|||
return processed;
|
||||
}
|
||||
|
||||
private static TreeNode<SearchTvShowViewModel> ParseIntoTreeNode(SearchTvShowViewModel result)
|
||||
{
|
||||
return new TreeNode<SearchTvShowViewModel>
|
||||
{
|
||||
Data = result,
|
||||
Children = new List<TreeNode<SearchTvShowViewModel>>
|
||||
{
|
||||
new TreeNode<SearchTvShowViewModel>
|
||||
{
|
||||
Data = result, Leaf = true
|
||||
}
|
||||
},
|
||||
Leaf = false
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<SearchTvShowViewModel>> ProcessResults<T>(IEnumerable<T> items)
|
||||
{
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
|
|
98
src/Ombi.Core/Engine/UserStatsEngine.cs
Normal file
98
src/Ombi.Core/Engine/UserStatsEngine.cs
Normal file
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class UserStatsEngine
|
||||
{
|
||||
public UserStatsEngine(OmbiUserManager um, IMovieRequestRepository movieRequest, ITvRequestRepository tvRequest)
|
||||
{
|
||||
_userManager = um;
|
||||
_movieRequest = movieRequest;
|
||||
_tvRequest = tvRequest;
|
||||
}
|
||||
|
||||
private readonly OmbiUserManager _userManager;
|
||||
private readonly IMovieRequestRepository _movieRequest;
|
||||
private readonly ITvRequestRepository _tvRequest;
|
||||
|
||||
public async Task<UserStatsSummary> GetSummary(SummaryRequest request)
|
||||
{
|
||||
/* What do we want?
|
||||
|
||||
This is Per week/month/all time (filter by date)
|
||||
|
||||
1. Total Requests
|
||||
2. Total Movie Requests
|
||||
3. Total Tv Requests
|
||||
4. Total Issues (If enabled)
|
||||
5. Total Requests fufilled (now available)
|
||||
|
||||
Then
|
||||
|
||||
2. Most requested user Movie
|
||||
3. Most requested user tv
|
||||
|
||||
Then
|
||||
|
||||
1.
|
||||
|
||||
*/
|
||||
|
||||
// get all movie requests
|
||||
var movies = _movieRequest.GetWithUser();
|
||||
var filteredMovies = movies.Where(x => x.RequestedDate >= request.From && x.RequestedDate <= request.To);
|
||||
var tv = _tvRequest.GetLite();
|
||||
var children = tv.SelectMany(x =>
|
||||
x.ChildRequests.Where(c => c.RequestedDate >= request.From && c.RequestedDate <= request.To));
|
||||
|
||||
var moviesCount = filteredMovies.CountAsync();
|
||||
var childrenCount = children.CountAsync();
|
||||
var availableMovies =
|
||||
movies.Select(x => x.MarkedAsAvailable >= request.From && x.MarkedAsAvailable <= request.To).CountAsync();
|
||||
var availableChildren = tv.SelectMany(x =>
|
||||
x.ChildRequests.Where(c => c.MarkedAsAvailable >= request.From && c.MarkedAsAvailable <= request.To)).CountAsync();
|
||||
|
||||
var userMovie = filteredMovies.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync();
|
||||
var userTv = children.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync();
|
||||
|
||||
|
||||
return new UserStatsSummary
|
||||
{
|
||||
TotalMovieRequests = await moviesCount,
|
||||
TotalTvRequests = await childrenCount,
|
||||
CompletedRequestsTv = await availableChildren,
|
||||
CompletedRequestsMovies = await availableMovies,
|
||||
MostRequestedUserMovie = (await userMovie).FirstOrDefault().RequestedUser,
|
||||
MostRequestedUserTv = (await userTv).FirstOrDefault().RequestedUser,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class SummaryRequest
|
||||
{
|
||||
public DateTime From { get; set; }
|
||||
public DateTime To { get; set; }
|
||||
}
|
||||
|
||||
public class UserStatsSummary
|
||||
{
|
||||
public int TotalRequests => TotalTvRequests + TotalTvRequests;
|
||||
public int TotalMovieRequests { get; set; }
|
||||
public int TotalTvRequests { get; set; }
|
||||
public int TotalIssues { get; set; }
|
||||
public int CompletedRequestsMovies { get; set; }
|
||||
public int CompletedRequestsTv { get; set; }
|
||||
public int CompletedRequests => CompletedRequestsMovies + CompletedRequestsTv;
|
||||
public OmbiUser MostRequestedUserMovie { get; set; }
|
||||
public OmbiUser MostRequestedUserTv { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -165,25 +165,15 @@ namespace Ombi.Core.Senders
|
|||
titleSlug = model.ParentRequest.Title,
|
||||
addOptions = new AddOptions
|
||||
{
|
||||
ignoreEpisodesWithFiles = true, // There shouldn't be any episodes with files, this is a new season
|
||||
ignoreEpisodesWithoutFiles = true, // We want all missing
|
||||
ignoreEpisodesWithFiles = false, // There shouldn't be any episodes with files, this is a new season
|
||||
ignoreEpisodesWithoutFiles = false, // We want all missing
|
||||
searchForMissingEpisodes = false // we want dont want to search yet. We want to make sure everything is unmonitored/monitored correctly.
|
||||
}
|
||||
};
|
||||
|
||||
// Montitor the correct seasons,
|
||||
// If we have that season in the model then it's monitored!
|
||||
var seasonsToAdd = new List<Season>();
|
||||
for (var i = 0; i < model.ParentRequest.TotalSeasons + 1; i++)
|
||||
{
|
||||
var index = i;
|
||||
var season = new Season
|
||||
{
|
||||
seasonNumber = i,
|
||||
monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0)
|
||||
};
|
||||
seasonsToAdd.Add(season);
|
||||
}
|
||||
var seasonsToAdd = GetSeasonsToCreate(model);
|
||||
newSeries.seasons = seasonsToAdd;
|
||||
var result = await SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri);
|
||||
existingSeries = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri);
|
||||
|
@ -237,7 +227,7 @@ namespace Ombi.Core.Senders
|
|||
{
|
||||
var sonarrEp = sonarrEpList.FirstOrDefault(x =>
|
||||
x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber);
|
||||
if (sonarrEp != null)
|
||||
if (sonarrEp != null && !sonarrEp.monitored)
|
||||
{
|
||||
sonarrEp.monitored = true;
|
||||
episodesToUpdate.Add(sonarrEp);
|
||||
|
@ -245,27 +235,64 @@ namespace Ombi.Core.Senders
|
|||
}
|
||||
}
|
||||
var seriesChanges = false;
|
||||
|
||||
foreach (var season in model.SeasonRequests)
|
||||
{
|
||||
var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber);
|
||||
var sonarrEpCount = sonarrSeason.Count();
|
||||
var ourRequestCount = season.Episodes.Count;
|
||||
|
||||
if (sonarrEpCount == ourRequestCount)
|
||||
{
|
||||
// We have the same amount of requests as all of the episodes in the season.
|
||||
var existingSeason =
|
||||
result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
|
||||
if (existingSeason == null)
|
||||
{
|
||||
Logger.LogError("The sonarr ep count was the same as out request count, but could not match the season number {0}", season.SeasonNumber);
|
||||
Logger.LogError("There was no season numer {0} in Sonarr for title {1}", season.SeasonNumber, model.ParentRequest.Title);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (sonarrEpCount == ourRequestCount)
|
||||
{
|
||||
// We have the same amount of requests as all of the episodes in the season.
|
||||
|
||||
if (!existingSeason.monitored)
|
||||
{
|
||||
existingSeason.monitored = true;
|
||||
seriesChanges = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure this season is set to monitored
|
||||
if (!existingSeason.monitored)
|
||||
{
|
||||
// We need to monitor it, problem being is all episodes will now be monitored
|
||||
// So we need to monior the series but unmonitor every episode
|
||||
// Except the episodes that are already monitored before we update the series (we do not want to unmonitor episodes that are monitored beforehand)
|
||||
existingSeason.monitored = true;
|
||||
var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber);
|
||||
sea.monitored = true;
|
||||
//var previouslyMonitoredEpisodes = sonarrEpList.Where(x =>
|
||||
// x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this
|
||||
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
|
||||
var epToUnmonitor = new List<Episode>();
|
||||
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the orignal member
|
||||
foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList())
|
||||
{
|
||||
//if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber))
|
||||
//{
|
||||
// // This was previously monitored.
|
||||
// continue;
|
||||
//}
|
||||
ep.monitored = false;
|
||||
epToUnmonitor.Add(ep);
|
||||
}
|
||||
|
||||
foreach (var epToUpdate in epToUnmonitor)
|
||||
{
|
||||
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
||||
}
|
||||
}
|
||||
// Now update the episodes that need updating
|
||||
foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber))
|
||||
{
|
||||
|
@ -285,6 +312,24 @@ namespace Ombi.Core.Senders
|
|||
}
|
||||
}
|
||||
|
||||
private static List<Season> GetSeasonsToCreate(ChildRequests model)
|
||||
{
|
||||
// Let's get a list of seasons just incase we need to change it
|
||||
var seasonsToUpdate = new List<Season>();
|
||||
for (var i = 0; i < model.ParentRequest.TotalSeasons + 1; i++)
|
||||
{
|
||||
var index = i;
|
||||
var sea = new Season
|
||||
{
|
||||
seasonNumber = i,
|
||||
monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0)
|
||||
};
|
||||
seasonsToUpdate.Add(sea);
|
||||
}
|
||||
|
||||
return seasonsToUpdate;
|
||||
}
|
||||
|
||||
private async Task<bool> SendToSickRage(ChildRequests model, SickRageSettings settings, string qualityId = null)
|
||||
{
|
||||
var tvdbid = model.ParentRequest.TvDbId;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Humanizer;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
|
@ -39,7 +40,7 @@ namespace Ombi.Notifications
|
|||
RequestedDate = req?.RequestedDate.ToString("D");
|
||||
if (Type.IsNullOrEmpty())
|
||||
{
|
||||
Type = req?.RequestType.ToString();
|
||||
Type = req?.RequestType.Humanize();
|
||||
}
|
||||
Overview = req?.Overview;
|
||||
Year = req?.ReleaseDate.Year.ToString();
|
||||
|
@ -91,7 +92,7 @@ namespace Ombi.Notifications
|
|||
RequestedDate = req?.RequestedDate.ToString("D");
|
||||
if (Type.IsNullOrEmpty())
|
||||
{
|
||||
Type = req?.RequestType.ToString();
|
||||
Type = req?.RequestType.Humanize();
|
||||
}
|
||||
|
||||
Overview = req?.ParentRequest.Overview;
|
||||
|
@ -161,7 +162,7 @@ namespace Ombi.Notifications
|
|||
IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty;
|
||||
NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty;
|
||||
UserName = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty;
|
||||
Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val : string.Empty;
|
||||
Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val.Humanize() : string.Empty;
|
||||
}
|
||||
|
||||
// User Defined
|
||||
|
|
|
@ -89,6 +89,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
_log.LogInformation("We have found the request {0} on Emby, sending the notification", movie?.Title ?? string.Empty);
|
||||
|
||||
movie.Available = true;
|
||||
movie.MarkedAsAvailable = DateTime.Now;
|
||||
if (movie.Available)
|
||||
{
|
||||
var recipient = movie.RequestedUser.Email.HasValue() ? movie.RequestedUser.Email : string.Empty;
|
||||
|
@ -185,6 +186,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
{
|
||||
// We have fulfulled this request!
|
||||
child.Available = true;
|
||||
child.MarkedAsAvailable = DateTime.Now;
|
||||
BackgroundJob.Enqueue(() => _notificationService.Publish(new NotificationOptions
|
||||
{
|
||||
DateTime = DateTime.Now,
|
||||
|
|
|
@ -123,6 +123,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
{
|
||||
// We have fulfulled this request!
|
||||
child.Available = true;
|
||||
child.MarkedAsAvailable = DateTime.Now;
|
||||
_backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions
|
||||
{
|
||||
DateTime = DateTime.Now,
|
||||
|
@ -163,6 +164,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
}
|
||||
|
||||
movie.Available = true;
|
||||
movie.MarkedAsAvailable = DateTime.Now;
|
||||
if (movie.Available)
|
||||
{
|
||||
_backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions
|
||||
|
|
|
@ -8,10 +8,13 @@ namespace Ombi.Store.Entities.Requests
|
|||
{
|
||||
public string Title { get; set; }
|
||||
public bool Approved { get; set; }
|
||||
public DateTime MarkedAsApproved { get; set; }
|
||||
public DateTime RequestedDate { get; set; }
|
||||
public bool Available { get; set; }
|
||||
public DateTime? MarkedAsAvailable { get; set; }
|
||||
public string RequestedUserId { get; set; }
|
||||
public bool? Denied { get; set; }
|
||||
public DateTime MarkedAsDenied { get; set; }
|
||||
public string DeniedReason { get; set; }
|
||||
public RequestType RequestType { get; set; }
|
||||
|
||||
|
|
988
src/Ombi.Store/Migrations/20180730085903_UserStats.Designer.cs
generated
Normal file
988
src/Ombi.Store/Migrations/20180730085903_UserStats.Designer.cs
generated
Normal file
|
@ -0,0 +1,988 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Ombi.Store.Context;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
[DbContext(typeof(OmbiContext))]
|
||||
[Migration("20180730085903_UserStats")]
|
||||
partial class UserStats
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("ProviderKey");
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ApplicationConfiguration");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Audit", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AuditArea");
|
||||
|
||||
b.Property<int>("AuditType");
|
||||
|
||||
b.Property<DateTime>("DateTime");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<string>("User");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Audit");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CouchPotatoCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
|
||||
b.Property<string>("TheMovieDbId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("TvDbId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<string>("Url");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("EmbyContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<string>("EmbyId");
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<string>("ParentId");
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.Property<string>("TheMovieDbId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Content");
|
||||
|
||||
b.Property<string>("SettingsName");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("GlobalSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("Agent");
|
||||
|
||||
b.Property<bool>("Enabled");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<int>("NotificationType");
|
||||
|
||||
b.Property<string>("Subject");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("NotificationTemplates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<string>("PlayerId");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("NotificationUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("Alias");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<string>("EmbyConnectUserId");
|
||||
|
||||
b.Property<int?>("EpisodeRequestLimit");
|
||||
|
||||
b.Property<DateTime?>("LastLoggedIn");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<int?>("MovieRequestLimit");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("ProviderUserId");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserAccessToken");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<int>("UserType");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<int>("GrandparentKey");
|
||||
|
||||
b.Property<int>("Key");
|
||||
|
||||
b.Property<int>("ParentKey");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GrandparentKey");
|
||||
|
||||
b.ToTable("PlexEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("ParentKey");
|
||||
|
||||
b.Property<int>("PlexContentId");
|
||||
|
||||
b.Property<int?>("PlexServerContentId");
|
||||
|
||||
b.Property<int>("SeasonKey");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexServerContentId");
|
||||
|
||||
b.ToTable("PlexSeasonsContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<int>("Key");
|
||||
|
||||
b.Property<string>("Quality");
|
||||
|
||||
b.Property<string>("ReleaseYear");
|
||||
|
||||
b.Property<string>("TheMovieDbId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("TvDbId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<string>("Url");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexServerContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("HasFile");
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RadarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<int>("ContentId");
|
||||
|
||||
b.Property<int>("ContentType");
|
||||
|
||||
b.Property<int?>("EpisodeNumber");
|
||||
|
||||
b.Property<int?>("SeasonNumber");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RecentlyAddedLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Approved");
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<bool?>("Denied");
|
||||
|
||||
b.Property<string>("DeniedReason");
|
||||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<DateTime>("MarkedAsApproved");
|
||||
|
||||
b.Property<DateTime?>("MarkedAsAvailable");
|
||||
|
||||
b.Property<DateTime>("MarkedAsDenied");
|
||||
|
||||
b.Property<int>("ParentRequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
||||
b.Property<int>("SeriesType");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentRequestId");
|
||||
|
||||
b.HasIndex("RequestedUserId");
|
||||
|
||||
b.ToTable("ChildRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("IssueCategory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Comment");
|
||||
|
||||
b.Property<DateTime>("Date");
|
||||
|
||||
b.Property<int?>("IssuesId");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IssuesId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("IssueComments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<int>("IssueCategoryId");
|
||||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
|
||||
b.Property<int?>("RequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<DateTime?>("ResovledDate");
|
||||
|
||||
b.Property<int>("Status");
|
||||
|
||||
b.Property<string>("Subject");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("UserReportedId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IssueCategoryId");
|
||||
|
||||
b.HasIndex("IssueId");
|
||||
|
||||
b.HasIndex("UserReportedId");
|
||||
|
||||
b.ToTable("Issues");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Approved");
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<string>("Background");
|
||||
|
||||
b.Property<bool?>("Denied");
|
||||
|
||||
b.Property<string>("DeniedReason");
|
||||
|
||||
b.Property<DateTime?>("DigitalReleaseDate");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<DateTime>("MarkedAsApproved");
|
||||
|
||||
b.Property<DateTime?>("MarkedAsAvailable");
|
||||
|
||||
b.Property<DateTime>("MarkedAsDenied");
|
||||
|
||||
b.Property<string>("Overview");
|
||||
|
||||
b.Property<string>("PosterPath");
|
||||
|
||||
b.Property<int>("QualityOverride");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
||||
b.Property<int>("RootPathOverride");
|
||||
|
||||
b.Property<string>("Status");
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RequestedUserId");
|
||||
|
||||
b.ToTable("MovieRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("EpisodeCount");
|
||||
|
||||
b.Property<DateTime>("RequestDate");
|
||||
|
||||
b.Property<int>("RequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("RequestLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Background");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<string>("Overview");
|
||||
|
||||
b.Property<string>("PosterPath");
|
||||
|
||||
b.Property<int?>("QualityOverride");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate");
|
||||
|
||||
b.Property<int?>("RootFolder");
|
||||
|
||||
b.Property<string>("Status");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TvRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("RequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("RequestSubscription");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<bool>("HasFile");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Token");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Tokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AirDate");
|
||||
|
||||
b.Property<bool>("Approved");
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<bool>("Requested");
|
||||
|
||||
b.Property<int>("SeasonId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("Url");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SeasonId");
|
||||
|
||||
b.ToTable("EpisodeRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("ChildRequestId");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ChildRequestId");
|
||||
|
||||
b.ToTable("SeasonRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany("NotificationUserIds")
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("GrandparentKey")
|
||||
.HasPrincipalKey("Key")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent")
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexServerContentId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest")
|
||||
.WithMany("ChildRequests")
|
||||
.HasForeignKey("ParentRequestId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("RequestedUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues")
|
||||
.WithMany("Comments")
|
||||
.HasForeignKey("IssuesId");
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("IssueCategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests")
|
||||
.WithMany("Issues")
|
||||
.HasForeignKey("IssueId");
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.Requests.MovieRequests")
|
||||
.WithMany("Issues")
|
||||
.HasForeignKey("IssueId");
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserReportedId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("RequestedUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("SeasonId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest")
|
||||
.WithMany("SeasonRequests")
|
||||
.HasForeignKey("ChildRequestId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
72
src/Ombi.Store/Migrations/20180730085903_UserStats.cs
Normal file
72
src/Ombi.Store/Migrations/20180730085903_UserStats.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
public partial class UserStats : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsApproved",
|
||||
table: "MovieRequests",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsAvailable",
|
||||
table: "MovieRequests",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsDenied",
|
||||
table: "MovieRequests",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsApproved",
|
||||
table: "ChildRequests",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsAvailable",
|
||||
table: "ChildRequests",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsDenied",
|
||||
table: "ChildRequests",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsApproved",
|
||||
table: "MovieRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsAvailable",
|
||||
table: "MovieRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsDenied",
|
||||
table: "MovieRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsApproved",
|
||||
table: "ChildRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsAvailable",
|
||||
table: "ChildRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsDenied",
|
||||
table: "ChildRequests");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,9 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
||||
using Ombi.Helpers;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using System;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
|
@ -20,7 +14,7 @@ namespace Ombi.Store.Migrations
|
|||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.0.3-rtm-10026");
|
||||
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
|
@ -481,6 +475,12 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<DateTime>("MarkedAsApproved");
|
||||
|
||||
b.Property<DateTime?>("MarkedAsAvailable");
|
||||
|
||||
b.Property<DateTime>("MarkedAsDenied");
|
||||
|
||||
b.Property<int>("ParentRequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
@ -595,6 +595,12 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<DateTime>("MarkedAsApproved");
|
||||
|
||||
b.Property<DateTime?>("MarkedAsAvailable");
|
||||
|
||||
b.Property<DateTime>("MarkedAsDenied");
|
||||
|
||||
b.Property<string>("Overview");
|
||||
|
||||
b.Property<string>("PosterPath");
|
||||
|
|
23
src/Ombi/.gitignore
vendored
23
src/Ombi/.gitignore
vendored
|
@ -1,23 +1,10 @@
|
|||
/wwwroot/css/**
|
||||
/wwwroot/fonts/**
|
||||
/wwwroot/lib/**
|
||||
/wwwroot/maps/**
|
||||
/wwwroot/dist/**
|
||||
/wwwroot/*.js.map
|
||||
/wwwroot/*.js
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/bower_components
|
||||
|
||||
# misc
|
||||
node_modules
|
||||
bin
|
||||
obj
|
||||
wwwroot/dist
|
||||
*.log
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage/*
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
testem.log
|
||||
#/typings
|
||||
/systemjs.config.js*
|
||||
/Logs/**
|
||||
**.db
|
||||
|
|
15
src/Ombi/.vscode/tasks.json
vendored
15
src/Ombi/.vscode/tasks.json
vendored
|
@ -4,7 +4,7 @@
|
|||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "restore",
|
||||
"label": "restore",
|
||||
"command": "npm",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
|
@ -14,7 +14,16 @@
|
|||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"taskName": "build",
|
||||
"label": "clean",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
"clean"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
|
@ -27,7 +36,7 @@
|
|||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"taskName": "lint",
|
||||
"label": "lint",
|
||||
"type": "shell",
|
||||
"command": "npm",
|
||||
"args": [
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { animate, style, transition, trigger } from "@angular/animations";
|
||||
import { AnimationEntryMetadata } from "@angular/core";
|
||||
import { AnimationTriggerMetadata } from "@angular/animations";
|
||||
|
||||
export const fadeInOutAnimation: AnimationEntryMetadata = trigger("fadeInOut", [
|
||||
export const fadeInOutAnimation: AnimationTriggerMetadata = trigger("fadeInOut", [
|
||||
transition(":enter", [ // :enter is alias to 'void => *'
|
||||
style({ opacity: 0 }),
|
||||
animate(1000, style({ opacity: 1 })),
|
||||
|
|
|
@ -134,6 +134,12 @@
|
|||
<li [ngClass]="{'active': 'no' === translate.currentLang}">
|
||||
<a (click)="translate.use('no')" [translate]="'NavigationBar.Language.Norwegian'"></a>
|
||||
</li>
|
||||
<li [ngClass]="{'active': 'pt' === translate.currentLang}">
|
||||
<a (click)="translate.use('pt')" [translate]="'NavigationBar.Language.BrazillianPortuguese'"></a>
|
||||
</li>
|
||||
<li [ngClass]="{'active': 'pl' === translate.currentLang}">
|
||||
<a (click)="translate.use('pl')" [translate]="'NavigationBar.Language.Polish'"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -40,13 +40,13 @@ export class AppComponent implements OnInit {
|
|||
__webpack_public_path__ = base + "/dist/";
|
||||
}
|
||||
|
||||
this.translate.addLangs(["en", "de", "fr","da","es","it","nl","sv","no"]);
|
||||
this.translate.addLangs(["en", "de", "fr", "da", "es", "it", "nl", "sv", "no", "pl", "pt"]);
|
||||
// this language will be used as a fallback when a translation isn't found in the current language
|
||||
this.translate.setDefaultLang("en");
|
||||
|
||||
// See if we can match the supported langs with the current browser lang
|
||||
const browserLang: string = translate.getBrowserLang();
|
||||
this.translate.use(browserLang.match(/en|fr|da|de|es|it|nl|sv|no/) ? browserLang : "en");
|
||||
this.translate.use(browserLang.match(/en|fr|da|de|es|it|nl|sv|no|pl|pt/) ? browserLang : "en");
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
|
@ -88,8 +88,8 @@ export class AppComponent implements OnInit {
|
|||
|
||||
public openMobileApp(event: any) {
|
||||
event.preventDefault();
|
||||
if(!this.customizationSettings.applicationUrl) {
|
||||
this.notificationService.warning("Mobile","Please ask your admin to setup the Application URL!");
|
||||
if (!this.customizationSettings.applicationUrl) {
|
||||
this.notificationService.warning("Mobile", "Please ask your admin to setup the Application URL!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import {CommonModule, PlatformLocation} from "@angular/common";
|
||||
import {HttpClient, HttpClientModule} from "@angular/common/http";
|
||||
import {NgModule} from "@angular/core";
|
||||
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||
import {HttpModule} from "@angular/http";
|
||||
import {MatButtonModule, MatCardModule, MatInputModule, MatTabsModule} from "@angular/material";
|
||||
import {BrowserModule} from "@angular/platform-browser";
|
||||
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
|
||||
import {RouterModule, Routes} from "@angular/router";
|
||||
import { CommonModule, PlatformLocation } from "@angular/common";
|
||||
import { HttpClient, HttpClientModule } from "@angular/common/http";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { HttpModule } from "@angular/http";
|
||||
import { MatButtonModule, MatCardModule, MatInputModule, MatTabsModule } from "@angular/material";
|
||||
import { BrowserModule } from "@angular/platform-browser";
|
||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { JwtModule } from "@auth0/angular-jwt";
|
||||
|
||||
|
@ -15,7 +15,7 @@ import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
|||
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
|
||||
import { CookieService } from "ng2-cookies";
|
||||
import { GrowlModule } from "primeng/components/growl/growl";
|
||||
import { ButtonModule, CaptchaModule, ConfirmationService, ConfirmDialogModule, DataTableModule,DialogModule, SharedModule, SidebarModule, TooltipModule } from "primeng/primeng";
|
||||
import { ButtonModule, CaptchaModule, ConfirmationService, ConfirmDialogModule, DataTableModule, DialogModule, SharedModule, SidebarModule, TooltipModule } from "primeng/primeng";
|
||||
|
||||
// Components
|
||||
import { AppComponent } from "./app.component";
|
||||
|
@ -67,6 +67,14 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
|
|||
return new TranslateHttpLoader(http, "/translations/", `.json?v=${version}`);
|
||||
}
|
||||
|
||||
export function JwtTokenGetter() {
|
||||
const token = localStorage.getItem("id_token");
|
||||
if (!token) {
|
||||
return "";
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot(routes),
|
||||
|
@ -92,13 +100,7 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
|
|||
CommonModule,
|
||||
JwtModule.forRoot({
|
||||
config: {
|
||||
tokenGetter: () => {
|
||||
const token = localStorage.getItem("id_token");
|
||||
if (!token) {
|
||||
return "";
|
||||
}
|
||||
return token;
|
||||
},
|
||||
tokenGetter: JwtTokenGetter,
|
||||
},
|
||||
}),
|
||||
TranslateModule.forRoot({
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { JwtHelperService } from "@auth0/angular-jwt";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../services";
|
||||
import { ILocalUser, IUserLogin } from "./IUserLogin";
|
||||
|
@ -53,9 +53,9 @@ export class AuthService extends ServiceHelpers {
|
|||
} else {
|
||||
u.roles.push(roles);
|
||||
}
|
||||
return <ILocalUser>u;
|
||||
return <ILocalUser> u;
|
||||
}
|
||||
return <ILocalUser>{};
|
||||
return <ILocalUser> { };
|
||||
}
|
||||
|
||||
public hasRole(role: string): boolean {
|
||||
|
|
|
@ -11,7 +11,7 @@ export class CookieComponent implements OnInit {
|
|||
|
||||
public ngOnInit() {
|
||||
const cookie = this.cookieService.getAll();
|
||||
if(cookie.Auth) {
|
||||
if (cookie.Auth) {
|
||||
const jwtVal = cookie.Auth;
|
||||
localStorage.setItem("id_token", jwtVal);
|
||||
this.router.navigate(["search"]);
|
||||
|
|
|
@ -71,6 +71,10 @@ export interface ITvRequests {
|
|||
status: string;
|
||||
childRequests: IChildRequests[];
|
||||
qualityOverride: number;
|
||||
background: any;
|
||||
totalSeasons: number;
|
||||
tvDbId: number;
|
||||
open: boolean; // THIS IS FOR THE UI
|
||||
|
||||
// For UI display
|
||||
qualityOverrideTitle: string;
|
||||
|
|
|
@ -28,8 +28,16 @@ export interface ISearchTvResult {
|
|||
available: boolean;
|
||||
plexUrl: string;
|
||||
embyUrl: string;
|
||||
quality: string;
|
||||
firstSeason: boolean;
|
||||
latestSeason: boolean;
|
||||
theTvDbId: string;
|
||||
subscribed: boolean;
|
||||
showSubscribe: boolean;
|
||||
fullyAvailable: boolean;
|
||||
partlyAvailable: boolean;
|
||||
background: any;
|
||||
open: boolean; // THIS IS FOR THE UI
|
||||
}
|
||||
|
||||
export interface ITvRequestViewModel {
|
||||
|
|
|
@ -23,6 +23,11 @@ export interface ICreateWizardUser {
|
|||
usePlexAdminAccount: boolean;
|
||||
}
|
||||
|
||||
export interface IWizardUserResult {
|
||||
result: boolean;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export enum UserType {
|
||||
LocalUser = 1,
|
||||
PlexUser = 2,
|
||||
|
|
|
@ -63,8 +63,8 @@ export class IssueDetailsComponent implements OnInit {
|
|||
issueCategoryId: x.issueCategoryId,
|
||||
subject: x.subject,
|
||||
description: x.description,
|
||||
status:x.status,
|
||||
resolvedDate:x.resolvedDate,
|
||||
status: x.status,
|
||||
resolvedDate: x.resolvedDate,
|
||||
title: x.title,
|
||||
requestType: x.requestType,
|
||||
requestId: x.requestId,
|
||||
|
@ -117,7 +117,7 @@ export class IssueDetailsComponent implements OnInit {
|
|||
|
||||
} else {
|
||||
this.imageService.getTvBackground(Number(issue.providerId)).subscribe(x => {
|
||||
if(x) {
|
||||
if (x) {
|
||||
this.backgroundPath = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(" + x + ")");
|
||||
}
|
||||
|
|
|
@ -61,5 +61,4 @@ export class IssuesComponent implements OnInit {
|
|||
this.resolvedIssues = x;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
public loginWithOmbi: boolean;
|
||||
|
||||
public get appName(): string {
|
||||
if(this.customizationSettings.applicationName) {
|
||||
if (this.customizationSettings.applicationName) {
|
||||
return this.customizationSettings.applicationName;
|
||||
} else {
|
||||
return "Ombi";
|
||||
|
@ -72,7 +72,7 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
}
|
||||
});
|
||||
|
||||
if(authService.loggedIn()) {
|
||||
if (authService.loggedIn()) {
|
||||
this.router.navigate(["search"]);
|
||||
}
|
||||
}
|
||||
|
@ -103,9 +103,9 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
return;
|
||||
}
|
||||
const value = form.value;
|
||||
const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false, plexTvPin: { id: 0, code: ""} };
|
||||
const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false, plexTvPin: { id: 0, code: "" } };
|
||||
this.authService.requiresPassword(user).subscribe(x => {
|
||||
if(x && this.authenticationSettings.allowNoPassword) {
|
||||
if (x && this.authenticationSettings.allowNoPassword) {
|
||||
// Looks like this user requires a password
|
||||
this.authenticationSettings.allowNoPassword = false;
|
||||
return;
|
||||
|
@ -125,9 +125,9 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
}
|
||||
|
||||
public oauth() {
|
||||
this.plexTv.GetPin(this.clientId, this.appName).subscribe(pin => {
|
||||
this.plexTv.GetPin(this.clientId, this.appName).subscribe((pin: any) => {
|
||||
|
||||
this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:"", plexTvPin: pin}).subscribe(x => {
|
||||
this.authService.login({ usePlexOAuth: true, password: "", rememberMe: true, username: "", plexTvPin: pin }).subscribe(x => {
|
||||
if (window.frameElement) {
|
||||
// in frame
|
||||
window.open(x.url, "_blank");
|
||||
|
|
|
@ -16,7 +16,6 @@ export class LoginOAuthComponent implements OnInit {
|
|||
this.route.params
|
||||
.subscribe((params: any) => {
|
||||
this.pin = params.pin;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -26,7 +25,7 @@ export class LoginOAuthComponent implements OnInit {
|
|||
|
||||
public auth() {
|
||||
this.authService.oAuth(this.pin).subscribe(x => {
|
||||
if(x.access_token) {
|
||||
if (x.access_token) {
|
||||
localStorage.setItem("id_token", x.access_token);
|
||||
|
||||
if (this.authService.loggedIn()) {
|
||||
|
@ -34,13 +33,12 @@ export class LoginOAuthComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if(x.errorMessage) {
|
||||
if (x.errorMessage) {
|
||||
this.error = x.errorMessage;
|
||||
}
|
||||
|
||||
}, err => {
|
||||
this.notify.error(err.statusText);
|
||||
|
||||
this.router.navigate(["login"]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { FormBuilder, FormGroup, Validators } from "@angular/forms";
|
|||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
|
||||
import { ICustomizationSettings } from "../interfaces";
|
||||
import { IdentityService, ImageService,NotificationService, SettingsService } from "../services";
|
||||
import { IdentityService, ImageService, NotificationService, SettingsService } from "../services";
|
||||
|
||||
@Component({
|
||||
templateUrl: "./resetpassword.component.html",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { NguCarousel } from "@ngu/carousel";
|
||||
import { NguCarouselConfig } from "@ngu/carousel";
|
||||
|
||||
import { ImageService, RecentlyAddedService } from "../services";
|
||||
import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "./../interfaces";
|
||||
|
@ -43,7 +43,7 @@ export class RecentlyAddedComponent implements OnInit {
|
|||
public groupTv: boolean = false;
|
||||
|
||||
// https://github.com/sheikalthaf/ngu-carousel
|
||||
public carouselTile: NguCarousel;
|
||||
public carouselTile: NguCarouselConfig;
|
||||
|
||||
constructor(private recentlyAddedService: RecentlyAddedService,
|
||||
private imageService: ImageService) {}
|
||||
|
@ -67,10 +67,10 @@ export class RecentlyAddedComponent implements OnInit {
|
|||
}
|
||||
|
||||
public close() {
|
||||
if(this.range.length < 2) {
|
||||
if (this.range.length < 2) {
|
||||
return;
|
||||
}
|
||||
if(!this.range[1]) {
|
||||
if (!this.range[1]) {
|
||||
// If we do not have a second date then just set it to now
|
||||
this.range[1] = new Date();
|
||||
}
|
||||
|
@ -82,13 +82,13 @@ export class RecentlyAddedComponent implements OnInit {
|
|||
}
|
||||
|
||||
private getShows() {
|
||||
if(this.groupTv) {
|
||||
if (this.groupTv) {
|
||||
this.recentlyAddedService.getRecentlyAddedTvGrouped().subscribe(x => {
|
||||
this.tv = x;
|
||||
|
||||
this.tv.forEach((t) => {
|
||||
this.imageService.getTvPoster(t.tvDbId).subscribe(p => {
|
||||
if(p) {
|
||||
if (p) {
|
||||
t.posterPath = p;
|
||||
}
|
||||
});
|
||||
|
@ -100,7 +100,7 @@ export class RecentlyAddedComponent implements OnInit {
|
|||
|
||||
this.tv.forEach((t) => {
|
||||
this.imageService.getTvPoster(t.tvDbId).subscribe(p => {
|
||||
if(p) {
|
||||
if (p) {
|
||||
t.posterPath = p;
|
||||
}
|
||||
});
|
||||
|
@ -114,11 +114,11 @@ export class RecentlyAddedComponent implements OnInit {
|
|||
this.movies = x;
|
||||
|
||||
this.movies.forEach((movie) => {
|
||||
if(movie.theMovieDbId) {
|
||||
if (movie.theMovieDbId) {
|
||||
this.imageService.getMoviePoster(movie.theMovieDbId).subscribe(p => {
|
||||
movie.posterPath = p;
|
||||
});
|
||||
} else if(movie.imdbId) {
|
||||
} else if (movie.imdbId) {
|
||||
this.imageService.getMoviePoster(movie.imdbId).subscribe(p => {
|
||||
movie.posterPath = p;
|
||||
});
|
||||
|
|
|
@ -45,8 +45,6 @@
|
|||
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
<div *ngFor="let request of movieRequests">
|
||||
<div class="row">
|
||||
<div class="myBg backdrop" [style.background-image]="request.backgroundPath"></div>
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
import { Subject } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { NotificationService, RadarrService, RequestService } from "../services";
|
||||
|
||||
import { FilterType, IFilter, IIssueCategory, IMovieRequests, IPagenator, IRadarrProfile, IRadarrRootFolder, OrderType } from "../interfaces";
|
||||
import { NotificationService, RadarrService, RequestService } from "../services";
|
||||
|
||||
@Component({
|
||||
selector: "movie-requests",
|
||||
|
@ -45,16 +42,17 @@ export class MovieRequestsComponent implements OnInit {
|
|||
private currentlyLoaded: number;
|
||||
private amountToLoad: number;
|
||||
|
||||
constructor(private requestService: RequestService,
|
||||
constructor(
|
||||
private requestService: RequestService,
|
||||
private auth: AuthService,
|
||||
private notificationService: NotificationService,
|
||||
private radarrService: RadarrService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
).subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.resetSearch();
|
||||
|
@ -102,7 +100,7 @@ export class MovieRequestsComponent implements OnInit {
|
|||
public changeAvailability(request: IMovieRequests, available: boolean) {
|
||||
request.available = available;
|
||||
|
||||
if(available) {
|
||||
if (available) {
|
||||
this.requestService.markMovieAvailable({ id: request.id }).subscribe(x => {
|
||||
if (x.result) {
|
||||
this.notificationService.success(
|
||||
|
@ -241,7 +239,7 @@ export class MovieRequestsComponent implements OnInit {
|
|||
this.requestService.getMovieRequests(amountToLoad, currentlyLoaded, this.orderType, this.filter)
|
||||
.subscribe(x => {
|
||||
this.setOverrides(x.collection);
|
||||
if(!this.movieRequests) {
|
||||
if (!this.movieRequests) {
|
||||
this.movieRequests = [];
|
||||
}
|
||||
this.movieRequests = x.collection;
|
||||
|
|
|
@ -4,7 +4,7 @@ import { IChildRequests } from "../interfaces";
|
|||
import { NotificationService, RequestService } from "../services";
|
||||
|
||||
@Component({
|
||||
selector:"tvrequests-children",
|
||||
selector: "tvrequests-children",
|
||||
templateUrl: "./tvrequest-children.component.html",
|
||||
})
|
||||
export class TvRequestChildrenComponent {
|
||||
|
@ -26,12 +26,12 @@ export class TvRequestChildrenComponent {
|
|||
|
||||
public changeAvailability(request: IChildRequests, available: boolean) {
|
||||
request.available = available;
|
||||
request.seasonRequests.forEach((season)=> {
|
||||
season.episodes.forEach((ep)=> {
|
||||
request.seasonRequests.forEach((season) => {
|
||||
season.episodes.forEach((ep) => {
|
||||
ep.available = available;
|
||||
});
|
||||
});
|
||||
if(available) {
|
||||
if (available) {
|
||||
this.requestService.markTvAvailable({ id: request.id }).subscribe(x => {
|
||||
if (x.result) {
|
||||
this.notificationService.success(
|
||||
|
|
|
@ -4,59 +4,41 @@
|
|||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<!--TODO: I believe this +1 is causing off by one error skipping loading of tv shows
|
||||
When removed and scrolling very slowly everything works as expected, however
|
||||
if you scroll really quickly then you start getting duplicates of movies
|
||||
since it's async and some subsequent results return first and then incrementer
|
||||
is increased so you see movies which had already been gotten show up...
|
||||
|
||||
Removing infinte-scroll and setting max to 1000 till we work out some sort of fix
|
||||
|
||||
-->
|
||||
<!--<div infinite-scroll
|
||||
[infiniteScrollDistance]="1"
|
||||
[infiniteScrollThrottle]="100"
|
||||
(scrolled)="loadMore()">-->
|
||||
<div>
|
||||
<p-treeTable [value]="tvRequests">
|
||||
|
||||
<p-column>
|
||||
<ng-template let-col let-node="rowData" pTemplate="header">
|
||||
Results
|
||||
</ng-template>
|
||||
<ng-template let-col let-node="rowData" pTemplate="body">
|
||||
<div>
|
||||
<div *ngFor="let node of tvRequests.collection">
|
||||
<!--This is the section that holds the parent level results set-->
|
||||
<div *ngIf="!node.leaf">
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="myBg backdrop" [style.background-image]="node?.data?.background"></div>
|
||||
<div class="myBg backdrop" [style.background-image]="node?.background"></div>
|
||||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
||||
|
||||
<div class="col-sm-2 small-padding" >
|
||||
<div class="col-sm-2 small-padding">
|
||||
|
||||
<img class="img-responsive poster" src="{{node.data.posterPath || null}}" alt="poster">
|
||||
<img class="img-responsive poster" src="{{node.posterPath || null}}" alt="poster">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5 small-padding">
|
||||
<div>
|
||||
<a href="http://www.imdb.com/title/{{node.data.imdbId}}/" target="_blank">
|
||||
<h4 class="request-title">{{node.data.title}} ({{node.data.releaseDate | date: 'yyyy'}})</h4>
|
||||
<a href="http://www.imdb.com/title/{{node.imdbId}}/" target="_blank">
|
||||
<h4 class="request-title">{{node.title}} ({{node.releaseDate | date: 'yyyy'}})</h4>
|
||||
</a>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<span>Status: </span>
|
||||
<span class="label label-success">{{node.data.status}}</span>
|
||||
<span class="label label-success">{{node.status}}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div>Release Date: {{node.data.releaseDate | date}}</div>
|
||||
<div>Release Date: {{node.releaseDate | date}}</div>
|
||||
<div *ngIf="isAdmin">
|
||||
<div *ngIf="node.data.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }}
|
||||
<span>{{node.data.qualityOverrideTitle}} </span>
|
||||
<div *ngIf="node.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }}
|
||||
<span>{{node.qualityOverrideTitle}} </span>
|
||||
</div>
|
||||
<div *ngIf="node.data.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' | translate }}
|
||||
<span>{{node.data.rootPathOverrideTitle}} </span>
|
||||
<div *ngIf="node.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' | translate }}
|
||||
<span>{{node.rootPathOverrideTitle}} </span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -64,7 +46,8 @@
|
|||
</div>
|
||||
<div class="col-sm-3 col-sm-push-3 small-padding">
|
||||
|
||||
<button style="text-align: right" class="btn btn-sm btn-success-outline" (click)="openClosestTab($event)"><i class="fa fa-plus"></i> View</button>
|
||||
<button style="text-align: right" class="btn btn-sm btn-success-outline" (click)="openClosestTab(node,$event)">
|
||||
<i class="fa fa-plus"></i> View</button>
|
||||
<div *ngIf="isAdmin">
|
||||
<!--Sonarr Root Folder-->
|
||||
<div *ngIf="sonarrRootFolders" class="btn-group btn-split" id="rootFolderBtn">
|
||||
|
@ -77,7 +60,7 @@
|
|||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ngFor="let folder of sonarrRootFolders">
|
||||
<a href="#" (click)="selectRootFolder(node.data, folder, $event)">{{folder.path}}</a>
|
||||
<a href="#" (click)="selectRootFolder(node, folder, $event)">{{folder.path}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -93,35 +76,39 @@
|
|||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ngFor="let profile of sonarrProfiles">
|
||||
<a href="#" (click)="selectQualityProfile(node.data, profile, $event)">{{profile.name}}</a>
|
||||
<a href="#" (click)="selectQualityProfile(node, profile, $event)">{{profile.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issueBtn">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<li *ngFor="let cat of issueCategories"><a [routerLink]="" (click)="reportIssue(cat, node.data)">{{cat.value}}</a></li>
|
||||
<li *ngFor="let cat of issueCategories">
|
||||
<a [routerLink]="" (click)="reportIssue(cat, node)">{{cat.value}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
|
||||
<div *ngIf="node.leaf">
|
||||
<tvrequests-children [childRequests]="node.data" [isAdmin] ="isAdmin"
|
||||
(requestDeleted)="childRequestDeleted($event)"></tvrequests-children>
|
||||
<div *ngIf="node.open">
|
||||
<tvrequests-children [childRequests]="node.childRequests" [isAdmin]="isAdmin" (requestDeleted)="childRequestDeleted($event)"></tvrequests-children>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-column>
|
||||
</p-treeTable>
|
||||
|
||||
<p-paginator [rows]="10" [totalRecords]="totalTv" (onPageChange)="paginate($event)"></p-paginator>
|
||||
</div>
|
||||
|
||||
<issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title"
|
||||
[issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId" (visibleChange)="issuesBarVisible = $event;"></issue-report>
|
||||
<issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title" [issueCategory]="issueCategorySelected"
|
||||
[id]="issueRequest?.id" [providerId]="issueProviderId" (visibleChange)="issuesBarVisible = $event;"></issue-report>
|
|
@ -1,21 +1,13 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
import { ImageService } from "./../services/image.service";
|
||||
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import { Subject } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { FilterType, IIssueCategory, IPagenator, IRequestsViewModel, ISonarrProfile, ISonarrRootFolder, ITvRequests, OrderType } from "../interfaces";
|
||||
import { NotificationService, RequestService, SonarrService } from "../services";
|
||||
|
||||
import { TreeNode } from "primeng/primeng";
|
||||
import { IIssueCategory, IPagenator, ISonarrProfile, ISonarrRootFolder, ITvRequests } from "../interfaces";
|
||||
import { ImageService } from "./../services/image.service";
|
||||
|
||||
@Component({
|
||||
selector: "tv-requests",
|
||||
|
@ -24,7 +16,7 @@ import { IIssueCategory, IPagenator, ISonarrProfile, ISonarrRootFolder, ITvRequ
|
|||
})
|
||||
export class TvRequestsComponent implements OnInit {
|
||||
|
||||
public tvRequests: TreeNode[];
|
||||
public tvRequests: IRequestsViewModel<ITvRequests>;
|
||||
public searchChanged = new Subject<string>();
|
||||
public searchText: string;
|
||||
public isAdmin: boolean;
|
||||
|
@ -46,27 +38,49 @@ export class TvRequestsComponent implements OnInit {
|
|||
private currentlyLoaded: number;
|
||||
private amountToLoad: number;
|
||||
|
||||
constructor(private requestService: RequestService,
|
||||
constructor(
|
||||
private requestService: RequestService,
|
||||
private auth: AuthService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private imageService: ImageService,
|
||||
private sonarrService: SonarrService,
|
||||
private notificationService: NotificationService,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
|
||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||
if (this.isAdmin) {
|
||||
this.sonarrService.getQualityProfilesWithoutSettings()
|
||||
.subscribe(x => this.sonarrProfiles = x);
|
||||
|
||||
this.sonarrService.getRootFoldersWithoutSettings()
|
||||
.subscribe(x => this.sonarrRootFolders = x);
|
||||
}
|
||||
}
|
||||
|
||||
public openClosestTab(node: ITvRequests,el: any) {
|
||||
el.preventDefault();
|
||||
node.open = !node.open;
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
this.amountToLoad = 10;
|
||||
this.currentlyLoaded = 10;
|
||||
this.tvRequests = {collection:[], total:0};
|
||||
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
).subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.resetSearch();
|
||||
return;
|
||||
}
|
||||
this.requestService.searchTvRequestsTree(this.searchText)
|
||||
this.requestService.searchTvRequests(this.searchText)
|
||||
.subscribe(m => {
|
||||
this.tvRequests = m;
|
||||
this.tvRequests.forEach((val) => this.loadBackdrop(val));
|
||||
this.tvRequests.forEach((val) => this.setOverride(val.data));
|
||||
this.tvRequests.collection = m;
|
||||
this.tvRequests.collection.forEach((val) => this.loadBackdrop(val));
|
||||
this.tvRequests.collection.forEach((val) => this.setOverride(val));
|
||||
});
|
||||
});
|
||||
this.defaultPoster = "../../../images/default_tv_poster.png";
|
||||
|
@ -74,50 +88,6 @@ export class TvRequestsComponent implements OnInit {
|
|||
if (base) {
|
||||
this.defaultPoster = "../../.." + base + "/images/default_tv_poster.png";
|
||||
}
|
||||
}
|
||||
|
||||
public openClosestTab(el: any) {
|
||||
const rowclass = "undefined ng-star-inserted";
|
||||
el = el.toElement || el.relatedTarget || el.target || el.srcElement;
|
||||
|
||||
if (el.nodeName === "BUTTON") {
|
||||
|
||||
const isButtonAlreadyActive = el.parentElement.querySelector(".active");
|
||||
// if a Button already has Class: .active
|
||||
if (isButtonAlreadyActive) {
|
||||
isButtonAlreadyActive.classList.remove("active");
|
||||
} else {
|
||||
el.className += " active";
|
||||
}
|
||||
}
|
||||
|
||||
while (el.className !== rowclass) {
|
||||
// Increment the loop to the parent node until we find the row we need
|
||||
el = el.parentNode;
|
||||
}
|
||||
// At this point, the while loop has stopped and `el` represents the element that has
|
||||
// the class you specified
|
||||
|
||||
// Then we loop through the children to find the caret which we want to click
|
||||
const caretright = "fa-caret-right";
|
||||
const caretdown = "fa-caret-down";
|
||||
for (const value of el.children) {
|
||||
// the caret from the ui has 2 class selectors depending on if expanded or not
|
||||
// we search for both since we want to still toggle the clicking
|
||||
if (value.className.includes(caretright) || value.className.includes(caretdown)) {
|
||||
// Then we tell JS to click the element even though we hid it from the UI
|
||||
value.click();
|
||||
//Break from loop since we no longer need to continue looking
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
this.amountToLoad = 10;
|
||||
this.currentlyLoaded = 10;
|
||||
this.tvRequests = [];
|
||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||
|
||||
this.loadInit();
|
||||
}
|
||||
|
@ -125,7 +95,7 @@ export class TvRequestsComponent implements OnInit {
|
|||
public paginate(event: IPagenator) {
|
||||
const skipAmount = event.first;
|
||||
|
||||
this.requestService.getTvRequestsTree(this.amountToLoad, skipAmount)
|
||||
this.requestService.getTvRequests(this.amountToLoad, skipAmount, OrderType.RequestedDateDesc, FilterType.None, FilterType.None)
|
||||
.subscribe(x => {
|
||||
this.tvRequests = x;
|
||||
this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad;
|
||||
|
@ -150,14 +120,14 @@ export class TvRequestsComponent implements OnInit {
|
|||
event.preventDefault();
|
||||
searchResult.rootFolder = rootFolderSelected.id;
|
||||
this.setOverride(searchResult);
|
||||
this.updateRequest(searchResult);
|
||||
this.setRootFolder(searchResult);
|
||||
}
|
||||
|
||||
public selectQualityProfile(searchResult: ITvRequests, profileSelected: ISonarrProfile, event: any) {
|
||||
event.preventDefault();
|
||||
searchResult.qualityOverride = profileSelected.id;
|
||||
this.setOverride(searchResult);
|
||||
this.updateRequest(searchResult);
|
||||
this.setQualityProfile(searchResult);
|
||||
}
|
||||
|
||||
public reportIssue(catId: IIssueCategory, req: ITvRequests) {
|
||||
|
@ -172,12 +142,23 @@ export class TvRequestsComponent implements OnInit {
|
|||
this.setRootFolderOverrides(req);
|
||||
}
|
||||
|
||||
private updateRequest(request: ITvRequests) {
|
||||
this.requestService.updateTvRequest(request)
|
||||
.subscribe(x => {
|
||||
this.notificationService.success("Request Updated");
|
||||
this.setOverride(x);
|
||||
request = x;
|
||||
private setQualityProfile(req: ITvRequests) {
|
||||
this.requestService.setQualityProfile(req.id, req.qualityOverride).subscribe(x => {
|
||||
if(x) {
|
||||
this.notificationService.success("Quality profile updated");
|
||||
} else {
|
||||
this.notificationService.error("Could not update the quality profile");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private setRootFolder(req: ITvRequests) {
|
||||
this.requestService.setRootFolder(req.id, req.rootFolder).subscribe(x => {
|
||||
if(x) {
|
||||
this.notificationService.success("Quality profile updated");
|
||||
} else {
|
||||
this.notificationService.error("Could not update the quality profile");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -204,23 +185,15 @@ export class TvRequestsComponent implements OnInit {
|
|||
|
||||
private loadInit() {
|
||||
this.requestService.getTotalTv().subscribe(x => this.totalTv = x);
|
||||
this.requestService.getTvRequestsTree(this.amountToLoad, 0)
|
||||
this.requestService.getTvRequests(this.amountToLoad, 0, OrderType.RequestedDateDesc, FilterType.None, FilterType.None)
|
||||
.subscribe(x => {
|
||||
this.tvRequests = x;
|
||||
this.tvRequests.forEach((val, index) => {
|
||||
this.tvRequests.collection.forEach((val, index) => {
|
||||
this.setDefaults(val);
|
||||
this.loadBackdrop(val);
|
||||
this.setOverride(val.data);
|
||||
this.setOverride(val);
|
||||
});
|
||||
});
|
||||
|
||||
if(this.isAdmin) {
|
||||
this.sonarrService.getQualityProfilesWithoutSettings()
|
||||
.subscribe(x => this.sonarrProfiles = x);
|
||||
|
||||
this.sonarrService.getRootFoldersWithoutSettings()
|
||||
.subscribe(x => this.sonarrRootFolders = x);
|
||||
}
|
||||
}
|
||||
|
||||
private resetSearch() {
|
||||
|
@ -228,20 +201,20 @@ export class TvRequestsComponent implements OnInit {
|
|||
this.loadInit();
|
||||
}
|
||||
|
||||
private setDefaults(val: any) {
|
||||
if (val.data.posterPath === null) {
|
||||
val.data.posterPath = this.defaultPoster;
|
||||
private setDefaults(val: ITvRequests) {
|
||||
if (val.posterPath === null) {
|
||||
val.posterPath = this.defaultPoster;
|
||||
}
|
||||
}
|
||||
|
||||
private loadBackdrop(val: TreeNode): void {
|
||||
if (val.data.background != null) {
|
||||
val.data.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(https://image.tmdb.org/t/p/w1280" + val.data.background + ")");
|
||||
private loadBackdrop(val: ITvRequests): void {
|
||||
if (val.background != null) {
|
||||
val.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(https://image.tmdb.org/t/p/w1280" + val.background + ")");
|
||||
} else {
|
||||
this.imageService.getTvBanner(val.data.tvDbId).subscribe(x => {
|
||||
if(x) {
|
||||
val.data.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
this.imageService.getTvBanner(val.tvDbId).subscribe(x => {
|
||||
if (x) {
|
||||
val.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(" + x + ")");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
import { Subject } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { IIssueCategory, IRequestEngineResult, ISearchMovieResult } from "../interfaces";
|
||||
|
@ -32,15 +30,16 @@ export class MovieSearchComponent implements OnInit {
|
|||
public issueCategorySelected: IIssueCategory;
|
||||
public defaultPoster: string;
|
||||
|
||||
constructor(private searchService: SearchService, private requestService: RequestService,
|
||||
constructor(
|
||||
private searchService: SearchService, private requestService: RequestService,
|
||||
private notificationService: NotificationService, private authService: AuthService,
|
||||
private readonly translate: TranslateService, private sanitizer: DomSanitizer,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
).subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.clearResults();
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { Subject } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { NotificationService, RequestService, SearchService } from "../services";
|
||||
|
||||
import { IRequestEngineResult, ISearchMovieResult, ISearchMovieResultContainer } from "../interfaces";
|
||||
import { NotificationService, RequestService, SearchService } from "../services";
|
||||
|
||||
@Component({
|
||||
selector: "movie-search-grid",
|
||||
|
@ -22,13 +19,14 @@ export class MovieSearchGridComponent implements OnInit {
|
|||
public result: IRequestEngineResult;
|
||||
public searchApplied = false;
|
||||
|
||||
constructor(private searchService: SearchService, private requestService: RequestService,
|
||||
constructor(
|
||||
private searchService: SearchService, private requestService: RequestService,
|
||||
private notificationService: NotificationService, private authService: AuthService) {
|
||||
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms afterthe last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
).subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.clearResults();
|
||||
|
@ -67,7 +65,7 @@ export class MovieSearchGridComponent implements OnInit {
|
|||
}
|
||||
|
||||
try {
|
||||
this.requestService.requestMovie({ theMovieDbId : searchResult.id})
|
||||
this.requestService.requestMovie({ theMovieDbId: searchResult.id })
|
||||
.subscribe(x => {
|
||||
this.result = x;
|
||||
|
||||
|
@ -149,15 +147,14 @@ export class MovieSearchGridComponent implements OnInit {
|
|||
}
|
||||
|
||||
private processGrid(movies: ISearchMovieResult[]) {
|
||||
let container = <ISearchMovieResultContainer>{ movies: [] };
|
||||
let container = <ISearchMovieResultContainer> { movies: [] };
|
||||
movies.forEach((movie, i) => {
|
||||
i++;
|
||||
if((i % 4) === 0) {
|
||||
if ((i % 4) === 0) {
|
||||
container.movies.push(movie);
|
||||
this.movieResultGrid.push(container);
|
||||
container = <ISearchMovieResultContainer>{ movies: [] };
|
||||
container = <ISearchMovieResultContainer> { movies: [] };
|
||||
} else {
|
||||
|
||||
container.movies.push(movie);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Component, Input, OnInit} from "@angular/core";
|
||||
import "rxjs/add/operator/takeUntil";
|
||||
|
||||
import { NotificationService } from "../services";
|
||||
import { RequestService } from "../services";
|
||||
|
@ -39,20 +38,20 @@ export class SeriesInformationComponent implements OnInit {
|
|||
});
|
||||
});
|
||||
|
||||
if(!selected) {
|
||||
if (!selected) {
|
||||
this.notificationService.error("You need to select some episodes!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.series.requested = true;
|
||||
|
||||
const viewModel = <ITvRequestViewModel>{ firstSeason: this.series.firstSeason, latestSeason: this.series.latestSeason, requestAll: this.series.requestAll, tvDbId: this.series.id};
|
||||
const viewModel = <ITvRequestViewModel> { firstSeason: this.series.firstSeason, latestSeason: this.series.latestSeason, requestAll: this.series.requestAll, tvDbId: this.series.id};
|
||||
viewModel.seasons = [];
|
||||
this.series.seasonRequests.forEach((season) => {
|
||||
const seasonsViewModel = <ISeasonsViewModel>{seasonNumber: season.seasonNumber, episodes: []};
|
||||
const seasonsViewModel = <ISeasonsViewModel> {seasonNumber: season.seasonNumber, episodes: []};
|
||||
season.episodes.forEach(ep => {
|
||||
if(!this.series.latestSeason || !this.series.requestAll || !this.series.firstSeason) {
|
||||
if(ep.selected) {
|
||||
if (!this.series.latestSeason || !this.series.requestAll || !this.series.firstSeason) {
|
||||
if (ep.selected) {
|
||||
seasonsViewModel.episodes.push({episodeNumber: ep.episodeNumber});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,46 +42,46 @@
|
|||
<i class='fa fa-film no-search-results-icon'></i>
|
||||
<div class='no-search-results-text'>{{ 'Search.NoResults' | translate }}</div>
|
||||
</div>
|
||||
<p-treeTable [value]="tvResults">
|
||||
<p-column>
|
||||
<ng-template let-col let-node="rowData" pTemplate="header">
|
||||
{{ 'Search.TvShows.Results' | translate }}
|
||||
</ng-template>
|
||||
<ng-template let-col let-node="rowData" pTemplate="body">
|
||||
<div *ngIf="tvResults" >
|
||||
<div *ngFor="let node of tvResults">
|
||||
<!--This is the section that holds the parent level search results set-->
|
||||
<div *ngIf="!node.leaf">
|
||||
<div class="row" >
|
||||
<div *ngIf="node">
|
||||
<div class="row">
|
||||
|
||||
<div class="myBg backdrop" [style.background-image]="node?.data?.background"></div>
|
||||
<div class="myBg backdrop" [style.background-image]="node?.background"></div>
|
||||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
||||
<div class="col-sm-2 small-padding">
|
||||
|
||||
<img *ngIf="node?.data?.banner" class="img-responsive poster" width="150" [src]="node.data.banner" alt="poster">
|
||||
<img *ngIf="node.banner" class="img-responsive poster" width="150" [src]="node.banner" alt="poster">
|
||||
|
||||
</div>
|
||||
<div class="col-sm-8 small-padding">
|
||||
<div>
|
||||
|
||||
<a *ngIf="node.data.imdbId" href="{{node.data.imdbId}}" target="_blank">
|
||||
<h4>{{node.data.title}} ({{node.data.firstAired | date: 'yyyy'}})</h4>
|
||||
<a *ngIf="node.imdbId" href="{{node.imdbId}}" target="_blank">
|
||||
<h4>{{node.title}} ({{node.firstAired | date: 'yyyy'}})</h4>
|
||||
|
||||
</a>
|
||||
<span class="tags">
|
||||
<a *ngIf="node.data.homepage" id="homepageLabel" href="{{node.data.homepage}}" target="_blank"><span class="label label-info" >{{ 'Search.Movies.HomePage' | translate }}</span></a>
|
||||
<a *ngIf="node.homepage" id="homepageLabel" href="{{node.homepage}}" target="_blank">
|
||||
<span class="label label-info">{{ 'Search.Movies.HomePage' | translate }}</span>
|
||||
</a>
|
||||
|
||||
<a *ngIf="node.data.trailer" id="trailerLabel" href="{{node.data.trailer}}" target="_blank"><span class="label label-info">{{ 'Search.Movies.Trailer' | translate }}</span></a>
|
||||
<a *ngIf="node.trailer" id="trailerLabel" href="{{node.trailer}}" target="_blank">
|
||||
<span class="label label-info">{{ 'Search.Movies.Trailer' | translate }}</span>
|
||||
</a>
|
||||
|
||||
<span *ngIf="node.data.status" class="label label-primary" id="statusLabel" target="_blank">{{node.data.status}}</span>
|
||||
<span *ngIf="node.status" class="label label-primary" id="statusLabel" target="_blank">{{node.status}}</span>
|
||||
|
||||
|
||||
<span *ngIf="node.data.firstAired" class="label label-info" target="_blank" id="airDateLabel">{{ 'Search.TvShows.AirDate' | translate }} {{node.data.firstAired | date: 'dd/MM/yyyy'}}</span>
|
||||
<span *ngIf="node.firstAired" class="label label-info" target="_blank" id="airDateLabel">{{ 'Search.TvShows.AirDate' | translate }} {{node.firstAired | date: 'dd/MM/yyyy'}}</span>
|
||||
|
||||
<span *ngIf="node.data.network" class="label label-info" id="networkLabel" target="_blank">{{node.data.network}}</span>
|
||||
<span *ngIf="node.network" class="label label-info" id="networkLabel" target="_blank">{{node.network}}</span>
|
||||
|
||||
|
||||
<span *ngIf="node.data.available" class="label label-success" id="availableLabel">{{ 'Common.Available' | translate }}</span>
|
||||
<span *ngIf="node.available" class="label label-success" id="availableLabel">{{ 'Common.Available' | translate }}</span>
|
||||
|
||||
<span *ngIf="node.data.partlyAvailable" class="label label-warning" id="partiallyAvailableLabel">{{ 'Common.PartlyAvailable' | translate }}</span>
|
||||
<span *ngIf="node.partlyAvailable" class="label label-warning" id="partiallyAvailableLabel">{{ 'Common.PartlyAvailable' | translate }}</span>
|
||||
|
||||
|
||||
|
||||
|
@ -90,47 +90,45 @@
|
|||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<p class="tv-overview">{{node.data.overview}}</p>
|
||||
<p class="tv-overview">{{node.overview}}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-2 small-padding">
|
||||
<div *ngIf="!node.data.fullyAvailable" class="dropdown">
|
||||
<div *ngIf="!node.fullyAvailable" class="dropdown">
|
||||
<button class="btn btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> {{ 'Common.Request' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<li>
|
||||
<a href="#" (click)="allSeasons(node.data, $event)">{{ 'Search.TvShows.AllSeasons' | translate }}</a>
|
||||
<a href="#" (click)="allSeasons(node, $event)">{{ 'Search.TvShows.AllSeasons' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="firstSeason(node.data, $event)">{{ 'Search.TvShows.FirstSeason' | translate }}</a>
|
||||
<a href="#" (click)="firstSeason(node, $event)">{{ 'Search.TvShows.FirstSeason' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="latestSeason(node.data, $event)">{{ 'Search.TvShows.LatestSeason' | translate }}</a>
|
||||
<a href="#" (click)="latestSeason(node, $event)">{{ 'Search.TvShows.LatestSeason' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="openClosestTab($event)">{{ 'Search.TvShows.Select' | translate }}</a>
|
||||
<a href="#" (click)="openClosestTab(node, $event)">{{ 'Search.TvShows.Select' | translate }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div *ngIf="node.data.fullyAvailable">
|
||||
<div *ngIf="node.fullyAvailable">
|
||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled>
|
||||
<i class="fa fa-check"></i> {{ 'Common.Available' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<br />
|
||||
<div *ngIf="node.data.plexUrl && node.data.available">
|
||||
<a style="text-align: right" class="btn btn-sm btn-success-outline" href="{{node.data.plexUrl}}"
|
||||
target="_blank">
|
||||
<div *ngIf="node.plexUrl && node.available">
|
||||
<a style="text-align: right" class="btn btn-sm btn-success-outline" href="{{node.plexUrl}}" target="_blank">
|
||||
<i class="fa fa-eye"></i> {{ 'Search.ViewOnPlex' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
<div *ngIf="node.data.embyUrl && node.data.available">
|
||||
<a style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline" href="{{node.data.embyUrl}}"
|
||||
target="_blank">
|
||||
<div *ngIf="node.embyUrl && node.available">
|
||||
<a style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline" href="{{node.embyUrl}}" target="_blank">
|
||||
<i class="fa fa-eye"></i> {{ 'Search.ViewOnEmby' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -142,11 +140,11 @@
|
|||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<li *ngFor="let cat of issueCategories">
|
||||
<a [routerLink]="" (click)="reportIssue(cat, node.data)">{{cat.value}}</a>
|
||||
<a [routerLink]="" (click)="reportIssue(cat, node)">{{cat.value}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div *ngIf="!node.data.available">
|
||||
<div *ngIf="!node.available">
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
@ -156,15 +154,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
|
||||
<div *ngIf="node.leaf">
|
||||
<seriesinformation [seriesId]="node.data.id"></seriesinformation>
|
||||
<div *ngIf="node.open">
|
||||
<seriesinformation [seriesId]="node.id"></seriesinformation>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
</ng-template>
|
||||
</p-column>
|
||||
</p-treeTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
import { Subject } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { IIssueCategory, IRequestEngineResult, ISearchTvResult, ISeasonsViewModel, ITvRequestViewModel } from "../interfaces";
|
||||
import { ImageService, NotificationService, RequestService, SearchService } from "../services";
|
||||
|
||||
import { TreeNode } from "primeng/primeng";
|
||||
import { IRequestEngineResult } from "../interfaces";
|
||||
import { IIssueCategory, ISearchTvResult, ISeasonsViewModel, ITvRequestViewModel } from "../interfaces";
|
||||
|
||||
@Component({
|
||||
selector: "tv-search",
|
||||
templateUrl: "./tvsearch.component.html",
|
||||
|
@ -19,7 +17,7 @@ export class TvSearchComponent implements OnInit {
|
|||
|
||||
public searchText: string;
|
||||
public searchChanged = new Subject<string>();
|
||||
public tvResults: TreeNode[];
|
||||
public tvResults: ISearchTvResult[];
|
||||
public result: IRequestEngineResult;
|
||||
public searchApplied = false;
|
||||
public defaultPoster: string;
|
||||
|
@ -32,21 +30,22 @@ export class TvSearchComponent implements OnInit {
|
|||
public issueProviderId: string;
|
||||
public issueCategorySelected: IIssueCategory;
|
||||
|
||||
constructor(private searchService: SearchService, private requestService: RequestService,
|
||||
constructor(
|
||||
private searchService: SearchService, private requestService: RequestService,
|
||||
private notificationService: NotificationService, private authService: AuthService,
|
||||
private imageService: ImageService, private sanitizer: DomSanitizer,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
).subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.clearResults();
|
||||
return;
|
||||
}
|
||||
this.searchService.searchTvTreeNode(this.searchText)
|
||||
this.searchService.searchTv(this.searchText)
|
||||
.subscribe(x => {
|
||||
this.tvResults = x;
|
||||
this.searchApplied = true;
|
||||
|
@ -55,34 +54,13 @@ export class TvSearchComponent implements OnInit {
|
|||
});
|
||||
this.defaultPoster = "../../../images/default_tv_poster.png";
|
||||
const base = this.platformLocation.getBaseHrefFromDOM();
|
||||
if(base) {
|
||||
if (base) {
|
||||
this.defaultPoster = "../../.." + base + "/images/default_tv_poster.png";
|
||||
}
|
||||
}
|
||||
public openClosestTab(el: any) {
|
||||
public openClosestTab(node: ISearchTvResult,el: any) {
|
||||
el.preventDefault();
|
||||
const rowclass = "undefined ng-star-inserted";
|
||||
el = el.toElement || el.relatedTarget || el.target;
|
||||
while (el.className !== rowclass) {
|
||||
// Increment the loop to the parent node until we find the row we need
|
||||
el = el.parentNode;
|
||||
}
|
||||
// At this point, the while loop has stopped and `el` represents the element that has
|
||||
// the class you specified
|
||||
|
||||
// Then we loop through the children to find the caret which we want to click
|
||||
const caretright = "fa-caret-right";
|
||||
const caretdown = "fa-caret-down";
|
||||
for (const value of el.children) {
|
||||
// the caret from the ui has 2 class selectors depending on if expanded or not
|
||||
// we search for both since we want to still toggle the clicking
|
||||
if (value.className.includes(caretright) || value.className.includes(caretdown)) {
|
||||
// Then we tell JS to click the element even though we hid it from the UI
|
||||
value.click();
|
||||
//Break from loop since we no longer need to continue looking
|
||||
break;
|
||||
}
|
||||
}
|
||||
node.open = !node.open;
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
|
@ -91,7 +69,7 @@ export class TvSearchComponent implements OnInit {
|
|||
this.result = {
|
||||
message: "",
|
||||
result: false,
|
||||
errorMessage:"",
|
||||
errorMessage: "",
|
||||
};
|
||||
this.popularShows();
|
||||
}
|
||||
|
@ -138,16 +116,16 @@ export class TvSearchComponent implements OnInit {
|
|||
|
||||
public getExtraInfo() {
|
||||
this.tvResults.forEach((val, index) => {
|
||||
this.imageService.getTvBanner(val.data.id).subscribe(x => {
|
||||
if(x) {
|
||||
val.data.background = this.sanitizer.
|
||||
this.imageService.getTvBanner(val.id).subscribe(x => {
|
||||
if (x) {
|
||||
val.background = this.sanitizer.
|
||||
bypassSecurityTrustStyle
|
||||
("url(" + x + ")");
|
||||
}
|
||||
});
|
||||
this.searchService.getShowInformationTreeNode(val.data.id)
|
||||
this.searchService.getShowInformation(val.id)
|
||||
.subscribe(x => {
|
||||
if (x.data) {
|
||||
if (x) {
|
||||
this.setDefaults(x);
|
||||
this.updateItem(val, x);
|
||||
} else {
|
||||
|
@ -166,14 +144,14 @@ export class TvSearchComponent implements OnInit {
|
|||
searchResult.approved = true;
|
||||
}
|
||||
|
||||
const viewModel = <ITvRequestViewModel>{ firstSeason: searchResult.firstSeason, latestSeason: searchResult.latestSeason, requestAll: searchResult.requestAll, tvDbId: searchResult.id};
|
||||
const viewModel = <ITvRequestViewModel> { firstSeason: searchResult.firstSeason, latestSeason: searchResult.latestSeason, requestAll: searchResult.requestAll, tvDbId: searchResult.id };
|
||||
viewModel.seasons = [];
|
||||
searchResult.seasonRequests.forEach((season) => {
|
||||
const seasonsViewModel = <ISeasonsViewModel>{seasonNumber: season.seasonNumber, episodes: []};
|
||||
const seasonsViewModel = <ISeasonsViewModel> { seasonNumber: season.seasonNumber, episodes: [] };
|
||||
season.episodes.forEach(ep => {
|
||||
if(!searchResult.latestSeason || !searchResult.requestAll || !searchResult.firstSeason) {
|
||||
if(ep.requested) {
|
||||
seasonsViewModel.episodes.push({episodeNumber: ep.episodeNumber});
|
||||
if (!searchResult.latestSeason || !searchResult.requestAll || !searchResult.firstSeason) {
|
||||
if (ep.requested) {
|
||||
seasonsViewModel.episodes.push({ episodeNumber: ep.episodeNumber });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -223,29 +201,29 @@ export class TvSearchComponent implements OnInit {
|
|||
this.issueProviderId = req.id.toString();
|
||||
}
|
||||
|
||||
private updateItem(key: TreeNode, updated: TreeNode) {
|
||||
private updateItem(key: ISearchTvResult, updated: ISearchTvResult) {
|
||||
const index = this.tvResults.indexOf(key, 0);
|
||||
if (index > -1) {
|
||||
// Update certain properties, otherwise we will loose some data
|
||||
this.tvResults[index].data.title = updated.data.title;
|
||||
this.tvResults[index].data.banner = updated.data.banner;
|
||||
this.tvResults[index].data.imdbId = updated.data.imdbId;
|
||||
this.tvResults[index].data.seasonRequests = updated.data.seasonRequests;
|
||||
this.tvResults[index].data.seriesId = updated.data.seriesId;
|
||||
this.tvResults[index].data.fullyAvailable = updated.data.fullyAvailable;
|
||||
this.tvResults[index].data.backdrop = updated.data.backdrop;
|
||||
this.tvResults[index].title = updated.title;
|
||||
this.tvResults[index].banner = updated.banner;
|
||||
this.tvResults[index].imdbId = updated.imdbId;
|
||||
this.tvResults[index].seasonRequests = updated.seasonRequests;
|
||||
this.tvResults[index].seriesId = updated.seriesId;
|
||||
this.tvResults[index].fullyAvailable = updated.fullyAvailable;
|
||||
this.tvResults[index].background = updated.banner;
|
||||
}
|
||||
}
|
||||
|
||||
private setDefaults(x: any) {
|
||||
if (x.data.banner === null) {
|
||||
x.data.banner = this.defaultPoster;
|
||||
private setDefaults(x: ISearchTvResult) {
|
||||
if (x.banner === null) {
|
||||
x.banner = this.defaultPoster;
|
||||
}
|
||||
|
||||
if (x.data.imdbId === null) {
|
||||
x.data.imdbId = "https://www.tvmaze.com/shows/" + x.data.seriesId;
|
||||
if (x.imdbId === null) {
|
||||
x.imdbId = "https://www.tvmaze.com/shows/" + x.seriesId;
|
||||
} else {
|
||||
x.data.imdbId = "http://www.imdb.com/title/" + x.data.imdbId + "/";
|
||||
x.imdbId = "http://www.imdb.com/title/" + x.imdbId + "/";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
import { IPlexAuthentication, IPlexLibResponse, IPlexOAuthViewModel,IPlexServer, IPlexServerViewModel, IUsersModel } from "../../interfaces";
|
||||
import { IPlexAuthentication, IPlexLibResponse, IPlexOAuthViewModel, IPlexServer, IPlexServerViewModel, IUsersModel } from "../../interfaces";
|
||||
|
||||
@Injectable()
|
||||
export class PlexService extends ServiceHelpers {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { HttpClient, HttpHeaders } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IPlexPin } from "../../interfaces";
|
||||
|
||||
|
@ -10,11 +10,10 @@ import { IPlexPin } from "../../interfaces";
|
|||
export class PlexTvService {
|
||||
|
||||
constructor(private http: HttpClient, public platformLocation: PlatformLocation) {
|
||||
|
||||
}
|
||||
|
||||
public GetPin(clientId: string, applicationName: string): Observable<IPlexPin> {
|
||||
const headers = new HttpHeaders({"Content-Type":"application/json",
|
||||
const headers = new HttpHeaders({"Content-Type": "application/json",
|
||||
"X-Plex-Client-Identifier": clientId,
|
||||
"X-Plex-Product": applicationName,
|
||||
"X-Plex-Version": "3",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IRadarrProfile, IRadarrRootFolder } from "../../interfaces";
|
||||
import { IRadarrSettings } from "../../interfaces";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ISonarrSettings } from "../../interfaces";
|
||||
import { ISonarrProfile, ISonarrRootFolder } from "../../interfaces";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
|
@ -37,6 +37,7 @@ export class TesterService extends ServiceHelpers {
|
|||
public pushbulletTest(settings: IPushbulletNotificationSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}pushbullet`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
||||
public pushoverTest(settings: IPushoverNotificationSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}pushover`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
@ -80,9 +81,11 @@ export class TesterService extends ServiceHelpers {
|
|||
public sickrageTest(settings: ISickRageSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}sickrage`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
||||
public newsletterTest(settings: INewsletterNotificationSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}newsletter`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
||||
public mobileNotificationTest(settings: IMobileNotificationTestSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}mobile`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ICheckbox, ICreateWizardUser, IIdentityResult, IResetPasswordToken, IUpdateLocalUser, IUser } from "../interfaces";
|
||||
import { ICheckbox, ICreateWizardUser, IIdentityResult, IResetPasswordToken, IUpdateLocalUser, IUser, IWizardUserResult } from "../interfaces";
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
||||
@Injectable()
|
||||
|
@ -12,8 +12,8 @@ export class IdentityService extends ServiceHelpers {
|
|||
constructor(http: HttpClient, public platformLocation: PlatformLocation) {
|
||||
super(http, "/api/v1/Identity/", platformLocation);
|
||||
}
|
||||
public createWizardUser(user: ICreateWizardUser): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}Wizard/`, JSON.stringify(user), {headers: this.headers});
|
||||
public createWizardUser(user: ICreateWizardUser): Observable<IWizardUserResult> {
|
||||
return this.http.post<IWizardUserResult>(`${this.url}Wizard/`, JSON.stringify(user), {headers: this.headers});
|
||||
}
|
||||
|
||||
public getUser(): Observable<IUser> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IIssueCategory, IIssueComments,IIssueCount, IIssues, IIssuesChat, INewIssueComments, IssueStatus, IUpdateStatus } from "../interfaces";
|
||||
import { IIssueCategory, IIssueComments, IIssueCount, IIssues, IIssuesChat, INewIssueComments, IssueStatus, IUpdateStatus } from "../interfaces";
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
||||
@Injectable()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IMobileUsersViewModel } from "./../interfaces";
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IMassEmailModel } from "./../interfaces";
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "./../interfaces";
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
@ -19,6 +19,7 @@ export class RecentlyAddedService extends ServiceHelpers {
|
|||
public getRecentlyAddedTv(): Observable<IRecentlyAddedTvShows[]> {
|
||||
return this.http.get<IRecentlyAddedTvShows[]>(`${this. url}tv/`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public getRecentlyAddedTvGrouped(): Observable<IRecentlyAddedTvShows[]> {
|
||||
return this.http.get<IRecentlyAddedTvShows[]>(`${this.url}tv/grouped`, {headers: this.headers});
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { TreeNode } from "primeng/primeng";
|
||||
import { IRequestEngineResult } from "../interfaces";
|
||||
import { IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestsViewModel, ITvRequests,ITvUpdateModel, OrderType } from "../interfaces";
|
||||
import { FilterType, IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces";
|
||||
import { ITvRequestViewModel } from "../interfaces";
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
||||
|
@ -64,8 +63,8 @@ export class RequestService extends ServiceHelpers {
|
|||
return this.http.put<IMovieRequests>(`${this.url}movie/`, JSON.stringify(request), {headers: this.headers});
|
||||
}
|
||||
|
||||
public getTvRequests(count: number, position: number): Observable<ITvRequests[]> {
|
||||
return this.http.get<ITvRequests[]>(`${this.url}tv/${count}/${position}`, {headers: this.headers});
|
||||
public getTvRequests(count: number, position: number, order: OrderType, status: FilterType, availability: FilterType): Observable<IRequestsViewModel<ITvRequests>> {
|
||||
return this.http.get<IRequestsViewModel<ITvRequests>>(`${this.url}tv/${count}/${position}/${order}/${status}/${availability}`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public getTvRequestsTree(count: number, position: number): Observable<TreeNode[]> {
|
||||
|
@ -127,4 +126,10 @@ export class RequestService extends ServiceHelpers {
|
|||
public unSubscribeToTv(requestId: number): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}tv/unsubscribe/${requestId}`, {headers: this.headers});
|
||||
}
|
||||
public setQualityProfile(requestId: number, qualityId: number): Observable<boolean> {
|
||||
return this.http.put<boolean>(`${this.url}tv/quality/${requestId}/${qualityId}`, {headers: this.headers});
|
||||
}
|
||||
public setRootFolder(requestId: number, rootFolderId: number): Observable<boolean> {
|
||||
return this.http.put<boolean>(`${this.url}tv/root/${requestId}/${rootFolderId}`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { TreeNode } from "primeng/primeng";
|
||||
import { ISearchMovieResult } from "../interfaces";
|
||||
|
@ -56,16 +56,16 @@ export class SearchService extends ServiceHelpers {
|
|||
return this.http.get<ISearchTvResult>(`${this.url}/Tv/info/${theTvDbId}`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public popularTv(): Observable<TreeNode[]> {
|
||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/popular/tree`, {headers: this.headers});
|
||||
public popularTv(): Observable<ISearchTvResult[]> {
|
||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/popular`, {headers: this.headers});
|
||||
}
|
||||
public mostWatchedTv(): Observable<TreeNode[]> {
|
||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/mostwatched/tree`, {headers: this.headers});
|
||||
public mostWatchedTv(): Observable<ISearchTvResult[]> {
|
||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/mostwatched`, {headers: this.headers});
|
||||
}
|
||||
public anticipatedTv(): Observable<TreeNode[]> {
|
||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/anticipated/tree`, {headers: this.headers});
|
||||
public anticipatedTv(): Observable<ISearchTvResult[]> {
|
||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/anticipated`, {headers: this.headers});
|
||||
}
|
||||
public trendingTv(): Observable<TreeNode[]> {
|
||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/trending/tree`, {headers: this.headers});
|
||||
public trendingTv(): Observable<ISearchTvResult[]> {
|
||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/trending`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient, HttpHeaders } from "@angular/common/http";
|
||||
import "rxjs/add/observable/throw";
|
||||
|
||||
export class ServiceHelpers {
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import {
|
||||
IAbout,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
||||
|
|
|
@ -25,13 +25,13 @@ export class CustomizationComponent implements OnInit {
|
|||
return item.fullName === this.settings.presetThemeName;
|
||||
})[0];
|
||||
|
||||
if(existingTheme) {
|
||||
if (existingTheme) {
|
||||
const index = this.themes.indexOf(existingTheme, 0);
|
||||
if (index > -1) {
|
||||
this.themes.splice(index, 1);
|
||||
}
|
||||
}
|
||||
if(x.hasPresetTheme) {
|
||||
if (x.hasPresetTheme) {
|
||||
this.themes.unshift({displayName: x.presetThemeDisplayName, fullName: x.presetThemeName, url: existingTheme.url, version: x.presetThemeVersion});
|
||||
this.themes.unshift({displayName: "None", fullName: "None", url: "", version: ""});
|
||||
} else {
|
||||
|
@ -45,8 +45,8 @@ export class CustomizationComponent implements OnInit {
|
|||
public save() {
|
||||
|
||||
this.settingsService.verifyUrl(this.settings.applicationUrl).subscribe(x => {
|
||||
if(this.settings.applicationUrl) {
|
||||
if(!x) {
|
||||
if (this.settings.applicationUrl) {
|
||||
if (!x) {
|
||||
this.notificationService.error(`The URL "${this.settings.applicationUrl}" is not valid. Please format it correctly e.g. http://www.google.com/`);
|
||||
return;
|
||||
}
|
||||
|
@ -64,16 +64,16 @@ export class CustomizationComponent implements OnInit {
|
|||
}
|
||||
|
||||
public dropDownChange(event: any): void {
|
||||
const selectedThemeFullName = <string>event.target.value;
|
||||
const selectedThemeFullName = <string> event.target.value;
|
||||
const selectedTheme = this.themes.filter((val) => {
|
||||
return val.fullName === selectedThemeFullName;
|
||||
})[0];
|
||||
|
||||
if(selectedTheme.fullName === this.settings.presetThemeName) {
|
||||
if (selectedTheme.fullName === this.settings.presetThemeName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(selectedTheme.fullName === "None" || selectedTheme.fullName === "-1") {
|
||||
if (selectedTheme.fullName === "None" || selectedTheme.fullName === "-1") {
|
||||
this.settings.presetThemeName = "";
|
||||
this.settings.presetThemeContent = "";
|
||||
return;
|
||||
|
|
|
@ -39,7 +39,7 @@ export class DiscordComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = <IDiscordNotifcationSettings>form.value;
|
||||
const settings = <IDiscordNotifcationSettings> form.value;
|
||||
settings.notificationTemplates = this.templates;
|
||||
|
||||
this.settingsService.saveDiscordNotificationSettings(settings).subscribe(x => {
|
||||
|
|
|
@ -54,7 +54,7 @@ export class EmailNotificationComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = <IEmailNotificationSettings>form.value;
|
||||
const settings = <IEmailNotificationSettings> form.value;
|
||||
settings.notificationTemplates = this.templates;
|
||||
|
||||
this.settingsService.saveEmailNotificationSettings(settings).subscribe(x => {
|
||||
|
|
|
@ -29,7 +29,7 @@ export class MattermostComponent implements OnInit {
|
|||
username: [x.username],
|
||||
webhookUrl: [x.webhookUrl, [Validators.required]],
|
||||
channel: [x.channel],
|
||||
iconUrl:[x.iconUrl],
|
||||
iconUrl: [x.iconUrl],
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -41,7 +41,7 @@ export class MattermostComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = <IMattermostNotifcationSettings>form.value;
|
||||
const settings = <IMattermostNotifcationSettings> form.value;
|
||||
settings.notificationTemplates = this.templates;
|
||||
|
||||
this.settingsService.saveMattermostNotificationSettings(settings).subscribe(x => {
|
||||
|
|
|
@ -32,9 +32,9 @@ export class MobileComponent implements OnInit {
|
|||
});
|
||||
|
||||
this.mobileService.getUserDeviceList().subscribe(x => {
|
||||
if(x.length <= 0) {
|
||||
if (x.length <= 0) {
|
||||
this.userList = [];
|
||||
this.userList.push({username:"None",devices:0, userId:""});
|
||||
this.userList.push({username: "None", devices: 0, userId: ""});
|
||||
} else {
|
||||
this.userList = x;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ export class MobileComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = <IMobileNotifcationSettings>form.value;
|
||||
const settings = <IMobileNotifcationSettings> form.value;
|
||||
settings.notificationTemplates = this.templates;
|
||||
|
||||
this.settingsService.saveMobileNotificationSettings(settings).subscribe(x => {
|
||||
|
@ -65,8 +65,8 @@ export class MobileComponent implements OnInit {
|
|||
this.notificationService.error("Please check your entered values");
|
||||
return;
|
||||
}
|
||||
if(!this.testUserId) {
|
||||
this.notificationService.warning("Warning","Please select a user to send the test notification");
|
||||
if (!this.testUserId) {
|
||||
this.notificationService.warning("Warning", "Please select a user to send the test notification");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,10 +51,10 @@ export class NewsletterComponent implements OnInit {
|
|||
}
|
||||
public addEmail() {
|
||||
|
||||
if(this.emailToAdd) {
|
||||
if (this.emailToAdd) {
|
||||
const emailRegex = "[a-zA-Z0-9.-_]{1,}@[a-zA-Z.-]{2,}[.]{1}[a-zA-Z]{2,}";
|
||||
const match = this.emailToAdd.match(emailRegex)!;
|
||||
if(match && match.length > 0) {
|
||||
if (match && match.length > 0) {
|
||||
this.settings.externalEmails.push(this.emailToAdd);
|
||||
this.emailToAdd = "";
|
||||
} else {
|
||||
|
|
|
@ -37,7 +37,7 @@ export class PushbulletComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = <IPushbulletNotificationSettings>form.value;
|
||||
const settings = <IPushbulletNotificationSettings> form.value;
|
||||
settings.notificationTemplates = this.templates;
|
||||
|
||||
this.settingsService.savePushbulletNotificationSettings(settings).subscribe(x => {
|
||||
|
|
|
@ -37,7 +37,7 @@ export class PushoverComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = <IPushoverNotificationSettings>form.value;
|
||||
const settings = <IPushoverNotificationSettings> form.value;
|
||||
settings.notificationTemplates = this.templates;
|
||||
|
||||
this.settingsService.savePushoverNotificationSettings(settings).subscribe(x => {
|
||||
|
|
|
@ -41,7 +41,7 @@ export class SlackComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = <ISlackNotificationSettings>form.value;
|
||||
const settings = <ISlackNotificationSettings> form.value;
|
||||
if (settings.iconEmoji && settings.iconUrl) {
|
||||
|
||||
this.notificationService.error("You cannot have a Emoji icon and a URL icon");
|
||||
|
@ -65,7 +65,7 @@ export class SlackComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = <ISlackNotificationSettings>form.value;
|
||||
const settings = <ISlackNotificationSettings> form.value;
|
||||
if (settings.iconEmoji && settings.iconUrl) {
|
||||
|
||||
this.notificationService.error("You cannot have a Emoji icon and a URL icon");
|
||||
|
|
|
@ -40,7 +40,7 @@ export class TelegramComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = <ITelegramNotifcationSettings>form.value;
|
||||
const settings = <ITelegramNotifcationSettings> form.value;
|
||||
settings.notificationTemplates = this.templates;
|
||||
|
||||
this.settingsService.saveTelegramNotificationSettings(settings).subscribe(x => {
|
||||
|
|
|
@ -41,9 +41,9 @@ export class OmbiComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const result = <IOmbiSettings>form.value;
|
||||
if(result.baseUrl && result.baseUrl.length > 0) {
|
||||
if(!result.baseUrl.startsWith("/")) {
|
||||
const result = <IOmbiSettings> form.value;
|
||||
if (result.baseUrl && result.baseUrl.length > 0) {
|
||||
if (!result.baseUrl.startsWith("/")) {
|
||||
this.notificationService.error("Please ensure your base url starts with a '/'");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import "rxjs/add/operator/takeUntil";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
|
||||
import { IPlexServerResponse, IPlexServerViewModel } from "../../interfaces";
|
||||
import { IPlexLibrariesSettings, IPlexServer, IPlexSettings } from "../../interfaces";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Subject } from "rxjs";
|
||||
import { takeUntil } from "rxjs/operators";
|
||||
|
||||
import { IPlexLibrariesSettings, IPlexServer, IPlexServerResponse, IPlexServerViewModel, IPlexSettings } from "../../interfaces";
|
||||
import { JobService, NotificationService, PlexService, SettingsService, TesterService } from "../../services";
|
||||
|
||||
@Component({
|
||||
|
@ -21,7 +19,8 @@ export class PlexComponent implements OnInit, OnDestroy {
|
|||
|
||||
private subscriptions = new Subject<void>();
|
||||
|
||||
constructor(private settingsService: SettingsService,
|
||||
constructor(
|
||||
private settingsService: SettingsService,
|
||||
private notificationService: NotificationService,
|
||||
private plexService: PlexService,
|
||||
private testerService: TesterService,
|
||||
|
@ -34,9 +33,9 @@ export class PlexComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
public requestServers(server: IPlexServer) {
|
||||
this.plexService.getServers(this.username, this.password)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.plexService.getServers(this.username, this.password).pipe(
|
||||
takeUntil(this.subscriptions),
|
||||
).subscribe(x => {
|
||||
if (x.success) {
|
||||
this.loadedServers = x;
|
||||
this.serversButton = true;
|
||||
|
@ -72,7 +71,7 @@ export class PlexComponent implements OnInit, OnDestroy {
|
|||
if (this.settings.servers == null) {
|
||||
this.settings.servers = [];
|
||||
}
|
||||
this.settings.servers.push(<IPlexServer>{ name: "New*", id: Math.floor(Math.random() * (99999 - 0 + 1) + 1) });
|
||||
this.settings.servers.push(<IPlexServer> { name: "New*", id: Math.floor(Math.random() * (99999 - 0 + 1) + 1) });
|
||||
|
||||
}
|
||||
|
||||
|
@ -120,7 +119,7 @@ export class PlexComponent implements OnInit, OnDestroy {
|
|||
|
||||
public runCacher(): void {
|
||||
this.jobService.runPlexCacher().subscribe(x => {
|
||||
if(x) {
|
||||
if (x) {
|
||||
this.notificationService.success("Triggered the Plex Full Sync");
|
||||
}
|
||||
});
|
||||
|
@ -128,7 +127,7 @@ export class PlexComponent implements OnInit, OnDestroy {
|
|||
|
||||
public runRecentlyAddedCacher(): void {
|
||||
this.jobService.runPlexRecentlyAddedCacher().subscribe(x => {
|
||||
if(x) {
|
||||
if (x) {
|
||||
this.notificationService.success("Triggered the Plex Recently Added Sync");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -93,7 +93,7 @@ export class RadarrComponent implements OnInit {
|
|||
this.notificationService.error("Please check your entered values");
|
||||
return;
|
||||
}
|
||||
const settings = <IRadarrSettings>form.value;
|
||||
const settings = <IRadarrSettings> form.value;
|
||||
this.testerService.radarrTest(settings).subscribe(x => {
|
||||
if (x === true) {
|
||||
this.notificationService.success("Successfully connected to Radarr!");
|
||||
|
@ -108,12 +108,12 @@ public onSubmit(form: FormGroup) {
|
|||
this.notificationService.error("Please check your entered values");
|
||||
return;
|
||||
}
|
||||
if(form.controls.defaultQualityProfile.value === "-1" || form.controls.defaultRootPath.value === "Please Select") {
|
||||
if (form.controls.defaultQualityProfile.value === "-1" || form.controls.defaultRootPath.value === "Please Select") {
|
||||
this.notificationService.error("Please check your entered values");
|
||||
return;
|
||||
}
|
||||
|
||||
const settings = <IRadarrSettings>form.value;
|
||||
const settings = <IRadarrSettings> form.value;
|
||||
this.settingsService.saveRadarr(settings).subscribe(x => {
|
||||
if (x) {
|
||||
this.notificationService.success("Successfully saved Radarr settings");
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import { CommonModule } from "@angular/common";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
import { NgbAccordionModule, NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
||||
import { ClipboardModule } from "ngx-clipboard/dist";
|
||||
import { ClipboardModule } from "ngx-clipboard";
|
||||
|
||||
import { AuthGuard } from "../auth/auth.guard";
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { CouchPotatoService, EmbyService, IssuesService, JobService, MobileService, NotificationMessageService, PlexService, RadarrService,
|
||||
SonarrService, TesterService, ValidationService } from "../services";
|
||||
import {
|
||||
CouchPotatoService, EmbyService, IssuesService, JobService, MobileService, NotificationMessageService, PlexService, RadarrService,
|
||||
SonarrService, TesterService, ValidationService,
|
||||
} from "../services";
|
||||
|
||||
import { PipeModule } from "../pipes/pipe.module";
|
||||
import { AboutComponent } from "./about/about.component";
|
||||
|
|
|
@ -40,7 +40,7 @@ export class SickRageComponent implements OnInit {
|
|||
this.notificationService.error("Please check your entered values");
|
||||
return;
|
||||
}
|
||||
const settings = <ISickRageSettings>form.value;
|
||||
const settings = <ISickRageSettings> form.value;
|
||||
this.testerService.sickrageTest(settings).subscribe(x => {
|
||||
if (x) {
|
||||
this.notificationService.success("Successfully connected to SickRage!");
|
||||
|
|
|
@ -93,7 +93,7 @@ export class SonarrComponent implements OnInit {
|
|||
this.notificationService.error("Please check your entered values");
|
||||
return;
|
||||
}
|
||||
const settings = <ISonarrSettings>form.value;
|
||||
const settings = <ISonarrSettings> form.value;
|
||||
this.testerService.sonarrTest(settings).subscribe(x => {
|
||||
if (x) {
|
||||
this.notificationService.success("Successfully connected to Sonarr!");
|
||||
|
@ -108,13 +108,13 @@ export class SonarrComponent implements OnInit {
|
|||
this.notificationService.error("Please check your entered values");
|
||||
return;
|
||||
}
|
||||
if(form.controls.defaultQualityProfile) {
|
||||
if(form.controls.defaultQualityProfile.value === "-1") {
|
||||
if (form.controls.defaultQualityProfile) {
|
||||
if (form.controls.defaultQualityProfile.value === "-1") {
|
||||
this.notificationService.error("Please check your entered values");
|
||||
}
|
||||
}
|
||||
if(form.controls.defaultRootPath) {
|
||||
if(form.controls.defaultRootPath.value === "Please Select") {
|
||||
if (form.controls.defaultRootPath) {
|
||||
if (form.controls.defaultRootPath.value === "Please Select") {
|
||||
this.notificationService.error("Please check your entered values");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ export class UpdateDetailsComponent implements OnInit {
|
|||
this.identityService.getUser().subscribe(x => {
|
||||
const localUser = x as IUpdateLocalUser;
|
||||
this.form = this.fb.group({
|
||||
id:[localUser.id],
|
||||
id: [localUser.id],
|
||||
username: [localUser.userName],
|
||||
emailAddress: [localUser.emailAddress, [Validators.email]],
|
||||
confirmNewPassword: [localUser.confirmNewPassword],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { ICheckbox, ICustomizationSettings, IEmailNotificationSettings,IUser } from "../interfaces";
|
||||
import { ICheckbox, ICustomizationSettings, IEmailNotificationSettings, IUser } from "../interfaces";
|
||||
import { IdentityService, NotificationService, SettingsService } from "../services";
|
||||
|
||||
@Component({
|
||||
|
@ -21,9 +21,9 @@ export class UserManagementComponent implements OnInit {
|
|||
public bulkMovieLimit?: number;
|
||||
public bulkEpisodeLimit?: number;
|
||||
|
||||
constructor(private readonly identityService: IdentityService,
|
||||
private readonly settingsService: SettingsService,
|
||||
private readonly notificationService: NotificationService) { }
|
||||
constructor(private identityService: IdentityService,
|
||||
private settingsService: SettingsService,
|
||||
private notificationService: NotificationService) { }
|
||||
|
||||
public ngOnInit() {
|
||||
this.users = [];
|
||||
|
@ -37,7 +37,7 @@ export class UserManagementComponent implements OnInit {
|
|||
}
|
||||
|
||||
public welcomeEmail(user: IUser) {
|
||||
if(!user.emailAddress) {
|
||||
if (!user.emailAddress) {
|
||||
this.notificationService.error("The user needs an email address.");
|
||||
return;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ export class UserManagementComponent implements OnInit {
|
|||
this.notificationService.error("Email Notifications are not setup, cannot send welcome email");
|
||||
return;
|
||||
}
|
||||
if(!this.emailSettings.notificationTemplates.some(x => {
|
||||
if (!this.emailSettings.notificationTemplates.some(x => {
|
||||
return x.enabled && x.notificationType === 8;
|
||||
})) {
|
||||
this.notificationService.error("The Welcome Email template is not enabled in the Email Setings");
|
||||
|
@ -74,20 +74,20 @@ export class UserManagementComponent implements OnInit {
|
|||
});
|
||||
|
||||
this.users.forEach(x => {
|
||||
if(!x.checked) {
|
||||
if (!x.checked) {
|
||||
return;
|
||||
}
|
||||
if(anyRoles) {
|
||||
if (anyRoles) {
|
||||
x.claims = this.availableClaims;
|
||||
}
|
||||
if(this.bulkEpisodeLimit) {
|
||||
if (this.bulkEpisodeLimit) {
|
||||
x.episodeRequestLimit = this.bulkEpisodeLimit;
|
||||
}
|
||||
if(this.bulkMovieLimit) {
|
||||
if (this.bulkMovieLimit) {
|
||||
x.movieRequestLimit = this.bulkMovieLimit;
|
||||
}
|
||||
this.identityService.updateUser(x).subscribe(y => {
|
||||
if(!y.successful) {
|
||||
if (!y.successful) {
|
||||
this.notificationService.error(`Could not update user ${x.userName}. Reason ${y.errors[0]}`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -17,10 +17,12 @@ export class CreateAdminComponent {
|
|||
|
||||
public createUser() {
|
||||
this.identityService.createWizardUser({username: this.username, password: this.password, usePlexAdminAccount: false}).subscribe(x => {
|
||||
if (x) {
|
||||
if (x.result) {
|
||||
this.router.navigate(["login"]);
|
||||
} else {
|
||||
this.notificationService.error("There was an error... You might want to put this on Github...");
|
||||
if(x.errors.length > 0) {
|
||||
this.notificationService.error(x.errors[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { IEmbySettings } from "../../interfaces";
|
|||
})
|
||||
export class EmbyComponent implements OnInit {
|
||||
|
||||
private embySettings: IEmbySettings;
|
||||
public embySettings: IEmbySettings;
|
||||
|
||||
constructor(private embyService: EmbyService,
|
||||
private router: Router,
|
||||
|
@ -21,7 +21,7 @@ export class EmbyComponent implements OnInit {
|
|||
public ngOnInit() {
|
||||
this.embySettings = {
|
||||
servers: [],
|
||||
id:0,
|
||||
id: 0,
|
||||
enable: true,
|
||||
};
|
||||
this.embySettings.servers.push({
|
||||
|
|
|
@ -35,10 +35,13 @@ export class PlexComponent implements OnInit {
|
|||
password: "",
|
||||
usePlexAdminAccount: true,
|
||||
}).subscribe(y => {
|
||||
if (y) {
|
||||
if (y.result) {
|
||||
this.router.navigate(["login"]);
|
||||
} else {
|
||||
this.notificationService.error("Could not get the Plex Admin Information");
|
||||
if(y.errors.length > 0) {
|
||||
this.notificationService.error(y.errors[0]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
@ -47,9 +50,9 @@ export class PlexComponent implements OnInit {
|
|||
}
|
||||
|
||||
public oauth() {
|
||||
this.plexTv.GetPin(this.clientId, "Ombi").subscribe(pin => {
|
||||
this.plexService.oAuth({wizard: true, pin}).subscribe(x => {
|
||||
if(x.url) {
|
||||
this.plexTv.GetPin(this.clientId, "Ombi").subscribe((pin: any) => {
|
||||
this.plexService.oAuth({ wizard: true, pin }).subscribe(x => {
|
||||
if (x.url) {
|
||||
window.location.href = x.url;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { IdentityService, PlexOAuthService, SettingsService } from "../../services";
|
||||
import { IdentityService, PlexOAuthService } from "../../services";
|
||||
import { AuthService } from "./../../auth/auth.service";
|
||||
|
||||
@Component({
|
||||
|
@ -13,7 +13,6 @@ export class PlexOAuthComponent implements OnInit {
|
|||
constructor(private route: ActivatedRoute,
|
||||
private plexOauth: PlexOAuthService,
|
||||
private identityService: IdentityService,
|
||||
private settings: SettingsService,
|
||||
private router: Router,
|
||||
private auth: AuthService) {
|
||||
|
||||
|
@ -25,7 +24,7 @@ export class PlexOAuthComponent implements OnInit {
|
|||
|
||||
public ngOnInit(): void {
|
||||
this.plexOauth.oAuth(this.pinId).subscribe(x => {
|
||||
if(!x.accessToken) {
|
||||
if (!x.accessToken) {
|
||||
return;
|
||||
// RETURN
|
||||
}
|
||||
|
@ -35,33 +34,20 @@ export class PlexOAuthComponent implements OnInit {
|
|||
password: "",
|
||||
usePlexAdminAccount: true,
|
||||
}).subscribe(u => {
|
||||
if (u) {
|
||||
if (u.result) {
|
||||
this.auth.oAuth(this.pinId).subscribe(c => {
|
||||
localStorage.setItem("id_token", c.access_token);
|
||||
|
||||
// Mark that we have done the settings now
|
||||
this.settings.getOmbi().subscribe(ombi => {
|
||||
ombi.wizard = true;
|
||||
|
||||
this.settings.saveOmbi(ombi).subscribe(s => {
|
||||
this.settings.getUserManagementSettings().subscribe(usr => {
|
||||
|
||||
usr.importPlexAdmin = true;
|
||||
this.settings.saveUserManagementSettings(usr).subscribe(saved => {
|
||||
this.router.navigate(["login"]);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
//this.notificationService.error("Could not get the Plex Admin Information");
|
||||
|
||||
if(u.errors.length > 0) {
|
||||
console.log(u.errors[0]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
import "rxjs/add/operator/catch";
|
||||
import "rxjs/add/operator/map";
|
|
@ -1,5 +1,6 @@
|
|||
import * as Pace from "pace-progress";
|
||||
// Main
|
||||
|
||||
import * as Pace from "pace-progress";
|
||||
Pace.start();
|
||||
|
||||
import "bootstrap/dist/js/bootstrap";
|
||||
|
@ -11,8 +12,6 @@ import "./polyfills";
|
|||
|
||||
import "hammerjs";
|
||||
|
||||
import "./imports";
|
||||
|
||||
import { enableProdMode } from "@angular/core";
|
||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||
import { AppModule } from "./app/app.module";
|
||||
|
@ -29,7 +28,9 @@ if (module.hot) {
|
|||
oldRootElem.parentNode.insertBefore(newRootElem, oldRootElem);
|
||||
oldRootElem.parentNode.removeChild(oldRootElem);
|
||||
}
|
||||
if (modulePromise) {
|
||||
modulePromise.then(appModule => appModule.destroy());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
enableProdMode();
|
||||
|
|
|
@ -9,6 +9,7 @@ declare var module: any;
|
|||
|
||||
if (module.hot) {
|
||||
Error.stackTraceLimit = Infinity;
|
||||
// tslint:disable:no-var-requires
|
||||
|
||||
// tslint:disable-next-line
|
||||
require("zone.js/dist/long-stack-trace-zone");
|
||||
}
|
||||
|
|
|
@ -24,3 +24,7 @@ $bg-colour-disabled: #252424;
|
|||
background: $primary-colour !important;
|
||||
color: white;
|
||||
}*/
|
||||
|
||||
.label {
|
||||
margin: 3px;
|
||||
}
|
|
@ -455,7 +455,7 @@ $border-radius: 10px;
|
|||
}
|
||||
|
||||
.checkbox input[type=checkbox] {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.checkbox input[type=checkbox]:checked + label:before {
|
||||
|
@ -883,11 +883,6 @@ textarea {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.ui-treetable-toggler.fa.fa-fw.ui-clickable.fa-caret-right,
|
||||
.ui-treetable-toggler.fa.fa-fw.ui-clickable.fa-caret-down {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-state-highlight {
|
||||
background: $primary-colour;
|
||||
}
|
||||
|
@ -900,15 +895,6 @@ textarea {
|
|||
border: 1px solid $form-color-lighter;
|
||||
}
|
||||
|
||||
.ui-treetable tfoot td, .ui-treetable th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ui-treetable tbody td {
|
||||
white-space: inherit;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
table a:not(.btn) {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ using Ombi.Settings.Settings.Models.External;
|
|||
|
||||
namespace Ombi.Controllers.External
|
||||
{
|
||||
[Admin]
|
||||
[PowerUser]
|
||||
[ApiV1]
|
||||
[Produces("application/json")]
|
||||
public class SonarrController : Controller
|
||||
|
@ -55,8 +55,12 @@ namespace Ombi.Controllers.External
|
|||
public async Task<IEnumerable<SonarrProfile>> GetProfiles()
|
||||
{
|
||||
var settings = await SonarrSettings.GetSettingsAsync();
|
||||
if (settings.Enabled)
|
||||
{
|
||||
return await SonarrApi.GetProfiles(settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Sonarr root folders.
|
||||
|
@ -66,7 +70,12 @@ namespace Ombi.Controllers.External
|
|||
public async Task<IEnumerable<SonarrRootFolder>> GetRootFolders()
|
||||
{
|
||||
var settings = await SonarrSettings.GetSettingsAsync();
|
||||
if (settings.Enabled)
|
||||
{
|
||||
return await SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,7 +59,8 @@ namespace Ombi.Controllers
|
|||
IRepository<Issues> issues,
|
||||
IRepository<IssueComments> issueComments,
|
||||
IRepository<NotificationUserId> notificationRepository,
|
||||
IRepository<RequestSubscription> subscriptionRepository)
|
||||
IRepository<RequestSubscription> subscriptionRepository,
|
||||
ISettingsService<UserManagementSettings> umSettings)
|
||||
{
|
||||
UserManager = user;
|
||||
Mapper = mapper;
|
||||
|
@ -79,6 +80,7 @@ namespace Ombi.Controllers
|
|||
OmbiSettings = ombiSettings;
|
||||
_requestSubscriptionRepository = subscriptionRepository;
|
||||
_notificationRepository = notificationRepository;
|
||||
_userManagementSettings = umSettings;
|
||||
}
|
||||
|
||||
private OmbiUserManager UserManager { get; }
|
||||
|
@ -87,6 +89,7 @@ namespace Ombi.Controllers
|
|||
private IEmailProvider EmailProvider { get; }
|
||||
private ISettingsService<EmailNotificationSettings> EmailSettings { get; }
|
||||
private ISettingsService<CustomizationSettings> CustomizationSettings { get; }
|
||||
private readonly ISettingsService<UserManagementSettings> _userManagementSettings;
|
||||
private ISettingsService<OmbiSettings> OmbiSettings { get; }
|
||||
private IWelcomeEmail WelcomeEmail { get; }
|
||||
private IMovieRequestRepository MovieRepo { get; }
|
||||
|
@ -113,13 +116,13 @@ namespace Ombi.Controllers
|
|||
[HttpPost("Wizard")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
[AllowAnonymous]
|
||||
public async Task<bool> CreateWizardUser([FromBody] CreateUserWizardModel user)
|
||||
public async Task<SaveWizardResult> CreateWizardUser([FromBody] CreateUserWizardModel user)
|
||||
{
|
||||
var users = UserManager.Users;
|
||||
if (users.Any(x => !x.UserName.Equals("api", StringComparison.CurrentCultureIgnoreCase)))
|
||||
if (users.Any(x => !x.UserName.Equals("api", StringComparison.InvariantCultureIgnoreCase)))
|
||||
{
|
||||
// No one should be calling this. Only the wizard
|
||||
return false;
|
||||
return new SaveWizardResult{ Result = false, Errors = new List<string> {"Looks like there is an existing user!"} };
|
||||
}
|
||||
|
||||
if (user.UsePlexAdminAccount)
|
||||
|
@ -129,7 +132,7 @@ namespace Ombi.Controllers
|
|||
if (authToken.IsNullOrEmpty())
|
||||
{
|
||||
_log.LogWarning("Could not find an auth token to create the plex user with");
|
||||
return false;
|
||||
return new SaveWizardResult { Result = false };
|
||||
}
|
||||
var plexUser = await _plexApi.GetAccount(authToken);
|
||||
var adminUser = new OmbiUser
|
||||
|
@ -140,6 +143,11 @@ namespace Ombi.Controllers
|
|||
ProviderUserId = plexUser.user.id
|
||||
};
|
||||
|
||||
await _userManagementSettings.SaveSettingsAsync(new UserManagementSettings
|
||||
{
|
||||
ImportPlexAdmin = true
|
||||
});
|
||||
|
||||
return await SaveWizardUser(user, adminUser);
|
||||
}
|
||||
|
||||
|
@ -152,9 +160,10 @@ namespace Ombi.Controllers
|
|||
return await SaveWizardUser(user, userToCreate);
|
||||
}
|
||||
|
||||
private async Task<bool> SaveWizardUser(CreateUserWizardModel user, OmbiUser userToCreate)
|
||||
private async Task<SaveWizardResult> SaveWizardUser(CreateUserWizardModel user, OmbiUser userToCreate)
|
||||
{
|
||||
IdentityResult result;
|
||||
var retVal = new SaveWizardResult();
|
||||
// When creating the admin as the plex user, we do not pass in the password.
|
||||
if (user.Password.HasValue())
|
||||
{
|
||||
|
@ -182,6 +191,7 @@ namespace Ombi.Controllers
|
|||
if (!result.Succeeded)
|
||||
{
|
||||
LogErrors(result);
|
||||
retVal.Errors.AddRange(result.Errors.Select(x => x.Description));
|
||||
}
|
||||
|
||||
// Update the wizard flag
|
||||
|
@ -189,7 +199,8 @@ namespace Ombi.Controllers
|
|||
settings.Wizard = true;
|
||||
await OmbiSettings.SaveSettingsAsync(settings);
|
||||
|
||||
return result.Succeeded;
|
||||
retVal.Result = result.Succeeded;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void LogErrors(IdentityResult result)
|
||||
|
|
|
@ -240,6 +240,18 @@ namespace Ombi.Controllers
|
|||
|
||||
return await _issueComments.Add(newComment);
|
||||
}
|
||||
/// <summary>
|
||||
/// Deletes a comment on a issue
|
||||
/// </summary>
|
||||
[HttpDelete("comments/{id:int}")]
|
||||
[PowerUser]
|
||||
public async Task<bool> DeleteComment(int commentId)
|
||||
{
|
||||
var comment = await _issueComments.GetAll().FirstOrDefaultAsync(x => x.Id == commentId);
|
||||
|
||||
await _issueComments.Delete(comment);
|
||||
return true;
|
||||
}
|
||||
|
||||
[HttpPost("status")]
|
||||
public async Task<bool> UpdateStatus([FromBody] IssueStateViewModel model)
|
||||
|
|
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using System.Diagnostics;
|
||||
using Ombi.Attributes;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Models;
|
||||
using Ombi.Store.Entities;
|
||||
|
@ -93,6 +94,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="requestId">The request identifier.</param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("movie/{requestId:int}")]
|
||||
[PowerUser]
|
||||
public async Task DeleteRequest(int requestId)
|
||||
{
|
||||
await MovieRequestEngine.RemoveMovieRequest(requestId);
|
||||
|
@ -104,6 +106,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("movie")]
|
||||
[PowerUser]
|
||||
public async Task<MovieRequests> UpdateRequest([FromBody] MovieRequests model)
|
||||
{
|
||||
return await MovieRequestEngine.UpdateMovieRequest(model);
|
||||
|
@ -115,6 +118,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("movie/approve")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> ApproveMovie([FromBody] MovieUpdateModel model)
|
||||
{
|
||||
return await MovieRequestEngine.ApproveMovieById(model.Id);
|
||||
|
@ -126,6 +130,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("movie/available")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> MarkMovieAvailable([FromBody] MovieUpdateModel model)
|
||||
{
|
||||
return await MovieRequestEngine.MarkAvailable(model.Id);
|
||||
|
@ -137,6 +142,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("movie/unavailable")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> MarkMovieUnAvailable([FromBody] MovieUpdateModel model)
|
||||
{
|
||||
return await MovieRequestEngine.MarkUnavailable(model.Id);
|
||||
|
@ -148,23 +154,12 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("movie/deny")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> DenyMovie([FromBody] MovieUpdateModel model)
|
||||
{
|
||||
return await MovieRequestEngine.DenyMovieById(model.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tv requests.
|
||||
/// </summary>
|
||||
/// <param name="count">The count of items you want to return.</param>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("tv/{count:int}/{position:int}/tree")]
|
||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetTvRequestsTree(int count, int position)
|
||||
{
|
||||
return await TvRequestEngine.GetRequestsTreeNode(count, position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total amount of TV requests.
|
||||
/// </summary>
|
||||
|
@ -267,23 +262,13 @@ namespace Ombi.Controllers
|
|||
return await TvRequestEngine.SearchTvRequest(searchTerm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a specific tv request
|
||||
/// </summary>
|
||||
/// <param name="searchTerm">The search term.</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("tv/search/{searchTerm}/tree")]
|
||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvTree(string searchTerm)
|
||||
{
|
||||
return await TvRequestEngine.SearchTvRequestTree(searchTerm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the a specific tv request
|
||||
/// </summary>
|
||||
/// <param name="requestId">The request identifier.</param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("tv/{requestId:int}")]
|
||||
[PowerUser]
|
||||
public async Task DeleteTvRequest(int requestId)
|
||||
{
|
||||
await TvRequestEngine.RemoveTvRequest(requestId);
|
||||
|
@ -295,17 +280,47 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The model.</param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("tv")]
|
||||
[PowerUser]
|
||||
public async Task<TvRequests> UpdateRequest([FromBody] TvRequests model)
|
||||
{
|
||||
return await TvRequestEngine.UpdateTvRequest(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the root path for this tv show
|
||||
/// </summary>
|
||||
/// <param name="requestId"></param>
|
||||
/// <param name="rootFolderId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("tv/root/{requestId:int}/{rootFolderId:int}")]
|
||||
[PowerUser]
|
||||
public async Task<bool> UpdateRootFolder(int requestId, int rootFolderId)
|
||||
{
|
||||
await TvRequestEngine.UpdateRootPath(requestId, rootFolderId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the quality profile for this tv show
|
||||
/// </summary>
|
||||
/// <param name="requestId"></param>
|
||||
/// <param name="qualityId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("tv/quality/{requestId:int}/{qualityId:int}")]
|
||||
[PowerUser]
|
||||
public async Task<bool> UpdateQuality(int requestId, int qualityId)
|
||||
{
|
||||
await TvRequestEngine.UpdateQualityProfile(requestId, qualityId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the a specific child request
|
||||
/// </summary>
|
||||
/// <param name="child">The model.</param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("tv/child")]
|
||||
[PowerUser]
|
||||
public async Task<ChildRequests> UpdateChild([FromBody] ChildRequests child)
|
||||
{
|
||||
return await TvRequestEngine.UpdateChildRequest(child);
|
||||
|
@ -317,6 +332,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">This is the child request's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("tv/deny")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> DenyChild([FromBody] TvUpdateModel model)
|
||||
{
|
||||
return await TvRequestEngine.DenyChildRequest(model.Id);
|
||||
|
@ -328,6 +344,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("tv/available")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> MarkTvAvailable([FromBody] TvUpdateModel model)
|
||||
{
|
||||
return await TvRequestEngine.MarkAvailable(model.Id);
|
||||
|
@ -339,6 +356,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("tv/unavailable")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> MarkTvUnAvailable([FromBody] TvUpdateModel model)
|
||||
{
|
||||
return await TvRequestEngine.MarkUnavailable(model.Id);
|
||||
|
@ -350,6 +368,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">This is the child request's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("tv/approve")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> ApproveChild([FromBody] TvUpdateModel model)
|
||||
{
|
||||
return await TvRequestEngine.ApproveChildRequest(model.Id);
|
||||
|
@ -360,6 +379,7 @@ namespace Ombi.Controllers
|
|||
/// </summary>
|
||||
/// <param name="requestId">The model.</param>
|
||||
/// <returns></returns>
|
||||
[PowerUser]
|
||||
[HttpDelete("tv/child/{requestId:int}")]
|
||||
public async Task<bool> DeleteChildRequest(int requestId)
|
||||
{
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue