mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-14 02:26:55 -07:00
Merge branch 'feature/v4' of https://github.com/tidusjar/Ombi into feature/v4
This commit is contained in:
commit
4179e557d4
30 changed files with 448 additions and 324 deletions
|
@ -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:
|
||||
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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'))
|
|
@ -24,7 +24,4 @@ variables:
|
|||
value: "$(Build.SourcesDirectory)/src/Ombi/ClientApp/"
|
||||
|
||||
- name: "BuildVersion"
|
||||
value: "4.0.$(Build.BuildId)"
|
||||
|
||||
- name: "ReleaseNotes"
|
||||
value: ""
|
||||
value: "4.0.$(Build.BuildId)"
|
41
src/Ombi.Api.Emby/EmbyApiFactory.cs
Normal file
41
src/Ombi.Api.Emby/EmbyApiFactory.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
33
src/Ombi.Api.Emby/IBaseEmbyApi.cs
Normal file
33
src/Ombi.Api.Emby/IBaseEmbyApi.cs
Normal file
|
@ -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.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<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);
|
||||
public interface IEmbyApi : IBaseEmbyApi
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
180
src/Ombi.Api.Emby/JellyfinApi.cs
Normal file
180
src/Ombi.Api.Emby/JellyfinApi.cs
Normal file
|
@ -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,
|
||||
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
|
||||
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)
|
||||
{
|
||||
_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> _embySettings;
|
||||
private readonly ISettingsService<AuthenticationSettings> _authSettings;
|
||||
|
||||
|
@ -146,9 +146,12 @@ namespace Ombi.Core.Authentication
|
|||
/// <returns></returns>
|
||||
private async Task<bool> 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;
|
||||
|
|
|
@ -151,6 +151,8 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IMusicBrainzApi, MusicBrainzApi>();
|
||||
services.AddTransient<IWhatsAppApi, WhatsAppApi>();
|
||||
services.AddTransient<ICloudMobileNotification, CloudMobileNotification>();
|
||||
services.AddTransient<IBaseEmbyApi, JellyfinApi>();
|
||||
services.AddTransient<IEmbyApiFactory, EmbyApiFactory>();
|
||||
}
|
||||
|
||||
public static void RegisterStore(this IServiceCollection services) {
|
||||
|
|
|
@ -25,17 +25,18 @@ namespace Ombi.HealthChecks.Checks
|
|||
using (var scope = CreateScope())
|
||||
{
|
||||
var settingsProvider = scope.ServiceProvider.GetRequiredService<ISettingsService<EmbySettings>>();
|
||||
var api = scope.ServiceProvider.GetRequiredService<IEmbyApi>();
|
||||
var api = scope.ServiceProvider.GetRequiredService<IEmbyApiFactory>();
|
||||
var settings = await settingsProvider.GetSettingsAsync();
|
||||
if (settings == null)
|
||||
{
|
||||
return HealthCheckResult.Healthy("Emby is not configured.");
|
||||
}
|
||||
|
||||
|
||||
var client = api.CreateClient(settings);
|
||||
var taskResult = new List<Task<EmbySystemInfo>>();
|
||||
foreach (var server in settings.Servers)
|
||||
{
|
||||
taskResult.Add(api.GetSystemInformation(server.ApiKey, server.FullUri));
|
||||
taskResult.Add(client.GetSystemInformation(server.ApiKey, server.FullUri));
|
||||
}
|
||||
|
||||
try
|
||||
|
|
|
@ -20,28 +20,30 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
{
|
||||
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)
|
||||
{
|
||||
_logger = logger;
|
||||
_settings = settings;
|
||||
_api = api;
|
||||
_apiFactory = api;
|
||||
_repo = repo;
|
||||
_notification = notification;
|
||||
}
|
||||
|
||||
private readonly ILogger<EmbyContentSync> _logger;
|
||||
private readonly ISettingsService<EmbySettings> _settings;
|
||||
private readonly IEmbyApi _api;
|
||||
private readonly IEmbyApiFactory _apiFactory;
|
||||
private readonly IEmbyContentRepository _repo;
|
||||
private readonly IHubContext<NotificationHub> _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();
|
||||
}
|
||||
|
|
|
@ -44,10 +44,10 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
{
|
||||
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)
|
||||
{
|
||||
_api = api;
|
||||
_apiFactory = api;
|
||||
_logger = l;
|
||||
_settings = s;
|
||||
_repo = repo;
|
||||
|
@ -55,16 +55,18 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
}
|
||||
|
||||
private readonly ISettingsService<EmbySettings> _settings;
|
||||
private readonly IEmbyApi _api;
|
||||
private readonly IEmbyApiFactory _apiFactory;
|
||||
private readonly ILogger<EmbyEpisodeSync> _logger;
|
||||
private readonly IEmbyContentRepository _repo;
|
||||
private readonly IHubContext<NotificationHub> _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<EmbyEpisode>();
|
||||
|
@ -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())
|
||||
|
|
|
@ -45,10 +45,10 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
{
|
||||
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)
|
||||
{
|
||||
_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<OmbiUser> _userManager;
|
||||
private readonly ILogger<EmbyUserImporter> _log;
|
||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
private readonly ISettingsService<UserManagementSettings> _userManagementSettings;
|
||||
private readonly IHubContext<NotificationHub> _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
|
||||
|
|
|
@ -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<PlexEpisode> FilterPlexEpisodes(IEnumerable<PlexEpisode> source, IQueryable<RecentlyAddedLog> recentlyAdded)
|
||||
{
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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,
|
||||
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;
|
||||
_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> _plexSettings;
|
||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
private readonly IEmbyApi _embyApi;
|
||||
private readonly IEmbyApiFactory _embyApiFactory;
|
||||
private readonly IHubContext<NotificationHub> _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)
|
||||
|
|
|
@ -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"]);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<table mat-table [dataSource]="dataSource" class="table" matSort matSortActive="requestedDate"
|
||||
matSortDisableClear matSortDirection="desc">
|
||||
<table mat-table [dataSource]="dataSource" class="table" matSort [matSortActive]="defaultSort"
|
||||
matSortDisableClear [matSortDirection]="defaultOrder">
|
||||
|
||||
|
||||
<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 { 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<IMovieRequests>) => {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</ng-template>
|
||||
</mat-tab>
|
||||
<mat-tab label="Albums">
|
||||
<h1>Some more tab content</h1>
|
||||
<h1>Coming soon</h1>
|
||||
<p>...</p>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<table mat-table [dataSource]="dataSource" class="table" matSort matSortActive="title" matSortDisableClear
|
||||
matSortDirection="desc">
|
||||
<table mat-table [dataSource]="dataSource" class="table" matSort [matSortActive]="defaultSort" matSortDisableClear
|
||||
[matSortDirection]="defaultOrder">
|
||||
|
||||
|
||||
<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 { 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,10 +33,21 @@ 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() {
|
||||
|
||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||
|
@ -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<IChildRequests>) => {
|
||||
|
|
|
@ -24,13 +24,13 @@ namespace Ombi.Controllers.V1.External
|
|||
/// </summary>
|
||||
/// <param name="emby"></param>
|
||||
/// <param name="embySettings"></param>
|
||||
public EmbyController(IEmbyApi emby, ISettingsService<EmbySettings> embySettings)
|
||||
public EmbyController(IEmbyApiFactory emby, ISettingsService<EmbySettings> embySettings)
|
||||
{
|
||||
EmbyApi = emby;
|
||||
EmbySettings = embySettings;
|
||||
}
|
||||
|
||||
private IEmbyApi EmbyApi { get; }
|
||||
private IEmbyApiFactory EmbyApi { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -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<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;
|
||||
}
|
||||
|
||||
|
@ -77,9 +79,10 @@ namespace Ombi.Controllers.V1.External
|
|||
{
|
||||
var vm = new List<UsersViewModel>();
|
||||
var s = await EmbySettings.GetSettingsAsync();
|
||||
var client = EmbyApi.CreateClient(s);
|
||||
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())
|
||||
{
|
||||
vm.AddRange(users.Select(u => new UsersViewModel
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace Ombi.Controllers.V1.External
|
|||
/// </summary>
|
||||
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<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,
|
||||
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<TesterController> 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)
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Ombi.Controllers.V1
|
|||
public class LandingPageController : ControllerBase
|
||||
{
|
||||
public LandingPageController(ISettingsService<PlexSettings> plex, ISettingsService<EmbySettings> 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> _plexSettings;
|
||||
private readonly ISettingsService<EmbySettings> _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++;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue