Merge from develop -> angular6 upgrade

This commit is contained in:
Matt Jeanes 2018-07-01 00:20:23 +01:00
commit 835202f935
58 changed files with 663 additions and 315 deletions

View file

@ -1,9 +1,63 @@
# Changelog # Changelog
## v3.0.3421 (2018-06-23)
### **New Features**
- Added TVRequestsLite. [Jamie]
- Added a smaller and simplier way of getting TV Request info. [Jamie Rees]
### **Fixes**
- Show the popular movies and tv shows by default. [Jamie]
- Fixed #2348. [Jamie]
## v3.0.3407 (2018-06-18)
### **New Features**
- Update appveyor.yml. [Jamie]
- Update build.cake. [Jamie]
### **Fixes**
- Fixed the issue where when we find an episode for the recently added sync, we don't check if we should run the availbility checker. [Jamie]
- Fixed the API not working due to a bug in .Net Core 2.1. [Jamie]
- Fixed #2321. [Jamie]
- Maybe this will fix #2298. [Jamie]
- Fixed #2312. [Jamie]
- Fixed the SickRage/Medusa Issue where it was always being set as Skipped/Ignore #2084. [Jamie]
- Fixed the sorting and filtering on the Movie Requests page, it all functions correctly now. [Jamie]
- Fixed #2288. [Jamie]
- Upgrade packages. [Jamie]
- Inital Migration. [Jamie]
- Fixed #2317. [Jamie]
## v3.0.3383 (2018-06-07) ## v3.0.3383 (2018-06-07)
### **New Features**
- Update CHANGELOG.md. [Jamie]
### **Fixes** ### **Fixes**
- Minor improvements. [Jamie]
- Run the availability checker on finish of the recentlty added sync. [Jamie] - Run the availability checker on finish of the recentlty added sync. [Jamie]
- Fixed the issue with the Recently Added Sync sometimes not working as expected. [Jamie] - Fixed the issue with the Recently Added Sync sometimes not working as expected. [Jamie]

View file

@ -15,19 +15,19 @@ test: off
after_build: after_build:
- cmd: >- - cmd: >-
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows.zip" appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows.zip"
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\osx.tar.gz" appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\osx.tar.gz"
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux.tar.gz" appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux.tar.gz"
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux-arm.tar.gz" appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm.tar.gz"
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows-32bit.zip" appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows-32bit.zip"
# appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz" # appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz"

View file

@ -26,7 +26,7 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj
var solutionFile = "Ombi.sln"; // Solution file if needed var solutionFile = "Ombi.sln"; // Solution file if needed
GitVersion versionInfo = null; GitVersion versionInfo = null;
var frameworkVer = "netcoreapp2.1"; var frameworkVer = "netcoreapp2.0";
var buildSettings = new DotNetCoreBuildSettings var buildSettings = new DotNetCoreBuildSettings
{ {

View file

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Internal;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ombi.Api.Emby.Models; using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Media.Tv; using Ombi.Api.Emby.Models.Media.Tv;
@ -98,19 +99,19 @@ namespace Ombi.Api.Emby
return await Api.Request<EmbyItemContainer<MovieInformation>>(request); return await Api.Request<EmbyItemContainer<MovieInformation>>(request);
} }
public async Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, string userId, string baseUri) public async Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri)
{ {
return await GetAll<EmbyMovie>("Movie", apiKey, userId, baseUri); return await GetAll<EmbyMovie>("Movie", apiKey, userId, baseUri, true, startIndex, count);
} }
public async Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, string userId, string baseUri) public async Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri)
{ {
return await GetAll<EmbyEpisodes>("Episode", apiKey, userId, baseUri); return await GetAll<EmbyEpisodes>("Episode", apiKey, userId, baseUri, false, startIndex, count);
} }
public async Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, string userId, string baseUri) public async Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri)
{ {
return await GetAll<EmbySeries>("Series", apiKey, userId, baseUri); return await GetAll<EmbySeries>("Series", apiKey, userId, baseUri, false, startIndex, count);
} }
public async Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl) public async Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl)
@ -129,20 +130,40 @@ namespace Ombi.Api.Emby
private async Task<T> GetInformation<T>(string mediaId, string apiKey, string userId, string baseUrl) private async Task<T> GetInformation<T>(string mediaId, string apiKey, string userId, string baseUrl)
{ {
var request = new Request($"emby/users/{userId}/items/{mediaId}", baseUrl, HttpMethod.Get); var request = new Request($"emby/users/{userId}/items/{mediaId}", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey); AddHeaders(request, apiKey);
var response = await Api.RequestContent(request); var response = await Api.RequestContent(request);
return JsonConvert.DeserializeObject<T>(response); return JsonConvert.DeserializeObject<T>(response);
} }
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview = false)
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri)
{ {
var request = new Request($"emby/users/{userId}/items", baseUri, HttpMethod.Get); var request = new Request($"emby/users/{userId}/items", baseUri, HttpMethod.Get);
request.AddQueryString("Recursive", true.ToString()); request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type); request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
request.AddQueryString("VirtualItem", "False");
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbyItemContainer<T>>(request);
return obj;
}
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count)
{
var request = new Request($"emby/users/{userId}/items", baseUri, HttpMethod.Get);
request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
request.AddQueryString("startIndex", startIndex.ToString());
request.AddQueryString("limit", count.ToString());
request.AddQueryString("VirtualItem", "False");
AddHeaders(request, apiKey); AddHeaders(request, apiKey);

View file

@ -14,9 +14,14 @@ namespace Ombi.Api.Emby
Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri); Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri);
Task<EmbyConnectUser> LoginConnectUser(string username, string password); Task<EmbyConnectUser> LoginConnectUser(string username, string password);
Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, string userId, string baseUri); Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId,
Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, string userId, string baseUri); string baseUri);
Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, string userId, string baseUri);
Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<MovieInformation>> GetCollection(string mediaId, string apiKey, string userId, Task<EmbyItemContainer<MovieInformation>> GetCollection(string mediaId, string apiKey, string userId,
string baseUrl); string baseUrl);

View file

@ -28,5 +28,7 @@ namespace Ombi.Api.Emby.Models.Movie
public string MediaType { get; set; } public string MediaType { get; set; }
public bool HasSubtitles { get; set; } public bool HasSubtitles { get; set; }
public int CriticRating { get; set; } public int CriticRating { get; set; }
public string Overview { get; set; }
public EmbyProviderids ProviderIds { get; set; }
} }
} }

View file

@ -39,5 +39,6 @@ namespace Ombi.Api.Emby.Models.Media.Tv
public string LocationType { get; set; } public string LocationType { get; set; }
public string MediaType { get; set; } public string MediaType { get; set; }
public bool HasSubtitles { get; set; } public bool HasSubtitles { get; set; }
public EmbyProviderids ProviderIds { get; set; }
} }
} }

View file

@ -26,5 +26,7 @@ namespace Ombi.Api.Emby.Models.Media.Tv
public string[] BackdropImageTags { get; set; } public string[] BackdropImageTags { get; set; }
public string LocationType { get; set; } public string LocationType { get; set; }
public DateTime EndDate { get; set; } public DateTime EndDate { get; set; }
public EmbyProviderids ProviderIds { get; set; }
} }
} }

View file

@ -21,6 +21,7 @@ namespace Ombi.Api.FanartTv
{ {
var request = new Request($"tv/{tvdbId}", Endpoint, HttpMethod.Get); var request = new Request($"tv/{tvdbId}", Endpoint, HttpMethod.Get);
request.AddHeader("api-key", token); request.AddHeader("api-key", token);
request.IgnoreErrors = true;
try try
{ {
return await Api.Request<TvResult>(request); return await Api.Request<TvResult>(request);
@ -36,6 +37,7 @@ namespace Ombi.Api.FanartTv
{ {
var request = new Request($"movies/{movieOrImdbId}", Endpoint, HttpMethod.Get); var request = new Request($"movies/{movieOrImdbId}", Endpoint, HttpMethod.Get);
request.AddHeader("api-key", token); request.AddHeader("api-key", token);
request.IgnoreErrors = true;
return await Api.Request<MovieResult>(request); return await Api.Request<MovieResult>(request);
} }

View file

@ -9,7 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -11,7 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="2.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -39,7 +39,11 @@ namespace Ombi.Api
if (!httpResponseMessage.IsSuccessStatusCode) if (!httpResponseMessage.IsSuccessStatusCode)
{ {
LogError(request, httpResponseMessage); if (!request.IgnoreErrors)
{
LogError(request, httpResponseMessage);
}
if (request.Retry) if (request.Retry)
{ {
@ -94,7 +98,10 @@ namespace Ombi.Api
var httpResponseMessage = await _client.SendAsync(httpRequestMessage); var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
if (!httpResponseMessage.IsSuccessStatusCode) if (!httpResponseMessage.IsSuccessStatusCode)
{ {
LogError(request, httpResponseMessage); if (!request.IgnoreErrors)
{
LogError(request, httpResponseMessage);
}
} }
// do something with the response // do something with the response
var data = httpResponseMessage.Content; var data = httpResponseMessage.Content;
@ -112,7 +119,10 @@ namespace Ombi.Api
var httpResponseMessage = await _client.SendAsync(httpRequestMessage); var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
if (!httpResponseMessage.IsSuccessStatusCode) if (!httpResponseMessage.IsSuccessStatusCode)
{ {
LogError(request, httpResponseMessage); if (!request.IgnoreErrors)
{
LogError(request, httpResponseMessage);
}
} }
} }
} }

View file

@ -9,7 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Polly" Version="5.8.0" /> <PackageReference Include="Polly" Version="5.8.0" />
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" /> <PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />

View file

@ -25,7 +25,7 @@ namespace Ombi.Api
public string Endpoint { get; } public string Endpoint { get; }
public string BaseUrl { get; } public string BaseUrl { get; }
public HttpMethod HttpMethod { get; } public HttpMethod HttpMethod { get; }
public bool IgnoreErrors { get; set; }
public bool Retry { get; set; } public bool Retry { get; set; }
public List<HttpStatusCode> StatusCodeToRetry { get; set; } = new List<HttpStatusCode>(); public List<HttpStatusCode> StatusCodeToRetry { get; set; } = new List<HttpStatusCode>();

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -9,7 +9,7 @@
<PackageReference Include="Nunit" Version="3.8.1" /> <PackageReference Include="Nunit" Version="3.8.1" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.7.0"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="15.7.2"></packagereference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Core.Models.Requests; using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search; using Ombi.Core.Models.UI;
using Ombi.Store.Entities.Requests; using Ombi.Store.Entities.Requests;
namespace Ombi.Core.Engine.Interfaces namespace Ombi.Core.Engine.Interfaces
@ -10,8 +10,10 @@ namespace Ombi.Core.Engine.Interfaces
{ {
Task RemoveTvRequest(int requestId); Task RemoveTvRequest(int requestId);
Task<TvRequests> GetTvRequest(int requestId);
Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv); Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv);
Task<RequestEngineResult> DenyChildRequest(int requestId); Task<RequestEngineResult> DenyChildRequest(int requestId);
Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type);
Task<IEnumerable<TvRequests>> SearchTvRequest(string search); Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search); Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search);
Task<TvRequests> UpdateTvRequest(TvRequests request); Task<TvRequests> UpdateTvRequest(TvRequests request);
@ -20,5 +22,6 @@ namespace Ombi.Core.Engine.Interfaces
Task<ChildRequests> UpdateChildRequest(ChildRequests request); Task<ChildRequests> UpdateChildRequest(ChildRequests request);
Task RemoveTvChild(int requestId); Task RemoveTvChild(int requestId);
Task<RequestEngineResult> ApproveChildRequest(int id); Task<RequestEngineResult> ApproveChildRequest(int id);
Task<IEnumerable<TvRequests>> GetRequestsLite();
} }
} }

View file

@ -60,7 +60,6 @@ namespace Ombi.Core.Engine
if (result != null) if (result != null)
{ {
Logger.LogDebug("Search Result: {result}", result);
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
} }
return null; return null;
@ -91,7 +90,6 @@ namespace Ombi.Core.Engine
var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12)); var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12));
if (result != null) if (result != null)
{ {
Logger.LogDebug("Search Result: {result}", result);
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
} }
return null; return null;
@ -106,7 +104,6 @@ namespace Ombi.Core.Engine
var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12)); var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12));
if (result != null) if (result != null)
{ {
Logger.LogDebug("Search Result: {result}", result);
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
} }
return null; return null;
@ -136,7 +133,6 @@ namespace Ombi.Core.Engine
var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12)); var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12));
if (result != null) if (result != null)
{ {
Logger.LogDebug("Search Result: {result}", result);
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
} }
return null; return null;

View file

@ -168,6 +168,35 @@ namespace Ombi.Core.Engine
}; };
} }
public async Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type)
{
var shouldHide = await HideFromOtherUsers();
List<TvRequests> allRequests;
if (shouldHide.Hide)
{
allRequests = await TvRepository.GetLite(shouldHide.UserId)
.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();
}
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
return new RequestsViewModel<TvRequests>
{
Collection = allRequests
};
}
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position) public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position)
{ {
var shouldHide = await HideFromOtherUsers(); var shouldHide = await HideFromOtherUsers();
@ -218,6 +247,45 @@ namespace Ombi.Core.Engine
return allRequests; return allRequests;
} }
public async Task<IEnumerable<TvRequests>> GetRequestsLite()
{
var shouldHide = await HideFromOtherUsers();
List<TvRequests> allRequests;
if (shouldHide.Hide)
{
allRequests = await TvRepository.GetLite(shouldHide.UserId).ToListAsync();
FilterChildren(allRequests, shouldHide);
}
else
{
allRequests = await TvRepository.GetLite().ToListAsync();
}
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
return allRequests;
}
public async Task<TvRequests> GetTvRequest(int requestId)
{
var shouldHide = await HideFromOtherUsers();
TvRequests request;
if (shouldHide.Hide)
{
request = await TvRepository.Get(shouldHide.UserId).Where(x => x.Id == requestId).FirstOrDefaultAsync();
FilterChildren(request, shouldHide);
}
else
{
request = await TvRepository.Get().Where(x => x.Id == requestId).FirstOrDefaultAsync();
}
await CheckForSubscription(shouldHide, request);
return request;
}
private static void FilterChildren(IEnumerable<TvRequests> allRequests, HideResult shouldHide) private static void FilterChildren(IEnumerable<TvRequests> allRequests, HideResult shouldHide)
{ {
// Filter out children // Filter out children
@ -225,16 +293,27 @@ namespace Ombi.Core.Engine
{ {
for (var j = 0; j < t.ChildRequests.Count; j++) for (var j = 0; j < t.ChildRequests.Count; j++)
{ {
var child = t.ChildRequests[j]; FilterChildren(t, shouldHide);
if (child.RequestedUserId != shouldHide.UserId)
{
t.ChildRequests.RemoveAt(j);
j--;
}
} }
} }
} }
private static void FilterChildren(TvRequests t, HideResult shouldHide)
{
// Filter out children
for (var j = 0; j < t.ChildRequests.Count; j++)
{
var child = t.ChildRequests[j];
if (child.RequestedUserId != shouldHide.UserId)
{
t.ChildRequests.RemoveAt(j);
j--;
}
}
}
public async Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId) public async Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId)
{ {
var shouldHide = await HideFromOtherUsers(); var shouldHide = await HideFromOtherUsers();
@ -470,7 +549,7 @@ namespace Ombi.Core.Engine
{ {
foreach (var tv in x.ChildRequests) foreach (var tv in x.ChildRequests)
{ {
await CheckForSubscription(shouldHide, tv); await CheckForSubscription(shouldHide, tv);
} }
} }

View file

@ -12,9 +12,9 @@
<PackageReference Include="AutoMapper" Version="6.1.1" /> <PackageReference Include="AutoMapper" Version="6.1.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" />
<PackageReference Include="Hangfire" Version="1.6.19" /> <PackageReference Include="Hangfire" Version="1.6.19" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="2.0.0-preview1-final" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.5" />
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" /> <PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />

View file

@ -9,9 +9,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -10,8 +10,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="EasyCrypto" Version="3.3.2" /> <PackageReference Include="EasyCrypto" Version="3.3.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Nito.AsyncEx" Version="5.0.0-pre-05" /> <PackageReference Include="Nito.AsyncEx" Version="5.0.0-pre-05" />
<PackageReference Include="System.Security.Claims" Version="4.3.0" /> <PackageReference Include="System.Security.Claims" Version="4.3.0" />

View file

@ -182,14 +182,16 @@
</tr> </tr>
<tr> <tr>
<td> <td>
{@RECENTLYADDED} {@RECENTLYADDED}
</td>
</tr>
<!-- END MAIN CONTENT AREA --> <!-- END MAIN CONTENT AREA -->
</table> </table>
<!-- START FOOTER --> <!-- START FOOTER -->
<div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;"> <div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%"> <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tr> <tr>
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center"> <td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
Powered by <a href="https://github.com/tidusjar/Ombi" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Ombi</a> Powered by <a href="https://github.com/tidusjar/Ombi" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Ombi</a>

View file

@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Nunit" Version="3.8.1" /> <PackageReference Include="Nunit" Version="3.8.1" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.7.0"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="15.7.2"></packagereference>
<PackageReference Include="Moq" Version="4.7.99" /> <PackageReference Include="Moq" Version="4.7.99" />
</ItemGroup> </ItemGroup>

View file

@ -264,13 +264,13 @@ namespace Ombi.Notifications.Agents
? MovieRequest?.RequestedUser?.NotificationUserIds ? MovieRequest?.RequestedUser?.NotificationUserIds
: TvRequest?.RequestedUser?.NotificationUserIds; : TvRequest?.RequestedUser?.NotificationUserIds;
} }
if (model.UserId.HasValue() && !notificationIds.Any()) if (model.UserId.HasValue() && (!notificationIds?.Any() ?? true))
{ {
var user= _userManager.Users.Include(x => x.NotificationUserIds).FirstOrDefault(x => x.Id == model.UserId); var user= _userManager.Users.Include(x => x.NotificationUserIds).FirstOrDefault(x => x.Id == model.UserId);
notificationIds = user.NotificationUserIds; notificationIds = user.NotificationUserIds;
} }
if (!notificationIds.Any()) if (!notificationIds?.Any() ?? true)
{ {
_logger.LogInformation( _logger.LogInformation(
$"there are no admins to send a notification for {type}, for agent {NotificationAgent.Mobile}"); $"there are no admins to send a notification for {type}, for agent {NotificationAgent.Mobile}");

View file

@ -13,7 +13,6 @@ namespace Ombi.Notifications
{ {
public class NotificationMessageCurlys public class NotificationMessageCurlys
{ {
public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s) public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s)
{ {
LoadIssues(opts); LoadIssues(opts);

View file

@ -10,7 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Ensure.That" Version="7.0.0-pre32" /> <PackageReference Include="Ensure.That" Version="7.0.0-pre32" />
<PackageReference Include="MailKit" Version="2.0.4" /> <PackageReference Include="MailKit" Version="2.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore" Version="2.0.3" />
<PackageReference Include="Moq" Version="4.7.99" /> <PackageReference Include="Moq" Version="4.7.99" />
<PackageReference Include="Nunit" Version="3.10.1" /> <PackageReference Include="Nunit" Version="3.10.1" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.8.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.8.0" />

View file

@ -5,7 +5,6 @@ using System.Linq.Expressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Castle.Components.DictionaryAdapter; using Castle.Components.DictionaryAdapter;
using Hangfire; using Hangfire;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using Ombi.Core.Notifications; using Ombi.Core.Notifications;
@ -27,7 +26,7 @@ namespace Ombi.Schedule.Tests
_tv = new Mock<ITvRequestRepository>(); _tv = new Mock<ITvRequestRepository>();
_movie = new Mock<IMovieRequestRepository>(); _movie = new Mock<IMovieRequestRepository>();
_notify = new Mock<INotificationService>(); _notify = new Mock<INotificationService>();
Checker = new PlexAvailabilityChecker(_repo.Object, _tv.Object, _movie.Object, _notify.Object, new Mock<IBackgroundJobClient>().Object, new Mock<ILogger<PlexAvailabilityChecker>>().Object); Checker = new PlexAvailabilityChecker(_repo.Object, _tv.Object, _movie.Object, _notify.Object, new Mock<IBackgroundJobClient>().Object, null);
} }

View file

@ -68,71 +68,102 @@ namespace Ombi.Schedule.Jobs.Emby
if (!ValidateSettings(server)) if (!ValidateSettings(server))
return; return;
await _repo.ExecuteSql("DELETE FROM EmbyEpisode"); //await _repo.ExecuteSql("DELETE FROM EmbyEpisode");
await _repo.ExecuteSql("DELETE FROM EmbyContent"); //await _repo.ExecuteSql("DELETE FROM EmbyContent");
var movies = await _api.GetAllMovies(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
var totalCount = movies.TotalRecordCount;
var processed = 1;
var movies = await _api.GetAllMovies(server.ApiKey, server.AdministratorId, server.FullUri);
var mediaToAdd = new HashSet<EmbyContent>(); var mediaToAdd = new HashSet<EmbyContent>();
foreach (var movie in movies.Items)
{
if (movie.Type.Equals("boxset", StringComparison.CurrentCultureIgnoreCase))
{
var movieInfo =
await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri);
foreach (var item in movieInfo.Items)
{
var info = await _api.GetMovieInformation(item.Id, server.ApiKey,
server.AdministratorId, server.FullUri);
await ProcessMovies(info, mediaToAdd);
}
}
else
{
// Regular movie
var movieInfo = await _api.GetMovieInformation(movie.Id, server.ApiKey,
server.AdministratorId, server.FullUri);
await ProcessMovies(movieInfo, mediaToAdd); while (processed < totalCount)
{
try
{
foreach (var movie in movies.Items)
{
processed++;
// Regular movie
await ProcessMovies(movie, mediaToAdd);
}
// Get the next batch
movies = await _api.GetAllMovies(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
await _repo.AddRange(mediaToAdd);
mediaToAdd.Clear();
}
catch (Exception)
{
throw;
} }
} }
// TV Time // TV Time
var tv = await _api.GetAllShows(server.ApiKey, server.AdministratorId, server.FullUri); var tv = await _api.GetAllShows(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
var totalTv = tv.TotalRecordCount;
foreach (var tvShow in tv.Items) processed = 1;
while (processed < totalTv)
{ {
var tvInfo = await _api.GetSeriesInformation(tvShow.Id, server.ApiKey, server.AdministratorId, foreach (var tvShow in tv.Items)
server.FullUri);
if (string.IsNullOrEmpty(tvInfo.ProviderIds?.Tvdb))
{ {
Log.Error("Provider Id on tv {0} is null", tvShow.Name); try
continue;
}
var existingTv = await _repo.GetByEmbyId(tvShow.Id);
if (existingTv == null)
mediaToAdd.Add(new EmbyContent
{ {
TvDbId = tvInfo.ProviderIds?.Tvdb,
ImdbId = tvInfo.ProviderIds?.Imdb, processed++;
TheMovieDbId = tvInfo.ProviderIds?.Tmdb, if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb))
Title = tvInfo.Name, {
Type = EmbyMediaType.Series, _logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name);
EmbyId = tvShow.Id, continue;
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), }
AddedAt = DateTime.UtcNow
}); var existingTv = await _repo.GetByEmbyId(tvShow.Id);
if (existingTv == null)
{
_logger.LogDebug("Adding new TV Show {0}", tvShow.Name);
mediaToAdd.Add(new EmbyContent
{
TvDbId = tvShow.ProviderIds?.Tvdb,
ImdbId = tvShow.ProviderIds?.Imdb,
TheMovieDbId = tvShow.ProviderIds?.Tmdb,
Title = tvShow.Name,
Type = EmbyMediaType.Series,
EmbyId = tvShow.Id,
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id),
AddedAt = DateTime.UtcNow
});
}
else
{
_logger.LogDebug("We already have TV Show {0}", tvShow.Name);
}
}
catch (Exception)
{
throw;
}
}
// Get the next batch
tv = await _api.GetAllShows(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
await _repo.AddRange(mediaToAdd);
mediaToAdd.Clear();
} }
if (mediaToAdd.Any()) if (mediaToAdd.Any())
await _repo.AddRange(mediaToAdd); await _repo.AddRange(mediaToAdd);
} }
private async Task ProcessMovies(MovieInformation movieInfo, ICollection<EmbyContent> content) private async Task ProcessMovies(EmbyMovie movieInfo, ICollection<EmbyContent> content)
{ {
// Check if it exists // Check if it exists
var existingMovie = await _repo.GetByEmbyId(movieInfo.Id); var existingMovie = await _repo.GetByEmbyId(movieInfo.Id);
if (existingMovie == null) if (existingMovie == null)
{
_logger.LogDebug("Adding new movie {0}", movieInfo.Name);
content.Add(new EmbyContent content.Add(new EmbyContent
{ {
ImdbId = movieInfo.ProviderIds.Imdb, ImdbId = movieInfo.ProviderIds.Imdb,
@ -143,6 +174,12 @@ namespace Ombi.Schedule.Jobs.Emby
Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id), Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id),
AddedAt = DateTime.UtcNow, AddedAt = DateTime.UtcNow,
}); });
}
else
{
// we have this
_logger.LogDebug("We already have movie {0}", movieInfo.Name);
}
} }
private bool ValidateSettings(EmbyServers server) private bool ValidateSettings(EmbyServers server)

View file

@ -73,49 +73,51 @@ namespace Ombi.Schedule.Jobs.Emby
private async Task CacheEpisodes(EmbyServers server) private async Task CacheEpisodes(EmbyServers server)
{ {
var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, server.AdministratorId, server.FullUri); var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
var epToAdd = new List<EmbyEpisode>(); var total = allEpisodes.TotalRecordCount;
var processed = 1;
foreach (var ep in allEpisodes.Items) var epToAdd = new HashSet<EmbyEpisode>();
while (processed < total)
{ {
if (ep.LocationType.Equals("Virtual", StringComparison.CurrentCultureIgnoreCase)) foreach (var ep in allEpisodes.Items)
{ {
// This means that we don't actully have the file, it's just Emby being nice and showing future stuff processed++;
continue; // Let's make sure we have the parent request, stop those pesky forign key errors,
} // Damn me having data integrity
var parent = await _repo.GetByEmbyId(ep.SeriesId);
var epInfo = await _api.GetEpisodeInformation(ep.Id, server.ApiKey, server.AdministratorId, server.FullUri); if (parent == null)
//if (epInfo?.ProviderIds?.Tvdb == null)
//{
// continue;
//}
// Let's make sure we have the parent request, stop those pesky forign key errors,
// Damn me having data integrity
var parent = await _repo.GetByEmbyId(epInfo.SeriesId);
if (parent == null)
{
_logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this", ep.Name);
continue;
}
var existingEpisode = await _repo.GetEpisodeByEmbyId(ep.Id);
if (existingEpisode == null)
{
// add it
epToAdd.Add(new EmbyEpisode
{ {
EmbyId = ep.Id, _logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this",
EpisodeNumber = ep.IndexNumber, ep.Name);
SeasonNumber = ep.ParentIndexNumber, continue;
ParentId = ep.SeriesId, }
TvDbId = epInfo.ProviderIds.Tvdb,
TheMovieDbId = epInfo.ProviderIds.Tmdb, var existingEpisode = await _repo.GetEpisodeByEmbyId(ep.Id);
ImdbId = epInfo.ProviderIds.Imdb, // Make sure it's not in the hashset too
Title = ep.Name, var existingInList = epToAdd.Any(x => x.EmbyId == ep.Id);
AddedAt = DateTime.UtcNow
}); if (existingEpisode == null && !existingInList)
{
_logger.LogDebug("Adding new episode {0} to parent {1}", ep.Name, ep.SeriesName);
// add it
epToAdd.Add(new EmbyEpisode
{
EmbyId = ep.Id,
EpisodeNumber = ep.IndexNumber,
SeasonNumber = ep.ParentIndexNumber,
ParentId = ep.SeriesId,
TvDbId = ep.ProviderIds.Tvdb,
TheMovieDbId = ep.ProviderIds.Tmdb,
ImdbId = ep.ProviderIds.Imdb,
Title = ep.Name,
AddedAt = DateTime.UtcNow
});
}
} }
await _repo.AddRange(epToAdd);
epToAdd.Clear();
allEpisodes = await _api.GetAllEpisodes(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
} }
if (epToAdd.Any()) if (epToAdd.Any())

View file

@ -122,7 +122,7 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
DateTime = DateTime.Now, DateTime = DateTime.Now,
NotificationType = NotificationType.RequestAvailable, NotificationType = NotificationType.RequestAvailable,
RequestId = child.ParentRequestId, RequestId = child.Id,
RequestType = RequestType.TvShow, RequestType = RequestType.TvShow,
Recipient = child.RequestedUser.Email Recipient = child.RequestedUser.Email
})); }));

View file

@ -104,13 +104,13 @@ namespace Ombi.Schedule.Jobs.Plex
BackgroundJob.Enqueue(() => EpisodeSync.Start()); BackgroundJob.Enqueue(() => EpisodeSync.Start());
} }
if (processedContent.HasProcessedContent && recentlyAddedSearch) if ((processedContent?.HasProcessedContent ?? false) && recentlyAddedSearch)
{ {
// Just check what we send it // Just check what we send it
BackgroundJob.Enqueue(() => Metadata.ProcessPlexServerContent(processedContent.Content)); BackgroundJob.Enqueue(() => Metadata.ProcessPlexServerContent(processedContent.Content));
} }
if (processedContent.HasProcessedEpisodes && recentlyAddedSearch) if ((processedContent?.HasProcessedEpisodes ?? false) && recentlyAddedSearch)
{ {
BackgroundJob.Enqueue(() => Checker.Start()); BackgroundJob.Enqueue(() => Checker.Start());
} }
@ -150,9 +150,9 @@ namespace Ombi.Schedule.Jobs.Plex
var retVal = new ProcessedContent(); var retVal = new ProcessedContent();
var contentProcessed = new Dictionary<int, int>(); var contentProcessed = new Dictionary<int, int>();
var episodesProcessed = new List<int>(); var episodesProcessed = new List<int>();
Logger.LogInformation("Getting all content from server {0}", servers.Name); Logger.LogDebug("Getting all content from server {0}", servers.Name);
var allContent = await GetAllContent(servers, recentlyAddedSearch); var allContent = await GetAllContent(servers, recentlyAddedSearch);
Logger.LogInformation("We found {0} items", allContent.Count); Logger.LogDebug("We found {0} items", allContent.Count);
// Let's now process this. // Let's now process this.
var contentToAdd = new HashSet<PlexServerContent>(); var contentToAdd = new HashSet<PlexServerContent>();
@ -163,9 +163,9 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
if (content.viewGroup.Equals(PlexMediaType.Episode.ToString(), StringComparison.CurrentCultureIgnoreCase)) if (content.viewGroup.Equals(PlexMediaType.Episode.ToString(), StringComparison.CurrentCultureIgnoreCase))
{ {
Logger.LogInformation("Found some episodes, this must be a recently added sync"); Logger.LogDebug("Found some episodes, this must be a recently added sync");
var count = 0; var count = 0;
foreach (var epInfo in content.Metadata) foreach (var epInfo in content.Metadata ?? new Metadata[]{})
{ {
count++; count++;
var grandParentKey = epInfo.grandparentRatingKey; var grandParentKey = epInfo.grandparentRatingKey;
@ -199,14 +199,16 @@ namespace Ombi.Schedule.Jobs.Plex
// Save just to make sure we don't leave anything hanging // Save just to make sure we don't leave anything hanging
await Repo.SaveChangesAsync(); await Repo.SaveChangesAsync();
if (content.Metadata != null)
var episodesAdded = await EpisodeSync.ProcessEpsiodes(content.Metadata, allEps); {
episodesProcessed.AddRange(episodesAdded.Select(x => x.Id)); var episodesAdded = await EpisodeSync.ProcessEpsiodes(content.Metadata, allEps);
episodesProcessed.AddRange(episodesAdded.Select(x => x.Id));
}
} }
if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)) if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase))
{ {
// Process Shows // Process Shows
Logger.LogInformation("Processing TV Shows"); Logger.LogDebug("Processing TV Shows");
var count = 0; var count = 0;
foreach (var show in content.Metadata ?? new Metadata[] { }) foreach (var show in content.Metadata ?? new Metadata[] { })
{ {
@ -235,7 +237,7 @@ namespace Ombi.Schedule.Jobs.Plex
} }
if (content.viewGroup.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)) if (content.viewGroup.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase))
{ {
Logger.LogInformation("Processing Movies"); Logger.LogDebug("Processing Movies");
foreach (var movie in content?.Metadata ?? new Metadata[] { }) foreach (var movie in content?.Metadata ?? new Metadata[] { })
{ {
// Let's check if we have this movie // Let's check if we have this movie
@ -249,7 +251,7 @@ namespace Ombi.Schedule.Jobs.Plex
//var existing = await Repo.GetByKey(movie.ratingKey); //var existing = await Repo.GetByKey(movie.ratingKey);
if (existing != null) if (existing != null)
{ {
Logger.LogInformation("We already have movie {0}", movie.title); Logger.LogDebug("We already have movie {0}", movie.title);
continue; continue;
} }
@ -259,7 +261,7 @@ namespace Ombi.Schedule.Jobs.Plex
await Repo.Delete(hasSameKey); await Repo.Delete(hasSameKey);
} }
Logger.LogInformation("Adding movie {0}", movie.title); Logger.LogDebug("Adding movie {0}", movie.title);
var metaData = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri, var metaData = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri,
movie.ratingKey); movie.ratingKey);
var providerIds = PlexHelper.GetProviderIdFromPlexGuid(metaData.MediaContainer.Metadata var providerIds = PlexHelper.GetProviderIdFromPlexGuid(metaData.MediaContainer.Metadata
@ -371,6 +373,10 @@ namespace Ombi.Schedule.Jobs.Plex
await Repo.Delete(existingKey); await Repo.Delete(existingKey);
existingKey = null; existingKey = null;
} }
else if(existingContent == null)
{
existingContent = await Repo.GetFirstContentByCustom(x => x.Key == show.ratingKey);
}
} }
if (existingContent != null) if (existingContent != null)
@ -415,7 +421,7 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
try try
{ {
Logger.LogInformation("We already have show {0} checking for new seasons", Logger.LogDebug("We already have show {0} checking for new seasons",
existingContent.Title); existingContent.Title);
// Ok so we have it, let's check if there are any new seasons // Ok so we have it, let's check if there are any new seasons
var itemAdded = false; var itemAdded = false;
@ -466,7 +472,7 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
try try
{ {
Logger.LogInformation("New show {0}, so add it", show.title); Logger.LogDebug("New show {0}, so add it", show.title);
// Get the show metadata... This sucks since the `metadata` var contains all information about the show // Get the show metadata... This sucks since the `metadata` var contains all information about the show
// But it does not contain the `guid` property that we need to pull out thetvdb id... // But it does not contain the `guid` property that we need to pull out thetvdb id...
@ -567,7 +573,7 @@ namespace Ombi.Schedule.Jobs.Plex
.Select(x => x.Key.ToString()).ToList(); .Select(x => x.Key.ToString()).ToList();
if (!keys.Contains(dir.key)) if (!keys.Contains(dir.key))
{ {
Logger.LogInformation("Lib {0} is not monitored, so skipping", dir.key); Logger.LogDebug("Lib {0} is not monitored, so skipping", dir.key);
// We are not monitoring this lib // We are not monitoring this lib
continue; continue;
} }

View file

@ -9,7 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup> </ItemGroup>

View file

@ -10,10 +10,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.9" /> <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.9" />
</ItemGroup> </ItemGroup>

View file

@ -14,7 +14,9 @@ namespace Ombi.Store.Repository.Requests
Task Delete(TvRequests request); Task Delete(TvRequests request);
Task DeleteChild(ChildRequests request); Task DeleteChild(ChildRequests request);
IQueryable<TvRequests> Get(); IQueryable<TvRequests> Get();
IQueryable<TvRequests> GetLite();
IQueryable<TvRequests> Get(string userId); IQueryable<TvRequests> Get(string userId);
IQueryable<TvRequests> GetLite(string userId);
Task<TvRequests> GetRequestAsync(int tvDbId); Task<TvRequests> GetRequestAsync(int tvDbId);
TvRequests GetRequest(int tvDbId); TvRequests GetRequest(int tvDbId);
Task Update(TvRequests request); Task Update(TvRequests request);

View file

@ -60,6 +60,24 @@ namespace Ombi.Store.Repository.Requests
.Where(x => x.ChildRequests.Any(a => a.RequestedUserId == userId)) .Where(x => x.ChildRequests.Any(a => a.RequestedUserId == userId))
.AsQueryable(); .AsQueryable();
} }
public IQueryable<TvRequests> GetLite(string userId)
{
return Db.TvRequests
.Include(x => x.ChildRequests)
.ThenInclude(x => x.RequestedUser)
.Where(x => x.ChildRequests.Any(a => a.RequestedUserId == userId))
.AsQueryable();
}
public IQueryable<TvRequests> GetLite()
{
return Db.TvRequests
.Include(x => x.ChildRequests)
.ThenInclude(x => x.RequestedUser)
.AsQueryable();
}
public IQueryable<ChildRequests> GetChild() public IQueryable<ChildRequests> GetChild()
{ {
return Db.ChildRequests return Db.ChildRequests

View file

@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.3" />
<PackageReference Include="Moq" Version="4.7.99" /> <PackageReference Include="Moq" Version="4.7.99" />
<PackageReference Include="Nunit" Version="3.8.1" /> <PackageReference Include="Nunit" Version="3.8.1" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />

View file

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -12,14 +12,14 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.1.1-beta" /> <PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.2" />
<PackageReference Include="Serilog" Version="2.6.0-dev-00892" /> <PackageReference Include="Serilog" Version="2.6.0-dev-00892" />
<PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" /> <PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" />
<PackageReference Include="Serilog.Sinks.File" Version="3.2.0" /> <PackageReference Include="Serilog.Sinks.File" Version="3.2.0" />

View file

@ -0,0 +1,104 @@
using System;
using System.Linq;
using System.Net;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Ombi.Core.Authentication;
using Ombi.Core.Settings;
using Ombi.Settings.Settings.Models;
namespace Ombi
{
public class ApiKeyMiddlewear
{
public ApiKeyMiddlewear(RequestDelegate next)
{
_next = next;
}
private readonly RequestDelegate _next;
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.StartsWithSegments(new PathString("/api")))
{
//Let's check if this is an API Call
if (context.Request.Headers.Keys.Contains("ApiKey", StringComparer.InvariantCultureIgnoreCase))
{
// validate the supplied API key
// Validate it
var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault();
await ValidateApiKey(context, _next, headerKey);
}
else if (context.Request.Query.ContainsKey("apikey"))
{
if (context.Request.Query.TryGetValue("apikey", out var queryKey))
{
await ValidateApiKey(context, _next, queryKey);
}
}
// User access token used by the mobile app
else if (context.Request.Headers["UserAccessToken"].Any())
{
var headerKey = context.Request.Headers["UserAccessToken"].FirstOrDefault();
await ValidateUserAccessToken(context, _next, headerKey);
}
else
{
await _next.Invoke(context);
}
}
else
{
await _next.Invoke(context);
}
}
private async Task ValidateUserAccessToken(HttpContext context, RequestDelegate next, string key)
{
if (string.IsNullOrEmpty(key))
{
await context.Response.WriteAsync("Invalid User Access Token");
return;
}
var um = context.RequestServices.GetService<OmbiUserManager>();
var user = await um.Users.FirstOrDefaultAsync(x => x.UserAccessToken == key);
if (user == null)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsync("Invalid User Access Token");
}
else
{
var identity = new GenericIdentity(user.UserName);
var roles = await um.GetRolesAsync(user);
var principal = new GenericPrincipal(identity, roles.ToArray());
context.User = principal;
await next.Invoke(context);
}
}
private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key)
{
var repo = context.RequestServices.GetService<ISettingsService<OmbiSettings>>();
var ombiSettings = await repo.GetSettingsAsync();
var valid = ombiSettings.ApiKey.Equals(key, StringComparison.CurrentCultureIgnoreCase);
if (!valid)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsync("Invalid API Key");
}
else
{
var identity = new GenericIdentity("API");
var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
context.User = principal;
await next.Invoke(context);
}
}
}
}

View file

@ -84,7 +84,7 @@ export class LoginComponent implements OnDestroy, OnInit {
}); });
this.timer = setInterval(() => { this.timer = setInterval(() => {
this.cycleBackground(); this.cycleBackground();
}, 10000); }, 7000);
const base = this.location.getBaseHrefFromDOM(); const base = this.location.getBaseHrefFromDOM();
if (base.length > 1) { if (base.length > 1) {

View file

@ -69,6 +69,7 @@ export class MovieSearchComponent implements OnInit {
result: false, result: false,
errorMessage: "", errorMessage: "",
}; };
this.popularMovies();
} }
public search(text: any) { public search(text: any) {

View file

@ -93,6 +93,7 @@ export class TvSearchComponent implements OnInit {
result: false, result: false,
errorMessage: "", errorMessage: "",
}; };
this.popularShows();
} }
public search(text: any) { public search(text: any) {

View file

@ -279,4 +279,7 @@ export class SettingsService extends ServiceHelpers {
return this.http return this.http
.post<boolean>(`${this.url}/notifications/newsletter`, JSON.stringify(settings), {headers: this.headers}); .post<boolean>(`${this.url}/notifications/newsletter`, JSON.stringify(settings), {headers: this.headers});
} }
public verifyUrl(url: string): Observable<boolean> {
return this.http.post<boolean>(`${this.url}/customization/urlverify`, JSON.stringify({url}), {headers: this.headers});
}
} }

View file

@ -43,13 +43,24 @@ export class CustomizationComponent implements OnInit {
} }
public save() { public save() {
this.settingsService.saveCustomization(this.settings).subscribe(x => {
if (x) { this.settingsService.verifyUrl(this.settings.applicationUrl).subscribe(x => {
this.notificationService.success("Successfully saved Ombi settings"); if(this.settings.applicationUrl) {
} else { if(!x) {
this.notificationService.success("There was an error when saving the Ombi settings"); this.notificationService.error(`The URL "${this.settings.applicationUrl}" is not valid. Please format it correctly e.g. http://www.google.com/`);
return;
}
} }
this.settingsService.saveCustomization(this.settings).subscribe(x => {
if (x) {
this.notificationService.success("Successfully saved Ombi settings");
} else {
this.notificationService.success("There was an error when saving the Ombi settings");
}
});
}); });
} }
public dropDownChange(event: any): void { public dropDownChange(event: any): void {

View file

@ -45,6 +45,12 @@ export class UserManagementComponent implements OnInit {
this.notificationService.error("Email Notifications are not setup, cannot send welcome email"); this.notificationService.error("Email Notifications are not setup, cannot send welcome email");
return; return;
} }
if(!this.emailSettings.notificationTemplates.some(x => {
return x.enabled && x.notificationType === 8;
})) {
this.notificationService.error("The Welcome Email template is not enabled in the Email Setings");
return;
}
this.identityService.sendWelcomeEmail(user).subscribe(); this.identityService.sendWelcomeEmail(user).subscribe();
this.notificationService.success(`Sent a welcome email to ${user.emailAddress}`); this.notificationService.success(`Sent a welcome email to ${user.emailAddress}`);
} }

View file

@ -95,6 +95,7 @@ hr {
.btn { .btn {
border-radius: .25rem $i; border-radius: .25rem $i;
margin-bottom: 10px;
} }
.btn-group-separated .btn, .btn-group-separated .btn,
@ -141,6 +142,10 @@ p {
font-size: 1.1rem $i; font-size: 1.1rem $i;
} }
.tags {
display: block;
}
label { label {
display: inline-block $i; display: inline-block $i;
margin-bottom: .5rem $i; margin-bottom: .5rem $i;
@ -443,6 +448,7 @@ $border-radius: 10px;
margin-right: 10px; margin-right: 10px;
position: absolute; position: absolute;
left: 0; left: 0;
top: 3px;
bottom: 1px; bottom: 1px;
border: 2px solid #eee; border: 2px solid #eee;
border-radius: 3px; border-radius: 3px;
@ -940,7 +946,7 @@ a > h4:hover {
.backdrop{ .backdrop{
box-shadow: 3px 3px 10px #000000; box-shadow: 3px 3px 10px #000000;
background-position: center; background-position: 50% 33%;
background-size: cover; background-size: cover;
} }

View file

@ -201,7 +201,10 @@ namespace Ombi.Controllers
result = await FanartTvApi.GetMovieImages(moviesArray[item].ToString(), key.Value); result = await FanartTvApi.GetMovieImages(moviesArray[item].ToString(), key.Value);
} }
movieUrl = result.moviebackground[0].url; var otherRand = new Random();
var res = otherRand.Next(result.moviebackground.Length);
movieUrl = result.moviebackground[res].url;
} }
if (tvArray.Any()) if (tvArray.Any())
{ {
@ -212,8 +215,10 @@ namespace Ombi.Controllers
{ {
result = await FanartTvApi.GetTvImages(tvArray[item], key.Value); result = await FanartTvApi.GetTvImages(tvArray[item], key.Value);
} }
var otherRand = new Random();
var res = otherRand.Next(result.showbackground.Length);
tvUrl = result.showbackground[0].url; tvUrl = result.showbackground[res].url;
} }
if (!string.IsNullOrEmpty(movieUrl) && !string.IsNullOrEmpty(tvUrl)) if (!string.IsNullOrEmpty(movieUrl) && !string.IsNullOrEmpty(tvUrl))

View file

@ -189,8 +189,28 @@ namespace Ombi.Controllers
return await TvRequestEngine.GetRequests(count, position, new OrderFilterModel return await TvRequestEngine.GetRequests(count, position, new OrderFilterModel
{ {
OrderType = (OrderType)orderType, OrderType = (OrderType)orderType,
AvailabilityFilter = (FilterType) availabilityType, AvailabilityFilter = (FilterType)availabilityType,
StatusFilter = (FilterType) statusType, StatusFilter = (FilterType)statusType,
});
}
/// <summary>
/// Gets the tv requests lite.
/// </summary>
/// <param name="count">The count of items you want to return.</param>
/// <param name="position">The position.</param>
/// <param name="orderType"></param>
/// <param name="statusType"></param>
/// <param name="availabilityType"></param>
/// <returns></returns>
[HttpGet("tvlite/{count:int}/{position:int}/{orderType:int}/{statusFilterType:int}/{availabilityFilterType:int}")]
public async Task<RequestsViewModel<TvRequests>> GetTvRequestsLite(int count, int position, int orderType, int statusType, int availabilityType)
{
return await TvRequestEngine.GetRequestsLite(count, position, new OrderFilterModel
{
OrderType = (OrderType)orderType,
AvailabilityFilter = (FilterType)availabilityType,
StatusFilter = (FilterType)statusType,
}); });
} }
@ -204,6 +224,27 @@ namespace Ombi.Controllers
return await TvRequestEngine.GetRequests(); return await TvRequestEngine.GetRequests();
} }
/// <summary>
/// Gets the tv requests without the whole object graph (Does not include seasons/episodes).
/// </summary>
/// <returns></returns>
[HttpGet("tvlite")]
public async Task<IEnumerable<TvRequests>> GetTvRequestsLite()
{
return await TvRequestEngine.GetRequestsLite();
}
/// <summary>
/// Returns the full request object for the specified requestId
/// </summary>
/// <param name="requestId"></param>
/// <returns></returns>
[HttpGet("tv/{requestId:int}")]
public async Task<TvRequests> GetTvRequest(int requestId)
{
return await TvRequestEngine.GetTvRequest(requestId);
}
/// <summary> /// <summary>
/// Requests a tv show/episode/season. /// Requests a tv show/episode/season.
/// </summary> /// </summary>

View file

@ -1,21 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AutoMapper; using AutoMapper;
using Hangfire; using Hangfire;
using Hangfire.RecurringJobExtensions;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.PlatformAbstractions;
using NCrontab; using NCrontab;
using Ombi.Api.Emby; using Ombi.Api.Emby;
using Ombi.Attributes; using Ombi.Attributes;
@ -45,16 +39,6 @@ namespace Ombi.Controllers
[Produces("application/json")] [Produces("application/json")]
public class SettingsController : Controller public class SettingsController : Controller
{ {
/// <summary>
/// Initializes a new instance of the <see cref="SettingsController" /> class.
/// </summary>
/// <param name="resolver">The resolver.</param>
/// <param name="mapper">The mapper.</param>
/// <param name="templateRepo">The templateRepo.</param>
/// <param name="embyApi">The embyApi.</param>
/// <param name="radarrSync">The radarrCacher.</param>
/// <param name="memCache">The memory cache.</param>
/// <param name="githubApi">The memory cache.</param>
public SettingsController(ISettingsResolver resolver, public SettingsController(ISettingsResolver resolver,
IMapper mapper, IMapper mapper,
INotificationTemplatesRepository templateRepo, INotificationTemplatesRepository templateRepo,
@ -115,7 +99,7 @@ namespace Ombi.Controllers
OsArchitecture = RuntimeInformation.OSArchitecture.ToString(), OsArchitecture = RuntimeInformation.OSArchitecture.ToString(),
OsDescription = RuntimeInformation.OSDescription, OsDescription = RuntimeInformation.OSDescription,
ProcessArchitecture = RuntimeInformation.ProcessArchitecture.ToString(), ProcessArchitecture = RuntimeInformation.ProcessArchitecture.ToString(),
ApplicationBasePath =PlatformServices.Default.Application.ApplicationBasePath ApplicationBasePath =Directory.GetCurrentDirectory()
}; };
var version = AssemblyHelper.GetRuntimeVersion(); var version = AssemblyHelper.GetRuntimeVersion();
@ -234,6 +218,13 @@ namespace Ombi.Controllers
return await Save(settings); return await Save(settings);
} }
[ApiExplorerSettings(IgnoreApi = true)]
[HttpPost("customization/urlverify")]
public bool VerifyUrl([FromBody]UrlVerifyModel url)
{
return Uri.TryCreate(url.Url, UriKind.Absolute, out var __);
}
/// <summary> /// <summary>
/// Get's the preset themes available /// Get's the preset themes available
/// </summary> /// </summary>

View file

@ -30,6 +30,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Ombi.Core.Settings; using Ombi.Core.Settings;
using Ombi.Helpers;
using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models;
namespace Ombi.Controllers namespace Ombi.Controllers
@ -57,6 +58,18 @@ namespace Ombi.Controllers
} }
/// <summary>
/// Returns information about this ombi instance
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("info")]
public string GetInfo()
{
return AssemblyHelper.GetRuntimeVersion();
}
/// <summary> /// <summary>
/// Checks to see if we have run through the wizard /// Checks to see if we have run through the wizard
/// </summary> /// </summary>

View file

