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
|
||||
|
||||
## (unreleased)
|
||||
## v3.0.3477 (2018-07-18)
|
||||
|
||||
### **New Features**
|
||||
|
||||
|
|
25
build.cake
25
build.cake
|
@ -1,10 +1,10 @@
|
|||
|
||||
#tool "nuget:?package=GitVersion.CommandLine"
|
||||
#addin "Cake.Gulp"
|
||||
#addin "nuget:?package=Cake.Npm&version=0.13.0"
|
||||
#addin "SharpZipLib"
|
||||
#addin nuget:?package=Cake.Compression&version=0.1.4
|
||||
#addin "Cake.Incubator"
|
||||
#addin "Cake.Yarn"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// ARGUMENTS
|
||||
|
@ -122,36 +122,19 @@ Task("SetVersionInfo")
|
|||
|
||||
Task("NPM")
|
||||
.Does(() => {
|
||||
var settings = new NpmInstallSettings {
|
||||
LogLevel = NpmLogLevel.Silent,
|
||||
WorkingDirectory = webProjDir,
|
||||
Production = true
|
||||
};
|
||||
|
||||
NpmInstall(settings);
|
||||
Yarn.FromPath(webProjDir).Install();
|
||||
});
|
||||
|
||||
Task("Gulp Publish")
|
||||
.IsDependentOn("NPM")
|
||||
.Does(() => {
|
||||
|
||||
var runScriptSettings = new NpmRunScriptSettings {
|
||||
ScriptName="publish",
|
||||
WorkingDirectory = webProjDir,
|
||||
};
|
||||
|
||||
NpmRunScript(runScriptSettings);
|
||||
Yarn.FromPath(webProjDir).RunScript("publish");
|
||||
});
|
||||
|
||||
Task("TSLint")
|
||||
.Does(() =>
|
||||
{
|
||||
var settings = new NpmRunScriptSettings {
|
||||
WorkingDirectory = webProjDir,
|
||||
ScriptName = "lint"
|
||||
};
|
||||
|
||||
NpmRunScript(settings);
|
||||
Yarn.FromPath(webProjDir).RunScript("lint");
|
||||
});
|
||||
|
||||
Task("PrePublish")
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.4.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -6,6 +6,30 @@ namespace Ombi.Api.Sonarr.Models
|
|||
{
|
||||
public class Episode
|
||||
{
|
||||
public Episode()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Episode(Episode ep)
|
||||
{
|
||||
seriesId = ep.seriesId;
|
||||
episodeFileId = ep.episodeFileId;
|
||||
seasonNumber = ep.seasonNumber;
|
||||
episodeNumber = ep.episodeNumber;
|
||||
title = ep.title;
|
||||
airDate = ep.airDate;
|
||||
airDateUtc = ep.airDateUtc;
|
||||
overview = ep.overview;
|
||||
hasFile = ep.hasFile;
|
||||
monitored = ep.monitored;
|
||||
unverifiedSceneNumbering = ep.unverifiedSceneNumbering;
|
||||
id = ep.id;
|
||||
absoluteEpisodeNumber = ep.absoluteEpisodeNumber;
|
||||
sceneAbsoluteEpisodeNumber = ep.sceneAbsoluteEpisodeNumber;
|
||||
sceneEpisodeNumber = ep.sceneEpisodeNumber;
|
||||
sceneSeasonNumber = ep.sceneSeasonNumber;
|
||||
}
|
||||
public int seriesId { get; set; }
|
||||
public int episodeFileId { get; set; }
|
||||
public int seasonNumber { get; set; }
|
||||
|
@ -27,6 +51,24 @@ namespace Ombi.Api.Sonarr.Models
|
|||
|
||||
public class Episodefile
|
||||
{
|
||||
public Episodefile()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Episodefile(Episodefile e)
|
||||
{
|
||||
seriesId = e.seriesId;
|
||||
seasonNumber = e.seasonNumber;
|
||||
relativePath = e.relativePath;
|
||||
path = e.path;
|
||||
size = e.size;
|
||||
dateAdded = e.dateAdded;
|
||||
sceneName = e.sceneName;
|
||||
quality = new EpisodeQuality(e.quality);
|
||||
qualityCutoffNotMet = e.qualityCutoffNotMet;
|
||||
id = e.id;
|
||||
}
|
||||
public int seriesId { get; set; }
|
||||
public int seasonNumber { get; set; }
|
||||
public string relativePath { get; set; }
|
||||
|
@ -41,12 +83,32 @@ namespace Ombi.Api.Sonarr.Models
|
|||
|
||||
public class EpisodeQuality
|
||||
{
|
||||
public EpisodeQuality()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EpisodeQuality(EpisodeQuality e)
|
||||
{
|
||||
quality = new Quality(e.quality);
|
||||
revision = new Revision(e.revision);
|
||||
}
|
||||
public Quality quality { get; set; }
|
||||
public Revision revision { get; set; }
|
||||
}
|
||||
|
||||
public class Revision
|
||||
{
|
||||
public Revision()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Revision(Revision r)
|
||||
{
|
||||
version = r.version;
|
||||
real = r.real;
|
||||
}
|
||||
public int version { get; set; }
|
||||
public int real { get; set; }
|
||||
}
|
||||
|
|
|
@ -2,6 +2,16 @@ namespace Ombi.Api.Sonarr.Models
|
|||
{
|
||||
public class Quality
|
||||
{
|
||||
public Quality()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Quality(Quality q)
|
||||
{
|
||||
id = q.id;
|
||||
name = q.name;
|
||||
}
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
|
|
@ -29,7 +29,10 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
{
|
||||
ProviderId = "123"
|
||||
});
|
||||
var search = new SearchMovieViewModel();
|
||||
var search = new SearchMovieViewModel()
|
||||
{
|
||||
TheMovieDbId = "123",
|
||||
};
|
||||
var result = await Rule.Execute(search);
|
||||
|
||||
Assert.True(result.Success);
|
||||
|
|
|
@ -15,13 +15,13 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
Task<RequestEngineResult> DenyChildRequest(int requestId);
|
||||
Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type);
|
||||
Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
|
||||
Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search);
|
||||
Task<TvRequests> UpdateTvRequest(TvRequests request);
|
||||
Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position);
|
||||
Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId);
|
||||
Task<ChildRequests> UpdateChildRequest(ChildRequests request);
|
||||
Task RemoveTvChild(int requestId);
|
||||
Task<RequestEngineResult> ApproveChildRequest(int id);
|
||||
Task<IEnumerable<TvRequests>> GetRequestsLite();
|
||||
Task UpdateQualityProfile(int requestId, int profileId);
|
||||
Task UpdateRootPath(int requestId, int rootPath);
|
||||
}
|
||||
}
|
|
@ -7,16 +7,10 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
public interface ITvSearchEngine
|
||||
{
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm);
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> SearchTreeNode(string searchTerm);
|
||||
Task<TreeNode<SearchTvShowViewModel>> GetShowInformationTreeNode(int tvdbid);
|
||||
Task<SearchTvShowViewModel> GetShowInformation(int tvdbid);
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> PopularTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Popular();
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> AnticipatedTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Anticipated();
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatchesTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> MostWatches();
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> TrendingTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Trending();
|
||||
}
|
||||
}
|
|
@ -452,6 +452,7 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
|
||||
request.Available = true;
|
||||
request.MarkedAsAvailable = DateTime.Now;
|
||||
NotificationHelper.Notify(request, NotificationType.RequestAvailable);
|
||||
await MovieRepository.Update(request);
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
||||
public class TreeNode<T>
|
||||
{
|
||||
public string Label { get; set; }
|
||||
public T Data { get; set; }
|
||||
public List<TreeNode<T>> Children { get; set; }
|
||||
public bool Leaf { get; set; }
|
||||
public bool Expanded { get; set; }
|
||||
}
|
||||
|
||||
public class TreeNode<T,U>
|
||||
{
|
||||
public string Label { get; set; }
|
||||
public T Data { get; set; }
|
||||
public List<TreeNode<U>> Children { get; set; }
|
||||
public bool Leaf { get; set; }
|
||||
public bool Expanded { get; set; }
|
||||
}
|
||||
}
|
|
@ -171,24 +171,30 @@ namespace Ombi.Core.Engine
|
|||
public async Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<TvRequests> allRequests;
|
||||
List<TvRequests> allRequests = null;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = await TvRepository.GetLite(shouldHide.UserId)
|
||||
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
var tv = TvRepository.GetLite(shouldHide.UserId);
|
||||
if (tv.Any() && tv.Select(x => x.ChildRequests).Any())
|
||||
{
|
||||
allRequests = await tv.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)).Skip(position).Take(count).ToListAsync();
|
||||
}
|
||||
|
||||
// Filter out children
|
||||
|
||||
FilterChildren(allRequests, shouldHide);
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests = await TvRepository.GetLite()
|
||||
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
var tv = TvRepository.GetLite();
|
||||
if (tv.Any() && tv.Select(x => x.ChildRequests).Any())
|
||||
{
|
||||
allRequests = await tv.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)).Skip(position).Take(count).ToListAsync();
|
||||
}
|
||||
}
|
||||
if (allRequests == null)
|
||||
{
|
||||
return new RequestsViewModel<TvRequests>();
|
||||
}
|
||||
|
||||
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
|
||||
return new RequestsViewModel<TvRequests>
|
||||
|
@ -196,38 +202,6 @@ namespace Ombi.Core.Engine
|
|||
Collection = allRequests
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<TvRequests> allRequests;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = await TvRepository.Get(shouldHide.UserId)
|
||||
.Include(x => x.ChildRequests)
|
||||
.ThenInclude(x => x.SeasonRequests)
|
||||
.ThenInclude(x => x.Episodes)
|
||||
.Where(x => x.ChildRequests.Any())
|
||||
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
|
||||
FilterChildren(allRequests, shouldHide);
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests = await TvRepository.Get()
|
||||
.Include(x => x.ChildRequests)
|
||||
.ThenInclude(x => x.SeasonRequests)
|
||||
.ThenInclude(x => x.Episodes)
|
||||
.Where(x => x.ChildRequests.Any())
|
||||
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
}
|
||||
|
||||
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
return ParseIntoTreeNode(allRequests);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TvRequests>> GetRequests()
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
|
@ -288,6 +262,10 @@ namespace Ombi.Core.Engine
|
|||
|
||||
private static void FilterChildren(IEnumerable<TvRequests> allRequests, HideResult shouldHide)
|
||||
{
|
||||
if (allRequests == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Filter out children
|
||||
foreach (var t in allRequests)
|
||||
{
|
||||
|
@ -350,21 +328,22 @@ namespace Ombi.Core.Engine
|
|||
return results;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search)
|
||||
public async Task UpdateRootPath(int requestId, int rootPath)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
IQueryable<TvRequests> allRequests;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = TvRepository.Get(shouldHide.UserId);
|
||||
var allRequests = TvRepository.Get();
|
||||
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
results.RootFolder = rootPath;
|
||||
|
||||
await TvRepository.Update(results);
|
||||
}
|
||||
else
|
||||
|
||||
public async Task UpdateQualityProfile(int requestId, int profileId)
|
||||
{
|
||||
allRequests = TvRepository.Get();
|
||||
}
|
||||
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
||||
results.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
return ParseIntoTreeNode(results);
|
||||
var allRequests = TvRepository.Get();
|
||||
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
results.QualityOverride = profileId;
|
||||
|
||||
await TvRepository.Update(results);
|
||||
}
|
||||
|
||||
public async Task<TvRequests> UpdateTvRequest(TvRequests request)
|
||||
|
@ -516,6 +495,7 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
request.Available = true;
|
||||
request.MarkedAsAvailable = DateTime.Now;
|
||||
foreach (var season in request.SeasonRequests)
|
||||
{
|
||||
foreach (var e in season.Episodes)
|
||||
|
@ -585,28 +565,6 @@ namespace Ombi.Core.Engine
|
|||
return await AfterRequest(model.ChildRequests.FirstOrDefault());
|
||||
}
|
||||
|
||||
private static List<TreeNode<TvRequests, List<ChildRequests>>> ParseIntoTreeNode(IEnumerable<TvRequests> result)
|
||||
{
|
||||
var node = new List<TreeNode<TvRequests, List<ChildRequests>>>();
|
||||
|
||||
foreach (var value in result)
|
||||
{
|
||||
node.Add(new TreeNode<TvRequests, List<ChildRequests>>
|
||||
{
|
||||
Data = value,
|
||||
Children = new List<TreeNode<List<ChildRequests>>>
|
||||
{
|
||||
new TreeNode<List<ChildRequests>>
|
||||
{
|
||||
Data = SortEpisodes(value.ChildRequests),
|
||||
Leaf = true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private static List<ChildRequests> SortEpisodes(List<ChildRequests> items)
|
||||
{
|
||||
foreach (var value in items)
|
||||
|
|
|
@ -59,11 +59,6 @@ namespace Ombi.Core.Engine
|
|||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> SearchTreeNode(string searchTerm)
|
||||
{
|
||||
var result = await Search(searchTerm);
|
||||
return result.Select(ParseIntoTreeNode).ToList();
|
||||
}
|
||||
public async Task<SearchTvShowViewModel> GetShowInformation(int tvdbid)
|
||||
{
|
||||
var show = await TvMazeApi.ShowLookupByTheTvDbId(tvdbid);
|
||||
|
@ -116,19 +111,6 @@ namespace Ombi.Core.Engine
|
|||
return await ProcessResult(mapped);
|
||||
}
|
||||
|
||||
public async Task<TreeNode<SearchTvShowViewModel>> GetShowInformationTreeNode(int tvdbid)
|
||||
{
|
||||
var result = await GetShowInformation(tvdbid);
|
||||
return ParseIntoTreeNode(result);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> PopularTree()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
return processed.Select(ParseIntoTreeNode).ToList();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> Popular()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
|
||||
|
@ -136,12 +118,6 @@ namespace Ombi.Core.Engine
|
|||
return processed;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> AnticipatedTree()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
return processed.Select(ParseIntoTreeNode).ToList();
|
||||
}
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> Anticipated()
|
||||
{
|
||||
|
||||
|
@ -150,12 +126,6 @@ namespace Ombi.Core.Engine
|
|||
return processed;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatchesTree()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
return processed.Select(ParseIntoTreeNode).ToList();
|
||||
}
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> MostWatches()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
|
||||
|
@ -163,13 +133,6 @@ namespace Ombi.Core.Engine
|
|||
return processed;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> TrendingTree()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
return processed.Select(ParseIntoTreeNode).ToList();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> Trending()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
|
||||
|
@ -177,22 +140,6 @@ namespace Ombi.Core.Engine
|
|||
return processed;
|
||||
}
|
||||
|
||||
private static TreeNode<SearchTvShowViewModel> ParseIntoTreeNode(SearchTvShowViewModel result)
|
||||
{
|
||||
return new TreeNode<SearchTvShowViewModel>
|
||||
{
|
||||
Data = result,
|
||||
Children = new List<TreeNode<SearchTvShowViewModel>>
|
||||
{
|
||||
new TreeNode<SearchTvShowViewModel>
|
||||
{
|
||||
Data = result, Leaf = true
|
||||
}
|
||||
},
|
||||
Leaf = false
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<SearchTvShowViewModel>> ProcessResults<T>(IEnumerable<T> items)
|
||||
{
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
|
|
98
src/Ombi.Core/Engine/UserStatsEngine.cs
Normal file
98
src/Ombi.Core/Engine/UserStatsEngine.cs
Normal file
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class UserStatsEngine
|
||||
{
|
||||
public UserStatsEngine(OmbiUserManager um, IMovieRequestRepository movieRequest, ITvRequestRepository tvRequest)
|
||||
{
|
||||
_userManager = um;
|
||||
_movieRequest = movieRequest;
|
||||
_tvRequest = tvRequest;
|
||||
}
|
||||
|
||||
private readonly OmbiUserManager _userManager;
|
||||
private readonly IMovieRequestRepository _movieRequest;
|
||||
private readonly ITvRequestRepository _tvRequest;
|
||||
|
||||
public async Task<UserStatsSummary> GetSummary(SummaryRequest request)
|
||||
{
|
||||
/* What do we want?
|
||||
|
||||
This is Per week/month/all time (filter by date)
|
||||
|
||||
1. Total Requests
|
||||
2. Total Movie Requests
|
||||
3. Total Tv Requests
|
||||
4. Total Issues (If enabled)
|
||||
5. Total Requests fufilled (now available)
|
||||
|
||||
Then
|
||||
|
||||
2. Most requested user Movie
|
||||
3. Most requested user tv
|
||||
|
||||
Then
|
||||
|
||||
1.
|
||||
|
||||
*/
|
||||
|
||||
// get all movie requests
|
||||
var movies = _movieRequest.GetWithUser();
|
||||
var filteredMovies = movies.Where(x => x.RequestedDate >= request.From && x.RequestedDate <= request.To);
|
||||
var tv = _tvRequest.GetLite();
|
||||
var children = tv.SelectMany(x =>
|
||||
x.ChildRequests.Where(c => c.RequestedDate >= request.From && c.RequestedDate <= request.To));
|
||||
|
||||
var moviesCount = filteredMovies.CountAsync();
|
||||
var childrenCount = children.CountAsync();
|
||||
var availableMovies =
|
||||
movies.Select(x => x.MarkedAsAvailable >= request.From && x.MarkedAsAvailable <= request.To).CountAsync();
|
||||
var availableChildren = tv.SelectMany(x =>
|
||||
x.ChildRequests.Where(c => c.MarkedAsAvailable >= request.From && c.MarkedAsAvailable <= request.To)).CountAsync();
|
||||
|
||||
var userMovie = filteredMovies.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync();
|
||||
var userTv = children.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync();
|
||||
|
||||
|
||||
return new UserStatsSummary
|
||||
{
|
||||
TotalMovieRequests = await moviesCount,
|
||||
TotalTvRequests = await childrenCount,
|
||||
CompletedRequestsTv = await availableChildren,
|
||||
CompletedRequestsMovies = await availableMovies,
|
||||
MostRequestedUserMovie = (await userMovie).FirstOrDefault().RequestedUser,
|
||||
MostRequestedUserTv = (await userTv).FirstOrDefault().RequestedUser,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class SummaryRequest
|
||||
{
|
||||
public DateTime From { get; set; }
|
||||
public DateTime To { get; set; }
|
||||
}
|
||||
|
||||
public class UserStatsSummary
|
||||
{
|
||||
public int TotalRequests => TotalTvRequests + TotalTvRequests;
|
||||
public int TotalMovieRequests { get; set; }
|
||||
public int TotalTvRequests { get; set; }
|
||||
public int TotalIssues { get; set; }
|
||||
public int CompletedRequestsMovies { get; set; }
|
||||
public int CompletedRequestsTv { get; set; }
|
||||
public int CompletedRequests => CompletedRequestsMovies + CompletedRequestsTv;
|
||||
public OmbiUser MostRequestedUserMovie { get; set; }
|
||||
public OmbiUser MostRequestedUserTv { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -165,25 +165,15 @@ namespace Ombi.Core.Senders
|
|||
titleSlug = model.ParentRequest.Title,
|
||||
addOptions = new AddOptions
|
||||
{
|
||||
ignoreEpisodesWithFiles = true, // There shouldn't be any episodes with files, this is a new season
|
||||
ignoreEpisodesWithoutFiles = true, // We want all missing
|
||||
ignoreEpisodesWithFiles = false, // There shouldn't be any episodes with files, this is a new season
|
||||
ignoreEpisodesWithoutFiles = false, // We want all missing
|
||||
searchForMissingEpisodes = false // we want dont want to search yet. We want to make sure everything is unmonitored/monitored correctly.
|
||||
}
|
||||
};
|
||||
|
||||
// Montitor the correct seasons,
|
||||
// If we have that season in the model then it's monitored!
|
||||
var seasonsToAdd = new List<Season>();
|
||||
for (var i = 0; i < model.ParentRequest.TotalSeasons + 1; i++)
|
||||
{
|
||||
var index = i;
|
||||
var season = new Season
|
||||
{
|
||||
seasonNumber = i,
|
||||
monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0)
|
||||
};
|
||||
seasonsToAdd.Add(season);
|
||||
}
|
||||
var seasonsToAdd = GetSeasonsToCreate(model);
|
||||
newSeries.seasons = seasonsToAdd;
|
||||
var result = await SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri);
|
||||
existingSeries = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri);
|
||||
|
@ -237,7 +227,7 @@ namespace Ombi.Core.Senders
|
|||
{
|
||||
var sonarrEp = sonarrEpList.FirstOrDefault(x =>
|
||||
x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber);
|
||||
if (sonarrEp != null)
|
||||
if (sonarrEp != null && !sonarrEp.monitored)
|
||||
{
|
||||
sonarrEp.monitored = true;
|
||||
episodesToUpdate.Add(sonarrEp);
|
||||
|
@ -245,27 +235,64 @@ namespace Ombi.Core.Senders
|
|||
}
|
||||
}
|
||||
var seriesChanges = false;
|
||||
|
||||
foreach (var season in model.SeasonRequests)
|
||||
{
|
||||
var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber);
|
||||
var sonarrEpCount = sonarrSeason.Count();
|
||||
var ourRequestCount = season.Episodes.Count;
|
||||
|
||||
if (sonarrEpCount == ourRequestCount)
|
||||
{
|
||||
// We have the same amount of requests as all of the episodes in the season.
|
||||
var existingSeason =
|
||||
result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
|
||||
if (existingSeason == null)
|
||||
{
|
||||
Logger.LogError("The sonarr ep count was the same as out request count, but could not match the season number {0}", season.SeasonNumber);
|
||||
Logger.LogError("There was no season numer {0} in Sonarr for title {1}", season.SeasonNumber, model.ParentRequest.Title);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (sonarrEpCount == ourRequestCount)
|
||||
{
|
||||
// We have the same amount of requests as all of the episodes in the season.
|
||||
|
||||
if (!existingSeason.monitored)
|
||||
{
|
||||
existingSeason.monitored = true;
|
||||
seriesChanges = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure this season is set to monitored
|
||||
if (!existingSeason.monitored)
|
||||
{
|
||||
// We need to monitor it, problem being is all episodes will now be monitored
|
||||
// So we need to monior the series but unmonitor every episode
|
||||
// Except the episodes that are already monitored before we update the series (we do not want to unmonitor episodes that are monitored beforehand)
|
||||
existingSeason.monitored = true;
|
||||
var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber);
|
||||
sea.monitored = true;
|
||||
//var previouslyMonitoredEpisodes = sonarrEpList.Where(x =>
|
||||
// x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this
|
||||
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
|
||||
var epToUnmonitor = new List<Episode>();
|
||||
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the orignal member
|
||||
foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList())
|
||||
{
|
||||
//if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber))
|
||||
//{
|
||||
// // This was previously monitored.
|
||||
// continue;
|
||||
//}
|
||||
ep.monitored = false;
|
||||
epToUnmonitor.Add(ep);
|
||||
}
|
||||
|
||||
foreach (var epToUpdate in epToUnmonitor)
|
||||
{
|
||||
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
||||
}
|
||||
}
|
||||
// Now update the episodes that need updating
|
||||
foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber))
|
||||
{
|
||||
|
@ -285,6 +312,24 @@ namespace Ombi.Core.Senders
|
|||
}
|
||||
}
|
||||
|
||||
private static List<Season> GetSeasonsToCreate(ChildRequests model)
|
||||
{
|
||||
// Let's get a list of seasons just incase we need to change it
|
||||
var seasonsToUpdate = new List<Season>();
|
||||
for (var i = 0; i < model.ParentRequest.TotalSeasons + 1; i++)
|
||||
{
|
||||
var index = i;
|
||||
var sea = new Season
|
||||
{
|
||||
seasonNumber = i,
|
||||
monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0)
|
||||
};
|
||||
seasonsToUpdate.Add(sea);
|
||||
}
|
||||
|
||||
return seasonsToUpdate;
|
||||
}
|
||||
|
||||
private async Task<bool> SendToSickRage(ChildRequests model, SickRageSettings settings, string qualityId = null)
|
||||
{
|
||||
var tvdbid = model.ParentRequest.TvDbId;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Humanizer;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
|
@ -39,7 +40,7 @@ namespace Ombi.Notifications
|
|||
RequestedDate = req?.RequestedDate.ToString("D");
|
||||
if (Type.IsNullOrEmpty())
|
||||
{
|
||||
Type = req?.RequestType.ToString();
|
||||
Type = req?.RequestType.Humanize();
|
||||
}
|
||||
Overview = req?.Overview;
|
||||
Year = req?.ReleaseDate.Year.ToString();
|
||||
|
@ -91,7 +92,7 @@ namespace Ombi.Notifications
|
|||
RequestedDate = req?.RequestedDate.ToString("D");
|
||||
if (Type.IsNullOrEmpty())
|
||||
{
|
||||
Type = req?.RequestType.ToString();
|
||||
Type = req?.RequestType.Humanize();
|
||||
}
|
||||
|
||||
Overview = req?.ParentRequest.Overview;
|
||||
|
@ -161,7 +162,7 @@ namespace Ombi.Notifications
|
|||
IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty;
|
||||
NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty;
|
||||
UserName = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty;
|
||||
Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val : string.Empty;
|
||||
Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val.Humanize() : string.Empty;
|
||||
}
|
||||
|
||||
// User Defined
|
||||
|
|
|
@ -89,6 +89,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
_log.LogInformation("We have found the request {0} on Emby, sending the notification", movie?.Title ?? string.Empty);
|
||||
|
||||
movie.Available = true;
|
||||
movie.MarkedAsAvailable = DateTime.Now;
|
||||
if (movie.Available)
|
||||
{
|
||||
var recipient = movie.RequestedUser.Email.HasValue() ? movie.RequestedUser.Email : string.Empty;
|
||||
|
@ -185,6 +186,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
{
|
||||
// We have fulfulled this request!
|
||||
child.Available = true;
|
||||
child.MarkedAsAvailable = DateTime.Now;
|
||||
BackgroundJob.Enqueue(() => _notificationService.Publish(new NotificationOptions
|
||||
{
|
||||
DateTime = DateTime.Now,
|
||||
|
|
|
@ -123,6 +123,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
{
|
||||
// We have fulfulled this request!
|
||||
child.Available = true;
|
||||
child.MarkedAsAvailable = DateTime.Now;
|
||||
_backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions
|
||||
{
|
||||
DateTime = DateTime.Now,
|
||||
|
@ -163,6 +164,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
}
|
||||
|
||||
movie.Available = true;
|
||||
movie.MarkedAsAvailable = DateTime.Now;
|
||||
if (movie.Available)
|
||||
{
|
||||
_backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions
|
||||
|
|
|
@ -8,10 +8,13 @@ namespace Ombi.Store.Entities.Requests
|
|||
{
|
||||
public string Title { get; set; }
|
||||
public bool Approved { get; set; }
|
||||
public DateTime MarkedAsApproved { get; set; }
|
||||
public DateTime RequestedDate { get; set; }
|
||||
public bool Available { get; set; }
|
||||
public DateTime? MarkedAsAvailable { get; set; }
|
||||
public string RequestedUserId { get; set; }
|
||||
public bool? Denied { get; set; }
|
||||
public DateTime MarkedAsDenied { get; set; }
|
||||
public string DeniedReason { get; set; }
|
||||
public RequestType RequestType { get; set; }
|
||||
|
||||
|
|
988
src/Ombi.Store/Migrations/20180730085903_UserStats.Designer.cs
generated
Normal file
988
src/Ombi.Store/Migrations/20180730085903_UserStats.Designer.cs
generated
Normal file
|
@ -0,0 +1,988 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Ombi.Store.Context;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
[DbContext(typeof(OmbiContext))]
|
||||
[Migration("20180730085903_UserStats")]
|
||||
partial class UserStats
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("ProviderKey");
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ApplicationConfiguration");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Audit", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AuditArea");
|
||||
|
||||
b.Property<int>("AuditType");
|
||||
|
||||
b.Property<DateTime>("DateTime");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<string>("User");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Audit");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CouchPotatoCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
|
||||
b.Property<string>("TheMovieDbId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("TvDbId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<string>("Url");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("EmbyContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<string>("EmbyId");
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<string>("ParentId");
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.Property<string>("TheMovieDbId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Content");
|
||||
|
||||
b.Property<string>("SettingsName");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("GlobalSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("Agent");
|
||||
|
||||
b.Property<bool>("Enabled");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<int>("NotificationType");
|
||||
|
||||
b.Property<string>("Subject");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("NotificationTemplates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<string>("PlayerId");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("NotificationUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("Alias");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<string>("EmbyConnectUserId");
|
||||
|
||||
b.Property<int?>("EpisodeRequestLimit");
|
||||
|
||||
b.Property<DateTime?>("LastLoggedIn");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<int?>("MovieRequestLimit");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("ProviderUserId");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserAccessToken");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<int>("UserType");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<int>("GrandparentKey");
|
||||
|
||||
b.Property<int>("Key");
|
||||
|
||||
b.Property<int>("ParentKey");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GrandparentKey");
|
||||
|
||||
b.ToTable("PlexEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("ParentKey");
|
||||
|
||||
b.Property<int>("PlexContentId");
|
||||
|
||||
b.Property<int?>("PlexServerContentId");
|
||||
|
||||
b.Property<int>("SeasonKey");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexServerContentId");
|
||||
|
||||
b.ToTable("PlexSeasonsContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<int>("Key");
|
||||
|
||||
b.Property<string>("Quality");
|
||||
|
||||
b.Property<string>("ReleaseYear");
|
||||
|
||||
b.Property<string>("TheMovieDbId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("TvDbId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<string>("Url");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexServerContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("HasFile");
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RadarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<int>("ContentId");
|
||||
|
||||
b.Property<int>("ContentType");
|
||||
|
||||
b.Property<int?>("EpisodeNumber");
|
||||
|
||||
b.Property<int?>("SeasonNumber");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RecentlyAddedLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Approved");
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<bool?>("Denied");
|
||||
|
||||
b.Property<string>("DeniedReason");
|
||||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<DateTime>("MarkedAsApproved");
|
||||
|
||||
b.Property<DateTime?>("MarkedAsAvailable");
|
||||
|
||||
b.Property<DateTime>("MarkedAsDenied");
|
||||
|
||||
b.Property<int>("ParentRequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
||||
b.Property<int>("SeriesType");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentRequestId");
|
||||
|
||||
b.HasIndex("RequestedUserId");
|
||||
|
||||
b.ToTable("ChildRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("IssueCategory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Comment");
|
||||
|
||||
b.Property<DateTime>("Date");
|
||||
|
||||
b.Property<int?>("IssuesId");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IssuesId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("IssueComments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<int>("IssueCategoryId");
|
||||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
|
||||
b.Property<int?>("RequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<DateTime?>("ResovledDate");
|
||||
|
||||
b.Property<int>("Status");
|
||||
|
||||
b.Property<string>("Subject");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("UserReportedId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IssueCategoryId");
|
||||
|
||||
b.HasIndex("IssueId");
|
||||
|
||||
b.HasIndex("UserReportedId");
|
||||
|
||||
b.ToTable("Issues");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Approved");
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<string>("Background");
|
||||
|
||||
b.Property<bool?>("Denied");
|
||||
|
||||
b.Property<string>("DeniedReason");
|
||||
|
||||
b.Property<DateTime?>("DigitalReleaseDate");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<DateTime>("MarkedAsApproved");
|
||||
|
||||
b.Property<DateTime?>("MarkedAsAvailable");
|
||||
|
||||
b.Property<DateTime>("MarkedAsDenied");
|
||||
|
||||
b.Property<string>("Overview");
|
||||
|
||||
b.Property<string>("PosterPath");
|
||||
|
||||
b.Property<int>("QualityOverride");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
||||
b.Property<int>("RootPathOverride");
|
||||
|
||||
b.Property<string>("Status");
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RequestedUserId");
|
||||
|
||||
b.ToTable("MovieRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("EpisodeCount");
|
||||
|
||||
b.Property<DateTime>("RequestDate");
|
||||
|
||||
b.Property<int>("RequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("RequestLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Background");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<string>("Overview");
|
||||
|
||||
b.Property<string>("PosterPath");
|
||||
|
||||
b.Property<int?>("QualityOverride");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate");
|
||||
|
||||
b.Property<int?>("RootFolder");
|
||||
|
||||
b.Property<string>("Status");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TvRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("RequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("RequestSubscription");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<bool>("HasFile");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Token");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Tokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AirDate");
|
||||
|
||||
b.Property<bool>("Approved");
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<bool>("Requested");
|
||||
|
||||
b.Property<int>("SeasonId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("Url");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SeasonId");
|
||||
|
||||
b.ToTable("EpisodeRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("ChildRequestId");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ChildRequestId");
|
||||
|
||||
b.ToTable("SeasonRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany("NotificationUserIds")
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("GrandparentKey")
|
||||
.HasPrincipalKey("Key")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent")
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexServerContentId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest")
|
||||
.WithMany("ChildRequests")
|
||||
.HasForeignKey("ParentRequestId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("RequestedUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues")
|
||||
.WithMany("Comments")
|
||||
.HasForeignKey("IssuesId");
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("IssueCategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests")
|
||||
.WithMany("Issues")
|
||||
.HasForeignKey("IssueId");
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.Requests.MovieRequests")
|
||||
.WithMany("Issues")
|
||||
.HasForeignKey("IssueId");
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserReportedId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("RequestedUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("SeasonId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest")
|
||||
.WithMany("SeasonRequests")
|
||||
.HasForeignKey("ChildRequestId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
72
src/Ombi.Store/Migrations/20180730085903_UserStats.cs
Normal file
72
src/Ombi.Store/Migrations/20180730085903_UserStats.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
public partial class UserStats : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsApproved",
|
||||
table: "MovieRequests",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsAvailable",
|
||||
table: "MovieRequests",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsDenied",
|
||||
table: "MovieRequests",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsApproved",
|
||||
table: "ChildRequests",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsAvailable",
|
||||
table: "ChildRequests",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "MarkedAsDenied",
|
||||
table: "ChildRequests",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsApproved",
|
||||
table: "MovieRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsAvailable",
|
||||
table: "MovieRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsDenied",
|
||||
table: "MovieRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsApproved",
|
||||
table: "ChildRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsAvailable",
|
||||
table: "ChildRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MarkedAsDenied",
|
||||
table: "ChildRequests");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,9 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
||||
using Ombi.Helpers;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using System;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
|
@ -20,7 +14,7 @@ namespace Ombi.Store.Migrations
|
|||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.0.3-rtm-10026");
|
||||
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
|
@ -481,6 +475,12 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<DateTime>("MarkedAsApproved");
|
||||
|
||||
b.Property<DateTime?>("MarkedAsAvailable");
|
||||
|
||||
b.Property<DateTime>("MarkedAsDenied");
|
||||
|
||||
b.Property<int>("ParentRequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
@ -595,6 +595,12 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<DateTime>("MarkedAsApproved");
|
||||
|
||||
b.Property<DateTime?>("MarkedAsAvailable");
|
||||
|
||||
b.Property<DateTime>("MarkedAsDenied");
|
||||
|
||||
b.Property<string>("Overview");
|
||||
|
||||
b.Property<string>("PosterPath");
|
||||
|
|
23
src/Ombi/.gitignore
vendored
23
src/Ombi/.gitignore
vendored
|
@ -1,23 +1,10 @@
|
|||
/wwwroot/css/**
|
||||
/wwwroot/fonts/**
|
||||
/wwwroot/lib/**
|
||||
/wwwroot/maps/**
|
||||
/wwwroot/dist/**
|
||||
/wwwroot/*.js.map
|
||||
/wwwroot/*.js
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/bower_components
|
||||
|
||||
# misc
|
||||
node_modules
|
||||
bin
|
||||
obj
|
||||
wwwroot/dist
|
||||
*.log
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage/*
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
testem.log
|
||||
#/typings
|
||||
/systemjs.config.js*
|
||||
/Logs/**
|
||||
**.db
|
||||
|
|
15
src/Ombi/.vscode/tasks.json
vendored
15
src/Ombi/.vscode/tasks.json
vendored
|
@ -4,7 +4,7 @@
|
|||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "restore",
|
||||
"label": "restore",
|
||||
"command": "npm",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
|
@ -14,7 +14,16 @@
|
|||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"taskName": "build",
|
||||
"label": "clean",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
"clean"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
|
@ -27,7 +36,7 @@
|
|||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"taskName": "lint",
|
||||
"label": "lint",
|
||||
"type": "shell",
|
||||
"command": "npm",
|
||||
"args": [
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { animate, style, transition, trigger } from "@angular/animations";
|
||||
import { AnimationEntryMetadata } from "@angular/core";
|
||||
import { AnimationTriggerMetadata } from "@angular/animations";
|
||||
|
||||
export const fadeInOutAnimation: AnimationEntryMetadata = trigger("fadeInOut", [
|
||||
export const fadeInOutAnimation: AnimationTriggerMetadata = trigger("fadeInOut", [
|
||||
transition(":enter", [ // :enter is alias to 'void => *'
|
||||
style({ opacity: 0 }),
|
||||
animate(1000, style({ opacity: 1 })),
|
||||
|
|
|
@ -134,6 +134,12 @@
|
|||
<li [ngClass]="{'active': 'no' === translate.currentLang}">
|
||||
<a (click)="translate.use('no')" [translate]="'NavigationBar.Language.Norwegian'"></a>
|
||||
</li>
|
||||
<li [ngClass]="{'active': 'pt' === translate.currentLang}">
|
||||
<a (click)="translate.use('pt')" [translate]="'NavigationBar.Language.BrazillianPortuguese'"></a>
|
||||
</li>
|
||||
<li [ngClass]="{'active': 'pl' === translate.currentLang}">
|
||||
<a (click)="translate.use('pl')" [translate]="'NavigationBar.Language.Polish'"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -40,13 +40,13 @@ export class AppComponent implements OnInit {
|
|||
__webpack_public_path__ = base + "/dist/";
|
||||
}
|
||||
|
||||
this.translate.addLangs(["en", "de", "fr","da","es","it","nl","sv","no"]);
|
||||
this.translate.addLangs(["en", "de", "fr", "da", "es", "it", "nl", "sv", "no", "pl", "pt"]);
|
||||
// this language will be used as a fallback when a translation isn't found in the current language
|
||||
this.translate.setDefaultLang("en");
|
||||
|
||||
// See if we can match the supported langs with the current browser lang
|
||||
const browserLang: string = translate.getBrowserLang();
|
||||
this.translate.use(browserLang.match(/en|fr|da|de|es|it|nl|sv|no/) ? browserLang : "en");
|
||||
this.translate.use(browserLang.match(/en|fr|da|de|es|it|nl|sv|no|pl|pt/) ? browserLang : "en");
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
|
|
|
@ -67,6 +67,14 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
|
|||
return new TranslateHttpLoader(http, "/translations/", `.json?v=${version}`);
|
||||
}
|
||||
|
||||
export function JwtTokenGetter() {
|
||||
const token = localStorage.getItem("id_token");
|
||||
if (!token) {
|
||||
return "";
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot(routes),
|
||||
|
@ -92,13 +100,7 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
|
|||
CommonModule,
|
||||
JwtModule.forRoot({
|
||||
config: {
|
||||
tokenGetter: () => {
|
||||
const token = localStorage.getItem("id_token");
|
||||
if (!token) {
|
||||
return "";
|
||||
}
|
||||
return token;
|
||||
},
|
||||
tokenGetter: JwtTokenGetter,
|
||||
},
|
||||
}),
|
||||
TranslateModule.forRoot({
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { JwtHelperService } from "@auth0/angular-jwt";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../services";
|
||||
import { ILocalUser, IUserLogin } from "./IUserLogin";
|
||||
|
|
|
@ -71,6 +71,10 @@ export interface ITvRequests {
|
|||
status: string;
|
||||
childRequests: IChildRequests[];
|
||||
qualityOverride: number;
|
||||
background: any;
|
||||
totalSeasons: number;
|
||||
tvDbId: number;
|
||||
open: boolean; // THIS IS FOR THE UI
|
||||
|
||||
// For UI display
|
||||
qualityOverrideTitle: string;
|
||||
|
|
|
@ -28,8 +28,16 @@ export interface ISearchTvResult {
|
|||
available: boolean;
|
||||
plexUrl: string;
|
||||
embyUrl: string;
|
||||
quality: string;
|
||||
firstSeason: boolean;
|
||||
latestSeason: boolean;
|
||||
theTvDbId: string;
|
||||
subscribed: boolean;
|
||||
showSubscribe: boolean;
|
||||
fullyAvailable: boolean;
|
||||
partlyAvailable: boolean;
|
||||
background: any;
|
||||
open: boolean; // THIS IS FOR THE UI
|
||||
}
|
||||
|
||||
export interface ITvRequestViewModel {
|
||||
|
|
|
@ -23,6 +23,11 @@ export interface ICreateWizardUser {
|
|||
usePlexAdminAccount: boolean;
|
||||
}
|
||||
|
||||
export interface IWizardUserResult {
|
||||
result: boolean;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export enum UserType {
|
||||
LocalUser = 1,
|
||||
PlexUser = 2,
|
||||
|
|
|
@ -61,5 +61,4 @@ export class IssuesComponent implements OnInit {
|
|||
this.resolvedIssues = x;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
}
|
||||
|
||||
public oauth() {
|
||||
this.plexTv.GetPin(this.clientId, this.appName).subscribe(pin => {
|
||||
this.plexTv.GetPin(this.clientId, this.appName).subscribe((pin: any) => {
|
||||
|
||||
this.authService.login({ usePlexOAuth: true, password: "", rememberMe: true, username: "", plexTvPin: pin }).subscribe(x => {
|
||||
if (window.frameElement) {
|
||||
|
|
|
@ -16,7 +16,6 @@ export class LoginOAuthComponent implements OnInit {
|
|||
this.route.params
|
||||
.subscribe((params: any) => {
|
||||
this.pin = params.pin;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -40,7 +39,6 @@ export class LoginOAuthComponent implements OnInit {
|
|||
|
||||
}, err => {
|
||||
this.notify.error(err.statusText);
|
||||
|
||||
this.router.navigate(["login"]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { NguCarousel } from "@ngu/carousel";
|
||||
import { NguCarouselConfig } from "@ngu/carousel";
|
||||
|
||||
import { ImageService, RecentlyAddedService } from "../services";
|
||||
import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "./../interfaces";
|
||||
|
@ -43,7 +43,7 @@ export class RecentlyAddedComponent implements OnInit {
|
|||
public groupTv: boolean = false;
|
||||
|
||||
// https://github.com/sheikalthaf/ngu-carousel
|
||||
public carouselTile: NguCarousel;
|
||||
public carouselTile: NguCarouselConfig;
|
||||
|
||||
constructor(private recentlyAddedService: RecentlyAddedService,
|
||||
private imageService: ImageService) {}
|
||||
|
|
|
@ -45,8 +45,6 @@
|
|||
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
<div *ngFor="let request of movieRequests">
|
||||
<div class="row">
|
||||
<div class="myBg backdrop" [style.background-image]="request.backgroundPath"></div>
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
import { Subject } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { NotificationService, RadarrService, RequestService } from "../services";
|
||||
|
||||
import { FilterType, IFilter, IIssueCategory, IMovieRequests, IPagenator, IRadarrProfile, IRadarrRootFolder, OrderType } from "../interfaces";
|
||||
import { NotificationService, RadarrService, RequestService } from "../services";
|
||||
|
||||
@Component({
|
||||
selector: "movie-requests",
|
||||
|
@ -45,16 +42,17 @@ export class MovieRequestsComponent implements OnInit {
|
|||
private currentlyLoaded: number;
|
||||
private amountToLoad: number;
|
||||
|
||||
constructor(private requestService: RequestService,
|
||||
constructor(
|
||||
private requestService: RequestService,
|
||||
private auth: AuthService,
|
||||
private notificationService: NotificationService,
|
||||
private radarrService: RadarrService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
).subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.resetSearch();
|
||||
|
|
|
@ -4,59 +4,41 @@
|
|||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<!--TODO: I believe this +1 is causing off by one error skipping loading of tv shows
|
||||
When removed and scrolling very slowly everything works as expected, however
|
||||
if you scroll really quickly then you start getting duplicates of movies
|
||||
since it's async and some subsequent results return first and then incrementer
|
||||
is increased so you see movies which had already been gotten show up...
|
||||
|
||||
Removing infinte-scroll and setting max to 1000 till we work out some sort of fix
|
||||
|
||||
-->
|
||||
<!--<div infinite-scroll
|
||||
[infiniteScrollDistance]="1"
|
||||
[infiniteScrollThrottle]="100"
|
||||
(scrolled)="loadMore()">-->
|
||||
<div>
|
||||
<p-treeTable [value]="tvRequests">
|
||||
|
||||
<p-column>
|
||||
<ng-template let-col let-node="rowData" pTemplate="header">
|
||||
Results
|
||||
</ng-template>
|
||||
<ng-template let-col let-node="rowData" pTemplate="body">
|
||||
<div *ngFor="let node of tvRequests.collection">
|
||||
<!--This is the section that holds the parent level results set-->
|
||||
<div *ngIf="!node.leaf">
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="myBg backdrop" [style.background-image]="node?.data?.background"></div>
|
||||
<div class="myBg backdrop" [style.background-image]="node?.background"></div>
|
||||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
||||
|
||||
<div class="col-sm-2 small-padding">
|
||||
|
||||
<img class="img-responsive poster" src="{{node.data.posterPath || null}}" alt="poster">
|
||||
<img class="img-responsive poster" src="{{node.posterPath || null}}" alt="poster">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5 small-padding">
|
||||
<div>
|
||||
<a href="http://www.imdb.com/title/{{node.data.imdbId}}/" target="_blank">
|
||||
<h4 class="request-title">{{node.data.title}} ({{node.data.releaseDate | date: 'yyyy'}})</h4>
|
||||
<a href="http://www.imdb.com/title/{{node.imdbId}}/" target="_blank">
|
||||
<h4 class="request-title">{{node.title}} ({{node.releaseDate | date: 'yyyy'}})</h4>
|
||||
</a>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<span>Status: </span>
|
||||
<span class="label label-success">{{node.data.status}}</span>
|
||||
<span class="label label-success">{{node.status}}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div>Release Date: {{node.data.releaseDate | date}}</div>
|
||||
<div>Release Date: {{node.releaseDate | date}}</div>
|
||||
<div *ngIf="isAdmin">
|
||||
<div *ngIf="node.data.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }}
|
||||
<span>{{node.data.qualityOverrideTitle}} </span>
|
||||
<div *ngIf="node.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }}
|
||||
<span>{{node.qualityOverrideTitle}} </span>
|
||||
</div>
|
||||
<div *ngIf="node.data.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' | translate }}
|
||||
<span>{{node.data.rootPathOverrideTitle}} </span>
|
||||
<div *ngIf="node.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' | translate }}
|
||||
<span>{{node.rootPathOverrideTitle}} </span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -64,7 +46,8 @@
|
|||
</div>
|
||||
<div class="col-sm-3 col-sm-push-3 small-padding">
|
||||
|
||||
<button style="text-align: right" class="btn btn-sm btn-success-outline" (click)="openClosestTab($event)"><i class="fa fa-plus"></i> View</button>
|
||||
<button style="text-align: right" class="btn btn-sm btn-success-outline" (click)="openClosestTab(node,$event)">
|
||||
<i class="fa fa-plus"></i> View</button>
|
||||
<div *ngIf="isAdmin">
|
||||
<!--Sonarr Root Folder-->
|
||||
<div *ngIf="sonarrRootFolders" class="btn-group btn-split" id="rootFolderBtn">
|
||||
|
@ -77,7 +60,7 @@
|
|||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ngFor="let folder of sonarrRootFolders">
|
||||
<a href="#" (click)="selectRootFolder(node.data, folder, $event)">{{folder.path}}</a>
|
||||
<a href="#" (click)="selectRootFolder(node, folder, $event)">{{folder.path}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -93,35 +76,39 @@
|
|||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ngFor="let profile of sonarrProfiles">
|
||||
<a href="#" (click)="selectQualityProfile(node.data, profile, $event)">{{profile.name}}</a>
|
||||
<a href="#" (click)="selectQualityProfile(node, profile, $event)">{{profile.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issueBtn">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<li *ngFor="let cat of issueCategories"><a [routerLink]="" (click)="reportIssue(cat, node.data)">{{cat.value}}</a></li>
|
||||
<li *ngFor="let cat of issueCategories">
|
||||
<a [routerLink]="" (click)="reportIssue(cat, node)">{{cat.value}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
|
||||
<div *ngIf="node.leaf">
|
||||
<tvrequests-children [childRequests]="node.data" [isAdmin] ="isAdmin"
|
||||
(requestDeleted)="childRequestDeleted($event)"></tvrequests-children>
|
||||
<div *ngIf="node.open">
|
||||
<tvrequests-children [childRequests]="node.childRequests" [isAdmin]="isAdmin" (requestDeleted)="childRequestDeleted($event)"></tvrequests-children>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-column>
|
||||
</p-treeTable>
|
||||
|
||||
<p-paginator [rows]="10" [totalRecords]="totalTv" (onPageChange)="paginate($event)"></p-paginator>
|
||||
</div>
|
||||
|
||||
<issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title"
|
||||
[issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId" (visibleChange)="issuesBarVisible = $event;"></issue-report>
|
||||
<issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title" [issueCategory]="issueCategorySelected"
|
||||
[id]="issueRequest?.id" [providerId]="issueProviderId" (visibleChange)="issuesBarVisible = $event;"></issue-report>
|
|
@ -1,21 +1,13 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
import { ImageService } from "./../services/image.service";
|
||||
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import { Subject } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { FilterType, IIssueCategory, IPagenator, IRequestsViewModel, ISonarrProfile, ISonarrRootFolder, ITvRequests, OrderType } from "../interfaces";
|
||||
import { NotificationService, RequestService, SonarrService } from "../services";
|
||||
|
||||
import { TreeNode } from "primeng/primeng";
|
||||
import { IIssueCategory, IPagenator, ISonarrProfile, ISonarrRootFolder, ITvRequests } from "../interfaces";
|
||||
import { ImageService } from "./../services/image.service";
|
||||
|
||||
@Component({
|
||||
selector: "tv-requests",
|
||||
|
@ -24,7 +16,7 @@ import { IIssueCategory, IPagenator, ISonarrProfile, ISonarrRootFolder, ITvRequ
|
|||
})
|
||||
export class TvRequestsComponent implements OnInit {
|
||||
|
||||
public tvRequests: TreeNode[];
|
||||
public tvRequests: IRequestsViewModel<ITvRequests>;
|
||||
public searchChanged = new Subject<string>();
|
||||
public searchText: string;
|
||||
public isAdmin: boolean;
|
||||
|
@ -46,27 +38,49 @@ export class TvRequestsComponent implements OnInit {
|
|||
private currentlyLoaded: number;
|
||||
private amountToLoad: number;
|
||||
|
||||
constructor(private requestService: RequestService,
|
||||
constructor(
|
||||
private requestService: RequestService,
|
||||
private auth: AuthService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private imageService: ImageService,
|
||||
private sonarrService: SonarrService,
|
||||
private notificationService: NotificationService,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
|
||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||
if (this.isAdmin) {
|
||||
this.sonarrService.getQualityProfilesWithoutSettings()
|
||||
.subscribe(x => this.sonarrProfiles = x);
|
||||
|
||||
this.sonarrService.getRootFoldersWithoutSettings()
|
||||
.subscribe(x => this.sonarrRootFolders = x);
|
||||
}
|
||||
}
|
||||
|
||||
public openClosestTab(node: ITvRequests,el: any) {
|
||||
el.preventDefault();
|
||||
node.open = !node.open;
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
this.amountToLoad = 10;
|
||||
this.currentlyLoaded = 10;
|
||||
this.tvRequests = {collection:[], total:0};
|
||||
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
).subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.resetSearch();
|
||||
return;
|
||||
}
|
||||
this.requestService.searchTvRequestsTree(this.searchText)
|
||||
this.requestService.searchTvRequests(this.searchText)
|
||||
.subscribe(m => {
|
||||
this.tvRequests = m;
|
||||
this.tvRequests.forEach((val) => this.loadBackdrop(val));
|
||||
this.tvRequests.forEach((val) => this.setOverride(val.data));
|
||||
this.tvRequests.collection = m;
|
||||
this.tvRequests.collection.forEach((val) => this.loadBackdrop(val));
|
||||
this.tvRequests.collection.forEach((val) => this.setOverride(val));
|
||||
});
|
||||
});
|
||||
this.defaultPoster = "../../../images/default_tv_poster.png";
|
||||
|
@ -74,50 +88,6 @@ export class TvRequestsComponent implements OnInit {
|
|||
if (base) {
|
||||
this.defaultPoster = "../../.." + base + "/images/default_tv_poster.png";
|
||||
}
|
||||
}
|
||||
|
||||
public openClosestTab(el: any) {
|
||||
const rowclass = "undefined ng-star-inserted";
|
||||
el = el.toElement || el.relatedTarget || el.target || el.srcElement;
|
||||
|
||||
if (el.nodeName === "BUTTON") {
|
||||
|
||||
const isButtonAlreadyActive = el.parentElement.querySelector(".active");
|
||||
// if a Button already has Class: .active
|
||||
if (isButtonAlreadyActive) {
|
||||
isButtonAlreadyActive.classList.remove("active");
|
||||
} else {
|
||||
el.className += " active";
|
||||
}
|
||||
}
|
||||
|
||||
while (el.className !== rowclass) {
|
||||
// Increment the loop to the parent node until we find the row we need
|
||||
el = el.parentNode;
|
||||
}
|
||||
// At this point, the while loop has stopped and `el` represents the element that has
|
||||
// the class you specified
|
||||
|
||||
// Then we loop through the children to find the caret which we want to click
|
||||
const caretright = "fa-caret-right";
|
||||
const caretdown = "fa-caret-down";
|
||||
for (const value of el.children) {
|
||||
// the caret from the ui has 2 class selectors depending on if expanded or not
|
||||
// we search for both since we want to still toggle the clicking
|
||||
if (value.className.includes(caretright) || value.className.includes(caretdown)) {
|
||||
// Then we tell JS to click the element even though we hid it from the UI
|
||||
value.click();
|
||||
//Break from loop since we no longer need to continue looking
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
this.amountToLoad = 10;
|
||||
this.currentlyLoaded = 10;
|
||||
this.tvRequests = [];
|
||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||
|
||||
this.loadInit();
|
||||
}
|
||||
|
@ -125,7 +95,7 @@ export class TvRequestsComponent implements OnInit {
|
|||
public paginate(event: IPagenator) {
|
||||
const skipAmount = event.first;
|
||||
|
||||
this.requestService.getTvRequestsTree(this.amountToLoad, skipAmount)
|
||||
this.requestService.getTvRequests(this.amountToLoad, skipAmount, OrderType.RequestedDateDesc, FilterType.None, FilterType.None)
|
||||
.subscribe(x => {
|
||||
this.tvRequests = x;
|
||||
this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad;
|
||||
|
@ -150,14 +120,14 @@ export class TvRequestsComponent implements OnInit {
|
|||
event.preventDefault();
|
||||
searchResult.rootFolder = rootFolderSelected.id;
|
||||
this.setOverride(searchResult);
|
||||
this.updateRequest(searchResult);
|
||||
this.setRootFolder(searchResult);
|
||||
}
|
||||
|
||||
public selectQualityProfile(searchResult: ITvRequests, profileSelected: ISonarrProfile, event: any) {
|
||||
event.preventDefault();
|
||||
searchResult.qualityOverride = profileSelected.id;
|
||||
this.setOverride(searchResult);
|
||||
this.updateRequest(searchResult);
|
||||
this.setQualityProfile(searchResult);
|
||||
}
|
||||
|
||||
public reportIssue(catId: IIssueCategory, req: ITvRequests) {
|
||||
|
@ -172,12 +142,23 @@ export class TvRequestsComponent implements OnInit {
|
|||
this.setRootFolderOverrides(req);
|
||||
}
|
||||
|
||||
private updateRequest(request: ITvRequests) {
|
||||
this.requestService.updateTvRequest(request)
|
||||
.subscribe(x => {
|
||||
this.notificationService.success("Request Updated");
|
||||
this.setOverride(x);
|
||||
request = x;
|
||||
private setQualityProfile(req: ITvRequests) {
|
||||
this.requestService.setQualityProfile(req.id, req.qualityOverride).subscribe(x => {
|
||||
if(x) {
|
||||
this.notificationService.success("Quality profile updated");
|
||||
} else {
|
||||
this.notificationService.error("Could not update the quality profile");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private setRootFolder(req: ITvRequests) {
|
||||
this.requestService.setRootFolder(req.id, req.rootFolder).subscribe(x => {
|
||||
if(x) {
|
||||
this.notificationService.success("Quality profile updated");
|
||||
} else {
|
||||
this.notificationService.error("Could not update the quality profile");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -204,23 +185,15 @@ export class TvRequestsComponent implements OnInit {
|
|||
|
||||
private loadInit() {
|
||||
this.requestService.getTotalTv().subscribe(x => this.totalTv = x);
|
||||
this.requestService.getTvRequestsTree(this.amountToLoad, 0)
|
||||
this.requestService.getTvRequests(this.amountToLoad, 0, OrderType.RequestedDateDesc, FilterType.None, FilterType.None)
|
||||
.subscribe(x => {
|
||||
this.tvRequests = x;
|
||||
this.tvRequests.forEach((val, index) => {
|
||||
this.tvRequests.collection.forEach((val, index) => {
|
||||
this.setDefaults(val);
|
||||
this.loadBackdrop(val);
|
||||
this.setOverride(val.data);
|
||||
this.setOverride(val);
|
||||
});
|
||||
});
|
||||
|
||||
if(this.isAdmin) {
|
||||
this.sonarrService.getQualityProfilesWithoutSettings()
|
||||
.subscribe(x => this.sonarrProfiles = x);
|
||||
|
||||
this.sonarrService.getRootFoldersWithoutSettings()
|
||||
.subscribe(x => this.sonarrRootFolders = x);
|
||||
}
|
||||
}
|
||||
|
||||
private resetSearch() {
|
||||
|
@ -228,20 +201,20 @@ export class TvRequestsComponent implements OnInit {
|
|||
this.loadInit();
|
||||
}
|
||||
|
||||
private setDefaults(val: any) {
|
||||
if (val.data.posterPath === null) {
|
||||
val.data.posterPath = this.defaultPoster;
|
||||
private setDefaults(val: ITvRequests) {
|
||||
if (val.posterPath === null) {
|
||||
val.posterPath = this.defaultPoster;
|
||||
}
|
||||
}
|
||||
|
||||
private loadBackdrop(val: TreeNode): void {
|
||||
if (val.data.background != null) {
|
||||
val.data.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(https://image.tmdb.org/t/p/w1280" + val.data.background + ")");
|
||||
private loadBackdrop(val: ITvRequests): void {
|
||||
if (val.background != null) {
|
||||
val.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(https://image.tmdb.org/t/p/w1280" + val.background + ")");
|
||||
} else {
|
||||
this.imageService.getTvBanner(val.data.tvDbId).subscribe(x => {
|
||||
this.imageService.getTvBanner(val.tvDbId).subscribe(x => {
|
||||
if (x) {
|
||||
val.data.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
val.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(" + x + ")");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
import { Subject } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { IIssueCategory, IRequestEngineResult, ISearchMovieResult } from "../interfaces";
|
||||
|
@ -32,15 +30,16 @@ export class MovieSearchComponent implements OnInit {
|
|||
public issueCategorySelected: IIssueCategory;
|
||||
public defaultPoster: string;
|
||||
|
||||
constructor(private searchService: SearchService, private requestService: RequestService,
|
||||
constructor(
|
||||
private searchService: SearchService, private requestService: RequestService,
|
||||
private notificationService: NotificationService, private authService: AuthService,
|
||||
private readonly translate: TranslateService, private sanitizer: DomSanitizer,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
).subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.clearResults();
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { Subject } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { NotificationService, RequestService, SearchService } from "../services";
|
||||
|
||||
import { IRequestEngineResult, ISearchMovieResult, ISearchMovieResultContainer } from "../interfaces";
|
||||
import { NotificationService, RequestService, SearchService } from "../services";
|
||||
|
||||
@Component({
|
||||
selector: "movie-search-grid",
|
||||
|
@ -22,13 +19,14 @@ export class MovieSearchGridComponent implements OnInit {
|
|||
public result: IRequestEngineResult;
|
||||
public searchApplied = false;
|
||||
|
||||
constructor(private searchService: SearchService, private requestService: RequestService,
|
||||
constructor(
|
||||
private searchService: SearchService, private requestService: RequestService,
|
||||
private notificationService: NotificationService, private authService: AuthService) {
|
||||
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms afterthe last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
).subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.clearResults();
|
||||
|
@ -157,7 +155,6 @@ export class MovieSearchGridComponent implements OnInit {
|
|||
this.movieResultGrid.push(container);
|
||||
container = <ISearchMovieResultContainer> { movies: [] };
|
||||
} else {
|
||||
|
||||
container.movies.push(movie);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Component, Input, OnInit} from "@angular/core";
|
||||
import "rxjs/add/operator/takeUntil";
|
||||
|
||||
import { NotificationService } from "../services";
|
||||
import { RequestService } from "../services";
|
||||
|
|
|
@ -42,46 +42,46 @@
|
|||
<i class='fa fa-film no-search-results-icon'></i>
|
||||
<div class='no-search-results-text'>{{ 'Search.NoResults' | translate }}</div>
|
||||
</div>
|
||||
<p-treeTable [value]="tvResults">
|
||||
<p-column>
|
||||
<ng-template let-col let-node="rowData" pTemplate="header">
|
||||
{{ 'Search.TvShows.Results' | translate }}
|
||||
</ng-template>
|
||||
<ng-template let-col let-node="rowData" pTemplate="body">
|
||||
<div *ngIf="tvResults" >
|
||||
<div *ngFor="let node of tvResults">
|
||||
<!--This is the section that holds the parent level search results set-->
|
||||
<div *ngIf="!node.leaf">
|
||||
<div *ngIf="node">
|
||||
<div class="row">
|
||||
|
||||
<div class="myBg backdrop" [style.background-image]="node?.data?.background"></div>
|
||||
<div class="myBg backdrop" [style.background-image]="node?.background"></div>
|
||||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
||||
<div class="col-sm-2 small-padding">
|
||||
|
||||
<img *ngIf="node?.data?.banner" class="img-responsive poster" width="150" [src]="node.data.banner" alt="poster">
|
||||
<img *ngIf="node.banner" class="img-responsive poster" width="150" [src]="node.banner" alt="poster">
|
||||
|
||||
</div>
|
||||
<div class="col-sm-8 small-padding">
|
||||
<div>
|
||||
|
||||
<a *ngIf="node.data.imdbId" href="{{node.data.imdbId}}" target="_blank">
|
||||
<h4>{{node.data.title}} ({{node.data.firstAired | date: 'yyyy'}})</h4>
|
||||
<a *ngIf="node.imdbId" href="{{node.imdbId}}" target="_blank">
|
||||
<h4>{{node.title}} ({{node.firstAired | date: 'yyyy'}})</h4>
|
||||
|
||||
</a>
|
||||
<span class="tags">
|
||||
<a *ngIf="node.data.homepage" id="homepageLabel" href="{{node.data.homepage}}" target="_blank"><span class="label label-info" >{{ 'Search.Movies.HomePage' | translate }}</span></a>
|
||||
<a *ngIf="node.homepage" id="homepageLabel" href="{{node.homepage}}" target="_blank">
|
||||
<span class="label label-info">{{ 'Search.Movies.HomePage' | translate }}</span>
|
||||
</a>
|
||||
|
||||
<a *ngIf="node.data.trailer" id="trailerLabel" href="{{node.data.trailer}}" target="_blank"><span class="label label-info">{{ 'Search.Movies.Trailer' | translate }}</span></a>
|
||||
<a *ngIf="node.trailer" id="trailerLabel" href="{{node.trailer}}" target="_blank">
|
||||
<span class="label label-info">{{ 'Search.Movies.Trailer' | translate }}</span>
|
||||
</a>
|
||||
|
||||
<span *ngIf="node.data.status" class="label label-primary" id="statusLabel" target="_blank">{{node.data.status}}</span>
|
||||
<span *ngIf="node.status" class="label label-primary" id="statusLabel" target="_blank">{{node.status}}</span>
|
||||
|
||||
|
||||
<span *ngIf="node.data.firstAired" class="label label-info" target="_blank" id="airDateLabel">{{ 'Search.TvShows.AirDate' | translate }} {{node.data.firstAired | date: 'dd/MM/yyyy'}}</span>
|
||||
<span *ngIf="node.firstAired" class="label label-info" target="_blank" id="airDateLabel">{{ 'Search.TvShows.AirDate' | translate }} {{node.firstAired | date: 'dd/MM/yyyy'}}</span>
|
||||
|
||||
<span *ngIf="node.data.network" class="label label-info" id="networkLabel" target="_blank">{{node.data.network}}</span>
|
||||
<span *ngIf="node.network" class="label label-info" id="networkLabel" target="_blank">{{node.network}}</span>
|
||||
|
||||
|
||||
<span *ngIf="node.data.available" class="label label-success" id="availableLabel">{{ 'Common.Available' | translate }}</span>
|
||||
<span *ngIf="node.available" class="label label-success" id="availableLabel">{{ 'Common.Available' | translate }}</span>
|
||||
|
||||
<span *ngIf="node.data.partlyAvailable" class="label label-warning" id="partiallyAvailableLabel">{{ 'Common.PartlyAvailable' | translate }}</span>
|
||||
<span *ngIf="node.partlyAvailable" class="label label-warning" id="partiallyAvailableLabel">{{ 'Common.PartlyAvailable' | translate }}</span>
|
||||
|
||||
|
||||
|
||||
|
@ -90,47 +90,45 @@
|
|||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<p class="tv-overview">{{node.data.overview}}</p>
|
||||
<p class="tv-overview">{{node.overview}}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-2 small-padding">
|
||||
<div *ngIf="!node.data.fullyAvailable" class="dropdown">
|
||||
<div *ngIf="!node.fullyAvailable" class="dropdown">
|
||||
<button class="btn btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> {{ 'Common.Request' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<li>
|
||||
<a href="#" (click)="allSeasons(node.data, $event)">{{ 'Search.TvShows.AllSeasons' | translate }}</a>
|
||||
<a href="#" (click)="allSeasons(node, $event)">{{ 'Search.TvShows.AllSeasons' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="firstSeason(node.data, $event)">{{ 'Search.TvShows.FirstSeason' | translate }}</a>
|
||||
<a href="#" (click)="firstSeason(node, $event)">{{ 'Search.TvShows.FirstSeason' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="latestSeason(node.data, $event)">{{ 'Search.TvShows.LatestSeason' | translate }}</a>
|
||||
<a href="#" (click)="latestSeason(node, $event)">{{ 'Search.TvShows.LatestSeason' | translate }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" (click)="openClosestTab($event)">{{ 'Search.TvShows.Select' | translate }}</a>
|
||||
<a href="#" (click)="openClosestTab(node, $event)">{{ 'Search.TvShows.Select' | translate }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div *ngIf="node.data.fullyAvailable">
|
||||
<div *ngIf="node.fullyAvailable">
|
||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled>
|
||||
<i class="fa fa-check"></i> {{ 'Common.Available' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<br />
|
||||
<div *ngIf="node.data.plexUrl && node.data.available">
|
||||
<a style="text-align: right" class="btn btn-sm btn-success-outline" href="{{node.data.plexUrl}}"
|
||||
target="_blank">
|
||||
<div *ngIf="node.plexUrl && node.available">
|
||||
<a style="text-align: right" class="btn btn-sm btn-success-outline" href="{{node.plexUrl}}" target="_blank">
|
||||
<i class="fa fa-eye"></i> {{ 'Search.ViewOnPlex' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
<div *ngIf="node.data.embyUrl && node.data.available">
|
||||
<a style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline" href="{{node.data.embyUrl}}"
|
||||
target="_blank">
|
||||
<div *ngIf="node.embyUrl && node.available">
|
||||
<a style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline" href="{{node.embyUrl}}" target="_blank">
|
||||
<i class="fa fa-eye"></i> {{ 'Search.ViewOnEmby' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -142,11 +140,11 @@
|
|||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<li *ngFor="let cat of issueCategories">
|
||||
<a [routerLink]="" (click)="reportIssue(cat, node.data)">{{cat.value}}</a>
|
||||
<a [routerLink]="" (click)="reportIssue(cat, node)">{{cat.value}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div *ngIf="!node.data.available">
|
||||
<div *ngIf="!node.available">
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
@ -156,15 +154,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
|
||||
<div *ngIf="node.leaf">
|
||||
<seriesinformation [seriesId]="node.data.id"></seriesinformation>
|
||||
<div *ngIf="node.open">
|
||||
<seriesinformation [seriesId]="node.id"></seriesinformation>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
</ng-template>
|
||||
</p-column>
|
||||
</p-treeTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
import { Subject } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { IIssueCategory, IRequestEngineResult, ISearchTvResult, ISeasonsViewModel, ITvRequestViewModel } from "../interfaces";
|
||||
import { ImageService, NotificationService, RequestService, SearchService } from "../services";
|
||||
|
||||
import { TreeNode } from "primeng/primeng";
|
||||
import { IRequestEngineResult } from "../interfaces";
|
||||
import { IIssueCategory, ISearchTvResult, ISeasonsViewModel, ITvRequestViewModel } from "../interfaces";
|
||||
|
||||
@Component({
|
||||
selector: "tv-search",
|
||||
templateUrl: "./tvsearch.component.html",
|
||||
|
@ -19,7 +17,7 @@ export class TvSearchComponent implements OnInit {
|
|||
|
||||
public searchText: string;
|
||||
public searchChanged = new Subject<string>();
|
||||
public tvResults: TreeNode[];
|
||||
public tvResults: ISearchTvResult[];
|
||||
public result: IRequestEngineResult;
|
||||
public searchApplied = false;
|
||||
public defaultPoster: string;
|
||||
|
@ -32,21 +30,22 @@ export class TvSearchComponent implements OnInit {
|
|||
public issueProviderId: string;
|
||||
public issueCategorySelected: IIssueCategory;
|
||||
|
||||
constructor(private searchService: SearchService, private requestService: RequestService,
|
||||
constructor(
|
||||
private searchService: SearchService, private requestService: RequestService,
|
||||
private notificationService: NotificationService, private authService: AuthService,
|
||||
private imageService: ImageService, private sanitizer: DomSanitizer,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
).subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.clearResults();
|
||||
return;
|
||||
}
|
||||
this.searchService.searchTvTreeNode(this.searchText)
|
||||
this.searchService.searchTv(this.searchText)
|
||||
.subscribe(x => {
|
||||
this.tvResults = x;
|
||||
this.searchApplied = true;
|
||||
|
@ -59,30 +58,9 @@ export class TvSearchComponent implements OnInit {
|
|||
this.defaultPoster = "../../.." + base + "/images/default_tv_poster.png";
|
||||
}
|
||||
}
|
||||
public openClosestTab(el: any) {
|
||||
public openClosestTab(node: ISearchTvResult,el: any) {
|
||||
el.preventDefault();
|
||||
const rowclass = "undefined ng-star-inserted";
|
||||
el = el.toElement || el.relatedTarget || el.target;
|
||||
while (el.className !== rowclass) {
|
||||
// Increment the loop to the parent node until we find the row we need
|
||||
el = el.parentNode;
|
||||
}
|
||||
// At this point, the while loop has stopped and `el` represents the element that has
|
||||
// the class you specified
|
||||
|
||||
// Then we loop through the children to find the caret which we want to click
|
||||
const caretright = "fa-caret-right";
|
||||
const caretdown = "fa-caret-down";
|
||||
for (const value of el.children) {
|
||||
// the caret from the ui has 2 class selectors depending on if expanded or not
|
||||
// we search for both since we want to still toggle the clicking
|
||||
if (value.className.includes(caretright) || value.className.includes(caretdown)) {
|
||||
// Then we tell JS to click the element even though we hid it from the UI
|
||||
value.click();
|
||||
//Break from loop since we no longer need to continue looking
|
||||
break;
|
||||
}
|
||||
}
|
||||
node.open = !node.open;
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
|
@ -138,16 +116,16 @@ export class TvSearchComponent implements OnInit {
|
|||
|
||||
public getExtraInfo() {
|
||||
this.tvResults.forEach((val, index) => {
|
||||
this.imageService.getTvBanner(val.data.id).subscribe(x => {
|
||||
this.imageService.getTvBanner(val.id).subscribe(x => {
|
||||
if (x) {
|
||||
val.data.background = this.sanitizer.
|
||||
val.background = this.sanitizer.
|
||||
bypassSecurityTrustStyle
|
||||
("url(" + x + ")");
|
||||
}
|
||||
});
|
||||
this.searchService.getShowInformationTreeNode(val.data.id)
|
||||
this.searchService.getShowInformation(val.id)
|
||||
.subscribe(x => {
|
||||
if (x.data) {
|
||||
if (x) {
|
||||
this.setDefaults(x);
|
||||
this.updateItem(val, x);
|
||||
} else {
|
||||
|
@ -223,29 +201,29 @@ export class TvSearchComponent implements OnInit {
|
|||
this.issueProviderId = req.id.toString();
|
||||
}
|
||||
|
||||
private updateItem(key: TreeNode, updated: TreeNode) {
|
||||
private updateItem(key: ISearchTvResult, updated: ISearchTvResult) {
|
||||
const index = this.tvResults.indexOf(key, 0);
|
||||
if (index > -1) {
|
||||
// Update certain properties, otherwise we will loose some data
|
||||
this.tvResults[index].data.title = updated.data.title;
|
||||
this.tvResults[index].data.banner = updated.data.banner;
|
||||
this.tvResults[index].data.imdbId = updated.data.imdbId;
|
||||
this.tvResults[index].data.seasonRequests = updated.data.seasonRequests;
|
||||
this.tvResults[index].data.seriesId = updated.data.seriesId;
|
||||
this.tvResults[index].data.fullyAvailable = updated.data.fullyAvailable;
|
||||
this.tvResults[index].data.backdrop = updated.data.backdrop;
|
||||
this.tvResults[index].title = updated.title;
|
||||
this.tvResults[index].banner = updated.banner;
|
||||
this.tvResults[index].imdbId = updated.imdbId;
|
||||
this.tvResults[index].seasonRequests = updated.seasonRequests;
|
||||
this.tvResults[index].seriesId = updated.seriesId;
|
||||
this.tvResults[index].fullyAvailable = updated.fullyAvailable;
|
||||
this.tvResults[index].background = updated.banner;
|
||||
}
|
||||
}
|
||||
|
||||
private setDefaults(x: any) {
|
||||
if (x.data.banner === null) {
|
||||
x.data.banner = this.defaultPoster;
|
||||
private setDefaults(x: ISearchTvResult) {
|
||||
if (x.banner === null) {
|
||||
x.banner = this.defaultPoster;
|
||||
}
|
||||
|
||||
if (x.data.imdbId === null) {
|
||||
x.data.imdbId = "https://www.tvmaze.com/shows/" + x.data.seriesId;
|
||||
if (x.imdbId === null) {
|
||||
x.imdbId = "https://www.tvmaze.com/shows/" + x.seriesId;
|
||||
} else {
|
||||
x.data.imdbId = "http://www.imdb.com/title/" + x.data.imdbId + "/";
|
||||
x.imdbId = "http://www.imdb.com/title/" + x.imdbId + "/";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { HttpClient, HttpHeaders } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IPlexPin } from "../../interfaces";
|
||||
|
||||
|
@ -10,7 +10,6 @@ import { IPlexPin } from "../../interfaces";
|
|||
export class PlexTvService {
|
||||
|
||||
constructor(private http: HttpClient, public platformLocation: PlatformLocation) {
|
||||
|
||||
}
|
||||
|
||||
public GetPin(clientId: string, applicationName: string): Observable<IPlexPin> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IRadarrProfile, IRadarrRootFolder } from "../../interfaces";
|
||||
import { IRadarrSettings } from "../../interfaces";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ISonarrSettings } from "../../interfaces";
|
||||
import { ISonarrProfile, ISonarrRootFolder } from "../../interfaces";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
|
@ -37,6 +37,7 @@ export class TesterService extends ServiceHelpers {
|
|||
public pushbulletTest(settings: IPushbulletNotificationSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}pushbullet`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
||||
public pushoverTest(settings: IPushoverNotificationSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}pushover`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
@ -80,9 +81,11 @@ export class TesterService extends ServiceHelpers {
|
|||
public sickrageTest(settings: ISickRageSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}sickrage`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
||||
public newsletterTest(settings: INewsletterNotificationSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}newsletter`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
||||
public mobileNotificationTest(settings: IMobileNotificationTestSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}mobile`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ICheckbox, ICreateWizardUser, IIdentityResult, IResetPasswordToken, IUpdateLocalUser, IUser } from "../interfaces";
|
||||
import { ICheckbox, ICreateWizardUser, IIdentityResult, IResetPasswordToken, IUpdateLocalUser, IUser, IWizardUserResult } from "../interfaces";
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
||||
@Injectable()
|
||||
|
@ -12,8 +12,8 @@ export class IdentityService extends ServiceHelpers {
|
|||
constructor(http: HttpClient, public platformLocation: PlatformLocation) {
|
||||
super(http, "/api/v1/Identity/", platformLocation);
|
||||
}
|
||||
public createWizardUser(user: ICreateWizardUser): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}Wizard/`, JSON.stringify(user), {headers: this.headers});
|
||||
public createWizardUser(user: ICreateWizardUser): Observable<IWizardUserResult> {
|
||||
return this.http.post<IWizardUserResult>(`${this.url}Wizard/`, JSON.stringify(user), {headers: this.headers});
|
||||
}
|
||||
|
||||
public getUser(): Observable<IUser> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IIssueCategory, IIssueComments, IIssueCount, IIssues, IIssuesChat, INewIssueComments, IssueStatus, IUpdateStatus } from "../interfaces";
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IMobileUsersViewModel } from "./../interfaces";
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IMassEmailModel } from "./../interfaces";
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "./../interfaces";
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
@ -19,6 +19,7 @@ export class RecentlyAddedService extends ServiceHelpers {
|
|||
public getRecentlyAddedTv(): Observable<IRecentlyAddedTvShows[]> {
|
||||
return this.http.get<IRecentlyAddedTvShows[]>(`${this. url}tv/`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public getRecentlyAddedTvGrouped(): Observable<IRecentlyAddedTvShows[]> {
|
||||
return this.http.get<IRecentlyAddedTvShows[]>(`${this.url}tv/grouped`, {headers: this.headers});
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { TreeNode } from "primeng/primeng";
|
||||
import { IRequestEngineResult } from "../interfaces";
|
||||
import { IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestsViewModel, ITvRequests,ITvUpdateModel, OrderType } from "../interfaces";
|
||||
import { FilterType, IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces";
|
||||
import { ITvRequestViewModel } from "../interfaces";
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
||||
|
@ -64,8 +63,8 @@ export class RequestService extends ServiceHelpers {
|
|||
return this.http.put<IMovieRequests>(`${this.url}movie/`, JSON.stringify(request), {headers: this.headers});
|
||||
}
|
||||
|
||||
public getTvRequests(count: number, position: number): Observable<ITvRequests[]> {
|
||||
return this.http.get<ITvRequests[]>(`${this.url}tv/${count}/${position}`, {headers: this.headers});
|
||||
public getTvRequests(count: number, position: number, order: OrderType, status: FilterType, availability: FilterType): Observable<IRequestsViewModel<ITvRequests>> {
|
||||
return this.http.get<IRequestsViewModel<ITvRequests>>(`${this.url}tv/${count}/${position}/${order}/${status}/${availability}`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public getTvRequestsTree(count: number, position: number): Observable<TreeNode[]> {
|
||||
|
@ -127,4 +126,10 @@ export class RequestService extends ServiceHelpers {
|
|||
public unSubscribeToTv(requestId: number): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}tv/unsubscribe/${requestId}`, {headers: this.headers});
|
||||
}
|
||||
public setQualityProfile(requestId: number, qualityId: number): Observable<boolean> {
|
||||
return this.http.put<boolean>(`${this.url}tv/quality/${requestId}/${qualityId}`, {headers: this.headers});
|
||||
}
|
||||
public setRootFolder(requestId: number, rootFolderId: number): Observable<boolean> {
|
||||
return this.http.put<boolean>(`${this.url}tv/root/${requestId}/${rootFolderId}`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { TreeNode } from "primeng/primeng";
|
||||
import { ISearchMovieResult } from "../interfaces";
|
||||
|
@ -56,16 +56,16 @@ export class SearchService extends ServiceHelpers {
|
|||
return this.http.get<ISearchTvResult>(`${this.url}/Tv/info/${theTvDbId}`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public popularTv(): Observable<TreeNode[]> {
|
||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/popular/tree`, {headers: this.headers});
|
||||
public popularTv(): Observable<ISearchTvResult[]> {
|
||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/popular`, {headers: this.headers});
|
||||
}
|
||||
public mostWatchedTv(): Observable<TreeNode[]> {
|
||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/mostwatched/tree`, {headers: this.headers});
|
||||
public mostWatchedTv(): Observable<ISearchTvResult[]> {
|
||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/mostwatched`, {headers: this.headers});
|
||||
}
|
||||
public anticipatedTv(): Observable<TreeNode[]> {
|
||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/anticipated/tree`, {headers: this.headers});
|
||||
public anticipatedTv(): Observable<ISearchTvResult[]> {
|
||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/anticipated`, {headers: this.headers});
|
||||
}
|
||||
public trendingTv(): Observable<TreeNode[]> {
|
||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/trending/tree`, {headers: this.headers});
|
||||
public trendingTv(): Observable<ISearchTvResult[]> {
|
||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/trending`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient, HttpHeaders } from "@angular/common/http";
|
||||
import "rxjs/add/observable/throw";
|
||||
|
||||
export class ServiceHelpers {
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import {
|
||||
IAbout,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { PlatformLocation } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs/Rx";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import "rxjs/add/operator/takeUntil";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
|
||||
import { IPlexServerResponse, IPlexServerViewModel } from "../../interfaces";
|
||||
import { IPlexLibrariesSettings, IPlexServer, IPlexSettings } from "../../interfaces";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Subject } from "rxjs";
|
||||
import { takeUntil } from "rxjs/operators";
|
||||
|
||||
import { IPlexLibrariesSettings, IPlexServer, IPlexServerResponse, IPlexServerViewModel, IPlexSettings } from "../../interfaces";
|
||||
import { JobService, NotificationService, PlexService, SettingsService, TesterService } from "../../services";
|
||||
|
||||
@Component({
|
||||
|
@ -21,7 +19,8 @@ export class PlexComponent implements OnInit, OnDestroy {
|
|||
|
||||
private subscriptions = new Subject<void>();
|
||||
|
||||
constructor(private settingsService: SettingsService,
|
||||
constructor(
|
||||
private settingsService: SettingsService,
|
||||
private notificationService: NotificationService,
|
||||
private plexService: PlexService,
|
||||
private testerService: TesterService,
|
||||
|
@ -34,9 +33,9 @@ export class PlexComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
public requestServers(server: IPlexServer) {
|
||||
this.plexService.getServers(this.username, this.password)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.plexService.getServers(this.username, this.password).pipe(
|
||||
takeUntil(this.subscriptions),
|
||||
).subscribe(x => {
|
||||
if (x.success) {
|
||||
this.loadedServers = x;
|
||||
this.serversButton = true;
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import { CommonModule } from "@angular/common";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
import { NgbAccordionModule, NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
||||
import { ClipboardModule } from "ngx-clipboard/dist";
|
||||
import { ClipboardModule } from "ngx-clipboard";
|
||||
|
||||
import { AuthGuard } from "../auth/auth.guard";
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { CouchPotatoService, EmbyService, IssuesService, JobService, MobileService, NotificationMessageService, PlexService, RadarrService,
|
||||
SonarrService, TesterService, ValidationService } from "../services";
|
||||
import {
|
||||
CouchPotatoService, EmbyService, IssuesService, JobService, MobileService, NotificationMessageService, PlexService, RadarrService,
|
||||
SonarrService, TesterService, ValidationService,
|
||||
} from "../services";
|
||||
|
||||
import { PipeModule } from "../pipes/pipe.module";
|
||||
import { AboutComponent } from "./about/about.component";
|
||||
|
|
|
@ -21,9 +21,9 @@ export class UserManagementComponent implements OnInit {
|
|||
public bulkMovieLimit?: number;
|
||||
public bulkEpisodeLimit?: number;
|
||||
|
||||
constructor(private readonly identityService: IdentityService,
|
||||
private readonly settingsService: SettingsService,
|
||||
private readonly notificationService: NotificationService) { }
|
||||
constructor(private identityService: IdentityService,
|
||||
private settingsService: SettingsService,
|
||||
private notificationService: NotificationService) { }
|
||||
|
||||
public ngOnInit() {
|
||||
this.users = [];
|
||||
|
|
|
@ -17,10 +17,12 @@ export class CreateAdminComponent {
|
|||
|
||||
public createUser() {
|
||||
this.identityService.createWizardUser({username: this.username, password: this.password, usePlexAdminAccount: false}).subscribe(x => {
|
||||
if (x) {
|
||||
if (x.result) {
|
||||
this.router.navigate(["login"]);
|
||||
} else {
|
||||
this.notificationService.error("There was an error... You might want to put this on Github...");
|
||||
if(x.errors.length > 0) {
|
||||
this.notificationService.error(x.errors[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { IEmbySettings } from "../../interfaces";
|
|||
})
|
||||
export class EmbyComponent implements OnInit {
|
||||
|
||||
private embySettings: IEmbySettings;
|
||||
public embySettings: IEmbySettings;
|
||||
|
||||
constructor(private embyService: EmbyService,
|
||||
private router: Router,
|
||||
|
|
|
@ -35,10 +35,13 @@ export class PlexComponent implements OnInit {
|
|||
password: "",
|
||||
usePlexAdminAccount: true,
|
||||
}).subscribe(y => {
|
||||
if (y) {
|
||||
if (y.result) {
|
||||
this.router.navigate(["login"]);
|
||||
} else {
|
||||
this.notificationService.error("Could not get the Plex Admin Information");
|
||||
if(y.errors.length > 0) {
|
||||
this.notificationService.error(y.errors[0]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
@ -47,7 +50,7 @@ export class PlexComponent implements OnInit {
|
|||
}
|
||||
|
||||
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 => {
|
||||
if (x.url) {
|
||||
window.location.href = x.url;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { IdentityService, PlexOAuthService, SettingsService } from "../../services";
|
||||
import { IdentityService, PlexOAuthService } from "../../services";
|
||||
import { AuthService } from "./../../auth/auth.service";
|
||||
|
||||
@Component({
|
||||
|
@ -13,7 +13,6 @@ export class PlexOAuthComponent implements OnInit {
|
|||
constructor(private route: ActivatedRoute,
|
||||
private plexOauth: PlexOAuthService,
|
||||
private identityService: IdentityService,
|
||||
private settings: SettingsService,
|
||||
private router: Router,
|
||||
private auth: AuthService) {
|
||||
|
||||
|
@ -35,33 +34,20 @@ export class PlexOAuthComponent implements OnInit {
|
|||
password: "",
|
||||
usePlexAdminAccount: true,
|
||||
}).subscribe(u => {
|
||||
if (u) {
|
||||
if (u.result) {
|
||||
this.auth.oAuth(this.pinId).subscribe(c => {
|
||||
localStorage.setItem("id_token", c.access_token);
|
||||
|
||||
// Mark that we have done the settings now
|
||||
this.settings.getOmbi().subscribe(ombi => {
|
||||
ombi.wizard = true;
|
||||
|
||||
this.settings.saveOmbi(ombi).subscribe(s => {
|
||||
this.settings.getUserManagementSettings().subscribe(usr => {
|
||||
|
||||
usr.importPlexAdmin = true;
|
||||
this.settings.saveUserManagementSettings(usr).subscribe(saved => {
|
||||
this.router.navigate(["login"]);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
//this.notificationService.error("Could not get the Plex Admin Information");
|
||||
|
||||
if(u.errors.length > 0) {
|
||||
console.log(u.errors[0]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
import "rxjs/add/operator/catch";
|
||||
import "rxjs/add/operator/map";
|
|
@ -1,5 +1,6 @@
|
|||
import * as Pace from "pace-progress";
|
||||
// Main
|
||||
|
||||
import * as Pace from "pace-progress";
|
||||
Pace.start();
|
||||
|
||||
import "bootstrap/dist/js/bootstrap";
|
||||
|
@ -11,8 +12,6 @@ import "./polyfills";
|
|||
|
||||
import "hammerjs";
|
||||
|
||||
import "./imports";
|
||||
|
||||
import { enableProdMode } from "@angular/core";
|
||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||
import { AppModule } from "./app/app.module";
|
||||
|
@ -29,7 +28,9 @@ if (module.hot) {
|
|||
oldRootElem.parentNode.insertBefore(newRootElem, oldRootElem);
|
||||
oldRootElem.parentNode.removeChild(oldRootElem);
|
||||
}
|
||||
if (modulePromise) {
|
||||
modulePromise.then(appModule => appModule.destroy());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
enableProdMode();
|
||||
|
|
|
@ -9,6 +9,7 @@ declare var module: any;
|
|||
|
||||
if (module.hot) {
|
||||
Error.stackTraceLimit = Infinity;
|
||||
// tslint:disable:no-var-requires
|
||||
|
||||
// tslint:disable-next-line
|
||||
require("zone.js/dist/long-stack-trace-zone");
|
||||
}
|
||||
|
|
|
@ -24,3 +24,7 @@ $bg-colour-disabled: #252424;
|
|||
background: $primary-colour !important;
|
||||
color: white;
|
||||
}*/
|
||||
|
||||
.label {
|
||||
margin: 3px;
|
||||
}
|
|
@ -455,7 +455,7 @@ $border-radius: 10px;
|
|||
}
|
||||
|
||||
.checkbox input[type=checkbox] {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.checkbox input[type=checkbox]:checked + label:before {
|
||||
|
@ -883,11 +883,6 @@ textarea {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.ui-treetable-toggler.fa.fa-fw.ui-clickable.fa-caret-right,
|
||||
.ui-treetable-toggler.fa.fa-fw.ui-clickable.fa-caret-down {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-state-highlight {
|
||||
background: $primary-colour;
|
||||
}
|
||||
|
@ -900,15 +895,6 @@ textarea {
|
|||
border: 1px solid $form-color-lighter;
|
||||
}
|
||||
|
||||
.ui-treetable tfoot td, .ui-treetable th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ui-treetable tbody td {
|
||||
white-space: inherit;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
table a:not(.btn) {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ using Ombi.Settings.Settings.Models.External;
|
|||
|
||||
namespace Ombi.Controllers.External
|
||||
{
|
||||
[Admin]
|
||||
[PowerUser]
|
||||
[ApiV1]
|
||||
[Produces("application/json")]
|
||||
public class SonarrController : Controller
|
||||
|
@ -55,8 +55,12 @@ namespace Ombi.Controllers.External
|
|||
public async Task<IEnumerable<SonarrProfile>> GetProfiles()
|
||||
{
|
||||
var settings = await SonarrSettings.GetSettingsAsync();
|
||||
if (settings.Enabled)
|
||||
{
|
||||
return await SonarrApi.GetProfiles(settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Sonarr root folders.
|
||||
|
@ -66,7 +70,12 @@ namespace Ombi.Controllers.External
|
|||
public async Task<IEnumerable<SonarrRootFolder>> GetRootFolders()
|
||||
{
|
||||
var settings = await SonarrSettings.GetSettingsAsync();
|
||||
if (settings.Enabled)
|
||||
{
|
||||
return await SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,7 +59,8 @@ namespace Ombi.Controllers
|
|||
IRepository<Issues> issues,
|
||||
IRepository<IssueComments> issueComments,
|
||||
IRepository<NotificationUserId> notificationRepository,
|
||||
IRepository<RequestSubscription> subscriptionRepository)
|
||||
IRepository<RequestSubscription> subscriptionRepository,
|
||||
ISettingsService<UserManagementSettings> umSettings)
|
||||
{
|
||||
UserManager = user;
|
||||
Mapper = mapper;
|
||||
|
@ -79,6 +80,7 @@ namespace Ombi.Controllers
|
|||
OmbiSettings = ombiSettings;
|
||||
_requestSubscriptionRepository = subscriptionRepository;
|
||||
_notificationRepository = notificationRepository;
|
||||
_userManagementSettings = umSettings;
|
||||
}
|
||||
|
||||
private OmbiUserManager UserManager { get; }
|
||||
|
@ -87,6 +89,7 @@ namespace Ombi.Controllers
|
|||
private IEmailProvider EmailProvider { get; }
|
||||
private ISettingsService<EmailNotificationSettings> EmailSettings { get; }
|
||||
private ISettingsService<CustomizationSettings> CustomizationSettings { get; }
|
||||
private readonly ISettingsService<UserManagementSettings> _userManagementSettings;
|
||||
private ISettingsService<OmbiSettings> OmbiSettings { get; }
|
||||
private IWelcomeEmail WelcomeEmail { get; }
|
||||
private IMovieRequestRepository MovieRepo { get; }
|
||||
|
@ -113,13 +116,13 @@ namespace Ombi.Controllers
|
|||
[HttpPost("Wizard")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
[AllowAnonymous]
|
||||
public async Task<bool> CreateWizardUser([FromBody] CreateUserWizardModel user)
|
||||
public async Task<SaveWizardResult> CreateWizardUser([FromBody] CreateUserWizardModel user)
|
||||
{
|
||||
var users = UserManager.Users;
|
||||
if (users.Any(x => !x.UserName.Equals("api", StringComparison.CurrentCultureIgnoreCase)))
|
||||
if (users.Any(x => !x.UserName.Equals("api", StringComparison.InvariantCultureIgnoreCase)))
|
||||
{
|
||||
// No one should be calling this. Only the wizard
|
||||
return false;
|
||||
return new SaveWizardResult{ Result = false, Errors = new List<string> {"Looks like there is an existing user!"} };
|
||||
}
|
||||
|
||||
if (user.UsePlexAdminAccount)
|
||||
|
@ -129,7 +132,7 @@ namespace Ombi.Controllers
|
|||
if (authToken.IsNullOrEmpty())
|
||||
{
|
||||
_log.LogWarning("Could not find an auth token to create the plex user with");
|
||||
return false;
|
||||
return new SaveWizardResult { Result = false };
|
||||
}
|
||||
var plexUser = await _plexApi.GetAccount(authToken);
|
||||
var adminUser = new OmbiUser
|
||||
|
@ -140,6 +143,11 @@ namespace Ombi.Controllers
|
|||
ProviderUserId = plexUser.user.id
|
||||
};
|
||||
|
||||
await _userManagementSettings.SaveSettingsAsync(new UserManagementSettings
|
||||
{
|
||||
ImportPlexAdmin = true
|
||||
});
|
||||
|
||||
return await SaveWizardUser(user, adminUser);
|
||||
}
|
||||
|
||||
|
@ -152,9 +160,10 @@ namespace Ombi.Controllers
|
|||
return await SaveWizardUser(user, userToCreate);
|
||||
}
|
||||
|
||||
private async Task<bool> SaveWizardUser(CreateUserWizardModel user, OmbiUser userToCreate)
|
||||
private async Task<SaveWizardResult> SaveWizardUser(CreateUserWizardModel user, OmbiUser userToCreate)
|
||||
{
|
||||
IdentityResult result;
|
||||
var retVal = new SaveWizardResult();
|
||||
// When creating the admin as the plex user, we do not pass in the password.
|
||||
if (user.Password.HasValue())
|
||||
{
|
||||
|
@ -182,6 +191,7 @@ namespace Ombi.Controllers
|
|||
if (!result.Succeeded)
|
||||
{
|
||||
LogErrors(result);
|
||||
retVal.Errors.AddRange(result.Errors.Select(x => x.Description));
|
||||
}
|
||||
|
||||
// Update the wizard flag
|
||||
|
@ -189,7 +199,8 @@ namespace Ombi.Controllers
|
|||
settings.Wizard = true;
|
||||
await OmbiSettings.SaveSettingsAsync(settings);
|
||||
|
||||
return result.Succeeded;
|
||||
retVal.Result = result.Succeeded;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void LogErrors(IdentityResult result)
|
||||
|
|
|
@ -240,6 +240,18 @@ namespace Ombi.Controllers
|
|||
|
||||
return await _issueComments.Add(newComment);
|
||||
}
|
||||
/// <summary>
|
||||
/// Deletes a comment on a issue
|
||||
/// </summary>
|
||||
[HttpDelete("comments/{id:int}")]
|
||||
[PowerUser]
|
||||
public async Task<bool> DeleteComment(int commentId)
|
||||
{
|
||||
var comment = await _issueComments.GetAll().FirstOrDefaultAsync(x => x.Id == commentId);
|
||||
|
||||
await _issueComments.Delete(comment);
|
||||
return true;
|
||||
}
|
||||
|
||||
[HttpPost("status")]
|
||||
public async Task<bool> UpdateStatus([FromBody] IssueStateViewModel model)
|
||||
|
|
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using System.Diagnostics;
|
||||
using Ombi.Attributes;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Models;
|
||||
using Ombi.Store.Entities;
|
||||
|
@ -93,6 +94,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="requestId">The request identifier.</param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("movie/{requestId:int}")]
|
||||
[PowerUser]
|
||||
public async Task DeleteRequest(int requestId)
|
||||
{
|
||||
await MovieRequestEngine.RemoveMovieRequest(requestId);
|
||||
|
@ -104,6 +106,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("movie")]
|
||||
[PowerUser]
|
||||
public async Task<MovieRequests> UpdateRequest([FromBody] MovieRequests model)
|
||||
{
|
||||
return await MovieRequestEngine.UpdateMovieRequest(model);
|
||||
|
@ -115,6 +118,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("movie/approve")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> ApproveMovie([FromBody] MovieUpdateModel model)
|
||||
{
|
||||
return await MovieRequestEngine.ApproveMovieById(model.Id);
|
||||
|
@ -126,6 +130,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("movie/available")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> MarkMovieAvailable([FromBody] MovieUpdateModel model)
|
||||
{
|
||||
return await MovieRequestEngine.MarkAvailable(model.Id);
|
||||
|
@ -137,6 +142,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("movie/unavailable")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> MarkMovieUnAvailable([FromBody] MovieUpdateModel model)
|
||||
{
|
||||
return await MovieRequestEngine.MarkUnavailable(model.Id);
|
||||
|
@ -148,23 +154,12 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("movie/deny")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> DenyMovie([FromBody] MovieUpdateModel model)
|
||||
{
|
||||
return await MovieRequestEngine.DenyMovieById(model.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tv requests.
|
||||
/// </summary>
|
||||
/// <param name="count">The count of items you want to return.</param>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("tv/{count:int}/{position:int}/tree")]
|
||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetTvRequestsTree(int count, int position)
|
||||
{
|
||||
return await TvRequestEngine.GetRequestsTreeNode(count, position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total amount of TV requests.
|
||||
/// </summary>
|
||||
|
@ -267,23 +262,13 @@ namespace Ombi.Controllers
|
|||
return await TvRequestEngine.SearchTvRequest(searchTerm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a specific tv request
|
||||
/// </summary>
|
||||
/// <param name="searchTerm">The search term.</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("tv/search/{searchTerm}/tree")]
|
||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvTree(string searchTerm)
|
||||
{
|
||||
return await TvRequestEngine.SearchTvRequestTree(searchTerm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the a specific tv request
|
||||
/// </summary>
|
||||
/// <param name="requestId">The request identifier.</param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("tv/{requestId:int}")]
|
||||
[PowerUser]
|
||||
public async Task DeleteTvRequest(int requestId)
|
||||
{
|
||||
await TvRequestEngine.RemoveTvRequest(requestId);
|
||||
|
@ -295,17 +280,47 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The model.</param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("tv")]
|
||||
[PowerUser]
|
||||
public async Task<TvRequests> UpdateRequest([FromBody] TvRequests model)
|
||||
{
|
||||
return await TvRequestEngine.UpdateTvRequest(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the root path for this tv show
|
||||
/// </summary>
|
||||
/// <param name="requestId"></param>
|
||||
/// <param name="rootFolderId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("tv/root/{requestId:int}/{rootFolderId:int}")]
|
||||
[PowerUser]
|
||||
public async Task<bool> UpdateRootFolder(int requestId, int rootFolderId)
|
||||
{
|
||||
await TvRequestEngine.UpdateRootPath(requestId, rootFolderId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the quality profile for this tv show
|
||||
/// </summary>
|
||||
/// <param name="requestId"></param>
|
||||
/// <param name="qualityId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("tv/quality/{requestId:int}/{qualityId:int}")]
|
||||
[PowerUser]
|
||||
public async Task<bool> UpdateQuality(int requestId, int qualityId)
|
||||
{
|
||||
await TvRequestEngine.UpdateQualityProfile(requestId, qualityId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the a specific child request
|
||||
/// </summary>
|
||||
/// <param name="child">The model.</param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("tv/child")]
|
||||
[PowerUser]
|
||||
public async Task<ChildRequests> UpdateChild([FromBody] ChildRequests child)
|
||||
{
|
||||
return await TvRequestEngine.UpdateChildRequest(child);
|
||||
|
@ -317,6 +332,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">This is the child request's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPut("tv/deny")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> DenyChild([FromBody] TvUpdateModel model)
|
||||
{
|
||||
return await TvRequestEngine.DenyChildRequest(model.Id);
|
||||
|
@ -328,6 +344,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("tv/available")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> MarkTvAvailable([FromBody] TvUpdateModel model)
|
||||
{
|
||||
return await TvRequestEngine.MarkAvailable(model.Id);
|
||||
|
@ -339,6 +356,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">The Movie's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("tv/unavailable")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> MarkTvUnAvailable([FromBody] TvUpdateModel model)
|
||||
{
|
||||
return await TvRequestEngine.MarkUnavailable(model.Id);
|
||||
|
@ -350,6 +368,7 @@ namespace Ombi.Controllers
|
|||
/// <param name="model">This is the child request's ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("tv/approve")]
|
||||
[PowerUser]
|
||||
public async Task<RequestEngineResult> ApproveChild([FromBody] TvUpdateModel model)
|
||||
{
|
||||
return await TvRequestEngine.ApproveChildRequest(model.Id);
|
||||
|
@ -360,6 +379,7 @@ namespace Ombi.Controllers
|
|||
/// </summary>
|
||||
/// <param name="requestId">The model.</param>
|
||||
/// <returns></returns>
|
||||
[PowerUser]
|
||||
[HttpDelete("tv/child/{requestId:int}")]
|
||||
public async Task<bool> DeleteChildRequest(int requestId)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
@ -127,31 +126,6 @@ namespace Ombi.Controllers
|
|||
return await TvEngine.Search(searchTerm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a Tv Show.
|
||||
/// </summary>
|
||||
/// <param name="searchTerm">The search term.</param>
|
||||
/// <remarks>We use TvMaze as the Provider</remarks>
|
||||
/// <returns></returns>
|
||||
[HttpGet("tv/{searchTerm}/tree")]
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> SearchTvTreeNode(string searchTerm)
|
||||
{
|
||||
return await TvEngine.SearchTreeNode(searchTerm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets extra show information.
|
||||
/// </summary>
|
||||
/// <param name="tvdbId">The TVDB identifier.</param>
|
||||
/// <remarks>We use TvMaze as the Provider</remarks>
|
||||
/// <returns></returns>
|
||||
[HttpGet("tv/info/{tvdbId}/tree")]
|
||||
public async Task<TreeNode<SearchTvShowViewModel>> GetShowInfoTreeNode(int tvdbId)
|
||||
{
|
||||
if (tvdbId == 0) return new TreeNode<SearchTvShowViewModel>();
|
||||
return await TvEngine.GetShowInformationTreeNode(tvdbId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets extra show information.
|
||||
/// </summary>
|
||||
|
@ -164,17 +138,6 @@ namespace Ombi.Controllers
|
|||
return await TvEngine.GetShowInformation(tvdbId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Popular Tv Shows
|
||||
/// </summary>
|
||||
/// <remarks>We use Trakt.tv as the Provider</remarks>
|
||||
/// <returns></returns>
|
||||
[HttpGet("tv/popular/tree")]
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> PopularTvTree()
|
||||
{
|
||||
return await TvEngine.PopularTree();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Popular Tv Shows
|
||||
/// </summary>
|
||||
|
@ -186,18 +149,6 @@ namespace Ombi.Controllers
|
|||
return await TvEngine.Popular();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns most Anticiplateds tv shows.
|
||||
/// </summary>
|
||||
/// <remarks>We use Trakt.tv as the Provider</remarks>
|
||||
/// <returns></returns>
|
||||
[HttpGet("tv/anticipated/tree")]
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> AnticipatedTvTree()
|
||||
{
|
||||
return await TvEngine.AnticipatedTree();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns most Anticiplateds tv shows.
|
||||
/// </summary>
|
||||
|
@ -209,16 +160,6 @@ namespace Ombi.Controllers
|
|||
return await TvEngine.Anticipated();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Most watched shows.
|
||||
/// </summary>
|
||||
/// <remarks>We use Trakt.tv as the Provider</remarks>
|
||||
/// <returns></returns>
|
||||
[HttpGet("tv/mostwatched/tree")]
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatchedTree()
|
||||
{
|
||||
return await TvEngine.MostWatchesTree();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Most watched shows.
|
||||
|
@ -231,17 +172,6 @@ namespace Ombi.Controllers
|
|||
return await TvEngine.MostWatches();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns trending shows
|
||||
/// </summary>
|
||||
/// <remarks>We use Trakt.tv as the Provider</remarks>
|
||||
/// <returns></returns>
|
||||
[HttpGet("tv/trending/tree")]
|
||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> TrendingTree()
|
||||
{
|
||||
return await TvEngine.TrendingTree();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns trending shows
|
||||
/// </summary>
|
||||
|
|
10
src/Ombi/Models/Identity/SaveWizardResult.cs
Normal file
10
src/Ombi/Models/Identity/SaveWizardResult.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Models.Identity
|
||||
{
|
||||
public class SaveWizardResult
|
||||
{
|
||||
public bool Result { get; set; }
|
||||
public List<string> Errors { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers>
|
||||
|
@ -9,6 +8,7 @@
|
|||
<FileVersion>$(SemVer)</FileVersion>
|
||||
<Version>$(FullVer)</Version>
|
||||
<PackageVersion></PackageVersion>
|
||||
<TypeScriptToolsVersion>2.8</TypeScriptToolsVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ServerGarbageCollection>false</ServerGarbageCollection>
|
||||
|
@ -24,10 +24,6 @@
|
|||
<DefineConstants>TRACE;RELEASE;NETCOREAPP2_0</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="NpmCommandsDebug" Condition="'$(Configuration)'=='Debug'" AfterTargets="Build">
|
||||
<Exec Command="npm run-script vendor" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Files not to show in IDE -->
|
||||
<Compile Remove="Logs\**" />
|
||||
|
@ -46,7 +42,17 @@
|
|||
<None Remove="Styles\**" />
|
||||
<None Remove="wwwroot\dist\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="NpmCommandsDebug" Condition="'$(Configuration)'=='Debug'" AfterTargets="Build">
|
||||
<Exec Command="npm run vendor" />
|
||||
</Target>
|
||||
<!-- Disabled as run by CI once for many different runtime builds
|
||||
<Target Name="NpmCommandsRelease" Condition="'$(Configuration)'=='Release'" AfterTargets="Build">
|
||||
<Exec Command="npm run publish" />
|
||||
</Target>
|
||||
-->
|
||||
<Target Name="NpmCommandsClean" AfterTargets="Clean">
|
||||
<Exec Command="npm run clean" />
|
||||
</Target>
|
||||
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
|
||||
<ItemGroup>
|
||||
<DistFiles Include="wwwroot\dist\**" />
|
||||
|
@ -56,8 +62,6 @@
|
|||
</ResolvedFileToPublish>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="6.1.1" />
|
||||
<PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
|
||||
|
@ -94,18 +98,4 @@
|
|||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Updater\Ombi.Updater.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="wwwroot\loading.css" />
|
||||
<None Include="wwwroot\translations\*.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="ClientApp\app\animations\fadeinout.ts" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<TypeScriptCompile Include="ClientApp\app\animations\fadeinout.ts" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -156,7 +156,12 @@ namespace Ombi
|
|||
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
|
||||
{
|
||||
HotModuleReplacement = true,
|
||||
ConfigFile = "webpack.dev.js"
|
||||
ConfigFile = "webpack.config.ts",
|
||||
|
||||
//EnvParam = new
|
||||
//{
|
||||
// aot = true // can't use AOT with HMR currently https://github.com/angular/angular-cli/issues/6347
|
||||
//}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -98,8 +98,10 @@
|
|||
|
||||
<link rel="stylesheet" href="~/loading.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/dist/vendor.css" asp-append-version="true" />
|
||||
<environment include="Development">
|
||||
<script src="~/dist/vendor.js" asp-append-version="true" defer></script>
|
||||
<script src="~/dist/main.js" asp-append-version="true" defer></script>
|
||||
</environment>
|
||||
<script src="~/dist/app.js" asp-append-version="true" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
@{
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
'use strict';
|
||||
"use strict";
|
||||
|
||||
const gulp = require('gulp');
|
||||
const run = require('gulp-run');
|
||||
const runSequence = require('run-sequence');
|
||||
const del = require('del');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const gulp = require("gulp");
|
||||
const run = require("gulp-run");
|
||||
const runSequence = require("run-sequence");
|
||||
const del = require("del");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
const outputDir = './wwwroot/dist';
|
||||
const outputDir = "./wwwroot/dist";
|
||||
global.aot = true;
|
||||
|
||||
function getEnvOptions() {
|
||||
var options = [];
|
||||
const options = [];
|
||||
if (global.prod) {
|
||||
options.push('--env.prod');
|
||||
options.push("--env.prod");
|
||||
}
|
||||
if (global.analyse) {
|
||||
options.push('--env.analyse');
|
||||
options.push("--env.analyse");
|
||||
}
|
||||
if (global.aot) {
|
||||
options.push("--env.aot");
|
||||
}
|
||||
if (options.length > 0) {
|
||||
return " " + options.join(" ");
|
||||
|
@ -24,12 +28,12 @@ function getEnvOptions() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function webpack(vendor) {
|
||||
return run(`webpack --config webpack.config${vendor ? '.vendor' : ''}.ts${getEnvOptions()}`).exec();
|
||||
function webpack(type) {
|
||||
// 'webpack' instead of direct path can cause https://github.com/angular/angular-cli/issues/6417
|
||||
return run(`node node_modules\\webpack\\bin\\webpack.js --config webpack.config${type ? `.${type}` : ""}.ts${getEnvOptions()}`).exec();
|
||||
}
|
||||
|
||||
gulp.task('vendor', () => {
|
||||
gulp.task("vendor", () => {
|
||||
let build = false;
|
||||
const vendorPath = path.join(outputDir, "vendor.js");
|
||||
const vendorExists = fs.existsSync(vendorPath);
|
||||
|
@ -37,44 +41,47 @@ gulp.task('vendor', () => {
|
|||
const vendorStat = fs.statSync(vendorPath);
|
||||
const packageStat = fs.statSync("package.json");
|
||||
const vendorConfigStat = fs.statSync("webpack.config.vendor.ts");
|
||||
const commonConfigStat = fs.statSync("webpack.config.common.ts");
|
||||
if (packageStat.mtime > vendorStat.mtime) {
|
||||
build = true;
|
||||
}
|
||||
if (vendorConfigStat.mtime > vendorStat.mtime) {
|
||||
build = true;
|
||||
}
|
||||
if (commonConfigStat.mtime > vendorStat.mtime) {
|
||||
build = true;
|
||||
}
|
||||
} else {
|
||||
build = true;
|
||||
}
|
||||
if (build) {
|
||||
return webpack(true);
|
||||
return webpack("vendor");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
gulp.task('vendor_force', () => {
|
||||
return webpack(true);
|
||||
gulp.task("vendor_force", () => {
|
||||
return webpack("vendor");
|
||||
})
|
||||
|
||||
gulp.task('main', () => {
|
||||
gulp.task("main", () => {
|
||||
return webpack()
|
||||
});
|
||||
|
||||
gulp.task('prod_var', () => {
|
||||
gulp.task("prod_var", () => {
|
||||
global.prod = true;
|
||||
})
|
||||
|
||||
gulp.task('analyse_var', () => {
|
||||
gulp.task("analyse_var", () => {
|
||||
global.analyse = true;
|
||||
})
|
||||
|
||||
gulp.task('clean', () => {
|
||||
gulp.task("clean", () => {
|
||||
del.sync(outputDir, { force: true });
|
||||
});
|
||||
|
||||
|
||||
gulp.task('lint', () => run("npm run lint").exec());
|
||||
gulp.task('build', callback => runSequence('vendor', 'main', callback));
|
||||
gulp.task('analyse', callback => runSequence('analyse_var', 'build'));
|
||||
gulp.task('full', callback => runSequence('clean', 'build'));
|
||||
gulp.task('publish', callback => runSequence('prod_var', 'build'));
|
||||
gulp.task("lint", () => run("npm run lint").exec());
|
||||
gulp.task("lint_fix", () => run("npm run lint -- --fix").exec());
|
||||
gulp.task("build", callback => runSequence("vendor", "main", callback));
|
||||
gulp.task("analyse", callback => runSequence("analyse_var", "clean", "build", callback));
|
||||
gulp.task("full", callback => runSequence("clean", "build", callback));
|
||||
gulp.task("publish", callback => runSequence("prod_var", "full", callback));
|
13399
src/Ombi/package-lock.json
generated
13399
src/Ombi/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,94 +1,93 @@
|
|||
{
|
||||
"name": "ombi",
|
||||
"version": "2.6.0",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"vendor": "gulp vendor",
|
||||
"main": "gulp main",
|
||||
"lint": "tslint -p .",
|
||||
"publish": "gulp publish",
|
||||
"lint": "tslint ClientApp/**/*.ts",
|
||||
"restore": "dotnet restore && npm install"
|
||||
"restore": "dotnet restore && yarn install",
|
||||
"clean": "gulp clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^5.2.5",
|
||||
"@angular/cdk": "^5.2.1",
|
||||
"@angular/common": "^5.2.5",
|
||||
"@angular/compiler": "^5.2.5",
|
||||
"@angular/core": "^5.2.5",
|
||||
"@angular/forms": "^5.2.5",
|
||||
"@angular/http": "^5.2.5",
|
||||
"@angular/material": "^5.2.1",
|
||||
"@angular/platform-browser": "^5.2.5",
|
||||
"@angular/platform-browser-dynamic": "^5.2.5",
|
||||
"@angular/platform-server": "^5.2.5",
|
||||
"@angular/router": "^5.2.5",
|
||||
"@auth0/angular-jwt": "1.0.0-beta.9",
|
||||
"@ng-bootstrap/ng-bootstrap": "^1.0.0",
|
||||
"@ngu/carousel": "^1.4.8",
|
||||
"@ngx-translate/core": "^8.0.0",
|
||||
"@ngx-translate/http-loader": "^2.0.1",
|
||||
"@types/core-js": "^0.9.46",
|
||||
"@types/extract-text-webpack-plugin": "^3.0.1",
|
||||
"@types/intro.js": "^2.4.3",
|
||||
"@types/node": "^8.9.4",
|
||||
"@types/webpack": "^3.8.7",
|
||||
"angular-router-loader": "^0.8.2",
|
||||
"angular2-moment": "^1.8.0",
|
||||
"@angular/animations": "^6.0.7",
|
||||
"@angular/cdk": "^6.3.1",
|
||||
"@angular/common": "^6.0.7",
|
||||
"@angular/compiler": "^6.0.7",
|
||||
"@angular/compiler-cli": "^6.0.7",
|
||||
"@angular/core": "^6.0.7",
|
||||
"@angular/forms": "^6.0.7",
|
||||
"@angular/http": "^6.0.7",
|
||||
"@angular/material": "^6.3.1",
|
||||
"@angular/platform-browser": "^6.0.7",
|
||||
"@angular/platform-browser-dynamic": "^6.0.7",
|
||||
"@angular/platform-server": "^6.0.7",
|
||||
"@angular/router": "^6.0.7",
|
||||
"@auth0/angular-jwt": "^2.0.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^2.2.0",
|
||||
"@ngtools/webpack": "^6.1.0-beta.2",
|
||||
"@ngu/carousel": "^1.4.9-beta-2",
|
||||
"@ngx-translate/core": "^10.0.2",
|
||||
"@ngx-translate/http-loader": "^3.0.1",
|
||||
"@types/core-js": "^2.5.0",
|
||||
"@types/mini-css-extract-plugin": "^0.2.0",
|
||||
"@types/node": "^10.5.1",
|
||||
"@types/webpack": "^4.4.4",
|
||||
"@types/webpack-bundle-analyzer": "^2.9.2",
|
||||
"@types/webpack-merge": "^4.1.3",
|
||||
"angular-router-loader": "^0.8.5",
|
||||
"angular2-template-loader": "^0.6.2",
|
||||
"aspnet-webpack": "^2.0.3",
|
||||
"awesome-typescript-loader": "^3.4.1",
|
||||
"aspnet-webpack": "^3.0.0",
|
||||
"awesome-typescript-loader": "^5.2.0",
|
||||
"bootstrap": "3.3.7",
|
||||
"bootswatch": "3.3.7",
|
||||
"core-js": "^2.5.3",
|
||||
"css": "^2.2.1",
|
||||
"css-loader": "^0.28.9",
|
||||
"copy-webpack-plugin": "^4.5.2",
|
||||
"core-js": "^2.5.7",
|
||||
"css": "^2.2.3",
|
||||
"css-loader": "^0.28.11",
|
||||
"del": "^3.0.0",
|
||||
"event-source-polyfill": "^0.0.11",
|
||||
"expose-loader": "^0.7.4",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^1.1.6",
|
||||
"event-source-polyfill": "^0.0.12",
|
||||
"expose-loader": "^0.7.5",
|
||||
"file-loader": "^1.1.11",
|
||||
"font-awesome": "^4.7.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-run": "^1.7.1",
|
||||
"hammerjs": "^2.0.8",
|
||||
"html-loader": "0.5.1",
|
||||
"html-loader": "^0.5.5",
|
||||
"jquery": "^3.3.1",
|
||||
"mini-css-extract-plugin": "^0.4.1",
|
||||
"moment": "^2.22.2",
|
||||
"ng2-cookies": "^1.0.12",
|
||||
"ngx-clipboard": "8.1.1",
|
||||
"ngx-infinite-scroll": "^0.6.1",
|
||||
"ngx-clipboard": "^11.1.1",
|
||||
"ngx-infinite-scroll": "^6.0.1",
|
||||
"ngx-moment": "^3.0.1",
|
||||
"ngx-order-pipe": "^2.0.1",
|
||||
"node-sass": "^4.7.2",
|
||||
"npm": "^5.6.0",
|
||||
"node-sass": "^4.9.0",
|
||||
"pace-progress": "^1.0.2",
|
||||
"primeng": "5.0.2",
|
||||
"reflect-metadata": "0.1.10",
|
||||
"primeng": "^6.0.0",
|
||||
"raw-loader": "^0.5.1",
|
||||
"reflect-metadata": "^0.1.12",
|
||||
"run-sequence": "^2.2.1",
|
||||
"rxjs": "5.5.2",
|
||||
"sass-loader": "^6.0.6",
|
||||
"style-loader": "^0.19.1",
|
||||
"rxjs": "^6.2.1",
|
||||
"sass-loader": "^7.0.3",
|
||||
"style-loader": "^0.21.0",
|
||||
"to-string-loader": "^1.1.5",
|
||||
"ts-node": "^3.3.0",
|
||||
"tslint": "^5.9.1",
|
||||
"tslint-language-service": "^0.9.8",
|
||||
"typescript": "^2.7.1",
|
||||
"uglify-es": "^3.3.10",
|
||||
"uglifyjs-webpack-plugin": "^1.1.8",
|
||||
"url-loader": "^0.6.2",
|
||||
"webpack": "^3.11.0",
|
||||
"webpack-bundle-analyzer": "^2.10.0",
|
||||
"webpack-hot-middleware": "^2.21.0",
|
||||
"zone.js": "^0.8.20"
|
||||
"ts-node": "^7.0.0",
|
||||
"tslint": "^5.10.0",
|
||||
"tslint-language-service": "^0.9.9",
|
||||
"typescript": "2.7.2",
|
||||
"uglify-es": "^3.3.9",
|
||||
"uglifyjs-webpack-plugin": "^1.2.7",
|
||||
"url-loader": "^1.0.1",
|
||||
"webpack": "^4.14.0",
|
||||
"webpack-bundle-analyzer": "^2.13.1",
|
||||
"webpack-cli": "^3.0.8",
|
||||
"webpack-dev-middleware": "^3.1.3",
|
||||
"webpack-hot-middleware": "^2.22.2",
|
||||
"webpack-merge": "^4.1.3",
|
||||
"zone.js": "^0.8.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "4.0.4",
|
||||
"@types/jasmine": "2.6.2",
|
||||
"chai": "4.1.2",
|
||||
"jasmine-core": "2.8.0",
|
||||
"karma": "1.7.1",
|
||||
"karma-chai": "0.1.0",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
"karma-cli": "1.0.1",
|
||||
"karma-jasmine": "1.1.0",
|
||||
"karma-webpack": "2.0.5"
|
||||
"resolutions": {
|
||||
"@types/tapable": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es6",
|
||||
"es2017",
|
||||
"dom"
|
||||
],
|
||||
"moduleResolution": "node",
|
||||
|
@ -27,12 +27,15 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"bin",
|
||||
"node_modules",
|
||||
"TempUpdate"
|
||||
"include": [
|
||||
"ClientApp/**/*",
|
||||
"typings/**/*",
|
||||
"webpack.config.*"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"preserveWhitespaces": false
|
||||
}
|
||||
"preserveWhitespaces": false,
|
||||
"genDir": "./ClientApp/app/ngfactory",
|
||||
"entryModule": "ClientApp/app/app.module#AppModule"
|
||||
},
|
||||
"compileOnSave": false
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
],
|
||||
"max-line-length": [
|
||||
true,
|
||||
200
|
||||
250
|
||||
],
|
||||
"arrow-parens": false,
|
||||
"radix": false,
|
||||
|
@ -16,22 +16,33 @@
|
|||
"indent": [
|
||||
false
|
||||
],
|
||||
"whitespace": [
|
||||
false
|
||||
],
|
||||
"no-unused-expression": [
|
||||
true,
|
||||
"allow-new"
|
||||
],
|
||||
"no-trailing-whitespace": [
|
||||
false
|
||||
],
|
||||
"max-classes-per-file": [
|
||||
false
|
||||
],
|
||||
"no-shadowed-variable": false,
|
||||
"comment-format": [
|
||||
false
|
||||
],
|
||||
"no-namespace": [
|
||||
false
|
||||
],
|
||||
"no-internal-module": [
|
||||
false
|
||||
],
|
||||
"whitespace": [
|
||||
false,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
],
|
||||
"no-trailing-whitespace":[
|
||||
false
|
||||
]
|
||||
}
|
||||
}
|
4
src/Ombi/typings/globals.d.ts
vendored
Normal file
4
src/Ombi/typings/globals.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Globals
|
||||
|
||||
declare module "pace-progress";
|
||||
declare var __webpack_public_path__: any;
|
12
src/Ombi/typings/globals/globals.d.ts
vendored
12
src/Ombi/typings/globals/globals.d.ts
vendored
|
@ -1,12 +0,0 @@
|
|||
// Globals
|
||||
|
||||
declare var module: any;
|
||||
declare var require: any;
|
||||
declare var localStorage: any;
|
||||
declare var introJs: any;
|
||||
|
||||
declare var __webpack_public_path__: any;
|
||||
|
||||
declare module "pace-progress";
|
||||
declare module "webpack-bundle-analyzer";
|
||||
declare module "uglifyjs-webpack-plugin";
|
2
src/Ombi/typings/index.d.ts
vendored
2
src/Ombi/typings/index.d.ts
vendored
|
@ -1 +1 @@
|
|||
/// <reference path="globals/globals.d.ts" />
|
||||
/// <reference path="globals.d.ts" />
|
||||
|
|
83
src/Ombi/webpack.config.common.ts
Normal file
83
src/Ombi/webpack.config.common.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
import { AngularCompilerPlugin } from "@ngtools/webpack";
|
||||
import * as MiniCssExtractPlugin from "mini-css-extract-plugin";
|
||||
import * as path from "path";
|
||||
import { Configuration, ContextReplacementPlugin, ProvidePlugin } from "webpack";
|
||||
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
||||
|
||||
export const outputDir = "./wwwroot/dist";
|
||||
|
||||
export function isProd(env: any) {
|
||||
return env && env.prod as boolean;
|
||||
}
|
||||
|
||||
export function isAOT(env: any) {
|
||||
return env && env.aot as boolean;
|
||||
}
|
||||
|
||||
export const WebpackCommonConfig = (env: any, type: string) => {
|
||||
const prod = isProd(env);
|
||||
const aot = isAOT(env);
|
||||
const vendor = type === "vendor";
|
||||
console.log(`${prod ? "Production" : "Dev"} ${type} build`);
|
||||
console.log(`Output directory: ${outputDir}`);
|
||||
console.log(`${aot ? "Using" : "Not using"} AOT compiler`);
|
||||
const analyse = env && env.analyse as boolean;
|
||||
if (analyse) { console.log("Analysing build"); }
|
||||
const cssLoader = prod ? "css-loader?minimize" : "css-loader";
|
||||
const bundleConfig: Configuration = {
|
||||
mode: prod ? "production" : "development",
|
||||
resolve: {
|
||||
extensions: [".ts", ".js"],
|
||||
alias: {
|
||||
pace: "pace-progress",
|
||||
},
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(outputDir),
|
||||
filename: "[name].js",
|
||||
chunkFilename: "[id].chunk.js",
|
||||
publicPath: "/dist/",
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.ts$/, loader: aot ? "@ngtools/webpack" : ["awesome-typescript-loader?silent=true", "angular2-template-loader", "angular-router-loader"] },
|
||||
{ test: /\.html$/, use: "html-loader?minimize=false" },
|
||||
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, cssLoader] },
|
||||
{ test: /\.scss$/, exclude: /ClientApp/, use: [MiniCssExtractPlugin.loader, cssLoader, "sass-loader"] },
|
||||
{ test: /\.scss$/, include: /ClientApp(\\|\/)app/, use: ["to-string-loader", cssLoader, "sass-loader"] },
|
||||
{ test: /\.scss$/, include: /ClientApp(\\|\/)styles/, use: ["style-loader", cssLoader, "sass-loader"] },
|
||||
{ test: /\.(png|jpg|jpeg|gif|woff|woff2|eot|ttf|svg)(\?|$)/, use: "url-loader?limit=8192" },
|
||||
{ test: /[\/\\]@angular[\/\\].+\.js$/, parser: { system: true } }, // ignore System.import warnings https://github.com/angular/angular/issues/21560
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: "[name].css",
|
||||
}),
|
||||
new ProvidePlugin({ $: "jquery", jQuery: "jquery", Hammer: "hammerjs/hammer" }), // Global identifiers
|
||||
].concat(aot && !vendor ? [
|
||||
new AngularCompilerPlugin({
|
||||
mainPath: "./ClientApp/main.ts",
|
||||
tsConfigPath: "./tsconfig.json",
|
||||
skipCodeGeneration: false,
|
||||
compilerOptions: {
|
||||
noEmit: false,
|
||||
},
|
||||
}),
|
||||
] : [
|
||||
// AOT chunk splitting does not work while this is active but doesn't seem to be needed under AOT anyway https://github.com/angular/angular-cli/issues/4431
|
||||
new ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)/, path.join(__dirname, "./ClientApp")), // Workaround for https://github.com/angular/angular/issues/14898
|
||||
]).concat(analyse ? [
|
||||
new BundleAnalyzerPlugin({
|
||||
analyzerMode: "static",
|
||||
reportFilename: `${type}.html`,
|
||||
openAnalyzer: false,
|
||||
}),
|
||||
] : []),
|
||||
node: {
|
||||
fs: "empty",
|
||||
},
|
||||
};
|
||||
|
||||
return bundleConfig;
|
||||
};
|
|
@ -1,59 +1,26 @@
|
|||
import { CheckerPlugin } from "awesome-typescript-loader";
|
||||
import * as path from "path";
|
||||
import * as UglifyJSPlugin from "uglifyjs-webpack-plugin";
|
||||
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
||||
import { Configuration, DllReferencePlugin } from "webpack";
|
||||
import * as webpackMerge from "webpack-merge";
|
||||
|
||||
import * as webpack from "webpack";
|
||||
import { isAOT, isProd, outputDir, WebpackCommonConfig } from "./webpack.config.common";
|
||||
|
||||
module.exports = (env: any) => {
|
||||
// const prod = env && env.prod as boolean;
|
||||
const prod = true;
|
||||
console.log(prod ? "Production" : "Dev" + " main build");
|
||||
const analyse = env && env.analyse as boolean;
|
||||
if (analyse) { console.log("Analysing build"); }
|
||||
const cssLoader = prod ? "css-loader?-url&minimize" : "css-loader?-url";
|
||||
const outputDir = "./wwwroot/dist";
|
||||
const bundleConfig: webpack.Configuration = {
|
||||
entry: { main: "./ClientApp/main.ts" },
|
||||
stats: { modules: false },
|
||||
context: __dirname,
|
||||
resolve: { extensions: [".ts", ".js"] },
|
||||
const prod = isProd(env);
|
||||
const aot = isAOT(env);
|
||||
if (!prod && aot) { console.warn("Vendor dll bundle will not be used as AOT is enabled"); }
|
||||
const bundleConfig: Configuration = webpackMerge(WebpackCommonConfig(env, "main"), {
|
||||
entry: {
|
||||
app: "./ClientApp/main.ts",
|
||||
},
|
||||
devtool: prod ? "source-map" : "eval-source-map",
|
||||
output: {
|
||||
filename: "[name].js",
|
||||
chunkFilename: "[id].[chunkhash].js",
|
||||
publicPath: "/dist/",
|
||||
path: path.join(__dirname, outputDir),
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.ts$/, include: /ClientApp/, use: ["awesome-typescript-loader?silent=true", "angular2-template-loader", "angular-router-loader"] },
|
||||
{ test: /\.html$/, use: "html-loader?minimize=false" },
|
||||
{ test: /\.css$/, use: ["to-string-loader", cssLoader] },
|
||||
{ test: /\.scss$/, include: /ClientApp(\\|\/)app/, use: ["to-string-loader", cssLoader, "sass-loader"] },
|
||||
{ test: /\.scss$/, include: /ClientApp(\\|\/)styles/, use: ["style-loader", cssLoader, "sass-loader"] },
|
||||
{ test: /\.(png|jpg|jpeg|gif|svg)$/, use: "url-loader?limit=25000" },
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new CheckerPlugin(),
|
||||
new webpack.DllReferencePlugin({
|
||||
plugins: prod || aot ? [] : [
|
||||
// AOT chunk splitting does not work while this is active https://github.com/angular/angular-cli/issues/4565
|
||||
new DllReferencePlugin({
|
||||
context: __dirname,
|
||||
manifest: require(path.join(__dirname, outputDir, "vendor-manifest.json")),
|
||||
}),
|
||||
].concat(prod ? [
|
||||
// Plugins that apply in production builds only
|
||||
new UglifyJSPlugin({ sourceMap: true }),
|
||||
] : [
|
||||
// Plugins that apply in development builds only
|
||||
]).concat(analyse ? [
|
||||
new BundleAnalyzerPlugin({
|
||||
analyzerMode: "static",
|
||||
reportFilename: "main.html",
|
||||
openAnalyzer: false,
|
||||
}),
|
||||
] : []),
|
||||
};
|
||||
],
|
||||
});
|
||||
|
||||
return bundleConfig;
|
||||
};
|
||||
|
|
|
@ -1,41 +1,25 @@
|
|||
import * as ExtractTextPlugin from "extract-text-webpack-plugin";
|
||||
import * as path from "path";
|
||||
import * as UglifyJSPlugin from "uglifyjs-webpack-plugin";
|
||||
import * as webpack from "webpack";
|
||||
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
||||
import * as webpackMerge from "webpack-merge";
|
||||
import { isProd, outputDir, WebpackCommonConfig } from "./webpack.config.common";
|
||||
|
||||
module.exports = (env: any) => {
|
||||
const extractCSS = new ExtractTextPlugin("vendor.css");
|
||||
const prod = env && env.prod as boolean;
|
||||
console.log(prod ? "Production" : "Dev" + " vendor build");
|
||||
const analyse = env && env.analyse as boolean;
|
||||
if (analyse) { console.log("Analysing build"); }
|
||||
const outputDir = "./wwwroot/dist";
|
||||
const bundleConfig = {
|
||||
stats: { modules: false },
|
||||
resolve: {
|
||||
extensions: [".js"],
|
||||
alias: {
|
||||
pace: "pace-progress",
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.(png|woff|woff2|eot|ttf|svg|gif)(\?|$)/, use: "url-loader?limit=100000" },
|
||||
{ test: /\.css(\?|$)/, use: extractCSS.extract({ use: prod ? "css-loader?minimize" : "css-loader" }) },
|
||||
{ test: /\.scss(\?|$)/, use: extractCSS.extract({ use: [prod ? "css-loader?minimize" : "css-loader", "sass-loader"] }) },
|
||||
],
|
||||
const prod = isProd(env);
|
||||
const bundleConfig = webpackMerge(WebpackCommonConfig(env, "vendor"), {
|
||||
output: {
|
||||
library: "[name]_[hash]",
|
||||
},
|
||||
entry: {
|
||||
vendor: [
|
||||
vendor: (<string[]>[ // add any vendor styles here e.g. bootstrap/dist/css/bootstrap.min.css
|
||||
"pace-progress/themes/orange/pace-theme-flash.css",
|
||||
"primeng/resources/primeng.min.css",
|
||||
"@angular/material/prebuilt-themes/deeppurple-amber.css",
|
||||
"font-awesome/scss/font-awesome.scss",
|
||||
"bootswatch/superhero/bootstrap.min.css",
|
||||
|
||||
]).concat(prod ? [] : [ // used to speed up dev launch time
|
||||
"@angular/animations",
|
||||
"@angular/common",
|
||||
"@angular/common/http",
|
||||
"@angular/compiler",
|
||||
"@angular/core",
|
||||
"@angular/forms",
|
||||
|
@ -67,41 +51,14 @@ module.exports = (env: any) => {
|
|||
"@ngx-translate/core",
|
||||
"@ngx-translate/http-loader",
|
||||
"ngx-order-pipe",
|
||||
//"smartbanner.js/dist/smartbanner.js",
|
||||
//"smartbanner.js/dist/smartbanner.css",
|
||||
],
|
||||
]),
|
||||
},
|
||||
output: {
|
||||
publicPath: "/dist/",
|
||||
filename: "[name].js",
|
||||
library: "[name]_[hash]",
|
||||
path: path.join(__dirname, outputDir),
|
||||
},
|
||||
node: {
|
||||
fs: "empty",
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", Hammer: "hammerjs/hammer" }), // Global identifiers
|
||||
new webpack.ContextReplacementPlugin(/\@angular(\\|\/)core(\\|\/)esm5/, path.join(__dirname, "./client")), // Workaround for https://github.com/angular/angular/issues/20357
|
||||
new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, "./ClientApp")), // Workaround for https://github.com/angular/angular/issues/11580
|
||||
new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, path.join(__dirname, "./ClientApp")), // Workaround for https://github.com/angular/angular/issues/14898
|
||||
extractCSS,
|
||||
plugins: prod ? [] : [
|
||||
new webpack.DllPlugin({
|
||||
path: path.join(__dirname, outputDir, "[name]-manifest.json"),
|
||||
name: "[name]_[hash]",
|
||||
}),
|
||||
].concat(prod ? [
|
||||
// Plugins that apply in production builds only
|
||||
new UglifyJSPlugin(),
|
||||
] : [
|
||||
// Plugins that apply in development builds only
|
||||
]).concat(analyse ? [
|
||||
new BundleAnalyzerPlugin({
|
||||
analyzerMode: "static",
|
||||
reportFilename: "vendor.html",
|
||||
openAnalyzer: false,
|
||||
}),
|
||||
] : []),
|
||||
};
|
||||
],
|
||||
});
|
||||
return bundleConfig;
|
||||
};
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
// https://github.com/aspnet/JavaScriptServices/issues/1046
|
||||
|
||||
require('ts-node/register')
|
||||
module.exports = require("./webpack.config.ts");
|
|
@ -62,9 +62,12 @@
|
|||
"Italian": "Italiensk",
|
||||
"Danish": "Dansk",
|
||||
"Dutch": "Hollandsk",
|
||||
"Norwegian": "Norsk"
|
||||
"Norwegian": "Norsk",
|
||||
"BrazillianPortuguese": "Brazillian Portuguese",
|
||||
"Polish": "Polish"
|
||||
},
|
||||
"OpenMobileApp": "Åbn mobilapp"
|
||||
"OpenMobileApp": "Åbn mobilapp",
|
||||
"RecentlyAdded": "Recently Added"
|
||||
},
|
||||
"Search": {
|
||||
"Title": "Søg",
|
||||
|
@ -113,6 +116,7 @@
|
|||
"RequestStatus": "Status for anmodning:",
|
||||
"Denied": " Afvist:",
|
||||
"TheatricalRelease": "Theatrical Release: {{date}}",
|
||||
"TheatricalReleaseSort": "Theatrical Release",
|
||||
"DigitalRelease": "Digital Release: {{date}}",
|
||||
"RequestDate": "Dato for anmodning:",
|
||||
"QualityOverride": "Tilsidesæt kvalitet:",
|
||||
|
@ -129,7 +133,14 @@
|
|||
"GridStatus": "Status",
|
||||
"ReportIssue": "Rapportér problem",
|
||||
"Filter": "Filter",
|
||||
"SeasonNumberHeading": "Sæson: {seasonNumber}"
|
||||
"Sort": "Sort",
|
||||
"SeasonNumberHeading": "Sæson: {seasonNumber}",
|
||||
"SortTitleAsc": "Title ▲",
|
||||
"SortTitleDesc": "Title ▼",
|
||||
"SortRequestDateAsc": "Request Date ▲",
|
||||
"SortRequestDateDesc": "Request Date ▼",
|
||||
"SortStatusAsc": "Status ▲",
|
||||
"SortStatusDesc": "Status ▼"
|
||||
},
|
||||
"Issues": {
|
||||
"Title": "Problemer",
|
||||
|
@ -154,6 +165,7 @@
|
|||
"ClearFilter": "Nulstil filter",
|
||||
"FilterHeaderAvailability": "Tilgængelighed",
|
||||
"FilterHeaderRequestStatus": "Status",
|
||||
"Approved": "Godkendt"
|
||||
"Approved": "Godkendt",
|
||||
"PendingApproval": "Pending Approval"
|
||||
}
|
||||
}
|
|
@ -62,9 +62,12 @@
|
|||
"Italian": "Italienisch",
|
||||
"Danish": "Dänisch",
|
||||
"Dutch": "Niederländisch",
|
||||
"Norwegian": "Norwegisch"
|
||||
"Norwegian": "Norwegisch",
|
||||
"BrazillianPortuguese": "Brazillian Portuguese",
|
||||
"Polish": "Polish"
|
||||
},
|
||||
"OpenMobileApp": "Mobile App"
|
||||
"OpenMobileApp": "Mobile App",
|
||||
"RecentlyAdded": "Recently Added"
|
||||
},
|
||||
"Search": {
|
||||
"Title": "Suche",
|
||||
|
@ -113,6 +116,7 @@
|
|||
"RequestStatus": "Anfrage Status:",
|
||||
"Denied": " Abgelehnt:",
|
||||
"TheatricalRelease": "Theatrical Release: {{date}}",
|
||||
"TheatricalReleaseSort": "Theatrical Release",
|
||||
"DigitalRelease": "Digital Release: {{date}}",
|
||||
"RequestDate": "Datum der Anfrage:",
|
||||
"QualityOverride": "Qualitäts Überschreiben:",
|
||||
|
@ -129,7 +133,14 @@
|
|||
"GridStatus": "Status",
|
||||
"ReportIssue": "Problem melden",
|
||||
"Filter": "Filter",
|
||||
"SeasonNumberHeading": "Staffel: {seasonNumber}"
|
||||
"Sort": "Sort",
|
||||
"SeasonNumberHeading": "Staffel: {seasonNumber}",
|
||||
"SortTitleAsc": "Title ▲",
|
||||
"SortTitleDesc": "Title ▼",
|
||||
"SortRequestDateAsc": "Request Date ▲",
|
||||
"SortRequestDateDesc": "Request Date ▼",
|
||||
"SortStatusAsc": "Status ▲",
|
||||
"SortStatusDesc": "Status ▼"
|
||||
},
|
||||
"Issues": {
|
||||
"Title": "Probleme",
|
||||
|
@ -154,6 +165,7 @@
|
|||
"ClearFilter": "Filter zurücksetzen",
|
||||
"FilterHeaderAvailability": "Verfügbarkeit",
|
||||
"FilterHeaderRequestStatus": "Status",
|
||||
"Approved": "Bestätigt"
|
||||
"Approved": "Bestätigt",
|
||||
"PendingApproval": "Pending Approval"
|
||||
}
|
||||
}
|
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