mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-14 02:26:55 -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**
|
||||
|
||||
|
|
27
build.cake
27
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);
|
||||
.Does(() => {
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
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.RootFolder = rootPath;
|
||||
|
||||
await TvRepository.Update(results);
|
||||
}
|
||||
|
||||
public async Task UpdateQualityProfile(int requestId, int profileId)
|
||||
{
|
||||
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,29 +565,7 @@ 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)
|
||||
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;
|
||||
|
||||
var existingSeason =
|
||||
result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
|
||||
if (existingSeason == null)
|
||||
{
|
||||
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.
|
||||
var existingSeason =
|
||||
result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
|
||||
if (existingSeason == null)
|
||||
|
||||
if (!existingSeason.monitored)
|
||||
{
|
||||
Logger.LogError("The sonarr ep count was the same as out request count, but could not match the season number {0}", season.SeasonNumber);
|
||||
continue;
|
||||
existingSeason.monitored = true;
|
||||
seriesChanges = true;
|
||||
}
|
||||
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>
|
||||
|
|
|
@ -33,20 +33,20 @@ export class AppComponent implements OnInit {
|
|||
private readonly jobService: JobService,
|
||||
public readonly translate: TranslateService,
|
||||
private readonly identityService: IdentityService,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
|
||||
const base = this.platformLocation.getBaseHrefFromDOM();
|
||||
const base = this.platformLocation.getBaseHrefFromDOM();
|
||||
if (base.length > 1) {
|
||||
__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),
|
||||
|
@ -89,18 +97,12 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
|
|||
CaptchaModule,
|
||||
TooltipModule,
|
||||
ConfirmDialogModule,
|
||||
CommonModule,
|
||||
CommonModule,
|
||||
JwtModule.forRoot({
|
||||
config: {
|
||||
tokenGetter: () => {
|
||||
const token = localStorage.getItem("id_token");
|
||||
if (!token) {
|
||||
return "";
|
||||
}
|
||||
return token;
|
||||
},
|
||||
tokenGetter: JwtTokenGetter,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
|
@ -119,7 +121,7 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
|
|||
TokenResetPasswordComponent,
|
||||
CookieComponent,
|
||||
LoginOAuthComponent,
|
||||
],
|
||||
],
|
||||
providers: [
|
||||
NotificationService,
|
||||
AuthService,
|
||||
|
|
|
@ -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";
|
||||
|
@ -26,13 +26,13 @@ export class AuthService extends ServiceHelpers {
|
|||
return this.http.post<boolean>(`${this.url}/requirePassword`, JSON.stringify(login), {headers: this.headers});
|
||||
}
|
||||
|
||||
public loggedIn() {
|
||||
public loggedIn() {
|
||||
const token: string = this.jwtHelperService.tokenGetter();
|
||||
|
||||
|
||||
if (!token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const tokenExpired: boolean = this.jwtHelperService.isTokenExpired(token);
|
||||
return !tokenExpired;
|
||||
}
|
||||
|
@ -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"]);
|
||||
|
|
|
@ -20,7 +20,7 @@ export interface IRecentlyAddedTvShows extends IRecentlyAddedMovies {
|
|||
|
||||
export interface IRecentlyAddedRangeModel {
|
||||
from: Date;
|
||||
to: Date;
|
||||
to: Date;
|
||||
}
|
||||
|
||||
export enum RecentlyAddedType {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -37,10 +37,10 @@ export class IssueDetailsComponent implements OnInit {
|
|||
private notificationService: NotificationService,
|
||||
private imageService: ImageService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
this.route.params
|
||||
.subscribe((params: any) => {
|
||||
this.issueId = parseInt(params.id);
|
||||
this.issueId = parseInt(params.id);
|
||||
});
|
||||
|
||||
this.isAdmin = this.authService.hasRole("Admin") || this.authService.hasRole("PowerUser");
|
||||
|
@ -53,8 +53,8 @@ export class IssueDetailsComponent implements OnInit {
|
|||
this.defaultPoster = "../../../images/";
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
|
||||
public ngOnInit() {
|
||||
this.issueService.getIssue(this.issueId).subscribe(x => {
|
||||
this.issue = {
|
||||
comments: x.comments,
|
||||
|
@ -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 + ")");
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ export class IssuesComponent implements OnInit {
|
|||
|
||||
constructor(private issueService: IssuesService) { }
|
||||
|
||||
public ngOnInit() {
|
||||
this.getPending();
|
||||
public ngOnInit() {
|
||||
this.getPending();
|
||||
this.getInProg();
|
||||
this.getResolved();
|
||||
this.issueService.getIssuesCount().subscribe(x => this.count = x);
|
||||
|
@ -61,5 +61,4 @@ export class IssuesComponent implements OnInit {
|
|||
this.resolvedIssues = x;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ export class IssuesTableComponent {
|
|||
@Input() public issues: IIssues[];
|
||||
@Input() public totalRecords: number;
|
||||
|
||||
@Output() public changePage = new EventEmitter<IPagenator>();
|
||||
@Output() public changePage = new EventEmitter<IPagenator>();
|
||||
|
||||
public IssueStatus = IssueStatus;
|
||||
|
||||
|
@ -47,7 +47,7 @@ export class IssuesTableComponent {
|
|||
//event.rows = Number of rows to display in new page
|
||||
//event.page = Index of the new page
|
||||
//event.pageCount = Total number of pages
|
||||
|
||||
|
||||
this.changePage.emit(event);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
public landingFlag: boolean;
|
||||
public baseUrl: string;
|
||||
public loginWithOmbi: boolean;
|
||||
|
||||
|
||||
public get appName(): string {
|
||||
if(this.customizationSettings.applicationName) {
|
||||
if (this.customizationSettings.applicationName) {
|
||||
return this.customizationSettings.applicationName;
|
||||
} else {
|
||||
return "Ombi";
|
||||
|
@ -41,7 +41,7 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
|
||||
private timer: any;
|
||||
private clientId: string;
|
||||
|
||||
|
||||
private errorBody: string;
|
||||
private errorValidation: string;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -144,12 +144,12 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
}
|
||||
|
||||
private cycleBackground() {
|
||||
this.images.getRandomBackground().subscribe(x => {
|
||||
this.background = "";
|
||||
});
|
||||
this.images.getRandomBackground().subscribe(x => {
|
||||
this.background = this.sanitizer
|
||||
.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%), url(" + x.url + ")");
|
||||
});
|
||||
this.images.getRandomBackground().subscribe(x => {
|
||||
this.background = "";
|
||||
});
|
||||
this.images.getRandomBackground().subscribe(x => {
|
||||
this.background = this.sanitizer
|
||||
.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%), url(" + x.url + ")");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ export class LoginOAuthComponent implements OnInit {
|
|||
this.route.params
|
||||
.subscribe((params: any) => {
|
||||
this.pin = params.pin;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -26,21 +25,20 @@ 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()) {
|
||||
this.router.navigate(["search"]);
|
||||
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";
|
||||
|
@ -41,13 +41,13 @@ export class RecentlyAddedComponent implements OnInit {
|
|||
public range: Date[];
|
||||
|
||||
public groupTv: boolean = false;
|
||||
|
||||
|
||||
// https://github.com/sheikalthaf/ngu-carousel
|
||||
public carouselTile: NguCarousel;
|
||||
|
||||
public carouselTile: NguCarouselConfig;
|
||||
|
||||
constructor(private recentlyAddedService: RecentlyAddedService,
|
||||
private imageService: ImageService) {}
|
||||
|
||||
|
||||
public ngOnInit() {
|
||||
this.getMovies();
|
||||
this.getShows();
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
@ -97,10 +97,10 @@ export class RecentlyAddedComponent implements OnInit {
|
|||
} else {
|
||||
this.recentlyAddedService.getRecentlyAddedTv().subscribe(x => {
|
||||
this.tv = x;
|
||||
|
||||
|
||||
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",
|
||||
|
@ -40,32 +37,33 @@ export class MovieRequestsComponent implements OnInit {
|
|||
|
||||
public orderType: OrderType = OrderType.RequestedDateDesc;
|
||||
public OrderType = OrderType;
|
||||
|
||||
|
||||
public totalMovies: number = 100;
|
||||
private currentlyLoaded: number;
|
||||
private amountToLoad: number;
|
||||
|
||||
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.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.resetSearch();
|
||||
return;
|
||||
}
|
||||
this.requestService.searchMovieRequests(this.searchText)
|
||||
.subscribe(m => {
|
||||
this.setOverrides(m);
|
||||
this.movieRequests = m;
|
||||
});
|
||||
});
|
||||
constructor(
|
||||
private requestService: RequestService,
|
||||
private auth: AuthService,
|
||||
private notificationService: NotificationService,
|
||||
private radarrService: RadarrService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
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.searchMovieRequests(this.searchText)
|
||||
.subscribe(m => {
|
||||
this.setOverrides(m);
|
||||
this.movieRequests = m;
|
||||
});
|
||||
});
|
||||
this.defaultPoster = "../../../images/default_movie_poster.png";
|
||||
const base = this.platformLocation.getBaseHrefFromDOM();
|
||||
if (base) {
|
||||
|
@ -75,7 +73,7 @@ export class MovieRequestsComponent implements OnInit {
|
|||
|
||||
public ngOnInit() {
|
||||
this.amountToLoad = 10;
|
||||
this.currentlyLoaded = 10;
|
||||
this.currentlyLoaded = 10;
|
||||
this.filter = {
|
||||
availabilityFilter: FilterType.None,
|
||||
statusFilter: FilterType.None,
|
||||
|
@ -85,7 +83,7 @@ export class MovieRequestsComponent implements OnInit {
|
|||
}
|
||||
|
||||
public paginate(event: IPagenator) {
|
||||
const skipAmount = event.first;
|
||||
const skipAmount = event.first;
|
||||
this.loadRequests(this.amountToLoad, skipAmount);
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -173,7 +171,7 @@ export class MovieRequestsComponent implements OnInit {
|
|||
this.filterDisplay = false;
|
||||
this.filter.availabilityFilter = FilterType.None;
|
||||
this.filter.statusFilter = FilterType.None;
|
||||
|
||||
|
||||
this.resetSearch();
|
||||
}
|
||||
|
||||
|
@ -199,11 +197,11 @@ export class MovieRequestsComponent implements OnInit {
|
|||
el.className = "active";
|
||||
|
||||
this.orderType = value;
|
||||
|
||||
|
||||
this.loadInit();
|
||||
}
|
||||
|
||||
public subscribe(request: IMovieRequests) {
|
||||
}
|
||||
|
||||
public subscribe(request: IMovieRequests) {
|
||||
request.subscribed = true;
|
||||
this.requestService.subscribeToMovie(request.id)
|
||||
.subscribe(x => {
|
||||
|
@ -238,10 +236,10 @@ export class MovieRequestsComponent implements OnInit {
|
|||
}
|
||||
|
||||
private loadRequests(amountToLoad: number, currentlyLoaded: number) {
|
||||
this.requestService.getMovieRequests(amountToLoad, currentlyLoaded, this.orderType, this.filter)
|
||||
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;
|
||||
|
@ -364,6 +362,6 @@ export class MovieRequestsComponent implements OnInit {
|
|||
|
||||
private setBackground(req: IMovieRequests): void {
|
||||
req.backgroundPath = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(" + "https://image.tmdb.org/t/p/w1280" + req.background + ")");
|
||||
("url(" + "https://image.tmdb.org/t/p/w1280" + req.background + ")");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -21,17 +21,17 @@ export class TvRequestChildrenComponent {
|
|||
.subscribe(x => {
|
||||
this.removeRequestFromUi(request);
|
||||
this.requestDeleted.emit(request.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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,124 +4,111 @@
|
|||
</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">
|
||||
<div>
|
||||
<div *ngFor="let node of tvRequests.collection">
|
||||
<!--This is the section that holds the parent level results set-->
|
||||
<div>
|
||||
<div class="row">
|
||||
<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>
|
||||
|
||||
<p-column>
|
||||
<ng-template let-col let-node="rowData" pTemplate="header">
|
||||
Results
|
||||
</ng-template>
|
||||
<ng-template let-col let-node="rowData" pTemplate="body">
|
||||
<!--This is the section that holds the parent level results set-->
|
||||
<div *ngIf="!node.leaf">
|
||||
<div class="row">
|
||||
<div class="myBg backdrop" [style.background-image]="node?.data?.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.posterPath || null}}" alt="poster">
|
||||
|
||||
<img class="img-responsive poster" src="{{node.data.posterPath || null}}" alt="poster">
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5 small-padding">
|
||||
<div>
|
||||
<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.status}}</span>
|
||||
</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>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<span>Status: </span>
|
||||
<span class="label label-success">{{node.data.status}}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div>Release Date: {{node.data.releaseDate | date}}</div>
|
||||
<div *ngIf="isAdmin">
|
||||
<div *ngIf="node.data.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }}
|
||||
<span>{{node.data.qualityOverrideTitle}} </span>
|
||||
</div>
|
||||
<div *ngIf="node.data.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' | translate }}
|
||||
<span>{{node.data.rootPathOverrideTitle}} </span>
|
||||
</div>
|
||||
<div>Release Date: {{node.releaseDate | date}}</div>
|
||||
<div *ngIf="isAdmin">
|
||||
<div *ngIf="node.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }}
|
||||
<span>{{node.qualityOverrideTitle}} </span>
|
||||
</div>
|
||||
<div *ngIf="node.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' | translate }}
|
||||
<span>{{node.rootPathOverrideTitle}} </span>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
</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>
|
||||
<div *ngIf="isAdmin">
|
||||
<!--Sonarr Root Folder-->
|
||||
<div *ngIf="sonarrRootFolders" class="btn-group btn-split" id="rootFolderBtn">
|
||||
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ngFor="let folder of sonarrRootFolders">
|
||||
<a href="#" (click)="selectRootFolder(node.data, folder, $event)">{{folder.path}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
<div class="col-sm-3 col-sm-push-3 small-padding">
|
||||
|
||||
<!--Sonarr Quality Profiles -->
|
||||
<div *ngIf="sonarrProfiles" class="btn-group btn-split" id="changeQualityBtn">
|
||||
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ngFor="let profile of sonarrProfiles">
|
||||
<a href="#" (click)="selectQualityProfile(node.data, 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">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||
<span class="caret"></span>
|
||||
<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">
|
||||
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
|
||||
</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>
|
||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ngFor="let folder of sonarrRootFolders">
|
||||
<a href="#" (click)="selectRootFolder(node, folder, $event)">{{folder.path}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!--Sonarr Quality Profiles -->
|
||||
<div *ngIf="sonarrProfiles" class="btn-group btn-split" id="changeQualityBtn">
|
||||
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ngFor="let profile of sonarrProfiles">
|
||||
<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">
|
||||
<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)">{{cat.value}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
||||
</ng-template>
|
||||
</p-column>
|
||||
</p-treeTable>
|
||||
</div>
|
||||
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
|
||||
<div *ngIf="node.open">
|
||||
<tvrequests-children [childRequests]="node.childRequests" [isAdmin]="isAdmin" (requestDeleted)="childRequestDeleted($event)"></tvrequests-children>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
</div>
|
||||
|
||||
<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,90 +38,68 @@ export class TvRequestsComponent implements OnInit {
|
|||
private currentlyLoaded: number;
|
||||
private amountToLoad: number;
|
||||
|
||||
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.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.resetSearch();
|
||||
return;
|
||||
}
|
||||
this.requestService.searchTvRequestsTree(this.searchText)
|
||||
.subscribe(m => {
|
||||
this.tvRequests = m;
|
||||
this.tvRequests.forEach((val) => this.loadBackdrop(val));
|
||||
this.tvRequests.forEach((val) => this.setOverride(val.data));
|
||||
});
|
||||
});
|
||||
this.defaultPoster = "../../../images/default_tv_poster.png";
|
||||
const base = this.platformLocation.getBaseHrefFromDOM();
|
||||
if (base) {
|
||||
this.defaultPoster = "../../.." + base + "/images/default_tv_poster.png";
|
||||
}
|
||||
constructor(
|
||||
private requestService: RequestService,
|
||||
private auth: AuthService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private imageService: ImageService,
|
||||
private sonarrService: SonarrService,
|
||||
private notificationService: NotificationService,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
|
||||
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(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 openClosestTab(node: ITvRequests,el: any) {
|
||||
el.preventDefault();
|
||||
node.open = !node.open;
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
this.amountToLoad = 10;
|
||||
this.currentlyLoaded = 10;
|
||||
this.tvRequests = [];
|
||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||
|
||||
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.searchTvRequests(this.searchText)
|
||||
.subscribe(m => {
|
||||
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";
|
||||
const base = this.platformLocation.getBaseHrefFromDOM();
|
||||
if (base) {
|
||||
this.defaultPoster = "../../.." + base + "/images/default_tv_poster.png";
|
||||
}
|
||||
|
||||
this.loadInit();
|
||||
}
|
||||
|
||||
public paginate(event: IPagenator) {
|
||||
const skipAmount = event.first;
|
||||
|
||||
this.requestService.getTvRequestsTree(this.amountToLoad, skipAmount)
|
||||
.subscribe(x => {
|
||||
this.tvRequests = x;
|
||||
this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad;
|
||||
});
|
||||
this.requestService.getTvRequests(this.amountToLoad, skipAmount, OrderType.RequestedDateDesc, FilterType.None, FilterType.None)
|
||||
.subscribe(x => {
|
||||
this.tvRequests = x;
|
||||
this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad;
|
||||
});
|
||||
}
|
||||
|
||||
public search(text: any) {
|
||||
|
@ -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,13 +142,24 @@ 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");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private setQualityOverrides(req: ITvRequests): void {
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
if(this.isAdmin) {
|
||||
this.sonarrService.getQualityProfilesWithoutSettings()
|
||||
.subscribe(x => this.sonarrProfiles = x);
|
||||
|
||||
this.sonarrService.getRootFoldersWithoutSettings()
|
||||
.subscribe(x => this.sonarrRootFolders = x);
|
||||
}
|
||||
this.setOverride(val);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private resetSearch() {
|
||||
|
@ -228,21 +201,21 @@ 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
|
||||
("url(" + x + ")");
|
||||
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";
|
||||
|
@ -22,7 +20,7 @@ export class MovieSearchComponent implements OnInit {
|
|||
public movieResults: ISearchMovieResult[];
|
||||
public result: IRequestEngineResult;
|
||||
public searchApplied = false;
|
||||
|
||||
|
||||
@Input() public issueCategories: IIssueCategory[];
|
||||
@Input() public issuesEnabled: boolean;
|
||||
public issuesBarVisible = false;
|
||||
|
@ -31,30 +29,31 @@ export class MovieSearchComponent implements OnInit {
|
|||
public issueProviderId: string;
|
||||
public issueCategorySelected: IIssueCategory;
|
||||
public defaultPoster: string;
|
||||
|
||||
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.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.clearResults();
|
||||
return;
|
||||
}
|
||||
this.searchService.searchMovie(this.searchText)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.searchApplied = true;
|
||||
// Now let's load some extra info including IMDB Id
|
||||
// This way the search is fast at displaying results.
|
||||
this.getExtraInfo();
|
||||
});
|
||||
});
|
||||
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.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.searchMovie(this.searchText)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.searchApplied = true;
|
||||
// Now let's load some extra info including IMDB Id
|
||||
// This way the search is fast at displaying results.
|
||||
this.getExtraInfo();
|
||||
});
|
||||
});
|
||||
this.defaultPoster = "../../../images/default_movie_poster.png";
|
||||
const base = this.platformLocation.getBaseHrefFromDOM();
|
||||
if (base) {
|
||||
|
@ -69,7 +68,7 @@ export class MovieSearchComponent implements OnInit {
|
|||
message: "",
|
||||
result: false,
|
||||
errorMessage: "",
|
||||
};
|
||||
};
|
||||
this.popularMovies();
|
||||
}
|
||||
|
||||
|
@ -105,7 +104,7 @@ export class MovieSearchComponent implements OnInit {
|
|||
searchResult.approved = false;
|
||||
searchResult.processed = false;
|
||||
searchResult.requestProcessing = false;
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
|
@ -160,12 +159,12 @@ export class MovieSearchComponent implements OnInit {
|
|||
public similarMovies(theMovieDbId: number) {
|
||||
this.clearResults();
|
||||
this.searchService.similarMovies(theMovieDbId)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.getExtraInfo();
|
||||
});
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.getExtraInfo();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public subscribe(r: ISearchMovieResult) {
|
||||
r.subscribed = true;
|
||||
this.requestService.subscribeToMovie(r.requestId)
|
||||
|
@ -182,17 +181,17 @@ export class MovieSearchComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
private getExtraInfo() {
|
||||
private getExtraInfo() {
|
||||
|
||||
this.movieResults.forEach((val, index) => {
|
||||
if (val.posterPath === null) {
|
||||
val.posterPath = this.defaultPoster;
|
||||
} else {
|
||||
val.posterPath = "https://image.tmdb.org/t/p/w300/" + val.posterPath;
|
||||
}
|
||||
val.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")");
|
||||
this.searchService.getMovieInformation(val.id)
|
||||
this.movieResults.forEach((val, index) => {
|
||||
if (val.posterPath === null) {
|
||||
val.posterPath = this.defaultPoster;
|
||||
} else {
|
||||
val.posterPath = "https://image.tmdb.org/t/p/w300/" + val.posterPath;
|
||||
}
|
||||
val.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")");
|
||||
this.searchService.getMovieInformation(val.id)
|
||||
.subscribe(m => {
|
||||
this.updateItem(val, m);
|
||||
});
|
||||
|
@ -203,7 +202,7 @@ export class MovieSearchComponent implements OnInit {
|
|||
const index = this.movieResults.indexOf(key, 0);
|
||||
if (index > -1) {
|
||||
const copy = { ...this.movieResults[index] };
|
||||
this.movieResults[index] = updated;
|
||||
this.movieResults[index] = updated;
|
||||
this.movieResults[index].background = copy.background;
|
||||
this.movieResults[index].posterPath = copy.posterPath;
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
@ -21,28 +18,29 @@ export class MovieSearchGridComponent implements OnInit {
|
|||
public movieResultGrid: ISearchMovieResultContainer[] = [];
|
||||
public result: IRequestEngineResult;
|
||||
public searchApplied = false;
|
||||
|
||||
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.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.clearResults();
|
||||
return;
|
||||
}
|
||||
this.searchService.searchMovie(this.searchText)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.searchApplied = true;
|
||||
// Now let's load some exta info including IMDBId
|
||||
// This way the search is fast at displaying results.
|
||||
this.getExtaInfo();
|
||||
});
|
||||
});
|
||||
constructor(
|
||||
private searchService: SearchService, private requestService: RequestService,
|
||||
private notificationService: NotificationService, private authService: AuthService) {
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
this.searchService.searchMovie(this.searchText)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.searchApplied = true;
|
||||
// Now let's load some exta info including IMDBId
|
||||
// This way the search is fast at displaying results.
|
||||
this.getExtaInfo();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -129,7 +127,7 @@ export class MovieSearchGridComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
private getExtaInfo() {
|
||||
private getExtaInfo() {
|
||||
this.movieResults.forEach((val) => {
|
||||
this.searchService.getMovieInformation(val.id)
|
||||
.subscribe(m => this.updateItem(val, m));
|
||||
|
@ -147,18 +145,17 @@ export class MovieSearchGridComponent implements OnInit {
|
|||
this.movieResults = [];
|
||||
this.searchApplied = false;
|
||||
}
|
||||
|
||||
|
||||
private processGrid(movies: ISearchMovieResult[]) {
|
||||
let container = <ISearchMovieResultContainer>{ movies: [] };
|
||||
let container = <ISearchMovieResultContainer> { movies: [] };
|
||||
movies.forEach((movie, i) => {
|
||||
i++;
|
||||
if((i % 4) === 0) {
|
||||
container.movies.push(movie);
|
||||
if ((i % 4) === 0) {
|
||||
container.movies.push(movie);
|
||||
this.movieResultGrid.push(container);
|
||||
container = <ISearchMovieResultContainer>{ movies: [] };
|
||||
container = <ISearchMovieResultContainer> { movies: [] };
|
||||
} else {
|
||||
|
||||
container.movies.push(movie);
|
||||
container.movies.push(movie);
|
||||
}
|
||||
});
|
||||
this.movieResultGrid.push(container);
|
||||
|
|
|
@ -25,7 +25,7 @@ const routes: Routes = [
|
|||
{ path: "show/:id", component: SeriesInformationComponent, canActivate: [AuthGuard] },
|
||||
];
|
||||
@NgModule({
|
||||
imports: [
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
RouterModule.forChild(routes),
|
||||
|
|
|
@ -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,25 +38,25 @@ 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});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
viewModel.seasons.push(seasonsViewModel);
|
||||
});
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div class="input-group-addon right-radius">
|
||||
<div class="btn-group">
|
||||
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
{{ 'Search.Suggestions' | translate }}
|
||||
{{ 'Search.Suggestions' | translate }}
|
||||
<i class="fa fa-chevron-down"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
@ -42,129 +42,125 @@
|
|||
<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">
|
||||
<!--This is the section that holds the parent level search results set-->
|
||||
<div *ngIf="!node.leaf">
|
||||
<div class="row" >
|
||||
|
||||
<div class="myBg backdrop" [style.background-image]="node?.data?.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 *ngIf="tvResults" >
|
||||
<div *ngFor="let node of tvResults">
|
||||
<!--This is the section that holds the parent level search results set-->
|
||||
<div *ngIf="node">
|
||||
<div class="row">
|
||||
|
||||
<img *ngIf="node?.data?.banner" class="img-responsive poster" width="150" [src]="node.data.banner" alt="poster">
|
||||
<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>
|
||||
<div class="col-sm-8 small-padding">
|
||||
<div>
|
||||
<img *ngIf="node.banner" class="img-responsive poster" width="150" [src]="node.banner" alt="poster">
|
||||
|
||||
<a *ngIf="node.data.imdbId" href="{{node.data.imdbId}}" target="_blank">
|
||||
<h4>{{node.data.title}} ({{node.data.firstAired | date: 'yyyy'}})</h4>
|
||||
</div>
|
||||
<div class="col-sm-8 small-padding">
|
||||
<div>
|
||||
|
||||
<a *ngIf="node.imdbId" href="{{node.imdbId}}" target="_blank">
|
||||
<h4>{{node.title}} ({{node.firstAired | date: 'yyyy'}})</h4>
|
||||
|
||||
</a>
|
||||
<span class="tags">
|
||||
<a *ngIf="node.homepage" id="homepageLabel" href="{{node.homepage}}" target="_blank">
|
||||
<span class="label label-info">{{ 'Search.Movies.HomePage' | translate }}</span>
|
||||
</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.data.trailer" id="trailerLabel" href="{{node.data.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>
|
||||
<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.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.network" class="label label-info" id="networkLabel" target="_blank">{{node.network}}</span>
|
||||
|
||||
|
||||
<span *ngIf="node.available" class="label label-success" id="availableLabel">{{ 'Common.Available' | translate }}</span>
|
||||
|
||||
<span *ngIf="node.partlyAvailable" class="label label-warning" id="partiallyAvailableLabel">{{ 'Common.PartlyAvailable' | translate }}</span>
|
||||
|
||||
<span *ngIf="node.data.network" class="label label-info" id="networkLabel" target="_blank">{{node.data.network}}</span>
|
||||
|
||||
|
||||
<span *ngIf="node.data.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>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<p class="tv-overview">{{node.data.overview}}</p>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<p class="tv-overview">{{node.overview}}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-2 small-padding">
|
||||
<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, $event)">{{ 'Search.TvShows.AllSeasons' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="firstSeason(node, $event)">{{ 'Search.TvShows.FirstSeason' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="latestSeason(node, $event)">{{ 'Search.TvShows.LatestSeason' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="openClosestTab(node, $event)">{{ 'Search.TvShows.Select' | translate }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-2 small-padding">
|
||||
<div *ngIf="!node.data.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>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="firstSeason(node.data, $event)">{{ 'Search.TvShows.FirstSeason' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="latestSeason(node.data, $event)">{{ 'Search.TvShows.LatestSeason' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="openClosestTab($event)">{{ 'Search.TvShows.Select' | translate }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div *ngIf="node.data.fullyAvailable">
|
||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled>
|
||||
<i class="fa fa-check"></i> {{ 'Common.Available' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<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.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.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>
|
||||
<div class="dropdown" *ngIf="issueCategories && issuesEnabled">
|
||||
<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)">{{cat.value}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div *ngIf="!node.available">
|
||||
<br />
|
||||
<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">
|
||||
<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">
|
||||
<i class="fa fa-eye"></i> {{ 'Search.ViewOnEmby' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="dropdown" *ngIf="issueCategories && issuesEnabled">
|
||||
<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>
|
||||
</ul>
|
||||
</div>
|
||||
<div *ngIf="!node.data.available">
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</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>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
</ng-template>
|
||||
</p-column>
|
||||
</p-treeTable>
|
||||
</div>
|
||||
</div>
|
||||
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
|
||||
<div *ngIf="node.open">
|
||||
<seriesinformation [seriesId]="node.id"></seriesinformation>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
</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,57 +30,37 @@ export class TvSearchComponent implements OnInit {
|
|||
public issueProviderId: string;
|
||||
public issueCategorySelected: IIssueCategory;
|
||||
|
||||
constructor(private searchService: SearchService, private requestService: RequestService,
|
||||
private notificationService: NotificationService, private authService: AuthService,
|
||||
private imageService: ImageService, private sanitizer: DomSanitizer,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
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.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.clearResults();
|
||||
return;
|
||||
}
|
||||
this.searchService.searchTvTreeNode(this.searchText)
|
||||
.subscribe(x => {
|
||||
this.tvResults = x;
|
||||
this.searchApplied = true;
|
||||
this.getExtraInfo();
|
||||
});
|
||||
});
|
||||
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.searchTv(this.searchText)
|
||||
.subscribe(x => {
|
||||
this.tvResults = x;
|
||||
this.searchApplied = true;
|
||||
this.getExtraInfo();
|
||||
});
|
||||
});
|
||||
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.
|
||||
bypassSecurityTrustStyle
|
||||
("url(" + x + ")");
|
||||
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 {
|
||||
|
@ -165,19 +143,19 @@ export class TvSearchComponent implements OnInit {
|
|||
if (this.authService.hasRole("admin") || this.authService.hasRole("AutoApproveMovie")) {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
viewModel.seasons.push(seasonsViewModel);
|
||||
});
|
||||
|
||||
|
@ -223,30 +201,30 @@ 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;
|
||||
}
|
||||
|
||||
if (x.data.imdbId === null) {
|
||||
x.data.imdbId = "https://www.tvmaze.com/shows/" + x.data.seriesId;
|
||||
} else {
|
||||
x.data.imdbId = "http://www.imdb.com/title/" + x.data.imdbId + "/";
|
||||
}
|
||||
private setDefaults(x: ISearchTvResult) {
|
||||
if (x.banner === null) {
|
||||
x.banner = this.defaultPoster;
|
||||
}
|
||||
|
||||
if (x.imdbId === null) {
|
||||
x.imdbId = "https://www.tvmaze.com/shows/" + x.seriesId;
|
||||
} else {
|
||||
x.imdbId = "http://www.imdb.com/title/" + x.imdbId + "/";
|
||||
}
|
||||
}
|
||||
|
||||
private clearResults() {
|
||||
|
|
|
@ -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,19 +2,18 @@
|
|||
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";
|
||||
|
||||
@Injectable()
|
||||
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});
|
||||
}
|
||||
|
@ -52,7 +53,7 @@ export class TesterService extends ServiceHelpers {
|
|||
public emailTest(settings: IEmailNotificationSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}email`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
||||
|
||||
public plexTest(settings: IPlexServer): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}plex`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
@ -67,11 +68,11 @@ export class TesterService extends ServiceHelpers {
|
|||
|
||||
public sonarrTest(settings: ISonarrSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}sonarr`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
||||
public couchPotatoTest(settings: ICouchPotatoSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}couchpotato`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
||||
public telegramTest(settings: ITelegramNotifcationSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}telegram`, JSON.stringify(settings), {headers: this.headers});
|
||||
|
@ -79,10 +80,12 @@ 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,14 +12,14 @@ 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> {
|
||||
return this.http.get<IUser>(this.url, {headers: this.headers});
|
||||
}
|
||||
|
||||
|
||||
public getAccessToken(): Observable<string> {
|
||||
return this.http.get<string>(`${this.url}accesstoken`, {headers: this.headers});
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
@ -20,21 +20,21 @@ export class ImageService extends ServiceHelpers {
|
|||
public getTvBanner(tvdbid: number): Observable<string> {
|
||||
return this.http.get<string>(`${this.url}tv/${tvdbid}`, {headers: this.headers});
|
||||
}
|
||||
|
||||
|
||||
public getMoviePoster(movieDbId: string): Observable<string> {
|
||||
return this.http.get<string>(`${this.url}poster/movie/${movieDbId}`, { headers: this.headers });
|
||||
}
|
||||
|
||||
|
||||
public getTvPoster(tvdbid: number): Observable<string> {
|
||||
return this.http.get<string>(`${this.url}poster/tv/${tvdbid}`, { headers: this.headers });
|
||||
}
|
||||
|
||||
|
||||
public getMovieBackground(movieDbId: string): Observable<string> {
|
||||
return this.http.get<string>(`${this.url}background/movie/${movieDbId}`, { headers: this.headers });
|
||||
}
|
||||
|
||||
|
||||
public getTvBackground(tvdbid: number): Observable<string> {
|
||||
return this.http.get<string>(`${this.url}background/tv/${tvdbid}`, { 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 { 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()
|
||||
|
@ -40,10 +40,10 @@ export class IssuesService extends ServiceHelpers {
|
|||
public createIssue(issue: IIssues): Observable<number> {
|
||||
return this.http.post<number>(this.url, JSON.stringify(issue), {headers: this.headers});
|
||||
}
|
||||
|
||||
|
||||
public getIssue(id: number): Observable<IIssues> {
|
||||
return this.http.get<IIssues>(`${this.url}${id}`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
||||
public getComments(id: number): Observable<IIssuesChat[]> {
|
||||
return this.http.get<IIssuesChat[]>(`${this.url}${id}/comments`, {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 { 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";
|
||||
|
@ -18,7 +18,8 @@ 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";
|
||||
|
||||
|
@ -22,8 +21,8 @@ export class RequestService extends ServiceHelpers {
|
|||
|
||||
public getTotalMovies(): Observable<number> {
|
||||
return this.http.get<number>(`${this.url}Movie/total`, {headers: this.headers});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public getTotalTv(): Observable<number> {
|
||||
return this.http.get<number>(`${this.url}tv/total`, {headers: this.headers});
|
||||
}
|
||||
|
@ -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[]> {
|
||||
|
@ -87,7 +86,7 @@ export class RequestService extends ServiceHelpers {
|
|||
public removeTvRequest(request: ITvRequests) {
|
||||
this.http.delete(`${this.url}tv/${request.id}`, {headers: this.headers}).subscribe();
|
||||
}
|
||||
|
||||
|
||||
public markTvAvailable(movie: ITvUpdateModel): Observable<IRequestEngineResult> {
|
||||
return this.http.post<IRequestEngineResult>(`${this.url}tv/available`, JSON.stringify(movie), {headers: this.headers});
|
||||
}
|
||||
|
@ -102,11 +101,11 @@ export class RequestService extends ServiceHelpers {
|
|||
|
||||
public updateChild(child: IChildRequests): Observable<IChildRequests> {
|
||||
return this.http.put<IChildRequests>(`${this.url}tv/child`, JSON.stringify(child), {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
||||
public denyChild(child: ITvUpdateModel): Observable<IRequestEngineResult> {
|
||||
return this.http.put<IRequestEngineResult>(`${this.url}tv/deny`, JSON.stringify(child), {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
||||
public approveChild(child: ITvUpdateModel): Observable<IRequestEngineResult> {
|
||||
return this.http.post<IRequestEngineResult>(`${this.url}tv/approve`, JSON.stringify(child), {headers: this.headers});
|
||||
|
@ -114,17 +113,23 @@ export class RequestService extends ServiceHelpers {
|
|||
public deleteChild(child: IChildRequests): Observable<boolean> {
|
||||
return this.http.delete<boolean>(`${this.url}tv/child/${child.id}`, {headers: this.headers});
|
||||
}
|
||||
|
||||
|
||||
public subscribeToMovie(requestId: number): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}movie/subscribe/${requestId}`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
public unSubscribeToMovie(requestId: number): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}movie/unsubscribe/${requestId}`, {headers: this.headers});
|
||||
}
|
||||
public subscribeToTv(requestId: number): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}tv/subscribe/${requestId}`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
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,
|
||||
|
@ -185,7 +185,7 @@ export class SettingsService extends ServiceHelpers {
|
|||
public getMobileNotificationSettings(): Observable<IMobileNotifcationSettings> {
|
||||
return this.http.get<IMobileNotifcationSettings>(`${this.url}/notifications/mobile`, {headers: this.headers});
|
||||
}
|
||||
|
||||
|
||||
public saveMobileNotificationSettings(settings: IMobileNotifcationSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}/notifications/mobile`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ export class SettingsService extends ServiceHelpers {
|
|||
|
||||
public getTelegramNotificationSettings(): Observable<ITelegramNotifcationSettings> {
|
||||
return this.http.get<ITelegramNotifcationSettings>(`${this.url}/notifications/telegram`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
||||
public saveTelegramNotificationSettings(settings: ITelegramNotifcationSettings): Observable<boolean> {
|
||||
return this.http
|
||||
|
@ -242,13 +242,13 @@ export class SettingsService extends ServiceHelpers {
|
|||
public saveJobSettings(settings: IJobSettings): Observable<IJobSettingsViewModel> {
|
||||
return this.http
|
||||
.post<IJobSettingsViewModel>(`${this.url}/jobs`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public testCron(body: ICronViewModelBody): Observable<ICronTestModel> {
|
||||
return this.http
|
||||
.post<ICronTestModel>(`${this.url}/testcron`, JSON.stringify(body), {headers: this.headers});
|
||||
}
|
||||
|
||||
|
||||
public getSickRageSettings(): Observable<ISickRageSettings> {
|
||||
return this.http.get<ISickRageSettings>(`${this.url}/sickrage`, {headers: this.headers});
|
||||
}
|
||||
|
@ -273,11 +273,11 @@ export class SettingsService extends ServiceHelpers {
|
|||
|
||||
public getNewsletterSettings(): Observable<INewsletterNotificationSettings> {
|
||||
return this.http.get<INewsletterNotificationSettings>(`${this.url}/notifications/newsletter`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
||||
public updateNewsletterDatabase(): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}/notifications/newsletterdatabase`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
||||
public saveNewsletterSettings(settings: INewsletterNotificationSettings): Observable<boolean> {
|
||||
return this.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 { 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,12 +49,12 @@ export class NewsletterComponent implements OnInit {
|
|||
});
|
||||
|
||||
}
|
||||
public addEmail() {
|
||||
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,11 +19,12 @@ export class PlexComponent implements OnInit, OnDestroy {
|
|||
|
||||
private subscriptions = new Subject<void>();
|
||||
|
||||
constructor(private settingsService: SettingsService,
|
||||
private notificationService: NotificationService,
|
||||
private plexService: PlexService,
|
||||
private testerService: TesterService,
|
||||
private jobService: JobService) { }
|
||||
constructor(
|
||||
private settingsService: SettingsService,
|
||||
private notificationService: NotificationService,
|
||||
private plexService: PlexService,
|
||||
private testerService: TesterService,
|
||||
private jobService: JobService) { }
|
||||
|
||||
public ngOnInit() {
|
||||
this.settingsService.getPlex().subscribe(x => {
|
||||
|
@ -34,17 +33,17 @@ export class PlexComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
public requestServers(server: IPlexServer) {
|
||||
this.plexService.getServers(this.username, this.password)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
if (x.success) {
|
||||
this.loadedServers = x;
|
||||
this.serversButton = true;
|
||||
this.notificationService.success("Found the servers! Please select one!");
|
||||
} else {
|
||||
this.notificationService.warning("Error When Requesting Plex Servers", "Please make sure your username and password are correct");
|
||||
}
|
||||
});
|
||||
this.plexService.getServers(this.username, this.password).pipe(
|
||||
takeUntil(this.subscriptions),
|
||||
).subscribe(x => {
|
||||
if (x.success) {
|
||||
this.loadedServers = x;
|
||||
this.serversButton = true;
|
||||
this.notificationService.success("Found the servers! Please select one!");
|
||||
} else {
|
||||
this.notificationService.warning("Error When Requesting Plex Servers", "Please make sure your username and password are correct");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public selectServer(selectedServer: IPlexServerResponse, server: IPlexServer) {
|
||||
|
@ -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) });
|
||||
|
||||
}
|
||||
|
||||
|
@ -91,19 +90,19 @@ export class PlexComponent implements OnInit, OnDestroy {
|
|||
this.plexService.getLibraries(server).subscribe(x => {
|
||||
server.plexSelectedLibraries = [];
|
||||
if (x.successful) {
|
||||
x.data.mediaContainer.directory.forEach((item) => {
|
||||
const lib: IPlexLibrariesSettings = {
|
||||
key: item.key,
|
||||
title: item.title,
|
||||
enabled: false,
|
||||
};
|
||||
server.plexSelectedLibraries.push(lib);
|
||||
});
|
||||
} else {
|
||||
this.notificationService.error(x.message);
|
||||
}
|
||||
},
|
||||
err => { this.notificationService.error(err); });
|
||||
x.data.mediaContainer.directory.forEach((item) => {
|
||||
const lib: IPlexLibrariesSettings = {
|
||||
key: item.key,
|
||||
title: item.title,
|
||||
enabled: false,
|
||||
};
|
||||
server.plexSelectedLibraries.push(lib);
|
||||
});
|
||||
} else {
|
||||
this.notificationService.error(x.message);
|
||||
}
|
||||
},
|
||||
err => { this.notificationService.error(err); });
|
||||
}
|
||||
|
||||
public save() {
|
||||
|
@ -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");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -54,7 +54,7 @@ export class RadarrComponent implements OnInit {
|
|||
|
||||
this.qualities = [];
|
||||
this.qualities.push({ name: "Please Select", id: -1 });
|
||||
|
||||
|
||||
this.rootFolders = [];
|
||||
this.rootFolders.push({ path: "Please Select", id: -1 });
|
||||
this.minimumAvailabilityOptions = [
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ import { SidebarModule } from "primeng/primeng";
|
|||
CommonModule,
|
||||
],
|
||||
exports: [
|
||||
TranslateModule,
|
||||
CommonModule,
|
||||
TranslateModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
SidebarModule,
|
||||
IssuesReportComponent,
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -13,7 +13,7 @@ import { NotificationService } from "../services";
|
|||
export class UserManagementEditComponent {
|
||||
public user: IUser;
|
||||
public userId: string;
|
||||
|
||||
|
||||
constructor(private identityService: IdentityService,
|
||||
private route: ActivatedRoute,
|
||||
private notificationService: NotificationService,
|
||||
|
@ -45,13 +45,13 @@ export class UserManagementEditComponent {
|
|||
this.notificationService.error(val);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
},
|
||||
reject: () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public resetPassword() {
|
||||
|
|
|
@ -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({
|
||||
|
@ -10,7 +10,7 @@ export class UserManagementComponent implements OnInit {
|
|||
|
||||
public users: IUser[];
|
||||
public checkAll = false;
|
||||
public emailSettings: IEmailNotificationSettings;
|
||||
public emailSettings: IEmailNotificationSettings;
|
||||
public customizationSettings: ICustomizationSettings;
|
||||
|
||||
public order: string = "userName";
|
||||
|
@ -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,13 +45,13 @@ 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");
|
||||
return;
|
||||
}
|
||||
this.identityService.sendWelcomeEmail(user).subscribe();
|
||||
this.identityService.sendWelcomeEmail(user).subscribe();
|
||||
this.notificationService.success(`Sent a welcome email to ${user.emailAddress}`);
|
||||
}
|
||||
|
||||
|
@ -74,31 +74,31 @@ 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]}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
this.notificationService.success(`Updated users`);
|
||||
this.showBulkEdit = false;
|
||||
this.bulkMovieLimit = undefined;
|
||||
this.bulkEpisodeLimit = undefined;
|
||||
}
|
||||
|
||||
|
||||
public setOrder(value: string, el: any) {
|
||||
el = el.toElement || el.relatedTarget || el.target || el.srcElement;
|
||||
|
||||
|
@ -115,7 +115,7 @@ export class UserManagementComponent implements OnInit {
|
|||
previousFilter.className = "";
|
||||
el.className = "active";
|
||||
}
|
||||
|
||||
|
||||
this.order = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -29,16 +29,19 @@ export class PlexComponent implements OnInit {
|
|||
this.notificationService.error("Username or password was incorrect. Could not authenticate with Plex.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.identityService.createWizardUser({
|
||||
username: "",
|
||||
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) {
|
||||
|
||||
|
@ -22,46 +21,33 @@ export class PlexOAuthComponent implements OnInit {
|
|||
this.pinId = params.pin;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.plexOauth.oAuth(this.pinId).subscribe(x => {
|
||||
if(!x.accessToken) {
|
||||
if (!x.accessToken) {
|
||||
return;
|
||||
// RETURN
|
||||
}
|
||||
|
||||
|
||||
this.identityService.createWizardUser({
|
||||
username: "",
|
||||
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"]);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
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);
|
||||
}
|
||||
modulePromise.then(appModule => appModule.destroy());
|
||||
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;
|
||||
}
|
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