mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-14 09:12:57 -07:00
commit
f55a581862
75 changed files with 1890 additions and 441 deletions
79
CHANGELOG.md
79
CHANGELOG.md
|
@ -1,5 +1,84 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## (unreleased)
|
||||||
|
|
||||||
|
### **New Features**
|
||||||
|
|
||||||
|
- Updated the Emby availability checker to bring it more in line with what we do with Plex. [TidusJar]
|
||||||
|
|
||||||
|
- Added the ability to impersonate a user when using the API Key. This allows people to use the API and request as a certain user. #2363. [Jamie Rees]
|
||||||
|
|
||||||
|
- Added more background images and it will loop through the available ones. [Jamie Rees]
|
||||||
|
|
||||||
|
- Added chunk hashing to resolve #2330. [Jamie Rees]
|
||||||
|
|
||||||
|
- Added API at /api/v1/status/info to get branch and version information #2331. [Jamie Rees]
|
||||||
|
|
||||||
|
- Update to .net 2.1.1. [Jamie]
|
||||||
|
|
||||||
|
### **Fixes**
|
||||||
|
|
||||||
|
- Fix #2322 caused by continue statement inside try catch block. [Anojh]
|
||||||
|
|
||||||
|
- Fixed #2367. [TidusJar]
|
||||||
|
|
||||||
|
- Fixed the issue where you could not delete a user #2365. [TidusJar]
|
||||||
|
|
||||||
|
- Another attempt to fix #2366. [Jamie Rees]
|
||||||
|
|
||||||
|
- Fixed the Plex OAuth warning. [Jamie]
|
||||||
|
|
||||||
|
- Revert "Fixed Plex OAuth, should no longer show Insecure warning" [Jamie Rees]
|
||||||
|
|
||||||
|
- Fixed Plex OAuth, should no longer show Insecure warning. [Jamie Rees]
|
||||||
|
|
||||||
|
- Fixed the View On Emby URL since the Link changed #2368. [Jamie Rees]
|
||||||
|
|
||||||
|
- Fixed the issue where episodes were not being marked as available in the search #2367. [Jamie Rees]
|
||||||
|
|
||||||
|
- Fixed #2371. [Jamie Rees]
|
||||||
|
|
||||||
|
- Fixed collection issues in Emby #2366. [Jamie Rees]
|
||||||
|
|
||||||
|
- Do not delete the Emby Information every time we run, let's keep the content now. [Jamie Rees]
|
||||||
|
|
||||||
|
- Emby Improvements: Batch up the amount we get from the server. [Jamie Rees]
|
||||||
|
|
||||||
|
- Log errors when they are uncaught. [Jamie Rees]
|
||||||
|
|
||||||
|
- Fix unclosed table tags causing overflow #2322. [Anojh]
|
||||||
|
|
||||||
|
- This should now fix #2350. [Jamie]
|
||||||
|
|
||||||
|
- Improve the validation around the Application URL. [Jamie Rees]
|
||||||
|
|
||||||
|
- Fixed #2341. [Jamie Rees]
|
||||||
|
|
||||||
|
- Stop spamming errors when FanArt doesn't have the image. [Jamie Rees]
|
||||||
|
|
||||||
|
- Fixed #2338. [Jamie Rees]
|
||||||
|
|
||||||
|
- Removed some logging statements. [Jamie Rees]
|
||||||
|
|
||||||
|
- Fixed the api key being case sensative #2350. [Jamie Rees]
|
||||||
|
|
||||||
|
- Improved the Emby API #2230 Thanks Luke! [Jamie Rees]
|
||||||
|
|
||||||
|
- Revert. [Jamie Rees]
|
||||||
|
|
||||||
|
- Fixed a small error in the Mobile Notification Provider. [Jamie Rees]
|
||||||
|
|
||||||
|
- Minor style tweaks. [Randall Bruder]
|
||||||
|
|
||||||
|
- Downgrade to .net core 2.0. [Jamie Rees]
|
||||||
|
|
||||||
|
- Downgrade Microsoft.AspNetCore.All package back to 2.0.8. [Jamie Rees]
|
||||||
|
|
||||||
|
- Removed old code. [Jamie Rees]
|
||||||
|
|
||||||
|
- Swap out the old way of validating the API key with a real middlewear this time. [Jamie Rees]
|
||||||
|
|
||||||
|
|
||||||
## v3.0.3421 (2018-06-23)
|
## v3.0.3421 (2018-06-23)
|
||||||
|
|
||||||
### **New Features**
|
### **New Features**
|
||||||
|
|
10
appveyor.yml
10
appveyor.yml
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -90,27 +91,31 @@ namespace Ombi.Api.Emby
|
||||||
request.AddContentHeader("Content-Type", "application/json");
|
request.AddContentHeader("Content-Type", "application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EmbyItemContainer<MovieInformation>> GetCollection(string mediaId, string apiKey, string userId, string baseUrl)
|
public async Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId, string apiKey, string userId, string baseUrl)
|
||||||
{
|
{
|
||||||
var request = new Request($"emby/users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get);
|
var request = new Request($"emby/users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get);
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
|
|
||||||
return await Api.Request<EmbyItemContainer<MovieInformation>>(request);
|
request.AddQueryString("Fields", "ProviderIds,Overview");
|
||||||
|
|
||||||
|
request.AddQueryString("VirtualItem", "False");
|
||||||
|
|
||||||
|
return await Api.Request<EmbyItemContainer<EmbyMovie>>(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 +134,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);
|
||||||
|
|
||||||
|
|
|
@ -14,12 +14,17 @@ 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<MovieInformation>> GetCollection(string mediaId, string apiKey, string userId,
|
Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId,
|
||||||
string baseUrl);
|
string baseUri);
|
||||||
|
|
||||||
|
Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId,
|
||||||
|
string baseUri);
|
||||||
|
|
||||||
|
Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId,
|
||||||
|
string apiKey, string userId, string baseUrl);
|
||||||
|
|
||||||
Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl);
|
Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl);
|
||||||
Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl);
|
Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl);
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,7 @@ namespace Ombi.Api.Plex
|
||||||
Task<PlexFriends> GetUsers(string authToken);
|
Task<PlexFriends> GetUsers(string authToken);
|
||||||
Task<PlexAccount> GetAccount(string authToken);
|
Task<PlexAccount> GetAccount(string authToken);
|
||||||
Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId);
|
Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId);
|
||||||
Task<OAuthPin> CreatePin();
|
|
||||||
Task<OAuthPin> GetPin(int pinId);
|
Task<OAuthPin> GetPin(int pinId);
|
||||||
Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard);
|
Task<Uri> GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,14 +16,16 @@ namespace Ombi.Api.Plex
|
||||||
{
|
{
|
||||||
public class PlexApi : IPlexApi
|
public class PlexApi : IPlexApi
|
||||||
{
|
{
|
||||||
public PlexApi(IApi api, ISettingsService<CustomizationSettings> settings)
|
public PlexApi(IApi api, ISettingsService<CustomizationSettings> settings, ISettingsService<PlexSettings> p)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
_custom = settings;
|
_custom = settings;
|
||||||
|
_plexSettings = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IApi Api { get; }
|
private IApi Api { get; }
|
||||||
private readonly ISettingsService<CustomizationSettings> _custom;
|
private readonly ISettingsService<CustomizationSettings> _custom;
|
||||||
|
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||||
|
|
||||||
private string _app;
|
private string _app;
|
||||||
private string ApplicationName
|
private string ApplicationName
|
||||||
|
@ -69,7 +71,7 @@ namespace Ombi.Api.Plex
|
||||||
};
|
};
|
||||||
var request = new Request(SignInUri, string.Empty, HttpMethod.Post);
|
var request = new Request(SignInUri, string.Empty, HttpMethod.Post);
|
||||||
|
|
||||||
AddHeaders(request);
|
await AddHeaders(request);
|
||||||
request.AddJsonBody(userModel);
|
request.AddJsonBody(userModel);
|
||||||
|
|
||||||
var obj = await Api.Request<PlexAuthentication>(request);
|
var obj = await Api.Request<PlexAuthentication>(request);
|
||||||
|
@ -80,14 +82,14 @@ namespace Ombi.Api.Plex
|
||||||
public async Task<PlexStatus> GetStatus(string authToken, string uri)
|
public async Task<PlexStatus> GetStatus(string authToken, string uri)
|
||||||
{
|
{
|
||||||
var request = new Request(uri, string.Empty, HttpMethod.Get);
|
var request = new Request(uri, string.Empty, HttpMethod.Get);
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
return await Api.Request<PlexStatus>(request);
|
return await Api.Request<PlexStatus>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PlexAccount> GetAccount(string authToken)
|
public async Task<PlexAccount> GetAccount(string authToken)
|
||||||
{
|
{
|
||||||
var request = new Request(GetAccountUri, string.Empty, HttpMethod.Get);
|
var request = new Request(GetAccountUri, string.Empty, HttpMethod.Get);
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
return await Api.Request<PlexAccount>(request);
|
return await Api.Request<PlexAccount>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +97,7 @@ namespace Ombi.Api.Plex
|
||||||
{
|
{
|
||||||
var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml);
|
var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml);
|
||||||
|
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
|
|
||||||
return await Api.Request<PlexServer>(request);
|
return await Api.Request<PlexServer>(request);
|
||||||
}
|
}
|
||||||
|
@ -103,14 +105,14 @@ namespace Ombi.Api.Plex
|
||||||
public async Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost)
|
public async Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost)
|
||||||
{
|
{
|
||||||
var request = new Request("library/sections", plexFullHost, HttpMethod.Get);
|
var request = new Request("library/sections", plexFullHost, HttpMethod.Get);
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
return await Api.Request<PlexContainer>(request);
|
return await Api.Request<PlexContainer>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PlexContainer> GetLibrary(string authToken, string plexFullHost, string libraryId)
|
public async Task<PlexContainer> GetLibrary(string authToken, string plexFullHost, string libraryId)
|
||||||
{
|
{
|
||||||
var request = new Request($"library/sections/{libraryId}/all", plexFullHost, HttpMethod.Get);
|
var request = new Request($"library/sections/{libraryId}/all", plexFullHost, HttpMethod.Get);
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
return await Api.Request<PlexContainer>(request);
|
return await Api.Request<PlexContainer>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,21 +130,21 @@ namespace Ombi.Api.Plex
|
||||||
public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey)
|
public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey)
|
||||||
{
|
{
|
||||||
var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get);
|
var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get);
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
return await Api.Request<PlexMetadata>(request);
|
return await Api.Request<PlexMetadata>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId)
|
public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId)
|
||||||
{
|
{
|
||||||
var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get);
|
var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get);
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
return await Api.Request<PlexMetadata>(request);
|
return await Api.Request<PlexMetadata>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey)
|
public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey)
|
||||||
{
|
{
|
||||||
var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get);
|
var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get);
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
return await Api.Request<PlexMetadata>(request);
|
return await Api.Request<PlexMetadata>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +163,7 @@ namespace Ombi.Api.Plex
|
||||||
|
|
||||||
request.AddQueryString("type", "4");
|
request.AddQueryString("type", "4");
|
||||||
AddLimitHeaders(request, start, retCount);
|
AddLimitHeaders(request, start, retCount);
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
|
|
||||||
return await Api.Request<PlexContainer>(request);
|
return await Api.Request<PlexContainer>(request);
|
||||||
}
|
}
|
||||||
|
@ -174,8 +176,8 @@ namespace Ombi.Api.Plex
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<PlexFriends> GetUsers(string authToken)
|
public async Task<PlexFriends> GetUsers(string authToken)
|
||||||
{
|
{
|
||||||
var request = new Request(string.Empty,FriendsUri, HttpMethod.Get, ContentType.Xml);
|
var request = new Request(string.Empty, FriendsUri, HttpMethod.Get, ContentType.Xml);
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
|
|
||||||
return await Api.Request<PlexFriends>(request);
|
return await Api.Request<PlexFriends>(request);
|
||||||
}
|
}
|
||||||
|
@ -183,33 +185,24 @@ namespace Ombi.Api.Plex
|
||||||
public async Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId)
|
public async Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId)
|
||||||
{
|
{
|
||||||
var request = new Request($"library/sections/{sectionId}/recentlyAdded", uri, HttpMethod.Get);
|
var request = new Request($"library/sections/{sectionId}/recentlyAdded", uri, HttpMethod.Get);
|
||||||
AddHeaders(request, authToken);
|
await AddHeaders(request, authToken);
|
||||||
AddLimitHeaders(request, 0, 50);
|
AddLimitHeaders(request, 0, 50);
|
||||||
|
|
||||||
return await Api.Request<PlexMetadata>(request);
|
return await Api.Request<PlexMetadata>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OAuthPin> CreatePin()
|
|
||||||
{
|
|
||||||
var request = new Request($"api/v2/pins", "https://plex.tv/", HttpMethod.Post);
|
|
||||||
request.AddQueryString("strong", "true");
|
|
||||||
AddHeaders(request);
|
|
||||||
|
|
||||||
return await Api.Request<OAuthPin>(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<OAuthPin> GetPin(int pinId)
|
public async Task<OAuthPin> GetPin(int pinId)
|
||||||
{
|
{
|
||||||
var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get);
|
var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get);
|
||||||
AddHeaders(request);
|
await AddHeaders(request);
|
||||||
|
|
||||||
return await Api.Request<OAuthPin>(request);
|
return await Api.Request<OAuthPin>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard)
|
public async Task<Uri> GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard)
|
||||||
{
|
{
|
||||||
var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get);
|
var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get);
|
||||||
AddHeaders(request);
|
await AddHeaders(request);
|
||||||
var forwardUrl = wizard
|
var forwardUrl = wizard
|
||||||
? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get)
|
? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get)
|
||||||
: new Request($"Login/OAuth/{pinId}", applicationUrl, HttpMethod.Get);
|
: new Request($"Login/OAuth/{pinId}", applicationUrl, HttpMethod.Get);
|
||||||
|
@ -217,9 +210,15 @@ namespace Ombi.Api.Plex
|
||||||
request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString());
|
request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString());
|
||||||
request.AddQueryString("pinID", pinId.ToString());
|
request.AddQueryString("pinID", pinId.ToString());
|
||||||
request.AddQueryString("code", code);
|
request.AddQueryString("code", code);
|
||||||
request.AddQueryString("context[device][product]", "Ombi");
|
request.AddQueryString("context[device][product]", ApplicationName);
|
||||||
request.AddQueryString("context[device][environment]", "bundled");
|
request.AddQueryString("context[device][environment]", "bundled");
|
||||||
request.AddQueryString("clientID", $"OmbiV3");
|
request.AddQueryString("context[device][layout]", "desktop");
|
||||||
|
request.AddQueryString("context[device][platform]", "Web");
|
||||||
|
request.AddQueryString("context[device][device]", "Ombi (Web)");
|
||||||
|
|
||||||
|
var s = await GetSettings();
|
||||||
|
await CheckInstallId(s);
|
||||||
|
request.AddQueryString("clientID", s.InstallId.ToString("N"));
|
||||||
|
|
||||||
if (request.FullUri.Fragment.Equals("#"))
|
if (request.FullUri.Fragment.Equals("#"))
|
||||||
{
|
{
|
||||||
|
@ -238,21 +237,25 @@ namespace Ombi.Api.Plex
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request"></param>
|
/// <param name="request"></param>
|
||||||
/// <param name="authToken"></param>
|
/// <param name="authToken"></param>
|
||||||
private void AddHeaders(Request request, string authToken)
|
private async Task AddHeaders(Request request, string authToken)
|
||||||
{
|
{
|
||||||
request.AddHeader("X-Plex-Token", authToken);
|
request.AddHeader("X-Plex-Token", authToken);
|
||||||
AddHeaders(request);
|
await AddHeaders(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the main required headers to the Plex Request
|
/// Adds the main required headers to the Plex Request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request"></param>
|
/// <param name="request"></param>
|
||||||
private void AddHeaders(Request request)
|
private async Task AddHeaders(Request request)
|
||||||
{
|
{
|
||||||
request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3");
|
var s = await GetSettings();
|
||||||
|
await CheckInstallId(s);
|
||||||
|
request.AddHeader("X-Plex-Client-Identifier", s.InstallId.ToString("N"));
|
||||||
request.AddHeader("X-Plex-Product", ApplicationName);
|
request.AddHeader("X-Plex-Product", ApplicationName);
|
||||||
request.AddHeader("X-Plex-Version", "3");
|
request.AddHeader("X-Plex-Version", "3");
|
||||||
|
request.AddHeader("X-Plex-Device", "Ombi (Web)");
|
||||||
|
request.AddHeader("X-Plex-Platform", "Web");
|
||||||
request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml");
|
request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml");
|
||||||
request.AddHeader("Accept", "application/json");
|
request.AddHeader("Accept", "application/json");
|
||||||
}
|
}
|
||||||
|
@ -262,5 +265,19 @@ namespace Ombi.Api.Plex
|
||||||
request.AddHeader("X-Plex-Container-Start", from.ToString());
|
request.AddHeader("X-Plex-Container-Start", from.ToString());
|
||||||
request.AddHeader("X-Plex-Container-Size", to.ToString());
|
request.AddHeader("X-Plex-Container-Size", to.ToString());
|
||||||
}
|
}
|
||||||
|
private async Task CheckInstallId(PlexSettings s)
|
||||||
|
{
|
||||||
|
if (s.InstallId == null || s.InstallId == Guid.Empty)
|
||||||
|
{
|
||||||
|
s.InstallId = Guid.NewGuid();
|
||||||
|
await _plexSettings.SaveSettingsAsync(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlexSettings _settings;
|
||||||
|
private async Task<PlexSettings> GetSettings()
|
||||||
|
{
|
||||||
|
return _settings ?? (_settings = await _plexSettings.GetSettingsAsync());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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>();
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -20,12 +20,6 @@ namespace Ombi.Core.Authentication
|
||||||
private readonly IPlexApi _api;
|
private readonly IPlexApi _api;
|
||||||
private readonly ISettingsService<CustomizationSettings> _customizationSettingsService;
|
private readonly ISettingsService<CustomizationSettings> _customizationSettingsService;
|
||||||
|
|
||||||
public async Task<OAuthPin> RequestPin()
|
|
||||||
{
|
|
||||||
var pin = await _api.CreatePin();
|
|
||||||
return pin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetAccessTokenFromPin(int pinId)
|
public async Task<string> GetAccessTokenFromPin(int pinId)
|
||||||
{
|
{
|
||||||
var pin = await _api.GetPin(pinId);
|
var pin = await _api.GetPin(pinId);
|
||||||
|
@ -58,14 +52,14 @@ namespace Ombi.Core.Authentication
|
||||||
public async Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null)
|
public async Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null)
|
||||||
{
|
{
|
||||||
var settings = await _customizationSettingsService.GetSettingsAsync();
|
var settings = await _customizationSettingsService.GetSettingsAsync();
|
||||||
var url = _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl, false);
|
var url = await _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl, false);
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress)
|
public async Task<Uri> GetWizardOAuthUrl(int pinId, string code, string websiteAddress)
|
||||||
{
|
{
|
||||||
var url = _api.GetOAuthUrl(pinId, code, websiteAddress, true);
|
var url = await _api.GetOAuthUrl(pinId, code, websiteAddress, true);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ombi.Api.Plex.Models;
|
using Ombi.Api.Plex.Models;
|
||||||
using Ombi.Api.Plex.Models.OAuth;
|
|
||||||
|
|
||||||
namespace Ombi.Core.Authentication
|
namespace Ombi.Core.Authentication
|
||||||
{
|
{
|
||||||
public interface IPlexOAuthManager
|
public interface IPlexOAuthManager
|
||||||
{
|
{
|
||||||
Task<string> GetAccessTokenFromPin(int pinId);
|
Task<string> GetAccessTokenFromPin(int pinId);
|
||||||
Task<OAuthPin> RequestPin();
|
|
||||||
Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null);
|
Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null);
|
||||||
Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress);
|
Task<Uri> GetWizardOAuthUrl(int pinId, string code, string websiteAddress);
|
||||||
Task<PlexAccount> GetAccount(string accessToken);
|
Task<PlexAccount> GetAccount(string accessToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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" />
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System.Linq;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
|
@ -59,10 +61,19 @@ namespace Ombi.Core.Rule.Rules.Search
|
||||||
{
|
{
|
||||||
EmbyEpisode epExists = null;
|
EmbyEpisode epExists = null;
|
||||||
|
|
||||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
if (item.HasImdb)
|
||||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
{
|
||||||
x.Series.ProviderId == item.ProviderId.ToString());
|
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
|
||||||
|
&& e.ImdbId == item.ImdbId);
|
||||||
|
} if (item.HasTvDb && epExists == null)
|
||||||
|
{
|
||||||
|
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
|
||||||
|
&& e.Series.TvDbId == item.TvDbId);
|
||||||
|
} if (item.HasTheMovieDb && epExists == null)
|
||||||
|
{
|
||||||
|
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
|
||||||
|
&& e.TheMovieDbId == item.TheMovieDbId);
|
||||||
|
}
|
||||||
|
|
||||||
if (epExists != null)
|
if (epExists != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -255,7 +255,12 @@ namespace Ombi.Core.Senders
|
||||||
{
|
{
|
||||||
// We have the same amount of requests as all of the episodes in the season.
|
// We have the same amount of requests as all of the episodes in the season.
|
||||||
var existingSeason =
|
var existingSeason =
|
||||||
result.seasons.First(x => x.seasonNumber == season.SeasonNumber);
|
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);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
existingSeason.monitored = true;
|
existingSeason.monitored = true;
|
||||||
seriesChanges = true;
|
seriesChanges = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace Ombi.Helpers
|
||||||
public static string GetEmbyMediaUrl(string mediaId)
|
public static string GetEmbyMediaUrl(string mediaId)
|
||||||
{
|
{
|
||||||
var url =
|
var url =
|
||||||
$"http://app.emby.media/itemdetails.html?id={mediaId}";
|
$"http://app.emby.media/#!/itemdetails.html?id={mediaId}";
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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}");
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -121,24 +121,53 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
|
|
||||||
foreach (var child in tv)
|
foreach (var child in tv)
|
||||||
{
|
{
|
||||||
IQueryable<EmbyEpisode> seriesEpisodes;
|
|
||||||
if (child.ParentRequest.TvDbId > 0)
|
var useImdb = false;
|
||||||
|
var useTvDb = false;
|
||||||
|
if (child.ParentRequest.ImdbId.HasValue())
|
||||||
{
|
{
|
||||||
seriesEpisodes = embyEpisodes.Where(x => x.Series.TvDbId == child.ParentRequest.TvDbId.ToString());
|
useImdb = true;
|
||||||
}
|
}
|
||||||
else if(child.ParentRequest.ImdbId.HasValue())
|
|
||||||
|
if (child.ParentRequest.TvDbId.ToString().HasValue())
|
||||||
{
|
{
|
||||||
seriesEpisodes = embyEpisodes.Where(x => x.Series.ImdbId == child.ParentRequest.ImdbId);
|
useTvDb = true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
var tvDbId = child.ParentRequest.TvDbId;
|
||||||
|
var imdbId = child.ParentRequest.ImdbId;
|
||||||
|
IQueryable<EmbyEpisode> seriesEpisodes = null;
|
||||||
|
if (useImdb)
|
||||||
|
{
|
||||||
|
seriesEpisodes = embyEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any()))
|
||||||
|
{
|
||||||
|
seriesEpisodes = embyEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seriesEpisodes == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!seriesEpisodes.Any())
|
||||||
|
{
|
||||||
|
// Let's try and match the series by name
|
||||||
|
seriesEpisodes = embyEpisodes.Where(x =>
|
||||||
|
x.Series.Title.Equals(child.Title, StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var season in child.SeasonRequests)
|
foreach (var season in child.SeasonRequests)
|
||||||
{
|
{
|
||||||
foreach (var episode in season.Episodes)
|
foreach (var episode in season.Episodes)
|
||||||
{
|
{
|
||||||
|
if (episode.Available)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var foundEp = await seriesEpisodes.FirstOrDefaultAsync(
|
var foundEp = await seriesEpisodes.FirstOrDefaultAsync(
|
||||||
x => x.EpisodeNumber == episode.EpisodeNumber &&
|
x => x.EpisodeNumber == episode.EpisodeNumber &&
|
||||||
x.SeasonNumber == episode.Season.SeasonNumber);
|
x.SeasonNumber == episode.Season.SeasonNumber);
|
||||||
|
@ -160,9 +189,9 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
{
|
{
|
||||||
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
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,71 +68,110 @@ 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)
|
|
||||||
|
while (processed < totalCount)
|
||||||
{
|
{
|
||||||
if (movie.Type.Equals("boxset", StringComparison.CurrentCultureIgnoreCase))
|
foreach (var movie in movies.Items)
|
||||||
{
|
{
|
||||||
var movieInfo =
|
if (movie.Type.Equals("boxset", StringComparison.InvariantCultureIgnoreCase))
|
||||||
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,
|
var movieInfo =
|
||||||
server.AdministratorId, server.FullUri);
|
await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri);
|
||||||
await ProcessMovies(info, mediaToAdd);
|
foreach (var item in movieInfo.Items)
|
||||||
|
{
|
||||||
|
await ProcessMovies(item, mediaToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
processed++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processed++;
|
||||||
|
// Regular movie
|
||||||
|
await ProcessMovies(movie, mediaToAdd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// Regular movie
|
|
||||||
var movieInfo = await _api.GetMovieInformation(movie.Id, server.ApiKey,
|
|
||||||
server.AdministratorId, server.FullUri);
|
|
||||||
|
|
||||||
await ProcessMovies(movieInfo, mediaToAdd);
|
// Get the next batch
|
||||||
}
|
movies = await _api.GetAllMovies(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
|
||||||
|
await _repo.AddRange(mediaToAdd);
|
||||||
|
mediaToAdd.Clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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);
|
||||||
|
var alreadyGoingToAdd = content.Any(x => x.EmbyId == movieInfo.Id);
|
||||||
if (existingMovie == null)
|
if (existingMovie == null && !alreadyGoingToAdd)
|
||||||
|
{
|
||||||
|
_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 +182,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)
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -492,41 +492,42 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
var orderedTv = series.OrderByDescending(x => x.AddedAt);
|
var orderedTv = series.OrderByDescending(x => x.AddedAt);
|
||||||
foreach (var t in orderedTv)
|
foreach (var t in orderedTv)
|
||||||
{
|
{
|
||||||
|
if (!t.HasTvDb)
|
||||||
|
{
|
||||||
|
// We may need to use themoviedb for the imdbid or their own id to get info
|
||||||
|
if (t.HasTheMovieDb)
|
||||||
|
{
|
||||||
|
int.TryParse(t.TheMovieDbId, out var movieId);
|
||||||
|
var externals = await _movieApi.GetTvExternals(movieId);
|
||||||
|
if (externals == null || externals.tvdb_id <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
t.TvDbId = externals.tvdb_id.ToString();
|
||||||
|
}
|
||||||
|
// WE could check the below but we need to get the moviedb and then perform the above, let the metadata job figure this out.
|
||||||
|
//else if(t.HasImdb)
|
||||||
|
//{
|
||||||
|
// // Check the imdbid
|
||||||
|
// var externals = await _movieApi.Find(t.ImdbId, ExternalSource.imdb_id);
|
||||||
|
// if (externals?.tv_results == null || externals.tv_results.Length <= 0)
|
||||||
|
// {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// t.TvDbId = externals.tv_results.FirstOrDefault()..ToString();
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int.TryParse(t.TvDbId, out var tvdbId);
|
||||||
|
var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId);
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!t.HasTvDb)
|
|
||||||
{
|
|
||||||
// We may need to use themoviedb for the imdbid or their own id to get info
|
|
||||||
if (t.HasTheMovieDb)
|
|
||||||
{
|
|
||||||
int.TryParse(t.TheMovieDbId, out var movieId);
|
|
||||||
var externals = await _movieApi.GetTvExternals(movieId);
|
|
||||||
if (externals == null || externals.tvdb_id <= 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
t.TvDbId = externals.tvdb_id.ToString();
|
|
||||||
}
|
|
||||||
// WE could check the below but we need to get the moviedb and then perform the above, let the metadata job figure this out.
|
|
||||||
//else if(t.HasImdb)
|
|
||||||
//{
|
|
||||||
// // Check the imdbid
|
|
||||||
// var externals = await _movieApi.Find(t.ImdbId, ExternalSource.imdb_id);
|
|
||||||
// if (externals?.tv_results == null || externals.tv_results.Length <= 0)
|
|
||||||
// {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// t.TvDbId = externals.tv_results.FirstOrDefault()..ToString();
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int.TryParse(t.TvDbId, out var tvdbId);
|
|
||||||
var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId);
|
|
||||||
if (info == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var banner = info.image?.original;
|
var banner = info.image?.original;
|
||||||
if (!string.IsNullOrEmpty(banner))
|
if (!string.IsNullOrEmpty(banner))
|
||||||
{
|
{
|
||||||
|
@ -676,20 +677,20 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
var orderedTv = series.OrderByDescending(x => x.AddedAt);
|
var orderedTv = series.OrderByDescending(x => x.AddedAt);
|
||||||
foreach (var t in orderedTv)
|
foreach (var t in orderedTv)
|
||||||
{
|
{
|
||||||
|
if (!t.TvDbId.HasValue())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int.TryParse(t.TvDbId, out var tvdbId);
|
||||||
|
var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId);
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!t.TvDbId.HasValue())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int.TryParse(t.TvDbId, out var tvdbId);
|
|
||||||
var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId);
|
|
||||||
if (info == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var banner = info.image?.original;
|
var banner = info.image?.original;
|
||||||
if (!string.IsNullOrEmpty(banner))
|
if (!string.IsNullOrEmpty(banner))
|
||||||
{
|
{
|
||||||
|
|
|
@ -79,11 +79,16 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString());
|
seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString());
|
||||||
}
|
}
|
||||||
if (useTvDb)
|
if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any()) )
|
||||||
{
|
{
|
||||||
seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString());
|
seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (seriesEpisodes == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!seriesEpisodes.Any())
|
if (!seriesEpisodes.Any())
|
||||||
{
|
{
|
||||||
// Let's try and match the series by name
|
// Let's try and match the series by name
|
||||||
|
@ -122,7 +127,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
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -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,7 +163,7 @@ 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 ?? new Metadata[]{})
|
foreach (var epInfo in content.Metadata ?? new Metadata[]{})
|
||||||
{
|
{
|
||||||
|
@ -208,7 +208,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
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[] { })
|
||||||
{
|
{
|
||||||
|
@ -237,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
|
||||||
|
@ -251,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,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
|
||||||
|
@ -421,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;
|
||||||
|
@ -472,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...
|
||||||
|
@ -573,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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Ombi.Settings.Settings.Models.External;
|
using Ombi.Settings.Settings.Models.External;
|
||||||
|
|
||||||
namespace Ombi.Core.Settings.Models.External
|
namespace Ombi.Core.Settings.Models.External
|
||||||
|
@ -6,6 +7,10 @@ namespace Ombi.Core.Settings.Models.External
|
||||||
public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings
|
public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings
|
||||||
{
|
{
|
||||||
public bool Enable { get; set; }
|
public bool Enable { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// This is the ClientId for OAuth
|
||||||
|
/// </summary>
|
||||||
|
public Guid InstallId { get; set; }
|
||||||
public List<PlexServers> Servers { get; set; }
|
public List<PlexServers> Servers { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
981
src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.Designer.cs
generated
Normal file
981
src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.Designer.cs
generated
Normal file
|
@ -0,0 +1,981 @@
|
||||||
|
// <auto-generated />
|
||||||
|
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 Ombi.Store.Context;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
using Ombi.Store.Entities.Requests;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(OmbiContext))]
|
||||||
|
[Migration("20180703200952_EmbyUrlFix")]
|
||||||
|
partial class EmbyUrlFix
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.0.3-rtm-10026");
|
||||||
|
|
||||||
|
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<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<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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.cs
Normal file
20
src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations
|
||||||
|
{
|
||||||
|
public partial class EmbyUrlFix : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
@"UPDATE EmbyContent SET Url = replace( Url, 'http://app.emby.media/itemdetails.html', 'http://app.emby.media/#!/itemdetails.html' ) WHERE Url LIKE 'http://app.emby.media/itemdetails.html%';");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
126
src/Ombi/ApiKeyMiddlewear.cs
Normal file
126
src/Ombi/ApiKeyMiddlewear.cs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
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
|
||||||
|
{
|
||||||
|
// Check if we have a UserName header if so we can impersonate that user
|
||||||
|
if (context.Request.Headers.Keys.Contains("UserName", StringComparer.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
var username = context.Request.Headers["UserName"].FirstOrDefault();
|
||||||
|
var um = context.RequestServices.GetService<OmbiUserManager>();
|
||||||
|
var user = await um.Users.FirstOrDefaultAsync(x =>
|
||||||
|
x.UserName.Equals(username, StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||||
|
await context.Response.WriteAsync("Invalid User");
|
||||||
|
await next.Invoke(context);
|
||||||
|
}
|
||||||
|
var roles = await um.GetRolesAsync(user);
|
||||||
|
var identity = new GenericIdentity(user.UserName);
|
||||||
|
var principal = new GenericPrincipal(identity, roles.ToArray());
|
||||||
|
context.User = principal;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var identity = new GenericIdentity("API");
|
||||||
|
var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
|
||||||
|
context.User = principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
await next.Invoke(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,7 +36,7 @@ import { ImageService } from "./services";
|
||||||
import { LandingPageService } from "./services";
|
import { LandingPageService } from "./services";
|
||||||
import { NotificationService } from "./services";
|
import { NotificationService } from "./services";
|
||||||
import { SettingsService } from "./services";
|
import { SettingsService } from "./services";
|
||||||
import { IssuesService, JobService, StatusService } from "./services";
|
import { IssuesService, JobService, PlexTvService, StatusService } from "./services";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "*", component: PageNotFoundComponent },
|
{ path: "*", component: PageNotFoundComponent },
|
||||||
|
@ -133,6 +133,7 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
|
||||||
CookieService,
|
CookieService,
|
||||||
JobService,
|
JobService,
|
||||||
IssuesService,
|
IssuesService,
|
||||||
|
PlexTvService,
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
export interface IUserLogin {
|
import { IPlexPin } from "../interfaces";
|
||||||
|
|
||||||
|
export interface IUserLogin {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
rememberMe: boolean;
|
rememberMe: boolean;
|
||||||
usePlexOAuth: boolean;
|
usePlexOAuth: boolean;
|
||||||
|
plexTvPin: IPlexPin;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILocalUser {
|
export interface ILocalUser {
|
||||||
|
|
|
@ -2,6 +2,16 @@
|
||||||
user: IPlexUser;
|
user: IPlexUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IPlexPin {
|
||||||
|
id: number;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPlexOAuthViewModel {
|
||||||
|
wizard: boolean;
|
||||||
|
pin: IPlexPin;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IPlexOAuthAccessToken {
|
export interface IPlexOAuthAccessToken {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { TranslateService } from "@ngx-translate/core";
|
||||||
import { PlatformLocation } from "@angular/common";
|
import { PlatformLocation } from "@angular/common";
|
||||||
import { AuthService } from "../auth/auth.service";
|
import { AuthService } from "../auth/auth.service";
|
||||||
import { IAuthenticationSettings, ICustomizationSettings } from "../interfaces";
|
import { IAuthenticationSettings, ICustomizationSettings } from "../interfaces";
|
||||||
import { NotificationService } from "../services";
|
import { NotificationService, PlexTvService } from "../services";
|
||||||
import { SettingsService } from "../services";
|
import { SettingsService } from "../services";
|
||||||
import { StatusService } from "../services";
|
import { StatusService } from "../services";
|
||||||
|
|
||||||
|
@ -40,13 +40,14 @@ export class LoginComponent implements OnDestroy, OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
private timer: any;
|
private timer: any;
|
||||||
|
private clientId: string;
|
||||||
|
|
||||||
private errorBody: string;
|
private errorBody: string;
|
||||||
private errorValidation: string;
|
private errorValidation: string;
|
||||||
|
|
||||||
constructor(private authService: AuthService, private router: Router, private notify: NotificationService, private status: StatusService,
|
constructor(private authService: AuthService, private router: Router, private notify: NotificationService, private status: StatusService,
|
||||||
private fb: FormBuilder, private settingsService: SettingsService, private images: ImageService, private sanitizer: DomSanitizer,
|
private fb: FormBuilder, private settingsService: SettingsService, private images: ImageService, private sanitizer: DomSanitizer,
|
||||||
private route: ActivatedRoute, private location: PlatformLocation, private readonly translate: TranslateService) {
|
private route: ActivatedRoute, private location: PlatformLocation, private translate: TranslateService, private plexTv: PlexTvService) {
|
||||||
this.route.params
|
this.route.params
|
||||||
.subscribe((params: any) => {
|
.subscribe((params: any) => {
|
||||||
this.landingFlag = params.landing;
|
this.landingFlag = params.landing;
|
||||||
|
@ -78,13 +79,14 @@ export class LoginComponent implements OnDestroy, OnInit {
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.settingsService.getAuthentication().subscribe(x => this.authenticationSettings = x);
|
this.settingsService.getAuthentication().subscribe(x => this.authenticationSettings = x);
|
||||||
|
this.settingsService.getClientId().subscribe(x => this.clientId = x);
|
||||||
this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x);
|
this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x);
|
||||||
this.images.getRandomBackground().subscribe(x => {
|
this.images.getRandomBackground().subscribe(x => {
|
||||||
this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%),url(" + x.url + ")");
|
this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%),url(" + x.url + ")");
|
||||||
});
|
});
|
||||||
this.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) {
|
||||||
|
@ -101,7 +103,7 @@ export class LoginComponent implements OnDestroy, OnInit {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const value = form.value;
|
const value = form.value;
|
||||||
const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false };
|
const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false, plexTvPin: { id: 0, code: ""} };
|
||||||
this.authService.requiresPassword(user).subscribe(x => {
|
this.authService.requiresPassword(user).subscribe(x => {
|
||||||
if(x && this.authenticationSettings.allowNoPassword) {
|
if(x && this.authenticationSettings.allowNoPassword) {
|
||||||
// Looks like this user requires a password
|
// Looks like this user requires a password
|
||||||
|
@ -123,14 +125,17 @@ export class LoginComponent implements OnDestroy, OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
public oauth() {
|
public oauth() {
|
||||||
this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:""}).subscribe(x => {
|
this.plexTv.GetPin(this.clientId, this.appName).subscribe(pin => {
|
||||||
if (window.frameElement) {
|
|
||||||
// in frame
|
this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:"", plexTvPin: pin}).subscribe(x => {
|
||||||
window.open(x.url, "_blank");
|
if (window.frameElement) {
|
||||||
} else {
|
// in frame
|
||||||
// not in frame
|
window.open(x.url, "_blank");
|
||||||
window.location.href = x.url;
|
} else {
|
||||||
}
|
// not in frame
|
||||||
|
window.location.href = x.url;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,4 @@ export * from "./radarr.service";
|
||||||
export * from "./sonarr.service";
|
export * from "./sonarr.service";
|
||||||
export * from "./tester.service";
|
export * from "./tester.service";
|
||||||
export * from "./plexoauth.service";
|
export * from "./plexoauth.service";
|
||||||
|
export * from "./plextv.service";
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { Observable } from "rxjs/Rx";
|
||||||
|
|
||||||
import { ServiceHelpers } from "../service.helpers";
|
import { ServiceHelpers } from "../service.helpers";
|
||||||
|
|
||||||
import { IPlexAuthentication, IPlexLibResponse, IPlexServer, IPlexServerViewModel, IUsersModel } from "../../interfaces";
|
import { IPlexAuthentication, IPlexLibResponse, IPlexOAuthViewModel,IPlexServer, IPlexServerViewModel, IUsersModel } from "../../interfaces";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PlexService extends ServiceHelpers {
|
export class PlexService extends ServiceHelpers {
|
||||||
|
@ -30,7 +30,7 @@ export class PlexService extends ServiceHelpers {
|
||||||
return this.http.get<IUsersModel[]>(`${this.url}Friends`, {headers: this.headers});
|
return this.http.get<IUsersModel[]>(`${this.url}Friends`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
public oAuth(wizard: boolean): Observable<any> {
|
public oAuth(wizard: IPlexOAuthViewModel): Observable<any> {
|
||||||
return this.http.get<any>(`${this.url}oauth/${wizard}`, {headers: this.headers});
|
return this.http.post<any>(`${this.url}oauth`, JSON.stringify(wizard), {headers: this.headers});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { PlatformLocation } from "@angular/common";
|
||||||
|
import { HttpClient, HttpHeaders } from "@angular/common/http";
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
import { Observable } from "rxjs/Rx";
|
||||||
|
|
||||||
|
import { IPlexPin } from "../../interfaces";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PlexTvService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient, public platformLocation: PlatformLocation) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetPin(clientId: string, applicationName: string): Observable<IPlexPin> {
|
||||||
|
const headers = new HttpHeaders({"Content-Type":"application/json",
|
||||||
|
"X-Plex-Client-Identifier": clientId,
|
||||||
|
"X-Plex-Product": applicationName,
|
||||||
|
"X-Plex-Version": "3",
|
||||||
|
"X-Plex-Device": "Ombi (Web)",
|
||||||
|
"X-Plex-Platform": "Web",
|
||||||
|
"Accept": "application/json",
|
||||||
|
});
|
||||||
|
return this.http.post<IPlexPin>("https://plex.tv/api/v2/pins?strong=true", null, {headers});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -95,6 +95,10 @@ export class SettingsService extends ServiceHelpers {
|
||||||
return this.http.get<IAuthenticationSettings>(`${this.url}/Authentication`, {headers: this.headers});
|
return this.http.get<IAuthenticationSettings>(`${this.url}/Authentication`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getClientId(): Observable<string> {
|
||||||
|
return this.http.get<string>(`${this.url}/clientid`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
public saveAuthentication(settings: IAuthenticationSettings): Observable<boolean> {
|
public saveAuthentication(settings: IAuthenticationSettings): Observable<boolean> {
|
||||||
return this.http.post<boolean>(`${this.url}/Authentication`, JSON.stringify(settings), {headers: this.headers});
|
return this.http.post<boolean>(`${this.url}/Authentication`, JSON.stringify(settings), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
@ -279,4 +283,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});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
import { Component } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
import { PlexService } from "../../services";
|
import { PlexService, PlexTvService, SettingsService } from "../../services";
|
||||||
import { IdentityService, NotificationService } from "../../services";
|
import { IdentityService, NotificationService } from "../../services";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./plex.component.html",
|
templateUrl: "./plex.component.html",
|
||||||
})
|
})
|
||||||
export class PlexComponent {
|
export class PlexComponent implements OnInit {
|
||||||
|
|
||||||
public login: string;
|
public login: string;
|
||||||
public password: string;
|
public password: string;
|
||||||
|
|
||||||
|
private clientId: string;
|
||||||
|
|
||||||
constructor(private plexService: PlexService, private router: Router,
|
constructor(private plexService: PlexService, private router: Router,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
private identityService: IdentityService) { }
|
private identityService: IdentityService, private plexTv: PlexTvService,
|
||||||
|
private settingsService: SettingsService) { }
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.settingsService.getClientId().subscribe(x => this.clientId = x);
|
||||||
|
}
|
||||||
|
|
||||||
public requestAuthToken() {
|
public requestAuthToken() {
|
||||||
this.plexService.logIn(this.login, this.password).subscribe(x => {
|
this.plexService.logIn(this.login, this.password).subscribe(x => {
|
||||||
|
@ -40,10 +47,12 @@ export class PlexComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
public oauth() {
|
public oauth() {
|
||||||
this.plexService.oAuth(true).subscribe(x => {
|
this.plexTv.GetPin(this.clientId, "Ombi").subscribe(pin => {
|
||||||
if(x.url) {
|
this.plexService.oAuth({wizard: true, pin}).subscribe(x => {
|
||||||
window.location.href = x.url;
|
if(x.url) {
|
||||||
}
|
window.location.href = x.url;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
src/Ombi/Controllers/External/PlexController.cs
vendored
13
src/Ombi/Controllers/External/PlexController.cs
vendored
|
@ -12,6 +12,7 @@ using Ombi.Core.Authentication;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Core.Settings.Models.External;
|
using Ombi.Core.Settings.Models.External;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Models;
|
||||||
using Ombi.Models.External;
|
using Ombi.Models.External;
|
||||||
|
|
||||||
namespace Ombi.Controllers.External
|
namespace Ombi.Controllers.External
|
||||||
|
@ -177,25 +178,23 @@ namespace Ombi.Controllers.External
|
||||||
return vm.DistinctBy(x => x.Id);
|
return vm.DistinctBy(x => x.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("oauth/{wizard:bool}")]
|
[HttpPost("oauth")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> OAuth(bool wizard)
|
public async Task<IActionResult> OAuth([FromBody]PlexOAuthViewModel wizard)
|
||||||
{
|
{
|
||||||
//https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd
|
//https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd
|
||||||
// Plex OAuth
|
// Plex OAuth
|
||||||
// Redirect them to Plex
|
// Redirect them to Plex
|
||||||
// We need a PIN first
|
|
||||||
var pin = await _plexOAuthManager.RequestPin();
|
|
||||||
|
|
||||||
Uri url;
|
Uri url;
|
||||||
if (!wizard)
|
if (!wizard.Wizard)
|
||||||
{
|
{
|
||||||
url = await _plexOAuthManager.GetOAuthUrl(pin.id, pin.code);
|
url = await _plexOAuthManager.GetOAuthUrl(wizard.Pin.id, wizard.Pin.code);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var websiteAddress =$"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
|
var websiteAddress =$"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
|
||||||
url = _plexOAuthManager.GetWizardOAuthUrl(pin.id, pin.code, websiteAddress);
|
url = await _plexOAuthManager.GetWizardOAuthUrl(wizard.Pin.id, wizard.Pin.code, websiteAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url == null)
|
if (url == null)
|
||||||
|
|
|
@ -57,7 +57,9 @@ namespace Ombi.Controllers
|
||||||
ISettingsService<PlexSettings> settings,
|
ISettingsService<PlexSettings> settings,
|
||||||
IRepository<RequestLog> requestLog,
|
IRepository<RequestLog> requestLog,
|
||||||
IRepository<Issues> issues,
|
IRepository<Issues> issues,
|
||||||
IRepository<IssueComments> issueComments)
|
IRepository<IssueComments> issueComments,
|
||||||
|
IRepository<NotificationUserId> notificationRepository,
|
||||||
|
IRepository<RequestSubscription> subscriptionRepository)
|
||||||
{
|
{
|
||||||
UserManager = user;
|
UserManager = user;
|
||||||
Mapper = mapper;
|
Mapper = mapper;
|
||||||
|
@ -75,6 +77,8 @@ namespace Ombi.Controllers
|
||||||
_requestLogRepository = requestLog;
|
_requestLogRepository = requestLog;
|
||||||
_issueCommentsRepository = issueComments;
|
_issueCommentsRepository = issueComments;
|
||||||
OmbiSettings = ombiSettings;
|
OmbiSettings = ombiSettings;
|
||||||
|
_requestSubscriptionRepository = subscriptionRepository;
|
||||||
|
_notificationRepository = notificationRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
private OmbiUserManager UserManager { get; }
|
private OmbiUserManager UserManager { get; }
|
||||||
|
@ -93,6 +97,8 @@ namespace Ombi.Controllers
|
||||||
private readonly IRepository<Issues> _issuesRepository;
|
private readonly IRepository<Issues> _issuesRepository;
|
||||||
private readonly IRepository<IssueComments> _issueCommentsRepository;
|
private readonly IRepository<IssueComments> _issueCommentsRepository;
|
||||||
private readonly IRepository<RequestLog> _requestLogRepository;
|
private readonly IRepository<RequestLog> _requestLogRepository;
|
||||||
|
private readonly IRepository<NotificationUserId> _notificationRepository;
|
||||||
|
private readonly IRepository<RequestSubscription> _requestSubscriptionRepository;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -569,6 +575,19 @@ namespace Ombi.Controllers
|
||||||
await _issueCommentsRepository.DeleteRange(issueComments);
|
await _issueCommentsRepository.DeleteRange(issueComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete the Subscriptions and mobile notification ids
|
||||||
|
var subs = _requestSubscriptionRepository.GetAll().Where(x => x.UserId == userId);
|
||||||
|
var mobileIds = _notificationRepository.GetAll().Where(x => x.UserId == userId);
|
||||||
|
if (subs.Any())
|
||||||
|
{
|
||||||
|
await _requestSubscriptionRepository.DeleteRange(subs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mobileIds.Any())
|
||||||
|
{
|
||||||
|
await _notificationRepository.DeleteRange(mobileIds);
|
||||||
|
}
|
||||||
|
|
||||||
var result = await UserManager.DeleteAsync(userToDelete);
|
var result = await UserManager.DeleteAsync(userToDelete);
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
@ -9,7 +10,6 @@ using AutoMapper;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.PlatformAbstractions;
|
|
||||||
using NCrontab;
|
using NCrontab;
|
||||||
using Ombi.Api.Emby;
|
using Ombi.Api.Emby;
|
||||||
using Ombi.Attributes;
|
using Ombi.Attributes;
|
||||||
|
@ -39,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,
|
||||||
|
@ -109,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();
|
||||||
|
@ -137,10 +127,23 @@ namespace Ombi.Controllers
|
||||||
public async Task<PlexSettings> PlexSettings()
|
public async Task<PlexSettings> PlexSettings()
|
||||||
{
|
{
|
||||||
var s = await Get<PlexSettings>();
|
var s = await Get<PlexSettings>();
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("clientid")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<string> GetClientId()
|
||||||
|
{
|
||||||
|
var s = await Get<PlexSettings>();
|
||||||
|
if (s.InstallId == Guid.Empty)
|
||||||
|
{
|
||||||
|
s.InstallId = Guid.NewGuid();
|
||||||
|
// Save it
|
||||||
|
await PlexSettings(s);
|
||||||
|
}
|
||||||
|
return s.InstallId.ToString("N");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save the Plex settings.
|
/// Save the Plex settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -149,6 +152,10 @@ namespace Ombi.Controllers
|
||||||
[HttpPost("plex")]
|
[HttpPost("plex")]
|
||||||
public async Task<bool> PlexSettings([FromBody]PlexSettings plex)
|
public async Task<bool> PlexSettings([FromBody]PlexSettings plex)
|
||||||
{
|
{
|
||||||
|
if (plex.InstallId == null || plex.InstallId == Guid.Empty)
|
||||||
|
{
|
||||||
|
plex.InstallId = Guid.NewGuid();
|
||||||
|
}
|
||||||
var result = await Save(plex);
|
var result = await Save(plex);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -228,6 +235,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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -80,12 +80,10 @@ namespace Ombi.Controllers
|
||||||
{
|
{
|
||||||
// Plex OAuth
|
// Plex OAuth
|
||||||
// Redirect them to Plex
|
// Redirect them to Plex
|
||||||
// We need a PIN first
|
|
||||||
var pin = await _plexOAuthManager.RequestPin();
|
|
||||||
|
|
||||||
var websiteAddress = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
|
var websiteAddress = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
|
||||||
//https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd
|
//https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd
|
||||||
var url = await _plexOAuthManager.GetOAuthUrl(pin.id, pin.code, websiteAddress);
|
var url = await _plexOAuthManager.GetOAuthUrl(model.PlexTvPin.id, model.PlexTvPin.code, websiteAddress);
|
||||||
if (url == null)
|
if (url == null)
|
||||||
{
|
{
|
||||||
return new JsonResult(new
|
return new JsonResult(new
|
||||||
|
|
|
@ -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;
|
||||||
|
|
10
src/Ombi/Models/PlexOAuthViewModel.cs
Normal file
10
src/Ombi/Models/PlexOAuthViewModel.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using Ombi.Api.Plex.Models.OAuth;
|
||||||
|
|
||||||
|
namespace Ombi.Models
|
||||||
|
{
|
||||||
|
public class PlexOAuthViewModel
|
||||||
|
{
|
||||||
|
public bool Wizard { get; set; }
|
||||||
|
public OAuthPin Pin { get; set; }
|
||||||
|
}
|
||||||
|
}
|
7
src/Ombi/Models/UrlVerifyModel.cs
Normal file
7
src/Ombi/Models/UrlVerifyModel.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ombi.Models
|
||||||
|
{
|
||||||
|
public class UrlVerifyModel
|
||||||
|
{
|
||||||
|
public string Url { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Ombi.Models
|
using Ombi.Api.Plex.Models.OAuth;
|
||||||
|
|
||||||
|
namespace Ombi.Models
|
||||||
{
|
{
|
||||||
public class UserAuthModel
|
public class UserAuthModel
|
||||||
{
|
{
|
||||||
|
@ -7,5 +9,6 @@
|
||||||
public bool RememberMe { get; set; }
|
public bool RememberMe { get; set; }
|
||||||
public bool UsePlexAdminAccount { get; set; }
|
public bool UsePlexAdminAccount { get; set; }
|
||||||
public bool UsePlexOAuth { get; set; }
|
public bool UsePlexOAuth { get; set; }
|
||||||
|
public OAuthPin PlexTvPin { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project 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>
|
||||||
|
@ -67,10 +67,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" />
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -1,20 +1,13 @@
|
||||||
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.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 +27,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
|
||||||
{
|
{
|
||||||
|
@ -137,6 +129,12 @@ namespace Ombi
|
||||||
//x.UseConsole();
|
//x.UseConsole();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
|
||||||
|
{
|
||||||
|
builder.AllowAnyOrigin()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader();
|
||||||
|
}));
|
||||||
|
|
||||||
// Build the intermediate service provider
|
// Build the intermediate service provider
|
||||||
return services.BuildServiceProvider();
|
return services.BuildServiceProvider();
|
||||||
|
@ -217,14 +215,18 @@ namespace Ombi
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
|
|
||||||
app.UseMiddleware<ErrorHandlingMiddleware>();
|
app.UseMiddleware<ErrorHandlingMiddleware>();
|
||||||
|
app.UseMiddleware<ApiKeyMiddlewear>();
|
||||||
|
|
||||||
app.ApiKeyMiddlewear(app.ApplicationServices);
|
app.UseCors("MyPolicy");
|
||||||
|
//app.ApiKeyMiddlewear(app.ApplicationServices);
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI(c =>
|
app.UseSwaggerUI(c =>
|
||||||
{
|
{
|
||||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
|
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.UseMvc(routes =>
|
app.UseMvc(routes =>
|
||||||
{
|
{
|
||||||
routes.MapRoute(
|
routes.MapRoute(
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,8 +2,8 @@
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"IncludeScopes": false,
|
"IncludeScopes": false,
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Debug",
|
"Default": "Information",
|
||||||
"System": "Debug",
|
"System": "Information",
|
||||||
"Microsoft": "None",
|
"Microsoft": "None",
|
||||||
"Hangfire": "None"
|
"Hangfire": "None"
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,13 @@
|
||||||
155,
|
155,
|
||||||
13,
|
13,
|
||||||
1891,
|
1891,
|
||||||
399106
|
399106,
|
||||||
|
351286,
|
||||||
|
348350,
|
||||||
|
260513,
|
||||||
|
372058,
|
||||||
|
299536,
|
||||||
|
383498
|
||||||
],
|
],
|
||||||
"TvShows": [
|
"TvShows": [
|
||||||
121361,
|
121361,
|
||||||
|
@ -36,7 +42,10 @@
|
||||||
81189,
|
81189,
|
||||||
79126,
|
79126,
|
||||||
79349,
|
79349,
|
||||||
275274
|
275274,
|
||||||
|
305288,
|
||||||
|
296762,
|
||||||
|
280619
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
||||||
import * as webpack from "webpack";
|
import * as webpack from "webpack";
|
||||||
|
|
||||||
module.exports = (env: any) => {
|
module.exports = (env: any) => {
|
||||||
const prod = env && env.prod as boolean;
|
// const prod = env && env.prod as boolean;
|
||||||
|
const prod = true;
|
||||||
console.log(prod ? "Production" : "Dev" + " main build");
|
console.log(prod ? "Production" : "Dev" + " main build");
|
||||||
const analyse = env && env.analyse as boolean;
|
const analyse = env && env.analyse as boolean;
|
||||||
if (analyse) { console.log("Analysing build"); }
|
if (analyse) { console.log("Analysing build"); }
|
||||||
|
@ -20,6 +21,7 @@ module.exports = (env: any) => {
|
||||||
devtool: prod ? "source-map" : "eval-source-map",
|
devtool: prod ? "source-map" : "eval-source-map",
|
||||||
output: {
|
output: {
|
||||||
filename: "[name].js",
|
filename: "[name].js",
|
||||||
|
chunkFilename: "[id].[chunkhash].js",
|
||||||
publicPath: "/dist/",
|
publicPath: "/dist/",
|
||||||
path: path.join(__dirname, outputDir),
|
path: path.join(__dirname, outputDir),
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue