Merge branch 'feature/v4' of https://github.com/tidusjar/Ombi into feature/v4

pull/3528/head
Jamie Rees 4 years ago
commit 4179e557d4

@ -1,18 +0,0 @@
name: '$(Build.SourceBranchName)_$(Date:yyyy.MM.dd)$(Rev:.r)'
trigger: none
variables:
- template: templates/variables.yml
jobs:
- job: Build
pool:
vmImage: ${{ variables.vmImage }}
steps:
- template: templates/build-steps.yml
- job: Publish
pool:
vmImage: ${{ variables.vmImage }}
steps:
- template: templates/publish-stepsnew.yml

@ -18,16 +18,28 @@ stages:
matrix: matrix:
win10-x64: win10-x64:
runtime: win10-x64 runtime: win10-x64
format: zip
compression: zip
win10-x86: win10-x86:
runtime: win10-x86 runtime: win10-x86
format: zip
compression: zip
osx-x64: osx-x64:
runtime: osx-x64 runtime: osx-x64
format: tar.gz
compression: tar
linux-x64: linux-x64:
runtime: linux-x64 runtime: linux-x64
format: tar.gz
compression: tar
linux-arm: linux-arm:
runtime: linux-arm runtime: linux-arm
format: tar.gz
compression: tar
linux-arm64: linux-arm64:
runtime: linux-arm64 runtime: linux-arm64
format: tar.gz
compression: tar
pool: pool:
vmImage: ${{ variables.vmImage }} vmImage: ${{ variables.vmImage }}
steps: steps:
@ -42,6 +54,14 @@ stages:
buildType: 'current' buildType: 'current'
targetPath: '$(System.ArtifactsDirectory)' targetPath: '$(System.ArtifactsDirectory)'
- task: PowerShell@2
displayName: 'Get Release Notes'
inputs:
targetType: 'inline'
script: |
$response = Invoke-WebRequest -Uri "https://ombireleasenote.azurewebsites.net/api/ReleaseNotesFunction?buildId=$(Build.BuildId)"
Write-Host "##vso[task.setvariable variable=ReleaseNotes;]$response"
- task: GitHubRelease@1 - task: GitHubRelease@1
inputs: inputs:
gitHubConnection: 'github.com_tidusjar' gitHubConnection: 'github.com_tidusjar'
@ -54,7 +74,7 @@ stages:
releaseNotesInline: '$(ReleaseNotes)' releaseNotesInline: '$(ReleaseNotes)'
assets: | assets: |
$(System.ArtifactsDirectory)/**/*.zip $(System.ArtifactsDirectory)/**/*.zip
$(System.ArtifactsDirectory)/S**/*.gz $(System.ArtifactsDirectory)/**/*.tar.gz
isPreRelease: true isPreRelease: true
changeLogCompareToRelease: 'lastNonDraftRelease' changeLogCompareToRelease: 'lastNonDraftRelease'
changeLogType: 'commitBased' changeLogType: 'commitBased'

@ -6,28 +6,6 @@ steps:
inputs: inputs:
packageType: 'sdk' packageType: 'sdk'
version: '3.x' version: '3.x'
- task: DotNetCoreInstaller@1
displayName: 'Use .NET Core sdk for versioning'
inputs:
packageType: 'sdk'
version: '2.1.x'
- task: PowerShell@2
displayName: 'Get Release Notes'
inputs:
targetType: 'inline'
script: |
$response = Invoke-WebRequest -Uri "https://ombireleasenote.azurewebsites.net/api/ReleaseNotesFunction?buildId=$(Build.BuildId)"
Write-Host "##vso[task.setvariable variable=ReleaseNotes;]$response"
- task: PowerShell@2
displayName: 'Set Version'
inputs:
targetType: 'inline'
script: |
dotnet tool install -g dotnet-setversion
setversion -r $(BuildVersion)
- task: Yarn@3 - task: Yarn@3
displayName: 'Install UI Dependancies' displayName: 'Install UI Dependancies'

@ -1,4 +1,24 @@
steps: steps:
- task: DotNetCoreInstaller@1
displayName: 'Use .NET Core sdk '
inputs:
packageType: 'sdk'
version: '3.x'
- task: DotNetCoreInstaller@1
displayName: 'Use .NET Core sdk for versioning'
inputs:
packageType: 'sdk'
version: '2.1.x'
- task: PowerShell@2
displayName: 'Set Version'
inputs:
targetType: 'inline'
script: |
dotnet tool install -g dotnet-setversion
setversion -r $(BuildVersion)
- task: DotNetCoreCLI@2 - task: DotNetCoreCLI@2
displayName: 'publish $(runtime)' displayName: 'publish $(runtime)'
inputs: inputs:
@ -26,12 +46,12 @@ steps:
inputs: inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/$(runtime)' rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/$(runtime)'
includeRootFolder: false includeRootFolder: false
archiveType: 'zip' archiveType: $(compression)
archiveFile: '$(Build.ArtifactStagingDirectory)/$(runtime).zip' archiveFile: '$(Build.ArtifactStagingDirectory)/$(runtime).$(format)'
replaceExistingArchive: true replaceExistingArchive: true
- task: PublishPipelineArtifact@1 - task: PublishPipelineArtifact@1
inputs: inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/$(runtime).zip' targetPath: '$(Build.ArtifactStagingDirectory)/$(runtime).$(format)'
artifact: '$(runtime)' artifact: '$(runtime)'
publishLocation: 'pipeline' publishLocation: 'pipeline'

@ -1,171 +0,0 @@
steps:
- task: DotNetCoreCLI@2
displayName: Publish Win10-x64
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-c $(BuildConfiguration) -r "win10-x64" -o $(Build.ArtifactStagingDirectory)/win-64'
zipAfterPublish: false
modifyOutputPath: false
- task: CopyFiles@2
displayName: 'Copy Angular App Win10-x64'
inputs:
SourceFolder: '$(UiLocation)dist'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/win-64/ClientApp/dist'
- task: DotNetCoreCLI@2
displayName: Publish Win10-x86
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-c $(BuildConfiguration) -r "win10-x86" -o $(Build.ArtifactStagingDirectory)/win-86'
zipAfterPublish: false
modifyOutputPath: false
- task: CopyFiles@2
displayName: 'Copy Angular App Win10-x86'
inputs:
SourceFolder: '$(UiLocation)dist'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/win-86/ClientApp/dist'
- task: DotNetCoreCLI@2
displayName: Publish OSX-x64
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-c $(BuildConfiguration) -r "osx-x64" -o $(Build.ArtifactStagingDirectory)/osx-64'
zipAfterPublish: false
modifyOutputPath: false
- task: CopyFiles@2
displayName: 'Copy Angular App OSX-x64'
inputs:
SourceFolder: '$(UiLocation)dist'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/osx-64/ClientApp/dist'
- task: DotNetCoreCLI@2
displayName: Publish Linux-x64
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-c $(BuildConfiguration) -r "linux-x64" -o $(Build.ArtifactStagingDirectory)/linux-64'
zipAfterPublish: false
modifyOutputPath: false
- task: CopyFiles@2
displayName: 'Copy Angular App Linux-x64'
inputs:
SourceFolder: '$(UiLocation)dist'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/linux-64/ClientApp/dist'
- task: DotNetCoreCLI@2
displayName: Publish Linux-ARM
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-c $(BuildConfiguration) -r "linux-arm" -o $(Build.ArtifactStagingDirectory)/linux-arm'
zipAfterPublish: false
modifyOutputPath: false
- task: CopyFiles@2
displayName: 'Copy Angular App Linux-ARM'
inputs:
SourceFolder: '$(UiLocation)dist'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/linux-arm/ClientApp/dist'
- task: DotNetCoreCLI@2
displayName: Publish Linux-ARM-x64
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-c $(BuildConfiguration) -r "linux-arm64" -o $(Build.ArtifactStagingDirectory)/linux-arm64'
zipAfterPublish: false
modifyOutputPath: false
- task: CopyFiles@2
displayName: 'Copy Angular App Linux-ARM64'
inputs:
SourceFolder: '$(UiLocation)dist'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/linux-arm64/ClientApp/dist'
### Zip them up
- task: ArchiveFiles@2
displayName: Zip Win-x64
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/win-64'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/win-x64-$(Build.BuildId).zip'
replaceExistingArchive: true
- task: ArchiveFiles@2
displayName: Zip Win-x86
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/win-86'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/win-x86-$(Build.BuildId).zip'
replaceExistingArchive: true
- task: ArchiveFiles@2
displayName: Zip OSX-x64
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/osx-64'
includeRootFolder: false
archiveType: 'tar'
archiveFile: '$(Build.ArtifactStagingDirectory)/osx-x64-$(Build.BuildId).tar.gz'
replaceExistingArchive: true
- task: ArchiveFiles@2
displayName: Zip Linux-x64
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/linux-64'
includeRootFolder: false
archiveType: 'tar'
archiveFile: '$(Build.ArtifactStagingDirectory)/linux-x64-$(Build.BuildId).tar.gz'
replaceExistingArchive: true
- task: ArchiveFiles@2
displayName: Zip Linux-ARM
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/linux-arm'
includeRootFolder: false
archiveType: 'tar'
archiveFile: '$(Build.ArtifactStagingDirectory)/linux-arm-$(Build.BuildId).tar.gz'
replaceExistingArchive: true
- task: ArchiveFiles@2
displayName: Zip Linux-ARM-x64
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/linux-arm64'
includeRootFolder: false
archiveType: 'tar'
archiveFile: '$(Build.ArtifactStagingDirectory)/linux-arm64-$(Build.BuildId).tar.gz'
replaceExistingArchive: true
- task: GitHubRelease@1
inputs:
gitHubConnection: 'github.com_tidusjar'
repositoryName: 'tidusjar/Ombi.Releases'
action: 'create'
target: 'c7fcbb77b58aef1076d635a9ef99e4374abc8672'
tagSource: 'userSpecifiedTag'
tag: '$(gitTag)'
releaseNotesSource: 'inline'
releaseNotesInline: '$(ReleaseNotes)'
assets: |
$(Build.ArtifactStagingDirectory)/*.zip
$(Build.ArtifactStagingDirectory)/*.gz
isPreRelease: true
changeLogCompareToRelease: 'lastNonDraftRelease'
changeLogType: 'commitBased'
condition: and(succeeded(), eq(variables['PublishToGithub'], 'true'))

@ -25,6 +25,3 @@ variables:
- name: "BuildVersion" - name: "BuildVersion"
value: "4.0.$(Build.BuildId)" value: "4.0.$(Build.BuildId)"
- name: "ReleaseNotes"
value: ""

@ -0,0 +1,41 @@
using Ombi.Api;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using System.Threading.Tasks;
namespace Ombi.Api.Emby
{
public class EmbyApiFactory : IEmbyApiFactory
{
private readonly ISettingsService<EmbySettings> _embySettings;
private readonly IApi _api;
// TODO, if we need to derive futher, need to rework
public EmbyApiFactory(ISettingsService<EmbySettings> embySettings, IApi api)
{
_embySettings = embySettings;
_api = api;
}
public async Task<IEmbyApi> CreateClient()
{
var settings = await _embySettings.GetSettingsAsync();
return CreateClient(settings);
}
public IEmbyApi CreateClient(EmbySettings settings)
{
if (settings.IsJellyfin)
{
return new JellyfinApi(_api);
}
return new EmbyApi(_api);
}
}
public interface IEmbyApiFactory
{
Task<IEmbyApi> CreateClient();
IEmbyApi CreateClient(EmbySettings settings);
}
}

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Media.Tv;
using Ombi.Api.Emby.Models.Movie;
namespace Ombi.Api.Emby
{
public interface IBaseEmbyApi
{
Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl);
Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey);
Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri);
Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbyMovie>> GetCollection(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<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<PublicInfo> GetPublicInformation(string baseUrl);
}
}

@ -1,34 +1,10 @@
using System; using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Api.Emby.Models; using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Media.Tv;
using Ombi.Api.Emby.Models.Movie;
namespace Ombi.Api.Emby namespace Ombi.Api.Emby
{ {
public interface IEmbyApi public interface IEmbyApi : IBaseEmbyApi
{ {
Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl);
Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey);
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, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbyMovie>> GetCollection(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<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<PublicInfo> GetPublicInformation(string baseUrl);
} }
} }

@ -0,0 +1,180 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Internal;
using Newtonsoft.Json;
using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Media.Tv;
using Ombi.Api.Emby.Models.Movie;
using Ombi.Helpers;
namespace Ombi.Api.Emby
{
public class JellyfinApi : IEmbyApi
{
public JellyfinApi(IApi api)
{
Api = api;
}
private IApi Api { get; }
/// <summary>
/// Returns all users from the Emby Instance
/// </summary>
/// <param name="baseUri"></param>
/// <param name="apiKey"></param>
public async Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey)
{
var request = new Request("jellyfin/users", baseUri, HttpMethod.Get);
AddHeaders(request, apiKey);
var obj = await Api.Request<List<EmbyUser>>(request);
return obj;
}
public async Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl)
{
var request = new Request("jellyfin/System/Info", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbySystemInfo>(request);
return obj;
}
public async Task<PublicInfo> GetPublicInformation(string baseUrl)
{
var request = new Request("jellyfin/System/Info/public", baseUrl, HttpMethod.Get);
AddHeaders(request, string.Empty);
var obj = await Api.Request<PublicInfo>(request);
return obj;
}
public async Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri)
{
var request = new Request("jellyfin/users/authenticatebyname", baseUri, HttpMethod.Post);
var body = new
{
username,
pw = password,
};
request.AddJsonBody(body);
request.AddHeader("X-Emby-Authorization",
$"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\"");
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbyUser>(request);
return obj;
}
public async Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId, string apiKey, string userId, string baseUrl)
{
var request = new Request($"jellyfin/users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
request.AddQueryString("Fields", "ProviderIds,Overview");
request.AddQueryString("IsVirtualItem", "False");
return await Api.Request<EmbyItemContainer<EmbyMovie>>(request);
}
public async Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri)
{
return await GetAll<EmbyMovie>("Movie", apiKey, userId, baseUri, true, startIndex, count);
}
public async Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri)
{
return await GetAll<EmbyEpisodes>("Episode", apiKey, userId, baseUri, false, startIndex, count);
}
public async Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId, string 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)
{
return await GetInformation<SeriesInformation>(mediaId, apiKey, userId, baseUrl);
}
public async Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl)
{
return await GetInformation<MovieInformation>(mediaId, apiKey, userId, baseUrl);
}
public async Task<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl)
{
return await GetInformation<EpisodeInformation>(mediaId, apiKey, userId, baseUrl);
}
private async Task<T> GetInformation<T>(string mediaId, string apiKey, string userId, string baseUrl)
{
var request = new Request($"jellyfin/users/{userId}/items/{mediaId}", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
var response = await Api.RequestContent(request);
return JsonConvert.DeserializeObject<T>(response);
}
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview = false)
{
var request = new Request($"jellyfin/users/{userId}/items", baseUri, HttpMethod.Get);
request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
request.AddQueryString("IsVirtualItem", "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($"jellyfin/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("IsVirtualItem", "False");
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbyItemContainer<T>>(request);
return obj;
}
private static void AddHeaders(Request req, string apiKey)
{
if (!string.IsNullOrEmpty(apiKey))
{
req.AddHeader("X-MediaBrowser-Token", apiKey);
}
req.AddHeader("Accept", "application/json");
req.AddContentHeader("Content-Type", "application/json");
req.AddHeader("Device", "Ombi");
}
public Task<EmbyConnectUser> LoginConnectUser(string username, string password)
{
throw new System.NotImplementedException();
}
}
}

@ -49,7 +49,7 @@ namespace Ombi.Core.Authentication
IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators, IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators,
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer, IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<OmbiUser>> logger, IPlexApi plexApi, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<OmbiUser>> logger, IPlexApi plexApi,
IEmbyApi embyApi, ISettingsService<EmbySettings> embySettings, ISettingsService<AuthenticationSettings> auth) IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings, ISettingsService<AuthenticationSettings> auth)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{ {
_plexApi = plexApi; _plexApi = plexApi;
@ -59,7 +59,7 @@ namespace Ombi.Core.Authentication
} }
private readonly IPlexApi _plexApi; private readonly IPlexApi _plexApi;
private readonly IEmbyApi _embyApi; private readonly IEmbyApiFactory _embyApi;
private readonly ISettingsService<EmbySettings> _embySettings; private readonly ISettingsService<EmbySettings> _embySettings;
private readonly ISettingsService<AuthenticationSettings> _authSettings; private readonly ISettingsService<AuthenticationSettings> _authSettings;
@ -146,9 +146,12 @@ namespace Ombi.Core.Authentication
/// <returns></returns> /// <returns></returns>
private async Task<bool> CheckEmbyPasswordAsync(OmbiUser user, string password) private async Task<bool> CheckEmbyPasswordAsync(OmbiUser user, string password)
{ {
var embySettings = await _embySettings.GetSettingsAsync();
var client = _embyApi.CreateClient(embySettings);
if (user.IsEmbyConnect) if (user.IsEmbyConnect)
{ {
var result = await _embyApi.LoginConnectUser(user.UserName, password); var result = await client.LoginConnectUser(user.UserName, password);
if (result.AccessToken.HasValue()) if (result.AccessToken.HasValue())
{ {
// We cannot update the email address in the user importer due to there is no way // We cannot update the email address in the user importer due to there is no way
@ -165,12 +168,11 @@ namespace Ombi.Core.Authentication
} }
} }
var embySettings = await _embySettings.GetSettingsAsync();
foreach (var server in embySettings.Servers) foreach (var server in embySettings.Servers)
{ {
try try
{ {
var result = await _embyApi.LogIn(user.UserName, password, server.ApiKey, server.FullUri); var result = await client.LogIn(user.UserName, password, server.ApiKey, server.FullUri);
if (result != null) if (result != null)
{ {
return true; return true;

@ -151,6 +151,8 @@ namespace Ombi.DependencyInjection
services.AddTransient<IMusicBrainzApi, MusicBrainzApi>(); services.AddTransient<IMusicBrainzApi, MusicBrainzApi>();
services.AddTransient<IWhatsAppApi, WhatsAppApi>(); services.AddTransient<IWhatsAppApi, WhatsAppApi>();
services.AddTransient<ICloudMobileNotification, CloudMobileNotification>(); services.AddTransient<ICloudMobileNotification, CloudMobileNotification>();
services.AddTransient<IBaseEmbyApi, JellyfinApi>();
services.AddTransient<IEmbyApiFactory, EmbyApiFactory>();
} }
public static void RegisterStore(this IServiceCollection services) { public static void RegisterStore(this IServiceCollection services) {

@ -25,17 +25,18 @@ namespace Ombi.HealthChecks.Checks
using (var scope = CreateScope()) using (var scope = CreateScope())
{ {
var settingsProvider = scope.ServiceProvider.GetRequiredService<ISettingsService<EmbySettings>>(); var settingsProvider = scope.ServiceProvider.GetRequiredService<ISettingsService<EmbySettings>>();
var api = scope.ServiceProvider.GetRequiredService<IEmbyApi>(); var api = scope.ServiceProvider.GetRequiredService<IEmbyApiFactory>();
var settings = await settingsProvider.GetSettingsAsync(); var settings = await settingsProvider.GetSettingsAsync();
if (settings == null) if (settings == null)
{ {
return HealthCheckResult.Healthy("Emby is not configured."); return HealthCheckResult.Healthy("Emby is not configured.");
} }
var client = api.CreateClient(settings);
var taskResult = new List<Task<EmbySystemInfo>>(); var taskResult = new List<Task<EmbySystemInfo>>();
foreach (var server in settings.Servers) foreach (var server in settings.Servers)
{ {
taskResult.Add(api.GetSystemInformation(server.ApiKey, server.FullUri)); taskResult.Add(client.GetSystemInformation(server.ApiKey, server.FullUri));
} }
try try

@ -20,21 +20,22 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
public class EmbyContentSync : IEmbyContentSync public class EmbyContentSync : IEmbyContentSync
{ {
public EmbyContentSync(ISettingsService<EmbySettings> settings, IEmbyApi api, ILogger<EmbyContentSync> logger, public EmbyContentSync(ISettingsService<EmbySettings> settings, IEmbyApiFactory api, ILogger<EmbyContentSync> logger,
IEmbyContentRepository repo, IHubContext<NotificationHub> notification) IEmbyContentRepository repo, IHubContext<NotificationHub> notification)
{ {
_logger = logger; _logger = logger;
_settings = settings; _settings = settings;
_api = api; _apiFactory = api;
_repo = repo; _repo = repo;
_notification = notification; _notification = notification;
} }
private readonly ILogger<EmbyContentSync> _logger; private readonly ILogger<EmbyContentSync> _logger;
private readonly ISettingsService<EmbySettings> _settings; private readonly ISettingsService<EmbySettings> _settings;
private readonly IEmbyApi _api; private readonly IEmbyApiFactory _apiFactory;
private readonly IEmbyContentRepository _repo; private readonly IEmbyContentRepository _repo;
private readonly IHubContext<NotificationHub> _notification; private readonly IHubContext<NotificationHub> _notification;
private IEmbyApi Api { get; set; }
public async Task Execute(IJobExecutionContext job) public async Task Execute(IJobExecutionContext job)
{ {
@ -42,6 +43,7 @@ namespace Ombi.Schedule.Jobs.Emby
if (!embySettings.Enable) if (!embySettings.Enable)
return; return;
Api = _apiFactory.CreateClient(embySettings);
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
.SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Started"); .SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Started");
@ -76,7 +78,7 @@ namespace Ombi.Schedule.Jobs.Emby
//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 movies = await Api.GetAllMovies(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
var totalCount = movies.TotalRecordCount; var totalCount = movies.TotalRecordCount;
var processed = 1; var processed = 1;
@ -89,7 +91,7 @@ namespace Ombi.Schedule.Jobs.Emby
if (movie.Type.Equals("boxset", StringComparison.InvariantCultureIgnoreCase)) if (movie.Type.Equals("boxset", StringComparison.InvariantCultureIgnoreCase))
{ {
var movieInfo = var movieInfo =
await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri); await Api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri);
foreach (var item in movieInfo.Items) foreach (var item in movieInfo.Items)
{ {
await ProcessMovies(item, mediaToAdd, server); await ProcessMovies(item, mediaToAdd, server);
@ -106,7 +108,7 @@ namespace Ombi.Schedule.Jobs.Emby
} }
// Get the next batch // Get the next batch
movies = await _api.GetAllMovies(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); movies = await Api.GetAllMovies(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
await _repo.AddRange(mediaToAdd); await _repo.AddRange(mediaToAdd);
mediaToAdd.Clear(); mediaToAdd.Clear();
@ -114,7 +116,7 @@ namespace Ombi.Schedule.Jobs.Emby
// TV Time // TV Time
var tv = await _api.GetAllShows(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); var tv = await Api.GetAllShows(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
var totalTv = tv.TotalRecordCount; var totalTv = tv.TotalRecordCount;
processed = 1; processed = 1;
while (processed < totalTv) while (processed < totalTv)
@ -160,7 +162,7 @@ namespace Ombi.Schedule.Jobs.Emby
} }
} }
// Get the next batch // Get the next batch
tv = await _api.GetAllShows(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); tv = await Api.GetAllShows(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
await _repo.AddRange(mediaToAdd); await _repo.AddRange(mediaToAdd);
mediaToAdd.Clear(); mediaToAdd.Clear();
} }

@ -44,10 +44,10 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
public class EmbyEpisodeSync : IEmbyEpisodeSync public class EmbyEpisodeSync : IEmbyEpisodeSync
{ {
public EmbyEpisodeSync(ISettingsService<EmbySettings> s, IEmbyApi api, ILogger<EmbyEpisodeSync> l, IEmbyContentRepository repo public EmbyEpisodeSync(ISettingsService<EmbySettings> s, IEmbyApiFactory api, ILogger<EmbyEpisodeSync> l, IEmbyContentRepository repo
, IHubContext<NotificationHub> notification) , IHubContext<NotificationHub> notification)
{ {
_api = api; _apiFactory = api;
_logger = l; _logger = l;
_settings = s; _settings = s;
_repo = repo; _repo = repo;
@ -55,16 +55,18 @@ namespace Ombi.Schedule.Jobs.Emby
} }
private readonly ISettingsService<EmbySettings> _settings; private readonly ISettingsService<EmbySettings> _settings;
private readonly IEmbyApi _api; private readonly IEmbyApiFactory _apiFactory;
private readonly ILogger<EmbyEpisodeSync> _logger; private readonly ILogger<EmbyEpisodeSync> _logger;
private readonly IEmbyContentRepository _repo; private readonly IEmbyContentRepository _repo;
private readonly IHubContext<NotificationHub> _notification; private readonly IHubContext<NotificationHub> _notification;
private IEmbyApi Api { get; set; }
public async Task Execute(IJobExecutionContext job) public async Task Execute(IJobExecutionContext job)
{ {
var settings = await _settings.GetSettingsAsync(); var settings = await _settings.GetSettingsAsync();
Api = _apiFactory.CreateClient(settings);
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
.SendAsync(NotificationHub.NotificationEvent, "Emby Episode Sync Started"); .SendAsync(NotificationHub.NotificationEvent, "Emby Episode Sync Started");
foreach (var server in settings.Servers) foreach (var server in settings.Servers)
@ -80,7 +82,7 @@ namespace Ombi.Schedule.Jobs.Emby
private async Task CacheEpisodes(EmbyServers server) private async Task CacheEpisodes(EmbyServers server)
{ {
var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); var allEpisodes = await Api.GetAllEpisodes(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
var total = allEpisodes.TotalRecordCount; var total = allEpisodes.TotalRecordCount;
var processed = 1; var processed = 1;
var epToAdd = new HashSet<EmbyEpisode>(); var epToAdd = new HashSet<EmbyEpisode>();
@ -147,7 +149,7 @@ namespace Ombi.Schedule.Jobs.Emby
await _repo.AddRange(epToAdd); await _repo.AddRange(epToAdd);
epToAdd.Clear(); epToAdd.Clear();
allEpisodes = await _api.GetAllEpisodes(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); allEpisodes = await Api.GetAllEpisodes(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
} }
if (epToAdd.Any()) if (epToAdd.Any())

@ -45,10 +45,10 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
public class EmbyUserImporter : IEmbyUserImporter public class EmbyUserImporter : IEmbyUserImporter
{ {
public EmbyUserImporter(IEmbyApi api, UserManager<OmbiUser> um, ILogger<EmbyUserImporter> log, public EmbyUserImporter(IEmbyApiFactory api, UserManager<OmbiUser> um, ILogger<EmbyUserImporter> log,
ISettingsService<EmbySettings> embySettings, ISettingsService<UserManagementSettings> ums, IHubContext<NotificationHub> notification) ISettingsService<EmbySettings> embySettings, ISettingsService<UserManagementSettings> ums, IHubContext<NotificationHub> notification)
{ {
_api = api; _apiFactory = api;
_userManager = um; _userManager = um;
_log = log; _log = log;
_embySettings = embySettings; _embySettings = embySettings;
@ -56,12 +56,13 @@ namespace Ombi.Schedule.Jobs.Emby
_notification = notification; _notification = notification;
} }
private readonly IEmbyApi _api; private readonly IEmbyApiFactory _apiFactory;
private readonly UserManager<OmbiUser> _userManager; private readonly UserManager<OmbiUser> _userManager;
private readonly ILogger<EmbyUserImporter> _log; private readonly ILogger<EmbyUserImporter> _log;
private readonly ISettingsService<EmbySettings> _embySettings; private readonly ISettingsService<EmbySettings> _embySettings;
private readonly ISettingsService<UserManagementSettings> _userManagementSettings; private readonly ISettingsService<UserManagementSettings> _userManagementSettings;
private readonly IHubContext<NotificationHub> _notification; private readonly IHubContext<NotificationHub> _notification;
private IEmbyApi Api { get; set; }
public async Task Execute(IJobExecutionContext job) public async Task Execute(IJobExecutionContext job)
{ {
@ -76,6 +77,8 @@ namespace Ombi.Schedule.Jobs.Emby
return; return;
} }
Api = _apiFactory.CreateClient(settings);
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
.SendAsync(NotificationHub.NotificationEvent, "Emby User Importer Started"); .SendAsync(NotificationHub.NotificationEvent, "Emby User Importer Started");
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser).ToListAsync(); var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser).ToListAsync();
@ -86,7 +89,7 @@ namespace Ombi.Schedule.Jobs.Emby
continue; continue;
} }
var embyUsers = await _api.GetUsers(server.FullUri, server.ApiKey); var embyUsers = await Api.GetUsers(server.FullUri, server.ApiKey);
foreach (var embyUser in embyUsers) foreach (var embyUser in embyUsers)
{ {
// Check if we should import this user // Check if we should import this user

@ -138,8 +138,8 @@ namespace Ombi.Schedule.Jobs.Ombi
// Filter out the ones that we haven't sent yet // Filter out the ones that we haven't sent yet
var plexContentLocalDataset = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && x.HasTheMovieDb).ToHashSet(); var plexContentLocalDataset = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
var embyContentLocalDataset = embyContent.Where(x => x.Type == EmbyMediaType.Movie && x.HasTheMovieDb).ToHashSet(); var embyContentLocalDataset = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
var plexContentMoviesToSend = plexContentLocalDataset.Where(x => !addedPlexMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))).ToHashSet(); var plexContentMoviesToSend = plexContentLocalDataset.Where(x => !addedPlexMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))).ToHashSet();
var embyContentMoviesToSend = embyContentLocalDataset.Where(x => !addedEmbyMoviesLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))).ToHashSet(); var embyContentMoviesToSend = embyContentLocalDataset.Where(x => !addedEmbyMoviesLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))).ToHashSet();
var lidarrContentAlbumsToSend = lidarrContent.Where(x => !addedAlbumLogIds.Contains(x.ForeignAlbumId)).ToHashSet(); var lidarrContentAlbumsToSend = lidarrContent.Where(x => !addedAlbumLogIds.Contains(x.ForeignAlbumId)).ToHashSet();
@ -148,16 +148,16 @@ namespace Ombi.Schedule.Jobs.Ombi
_log.LogInformation("Albums to send: {0}", lidarrContentAlbumsToSend.Count()); _log.LogInformation("Albums to send: {0}", lidarrContentAlbumsToSend.Count());
// Find the movies that do not yet have MovieDbIds // Find the movies that do not yet have MovieDbIds
var needsMovieDbPlex = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !x.HasTheMovieDb).ToHashSet(); var needsMovieDbPlex = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
var needsMovieDbEmby = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !x.HasTheMovieDb).ToHashSet(); var needsMovieDbEmby = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
var newPlexMovies = await GetMoviesWithoutId(addedPlexMovieLogIds, needsMovieDbPlex); var newPlexMovies = await GetMoviesWithoutId(addedPlexMovieLogIds, needsMovieDbPlex);
var newEmbyMovies = await GetMoviesWithoutId(addedEmbyMoviesLogIds, needsMovieDbEmby); var newEmbyMovies = await GetMoviesWithoutId(addedEmbyMoviesLogIds, needsMovieDbEmby);
plexContentMoviesToSend = plexContentMoviesToSend.Union(newPlexMovies).ToHashSet(); plexContentMoviesToSend = plexContentMoviesToSend.Union(newPlexMovies).ToHashSet();
embyContentMoviesToSend = embyContentMoviesToSend.Union(newEmbyMovies).ToHashSet(); embyContentMoviesToSend = embyContentMoviesToSend.Union(newEmbyMovies).ToHashSet();
var plexEpisodesToSend = var plexEpisodesToSend =
FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).Where(x => x.Series.HasTvDb).AsNoTracking(), addedPlexEpisodesLogIds); FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), addedPlexEpisodesLogIds);
var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).Where(x => x.Series.HasTvDb).AsNoTracking(), var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).AsNoTracking(),
addedEmbyEpisodesLogIds); addedEmbyEpisodesLogIds);
_log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count()); _log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count());
@ -386,7 +386,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private HashSet<PlexEpisode> FilterPlexEpisodes(IEnumerable<PlexEpisode> source, IQueryable<RecentlyAddedLog> recentlyAdded) private HashSet<PlexEpisode> FilterPlexEpisodes(IEnumerable<PlexEpisode> source, IQueryable<RecentlyAddedLog> recentlyAdded)
{ {
var itemsToReturn = new HashSet<PlexEpisode>(); var itemsToReturn = new HashSet<PlexEpisode>();
foreach (var ep in source) foreach (var ep in source.Where(x => x.Series.HasTvDb))
{ {
var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId); var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId);
if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber)) if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber))
@ -403,7 +403,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private HashSet<EmbyEpisode> FilterEmbyEpisodes(IEnumerable<EmbyEpisode> source, IQueryable<RecentlyAddedLog> recentlyAdded) private HashSet<EmbyEpisode> FilterEmbyEpisodes(IEnumerable<EmbyEpisode> source, IQueryable<RecentlyAddedLog> recentlyAdded)
{ {
var itemsToReturn = new HashSet<EmbyEpisode>(); var itemsToReturn = new HashSet<EmbyEpisode>();
foreach (var ep in source) foreach (var ep in source.Where(x => x.Series.HasTvDb))
{ {
var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId); var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId);
if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber)) if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber))

@ -23,7 +23,7 @@ namespace Ombi.Schedule.Jobs.Ombi
{ {
public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo,
ILogger<RefreshMetadata> log, ITvMazeApi tvApi, ISettingsService<PlexSettings> plexSettings, ILogger<RefreshMetadata> log, ITvMazeApi tvApi, ISettingsService<PlexSettings> plexSettings,
IMovieDbApi movieApi, ISettingsService<EmbySettings> embySettings, IEmbyApi embyApi, IHubContext<NotificationHub> notification) IMovieDbApi movieApi, ISettingsService<EmbySettings> embySettings, IEmbyApiFactory embyApi, IHubContext<NotificationHub> notification)
{ {
_plexRepo = plexRepo; _plexRepo = plexRepo;
_embyRepo = embyRepo; _embyRepo = embyRepo;
@ -32,7 +32,7 @@ namespace Ombi.Schedule.Jobs.Ombi
_tvApi = tvApi; _tvApi = tvApi;
_plexSettings = plexSettings; _plexSettings = plexSettings;
_embySettings = embySettings; _embySettings = embySettings;
_embyApi = embyApi; _embyApiFactory = embyApi;
_notification = notification; _notification = notification;
} }
@ -43,8 +43,9 @@ namespace Ombi.Schedule.Jobs.Ombi
private readonly ITvMazeApi _tvApi; private readonly ITvMazeApi _tvApi;
private readonly ISettingsService<PlexSettings> _plexSettings; private readonly ISettingsService<PlexSettings> _plexSettings;
private readonly ISettingsService<EmbySettings> _embySettings; private readonly ISettingsService<EmbySettings> _embySettings;
private readonly IEmbyApi _embyApi; private readonly IEmbyApiFactory _embyApiFactory;
private readonly IHubContext<NotificationHub> _notification; private readonly IHubContext<NotificationHub> _notification;
private IEmbyApi EmbyApi { get; set; }
public async Task Execute(IJobExecutionContext job) public async Task Execute(IJobExecutionContext job)
{ {
@ -94,6 +95,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private async Task StartEmby(EmbySettings s) private async Task StartEmby(EmbySettings s)
{ {
EmbyApi = _embyApiFactory.CreateClient(s);
await StartEmbyMovies(s); await StartEmbyMovies(s);
await StartEmbyTv(); await StartEmbyTv();
} }
@ -221,7 +223,7 @@ namespace Ombi.Schedule.Jobs.Ombi
foreach (var server in settings.Servers) foreach (var server in settings.Servers)
{ {
_log.LogInformation($"Checking server {server.Name} for upto date metadata"); _log.LogInformation($"Checking server {server.Name} for upto date metadata");
var movieInfo = await _embyApi.GetMovieInformation(movie.EmbyId, server.ApiKey, server.AdministratorId, var movieInfo = await EmbyApi.GetMovieInformation(movie.EmbyId, server.ApiKey, server.AdministratorId,
server.FullUri); server.FullUri);
if (movieInfo.ProviderIds?.Imdb.HasValue() ?? false) if (movieInfo.ProviderIds?.Imdb.HasValue() ?? false)

@ -16,7 +16,7 @@ export class CookieComponent implements OnInit {
if (cookie.Auth) { if (cookie.Auth) {
const jwtVal = cookie.Auth; const jwtVal = cookie.Auth;
this.store.save("id_token", jwtVal); this.store.save("id_token", jwtVal);
this.router.navigate(["search"]); this.router.navigate(["discover"]);
} else { } else {
this.router.navigate(["login"]); this.router.navigate(["login"]);
} }

@ -3,6 +3,7 @@ import { SearchV2Service } from "../../../services";
import { ISearchMovieResult, ISearchTvResult, RequestType } from "../../../interfaces"; import { ISearchMovieResult, ISearchTvResult, RequestType } from "../../../interfaces";
import { IDiscoverCardResult, DiscoverOption } from "../../interfaces"; import { IDiscoverCardResult, DiscoverOption } from "../../interfaces";
import { trigger, transition, style, animate } from "@angular/animations"; import { trigger, transition, style, animate } from "@angular/animations";
import { StorageService } from "../../../shared/storage/storage-service";
@Component({ @Component({
templateUrl: "./discover.component.html", templateUrl: "./discover.component.html",
@ -36,22 +37,28 @@ export class DiscoverComponent implements OnInit {
private contentLoaded: number; private contentLoaded: number;
private isScrolling: boolean = false; private isScrolling: boolean = false;
private mediaTypeStorageKey = "DiscoverOptions";
constructor(private searchService: SearchV2Service) { } constructor(private searchService: SearchV2Service,
private storageService: StorageService) { }
public async ngOnInit() { public async ngOnInit() {
this.loading() this.loading()
const localDiscoverOptions = +this.storageService.get(this.mediaTypeStorageKey);
if (localDiscoverOptions) {
this.discoverOptions = DiscoverOption[DiscoverOption[localDiscoverOptions]];
}
this.scrollDisabled = true; this.scrollDisabled = true;
switch (this.discoverOptions) { switch (this.discoverOptions) {
case DiscoverOption.Combined: case DiscoverOption.Combined:
this.movies = await this.searchService.popularMoviesByPage(0,12); this.movies = await this.searchService.popularMoviesByPage(0, 12);
this.tvShows = await this.searchService.popularTvByPage(0,12); this.tvShows = await this.searchService.popularTvByPage(0, 12);
break; break;
case DiscoverOption.Movie: case DiscoverOption.Movie:
this.movies = await this.searchService.popularMoviesByPage(0,12); this.movies = await this.searchService.popularMoviesByPage(0, 12);
break; break;
case DiscoverOption.Tv: case DiscoverOption.Tv:
this.tvShows = await this.searchService.popularTvByPage(0,12); this.tvShows = await this.searchService.popularTvByPage(0, 12);
break; break;
} }
@ -200,6 +207,7 @@ export class DiscoverComponent implements OnInit {
this.loading(); this.loading();
this.clear(); this.clear();
this.discoverOptions = newMode; this.discoverOptions = newMode;
this.storageService.save(this.mediaTypeStorageKey, newMode.toString());
await this.ngOnInit(); await this.ngOnInit();
this.finishLoading(); this.finishLoading();
} }

@ -10,8 +10,8 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<table mat-table [dataSource]="dataSource" class="table" matSort matSortActive="requestedDate" <table mat-table [dataSource]="dataSource" class="table" matSort [matSortActive]="defaultSort"
matSortDisableClear matSortDirection="desc"> matSortDisableClear [matSortDirection]="defaultOrder">
<ng-container matColumnDef="requestedUser.requestedBy"> <ng-container matColumnDef="requestedUser.requestedBy">

@ -1,4 +1,4 @@
import { Component, AfterViewInit, ViewChild, EventEmitter, Output, ChangeDetectorRef } from "@angular/core"; import { Component, AfterViewInit, ViewChild, EventEmitter, Output, ChangeDetectorRef, OnInit } from "@angular/core";
import { IMovieRequests, IRequestsViewModel } from "../../../interfaces"; import { IMovieRequests, IRequestsViewModel } from "../../../interfaces";
import { MatPaginator, MatSort } from "@angular/material"; import { MatPaginator, MatSort } from "@angular/material";
import { merge, Observable, of as observableOf } from 'rxjs'; import { merge, Observable, of as observableOf } from 'rxjs';
@ -6,13 +6,14 @@ import { catchError, map, startWith, switchMap } from 'rxjs/operators';
import { RequestServiceV2 } from "../../../services/requestV2.service"; import { RequestServiceV2 } from "../../../services/requestV2.service";
import { AuthService } from "../../../auth/auth.service"; import { AuthService } from "../../../auth/auth.service";
import { StorageService } from "../../../shared/storage/storage-service";
@Component({ @Component({
templateUrl: "./movies-grid.component.html", templateUrl: "./movies-grid.component.html",
selector: "movies-grid", selector: "movies-grid",
styleUrls: ["../requests-list.component.scss"] styleUrls: ["../requests-list.component.scss"]
}) })
export class MoviesGridComponent implements AfterViewInit { export class MoviesGridComponent implements OnInit, AfterViewInit {
public dataSource: IMovieRequests[] = []; public dataSource: IMovieRequests[] = [];
public resultsLength: number; public resultsLength: number;
public isLoadingResults = true; public isLoadingResults = true;
@ -20,6 +21,11 @@ export class MoviesGridComponent implements AfterViewInit {
public gridCount: string = "15"; public gridCount: string = "15";
public showUnavailableRequests: boolean; public showUnavailableRequests: boolean;
public isAdmin: boolean; public isAdmin: boolean;
public defaultSort: string = "requestedDate";
public defaultOrder: string = "desc";
private storageKey = "Movie_DefaultRequestListSort";
private storageKeyOrder = "Movie_DefaultRequestListSortOrder";
@Output() public onOpenOptions = new EventEmitter<{ request: any, filter: any, onChange: any }>(); @Output() public onOpenOptions = new EventEmitter<{ request: any, filter: any, onChange: any }>();
@ -27,8 +33,19 @@ export class MoviesGridComponent implements AfterViewInit {
@ViewChild(MatSort, { static: false }) sort: MatSort; @ViewChild(MatSort, { static: false }) sort: MatSort;
constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef, constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef,
private auth: AuthService) { private auth: AuthService, private storageService: StorageService) {
}
public ngOnInit() {
const defaultSort = this.storageService.get(this.storageKey);
const defaultOrder = this.storageService.get(this.storageKeyOrder);
if (defaultSort) {
this.defaultSort = defaultSort;
}
if (defaultOrder) {
this.defaultOrder = defaultOrder;
}
} }
public async ngAfterViewInit() { public async ngAfterViewInit() {
@ -45,10 +62,12 @@ export class MoviesGridComponent implements AfterViewInit {
merge(this.sort.sortChange, this.paginator.page) merge(this.sort.sortChange, this.paginator.page)
.pipe( .pipe(
startWith({}), startWith({}),
switchMap(() => { switchMap((value: any) => {
this.isLoadingResults = true; this.isLoadingResults = true;
// eturn this.exampleDatabase!.getRepoIssues( if (value.active || value.direction) {
// this.sort.active, this.sort.direction, this.paginator.pageIndex); this.storageService.save(this.storageKey, value.active);
this.storageService.save(this.storageKeyOrder, value.direction);
}
return this.loadData(); return this.loadData();
}), }),
map((data: IRequestsViewModel<IMovieRequests>) => { map((data: IRequestsViewModel<IMovieRequests>) => {

@ -11,7 +11,7 @@
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<mat-tab label="Albums"> <mat-tab label="Albums">
<h1>Some more tab content</h1> <h1>Coming soon</h1>
<p>...</p> <p>...</p>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>

@ -11,8 +11,8 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<table mat-table [dataSource]="dataSource" class="table" matSort matSortActive="title" matSortDisableClear <table mat-table [dataSource]="dataSource" class="table" matSort [matSortActive]="defaultSort" matSortDisableClear
matSortDirection="desc"> [matSortDirection]="defaultOrder">
<ng-container matColumnDef="series"> <ng-container matColumnDef="series">

@ -1,4 +1,4 @@
import { Component, AfterViewInit, ViewChild, Output, EventEmitter, ChangeDetectorRef } from "@angular/core"; import { Component, AfterViewInit, ViewChild, Output, EventEmitter, ChangeDetectorRef, OnInit } from "@angular/core";
import { IRequestsViewModel, IChildRequests } from "../../../interfaces"; import { IRequestsViewModel, IChildRequests } from "../../../interfaces";
import { MatPaginator, MatSort } from "@angular/material"; import { MatPaginator, MatSort } from "@angular/material";
import { merge, of as observableOf, Observable } from 'rxjs'; import { merge, of as observableOf, Observable } from 'rxjs';
@ -6,13 +6,14 @@ import { catchError, map, startWith, switchMap } from 'rxjs/operators';
import { RequestServiceV2 } from "../../../services/requestV2.service"; import { RequestServiceV2 } from "../../../services/requestV2.service";
import { AuthService } from "../../../auth/auth.service"; import { AuthService } from "../../../auth/auth.service";
import { StorageService } from "../../../shared/storage/storage-service";
@Component({ @Component({
templateUrl: "./tv-grid.component.html", templateUrl: "./tv-grid.component.html",
selector: "tv-grid", selector: "tv-grid",
styleUrls: ["../requests-list.component.scss"] styleUrls: ["../requests-list.component.scss"]
}) })
export class TvGridComponent implements AfterViewInit { export class TvGridComponent implements OnInit, AfterViewInit {
public dataSource: IChildRequests[] = []; public dataSource: IChildRequests[] = [];
public resultsLength: number; public resultsLength: number;
public isLoadingResults = true; public isLoadingResults = true;
@ -20,6 +21,11 @@ export class TvGridComponent implements AfterViewInit {
public gridCount: string = "15"; public gridCount: string = "15";
public showUnavailableRequests: boolean; public showUnavailableRequests: boolean;
public isAdmin: boolean; public isAdmin: boolean;
public defaultSort: string = "requestedDate";
public defaultOrder: string = "desc";
private storageKey = "Tv_DefaultRequestListSort";
private storageKeyOrder = "Tv_DefaultRequestListSortOrder";
@Output() public onOpenOptions = new EventEmitter<{request: any, filter: any, onChange: any}>(); @Output() public onOpenOptions = new EventEmitter<{request: any, filter: any, onChange: any}>();
@ -27,8 +33,19 @@ export class TvGridComponent implements AfterViewInit {
@ViewChild(MatSort, {static: false}) sort: MatSort; @ViewChild(MatSort, {static: false}) sort: MatSort;
constructor(private requestService: RequestServiceV2, private auth: AuthService, constructor(private requestService: RequestServiceV2, private auth: AuthService,
private ref: ChangeDetectorRef) { private ref: ChangeDetectorRef, private storageService: StorageService) {
}
public ngOnInit() {
const defaultSort = this.storageService.get(this.storageKey);
const defaultOrder = this.storageService.get(this.storageKeyOrder);
if (defaultSort) {
this.defaultSort = defaultSort;
}
if (defaultOrder) {
this.defaultOrder = defaultOrder;
}
} }
public async ngAfterViewInit() { public async ngAfterViewInit() {
@ -40,8 +57,13 @@ export class TvGridComponent implements AfterViewInit {
merge(this.sort.sortChange, this.paginator.page) merge(this.sort.sortChange, this.paginator.page)
.pipe( .pipe(
startWith({}), startWith({}),
switchMap(() => { switchMap((value: any) => {
this.isLoadingResults = true; this.isLoadingResults = true;
if (value.active || value.direction) {
this.storageService.save(this.storageKey, value.active);
this.storageService.save(this.storageKeyOrder, value.direction);
}
return this.loadData(); return this.loadData();
}), }),
map((data: IRequestsViewModel<IChildRequests>) => { map((data: IRequestsViewModel<IChildRequests>) => {

@ -24,13 +24,13 @@ namespace Ombi.Controllers.V1.External
/// </summary> /// </summary>
/// <param name="emby"></param> /// <param name="emby"></param>
/// <param name="embySettings"></param> /// <param name="embySettings"></param>
public EmbyController(IEmbyApi emby, ISettingsService<EmbySettings> embySettings) public EmbyController(IEmbyApiFactory emby, ISettingsService<EmbySettings> embySettings)
{ {
EmbyApi = emby; EmbyApi = emby;
EmbySettings = embySettings; EmbySettings = embySettings;
} }
private IEmbyApi EmbyApi { get; } private IEmbyApiFactory EmbyApi { get; }
private ISettingsService<EmbySettings> EmbySettings { get; } private ISettingsService<EmbySettings> EmbySettings { get; }
/// <summary> /// <summary>
@ -46,10 +46,11 @@ namespace Ombi.Controllers.V1.External
var settings = await EmbySettings.GetSettingsAsync(); var settings = await EmbySettings.GetSettingsAsync();
if (settings?.Servers?.Any() ?? false) return null; if (settings?.Servers?.Any() ?? false) return null;
var client = await EmbyApi.CreateClient();
request.Enable = true; request.Enable = true;
var firstServer = request.Servers.FirstOrDefault(); var firstServer = request.Servers.FirstOrDefault();
// Test that we can connect // Test that we can connect
var result = await EmbyApi.GetUsers(firstServer.FullUri, firstServer.ApiKey); var result = await client.GetUsers(firstServer.FullUri, firstServer.ApiKey);
if (result != null && result.Any()) if (result != null && result.Any())
{ {
@ -64,7 +65,8 @@ namespace Ombi.Controllers.V1.External
[HttpPost("info")] [HttpPost("info")]
public async Task<PublicInfo> GetServerInfo([FromBody] EmbyServers server) public async Task<PublicInfo> GetServerInfo([FromBody] EmbyServers server)
{ {
var result = await EmbyApi.GetPublicInformation(server.FullUri); var client = await EmbyApi.CreateClient();
var result = await client.GetPublicInformation(server.FullUri);
return result; return result;
} }
@ -77,9 +79,10 @@ namespace Ombi.Controllers.V1.External
{ {
var vm = new List<UsersViewModel>(); var vm = new List<UsersViewModel>();
var s = await EmbySettings.GetSettingsAsync(); var s = await EmbySettings.GetSettingsAsync();
var client = EmbyApi.CreateClient(s);
foreach (var server in s?.Servers ?? new List<EmbyServers>()) foreach (var server in s?.Servers ?? new List<EmbyServers>())
{ {
var users = await EmbyApi.GetUsers(server.FullUri, server.ApiKey); var users = await client.GetUsers(server.FullUri, server.ApiKey);
if (users != null && users.Any()) if (users != null && users.Any())
{ {
vm.AddRange(users.Select(u => new UsersViewModel vm.AddRange(users.Select(u => new UsersViewModel

@ -42,7 +42,7 @@ namespace Ombi.Controllers.V1.External
/// </summary> /// </summary>
public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN, public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN,
IPushbulletNotification pushbullet, ISlackNotification slack, IPushoverNotification po, IMattermostNotification mm, IPushbulletNotification pushbullet, ISlackNotification slack, IPushoverNotification po, IMattermostNotification mm,
IPlexApi plex, IEmbyApi emby, IRadarrApi radarr, ISonarrApi sonarr, ILogger<TesterController> log, IEmailProvider provider, IPlexApi plex, IEmbyApiFactory emby, IRadarrApi radarr, ISonarrApi sonarr, ILogger<TesterController> log, IEmailProvider provider,
ICouchPotatoApi cpApi, ITelegramNotification telegram, ISickRageApi srApi, INewsletterJob newsletter, ILegacyMobileNotification mobileNotification, ICouchPotatoApi cpApi, ITelegramNotification telegram, ISickRageApi srApi, INewsletterJob newsletter, ILegacyMobileNotification mobileNotification,
ILidarrApi lidarrApi, IGotifyNotification gotifyNotification, IWhatsAppApi whatsAppApi, OmbiUserManager um, IWebhookNotification webhookNotification) ILidarrApi lidarrApi, IGotifyNotification gotifyNotification, IWhatsAppApi whatsAppApi, OmbiUserManager um, IWebhookNotification webhookNotification)
{ {
@ -82,7 +82,7 @@ namespace Ombi.Controllers.V1.External
private IMattermostNotification MattermostNotification { get; } private IMattermostNotification MattermostNotification { get; }
private IPlexApi PlexApi { get; } private IPlexApi PlexApi { get; }
private IRadarrApi RadarrApi { get; } private IRadarrApi RadarrApi { get; }
private IEmbyApi EmbyApi { get; } private IEmbyApiFactory EmbyApi { get; }
private ISonarrApi SonarrApi { get; } private ISonarrApi SonarrApi { get; }
private ICouchPotatoApi CouchPotatoApi { get; } private ICouchPotatoApi CouchPotatoApi { get; }
private ILogger<TesterController> Log { get; } private ILogger<TesterController> Log { get; }
@ -322,8 +322,8 @@ namespace Ombi.Controllers.V1.External
{ {
try try
{ {
var client = await EmbyApi.CreateClient();
var result = await EmbyApi.GetUsers(settings.FullUri, settings.ApiKey); var result = await client.GetUsers(settings.FullUri, settings.ApiKey);
return result.Any(); return result.Any();
} }
catch (Exception e) catch (Exception e)

@ -18,7 +18,7 @@ namespace Ombi.Controllers.V1
public class LandingPageController : ControllerBase public class LandingPageController : ControllerBase
{ {
public LandingPageController(ISettingsService<PlexSettings> plex, ISettingsService<EmbySettings> emby, public LandingPageController(ISettingsService<PlexSettings> plex, ISettingsService<EmbySettings> emby,
IPlexApi plexApi, IEmbyApi embyApi) IPlexApi plexApi, IEmbyApiFactory embyApi)
{ {
_plexSettings = plex; _plexSettings = plex;
_embySettings = emby; _embySettings = emby;
@ -27,7 +27,7 @@ namespace Ombi.Controllers.V1
} }
private readonly IPlexApi _plexApi; private readonly IPlexApi _plexApi;
private readonly IEmbyApi _embyApi; private readonly IEmbyApiFactory _embyApi;
private readonly ISettingsService<PlexSettings> _plexSettings; private readonly ISettingsService<PlexSettings> _plexSettings;
private readonly ISettingsService<EmbySettings> _embySettings; private readonly ISettingsService<EmbySettings> _embySettings;
@ -65,11 +65,12 @@ namespace Ombi.Controllers.V1
var emby = await _embySettings.GetSettingsAsync(); var emby = await _embySettings.GetSettingsAsync();
if (emby.Enable) if (emby.Enable)
{ {
var client = _embyApi.CreateClient(emby);
foreach (var server in emby.Servers) foreach (var server in emby.Servers)
{ {
try try
{ {
var result = await _embyApi.GetUsers(server.FullUri, server.ApiKey); var result = await client.GetUsers(server.FullUri, server.ApiKey);
if (result.Any()) if (result.Any())
{ {
model.ServersAvailable++; model.ServersAvailable++;

@ -45,7 +45,7 @@ namespace Ombi.Controllers.V1
public SettingsController(ISettingsResolver resolver, public SettingsController(ISettingsResolver resolver,
IMapper mapper, IMapper mapper,
INotificationTemplatesRepository templateRepo, INotificationTemplatesRepository templateRepo,
IEmbyApi embyApi, IEmbyApiFactory embyApi,
ICacheService memCache, ICacheService memCache,
IGithubApi githubApi, IGithubApi githubApi,
IRecentlyAddedEngine engine) IRecentlyAddedEngine engine)
@ -62,7 +62,7 @@ namespace Ombi.Controllers.V1
private ISettingsResolver SettingsResolver { get; } private ISettingsResolver SettingsResolver { get; }
private IMapper Mapper { get; } private IMapper Mapper { get; }
private INotificationTemplatesRepository TemplateRepository { get; } private INotificationTemplatesRepository TemplateRepository { get; }
private readonly IEmbyApi _embyApi; private readonly IEmbyApiFactory _embyApi;
private readonly ICacheService _cache; private readonly ICacheService _cache;
private readonly IGithubApi _githubApi; private readonly IGithubApi _githubApi;
private readonly IRecentlyAddedEngine _recentlyAdded; private readonly IRecentlyAddedEngine _recentlyAdded;
@ -212,9 +212,10 @@ namespace Ombi.Controllers.V1
{ {
if (emby.Enable) if (emby.Enable)
{ {
var client = await _embyApi.CreateClient();
foreach (var server in emby.Servers) foreach (var server in emby.Servers)
{ {
var users = await _embyApi.GetUsers(server.FullUri, server.ApiKey); var users = await client.GetUsers(server.FullUri, server.ApiKey);
var admin = users.FirstOrDefault(x => x.Policy.IsAdministrator); var admin = users.FirstOrDefault(x => x.Policy.IsAdministrator);
server.AdministratorId = admin?.Id; server.AdministratorId = admin?.Id;
} }

Loading…
Cancel
Save