@ -2,6 +2,8 @@
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Ombi namespace Ombi
@ -29,6 +31,9 @@ namespace Ombi
private static Task HandleExceptionAsync(HttpContext context, Exception exception) private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{ {
var loggerFact = context.RequestServices.GetService<ILoggerFactory>();
var logger = loggerFact.CreateLogger<ErrorHandlingMiddleware>();
logger.LogError(exception, "Something bad happened, ErrorMiddleware caught this");
var code = HttpStatusCode.InternalServerError; // 500 if unexpected var code = HttpStatusCode.InternalServerError; // 500 if unexpected
//if (exception is NotFoundException) code = HttpStatusCode.NotFound; //if (exception is NotFoundException) code = HttpStatusCode.NotFound;

View file

@ -0,0 +1,7 @@
namespace Ombi.Models
{
public class UrlVerifyModel
{
public string Url { get; set; }
}
}

View file

@ -1,6 +1,6 @@
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk.Web"> <Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion> <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
@ -71,10 +71,10 @@
<PackageReference Include="Hangfire.RecurringJobExtensions" Version="1.1.6" /> <PackageReference Include="Hangfire.RecurringJobExtensions" Version="1.1.6" />
<PackageReference Include="Hangfire.SQLite" Version="1.4.2" /> <PackageReference Include="Hangfire.SQLite" Version="1.4.2" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.2" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.1.0" /> <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.0.3" />
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.0.0-alpha6-79" /> <PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.0.0-alpha6-79" />
<PackageReference Include="ncrontab" Version="3.3.0" /> <PackageReference Include="ncrontab" Version="3.3.0" />
<PackageReference Include="Serilog" Version="2.6.0-dev-00892" /> <PackageReference Include="Serilog" Version="2.6.0-dev-00892" />

View file

@ -3,14 +3,14 @@
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:5000/", "applicationUrl": "http://localhost:3579/",
"sslPort": 0 "sslPort": 0
} }
}, },
"profiles": { "profiles": {
"IIS Express": { "IIS Express": {
"commandName": "IISExpress", "commandName": "IISExpress",
"commandLineArgs": "-baseurl /testing", "commandLineArgs": "--host http://*:3579",
"launchBrowser": true, "launchBrowser": true,
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"

View file

@ -1,20 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Net;
using System.Security.Principal;
using System.Threading.Tasks;
using AutoMapper; using AutoMapper;
using AutoMapper.EquivalencyExpression; using AutoMapper.EquivalencyExpression;
using Hangfire; using Hangfire;
using Hangfire.Console;
using Hangfire.Dashboard; using Hangfire.Dashboard;
using Hangfire.SQLite; using Hangfire.SQLite;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SpaServices.Webpack; using Microsoft.AspNetCore.SpaServices.Webpack;
@ -34,7 +26,6 @@ using Ombi.Store.Context;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Serilog; using Serilog;
using Serilog.Events;
namespace Ombi namespace Ombi
{ {
@ -87,7 +78,6 @@ namespace Ombi
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services) public IServiceProvider ConfigureServices(IServiceCollection services)
{ {
TelemetryConfiguration.Active.DisableTelemetry = true;
// Add framework services. // Add framework services.
services.AddDbContext<OmbiContext>(); services.AddDbContext<OmbiContext>();
@ -221,8 +211,9 @@ namespace Ombi
app.UseAuthentication(); app.UseAuthentication();
app.UseMiddleware<ErrorHandlingMiddleware>(); app.UseMiddleware<ErrorHandlingMiddleware>();
app.UseMiddleware<ApiKeyMiddlewear>();
app.ApiKeyMiddlewear(app.ApplicationServices); //app.ApiKeyMiddlewear(app.ApplicationServices);
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(c => app.UseSwaggerUI(c =>
{ {

View file

@ -2,6 +2,7 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Reflection;
using System.Security.Principal; using System.Security.Principal;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -12,7 +13,6 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.PlatformAbstractions; using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Ombi.Config; using Ombi.Config;
using Ombi.Core.Authentication; using Ombi.Core.Authentication;
@ -114,90 +114,5 @@ namespace Ombi
x.TokenValidationParameters = tokenValidationParameters; x.TokenValidationParameters = tokenValidationParameters;
}); });
} }
public static void ApiKeyMiddlewear(this IApplicationBuilder app, IServiceProvider serviceProvider)
{
app.Use(async (context, next) =>
{
if (context.Request.Path.StartsWithSegments(new PathString("/api")))
{
// Let's check if this is an API Call
if (context.Request.Headers["ApiKey"].Any())
{
// validate the supplied API key
// Validate it
var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault();
await ValidateApiKey(serviceProvider, context, next, headerKey);
}
else if (context.Request.Query.ContainsKey("apikey"))
{
if (context.Request.Query.TryGetValue("apikey", out var queryKey))
{
await ValidateApiKey(serviceProvider, context, next, queryKey);
}
}
// User access token used by the mobile app
else if (context.Request.Headers["UserAccessToken"].Any())
{
var headerKey = context.Request.Headers["UserAccessToken"].FirstOrDefault();
await ValidateUserAccessToken(serviceProvider, context, next, headerKey);
}
else
{
await next();
}
}
else
{
await next();
}
});
}
private static async Task ValidateUserAccessToken(IServiceProvider serviceProvider, HttpContext context, Func<Task> next, string key)
{
if (key.IsNullOrEmpty())
{
await context.Response.WriteAsync("Invalid User Access Token");
return;
}
var um = serviceProvider.GetService<OmbiUserManager>();
var user = await um.Users.FirstOrDefaultAsync(x => x.UserAccessToken == key);
if (user == null)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsync("Invalid User Access Token");
}
else
{
var identity = new GenericIdentity(user.UserName);
var roles = await um.GetRolesAsync(user);
var principal = new GenericPrincipal(identity, roles.ToArray());
context.User = principal;
await next();
}
}
private static async Task ValidateApiKey(IServiceProvider serviceProvider, HttpContext context, Func<Task> next, string key)
{
var settingsProvider = serviceProvider.GetService<ISettingsService<OmbiSettings>>();
var ombiSettings = settingsProvider.GetSettings();
var valid = ombiSettings.ApiKey.Equals(key, StringComparison.CurrentCultureIgnoreCase);
if (!valid)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsync("Invalid API Key");
}
else
{
var identity = new GenericIdentity("API");
var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
context.User = principal;
await next();
}
}
} }
} }

View file

@ -2,15 +2,14 @@
"Logging": { "Logging": {
"IncludeScopes": false, "IncludeScopes": false,
"LogLevel": { "LogLevel": {
"Default": "Debug", "Default": "Information",
"System": "Debug", "System": "Information",
"Microsoft": "None", "Microsoft": "None",
"Hangfire": "None" "Hangfire": "None"
} }
}, },
"ApplicationSettings": { "ApplicationSettings": {
"Verison": "{{VERSIONNUMBER}}", "Verison": "{{VERSIONNUMBER}}",
"OmbiService": "https://ombiservice.azurewebsites.net/",
"Branch": "{{BRANCH}}", "Branch": "{{BRANCH}}",
"FriendlyVersion": "v3.0.0" "FriendlyVersion": "v3.0.0"
}, },
@ -29,7 +28,13 @@
155, 155,
13, 13,
1891, 1891,
399106 399106,
351286,
348350,
260513,
372058,
299536,
383498
], ],
"TvShows": [ "TvShows": [
121361, 121361,
@ -37,7 +42,10 @@
81189, 81189,
79126, 79126,
79349, 79349,
275274 275274,
305288,
296762,
280619
] ]
} }
} }