diff --git a/.azuredevops/pipelines/build.yml b/.azuredevops/pipelines/build.yml deleted file mode 100644 index e69de29bb..000000000 diff --git a/.azuredevops/pipelines/main.yml b/.azuredevops/pipelines/main.yml deleted file mode 100644 index 9a6f953fe..000000000 --- a/.azuredevops/pipelines/main.yml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/.azuredevops/pipelines/publish-job.yml b/.azuredevops/pipelines/publish-job.yml index 43bdcf547..3356685f5 100644 --- a/.azuredevops/pipelines/publish-job.yml +++ b/.azuredevops/pipelines/publish-job.yml @@ -18,16 +18,28 @@ stages: matrix: win10-x64: runtime: win10-x64 + format: zip + compression: zip win10-x86: runtime: win10-x86 + format: zip + compression: zip osx-x64: runtime: osx-x64 + format: tar.gz + compression: tar linux-x64: runtime: linux-x64 + format: tar.gz + compression: tar linux-arm: runtime: linux-arm + format: tar.gz + compression: tar linux-arm64: runtime: linux-arm64 + format: tar.gz + compression: tar pool: vmImage: ${{ variables.vmImage }} steps: @@ -42,6 +54,14 @@ stages: buildType: 'current' 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 inputs: gitHubConnection: 'github.com_tidusjar' @@ -54,7 +74,7 @@ stages: releaseNotesInline: '$(ReleaseNotes)' assets: | $(System.ArtifactsDirectory)/**/*.zip - $(System.ArtifactsDirectory)/S**/*.gz + $(System.ArtifactsDirectory)/**/*.tar.gz isPreRelease: true changeLogCompareToRelease: 'lastNonDraftRelease' changeLogType: 'commitBased' diff --git a/.azuredevops/pipelines/templates/build-steps.yml b/.azuredevops/pipelines/templates/build-steps.yml index 92eee9c88..719b0470b 100644 --- a/.azuredevops/pipelines/templates/build-steps.yml +++ b/.azuredevops/pipelines/templates/build-steps.yml @@ -6,28 +6,6 @@ steps: 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: '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 displayName: 'Install UI Dependancies' diff --git a/.azuredevops/pipelines/templates/publish-os-steps.yml b/.azuredevops/pipelines/templates/publish-os-steps.yml index b0e4a45a8..ad72212cf 100644 --- a/.azuredevops/pipelines/templates/publish-os-steps.yml +++ b/.azuredevops/pipelines/templates/publish-os-steps.yml @@ -1,4 +1,24 @@ 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 displayName: 'publish $(runtime)' inputs: @@ -26,12 +46,12 @@ steps: inputs: rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/$(runtime)' includeRootFolder: false - archiveType: 'zip' - archiveFile: '$(Build.ArtifactStagingDirectory)/$(runtime).zip' + archiveType: $(compression) + archiveFile: '$(Build.ArtifactStagingDirectory)/$(runtime).$(format)' replaceExistingArchive: true - task: PublishPipelineArtifact@1 inputs: - targetPath: '$(Build.ArtifactStagingDirectory)/$(runtime).zip' + targetPath: '$(Build.ArtifactStagingDirectory)/$(runtime).$(format)' artifact: '$(runtime)' publishLocation: 'pipeline' diff --git a/.azuredevops/pipelines/templates/publish-steps.yml b/.azuredevops/pipelines/templates/publish-steps.yml deleted file mode 100644 index 80ec2ce5c..000000000 --- a/.azuredevops/pipelines/templates/publish-steps.yml +++ /dev/null @@ -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')) \ No newline at end of file diff --git a/.azuredevops/pipelines/templates/variables.yml b/.azuredevops/pipelines/templates/variables.yml index 2a42b65f3..737f7b9d1 100644 --- a/.azuredevops/pipelines/templates/variables.yml +++ b/.azuredevops/pipelines/templates/variables.yml @@ -24,7 +24,4 @@ variables: value: "$(Build.SourcesDirectory)/src/Ombi/ClientApp/" - name: "BuildVersion" - value: "4.0.$(Build.BuildId)" - - - name: "ReleaseNotes" - value: "" \ No newline at end of file + value: "4.0.$(Build.BuildId)" \ No newline at end of file diff --git a/src/Ombi.Api.Emby/EmbyApiFactory.cs b/src/Ombi.Api.Emby/EmbyApiFactory.cs new file mode 100644 index 000000000..c5c6e1c02 --- /dev/null +++ b/src/Ombi.Api.Emby/EmbyApiFactory.cs @@ -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; + private readonly IApi _api; + + // TODO, if we need to derive futher, need to rework + public EmbyApiFactory(ISettingsService embySettings, IApi api) + { + _embySettings = embySettings; + _api = api; + } + + public async Task 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 CreateClient(); + IEmbyApi CreateClient(EmbySettings settings); + } +} diff --git a/src/Ombi.Api.Emby/IBaseEmbyApi.cs b/src/Ombi.Api.Emby/IBaseEmbyApi.cs new file mode 100644 index 000000000..6f9a6bc7f --- /dev/null +++ b/src/Ombi.Api.Emby/IBaseEmbyApi.cs @@ -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 GetSystemInformation(string apiKey, string baseUrl); + Task> GetUsers(string baseUri, string apiKey); + Task LogIn(string username, string password, string apiKey, string baseUri); + + Task> GetAllMovies(string apiKey, int startIndex, int count, string userId, + string baseUri); + + Task> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, + string baseUri); + + Task> GetAllShows(string apiKey, int startIndex, int count, string userId, + string baseUri); + + Task> GetCollection(string mediaId, + string apiKey, string userId, string baseUrl); + + Task GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl); + Task GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl); + Task GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl); + Task GetPublicInformation(string baseUrl); + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Emby/IEmbyApi.cs b/src/Ombi.Api.Emby/IEmbyApi.cs index 3c29878b7..e7803116d 100644 --- a/src/Ombi.Api.Emby/IEmbyApi.cs +++ b/src/Ombi.Api.Emby/IEmbyApi.cs @@ -1,34 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; +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 IEmbyApi - { - Task GetSystemInformation(string apiKey, string baseUrl); - Task> GetUsers(string baseUri, string apiKey); - Task LogIn(string username, string password, string apiKey, string baseUri); + public interface IEmbyApi : IBaseEmbyApi + { Task LoginConnectUser(string username, string password); - - Task> GetAllMovies(string apiKey, int startIndex, int count, string userId, - string baseUri); - - Task> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, - string baseUri); - - Task> GetAllShows(string apiKey, int startIndex, int count, string userId, - string baseUri); - - Task> GetCollection(string mediaId, - string apiKey, string userId, string baseUrl); - - Task GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl); - Task GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl); - Task GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl); - Task GetPublicInformation(string baseUrl); } } \ No newline at end of file diff --git a/src/Ombi.Api.Emby/JellyfinApi.cs b/src/Ombi.Api.Emby/JellyfinApi.cs new file mode 100644 index 000000000..3fbec3806 --- /dev/null +++ b/src/Ombi.Api.Emby/JellyfinApi.cs @@ -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; } + + /// + /// Returns all users from the Emby Instance + /// + /// + /// + public async Task> GetUsers(string baseUri, string apiKey) + { + var request = new Request("jellyfin/users", baseUri, HttpMethod.Get); + + AddHeaders(request, apiKey); + var obj = await Api.Request>(request); + + return obj; + } + + public async Task GetSystemInformation(string apiKey, string baseUrl) + { + var request = new Request("jellyfin/System/Info", baseUrl, HttpMethod.Get); + + AddHeaders(request, apiKey); + + var obj = await Api.Request(request); + + return obj; + } + + public async Task GetPublicInformation(string baseUrl) + { + var request = new Request("jellyfin/System/Info/public", baseUrl, HttpMethod.Get); + + AddHeaders(request, string.Empty); + + var obj = await Api.Request(request); + + return obj; + } + + public async Task 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(request); + return obj; + } + + public async Task> 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>(request); + } + + public async Task> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri) + { + return await GetAll("Movie", apiKey, userId, baseUri, true, startIndex, count); + } + + public async Task> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri) + { + return await GetAll("Episode", apiKey, userId, baseUri, false, startIndex, count); + } + + public async Task> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri) + { + return await GetAll("Series", apiKey, userId, baseUri, false, startIndex, count); + } + + public async Task GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl) + { + return await GetInformation(mediaId, apiKey, userId, baseUrl); + } + public async Task GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl) + { + return await GetInformation(mediaId, apiKey, userId, baseUrl); + } + + public async Task GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl) + { + return await GetInformation(mediaId, apiKey, userId, baseUrl); + } + + private async Task GetInformation(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(response); + } + + private async Task> GetAll(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>(request); + return obj; + } + private async Task> GetAll(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>(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 LoginConnectUser(string username, string password) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/src/Ombi.Core/Authentication/OmbiUserManager.cs b/src/Ombi.Core/Authentication/OmbiUserManager.cs index 2c78f39bf..135196051 100644 --- a/src/Ombi.Core/Authentication/OmbiUserManager.cs +++ b/src/Ombi.Core/Authentication/OmbiUserManager.cs @@ -49,7 +49,7 @@ namespace Ombi.Core.Authentication IPasswordHasher passwordHasher, IEnumerable> userValidators, IEnumerable> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger> logger, IPlexApi plexApi, - IEmbyApi embyApi, ISettingsService embySettings, ISettingsService auth) + IEmbyApiFactory embyApi, ISettingsService embySettings, ISettingsService auth) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) { _plexApi = plexApi; @@ -59,7 +59,7 @@ namespace Ombi.Core.Authentication } private readonly IPlexApi _plexApi; - private readonly IEmbyApi _embyApi; + private readonly IEmbyApiFactory _embyApi; private readonly ISettingsService _embySettings; private readonly ISettingsService _authSettings; @@ -146,9 +146,12 @@ namespace Ombi.Core.Authentication /// private async Task CheckEmbyPasswordAsync(OmbiUser user, string password) { + var embySettings = await _embySettings.GetSettingsAsync(); + var client = _embyApi.CreateClient(embySettings); + if (user.IsEmbyConnect) { - var result = await _embyApi.LoginConnectUser(user.UserName, password); + var result = await client.LoginConnectUser(user.UserName, password); if (result.AccessToken.HasValue()) { // 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) { 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) { return true; diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 22129f287..76f2f42bc 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -151,6 +151,8 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); } public static void RegisterStore(this IServiceCollection services) { diff --git a/src/Ombi.HealthChecks/Checks/EmbyHealthCheck.cs b/src/Ombi.HealthChecks/Checks/EmbyHealthCheck.cs index d601cdeef..45c227033 100644 --- a/src/Ombi.HealthChecks/Checks/EmbyHealthCheck.cs +++ b/src/Ombi.HealthChecks/Checks/EmbyHealthCheck.cs @@ -25,17 +25,18 @@ namespace Ombi.HealthChecks.Checks using (var scope = CreateScope()) { var settingsProvider = scope.ServiceProvider.GetRequiredService>(); - var api = scope.ServiceProvider.GetRequiredService(); + var api = scope.ServiceProvider.GetRequiredService(); var settings = await settingsProvider.GetSettingsAsync(); if (settings == null) { return HealthCheckResult.Healthy("Emby is not configured."); } - + + var client = api.CreateClient(settings); var taskResult = new List>(); foreach (var server in settings.Servers) { - taskResult.Add(api.GetSystemInformation(server.ApiKey, server.FullUri)); + taskResult.Add(client.GetSystemInformation(server.ApiKey, server.FullUri)); } try diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index 0968ccbb4..8fa02d5ac 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -20,28 +20,30 @@ namespace Ombi.Schedule.Jobs.Emby { public class EmbyContentSync : IEmbyContentSync { - public EmbyContentSync(ISettingsService settings, IEmbyApi api, ILogger logger, + public EmbyContentSync(ISettingsService settings, IEmbyApiFactory api, ILogger logger, IEmbyContentRepository repo, IHubContext notification) { _logger = logger; _settings = settings; - _api = api; + _apiFactory = api; _repo = repo; _notification = notification; } private readonly ILogger _logger; private readonly ISettingsService _settings; - private readonly IEmbyApi _api; + private readonly IEmbyApiFactory _apiFactory; private readonly IEmbyContentRepository _repo; private readonly IHubContext _notification; + private IEmbyApi Api { get; set; } public async Task Execute(IJobExecutionContext job) { var embySettings = await _settings.GetSettingsAsync(); if (!embySettings.Enable) return; - + + Api = _apiFactory.CreateClient(embySettings); await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) .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 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 processed = 1; @@ -89,7 +91,7 @@ namespace Ombi.Schedule.Jobs.Emby if (movie.Type.Equals("boxset", StringComparison.InvariantCultureIgnoreCase)) { 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) { await ProcessMovies(item, mediaToAdd, server); @@ -106,7 +108,7 @@ namespace Ombi.Schedule.Jobs.Emby } // 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); mediaToAdd.Clear(); @@ -114,7 +116,7 @@ namespace Ombi.Schedule.Jobs.Emby // 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; processed = 1; while (processed < totalTv) @@ -160,7 +162,7 @@ namespace Ombi.Schedule.Jobs.Emby } } // 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); mediaToAdd.Clear(); } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index 292c9ed13..6dda89979 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -44,10 +44,10 @@ namespace Ombi.Schedule.Jobs.Emby { public class EmbyEpisodeSync : IEmbyEpisodeSync { - public EmbyEpisodeSync(ISettingsService s, IEmbyApi api, ILogger l, IEmbyContentRepository repo + public EmbyEpisodeSync(ISettingsService s, IEmbyApiFactory api, ILogger l, IEmbyContentRepository repo , IHubContext notification) { - _api = api; + _apiFactory = api; _logger = l; _settings = s; _repo = repo; @@ -55,16 +55,18 @@ namespace Ombi.Schedule.Jobs.Emby } private readonly ISettingsService _settings; - private readonly IEmbyApi _api; + private readonly IEmbyApiFactory _apiFactory; private readonly ILogger _logger; private readonly IEmbyContentRepository _repo; private readonly IHubContext _notification; + private IEmbyApi Api { get; set; } public async Task Execute(IJobExecutionContext job) { var settings = await _settings.GetSettingsAsync(); - + + Api = _apiFactory.CreateClient(settings); await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) .SendAsync(NotificationHub.NotificationEvent, "Emby Episode Sync Started"); foreach (var server in settings.Servers) @@ -80,7 +82,7 @@ namespace Ombi.Schedule.Jobs.Emby 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 processed = 1; var epToAdd = new HashSet(); @@ -147,7 +149,7 @@ namespace Ombi.Schedule.Jobs.Emby await _repo.AddRange(epToAdd); 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()) diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs index cd0bd0b8e..c5f8ad862 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs @@ -45,10 +45,10 @@ namespace Ombi.Schedule.Jobs.Emby { public class EmbyUserImporter : IEmbyUserImporter { - public EmbyUserImporter(IEmbyApi api, UserManager um, ILogger log, + public EmbyUserImporter(IEmbyApiFactory api, UserManager um, ILogger log, ISettingsService embySettings, ISettingsService ums, IHubContext notification) { - _api = api; + _apiFactory = api; _userManager = um; _log = log; _embySettings = embySettings; @@ -56,12 +56,13 @@ namespace Ombi.Schedule.Jobs.Emby _notification = notification; } - private readonly IEmbyApi _api; + private readonly IEmbyApiFactory _apiFactory; private readonly UserManager _userManager; private readonly ILogger _log; private readonly ISettingsService _embySettings; private readonly ISettingsService _userManagementSettings; private readonly IHubContext _notification; + private IEmbyApi Api { get; set; } public async Task Execute(IJobExecutionContext job) { @@ -76,6 +77,8 @@ namespace Ombi.Schedule.Jobs.Emby return; } + Api = _apiFactory.CreateClient(settings); + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) .SendAsync(NotificationHub.NotificationEvent, "Emby User Importer Started"); var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser).ToListAsync(); @@ -86,7 +89,7 @@ namespace Ombi.Schedule.Jobs.Emby continue; } - var embyUsers = await _api.GetUsers(server.FullUri, server.ApiKey); + var embyUsers = await Api.GetUsers(server.FullUri, server.ApiKey); foreach (var embyUser in embyUsers) { // Check if we should import this user diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 60d7b5d70..cafcbb570 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -138,8 +138,8 @@ namespace Ombi.Schedule.Jobs.Ombi // Filter out the ones that we haven't sent yet - var plexContentLocalDataset = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && x.HasTheMovieDb).ToHashSet(); - var embyContentLocalDataset = embyContent.Where(x => x.Type == EmbyMediaType.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 && !string.IsNullOrEmpty(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 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()); // Find the movies that do not yet have MovieDbIds - var needsMovieDbPlex = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !x.HasTheMovieDb).ToHashSet(); - var needsMovieDbEmby = embyContent.Where(x => x.Type == EmbyMediaType.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 && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet(); var newPlexMovies = await GetMoviesWithoutId(addedPlexMovieLogIds, needsMovieDbPlex); var newEmbyMovies = await GetMoviesWithoutId(addedEmbyMoviesLogIds, needsMovieDbEmby); plexContentMoviesToSend = plexContentMoviesToSend.Union(newPlexMovies).ToHashSet(); embyContentMoviesToSend = embyContentMoviesToSend.Union(newEmbyMovies).ToHashSet(); var plexEpisodesToSend = - FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).Where(x => x.Series.HasTvDb).AsNoTracking(), addedPlexEpisodesLogIds); - var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).Where(x => x.Series.HasTvDb).AsNoTracking(), + FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), addedPlexEpisodesLogIds); + var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), addedEmbyEpisodesLogIds); _log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count()); @@ -386,7 +386,7 @@ namespace Ombi.Schedule.Jobs.Ombi private HashSet FilterPlexEpisodes(IEnumerable source, IQueryable recentlyAdded) { var itemsToReturn = new HashSet(); - foreach (var ep in source) + foreach (var ep in source.Where(x => x.Series.HasTvDb)) { var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId); 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 FilterEmbyEpisodes(IEnumerable source, IQueryable recentlyAdded) { var itemsToReturn = new HashSet(); - foreach (var ep in source) + foreach (var ep in source.Where(x => x.Series.HasTvDb)) { var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId); if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber)) diff --git a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs index a24914e99..55d09894e 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs @@ -23,7 +23,7 @@ namespace Ombi.Schedule.Jobs.Ombi { public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, ILogger log, ITvMazeApi tvApi, ISettingsService plexSettings, - IMovieDbApi movieApi, ISettingsService embySettings, IEmbyApi embyApi, IHubContext notification) + IMovieDbApi movieApi, ISettingsService embySettings, IEmbyApiFactory embyApi, IHubContext notification) { _plexRepo = plexRepo; _embyRepo = embyRepo; @@ -32,7 +32,7 @@ namespace Ombi.Schedule.Jobs.Ombi _tvApi = tvApi; _plexSettings = plexSettings; _embySettings = embySettings; - _embyApi = embyApi; + _embyApiFactory = embyApi; _notification = notification; } @@ -43,8 +43,9 @@ namespace Ombi.Schedule.Jobs.Ombi private readonly ITvMazeApi _tvApi; private readonly ISettingsService _plexSettings; private readonly ISettingsService _embySettings; - private readonly IEmbyApi _embyApi; + private readonly IEmbyApiFactory _embyApiFactory; private readonly IHubContext _notification; + private IEmbyApi EmbyApi { get; set; } public async Task Execute(IJobExecutionContext job) { @@ -94,6 +95,7 @@ namespace Ombi.Schedule.Jobs.Ombi private async Task StartEmby(EmbySettings s) { + EmbyApi = _embyApiFactory.CreateClient(s); await StartEmbyMovies(s); await StartEmbyTv(); } @@ -221,7 +223,7 @@ namespace Ombi.Schedule.Jobs.Ombi foreach (var server in settings.Servers) { _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); if (movieInfo.ProviderIds?.Imdb.HasValue() ?? false) diff --git a/src/Ombi/ClientApp/src/app/auth/cookie.component.ts b/src/Ombi/ClientApp/src/app/auth/cookie.component.ts index 515f8c603..1a6cf9144 100644 --- a/src/Ombi/ClientApp/src/app/auth/cookie.component.ts +++ b/src/Ombi/ClientApp/src/app/auth/cookie.component.ts @@ -16,7 +16,7 @@ export class CookieComponent implements OnInit { if (cookie.Auth) { const jwtVal = cookie.Auth; this.store.save("id_token", jwtVal); - this.router.navigate(["search"]); + this.router.navigate(["discover"]); } else { this.router.navigate(["login"]); } diff --git a/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.ts b/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.ts index 47695073b..3238ea5ed 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.ts @@ -3,6 +3,7 @@ import { SearchV2Service } from "../../../services"; import { ISearchMovieResult, ISearchTvResult, RequestType } from "../../../interfaces"; import { IDiscoverCardResult, DiscoverOption } from "../../interfaces"; import { trigger, transition, style, animate } from "@angular/animations"; +import { StorageService } from "../../../shared/storage/storage-service"; @Component({ templateUrl: "./discover.component.html", @@ -21,7 +22,7 @@ export class DiscoverComponent implements OnInit { public discoverResults: IDiscoverCardResult[] = []; public movies: ISearchMovieResult[] = []; public tvShows: ISearchTvResult[] = []; - + public discoverOptions: DiscoverOption = DiscoverOption.Combined; public DiscoverOption = DiscoverOption; @@ -36,22 +37,28 @@ export class DiscoverComponent implements OnInit { private contentLoaded: number; private isScrolling: boolean = false; + private mediaTypeStorageKey = "DiscoverOptions"; - constructor(private searchService: SearchV2Service) { } + constructor(private searchService: SearchV2Service, + private storageService: StorageService) { } public async ngOnInit() { this.loading() + const localDiscoverOptions = +this.storageService.get(this.mediaTypeStorageKey); + if (localDiscoverOptions) { + this.discoverOptions = DiscoverOption[DiscoverOption[localDiscoverOptions]]; + } this.scrollDisabled = true; switch (this.discoverOptions) { case DiscoverOption.Combined: - this.movies = await this.searchService.popularMoviesByPage(0,12); - this.tvShows = await this.searchService.popularTvByPage(0,12); + this.movies = await this.searchService.popularMoviesByPage(0, 12); + this.tvShows = await this.searchService.popularTvByPage(0, 12); break; case DiscoverOption.Movie: - this.movies = await this.searchService.popularMoviesByPage(0,12); + this.movies = await this.searchService.popularMoviesByPage(0, 12); break; case DiscoverOption.Tv: - this.tvShows = await this.searchService.popularTvByPage(0,12); + this.tvShows = await this.searchService.popularTvByPage(0, 12); break; } @@ -108,7 +115,7 @@ export class DiscoverComponent implements OnInit { case DiscoverOption.Tv: this.tvShows = await this.searchService.anticipatedTvByPage(this.contentLoaded, 12); break; - } + } } this.contentLoaded += 12; @@ -199,7 +206,8 @@ export class DiscoverComponent implements OnInit { public async switchDiscoverMode(newMode: DiscoverOption) { this.loading(); this.clear(); - this.discoverOptions = newMode; + this.discoverOptions = newMode; + this.storageService.save(this.mediaTypeStorageKey, newMode.toString()); await this.ngOnInit(); this.finishLoading(); } diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html index 7c91e9900..228ae9cdb 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html +++ b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html @@ -10,8 +10,8 @@ - +
diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts index 513558fa0..4443cd4ed 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts @@ -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 { MatPaginator, MatSort } from "@angular/material"; 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 { AuthService } from "../../../auth/auth.service"; +import { StorageService } from "../../../shared/storage/storage-service"; @Component({ templateUrl: "./movies-grid.component.html", selector: "movies-grid", styleUrls: ["../requests-list.component.scss"] }) -export class MoviesGridComponent implements AfterViewInit { +export class MoviesGridComponent implements OnInit, AfterViewInit { public dataSource: IMovieRequests[] = []; public resultsLength: number; public isLoadingResults = true; @@ -20,6 +21,11 @@ export class MoviesGridComponent implements AfterViewInit { public gridCount: string = "15"; public showUnavailableRequests: 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 }>(); @@ -27,9 +33,20 @@ export class MoviesGridComponent implements AfterViewInit { @ViewChild(MatSort, { static: false }) sort: MatSort; 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() { // const results = await this.requestService.getMovieRequests(this.gridCount, 0, OrderType.RequestedDateDesc, @@ -45,10 +62,12 @@ export class MoviesGridComponent implements AfterViewInit { merge(this.sort.sortChange, this.paginator.page) .pipe( startWith({}), - switchMap(() => { + switchMap((value: any) => { this.isLoadingResults = true; - // eturn this.exampleDatabase!.getRepoIssues( - // this.sort.active, this.sort.direction, this.paginator.pageIndex); + if (value.active || value.direction) { + this.storageService.save(this.storageKey, value.active); + this.storageService.save(this.storageKeyOrder, value.direction); + } return this.loadData(); }), map((data: IRequestsViewModel) => { diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.html index 381865c0c..a5b9364fe 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.html +++ b/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.html @@ -11,7 +11,7 @@ -

Some more tab content

+

Coming soon

...

diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.html index 343b9f255..2b9cec0d6 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.html +++ b/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.html @@ -11,8 +11,8 @@ -
+
diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.ts index 78dd23c1a..3e13a5ce2 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.ts @@ -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 { MatPaginator, MatSort } from "@angular/material"; 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 { AuthService } from "../../../auth/auth.service"; +import { StorageService } from "../../../shared/storage/storage-service"; @Component({ templateUrl: "./tv-grid.component.html", selector: "tv-grid", styleUrls: ["../requests-list.component.scss"] }) -export class TvGridComponent implements AfterViewInit { +export class TvGridComponent implements OnInit, AfterViewInit { public dataSource: IChildRequests[] = []; public resultsLength: number; public isLoadingResults = true; @@ -20,6 +21,11 @@ export class TvGridComponent implements AfterViewInit { public gridCount: string = "15"; public showUnavailableRequests: 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}>(); @@ -27,8 +33,19 @@ export class TvGridComponent implements AfterViewInit { @ViewChild(MatSort, {static: false}) sort: MatSort; 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() { @@ -40,8 +57,13 @@ export class TvGridComponent implements AfterViewInit { merge(this.sort.sortChange, this.paginator.page) .pipe( startWith({}), - switchMap(() => { + switchMap((value: any) => { 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(); }), map((data: IRequestsViewModel) => { diff --git a/src/Ombi/Controllers/V1/External/EmbyController.cs b/src/Ombi/Controllers/V1/External/EmbyController.cs index d8ef5c158..4b201fe7c 100644 --- a/src/Ombi/Controllers/V1/External/EmbyController.cs +++ b/src/Ombi/Controllers/V1/External/EmbyController.cs @@ -24,13 +24,13 @@ namespace Ombi.Controllers.V1.External /// /// /// - public EmbyController(IEmbyApi emby, ISettingsService embySettings) + public EmbyController(IEmbyApiFactory emby, ISettingsService embySettings) { EmbyApi = emby; EmbySettings = embySettings; } - private IEmbyApi EmbyApi { get; } + private IEmbyApiFactory EmbyApi { get; } private ISettingsService EmbySettings { get; } /// @@ -46,10 +46,11 @@ namespace Ombi.Controllers.V1.External var settings = await EmbySettings.GetSettingsAsync(); if (settings?.Servers?.Any() ?? false) return null; + var client = await EmbyApi.CreateClient(); request.Enable = true; var firstServer = request.Servers.FirstOrDefault(); // 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()) { @@ -64,7 +65,8 @@ namespace Ombi.Controllers.V1.External [HttpPost("info")] public async Task 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; } @@ -77,9 +79,10 @@ namespace Ombi.Controllers.V1.External { var vm = new List(); var s = await EmbySettings.GetSettingsAsync(); + var client = EmbyApi.CreateClient(s); foreach (var server in s?.Servers ?? new List()) { - var users = await EmbyApi.GetUsers(server.FullUri, server.ApiKey); + var users = await client.GetUsers(server.FullUri, server.ApiKey); if (users != null && users.Any()) { vm.AddRange(users.Select(u => new UsersViewModel diff --git a/src/Ombi/Controllers/V1/External/TesterController.cs b/src/Ombi/Controllers/V1/External/TesterController.cs index d66fa29e9..0dbaac688 100644 --- a/src/Ombi/Controllers/V1/External/TesterController.cs +++ b/src/Ombi/Controllers/V1/External/TesterController.cs @@ -42,7 +42,7 @@ namespace Ombi.Controllers.V1.External /// public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN, IPushbulletNotification pushbullet, ISlackNotification slack, IPushoverNotification po, IMattermostNotification mm, - IPlexApi plex, IEmbyApi emby, IRadarrApi radarr, ISonarrApi sonarr, ILogger log, IEmailProvider provider, + IPlexApi plex, IEmbyApiFactory emby, IRadarrApi radarr, ISonarrApi sonarr, ILogger log, IEmailProvider provider, ICouchPotatoApi cpApi, ITelegramNotification telegram, ISickRageApi srApi, INewsletterJob newsletter, ILegacyMobileNotification mobileNotification, ILidarrApi lidarrApi, IGotifyNotification gotifyNotification, IWhatsAppApi whatsAppApi, OmbiUserManager um, IWebhookNotification webhookNotification) { @@ -82,7 +82,7 @@ namespace Ombi.Controllers.V1.External private IMattermostNotification MattermostNotification { get; } private IPlexApi PlexApi { get; } private IRadarrApi RadarrApi { get; } - private IEmbyApi EmbyApi { get; } + private IEmbyApiFactory EmbyApi { get; } private ISonarrApi SonarrApi { get; } private ICouchPotatoApi CouchPotatoApi { get; } private ILogger Log { get; } @@ -322,8 +322,8 @@ namespace Ombi.Controllers.V1.External { try { - - var result = await EmbyApi.GetUsers(settings.FullUri, settings.ApiKey); + var client = await EmbyApi.CreateClient(); + var result = await client.GetUsers(settings.FullUri, settings.ApiKey); return result.Any(); } catch (Exception e) diff --git a/src/Ombi/Controllers/V1/LandingPageController.cs b/src/Ombi/Controllers/V1/LandingPageController.cs index 48d10d727..54e1638b4 100644 --- a/src/Ombi/Controllers/V1/LandingPageController.cs +++ b/src/Ombi/Controllers/V1/LandingPageController.cs @@ -18,7 +18,7 @@ namespace Ombi.Controllers.V1 public class LandingPageController : ControllerBase { public LandingPageController(ISettingsService plex, ISettingsService emby, - IPlexApi plexApi, IEmbyApi embyApi) + IPlexApi plexApi, IEmbyApiFactory embyApi) { _plexSettings = plex; _embySettings = emby; @@ -27,7 +27,7 @@ namespace Ombi.Controllers.V1 } private readonly IPlexApi _plexApi; - private readonly IEmbyApi _embyApi; + private readonly IEmbyApiFactory _embyApi; private readonly ISettingsService _plexSettings; private readonly ISettingsService _embySettings; @@ -65,11 +65,12 @@ namespace Ombi.Controllers.V1 var emby = await _embySettings.GetSettingsAsync(); if (emby.Enable) { + var client = _embyApi.CreateClient(emby); foreach (var server in emby.Servers) { try { - var result = await _embyApi.GetUsers(server.FullUri, server.ApiKey); + var result = await client.GetUsers(server.FullUri, server.ApiKey); if (result.Any()) { model.ServersAvailable++; diff --git a/src/Ombi/Controllers/V1/SettingsController.cs b/src/Ombi/Controllers/V1/SettingsController.cs index 200fc59d9..8a0e52018 100644 --- a/src/Ombi/Controllers/V1/SettingsController.cs +++ b/src/Ombi/Controllers/V1/SettingsController.cs @@ -45,7 +45,7 @@ namespace Ombi.Controllers.V1 public SettingsController(ISettingsResolver resolver, IMapper mapper, INotificationTemplatesRepository templateRepo, - IEmbyApi embyApi, + IEmbyApiFactory embyApi, ICacheService memCache, IGithubApi githubApi, IRecentlyAddedEngine engine) @@ -62,7 +62,7 @@ namespace Ombi.Controllers.V1 private ISettingsResolver SettingsResolver { get; } private IMapper Mapper { get; } private INotificationTemplatesRepository TemplateRepository { get; } - private readonly IEmbyApi _embyApi; + private readonly IEmbyApiFactory _embyApi; private readonly ICacheService _cache; private readonly IGithubApi _githubApi; private readonly IRecentlyAddedEngine _recentlyAdded; @@ -212,9 +212,10 @@ namespace Ombi.Controllers.V1 { if (emby.Enable) { + var client = await _embyApi.CreateClient(); 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); server.AdministratorId = admin?.Id; }