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