Merge remote-tracking branch 'upstream/master' into integration-tests

This commit is contained in:
Mark Monteiro 2020-04-23 18:54:13 -04:00
commit cd98938190
94 changed files with 1086 additions and 1504 deletions

View file

@ -1,13 +1,13 @@
parameters: parameters:
- name: Packages - name: Packages
type: object type: object
default: {} default: {}
- name: LinuxImage - name: LinuxImage
type: string type: string
default: "ubuntu-latest" default: "ubuntu-latest"
- name: DotNetSdkVersion - name: DotNetSdkVersion
type: string type: string
default: 3.1.100 default: 3.1.100
jobs: jobs:
- job: CompatibilityCheck - job: CompatibilityCheck

View file

@ -20,41 +20,34 @@ jobs:
submodules: true submodules: true
persistCredentials: true persistCredentials: true
- task: CmdLine@2 - task: DownloadPipelineArtifact@2
displayName: "Clone Web Branch" displayName: "Download Web Branch"
condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) condition: in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')
inputs: inputs:
script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web" path: '$(Agent.TempDirectory)'
artifact: 'jellyfin-web-production'
source: 'specific'
project: 'jellyfin'
pipeline: 'Jellyfin Web'
runBranch: variables['Build.SourceBranch']
- task: CmdLine@2 - task: DownloadPipelineArtifact@2
displayName: "Clone Web Target" displayName: "Download Web Target"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest')) condition: eq(variables['Build.Reason'], 'PullRequest')
inputs: inputs:
script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web" path: '$(Agent.TempDirectory)'
artifact: 'jellyfin-web-production'
source: 'specific'
project: 'jellyfin'
pipeline: 'Jellyfin Web'
runBranch: variables['System.PullRequest.TargetBranch']
- task: NodeTool@0 - task: ExtractFiles@1
displayName: "Install Node" displayName: "Extract Web Client"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs: inputs:
versionSpec: "12.x" archiveFilePatterns: '$(Agent.TempDirectory)/*.zip'
destinationFolder: '$(Build.SourcesDirectory)/MediaBrowser.WebDashboard'
- task: CmdLine@2 cleanDestinationFolder: false
displayName: "Build Web Client"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
script: yarn install
workingDirectory: $(Agent.TempDirectory)/jellyfin-web
- task: CopyFiles@2
displayName: "Copy Web Client"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist
contents: "**"
targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
cleanTargetFolder: true
overWrite: true
flattenFolders: false
- task: UseDotNet@2 - task: UseDotNet@2
displayName: "Update DotNet" displayName: "Update DotNet"

View file

@ -1,26 +1,25 @@
parameters: parameters:
- name: ImageNames - name: ImageNames
type: object type: object
default: default:
Linux: "ubuntu-latest" Linux: "ubuntu-latest"
Windows: "windows-latest" Windows: "windows-latest"
macOS: "macos-latest" macOS: "macos-latest"
- name: TestProjects - name: TestProjects
type: string type: string
default: "tests/**/*Tests.csproj" default: "tests/**/*Tests.csproj"
- name: DotNetSdkVersion - name: DotNetSdkVersion
type: string type: string
default: 3.1.100 default: 3.1.100
jobs: jobs:
- job: MainTest - job: Test
displayName: Main Test displayName: Test
strategy: strategy:
matrix: matrix:
${{ each imageName in parameters.ImageNames }}: ${{ each imageName in parameters.ImageNames }}:
${{ imageName.key }}: ${{ imageName.key }}:
ImageName: ${{ imageName.value }} ImageName: ${{ imageName.value }}
maxParallel: 3
pool: pool:
vmImage: "$(ImageName)" vmImage: "$(ImageName)"
steps: steps:
@ -29,14 +28,30 @@ jobs:
submodules: true submodules: true
persistCredentials: false persistCredentials: false
# This is required for the SonarCloud analyzer
- task: UseDotNet@2
displayName: "Install .NET Core SDK 2.1"
condition: eq(variables['ImageName'], 'ubuntu-latest')
inputs:
packageType: sdk
version: '2.1.805'
- task: UseDotNet@2 - task: UseDotNet@2
displayName: "Update DotNet" displayName: "Update DotNet"
inputs: inputs:
packageType: sdk packageType: sdk
version: ${{ parameters.DotNetSdkVersion }} version: ${{ parameters.DotNetSdkVersion }}
- task: SonarCloudPrepare@1
displayName: 'Prepare analysis on SonarCloud'
condition: eq(variables['ImageName'], 'ubuntu-latest')
inputs:
SonarCloud: 'Sonarcloud for Jellyfin'
organization: 'jellyfin'
projectKey: 'jellyfin_jellyfin'
- task: DotNetCoreCLI@2 - task: DotNetCoreCLI@2
displayName: Run .NET Core CLI tests displayName: 'Run CLI Tests'
inputs: inputs:
command: "test" command: "test"
projects: ${{ parameters.TestProjects }} projects: ${{ parameters.TestProjects }}
@ -45,9 +60,17 @@ jobs:
testRunTitle: $(Agent.JobName) testRunTitle: $(Agent.JobName)
workingDirectory: "$(Build.SourcesDirectory)" workingDirectory: "$(Build.SourcesDirectory)"
- task: SonarCloudAnalyze@1
displayName: 'Run Code Analysis'
condition: eq(variables['ImageName'], 'ubuntu-latest')
- task: SonarCloudPublish@1
displayName: 'Publish Quality Gate Result'
condition: eq(variables['ImageName'], 'ubuntu-latest')
- task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4 - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
displayName: ReportGenerator (merge) displayName: 'Run ReportGenerator'
inputs: inputs:
reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml" reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"
targetdir: "$(Agent.TempDirectory)/merged/" targetdir: "$(Agent.TempDirectory)/merged/"
@ -56,10 +79,11 @@ jobs:
## V2 is already in the repository but it does not work "wrong number of segments" YAML error. ## V2 is already in the repository but it does not work "wrong number of segments" YAML error.
- task: PublishCodeCoverageResults@1 - task: PublishCodeCoverageResults@1
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
displayName: Publish Code Coverage displayName: 'Publish Code Coverage'
inputs: inputs:
codeCoverageTool: "cobertura" codeCoverageTool: "cobertura"
#summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2 #summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2
summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml" summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml"
pathToSources: $(Build.SourcesDirectory) pathToSources: $(Build.SourcesDirectory)
failIfCoverageEmpty: true failIfCoverageEmpty: true

View file

@ -1,12 +1,12 @@
name: $(Date:yyyyMMdd)$(Rev:.r) name: $(Date:yyyyMMdd)$(Rev:.r)
variables: variables:
- name: TestProjects - name: TestProjects
value: "tests/**/*Tests.csproj" value: "tests/**/*Tests.csproj"
- name: RestoreBuildProjects - name: RestoreBuildProjects
value: "Jellyfin.Server/Jellyfin.Server.csproj" value: "Jellyfin.Server/Jellyfin.Server.csproj"
- name: DotNetSdkVersion - name: DotNetSdkVersion
value: 3.1.100 value: 3.1.100
pr: pr:
autoCancel: true autoCancel: true

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{713F42B5-878E-499D-A878-E4C652B1D5E8}</ProjectGuid>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\SharedVersion.cs" /> <Compile Include="..\SharedVersion.cs" />
</ItemGroup> </ItemGroup>

View file

@ -1,3 +1,4 @@
#nullable enable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{805844AB-E92F-45E6-9D99-4F6D48D129A5}</ProjectGuid>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\SharedVersion.cs" /> <Compile Include="..\SharedVersion.cs" />
</ItemGroup> </ItemGroup>

View file

@ -1,10 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{08FFF49B-F175-4807-A2B5-73B0EBD9F716}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -8,7 +8,6 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -33,8 +32,7 @@ namespace Emby.Drawing
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths; private readonly IServerApplicationPaths _appPaths;
private readonly IImageEncoder _imageEncoder; private readonly IImageEncoder _imageEncoder;
private readonly Func<ILibraryManager> _libraryManager; private readonly IMediaEncoder _mediaEncoder;
private readonly Func<IMediaEncoder> _mediaEncoder;
private bool _disposed = false; private bool _disposed = false;
@ -45,20 +43,17 @@ namespace Emby.Drawing
/// <param name="appPaths">The server application paths.</param> /// <param name="appPaths">The server application paths.</param>
/// <param name="fileSystem">The filesystem.</param> /// <param name="fileSystem">The filesystem.</param>
/// <param name="imageEncoder">The image encoder.</param> /// <param name="imageEncoder">The image encoder.</param>
/// <param name="libraryManager">The library manager.</param>
/// <param name="mediaEncoder">The media encoder.</param> /// <param name="mediaEncoder">The media encoder.</param>
public ImageProcessor( public ImageProcessor(
ILogger<ImageProcessor> logger, ILogger<ImageProcessor> logger,
IServerApplicationPaths appPaths, IServerApplicationPaths appPaths,
IFileSystem fileSystem, IFileSystem fileSystem,
IImageEncoder imageEncoder, IImageEncoder imageEncoder,
Func<ILibraryManager> libraryManager, IMediaEncoder mediaEncoder)
Func<IMediaEncoder> mediaEncoder)
{ {
_logger = logger; _logger = logger;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_imageEncoder = imageEncoder; _imageEncoder = imageEncoder;
_libraryManager = libraryManager;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_appPaths = appPaths; _appPaths = appPaths;
} }
@ -121,26 +116,9 @@ namespace Emby.Drawing
/// <inheritdoc /> /// <inheritdoc />
public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options) public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
{ {
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
var libraryManager = _libraryManager();
ItemImageInfo originalImage = options.Image; ItemImageInfo originalImage = options.Image;
BaseItem item = options.Item; BaseItem item = options.Item;
if (!originalImage.IsLocalFile)
{
if (item == null)
{
item = libraryManager.GetItemById(options.ItemId);
}
originalImage = await libraryManager.ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false);
}
string originalImagePath = originalImage.Path; string originalImagePath = originalImage.Path;
DateTime dateModified = originalImage.DateModified; DateTime dateModified = originalImage.DateModified;
ImageDimensions? originalImageSize = null; ImageDimensions? originalImageSize = null;
@ -312,10 +290,6 @@ namespace Emby.Drawing
/// <inheritdoc /> /// <inheritdoc />
public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info) public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info)
=> GetImageDimensions(item, info, true);
/// <inheritdoc />
public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem)
{ {
int width = info.Width; int width = info.Width;
int height = info.Height; int height = info.Height;
@ -332,11 +306,6 @@ namespace Emby.Drawing
info.Width = size.Width; info.Width = size.Width;
info.Height = size.Height; info.Height = size.Height;
if (updateItem)
{
_libraryManager().UpdateImages(item);
}
return size; return size;
} }
@ -351,19 +320,12 @@ namespace Emby.Drawing
/// <inheritdoc /> /// <inheritdoc />
public string GetImageCacheTag(BaseItem item, ChapterInfo chapter) public string GetImageCacheTag(BaseItem item, ChapterInfo chapter)
{ {
try return GetImageCacheTag(item, new ItemImageInfo
{ {
return GetImageCacheTag(item, new ItemImageInfo Path = chapter.ImagePath,
{ Type = ImageType.Chapter,
Path = chapter.ImagePath, DateModified = chapter.ImageDateModified
Type = ImageType.Chapter, });
DateModified = chapter.ImageDateModified
});
}
catch
{
return null;
}
} }
private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
@ -384,13 +346,13 @@ namespace Emby.Drawing
{ {
string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture); string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture);
string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png"; string cacheExtension = _mediaEncoder.SupportsEncoder("libwebp") ? ".webp" : ".png";
var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension); var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);
var file = _fileSystem.GetFileInfo(outputPath); var file = _fileSystem.GetFileInfo(outputPath);
if (!file.Exists) if (!file.Exists)
{ {
await _mediaEncoder().ConvertImage(originalImagePath, outputPath).ConfigureAwait(false); await _mediaEncoder.ConvertImage(originalImagePath, outputPath).ConfigureAwait(false);
dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath); dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath);
} }
else else

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{2E030C33-6923-4530-9E54-FA29FA6AD1A9}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View file

@ -1,4 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{89AB4548-770D-41FD-A891-8DAFF44F452C}</ProjectGuid>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />

View file

@ -160,7 +160,7 @@ namespace Emby.Photos
try try
{ {
var size = _imageProcessor.GetImageDimensions(item, img, false); var size = _imageProcessor.GetImageDimensions(item, img);
if (size.Width > 0 && size.Height > 0) if (size.Width > 0 && size.Height > 0)
{ {

View file

@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.Activity
}); });
} }
private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, PackageVersionInfo)> e) private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, VersionInfo)> e)
{ {
CreateLogEntry(new ActivityLogEntry CreateLogEntry(new ActivityLogEntry
{ {
@ -434,8 +434,8 @@ namespace Emby.Server.Implementations.Activity
ShortOverview = string.Format( ShortOverview = string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"), _localization.GetLocalizedString("VersionNumber"),
e.Argument.Item2.versionStr), e.Argument.Item2.version),
Overview = e.Argument.Item2.description Overview = e.Argument.Item2.changelog
}); });
} }
@ -451,7 +451,7 @@ namespace Emby.Server.Implementations.Activity
}); });
} }
private void OnPluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e) private void OnPluginInstalled(object sender, GenericEventArgs<VersionInfo> e)
{ {
CreateLogEntry(new ActivityLogEntry CreateLogEntry(new ActivityLogEntry
{ {
@ -463,7 +463,7 @@ namespace Emby.Server.Implementations.Activity
ShortOverview = string.Format( ShortOverview = string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"), _localization.GetLocalizedString("VersionNumber"),
e.Argument.versionStr) e.Argument.version)
}); });
} }

View file

@ -86,7 +86,6 @@ using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
@ -104,7 +103,6 @@ using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers; using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
@ -121,14 +119,20 @@ namespace Emby.Server.Implementations
/// </summary> /// </summary>
private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" }; private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" };
private SqliteUserRepository _userRepository; private readonly IFileSystem _fileSystemManager;
private SqliteDisplayPreferencesRepository _displayPreferencesRepository; private readonly INetworkManager _networkManager;
private readonly IXmlSerializer _xmlSerializer;
private readonly IStartupOptions _startupOptions;
private IMediaEncoder _mediaEncoder;
private ISessionManager _sessionManager;
private IHttpServer _httpServer;
private IHttpClient _httpClient;
/// <summary> /// <summary>
/// Gets a value indicating whether this instance can self restart. /// Gets a value indicating whether this instance can self restart.
/// </summary> /// </summary>
/// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value> public bool CanSelfRestart => _startupOptions.RestartPath != null;
public abstract bool CanSelfRestart { get; }
public virtual bool CanLaunchWebBrowser public virtual bool CanLaunchWebBrowser
{ {
@ -139,7 +143,7 @@ namespace Emby.Server.Implementations
return false; return false;
} }
if (StartupOptions.IsService) if (_startupOptions.IsService)
{ {
return false; return false;
} }
@ -209,21 +213,6 @@ namespace Emby.Server.Implementations
/// <value>The configuration manager.</value> /// <value>The configuration manager.</value>
protected IConfigurationManager ConfigurationManager { get; set; } protected IConfigurationManager ConfigurationManager { get; set; }
public IFileSystem FileSystemManager { get; set; }
/// <inheritdoc />
public PackageVersionClass SystemUpdateLevel
{
get
{
#if BETA
return PackageVersionClass.Beta;
#else
return PackageVersionClass.Release;
#endif
}
}
/// <summary> /// <summary>
/// Gets or sets the service provider. /// Gets or sets the service provider.
/// </summary> /// </summary>
@ -245,110 +234,6 @@ namespace Emby.Server.Implementations
/// <value>The server configuration manager.</value> /// <value>The server configuration manager.</value>
public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager; public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager;
/// <summary>
/// Gets or sets the user manager.
/// </summary>
/// <value>The user manager.</value>
public IUserManager UserManager { get; set; }
/// <summary>
/// Gets or sets the library manager.
/// </summary>
/// <value>The library manager.</value>
internal ILibraryManager LibraryManager { get; set; }
/// <summary>
/// Gets or sets the directory watchers.
/// </summary>
/// <value>The directory watchers.</value>
private ILibraryMonitor LibraryMonitor { get; set; }
/// <summary>
/// Gets or sets the provider manager.
/// </summary>
/// <value>The provider manager.</value>
private IProviderManager ProviderManager { get; set; }
/// <summary>
/// Gets or sets the HTTP server.
/// </summary>
/// <value>The HTTP server.</value>
private IHttpServer HttpServer { get; set; }
private IDtoService DtoService { get; set; }
public IImageProcessor ImageProcessor { get; set; }
/// <summary>
/// Gets or sets the media encoder.
/// </summary>
/// <value>The media encoder.</value>
private IMediaEncoder MediaEncoder { get; set; }
private ISubtitleEncoder SubtitleEncoder { get; set; }
private ISessionManager SessionManager { get; set; }
private ILiveTvManager LiveTvManager { get; set; }
public LocalizationManager LocalizationManager { get; set; }
private IEncodingManager EncodingManager { get; set; }
private IChannelManager ChannelManager { get; set; }
/// <summary>
/// Gets or sets the user data repository.
/// </summary>
/// <value>The user data repository.</value>
private IUserDataManager UserDataManager { get; set; }
internal SqliteItemRepository ItemRepository { get; set; }
private INotificationManager NotificationManager { get; set; }
private ISubtitleManager SubtitleManager { get; set; }
private IChapterManager ChapterManager { get; set; }
private IDeviceManager DeviceManager { get; set; }
internal IUserViewManager UserViewManager { get; set; }
private IAuthenticationRepository AuthenticationRepository { get; set; }
private ITVSeriesManager TVSeriesManager { get; set; }
private ICollectionManager CollectionManager { get; set; }
private IMediaSourceManager MediaSourceManager { get; set; }
/// <summary>
/// Gets the installation manager.
/// </summary>
/// <value>The installation manager.</value>
protected IInstallationManager InstallationManager { get; private set; }
protected IAuthService AuthService { get; private set; }
public IStartupOptions StartupOptions { get; }
internal IImageEncoder ImageEncoder { get; private set; }
protected readonly IXmlSerializer XmlSerializer;
protected ISocketFactory SocketFactory { get; private set; }
protected ITaskManager TaskManager { get; private set; }
public IHttpClient HttpClient { get; private set; }
protected INetworkManager NetworkManager { get; set; }
public IJsonSerializer JsonSerializer { get; private set; }
protected IIsoManager IsoManager { get; private set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ApplicationHost" /> class. /// Initializes a new instance of the <see cref="ApplicationHost" /> class.
/// </summary> /// </summary>
@ -357,29 +242,33 @@ namespace Emby.Server.Implementations
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
IStartupOptions options, IStartupOptions options,
IFileSystem fileSystem, IFileSystem fileSystem,
IImageEncoder imageEncoder,
INetworkManager networkManager) INetworkManager networkManager)
{ {
XmlSerializer = new MyXmlSerializer(); _xmlSerializer = new MyXmlSerializer();
NetworkManager = networkManager; _networkManager = networkManager;
networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
ApplicationPaths = applicationPaths; ApplicationPaths = applicationPaths;
LoggerFactory = loggerFactory; LoggerFactory = loggerFactory;
FileSystemManager = fileSystem; _fileSystemManager = fileSystem;
ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, XmlSerializer, FileSystemManager); ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager);
Logger = LoggerFactory.CreateLogger("App"); Logger = LoggerFactory.CreateLogger<ApplicationHost>();
StartupOptions = options; _startupOptions = options;
ImageEncoder = imageEncoder;
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
NetworkManager.NetworkChanged += OnNetworkChanged; _networkManager.NetworkChanged += OnNetworkChanged;
CertificateInfo = new CertificateInfo
{
Path = ServerConfigurationManager.Configuration.CertificatePath,
Password = ServerConfigurationManager.Configuration.CertificatePassword
};
Certificate = GetCertificate(CertificateInfo);
} }
public string ExpandVirtualPath(string path) public string ExpandVirtualPath(string path)
@ -447,10 +336,7 @@ namespace Emby.Server.Implementations
} }
} }
/// <summary> /// <inheritdoc/>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
public string Name => ApplicationProductName; public string Name => ApplicationProductName;
/// <summary> /// <summary>
@ -540,7 +426,7 @@ namespace Emby.Server.Implementations
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated; ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
MediaEncoder.SetFFmpegPath(); _mediaEncoder.SetFFmpegPath();
Logger.LogInformation("ServerId: {0}", SystemId); Logger.LogInformation("ServerId: {0}", SystemId);
@ -552,7 +438,7 @@ namespace Emby.Server.Implementations
Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed); Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed);
Logger.LogInformation("Core startup complete"); Logger.LogInformation("Core startup complete");
HttpServer.GlobalResponse = null; _httpServer.GlobalResponse = null;
stopWatch.Restart(); stopWatch.Restart();
await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false);
@ -576,7 +462,7 @@ namespace Emby.Server.Implementations
} }
/// <inheritdoc/> /// <inheritdoc/>
public async Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig) public void Init(IServiceCollection serviceCollection)
{ {
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@ -588,8 +474,6 @@ namespace Emby.Server.Implementations
HttpsPort = ServerConfiguration.DefaultHttpsPort; HttpsPort = ServerConfiguration.DefaultHttpsPort;
} }
JsonSerializer = new JsonSerializer();
if (Plugins != null) if (Plugins != null)
{ {
var pluginBuilder = new StringBuilder(); var pluginBuilder = new StringBuilder();
@ -609,7 +493,7 @@ namespace Emby.Server.Implementations
DiscoverTypes(); DiscoverTypes();
await RegisterServices(serviceCollection, startupConfig).ConfigureAwait(false); RegisterServices(serviceCollection);
} }
public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next) public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
@ -620,7 +504,7 @@ namespace Emby.Server.Implementations
return; return;
} }
await HttpServer.ProcessWebSocketRequest(context).ConfigureAwait(false); await _httpServer.ProcessWebSocketRequest(context).ConfigureAwait(false);
} }
public async Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next) public async Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
@ -636,14 +520,16 @@ namespace Emby.Server.Implementations
var localPath = context.Request.Path.ToString(); var localPath = context.Request.Path.ToString();
var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger<WebSocketSharpRequest>()); var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger<WebSocketSharpRequest>());
await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false); await _httpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
} }
/// <summary> /// <summary>
/// Registers services/resources with the service collection that will be available via DI. /// Registers services/resources with the service collection that will be available via DI.
/// </summary> /// </summary>
protected async Task RegisterServices(IServiceCollection serviceCollection, IConfiguration startupConfig) protected virtual void RegisterServices(IServiceCollection serviceCollection)
{ {
serviceCollection.AddSingleton(_startupOptions);
serviceCollection.AddMemoryCache(); serviceCollection.AddMemoryCache();
serviceCollection.AddSingleton(ConfigurationManager); serviceCollection.AddSingleton(ConfigurationManager);
@ -651,240 +537,169 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths); serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
serviceCollection.AddSingleton(JsonSerializer); serviceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
// TODO: Support for injecting ILogger should be deprecated in favour of ILogger<T> and this removed // TODO: Remove support for injecting ILogger completely
serviceCollection.AddSingleton<ILogger>(Logger); serviceCollection.AddSingleton((provider) =>
{
Logger.LogWarning("Injecting ILogger directly is deprecated and should be replaced with ILogger<T>");
return Logger;
});
serviceCollection.AddSingleton(FileSystemManager); serviceCollection.AddSingleton(_fileSystemManager);
serviceCollection.AddSingleton<TvdbClientManager>(); serviceCollection.AddSingleton<TvdbClientManager>();
HttpClient = new HttpClientManager.HttpClientManager( serviceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
ApplicationPaths,
LoggerFactory.CreateLogger<HttpClientManager.HttpClientManager>(),
FileSystemManager,
() => ApplicationUserAgent);
serviceCollection.AddSingleton(HttpClient);
serviceCollection.AddSingleton(NetworkManager); serviceCollection.AddSingleton(_networkManager);
IsoManager = new IsoManager(); serviceCollection.AddSingleton<IIsoManager, IsoManager>();
serviceCollection.AddSingleton(IsoManager);
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LoggerFactory, FileSystemManager); serviceCollection.AddSingleton<ITaskManager, TaskManager>();
serviceCollection.AddSingleton(TaskManager);
serviceCollection.AddSingleton(XmlSerializer); serviceCollection.AddSingleton(_xmlSerializer);
serviceCollection.AddSingleton(typeof(IStreamHelper), typeof(StreamHelper)); serviceCollection.AddSingleton<IStreamHelper, StreamHelper>();
var cryptoProvider = new CryptographyProvider(); serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
serviceCollection.AddSingleton<ICryptoProvider>(cryptoProvider);
SocketFactory = new SocketFactory(); serviceCollection.AddSingleton<ISocketFactory, SocketFactory>();
serviceCollection.AddSingleton(SocketFactory);
serviceCollection.AddSingleton(typeof(IInstallationManager), typeof(InstallationManager)); serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
serviceCollection.AddSingleton(typeof(IZipClient), typeof(ZipClient)); serviceCollection.AddSingleton<IZipClient, ZipClient>();
serviceCollection.AddSingleton(typeof(IHttpResultFactory), typeof(HttpResultFactory)); serviceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
serviceCollection.AddSingleton<IServerApplicationHost>(this); serviceCollection.AddSingleton<IServerApplicationHost>(this);
serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths); serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
serviceCollection.AddSingleton(ServerConfigurationManager); serviceCollection.AddSingleton(ServerConfigurationManager);
LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory.CreateLogger<LocalizationManager>()); serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
await LocalizationManager.LoadAll().ConfigureAwait(false);
serviceCollection.AddSingleton<ILocalizationManager>(LocalizationManager);
serviceCollection.AddSingleton<IBlurayExaminer>(new BdInfoExaminer(FileSystemManager)); serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager); serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
serviceCollection.AddSingleton(UserDataManager); serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
_displayPreferencesRepository = new SqliteDisplayPreferencesRepository( serviceCollection.AddSingleton<IDisplayPreferencesRepository, SqliteDisplayPreferencesRepository>();
LoggerFactory.CreateLogger<SqliteDisplayPreferencesRepository>(),
ApplicationPaths,
FileSystemManager);
serviceCollection.AddSingleton<IDisplayPreferencesRepository>(_displayPreferencesRepository);
ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, LoggerFactory.CreateLogger<SqliteItemRepository>(), LocalizationManager); serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
serviceCollection.AddSingleton<IItemRepository>(ItemRepository);
AuthenticationRepository = GetAuthenticationRepository(); serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
serviceCollection.AddSingleton(AuthenticationRepository);
_userRepository = GetUserRepository(); serviceCollection.AddSingleton<IUserRepository, SqliteUserRepository>();
UserManager = new UserManager( // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
LoggerFactory.CreateLogger<UserManager>(), serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
_userRepository, serviceCollection.AddSingleton<IUserManager, UserManager>();
XmlSerializer,
NetworkManager,
() => ImageProcessor,
() => DtoService,
this,
JsonSerializer,
FileSystemManager,
cryptoProvider);
serviceCollection.AddSingleton(UserManager); // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
// TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
serviceCollection.AddSingleton<IMediaEncoder>(provider =>
ActivatorUtilities.CreateInstance<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(provider, _startupOptions.FFmpegPath ?? string.Empty));
MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(), serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
ServerConfigurationManager, serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
FileSystemManager, serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
LocalizationManager, serviceCollection.AddSingleton<ILibraryManager, LibraryManager>();
() => SubtitleEncoder,
startupConfig,
StartupOptions.FFmpegPath);
serviceCollection.AddSingleton(MediaEncoder);
LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager, MediaEncoder); serviceCollection.AddSingleton<IMusicManager, MusicManager>();
serviceCollection.AddSingleton(LibraryManager);
var musicManager = new MusicManager(LibraryManager); serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
serviceCollection.AddSingleton<IMusicManager>(musicManager);
LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager); serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
serviceCollection.AddSingleton(LibraryMonitor);
serviceCollection.AddSingleton<ISearchEngine>(new SearchEngine(LoggerFactory, LibraryManager, UserManager));
CertificateInfo = GetCertificateInfo(true);
Certificate = GetCertificate(CertificateInfo);
serviceCollection.AddSingleton<ServiceController>(); serviceCollection.AddSingleton<ServiceController>();
serviceCollection.AddSingleton<IHttpListener, WebSocketSharpListener>(); serviceCollection.AddSingleton<IHttpListener, WebSocketSharpListener>();
serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>(); serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger<ImageProcessor>(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
serviceCollection.AddSingleton(ImageProcessor);
TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager); serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
serviceCollection.AddSingleton(TVSeriesManager);
DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager); serviceCollection.AddSingleton<IDeviceManager, DeviceManager>();
serviceCollection.AddSingleton(DeviceManager);
MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder); serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
serviceCollection.AddSingleton(MediaSourceManager);
SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager); serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
serviceCollection.AddSingleton(SubtitleManager);
ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer); serviceCollection.AddSingleton<IProviderManager, ProviderManager>();
serviceCollection.AddSingleton(ProviderManager);
DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager); // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
serviceCollection.AddSingleton(DtoService); serviceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
serviceCollection.AddSingleton<IDtoService, DtoService>();
ChannelManager = new ChannelManager( serviceCollection.AddSingleton<IChannelManager, ChannelManager>();
UserManager,
DtoService,
LibraryManager,
LoggerFactory.CreateLogger<ChannelManager>(),
ServerConfigurationManager,
FileSystemManager,
UserDataManager,
JsonSerializer,
ProviderManager);
serviceCollection.AddSingleton(ChannelManager);
SessionManager = new SessionManager( serviceCollection.AddSingleton<ISessionManager, SessionManager>();
LoggerFactory.CreateLogger<SessionManager>(),
UserDataManager,
LibraryManager,
UserManager,
musicManager,
DtoService,
ImageProcessor,
this,
AuthenticationRepository,
DeviceManager,
MediaSourceManager);
serviceCollection.AddSingleton(SessionManager);
serviceCollection.AddSingleton<IDlnaManager>( serviceCollection.AddSingleton<IDlnaManager, DlnaManager>();
new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this));
CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager); serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
serviceCollection.AddSingleton(CollectionManager);
serviceCollection.AddSingleton(typeof(IPlaylistManager), typeof(PlaylistManager)); serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager); serviceCollection.AddSingleton<LiveTvDtoService>();
serviceCollection.AddSingleton(LiveTvManager); serviceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
serviceCollection.AddSingleton(UserViewManager);
NotificationManager = new NotificationManager( serviceCollection.AddSingleton<INotificationManager, NotificationManager>();
LoggerFactory.CreateLogger<NotificationManager>(),
UserManager,
ServerConfigurationManager);
serviceCollection.AddSingleton(NotificationManager);
serviceCollection.AddSingleton<IDeviceDiscovery>(new DeviceDiscovery(ServerConfigurationManager)); serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
ChapterManager = new ChapterManager(ItemRepository); serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
serviceCollection.AddSingleton(ChapterManager);
EncodingManager = new MediaEncoder.EncodingManager( serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
LoggerFactory.CreateLogger<MediaEncoder.EncodingManager>(),
FileSystemManager,
MediaEncoder,
ChapterManager,
LibraryManager);
serviceCollection.AddSingleton(EncodingManager);
var activityLogRepo = GetActivityLogRepository(); serviceCollection.AddSingleton<IActivityRepository, ActivityRepository>();
serviceCollection.AddSingleton(activityLogRepo); serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
serviceCollection.AddSingleton<IActivityManager>(new ActivityManager(activityLogRepo, UserManager));
var authContext = new AuthorizationContext(AuthenticationRepository, UserManager); serviceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>();
serviceCollection.AddSingleton<IAuthorizationContext>(authContext); serviceCollection.AddSingleton<ISessionContext, SessionContext>();
serviceCollection.AddSingleton<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
AuthService = new AuthService(LoggerFactory.CreateLogger<AuthService>(), authContext, ServerConfigurationManager, SessionManager, NetworkManager); serviceCollection.AddSingleton<IAuthService, AuthService>();
serviceCollection.AddSingleton(AuthService);
SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder( serviceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
LibraryManager,
LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>(),
ApplicationPaths,
FileSystemManager,
MediaEncoder,
HttpClient,
MediaSourceManager);
serviceCollection.AddSingleton(SubtitleEncoder);
serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager)); serviceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>();
serviceCollection.AddSingleton<EncodingHelper>(); serviceCollection.AddSingleton<EncodingHelper>();
serviceCollection.AddSingleton(typeof(IAttachmentExtractor), typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor)); serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
_displayPreferencesRepository.Initialize();
var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger<SqliteUserDataRepository>(), ApplicationPaths);
SetStaticProperties();
((UserManager)UserManager).Initialize();
((UserDataManager)UserDataManager).Repository = userDataRepo;
ItemRepository.Initialize(userDataRepo, UserManager);
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
} }
/// <summary> /// <summary>
/// Create services registered with the service container that need to be initialized at application startup. /// Create services registered with the service container that need to be initialized at application startup.
/// </summary> /// </summary>
public void InitializeServices() /// <returns>A task representing the service initialization operation.</returns>
public async Task InitializeServices()
{ {
HttpServer = Resolve<IHttpServer>(); var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>();
await localizationManager.LoadAll().ConfigureAwait(false);
_mediaEncoder = Resolve<IMediaEncoder>();
_sessionManager = Resolve<ISessionManager>();
_httpServer = Resolve<IHttpServer>();
_httpClient = Resolve<IHttpClient>();
((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize();
((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
((SqliteUserRepository)Resolve<IUserRepository>()).Initialize();
((ActivityRepository)Resolve<IActivityRepository>()).Initialize();
SetStaticProperties();
var userManager = (UserManager)Resolve<IUserManager>();
userManager.Initialize();
var userDataRepo = (SqliteUserDataRepository)Resolve<IUserDataRepository>();
((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, userManager);
FindParts();
} }
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths) public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
@ -953,75 +768,38 @@ namespace Emby.Server.Implementations
} }
} }
/// <summary>
/// Gets the user repository.
/// </summary>
/// <returns><see cref="Task{SqliteUserRepository}" />.</returns>
private SqliteUserRepository GetUserRepository()
{
var repo = new SqliteUserRepository(
LoggerFactory.CreateLogger<SqliteUserRepository>(),
ApplicationPaths);
repo.Initialize();
return repo;
}
private IAuthenticationRepository GetAuthenticationRepository()
{
var repo = new AuthenticationRepository(LoggerFactory, ServerConfigurationManager);
repo.Initialize();
return repo;
}
private IActivityRepository GetActivityLogRepository()
{
var repo = new ActivityRepository(LoggerFactory.CreateLogger<ActivityRepository>(), ServerConfigurationManager.ApplicationPaths, FileSystemManager);
repo.Initialize();
return repo;
}
/// <summary> /// <summary>
/// Dirty hacks. /// Dirty hacks.
/// </summary> /// </summary>
private void SetStaticProperties() private void SetStaticProperties()
{ {
ItemRepository.ImageProcessor = ImageProcessor;
// For now there's no real way to inject these properly // For now there's no real way to inject these properly
BaseItem.Logger = LoggerFactory.CreateLogger("BaseItem"); BaseItem.Logger = Resolve<ILogger<BaseItem>>();
BaseItem.ConfigurationManager = ServerConfigurationManager; BaseItem.ConfigurationManager = ServerConfigurationManager;
BaseItem.LibraryManager = LibraryManager; BaseItem.LibraryManager = Resolve<ILibraryManager>();
BaseItem.ProviderManager = ProviderManager; BaseItem.ProviderManager = Resolve<IProviderManager>();
BaseItem.LocalizationManager = LocalizationManager; BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
BaseItem.ItemRepository = ItemRepository; BaseItem.ItemRepository = Resolve<IItemRepository>();
User.UserManager = UserManager; User.UserManager = Resolve<IUserManager>();
BaseItem.FileSystem = FileSystemManager; BaseItem.FileSystem = _fileSystemManager;
BaseItem.UserDataManager = UserDataManager; BaseItem.UserDataManager = Resolve<IUserDataManager>();
BaseItem.ChannelManager = ChannelManager; BaseItem.ChannelManager = Resolve<IChannelManager>();
Video.LiveTvManager = LiveTvManager; Video.LiveTvManager = Resolve<ILiveTvManager>();
Folder.UserViewManager = UserViewManager; Folder.UserViewManager = Resolve<IUserViewManager>();
UserView.TVSeriesManager = TVSeriesManager; UserView.TVSeriesManager = Resolve<ITVSeriesManager>();
UserView.CollectionManager = CollectionManager; UserView.CollectionManager = Resolve<ICollectionManager>();
BaseItem.MediaSourceManager = MediaSourceManager; BaseItem.MediaSourceManager = Resolve<IMediaSourceManager>();
CollectionFolder.XmlSerializer = XmlSerializer; CollectionFolder.XmlSerializer = _xmlSerializer;
CollectionFolder.JsonSerializer = JsonSerializer; CollectionFolder.JsonSerializer = Resolve<IJsonSerializer>();
CollectionFolder.ApplicationHost = this; CollectionFolder.ApplicationHost = this;
AuthenticatedAttribute.AuthService = AuthService; AuthenticatedAttribute.AuthService = Resolve<IAuthService>();
} }
/// <summary> /// <summary>
/// Finds the parts. /// Finds plugin components and register them with the appropriate services.
/// </summary> /// </summary>
public void FindParts() private void FindParts()
{ {
InstallationManager = ServiceProvider.GetService<IInstallationManager>();
if (!ServerConfigurationManager.Configuration.IsPortAuthorized) if (!ServerConfigurationManager.Configuration.IsPortAuthorized)
{ {
ServerConfigurationManager.Configuration.IsPortAuthorized = true; ServerConfigurationManager.Configuration.IsPortAuthorized = true;
@ -1034,34 +812,34 @@ namespace Emby.Server.Implementations
.Where(i => i != null) .Where(i => i != null)
.ToArray(); .ToArray();
HttpServer.Init(GetExportTypes<IService>(), GetExports<IWebSocketListener>(), GetUrlPrefixes()); _httpServer.Init(GetExportTypes<IService>(), GetExports<IWebSocketListener>(), GetUrlPrefixes());
LibraryManager.AddParts( Resolve<ILibraryManager>().AddParts(
GetExports<IResolverIgnoreRule>(), GetExports<IResolverIgnoreRule>(),
GetExports<IItemResolver>(), GetExports<IItemResolver>(),
GetExports<IIntroProvider>(), GetExports<IIntroProvider>(),
GetExports<IBaseItemComparer>(), GetExports<IBaseItemComparer>(),
GetExports<ILibraryPostScanTask>()); GetExports<ILibraryPostScanTask>());
ProviderManager.AddParts( Resolve<IProviderManager>().AddParts(
GetExports<IImageProvider>(), GetExports<IImageProvider>(),
GetExports<IMetadataService>(), GetExports<IMetadataService>(),
GetExports<IMetadataProvider>(), GetExports<IMetadataProvider>(),
GetExports<IMetadataSaver>(), GetExports<IMetadataSaver>(),
GetExports<IExternalId>()); GetExports<IExternalId>());
LiveTvManager.AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>()); Resolve<ILiveTvManager>().AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
SubtitleManager.AddParts(GetExports<ISubtitleProvider>()); Resolve<ISubtitleManager>().AddParts(GetExports<ISubtitleProvider>());
ChannelManager.AddParts(GetExports<IChannel>()); Resolve<IChannelManager>().AddParts(GetExports<IChannel>());
MediaSourceManager.AddParts(GetExports<IMediaSourceProvider>()); Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>());
NotificationManager.AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>()); Resolve<INotificationManager>().AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>());
UserManager.AddParts(GetExports<IAuthenticationProvider>(), GetExports<IPasswordResetProvider>()); Resolve<IUserManager>().AddParts(GetExports<IAuthenticationProvider>(), GetExports<IPasswordResetProvider>());
IsoManager.AddParts(GetExports<IIsoMounter>()); Resolve<IIsoManager>().AddParts(GetExports<IIsoMounter>());
} }
private IPlugin LoadPlugin(IPlugin plugin) private IPlugin LoadPlugin(IPlugin plugin)
@ -1168,16 +946,6 @@ namespace Emby.Server.Implementations
}); });
} }
private CertificateInfo GetCertificateInfo(bool generateCertificate)
{
// Custom cert
return new CertificateInfo
{
Path = ServerConfigurationManager.Configuration.CertificatePath,
Password = ServerConfigurationManager.Configuration.CertificatePassword
};
}
/// <summary> /// <summary>
/// Called when [configuration updated]. /// Called when [configuration updated].
/// </summary> /// </summary>
@ -1204,14 +972,13 @@ namespace Emby.Server.Implementations
} }
} }
if (!HttpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase)) if (!_httpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase))
{ {
requiresRestart = true; requiresRestart = true;
} }
var currentCertPath = CertificateInfo?.Path; var currentCertPath = CertificateInfo?.Path;
var newCertInfo = GetCertificateInfo(false); var newCertPath = ServerConfigurationManager.Configuration.CertificatePath;
var newCertPath = newCertInfo?.Path;
if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase))
{ {
@ -1264,7 +1031,7 @@ namespace Emby.Server.Implementations
{ {
try try
{ {
await SessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false); await _sessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -1368,7 +1135,7 @@ namespace Emby.Server.Implementations
IsShuttingDown = IsShuttingDown, IsShuttingDown = IsShuttingDown,
Version = ApplicationVersionString, Version = ApplicationVersionString,
WebSocketPortNumber = HttpPort, WebSocketPortNumber = HttpPort,
CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(), CompletedInstallations = Resolve<IInstallationManager>().CompletedInstallations.ToArray(),
Id = SystemId, Id = SystemId,
ProgramDataPath = ApplicationPaths.ProgramDataPath, ProgramDataPath = ApplicationPaths.ProgramDataPath,
WebPath = ApplicationPaths.WebPath, WebPath = ApplicationPaths.WebPath,
@ -1388,15 +1155,14 @@ namespace Emby.Server.Implementations
ServerName = FriendlyName, ServerName = FriendlyName,
LocalAddress = localAddress, LocalAddress = localAddress,
SupportsLibraryMonitor = true, SupportsLibraryMonitor = true,
EncoderLocation = MediaEncoder.EncoderLocation, EncoderLocation = _mediaEncoder.EncoderLocation,
SystemArchitecture = RuntimeInformation.OSArchitecture, SystemArchitecture = RuntimeInformation.OSArchitecture,
SystemUpdateLevel = SystemUpdateLevel, PackageName = _startupOptions.PackageName
PackageName = StartupOptions.PackageName
}; };
} }
public IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo() public IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo()
=> NetworkManager.GetMacAddresses() => _networkManager.GetMacAddresses()
.Select(i => new WakeOnLanInfo(i)) .Select(i => new WakeOnLanInfo(i))
.ToList(); .ToList();
@ -1508,7 +1274,7 @@ namespace Emby.Server.Implementations
if (addresses.Count == 0) if (addresses.Count == 0)
{ {
addresses.AddRange(NetworkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces)); addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
} }
var resultList = new List<IPAddress>(); var resultList = new List<IPAddress>();
@ -1575,7 +1341,7 @@ namespace Emby.Server.Implementations
try try
{ {
using (var response = await HttpClient.SendAsync( using (var response = await _httpClient.SendAsync(
new HttpRequestOptions new HttpRequestOptions
{ {
Url = apiUrl, Url = apiUrl,
@ -1628,7 +1394,7 @@ namespace Emby.Server.Implementations
try try
{ {
await SessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false); await _sessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -1749,14 +1515,8 @@ namespace Emby.Server.Implementations
Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name); Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name);
} }
} }
_userRepository?.Dispose();
_displayPreferencesRepository?.Dispose();
} }
_userRepository = null;
_displayPreferencesRepository = null;
_disposed = true; _disposed = true;
} }
} }

View file

@ -18,7 +18,7 @@ namespace Emby.Server.Implementations
{ {
{ HostWebClientKey, bool.TrueString }, { HostWebClientKey, bool.TrueString },
{ HttpListenerHost.DefaultRedirectKey, "web/index.html" }, { HttpListenerHost.DefaultRedirectKey, "web/index.html" },
{ InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest.json" }, { InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest-stable.json" },
{ FfmpegProbeSizeKey, "1G" }, { FfmpegProbeSizeKey, "1G" },
{ FfmpegAnalyzeDurationKey, "200M" }, { FfmpegAnalyzeDurationKey, "200M" },
{ PlaylistsAllowDuplicatesKey, bool.TrueString } { PlaylistsAllowDuplicatesKey, bool.TrueString }

View file

@ -39,12 +39,11 @@ namespace Emby.Server.Implementations.Data
{ {
private const string ChaptersTableName = "Chapters2"; private const string ChaptersTableName = "Chapters2";
/// <summary>
/// The _app paths
/// </summary>
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
// TODO: Remove this dependency. GetImageCacheTag() is the only method used and it can be converted to a static helper method
private readonly IImageProcessor _imageProcessor;
private readonly TypeMapper _typeMapper; private readonly TypeMapper _typeMapper;
private readonly JsonSerializerOptions _jsonOptions; private readonly JsonSerializerOptions _jsonOptions;
@ -71,7 +70,8 @@ namespace Emby.Server.Implementations.Data
IServerConfigurationManager config, IServerConfigurationManager config,
IServerApplicationHost appHost, IServerApplicationHost appHost,
ILogger<SqliteItemRepository> logger, ILogger<SqliteItemRepository> logger,
ILocalizationManager localization) ILocalizationManager localization,
IImageProcessor imageProcessor)
: base(logger) : base(logger)
{ {
if (config == null) if (config == null)
@ -82,6 +82,7 @@ namespace Emby.Server.Implementations.Data
_config = config; _config = config;
_appHost = appHost; _appHost = appHost;
_localization = localization; _localization = localization;
_imageProcessor = imageProcessor;
_typeMapper = new TypeMapper(); _typeMapper = new TypeMapper();
_jsonOptions = JsonDefaults.GetOptions(); _jsonOptions = JsonDefaults.GetOptions();
@ -98,8 +99,6 @@ namespace Emby.Server.Implementations.Data
/// <inheritdoc /> /// <inheritdoc />
protected override TempStoreMode TempStore => TempStoreMode.Memory; protected override TempStoreMode TempStore => TempStoreMode.Memory;
public IImageProcessor ImageProcessor { get; set; }
/// <summary> /// <summary>
/// Opens the connection to the database /// Opens the connection to the database
/// </summary> /// </summary>
@ -1991,7 +1990,14 @@ namespace Emby.Server.Implementations.Data
if (!string.IsNullOrEmpty(chapter.ImagePath)) if (!string.IsNullOrEmpty(chapter.ImagePath))
{ {
chapter.ImageTag = ImageProcessor.GetImageCacheTag(item, chapter); try
{
chapter.ImageTag = _imageProcessor.GetImageCacheTag(item, chapter);
}
catch (Exception ex)
{
Logger.LogError(ex, "Failed to create image cache tag.");
}
} }
} }

View file

@ -38,10 +38,11 @@ namespace Emby.Server.Implementations.Devices
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localizationManager; private readonly ILocalizationManager _localizationManager;
private readonly IAuthenticationRepository _authRepo; private readonly IAuthenticationRepository _authRepo;
private readonly Dictionary<string, ClientCapabilities> _capabilitiesCache;
public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated; public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded; public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
private readonly object _cameraUploadSyncLock = new object(); private readonly object _cameraUploadSyncLock = new object();
@ -65,10 +66,9 @@ namespace Emby.Server.Implementations.Devices
_libraryManager = libraryManager; _libraryManager = libraryManager;
_localizationManager = localizationManager; _localizationManager = localizationManager;
_authRepo = authRepo; _authRepo = authRepo;
_capabilitiesCache = new Dictionary<string, ClientCapabilities>(StringComparer.OrdinalIgnoreCase);
} }
private Dictionary<string, ClientCapabilities> _capabilitiesCache = new Dictionary<string, ClientCapabilities>(StringComparer.OrdinalIgnoreCase);
public void SaveCapabilities(string deviceId, ClientCapabilities capabilities) public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
{ {
var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json"); var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json");

View file

@ -38,21 +38,23 @@ namespace Emby.Server.Implementations.Dto
private readonly IProviderManager _providerManager; private readonly IProviderManager _providerManager;
private readonly IApplicationHost _appHost; private readonly IApplicationHost _appHost;
private readonly Func<IMediaSourceManager> _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly Func<ILiveTvManager> _livetvManager; private readonly Lazy<ILiveTvManager> _livetvManagerFactory;
private ILiveTvManager LivetvManager => _livetvManagerFactory.Value;
public DtoService( public DtoService(
ILoggerFactory loggerFactory, ILogger<DtoService> logger,
ILibraryManager libraryManager, ILibraryManager libraryManager,
IUserDataManager userDataRepository, IUserDataManager userDataRepository,
IItemRepository itemRepo, IItemRepository itemRepo,
IImageProcessor imageProcessor, IImageProcessor imageProcessor,
IProviderManager providerManager, IProviderManager providerManager,
IApplicationHost appHost, IApplicationHost appHost,
Func<IMediaSourceManager> mediaSourceManager, IMediaSourceManager mediaSourceManager,
Func<ILiveTvManager> livetvManager) Lazy<ILiveTvManager> livetvManagerFactory)
{ {
_logger = loggerFactory.CreateLogger(nameof(DtoService)); _logger = logger;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_userDataRepository = userDataRepository; _userDataRepository = userDataRepository;
_itemRepo = itemRepo; _itemRepo = itemRepo;
@ -60,7 +62,7 @@ namespace Emby.Server.Implementations.Dto
_providerManager = providerManager; _providerManager = providerManager;
_appHost = appHost; _appHost = appHost;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_livetvManager = livetvManager; _livetvManagerFactory = livetvManagerFactory;
} }
/// <summary> /// <summary>
@ -125,12 +127,12 @@ namespace Emby.Server.Implementations.Dto
if (programTuples.Count > 0) if (programTuples.Count > 0)
{ {
_livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult(); LivetvManager.AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult();
} }
if (channelTuples.Count > 0) if (channelTuples.Count > 0)
{ {
_livetvManager().AddChannelInfo(channelTuples, options, user); LivetvManager.AddChannelInfo(channelTuples, options, user);
} }
return returnItems; return returnItems;
@ -142,12 +144,12 @@ namespace Emby.Server.Implementations.Dto
if (item is LiveTvChannel tvChannel) if (item is LiveTvChannel tvChannel)
{ {
var list = new List<(BaseItemDto, LiveTvChannel)>(1) { (dto, tvChannel) }; var list = new List<(BaseItemDto, LiveTvChannel)>(1) { (dto, tvChannel) };
_livetvManager().AddChannelInfo(list, options, user); LivetvManager.AddChannelInfo(list, options, user);
} }
else if (item is LiveTvProgram) else if (item is LiveTvProgram)
{ {
var list = new List<(BaseItem, BaseItemDto)>(1) { (item, dto) }; var list = new List<(BaseItem, BaseItemDto)>(1) { (item, dto) };
var task = _livetvManager().AddInfoToProgramDto(list, options.Fields, user); var task = LivetvManager.AddInfoToProgramDto(list, options.Fields, user);
Task.WaitAll(task); Task.WaitAll(task);
} }
@ -223,7 +225,7 @@ namespace Emby.Server.Implementations.Dto
if (item is IHasMediaSources if (item is IHasMediaSources
&& options.ContainsField(ItemFields.MediaSources)) && options.ContainsField(ItemFields.MediaSources))
{ {
dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(item, true, user).ToArray(); dto.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, true, user).ToArray();
NormalizeMediaSourceContainers(dto); NormalizeMediaSourceContainers(dto);
} }
@ -254,7 +256,7 @@ namespace Emby.Server.Implementations.Dto
dto.Etag = item.GetEtag(user); dto.Etag = item.GetEtag(user);
} }
var liveTvManager = _livetvManager(); var liveTvManager = LivetvManager;
var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path); var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path);
if (activeRecording != null) if (activeRecording != null)
{ {
@ -1045,7 +1047,7 @@ namespace Emby.Server.Implementations.Dto
} }
else else
{ {
mediaStreams = _mediaSourceManager().GetStaticMediaSources(item, true)[0].MediaStreams.ToArray(); mediaStreams = _mediaSourceManager.GetStaticMediaSources(item, true)[0].MediaStreams.ToArray();
} }
dto.MediaStreams = mediaStreams; dto.MediaStreams = mediaStreams;

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{E383961B-9356-4D5D-8233-9A1079D03055}</ProjectGuid>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" /> <ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
<ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" /> <ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" />

View file

@ -16,46 +16,63 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IConfiguration _appConfig; private readonly IConfiguration _appConfig;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IStartupOptions _startupOptions;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="StartupWizard"/> class. /// Initializes a new instance of the <see cref="StartupWizard"/> class.
/// </summary> /// </summary>
/// <param name="appHost">The application host.</param> /// <param name="appHost">The application host.</param>
/// <param name="appConfig">The application configuration.</param>
/// <param name="config">The configuration manager.</param> /// <param name="config">The configuration manager.</param>
public StartupWizard(IServerApplicationHost appHost, IConfiguration appConfig, IServerConfigurationManager config) /// <param name="startupOptions">The application startup options.</param>
public StartupWizard(
IServerApplicationHost appHost,
IConfiguration appConfig,
IServerConfigurationManager config,
IStartupOptions startupOptions)
{ {
_appHost = appHost; _appHost = appHost;
_appConfig = appConfig; _appConfig = appConfig;
_config = config; _config = config;
_startupOptions = startupOptions;
} }
/// <inheritdoc /> /// <inheritdoc />
public Task RunAsync() public Task RunAsync()
{
Run();
return Task.CompletedTask;
}
private void Run()
{ {
if (!_appHost.CanLaunchWebBrowser) if (!_appHost.CanLaunchWebBrowser)
{ {
return Task.CompletedTask; return;
} }
if (!_appConfig.HostWebClient()) // Always launch the startup wizard if possible when it has not been completed
if (!_config.Configuration.IsStartupWizardCompleted && _appConfig.HostWebClient())
{ {
BrowserLauncher.OpenSwaggerPage(_appHost); BrowserLauncher.OpenWebApp(_appHost);
return;
} }
else if (!_config.Configuration.IsStartupWizardCompleted)
// Do nothing if the web app is configured to not run automatically
if (!_config.Configuration.AutoRunWebApp || _startupOptions.NoAutoRunWebApp)
{
return;
}
// Launch the swagger page if the web client is not hosted, otherwise open the web client
if (_appConfig.HostWebClient())
{ {
BrowserLauncher.OpenWebApp(_appHost); BrowserLauncher.OpenWebApp(_appHost);
} }
else if (_config.Configuration.AutoRunWebApp) else
{ {
var options = ((ApplicationHost)_appHost).StartupOptions; BrowserLauncher.OpenSwaggerPage(_appHost);
if (!options.NoAutoRunWebApp)
{
BrowserLauncher.OpenWebApp(_appHost);
}
} }
return Task.CompletedTask;
} }
/// <inheritdoc /> /// <inheritdoc />

View file

@ -6,6 +6,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
@ -24,7 +25,7 @@ namespace Emby.Server.Implementations.HttpClientManager
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly Func<string> _defaultUserAgentFn; private readonly IApplicationHost _appHost;
/// <summary> /// <summary>
/// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests. /// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests.
@ -40,12 +41,12 @@ namespace Emby.Server.Implementations.HttpClientManager
IApplicationPaths appPaths, IApplicationPaths appPaths,
ILogger<HttpClientManager> logger, ILogger<HttpClientManager> logger,
IFileSystem fileSystem, IFileSystem fileSystem,
Func<string> defaultUserAgentFn) IApplicationHost appHost)
{ {
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_fileSystem = fileSystem; _fileSystem = fileSystem;
_appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths)); _appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths));
_defaultUserAgentFn = defaultUserAgentFn; _appHost = appHost;
} }
/// <summary> /// <summary>
@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.HttpClientManager
if (options.EnableDefaultUserAgent if (options.EnableDefaultUserAgent
&& !request.Headers.TryGetValues(HeaderNames.UserAgent, out _)) && !request.Headers.TryGetValues(HeaderNames.UserAgent, out _))
{ {
request.Headers.Add(HeaderNames.UserAgent, _defaultUserAgentFn()); request.Headers.Add(HeaderNames.UserAgent, _appHost.ApplicationUserAgent);
} }
switch (options.DecompressionMethod) switch (options.DecompressionMethod)

View file

@ -14,6 +14,7 @@ using Emby.Server.Implementations.Services;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
@ -230,7 +231,8 @@ namespace Emby.Server.Implementations.HttpServer
switch (ex) switch (ex)
{ {
case ArgumentException _: return 400; case ArgumentException _: return 400;
case SecurityException _: return 401; case AuthenticationException _: return 401;
case SecurityException _: return 403;
case DirectoryNotFoundException _: case DirectoryNotFoundException _:
case FileNotFoundException _: case FileNotFoundException _:
case ResourceNotFoundException _: return 404; case ResourceNotFoundException _: return 404;
@ -239,55 +241,52 @@ namespace Emby.Server.Implementations.HttpServer
} }
} }
private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace, string urlToLog) private async Task ErrorHandler(Exception ex, IRequest httpReq, int statusCode, string urlToLog)
{ {
try bool ignoreStackTrace =
ex is SocketException
|| ex is IOException
|| ex is OperationCanceledException
|| ex is SecurityException
|| ex is AuthenticationException
|| ex is FileNotFoundException;
if (ignoreStackTrace)
{ {
ex = GetActualException(ex); _logger.LogError("Error processing request: {Message}. URL: {Url}", ex.Message.TrimEnd('.'), urlToLog);
if (logExceptionStackTrace)
{
_logger.LogError(ex, "Error processing request. URL: {Url}", urlToLog);
}
else
{
_logger.LogError("Error processing request: {Message}. URL: {Url}", ex.Message.TrimEnd('.'), urlToLog);
}
var httpRes = httpReq.Response;
if (httpRes.HasStarted)
{
return;
}
var statusCode = GetStatusCode(ex);
httpRes.StatusCode = statusCode;
var errContent = NormalizeExceptionMessage(ex.Message);
httpRes.ContentType = "text/plain";
httpRes.ContentLength = errContent.Length;
await httpRes.WriteAsync(errContent).ConfigureAwait(false);
} }
catch (Exception errorEx) else
{ {
_logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response). URL: {Url}", urlToLog); _logger.LogError(ex, "Error processing request. URL: {Url}", urlToLog);
} }
var httpRes = httpReq.Response;
if (httpRes.HasStarted)
{
return;
}
httpRes.StatusCode = statusCode;
var errContent = NormalizeExceptionMessage(ex) ?? string.Empty;
httpRes.ContentType = "text/plain";
httpRes.ContentLength = errContent.Length;
await httpRes.WriteAsync(errContent).ConfigureAwait(false);
} }
private string NormalizeExceptionMessage(string msg) private string NormalizeExceptionMessage(Exception ex)
{ {
if (msg == null) // Do not expose the exception message for AuthenticationException
if (ex is AuthenticationException)
{ {
return string.Empty; return null;
} }
// Strip any information we don't want to reveal // Strip any information we don't want to reveal
return ex.Message
msg = msg.Replace(_config.ApplicationPaths.ProgramSystemPath, string.Empty, StringComparison.OrdinalIgnoreCase); ?.Replace(_config.ApplicationPaths.ProgramSystemPath, string.Empty, StringComparison.OrdinalIgnoreCase)
msg = msg.Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase); .Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase);
return msg;
} }
/// <summary> /// <summary>
@ -536,22 +535,32 @@ namespace Emby.Server.Implementations.HttpServer
throw new FileNotFoundException(); throw new FileNotFoundException();
} }
} }
catch (Exception ex) catch (Exception requestEx)
{ {
// Do not handle exceptions manually when in development mode try
// The framework-defined development exception page will be returned instead
if (_hostEnvironment.IsDevelopment())
{ {
throw; var requestInnerEx = GetActualException(requestEx);
} var statusCode = GetStatusCode(requestInnerEx);
bool ignoreStackTrace = // Do not handle 500 server exceptions manually when in development mode
ex is SocketException // The framework-defined development exception page will be returned instead
|| ex is IOException if (statusCode == 500 && _hostEnvironment.IsDevelopment())
|| ex is OperationCanceledException {
|| ex is SecurityException throw;
|| ex is FileNotFoundException; }
await ErrorHandler(ex, httpReq, !ignoreStackTrace, urlToLog).ConfigureAwait(false);
await ErrorHandler(requestInnerEx, httpReq, statusCode, urlToLog).ConfigureAwait(false);
}
catch (Exception handlerException)
{
var aggregateEx = new AggregateException("Error while handling request exception", requestEx, handlerException);
_logger.LogError(aggregateEx, "Error while handling exception in response to {Url}", urlToLog);
if (_hostEnvironment.IsDevelopment())
{
throw aggregateEx;
}
}
} }
finally finally
{ {

View file

@ -2,6 +2,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Security.Authentication;
using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.SocketSharp;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -68,7 +69,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (user == null && auth.UserId != Guid.Empty) if (user == null && auth.UserId != Guid.Empty)
{ {
throw new SecurityException("User with Id " + auth.UserId + " not found"); throw new AuthenticationException("User with Id " + auth.UserId + " not found");
} }
if (user != null) if (user != null)
@ -108,18 +109,12 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
if (user.Policy.IsDisabled) if (user.Policy.IsDisabled)
{ {
throw new SecurityException("User account has been disabled.") throw new SecurityException("User account has been disabled.");
{
SecurityExceptionType = SecurityExceptionType.Unauthenticated
};
} }
if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp)) if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp))
{ {
throw new SecurityException("User account has been disabled.") throw new SecurityException("User account has been disabled.");
{
SecurityExceptionType = SecurityExceptionType.Unauthenticated
};
} }
if (!user.Policy.IsAdministrator if (!user.Policy.IsAdministrator
@ -128,10 +123,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl"); request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
throw new SecurityException("This user account is not allowed access at this time.") throw new SecurityException("This user account is not allowed access at this time.");
{
SecurityExceptionType = SecurityExceptionType.ParentalControl
};
} }
} }
@ -190,10 +182,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
if (user == null || !user.Policy.IsAdministrator) if (user == null || !user.Policy.IsAdministrator)
{ {
throw new SecurityException("User does not have admin access.") throw new SecurityException("User does not have admin access.");
{
SecurityExceptionType = SecurityExceptionType.Unauthenticated
};
} }
} }
@ -201,10 +190,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
if (user == null || !user.Policy.EnableContentDeletion) if (user == null || !user.Policy.EnableContentDeletion)
{ {
throw new SecurityException("User does not have delete access.") throw new SecurityException("User does not have delete access.");
{
SecurityExceptionType = SecurityExceptionType.Unauthenticated
};
} }
} }
@ -212,10 +198,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
if (user == null || !user.Policy.EnableContentDownloading) if (user == null || !user.Policy.EnableContentDownloading)
{ {
throw new SecurityException("User does not have download access.") throw new SecurityException("User does not have download access.");
{
SecurityExceptionType = SecurityExceptionType.Unauthenticated
};
} }
} }
} }
@ -230,14 +213,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
if (string.IsNullOrEmpty(token)) if (string.IsNullOrEmpty(token))
{ {
throw new SecurityException("Access token is required."); throw new AuthenticationException("Access token is required.");
} }
var info = GetTokenInfo(request); var info = GetTokenInfo(request);
if (info == null) if (info == null)
{ {
throw new SecurityException("Access token is invalid or expired."); throw new AuthenticationException("Access token is invalid or expired.");
} }
//if (!string.IsNullOrEmpty(info.UserId)) //if (!string.IsNullOrEmpty(info.UserId))

View file

@ -17,6 +17,11 @@ namespace Emby.Server.Implementations.IO
{ {
public class LibraryMonitor : ILibraryMonitor public class LibraryMonitor : ILibraryMonitor
{ {
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
/// <summary> /// <summary>
/// The file system watchers. /// The file system watchers.
/// </summary> /// </summary>
@ -113,34 +118,23 @@ namespace Emby.Server.Implementations.IO
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "Error in ReportFileSystemChanged for {path}", path); _logger.LogError(ex, "Error in ReportFileSystemChanged for {path}", path);
} }
} }
} }
/// <summary>
/// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
private ILogger Logger { get; set; }
private ILibraryManager LibraryManager { get; set; }
private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LibraryMonitor" /> class. /// Initializes a new instance of the <see cref="LibraryMonitor" /> class.
/// </summary> /// </summary>
public LibraryMonitor( public LibraryMonitor(
ILoggerFactory loggerFactory, ILogger<LibraryMonitor> logger,
ILibraryManager libraryManager, ILibraryManager libraryManager,
IServerConfigurationManager configurationManager, IServerConfigurationManager configurationManager,
IFileSystem fileSystem) IFileSystem fileSystem)
{ {
LibraryManager = libraryManager; _libraryManager = libraryManager;
Logger = loggerFactory.CreateLogger(GetType().Name); _logger = logger;
ConfigurationManager = configurationManager; _configurationManager = configurationManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
} }
@ -151,7 +145,7 @@ namespace Emby.Server.Implementations.IO
return false; return false;
} }
var options = LibraryManager.GetLibraryOptions(item); var options = _libraryManager.GetLibraryOptions(item);
if (options != null) if (options != null)
{ {
@ -163,12 +157,12 @@ namespace Emby.Server.Implementations.IO
public void Start() public void Start()
{ {
LibraryManager.ItemAdded += OnLibraryManagerItemAdded; _libraryManager.ItemAdded += OnLibraryManagerItemAdded;
LibraryManager.ItemRemoved += OnLibraryManagerItemRemoved; _libraryManager.ItemRemoved += OnLibraryManagerItemRemoved;
var pathsToWatch = new List<string>(); var pathsToWatch = new List<string>();
var paths = LibraryManager var paths = _libraryManager
.RootFolder .RootFolder
.Children .Children
.Where(IsLibraryMonitorEnabled) .Where(IsLibraryMonitorEnabled)
@ -261,7 +255,7 @@ namespace Emby.Server.Implementations.IO
if (!Directory.Exists(path)) if (!Directory.Exists(path))
{ {
// Seeing a crash in the mono runtime due to an exception being thrown on a different thread // Seeing a crash in the mono runtime due to an exception being thrown on a different thread
Logger.LogInformation("Skipping realtime monitor for {Path} because the path does not exist", path); _logger.LogInformation("Skipping realtime monitor for {Path} because the path does not exist", path);
return; return;
} }
@ -297,7 +291,7 @@ namespace Emby.Server.Implementations.IO
if (_fileSystemWatchers.TryAdd(path, newWatcher)) if (_fileSystemWatchers.TryAdd(path, newWatcher))
{ {
newWatcher.EnableRaisingEvents = true; newWatcher.EnableRaisingEvents = true;
Logger.LogInformation("Watching directory " + path); _logger.LogInformation("Watching directory " + path);
} }
else else
{ {
@ -307,7 +301,7 @@ namespace Emby.Server.Implementations.IO
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "Error watching path: {path}", path); _logger.LogError(ex, "Error watching path: {path}", path);
} }
}); });
} }
@ -333,7 +327,7 @@ namespace Emby.Server.Implementations.IO
{ {
using (watcher) using (watcher)
{ {
Logger.LogInformation("Stopping directory watching for path {Path}", watcher.Path); _logger.LogInformation("Stopping directory watching for path {Path}", watcher.Path);
watcher.Created -= OnWatcherChanged; watcher.Created -= OnWatcherChanged;
watcher.Deleted -= OnWatcherChanged; watcher.Deleted -= OnWatcherChanged;
@ -372,7 +366,7 @@ namespace Emby.Server.Implementations.IO
var ex = e.GetException(); var ex = e.GetException();
var dw = (FileSystemWatcher)sender; var dw = (FileSystemWatcher)sender;
Logger.LogError(ex, "Error in Directory watcher for: {Path}", dw.Path); _logger.LogError(ex, "Error in Directory watcher for: {Path}", dw.Path);
DisposeWatcher(dw, true); DisposeWatcher(dw, true);
} }
@ -390,7 +384,7 @@ namespace Emby.Server.Implementations.IO
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "Exception in ReportFileSystemChanged. Path: {FullPath}", e.FullPath); _logger.LogError(ex, "Exception in ReportFileSystemChanged. Path: {FullPath}", e.FullPath);
} }
} }
@ -416,13 +410,13 @@ namespace Emby.Server.Implementations.IO
{ {
if (_fileSystem.AreEqual(i, path)) if (_fileSystem.AreEqual(i, path))
{ {
Logger.LogDebug("Ignoring change to {Path}", path); _logger.LogDebug("Ignoring change to {Path}", path);
return true; return true;
} }
if (_fileSystem.ContainsSubPath(i, path)) if (_fileSystem.ContainsSubPath(i, path))
{ {
Logger.LogDebug("Ignoring change to {Path}", path); _logger.LogDebug("Ignoring change to {Path}", path);
return true; return true;
} }
@ -430,7 +424,7 @@ namespace Emby.Server.Implementations.IO
var parent = Path.GetDirectoryName(i); var parent = Path.GetDirectoryName(i);
if (!string.IsNullOrEmpty(parent) && _fileSystem.AreEqual(parent, path)) if (!string.IsNullOrEmpty(parent) && _fileSystem.AreEqual(parent, path))
{ {
Logger.LogDebug("Ignoring change to {Path}", path); _logger.LogDebug("Ignoring change to {Path}", path);
return true; return true;
} }
@ -485,7 +479,7 @@ namespace Emby.Server.Implementations.IO
} }
} }
var newRefresher = new FileRefresher(path, ConfigurationManager, LibraryManager, Logger); var newRefresher = new FileRefresher(path, _configurationManager, _libraryManager, _logger);
newRefresher.Completed += NewRefresher_Completed; newRefresher.Completed += NewRefresher_Completed;
_activeRefreshers.Add(newRefresher); _activeRefreshers.Add(newRefresher);
} }
@ -502,8 +496,8 @@ namespace Emby.Server.Implementations.IO
/// </summary> /// </summary>
public void Stop() public void Stop()
{ {
LibraryManager.ItemAdded -= OnLibraryManagerItemAdded; _libraryManager.ItemAdded -= OnLibraryManagerItemAdded;
LibraryManager.ItemRemoved -= OnLibraryManagerItemRemoved; _libraryManager.ItemRemoved -= OnLibraryManagerItemRemoved;
foreach (var watcher in _fileSystemWatchers.Values.ToList()) foreach (var watcher in _fileSystemWatchers.Values.ToList())
{ {

View file

@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.Library
{ {
if (resolvedUser == null) if (resolvedUser == null)
{ {
throw new ArgumentNullException(nameof(resolvedUser)); throw new AuthenticationException($"Specified user does not exist.");
} }
bool success = false; bool success = false;

View file

@ -54,9 +54,29 @@ namespace Emby.Server.Implementations.Library
/// </summary> /// </summary>
public class LibraryManager : ILibraryManager public class LibraryManager : ILibraryManager
{ {
private readonly ILogger _logger;
private readonly ITaskManager _taskManager;
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataRepository;
private readonly IServerConfigurationManager _configurationManager;
private readonly Lazy<ILibraryMonitor> _libraryMonitorFactory;
private readonly Lazy<IProviderManager> _providerManagerFactory;
private readonly Lazy<IUserViewManager> _userviewManagerFactory;
private readonly IServerApplicationHost _appHost;
private readonly IMediaEncoder _mediaEncoder;
private readonly IFileSystem _fileSystem;
private readonly IItemRepository _itemRepository;
private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
private NamingOptions _namingOptions; private NamingOptions _namingOptions;
private string[] _videoFileExtensions; private string[] _videoFileExtensions;
private ILibraryMonitor LibraryMonitor => _libraryMonitorFactory.Value;
private IProviderManager ProviderManager => _providerManagerFactory.Value;
private IUserViewManager UserViewManager => _userviewManagerFactory.Value;
/// <summary> /// <summary>
/// Gets or sets the postscan tasks. /// Gets or sets the postscan tasks.
/// </summary> /// </summary>
@ -89,12 +109,6 @@ namespace Emby.Server.Implementations.Library
/// <value>The comparers.</value> /// <value>The comparers.</value>
private IBaseItemComparer[] Comparers { get; set; } private IBaseItemComparer[] Comparers { get; set; }
/// <summary>
/// Gets or sets the active item repository
/// </summary>
/// <value>The item repository.</value>
public IItemRepository ItemRepository { get; set; }
/// <summary> /// <summary>
/// Occurs when [item added]. /// Occurs when [item added].
/// </summary> /// </summary>
@ -110,90 +124,47 @@ namespace Emby.Server.Implementations.Library
/// </summary> /// </summary>
public event EventHandler<ItemChangeEventArgs> ItemRemoved; public event EventHandler<ItemChangeEventArgs> ItemRemoved;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// The _task manager
/// </summary>
private readonly ITaskManager _taskManager;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _user data repository
/// </summary>
private readonly IUserDataManager _userDataRepository;
/// <summary>
/// Gets or sets the configuration manager.
/// </summary>
/// <value>The configuration manager.</value>
private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
private readonly Func<IProviderManager> _providerManagerFactory;
private readonly Func<IUserViewManager> _userviewManager;
public bool IsScanRunning { get; private set; } public bool IsScanRunning { get; private set; }
private IServerApplicationHost _appHost;
private readonly IMediaEncoder _mediaEncoder;
/// <summary>
/// The _library items cache
/// </summary>
private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
/// <summary>
/// Gets the library items cache.
/// </summary>
/// <value>The library items cache.</value>
private ConcurrentDictionary<Guid, BaseItem> LibraryItemsCache => _libraryItemsCache;
private readonly IFileSystem _fileSystem;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LibraryManager" /> class. /// Initializes a new instance of the <see cref="LibraryManager" /> class.
/// </summary> /// </summary>
/// <param name="appHost">The application host</param> /// <param name="appHost">The application host</param>
/// <param name="loggerFactory">The logger factory.</param> /// <param name="logger">The logger.</param>
/// <param name="taskManager">The task manager.</param> /// <param name="taskManager">The task manager.</param>
/// <param name="userManager">The user manager.</param> /// <param name="userManager">The user manager.</param>
/// <param name="configurationManager">The configuration manager.</param> /// <param name="configurationManager">The configuration manager.</param>
/// <param name="userDataRepository">The user data repository.</param> /// <param name="userDataRepository">The user data repository.</param>
public LibraryManager( public LibraryManager(
IServerApplicationHost appHost, IServerApplicationHost appHost,
ILoggerFactory loggerFactory, ILogger<LibraryManager> logger,
ITaskManager taskManager, ITaskManager taskManager,
IUserManager userManager, IUserManager userManager,
IServerConfigurationManager configurationManager, IServerConfigurationManager configurationManager,
IUserDataManager userDataRepository, IUserDataManager userDataRepository,
Func<ILibraryMonitor> libraryMonitorFactory, Lazy<ILibraryMonitor> libraryMonitorFactory,
IFileSystem fileSystem, IFileSystem fileSystem,
Func<IProviderManager> providerManagerFactory, Lazy<IProviderManager> providerManagerFactory,
Func<IUserViewManager> userviewManager, Lazy<IUserViewManager> userviewManagerFactory,
IMediaEncoder mediaEncoder) IMediaEncoder mediaEncoder,
IItemRepository itemRepository)
{ {
_appHost = appHost; _appHost = appHost;
_logger = loggerFactory.CreateLogger(nameof(LibraryManager)); _logger = logger;
_taskManager = taskManager; _taskManager = taskManager;
_userManager = userManager; _userManager = userManager;
ConfigurationManager = configurationManager; _configurationManager = configurationManager;
_userDataRepository = userDataRepository; _userDataRepository = userDataRepository;
_libraryMonitorFactory = libraryMonitorFactory; _libraryMonitorFactory = libraryMonitorFactory;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_providerManagerFactory = providerManagerFactory; _providerManagerFactory = providerManagerFactory;
_userviewManager = userviewManager; _userviewManagerFactory = userviewManagerFactory;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_itemRepository = itemRepository;
_libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>(); _libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated; _configurationManager.ConfigurationUpdated += ConfigurationUpdated;
RecordConfigurationValues(configurationManager.Configuration); RecordConfigurationValues(configurationManager.Configuration);
} }
@ -272,7 +243,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
private void ConfigurationUpdated(object sender, EventArgs e) private void ConfigurationUpdated(object sender, EventArgs e)
{ {
var config = ConfigurationManager.Configuration; var config = _configurationManager.Configuration;
var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted; var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted;
@ -306,7 +277,7 @@ namespace Emby.Server.Implementations.Library
} }
} }
LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; }); _libraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
} }
public void DeleteItem(BaseItem item, DeleteOptions options) public void DeleteItem(BaseItem item, DeleteOptions options)
@ -437,10 +408,10 @@ namespace Emby.Server.Implementations.Library
item.SetParent(null); item.SetParent(null);
ItemRepository.DeleteItem(item.Id); _itemRepository.DeleteItem(item.Id);
foreach (var child in children) foreach (var child in children)
{ {
ItemRepository.DeleteItem(child.Id); _itemRepository.DeleteItem(child.Id);
} }
_libraryItemsCache.TryRemove(item.Id, out BaseItem removed); _libraryItemsCache.TryRemove(item.Id, out BaseItem removed);
@ -509,15 +480,15 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(type));
} }
if (key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal)) if (key.StartsWith(_configurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal))
{ {
// Try to normalize paths located underneath program-data in an attempt to make them more portable // Try to normalize paths located underneath program-data in an attempt to make them more portable
key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length) key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
.TrimStart(new[] { '/', '\\' }) .TrimStart(new[] { '/', '\\' })
.Replace("/", "\\"); .Replace("/", "\\");
} }
if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds)
{ {
key = key.ToLowerInvariant(); key = key.ToLowerInvariant();
} }
@ -550,7 +521,7 @@ namespace Emby.Server.Implementations.Library
collectionType = GetContentTypeOverride(fullPath, true); collectionType = GetContentTypeOverride(fullPath, true);
} }
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService) var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService)
{ {
Parent = parent, Parent = parent,
Path = fullPath, Path = fullPath,
@ -720,7 +691,7 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="InvalidOperationException">Cannot create the root folder until plugins have loaded.</exception> /// <exception cref="InvalidOperationException">Cannot create the root folder until plugins have loaded.</exception>
public AggregateFolder CreateRootFolder() public AggregateFolder CreateRootFolder()
{ {
var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath; var rootFolderPath = _configurationManager.ApplicationPaths.RootFolderPath;
Directory.CreateDirectory(rootFolderPath); Directory.CreateDirectory(rootFolderPath);
@ -734,7 +705,7 @@ namespace Emby.Server.Implementations.Library
} }
// Add in the plug-in folders // Add in the plug-in folders
var path = Path.Combine(ConfigurationManager.ApplicationPaths.DataPath, "playlists"); var path = Path.Combine(_configurationManager.ApplicationPaths.DataPath, "playlists");
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
@ -786,7 +757,7 @@ namespace Emby.Server.Implementations.Library
{ {
if (_userRootFolder == null) if (_userRootFolder == null)
{ {
var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; var userRootPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
_logger.LogDebug("Creating userRootPath at {path}", userRootPath); _logger.LogDebug("Creating userRootPath at {path}", userRootPath);
Directory.CreateDirectory(userRootPath); Directory.CreateDirectory(userRootPath);
@ -980,7 +951,7 @@ namespace Emby.Server.Implementations.Library
where T : BaseItem, new() where T : BaseItem, new()
{ {
var path = getPathFn(name); var path = getPathFn(name);
var forceCaseInsensitiveId = ConfigurationManager.Configuration.EnableNormalizedItemByNameIds; var forceCaseInsensitiveId = _configurationManager.Configuration.EnableNormalizedItemByNameIds;
return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId); return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
} }
@ -994,7 +965,7 @@ namespace Emby.Server.Implementations.Library
public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress) public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
{ {
// Ensure the location is available. // Ensure the location is available.
Directory.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath); Directory.CreateDirectory(_configurationManager.ApplicationPaths.PeoplePath);
return new PeopleValidator(this, _logger, _fileSystem).ValidatePeople(cancellationToken, progress); return new PeopleValidator(this, _logger, _fileSystem).ValidatePeople(cancellationToken, progress);
} }
@ -1031,7 +1002,7 @@ namespace Emby.Server.Implementations.Library
public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken) public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
{ {
IsScanRunning = true; IsScanRunning = true;
_libraryMonitorFactory().Stop(); LibraryMonitor.Stop();
try try
{ {
@ -1039,7 +1010,7 @@ namespace Emby.Server.Implementations.Library
} }
finally finally
{ {
_libraryMonitorFactory().Start(); LibraryMonitor.Start();
IsScanRunning = false; IsScanRunning = false;
} }
} }
@ -1148,7 +1119,7 @@ namespace Emby.Server.Implementations.Library
progress.Report(percent * 100); progress.Report(percent * 100);
} }
ItemRepository.UpdateInheritedValues(cancellationToken); _itemRepository.UpdateInheritedValues(cancellationToken);
progress.Report(100); progress.Report(100);
} }
@ -1168,9 +1139,9 @@ namespace Emby.Server.Implementations.Library
var topLibraryFolders = GetUserRootFolder().Children.ToList(); var topLibraryFolders = GetUserRootFolder().Children.ToList();
_logger.LogDebug("Getting refreshQueue"); _logger.LogDebug("Getting refreshQueue");
var refreshQueue = includeRefreshState ? _providerManagerFactory().GetRefreshQueue() : null; var refreshQueue = includeRefreshState ? ProviderManager.GetRefreshQueue() : null;
return _fileSystem.GetDirectoryPaths(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath) return _fileSystem.GetDirectoryPaths(_configurationManager.ApplicationPaths.DefaultUserViewsPath)
.Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders, refreshQueue)) .Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders, refreshQueue))
.ToList(); .ToList();
} }
@ -1245,7 +1216,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentException("Guid can't be empty", nameof(id)); throw new ArgumentException("Guid can't be empty", nameof(id));
} }
if (LibraryItemsCache.TryGetValue(id, out BaseItem item)) if (_libraryItemsCache.TryGetValue(id, out BaseItem item))
{ {
return item; return item;
} }
@ -1276,7 +1247,7 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User, allowExternalContent); AddUserToQuery(query, query.User, allowExternalContent);
} }
return ItemRepository.GetItemList(query); return _itemRepository.GetItemList(query);
} }
public List<BaseItem> GetItemList(InternalItemsQuery query) public List<BaseItem> GetItemList(InternalItemsQuery query)
@ -1300,7 +1271,7 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User); AddUserToQuery(query, query.User);
} }
return ItemRepository.GetCount(query); return _itemRepository.GetCount(query);
} }
public List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents) public List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
@ -1315,7 +1286,7 @@ namespace Emby.Server.Implementations.Library
} }
} }
return ItemRepository.GetItemList(query); return _itemRepository.GetItemList(query);
} }
public QueryResult<BaseItem> QueryItems(InternalItemsQuery query) public QueryResult<BaseItem> QueryItems(InternalItemsQuery query)
@ -1327,12 +1298,12 @@ namespace Emby.Server.Implementations.Library
if (query.EnableTotalRecordCount) if (query.EnableTotalRecordCount)
{ {
return ItemRepository.GetItems(query); return _itemRepository.GetItems(query);
} }
return new QueryResult<BaseItem> return new QueryResult<BaseItem>
{ {
Items = ItemRepository.GetItemList(query).ToArray() Items = _itemRepository.GetItemList(query).ToArray()
}; };
} }
@ -1343,7 +1314,7 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User); AddUserToQuery(query, query.User);
} }
return ItemRepository.GetItemIdsList(query); return _itemRepository.GetItemIdsList(query);
} }
public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query) public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
@ -1354,7 +1325,7 @@ namespace Emby.Server.Implementations.Library
} }
SetTopParentOrAncestorIds(query); SetTopParentOrAncestorIds(query);
return ItemRepository.GetStudios(query); return _itemRepository.GetStudios(query);
} }
public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query) public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
@ -1365,7 +1336,7 @@ namespace Emby.Server.Implementations.Library
} }
SetTopParentOrAncestorIds(query); SetTopParentOrAncestorIds(query);
return ItemRepository.GetGenres(query); return _itemRepository.GetGenres(query);
} }
public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query) public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
@ -1376,7 +1347,7 @@ namespace Emby.Server.Implementations.Library
} }
SetTopParentOrAncestorIds(query); SetTopParentOrAncestorIds(query);
return ItemRepository.GetMusicGenres(query); return _itemRepository.GetMusicGenres(query);
} }
public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query) public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
@ -1387,7 +1358,7 @@ namespace Emby.Server.Implementations.Library
} }
SetTopParentOrAncestorIds(query); SetTopParentOrAncestorIds(query);
return ItemRepository.GetAllArtists(query); return _itemRepository.GetAllArtists(query);
} }
public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query) public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
@ -1398,7 +1369,7 @@ namespace Emby.Server.Implementations.Library
} }
SetTopParentOrAncestorIds(query); SetTopParentOrAncestorIds(query);
return ItemRepository.GetArtists(query); return _itemRepository.GetArtists(query);
} }
private void SetTopParentOrAncestorIds(InternalItemsQuery query) private void SetTopParentOrAncestorIds(InternalItemsQuery query)
@ -1439,7 +1410,7 @@ namespace Emby.Server.Implementations.Library
} }
SetTopParentOrAncestorIds(query); SetTopParentOrAncestorIds(query);
return ItemRepository.GetAlbumArtists(query); return _itemRepository.GetAlbumArtists(query);
} }
public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query) public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query)
@ -1460,10 +1431,10 @@ namespace Emby.Server.Implementations.Library
if (query.EnableTotalRecordCount) if (query.EnableTotalRecordCount)
{ {
return ItemRepository.GetItems(query); return _itemRepository.GetItems(query);
} }
var list = ItemRepository.GetItemList(query); var list = _itemRepository.GetItemList(query);
return new QueryResult<BaseItem> return new QueryResult<BaseItem>
{ {
@ -1509,7 +1480,7 @@ namespace Emby.Server.Implementations.Library
string.IsNullOrEmpty(query.SeriesPresentationUniqueKey) && string.IsNullOrEmpty(query.SeriesPresentationUniqueKey) &&
query.ItemIds.Length == 0) query.ItemIds.Length == 0)
{ {
var userViews = _userviewManager().GetUserViews(new UserViewQuery var userViews = UserViewManager.GetUserViews(new UserViewQuery
{ {
UserId = user.Id, UserId = user.Id,
IncludeHidden = true, IncludeHidden = true,
@ -1809,7 +1780,7 @@ namespace Emby.Server.Implementations.Library
// Don't iterate multiple times // Don't iterate multiple times
var itemsList = items.ToList(); var itemsList = items.ToList();
ItemRepository.SaveItems(itemsList, cancellationToken); _itemRepository.SaveItems(itemsList, cancellationToken);
foreach (var item in itemsList) foreach (var item in itemsList)
{ {
@ -1846,7 +1817,7 @@ namespace Emby.Server.Implementations.Library
public void UpdateImages(BaseItem item) public void UpdateImages(BaseItem item)
{ {
ItemRepository.SaveImages(item); _itemRepository.SaveImages(item);
RegisterItem(item); RegisterItem(item);
} }
@ -1863,7 +1834,7 @@ namespace Emby.Server.Implementations.Library
{ {
if (item.IsFileProtocol) if (item.IsFileProtocol)
{ {
_providerManagerFactory().SaveMetadata(item, updateReason); ProviderManager.SaveMetadata(item, updateReason);
} }
item.DateLastSaved = DateTime.UtcNow; item.DateLastSaved = DateTime.UtcNow;
@ -1871,7 +1842,7 @@ namespace Emby.Server.Implementations.Library
RegisterItem(item); RegisterItem(item);
} }
ItemRepository.SaveItems(itemsList, cancellationToken); _itemRepository.SaveItems(itemsList, cancellationToken);
if (ItemUpdated != null) if (ItemUpdated != null)
{ {
@ -1947,7 +1918,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
public BaseItem RetrieveItem(Guid id) public BaseItem RetrieveItem(Guid id)
{ {
return ItemRepository.RetrieveItem(id); return _itemRepository.RetrieveItem(id);
} }
public List<Folder> GetCollectionFolders(BaseItem item) public List<Folder> GetCollectionFolders(BaseItem item)
@ -2066,7 +2037,7 @@ namespace Emby.Server.Implementations.Library
private string GetContentTypeOverride(string path, bool inherit) private string GetContentTypeOverride(string path, bool inherit)
{ {
var nameValuePair = ConfigurationManager.Configuration.ContentTypes var nameValuePair = _configurationManager.Configuration.ContentTypes
.FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path) .FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path)
|| (inherit && !string.IsNullOrEmpty(i.Name) || (inherit && !string.IsNullOrEmpty(i.Name)
&& _fileSystem.ContainsSubPath(i.Name, path))); && _fileSystem.ContainsSubPath(i.Name, path)));
@ -2115,7 +2086,7 @@ namespace Emby.Server.Implementations.Library
string sortName) string sortName)
{ {
var path = Path.Combine( var path = Path.Combine(
ConfigurationManager.ApplicationPaths.InternalMetadataPath, _configurationManager.ApplicationPaths.InternalMetadataPath,
"views", "views",
_fileSystem.GetValidFilename(viewType)); _fileSystem.GetValidFilename(viewType));
@ -2147,7 +2118,7 @@ namespace Emby.Server.Implementations.Library
if (refresh) if (refresh)
{ {
item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
_providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal); ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
} }
return item; return item;
@ -2165,7 +2136,7 @@ namespace Emby.Server.Implementations.Library
var id = GetNewItemId(idValues, typeof(UserView)); var id = GetNewItemId(idValues, typeof(UserView));
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture)); var path = Path.Combine(_configurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
var item = GetItemById(id) as UserView; var item = GetItemById(id) as UserView;
@ -2202,7 +2173,7 @@ namespace Emby.Server.Implementations.Library
if (refresh) if (refresh)
{ {
_providerManagerFactory().QueueRefresh( ProviderManager.QueueRefresh(
item.Id, item.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem)) new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
@ -2269,7 +2240,7 @@ namespace Emby.Server.Implementations.Library
if (refresh) if (refresh)
{ {
_providerManagerFactory().QueueRefresh( ProviderManager.QueueRefresh(
item.Id, item.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem)) new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
@ -2303,7 +2274,7 @@ namespace Emby.Server.Implementations.Library
var id = GetNewItemId(idValues, typeof(UserView)); var id = GetNewItemId(idValues, typeof(UserView));
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture)); var path = Path.Combine(_configurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
var item = GetItemById(id) as UserView; var item = GetItemById(id) as UserView;
@ -2346,7 +2317,7 @@ namespace Emby.Server.Implementations.Library
if (refresh) if (refresh)
{ {
_providerManagerFactory().QueueRefresh( ProviderManager.QueueRefresh(
item.Id, item.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem)) new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
@ -2675,8 +2646,8 @@ namespace Emby.Server.Implementations.Library
} }
} }
var metadataPath = ConfigurationManager.Configuration.MetadataPath; var metadataPath = _configurationManager.Configuration.MetadataPath;
var metadataNetworkPath = ConfigurationManager.Configuration.MetadataNetworkPath; var metadataNetworkPath = _configurationManager.Configuration.MetadataNetworkPath;
if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath)) if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath))
{ {
@ -2687,7 +2658,7 @@ namespace Emby.Server.Implementations.Library
} }
} }
foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) foreach (var map in _configurationManager.Configuration.PathSubstitutions)
{ {
if (!string.IsNullOrWhiteSpace(map.From)) if (!string.IsNullOrWhiteSpace(map.From))
{ {
@ -2756,7 +2727,7 @@ namespace Emby.Server.Implementations.Library
public List<PersonInfo> GetPeople(InternalPeopleQuery query) public List<PersonInfo> GetPeople(InternalPeopleQuery query)
{ {
return ItemRepository.GetPeople(query); return _itemRepository.GetPeople(query);
} }
public List<PersonInfo> GetPeople(BaseItem item) public List<PersonInfo> GetPeople(BaseItem item)
@ -2779,7 +2750,7 @@ namespace Emby.Server.Implementations.Library
public List<Person> GetPeopleItems(InternalPeopleQuery query) public List<Person> GetPeopleItems(InternalPeopleQuery query)
{ {
return ItemRepository.GetPeopleNames(query).Select(i => return _itemRepository.GetPeopleNames(query).Select(i =>
{ {
try try
{ {
@ -2796,7 +2767,7 @@ namespace Emby.Server.Implementations.Library
public List<string> GetPeopleNames(InternalPeopleQuery query) public List<string> GetPeopleNames(InternalPeopleQuery query)
{ {
return ItemRepository.GetPeopleNames(query); return _itemRepository.GetPeopleNames(query);
} }
public void UpdatePeople(BaseItem item, List<PersonInfo> people) public void UpdatePeople(BaseItem item, List<PersonInfo> people)
@ -2806,7 +2777,7 @@ namespace Emby.Server.Implementations.Library
return; return;
} }
ItemRepository.UpdatePeople(item.Id, people); _itemRepository.UpdatePeople(item.Id, people);
} }
public async Task<ItemImageInfo> ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex) public async Task<ItemImageInfo> ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex)
@ -2817,7 +2788,7 @@ namespace Emby.Server.Implementations.Library
{ {
_logger.LogDebug("ConvertImageToLocal item {0} - image url: {1}", item.Id, url); _logger.LogDebug("ConvertImageToLocal item {0} - image url: {1}", item.Id, url);
await _providerManagerFactory().SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false); await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
@ -2850,7 +2821,7 @@ namespace Emby.Server.Implementations.Library
name = _fileSystem.GetValidFilename(name); name = _fileSystem.GetValidFilename(name);
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, name); var virtualFolderPath = Path.Combine(rootFolderPath, name);
while (Directory.Exists(virtualFolderPath)) while (Directory.Exists(virtualFolderPath))
@ -2869,7 +2840,7 @@ namespace Emby.Server.Implementations.Library
} }
} }
_libraryMonitorFactory().Stop(); LibraryMonitor.Stop();
try try
{ {
@ -2904,7 +2875,7 @@ namespace Emby.Server.Implementations.Library
{ {
// Need to add a delay here or directory watchers may still pick up the changes // Need to add a delay here or directory watchers may still pick up the changes
await Task.Delay(1000).ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false);
_libraryMonitorFactory().Start(); LibraryMonitor.Start();
} }
} }
} }
@ -2964,7 +2935,7 @@ namespace Emby.Server.Implementations.Library
throw new FileNotFoundException("The network path does not exist."); throw new FileNotFoundException("The network path does not exist.");
} }
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
var shortcutFilename = Path.GetFileNameWithoutExtension(path); var shortcutFilename = Path.GetFileNameWithoutExtension(path);
@ -3007,7 +2978,7 @@ namespace Emby.Server.Implementations.Library
throw new FileNotFoundException("The network path does not exist."); throw new FileNotFoundException("The network path does not exist.");
} }
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath);
@ -3060,7 +3031,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(name)); throw new ArgumentNullException(nameof(name));
} }
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var path = Path.Combine(rootFolderPath, name); var path = Path.Combine(rootFolderPath, name);
@ -3069,7 +3040,7 @@ namespace Emby.Server.Implementations.Library
throw new FileNotFoundException("The media folder does not exist"); throw new FileNotFoundException("The media folder does not exist");
} }
_libraryMonitorFactory().Stop(); LibraryMonitor.Stop();
try try
{ {
@ -3089,7 +3060,7 @@ namespace Emby.Server.Implementations.Library
{ {
// Need to add a delay here or directory watchers may still pick up the changes // Need to add a delay here or directory watchers may still pick up the changes
await Task.Delay(1000).ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false);
_libraryMonitorFactory().Start(); LibraryMonitor.Start();
} }
} }
} }
@ -3103,7 +3074,7 @@ namespace Emby.Server.Implementations.Library
var removeList = new List<NameValuePair>(); var removeList = new List<NameValuePair>();
foreach (var contentType in ConfigurationManager.Configuration.ContentTypes) foreach (var contentType in _configurationManager.Configuration.ContentTypes)
{ {
if (string.IsNullOrWhiteSpace(contentType.Name)) if (string.IsNullOrWhiteSpace(contentType.Name))
{ {
@ -3118,11 +3089,11 @@ namespace Emby.Server.Implementations.Library
if (removeList.Count > 0) if (removeList.Count > 0)
{ {
ConfigurationManager.Configuration.ContentTypes = ConfigurationManager.Configuration.ContentTypes _configurationManager.Configuration.ContentTypes = _configurationManager.Configuration.ContentTypes
.Except(removeList) .Except(removeList)
.ToArray(); .ToArray();
ConfigurationManager.SaveConfiguration(); _configurationManager.SaveConfiguration();
} }
} }
@ -3133,7 +3104,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(mediaPath)); throw new ArgumentNullException(nameof(mediaPath));
} }
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
if (!Directory.Exists(virtualFolderPath)) if (!Directory.Exists(virtualFolderPath))

View file

@ -33,13 +33,13 @@ namespace Emby.Server.Implementations.Library
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private IMediaSourceProvider[] _providers;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly Func<IMediaEncoder> _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private ILocalizationManager _localizationManager; private readonly ILocalizationManager _localizationManager;
private IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private IMediaSourceProvider[] _providers;
public MediaSourceManager( public MediaSourceManager(
IItemRepository itemRepo, IItemRepository itemRepo,
@ -47,16 +47,16 @@ namespace Emby.Server.Implementations.Library
ILocalizationManager localizationManager, ILocalizationManager localizationManager,
IUserManager userManager, IUserManager userManager,
ILibraryManager libraryManager, ILibraryManager libraryManager,
ILoggerFactory loggerFactory, ILogger<MediaSourceManager> logger,
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
IFileSystem fileSystem, IFileSystem fileSystem,
IUserDataManager userDataManager, IUserDataManager userDataManager,
Func<IMediaEncoder> mediaEncoder) IMediaEncoder mediaEncoder)
{ {
_itemRepo = itemRepo; _itemRepo = itemRepo;
_userManager = userManager; _userManager = userManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = loggerFactory.CreateLogger(nameof(MediaSourceManager)); _logger = logger;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_userDataManager = userDataManager; _userDataManager = userDataManager;
@ -496,7 +496,7 @@ namespace Emby.Server.Implementations.Library
// hack - these two values were taken from LiveTVMediaSourceProvider // hack - these two values were taken from LiveTVMediaSourceProvider
string cacheKey = request.OpenToken; string cacheKey = request.OpenToken;
await new LiveStreamHelper(_mediaEncoder(), _logger, _jsonSerializer, _appPaths) await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _appPaths)
.AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken) .AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -621,7 +621,7 @@ namespace Emby.Server.Implementations.Library
if (liveStreamInfo is IDirectStreamProvider) if (liveStreamInfo is IDirectStreamProvider)
{ {
var info = await _mediaEncoder().GetMediaInfo(new MediaInfoRequest var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
{ {
MediaSource = mediaSource, MediaSource = mediaSource,
ExtractChapters = false, ExtractChapters = false,
@ -674,7 +674,7 @@ namespace Emby.Server.Implementations.Library
mediaSource.AnalyzeDurationMs = 3000; mediaSource.AnalyzeDurationMs = 3000;
} }
mediaInfo = await _mediaEncoder().GetMediaInfo(new MediaInfoRequest mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
{ {
MediaSource = mediaSource, MediaSource = mediaSource,
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video, MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,

View file

@ -17,16 +17,15 @@ namespace Emby.Server.Implementations.Library
{ {
public class SearchEngine : ISearchEngine public class SearchEngine : ISearchEngine
{ {
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly ILogger _logger;
public SearchEngine(ILoggerFactory loggerFactory, ILibraryManager libraryManager, IUserManager userManager) public SearchEngine(ILogger<SearchEngine> logger, ILibraryManager libraryManager, IUserManager userManager)
{ {
_logger = logger;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_userManager = userManager; _userManager = userManager;
_logger = loggerFactory.CreateLogger("SearchEngine");
} }
public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query) public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)

View file

@ -28,25 +28,24 @@ namespace Emby.Server.Implementations.Library
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager;
private readonly IUserDataRepository _repository;
private Func<IUserManager> _userManager; public UserDataManager(
ILogger<UserDataManager> logger,
public UserDataManager(ILoggerFactory loggerFactory, IServerConfigurationManager config, Func<IUserManager> userManager) IServerConfigurationManager config,
IUserManager userManager,
IUserDataRepository repository)
{ {
_logger = logger;
_config = config; _config = config;
_logger = loggerFactory.CreateLogger(GetType().Name);
_userManager = userManager; _userManager = userManager;
_repository = repository;
} }
/// <summary>
/// Gets or sets the repository.
/// </summary>
/// <value>The repository.</value>
public IUserDataRepository Repository { get; set; }
public void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken) public void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
{ {
var user = _userManager().GetUserById(userId); var user = _userManager.GetUserById(userId);
SaveUserData(user, item, userData, reason, cancellationToken); SaveUserData(user, item, userData, reason, cancellationToken);
} }
@ -71,7 +70,7 @@ namespace Emby.Server.Implementations.Library
foreach (var key in keys) foreach (var key in keys)
{ {
Repository.SaveUserData(userId, key, userData, cancellationToken); _repository.SaveUserData(userId, key, userData, cancellationToken);
} }
var cacheKey = GetCacheKey(userId, item.Id); var cacheKey = GetCacheKey(userId, item.Id);
@ -96,9 +95,9 @@ namespace Emby.Server.Implementations.Library
/// <returns></returns> /// <returns></returns>
public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken) public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken)
{ {
var user = _userManager().GetUserById(userId); var user = _userManager.GetUserById(userId);
Repository.SaveAllUserData(user.InternalId, userData, cancellationToken); _repository.SaveAllUserData(user.InternalId, userData, cancellationToken);
} }
/// <summary> /// <summary>
@ -108,14 +107,14 @@ namespace Emby.Server.Implementations.Library
/// <returns></returns> /// <returns></returns>
public List<UserItemData> GetAllUserData(Guid userId) public List<UserItemData> GetAllUserData(Guid userId)
{ {
var user = _userManager().GetUserById(userId); var user = _userManager.GetUserById(userId);
return Repository.GetAllUserData(user.InternalId); return _repository.GetAllUserData(user.InternalId);
} }
public UserItemData GetUserData(Guid userId, Guid itemId, List<string> keys) public UserItemData GetUserData(Guid userId, Guid itemId, List<string> keys)
{ {
var user = _userManager().GetUserById(userId); var user = _userManager.GetUserById(userId);
return GetUserData(user, itemId, keys); return GetUserData(user, itemId, keys);
} }
@ -131,7 +130,7 @@ namespace Emby.Server.Implementations.Library
private UserItemData GetUserDataInternal(long internalUserId, List<string> keys) private UserItemData GetUserDataInternal(long internalUserId, List<string> keys)
{ {
var userData = Repository.GetUserData(internalUserId, keys); var userData = _repository.GetUserData(internalUserId, keys);
if (userData != null) if (userData != null)
{ {

View file

@ -20,6 +20,7 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -44,22 +45,14 @@ namespace Emby.Server.Implementations.Library
{ {
private readonly object _policySyncLock = new object(); private readonly object _policySyncLock = new object();
private readonly object _configSyncLock = new object(); private readonly object _configSyncLock = new object();
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger _logger;
/// <summary> private readonly ILogger _logger;
/// Gets the active user repository.
/// </summary>
/// <value>The user repository.</value>
private readonly IUserRepository _userRepository; private readonly IUserRepository _userRepository;
private readonly IXmlSerializer _xmlSerializer; private readonly IXmlSerializer _xmlSerializer;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly INetworkManager _networkManager; private readonly INetworkManager _networkManager;
private readonly IImageProcessor _imageProcessor;
private readonly Func<IImageProcessor> _imageProcessorFactory; private readonly Lazy<IDtoService> _dtoServiceFactory;
private readonly Func<IDtoService> _dtoServiceFactory;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ICryptoProvider _cryptoProvider; private readonly ICryptoProvider _cryptoProvider;
@ -74,13 +67,15 @@ namespace Emby.Server.Implementations.Library
private IPasswordResetProvider[] _passwordResetProviders; private IPasswordResetProvider[] _passwordResetProviders;
private DefaultPasswordResetProvider _defaultPasswordResetProvider; private DefaultPasswordResetProvider _defaultPasswordResetProvider;
private IDtoService DtoService => _dtoServiceFactory.Value;
public UserManager( public UserManager(
ILogger<UserManager> logger, ILogger<UserManager> logger,
IUserRepository userRepository, IUserRepository userRepository,
IXmlSerializer xmlSerializer, IXmlSerializer xmlSerializer,
INetworkManager networkManager, INetworkManager networkManager,
Func<IImageProcessor> imageProcessorFactory, IImageProcessor imageProcessor,
Func<IDtoService> dtoServiceFactory, Lazy<IDtoService> dtoServiceFactory,
IServerApplicationHost appHost, IServerApplicationHost appHost,
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
IFileSystem fileSystem, IFileSystem fileSystem,
@ -90,7 +85,7 @@ namespace Emby.Server.Implementations.Library
_userRepository = userRepository; _userRepository = userRepository;
_xmlSerializer = xmlSerializer; _xmlSerializer = xmlSerializer;
_networkManager = networkManager; _networkManager = networkManager;
_imageProcessorFactory = imageProcessorFactory; _imageProcessor = imageProcessor;
_dtoServiceFactory = dtoServiceFactory; _dtoServiceFactory = dtoServiceFactory;
_appHost = appHost; _appHost = appHost;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
@ -327,23 +322,19 @@ namespace Emby.Server.Implementations.Library
if (user.Policy.IsDisabled) if (user.Policy.IsDisabled)
{ {
_logger.LogInformation("Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).", username, remoteEndPoint); _logger.LogInformation("Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).", username, remoteEndPoint);
throw new AuthenticationException( throw new SecurityException($"The {user.Name} account is currently disabled. Please consult with your administrator.");
string.Format(
CultureInfo.InvariantCulture,
"The {0} account is currently disabled. Please consult with your administrator.",
user.Name));
} }
if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint)) if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
{ {
_logger.LogInformation("Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).", username, remoteEndPoint); _logger.LogInformation("Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).", username, remoteEndPoint);
throw new AuthenticationException("Forbidden."); throw new SecurityException("Forbidden.");
} }
if (!user.IsParentalScheduleAllowed()) if (!user.IsParentalScheduleAllowed())
{ {
_logger.LogInformation("Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", username, remoteEndPoint); _logger.LogInformation("Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", username, remoteEndPoint);
throw new AuthenticationException("User is not allowed access at this time."); throw new SecurityException("User is not allowed access at this time.");
} }
// Update LastActivityDate and LastLoginDate, then save // Update LastActivityDate and LastLoginDate, then save
@ -605,7 +596,7 @@ namespace Emby.Server.Implementations.Library
try try
{ {
_dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user); DtoService.AttachPrimaryImageAspectRatio(dto, user);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -630,7 +621,7 @@ namespace Emby.Server.Implementations.Library
{ {
try try
{ {
return _imageProcessorFactory().GetImageCacheTag(item, image); return _imageProcessor.GetImageCacheTag(item, image);
} }
catch (Exception ex) catch (Exception ex)
{ {

View file

@ -28,7 +28,6 @@ namespace Emby.Server.Implementations.LiveTv
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IImageProcessor _imageProcessor; private readonly IImageProcessor _imageProcessor;
private readonly IDtoService _dtoService; private readonly IDtoService _dtoService;
private readonly IApplicationHost _appHost; private readonly IApplicationHost _appHost;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;

View file

@ -49,29 +49,24 @@ namespace Emby.Server.Implementations.LiveTv
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly IDtoService _dtoService;
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ITaskManager _taskManager; private readonly ITaskManager _taskManager;
private readonly IJsonSerializer _jsonSerializer;
private readonly Func<IChannelManager> _channelManager;
private readonly IDtoService _dtoService;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
private readonly IChannelManager _channelManager;
private readonly LiveTvDtoService _tvDtoService; private readonly LiveTvDtoService _tvDtoService;
private ILiveTvService[] _services = Array.Empty<ILiveTvService>(); private ILiveTvService[] _services = Array.Empty<ILiveTvService>();
private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>(); private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>(); private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
private readonly IFileSystem _fileSystem;
public LiveTvManager( public LiveTvManager(
IServerApplicationHost appHost,
IServerConfigurationManager config, IServerConfigurationManager config,
ILoggerFactory loggerFactory, ILogger<LiveTvManager> logger,
IItemRepository itemRepo, IItemRepository itemRepo,
IImageProcessor imageProcessor,
IUserDataManager userDataManager, IUserDataManager userDataManager,
IDtoService dtoService, IDtoService dtoService,
IUserManager userManager, IUserManager userManager,
@ -80,10 +75,11 @@ namespace Emby.Server.Implementations.LiveTv
ILocalizationManager localization, ILocalizationManager localization,
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
IFileSystem fileSystem, IFileSystem fileSystem,
Func<IChannelManager> channelManager) IChannelManager channelManager,
LiveTvDtoService liveTvDtoService)
{ {
_config = config; _config = config;
_logger = loggerFactory.CreateLogger(nameof(LiveTvManager)); _logger = logger;
_itemRepo = itemRepo; _itemRepo = itemRepo;
_userManager = userManager; _userManager = userManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
@ -94,8 +90,7 @@ namespace Emby.Server.Implementations.LiveTv
_dtoService = dtoService; _dtoService = dtoService;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_channelManager = channelManager; _channelManager = channelManager;
_tvDtoService = liveTvDtoService;
_tvDtoService = new LiveTvDtoService(dtoService, imageProcessor, loggerFactory.CreateLogger<LiveTvDtoService>(), appHost, _libraryManager);
} }
public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled; public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
@ -2496,7 +2491,7 @@ namespace Emby.Server.Implementations.LiveTv
.OrderBy(i => i.SortName) .OrderBy(i => i.SortName)
.ToList(); .ToList();
folders.AddRange(_channelManager().GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery folders.AddRange(_channelManager.GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery
{ {
UserId = user.Id, UserId = user.Id,
IsRecordingsFolder = true, IsRecordingsFolder = true,

View file

@ -23,7 +23,7 @@
"HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه", "HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه",
"HeaderFavoriteShows": "سریال‌های مورد علاقه", "HeaderFavoriteShows": "سریال‌های مورد علاقه",
"HeaderFavoriteSongs": "آهنگ‌های مورد علاقه", "HeaderFavoriteSongs": "آهنگ‌های مورد علاقه",
"HeaderLiveTV": "تلویزیون زنده", "HeaderLiveTV": "پخش زنده",
"HeaderNextUp": "قسمت بعدی", "HeaderNextUp": "قسمت بعدی",
"HeaderRecordingGroups": "گروه‌های ضبط", "HeaderRecordingGroups": "گروه‌های ضبط",
"HomeVideos": "ویدیوهای خانگی", "HomeVideos": "ویدیوهای خانگی",

View file

@ -104,13 +104,14 @@
"TasksMaintenanceCategory": "メンテナンス", "TasksMaintenanceCategory": "メンテナンス",
"TaskRefreshChannelsDescription": "ネットチャンネルの情報をリフレッシュします。", "TaskRefreshChannelsDescription": "ネットチャンネルの情報をリフレッシュします。",
"TaskRefreshChannels": "チャンネルのリフレッシュ", "TaskRefreshChannels": "チャンネルのリフレッシュ",
"TaskCleanTranscodeDescription": "一日以上前のトランスコードを消去します。", "TaskCleanTranscodeDescription": "1日以上経過したトランスコードファイルを削除します。",
"TaskCleanTranscode": "トランスコード用のディレクトリの掃除", "TaskCleanTranscode": "トランスコードディレクトリの削除",
"TaskUpdatePluginsDescription": "自動更新可能なプラグインのアップデートをダウンロードしてインストールします。", "TaskUpdatePluginsDescription": "自動更新可能なプラグインのアップデートをダウンロードしてインストールします。",
"TaskUpdatePlugins": "プラグインの更新", "TaskUpdatePlugins": "プラグインの更新",
"TaskRefreshPeopleDescription": "メディアライブラリで俳優や監督のメタデータをリフレッシュします。", "TaskRefreshPeopleDescription": "メディアライブラリで俳優や監督のメタデータを更新します。",
"TaskRefreshPeople": "俳優や監督のデータのリフレッシュ", "TaskRefreshPeople": "俳優や監督のデータの更新",
"TaskDownloadMissingSubtitlesDescription": "メタデータ構成に基づいて、欠落している字幕をインターネットで検索します。", "TaskDownloadMissingSubtitlesDescription": "メタデータ構成に基づいて、欠落している字幕をインターネットで検索します。",
"TaskRefreshChapterImagesDescription": "チャプターのあるビデオのサムネイルを作成します。", "TaskRefreshChapterImagesDescription": "チャプターのあるビデオのサムネイルを作成します。",
"TaskRefreshChapterImages": "チャプター画像を抽出する" "TaskRefreshChapterImages": "チャプター画像を抽出する",
"TaskDownloadMissingSubtitles": "不足している字幕をダウンロードする"
} }

View file

@ -1,11 +1,11 @@
{ {
"Albums": "Albums", "Albums": "Albums",
"AppDeviceValues": "App: {0}, Apparaat: {1}", "AppDeviceValues": "App: {0}, Apparaat: {1}",
"Application": "Applicatie", "Application": "Programma",
"Artists": "Artiesten", "Artists": "Artiesten",
"AuthenticationSucceededWithUserName": "{0} succesvol geauthenticeerd", "AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd",
"Books": "Boeken", "Books": "Boeken",
"CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd van {0}", "CameraImageUploadedFrom": "Er is een nieuwe afbeelding toegevoegd via {0}",
"Channels": "Kanalen", "Channels": "Kanalen",
"ChapterNameValue": "Hoofdstuk {0}", "ChapterNameValue": "Hoofdstuk {0}",
"Collections": "Verzamelingen", "Collections": "Verzamelingen",

View file

@ -26,7 +26,7 @@
"HeaderLiveTV": "TV em Direto", "HeaderLiveTV": "TV em Direto",
"HeaderNextUp": "A Seguir", "HeaderNextUp": "A Seguir",
"HeaderRecordingGroups": "Grupos de Gravação", "HeaderRecordingGroups": "Grupos de Gravação",
"HomeVideos": "Home videos", "HomeVideos": "Videos caseiros",
"Inherit": "Herdar", "Inherit": "Herdar",
"ItemAddedWithName": "{0} foi adicionado à biblioteca", "ItemAddedWithName": "{0} foi adicionado à biblioteca",
"ItemRemovedWithName": "{0} foi removido da biblioteca", "ItemRemovedWithName": "{0} foi removido da biblioteca",
@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}", "UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}",
"ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia", "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia",
"ValueSpecialEpisodeName": "Especial - {0}", "ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versão {0}" "VersionNumber": "Versão {0}",
"TaskDownloadMissingSubtitlesDescription": "Procurar na internet por legendas em falta baseado na configuração de metadados.",
"TaskDownloadMissingSubtitles": "Fazer download de legendas em falta",
"TaskRefreshChannelsDescription": "Atualizar informação sobre canais da Internet.",
"TaskRefreshChannels": "Atualizar Canais",
"TaskCleanTranscodeDescription": "Apagar ficheiros de transcode com mais de um dia.",
"TaskCleanTranscode": "Limpar a Diretoria de Transcode",
"TaskUpdatePluginsDescription": "Faz o download e instala updates para os plugins que estão configurados para atualizar automaticamente.",
"TaskUpdatePlugins": "Atualizar Plugins",
"TaskRefreshPeopleDescription": "Atualizar metadados para atores e diretores na biblioteca.",
"TaskRefreshPeople": "Atualizar Pessoas",
"TaskCleanLogsDescription": "Apagar ficheiros de log que têm mais de {0} dias.",
"TaskCleanLogs": "Limpar a Diretoria de Logs",
"TaskRefreshLibraryDescription": "Scannear a biblioteca de música para novos ficheiros e atualizar os metadados.",
"TaskRefreshLibrary": "Scannear Biblioteca de Música",
"TaskRefreshChapterImagesDescription": "Criar thumbnails para os vídeos que têm capítulos.",
"TaskRefreshChapterImages": "Extrair Imagens dos Capítulos",
"TaskCleanCacheDescription": "Apagar ficheiros em cache que já não são necessários.",
"TaskCleanCache": "Limpar Cache",
"TasksChannelsCategory": "Canais da Internet",
"TasksApplicationCategory": "Aplicação",
"TasksLibraryCategory": "Biblioteca",
"TasksMaintenanceCategory": "Manutenção"
} }

View file

@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Ses çalma başladı", "NotificationOptionAudioPlayback": "Ses çalma başladı",
"NotificationOptionAudioPlaybackStopped": "Ses çalma durduruldu", "NotificationOptionAudioPlaybackStopped": "Ses çalma durduruldu",
"NotificationOptionCameraImageUploaded": "Kamera fotoğrafı yüklendi", "NotificationOptionCameraImageUploaded": "Kamera fotoğrafı yüklendi",
"NotificationOptionInstallationFailed": "Yükleme başarısız oldu", "NotificationOptionInstallationFailed": "Kurulum hatası",
"NotificationOptionNewLibraryContent": "Yeni içerik eklendi", "NotificationOptionNewLibraryContent": "Yeni içerik eklendi",
"NotificationOptionPluginError": "Eklenti hatası", "NotificationOptionPluginError": "Eklenti hatası",
"NotificationOptionPluginInstalled": "Eklenti yüklendi", "NotificationOptionPluginInstalled": "Eklenti yüklendi",
@ -95,7 +95,24 @@
"VersionNumber": "Versiyon {0}", "VersionNumber": "Versiyon {0}",
"TaskCleanCache": "Geçici dosya klasörünü temizle", "TaskCleanCache": "Geçici dosya klasörünü temizle",
"TasksChannelsCategory": "İnternet kanalları", "TasksChannelsCategory": "İnternet kanalları",
"TasksApplicationCategory": "Yazılım", "TasksApplicationCategory": "Uygulama",
"TasksLibraryCategory": "Kütüphane", "TasksLibraryCategory": "Kütüphane",
"TasksMaintenanceCategory": "Onarım" "TasksMaintenanceCategory": "Onarım",
"TaskRefreshPeopleDescription": "Medya kütüphanenizdeki videoların oyuncu ve yönetmen bilgilerini günceller.",
"TaskDownloadMissingSubtitlesDescription": "Metadata ayarlarını baz alarak eksik altyazıları internette arar.",
"TaskDownloadMissingSubtitles": "Eksik altyazıları indir",
"TaskRefreshChannelsDescription": "Internet kanal bilgilerini yenile.",
"TaskRefreshChannels": "Kanalları Yenile",
"TaskCleanTranscodeDescription": "Bir günü dolmuş dönüştürme bilgisi içeren dosyaları siler.",
"TaskCleanTranscode": "Dönüşüm Dizinini Temizle",
"TaskUpdatePluginsDescription": "Otomatik güncellenmeye ayarlanmış eklentilerin güncellemelerini indirir ve kurar.",
"TaskUpdatePlugins": "Eklentileri Güncelle",
"TaskRefreshPeople": "Kullanıcıları Yenile",
"TaskCleanLogsDescription": "{0} günden eski log dosyalarını siler.",
"TaskCleanLogs": "Log Dizinini Temizle",
"TaskRefreshLibraryDescription": "Medya kütüphanenize eklenen yeni dosyaları arar ve bilgileri yeniler.",
"TaskRefreshLibrary": "Medya Kütüphanesini Tara",
"TaskRefreshChapterImagesDescription": "Sahnelere ayrılmış videolar için küçük resimler oluştur.",
"TaskRefreshChapterImages": "Bölüm Resimlerini Çıkar",
"TaskCleanCacheDescription": "Sistem tarafından artık ihtiyaç duyulmayan önbellek dosyalarını siler."
} }

View file

@ -50,10 +50,10 @@
"NotificationOptionCameraImageUploaded": "相機相片已上傳", "NotificationOptionCameraImageUploaded": "相機相片已上傳",
"NotificationOptionInstallationFailed": "安裝失敗", "NotificationOptionInstallationFailed": "安裝失敗",
"NotificationOptionNewLibraryContent": "已新增新內容", "NotificationOptionNewLibraryContent": "已新增新內容",
"NotificationOptionPluginError": "擴充元件錯誤", "NotificationOptionPluginError": "插件安裝錯誤",
"NotificationOptionPluginInstalled": "擴充元件已安裝", "NotificationOptionPluginInstalled": "件已安裝",
"NotificationOptionPluginUninstalled": "擴充元件已移除", "NotificationOptionPluginUninstalled": "件已移除",
"NotificationOptionPluginUpdateInstalled": "已更新擴充元件", "NotificationOptionPluginUpdateInstalled": "插件已更新",
"NotificationOptionServerRestartRequired": "伺服器需要重新啟動", "NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
"NotificationOptionTaskFailed": "排程任務失敗", "NotificationOptionTaskFailed": "排程任務失敗",
"NotificationOptionUserLockedOut": "使用者已鎖定", "NotificationOptionUserLockedOut": "使用者已鎖定",
@ -61,7 +61,7 @@
"NotificationOptionVideoPlaybackStopped": "影片停止播放", "NotificationOptionVideoPlaybackStopped": "影片停止播放",
"Photos": "相片", "Photos": "相片",
"Playlists": "播放清單", "Playlists": "播放清單",
"Plugin": "外掛", "Plugin": "插件",
"PluginInstalledWithName": "{0} 已安裝", "PluginInstalledWithName": "{0} 已安裝",
"PluginUninstalledWithName": "{0} 已移除", "PluginUninstalledWithName": "{0} 已移除",
"PluginUpdatedWithName": "{0} 已更新", "PluginUpdatedWithName": "{0} 已更新",
@ -91,5 +91,27 @@
"VersionNumber": "版本 {0}", "VersionNumber": "版本 {0}",
"HeaderRecordingGroups": "錄製組", "HeaderRecordingGroups": "錄製組",
"Inherit": "繼承", "Inherit": "繼承",
"SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕" "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕",
"TaskDownloadMissingSubtitlesDescription": "在網路上透過描述資料搜尋遺失的字幕。",
"TaskDownloadMissingSubtitles": "下載遺失的字幕",
"TaskRefreshChannels": "重新整理頻道",
"TaskUpdatePlugins": "更新插件",
"TaskRefreshPeople": "重新整理人員",
"TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔案。",
"TaskCleanLogs": "清空紀錄資料夾",
"TaskRefreshLibraryDescription": "掃描媒體庫內新的檔案並重新整理描述資料。",
"TaskRefreshLibrary": "掃描媒體庫",
"TaskRefreshChapterImages": "擷取章節圖片",
"TaskCleanCacheDescription": "刪除系統長時間不需要的快取。",
"TaskCleanCache": "清除快取資料夾",
"TasksLibraryCategory": "媒體庫",
"TaskRefreshChannelsDescription": "重新整理網絡頻道資料。",
"TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。",
"TaskCleanTranscode": "清除轉碼資料夾",
"TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。",
"TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。",
"TaskRefreshChapterImagesDescription": "為有章節的視頻創建縮圖。",
"TasksChannelsCategory": "網絡頻道",
"TasksApplicationCategory": "應用程式",
"TasksMaintenanceCategory": "維修"
} }

View file

@ -23,9 +23,6 @@ namespace Emby.Server.Implementations.Localization
private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly;
private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
/// <summary>
/// The _configuration manager.
/// </summary>
private readonly IServerConfigurationManager _configurationManager; private readonly IServerConfigurationManager _configurationManager;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly ILogger _logger; private readonly ILogger _logger;

View file

@ -32,22 +32,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue = private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
new ConcurrentQueue<Tuple<Type, TaskOptions>>(); new ConcurrentQueue<Tuple<Type, TaskOptions>>();
/// <summary>
/// Gets or sets the json serializer.
/// </summary>
/// <value>The json serializer.</value>
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
/// <summary>
/// Gets or sets the application paths.
/// </summary>
/// <value>The application paths.</value>
private readonly IApplicationPaths _applicationPaths; private readonly IApplicationPaths _applicationPaths;
/// <summary>
/// Gets the logger.
/// </summary>
/// <value>The logger.</value>
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
@ -56,17 +42,17 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary> /// </summary>
/// <param name="applicationPaths">The application paths.</param> /// <param name="applicationPaths">The application paths.</param>
/// <param name="jsonSerializer">The json serializer.</param> /// <param name="jsonSerializer">The json serializer.</param>
/// <param name="loggerFactory">The logger factory.</param> /// <param name="logger">The logger.</param>
/// <param name="fileSystem">The filesystem manager.</param> /// <param name="fileSystem">The filesystem manager.</param>
public TaskManager( public TaskManager(
IApplicationPaths applicationPaths, IApplicationPaths applicationPaths,
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
ILoggerFactory loggerFactory, ILogger<TaskManager> logger,
IFileSystem fileSystem) IFileSystem fileSystem)
{ {
_applicationPaths = applicationPaths; _applicationPaths = applicationPaths;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_logger = loggerFactory.CreateLogger(nameof(TaskManager)); _logger = logger;
_fileSystem = fileSystem; _fileSystem = fileSystem;
ScheduledTasks = Array.Empty<IScheduledTaskWorker>(); ScheduledTasks = Array.Empty<IScheduledTaskWorker>();

View file

@ -15,8 +15,8 @@ namespace Emby.Server.Implementations.Security
{ {
public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository
{ {
public AuthenticationRepository(ILoggerFactory loggerFactory, IServerConfigurationManager config) public AuthenticationRepository(ILogger<AuthenticationRepository> logger, IServerConfigurationManager config)
: base(loggerFactory.CreateLogger(nameof(AuthenticationRepository))) : base(logger)
{ {
DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "authentication.db"); DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "authentication.db");
} }

View file

@ -1414,7 +1414,7 @@ namespace Emby.Server.Implementations.Session
if (user == null) if (user == null)
{ {
AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request)); AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
throw new SecurityException("Invalid username or password entered."); throw new AuthenticationException("Invalid username or password entered.");
} }
if (!string.IsNullOrEmpty(request.DeviceId) if (!string.IsNullOrEmpty(request.DeviceId)

View file

@ -26,7 +26,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Updates namespace Emby.Server.Implementations.Updates
{ {
/// <summary> /// <summary>
/// Manages all install, uninstall and update operations (both plugins and system). /// Manages all install, uninstall, and update operations for the system and individual plugins.
/// </summary> /// </summary>
public class InstallationManager : IInstallationManager public class InstallationManager : IInstallationManager
{ {
@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Updates
public const string PluginManifestUrlKey = "InstallationManager:PluginManifestUrl"; public const string PluginManifestUrlKey = "InstallationManager:PluginManifestUrl";
/// <summary> /// <summary>
/// The _logger. /// The logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
@ -112,10 +112,10 @@ namespace Emby.Server.Implementations.Updates
public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled; public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated; public event EventHandler<GenericEventArgs<(IPlugin, VersionInfo)>> PluginUpdated;
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled; public event EventHandler<GenericEventArgs<VersionInfo>> PluginInstalled;
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal; public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal;
@ -183,61 +183,56 @@ namespace Emby.Server.Implementations.Updates
} }
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<PackageVersionInfo> GetCompatibleVersions( public IEnumerable<VersionInfo> GetCompatibleVersions(
IEnumerable<PackageVersionInfo> availableVersions, IEnumerable<VersionInfo> availableVersions,
Version minVersion = null, Version minVersion = null)
PackageVersionClass classification = PackageVersionClass.Release)
{ {
var appVer = _applicationHost.ApplicationVersion; var appVer = _applicationHost.ApplicationVersion;
availableVersions = availableVersions availableVersions = availableVersions
.Where(x => x.classification == classification .Where(x => Version.Parse(x.targetAbi) <= appVer);
&& Version.Parse(x.requiredVersionStr) <= appVer);
if (minVersion != null) if (minVersion != null)
{ {
availableVersions = availableVersions.Where(x => x.Version >= minVersion); availableVersions = availableVersions.Where(x => x.version >= minVersion);
} }
return availableVersions.OrderByDescending(x => x.Version); return availableVersions.OrderByDescending(x => x.version);
} }
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<PackageVersionInfo> GetCompatibleVersions( public IEnumerable<VersionInfo> GetCompatibleVersions(
IEnumerable<PackageInfo> availablePackages, IEnumerable<PackageInfo> availablePackages,
string name = null, string name = null,
Guid guid = default, Guid guid = default,
Version minVersion = null, Version minVersion = null)
PackageVersionClass classification = PackageVersionClass.Release)
{ {
var package = FilterPackages(availablePackages, name, guid).FirstOrDefault(); var package = FilterPackages(availablePackages, name, guid).FirstOrDefault();
// Package not found. // Package not found in repository
if (package == null) if (package == null)
{ {
return Enumerable.Empty<PackageVersionInfo>(); return Enumerable.Empty<VersionInfo>();
} }
return GetCompatibleVersions( return GetCompatibleVersions(
package.versions, package.versions,
minVersion, minVersion);
classification);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default) public async Task<IEnumerable<VersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default)
{ {
var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false); var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false);
return GetAvailablePluginUpdates(catalog); return GetAvailablePluginUpdates(catalog);
} }
private IEnumerable<PackageVersionInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog) private IEnumerable<VersionInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog)
{ {
foreach (var plugin in _applicationHost.Plugins) foreach (var plugin in _applicationHost.Plugins)
{ {
var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version, _applicationHost.SystemUpdateLevel); var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version);
var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version); var version = compatibleversions.FirstOrDefault(y => y.version > plugin.Version);
if (version != null if (version != null && !CompletedInstallations.Any(x => string.Equals(x.Guid, version.guid, StringComparison.OrdinalIgnoreCase)))
&& !CompletedInstallations.Any(x => string.Equals(x.AssemblyGuid, version.guid, StringComparison.OrdinalIgnoreCase)))
{ {
yield return version; yield return version;
} }
@ -245,7 +240,7 @@ namespace Emby.Server.Implementations.Updates
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken) public async Task InstallPackage(VersionInfo package, CancellationToken cancellationToken)
{ {
if (package == null) if (package == null)
{ {
@ -254,11 +249,9 @@ namespace Emby.Server.Implementations.Updates
var installationInfo = new InstallationInfo var installationInfo = new InstallationInfo
{ {
Id = Guid.NewGuid(), Guid = package.guid,
Name = package.name, Name = package.name,
AssemblyGuid = package.guid, Version = package.version.ToString()
UpdateClass = package.classification,
Version = package.versionStr
}; };
var innerCancellationTokenSource = new CancellationTokenSource(); var innerCancellationTokenSource = new CancellationTokenSource();
@ -276,7 +269,7 @@ namespace Emby.Server.Implementations.Updates
var installationEventArgs = new InstallationEventArgs var installationEventArgs = new InstallationEventArgs
{ {
InstallationInfo = installationInfo, InstallationInfo = installationInfo,
PackageVersionInfo = package VersionInfo = package
}; };
PackageInstalling?.Invoke(this, installationEventArgs); PackageInstalling?.Invoke(this, installationEventArgs);
@ -301,7 +294,7 @@ namespace Emby.Server.Implementations.Updates
_currentInstallations.Remove(tuple); _currentInstallations.Remove(tuple);
} }
_logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.versionStr); _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.version);
PackageInstallationCancelled?.Invoke(this, installationEventArgs); PackageInstallationCancelled?.Invoke(this, installationEventArgs);
@ -337,7 +330,7 @@ namespace Emby.Server.Implementations.Updates
/// <param name="package">The package.</param> /// <param name="package">The package.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="Task" />.</returns> /// <returns><see cref="Task" />.</returns>
private async Task InstallPackageInternal(PackageVersionInfo package, CancellationToken cancellationToken) private async Task InstallPackageInternal(VersionInfo package, CancellationToken cancellationToken)
{ {
// Set last update time if we were installed before // Set last update time if we were installed before
IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase)) IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
@ -349,26 +342,26 @@ namespace Emby.Server.Implementations.Updates
// Do plugin-specific processing // Do plugin-specific processing
if (plugin == null) if (plugin == null)
{ {
_logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification); _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.version);
PluginInstalled?.Invoke(this, new GenericEventArgs<PackageVersionInfo>(package)); PluginInstalled?.Invoke(this, new GenericEventArgs<VersionInfo>(package));
} }
else else
{ {
_logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification); _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.version);
PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, PackageVersionInfo)>((plugin, package))); PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, VersionInfo)>((plugin, package)));
} }
_applicationHost.NotifyPendingRestart(); _applicationHost.NotifyPendingRestart();
} }
private async Task PerformPackageInstallation(PackageVersionInfo package, CancellationToken cancellationToken) private async Task PerformPackageInstallation(VersionInfo package, CancellationToken cancellationToken)
{ {
var extension = Path.GetExtension(package.targetFilename); var extension = Path.GetExtension(package.filename);
if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase)) if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
{ {
_logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename); _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.filename);
return; return;
} }
@ -415,7 +408,7 @@ namespace Emby.Server.Implementations.Updates
} }
/// <summary> /// <summary>
/// Uninstalls a plugin /// Uninstalls a plugin.
/// </summary> /// </summary>
/// <param name="plugin">The plugin.</param> /// <param name="plugin">The plugin.</param>
public void UninstallPlugin(IPlugin plugin) public void UninstallPlugin(IPlugin plugin)
@ -473,7 +466,7 @@ namespace Emby.Server.Implementations.Updates
{ {
lock (_currentInstallationsLock) lock (_currentInstallationsLock)
{ {
var install = _currentInstallations.Find(x => x.info.Id == id); var install = _currentInstallations.Find(x => x.info.Guid == id.ToString());
if (install == default((InstallationInfo, CancellationTokenSource))) if (install == default((InstallationInfo, CancellationTokenSource)))
{ {
return false; return false;

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{DFBEFB4C-DA19-4143-98B7-27320C7F7163}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>

View file

@ -1,10 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{154872D9-6C12-4007-96E3-8F70A58386CE}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -26,7 +26,7 @@ namespace Jellyfin.Drawing.Skia
{ {
paint.Color = SKColor.Parse("#CC00A4DC"); paint.Color = SKColor.Parse("#CC00A4DC");
paint.Style = SKPaintStyle.Fill; paint.Style = SKPaintStyle.Fill;
canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint); canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
} }
using (var paint = new SKPaint()) using (var paint = new SKPaint())
@ -39,16 +39,13 @@ namespace Jellyfin.Drawing.Skia
// or: // or:
// var emojiChar = 0x1F680; // var emojiChar = 0x1F680;
var text = "✔️"; const string Text = "✔️";
var emojiChar = StringUtilities.GetUnicodeCharacterCode(text, SKTextEncoding.Utf32); var emojiChar = StringUtilities.GetUnicodeCharacterCode(Text, SKTextEncoding.Utf32);
// ask the font manager for a font with that character // ask the font manager for a font with that character
var fontManager = SKFontManager.Default; paint.Typeface = SKFontManager.Default.MatchCharacter(emojiChar);
var emojiTypeface = fontManager.MatchCharacter(emojiChar);
paint.Typeface = emojiTypeface; canvas.DrawText(Text, (float)x - 20, OffsetFromTopRightCorner + 12, paint);
canvas.DrawText(text, (float)x - 20, OffsetFromTopRightCorner + 12, paint);
} }
} }
} }

View file

@ -78,12 +78,21 @@ namespace Jellyfin.Drawing.Skia
=> new HashSet<ImageFormat>() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png }; => new HashSet<ImageFormat>() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
/// <summary> /// <summary>
/// Test to determine if the native lib is available. /// Check if the native lib is available.
/// </summary> /// </summary>
public static void TestSkia() /// <returns>True if the native lib is available, otherwise false.</returns>
public static bool IsNativeLibAvailable()
{ {
// test an operation that requires the native library try
SKPMColor.PreMultiply(SKColors.Black); {
// test an operation that requires the native library
SKPMColor.PreMultiply(SKColors.Black);
return true;
}
catch (Exception)
{
return false;
}
} }
private static bool IsTransparent(SKColor color) private static bool IsTransparent(SKColor color)
@ -205,11 +214,6 @@ namespace Jellyfin.Drawing.Skia
/// <exception cref="SkiaCodecException">The file at the specified path could not be used to generate a codec.</exception> /// <exception cref="SkiaCodecException">The file at the specified path could not be used to generate a codec.</exception>
public ImageDimensions GetImageSize(string path) public ImageDimensions GetImageSize(string path)
{ {
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (!File.Exists(path)) if (!File.Exists(path))
{ {
throw new FileNotFoundException("File not found", path); throw new FileNotFoundException("File not found", path);
@ -297,7 +301,7 @@ namespace Jellyfin.Drawing.Skia
/// <param name="orientation">The orientation of the image.</param> /// <param name="orientation">The orientation of the image.</param>
/// <param name="origin">The detected origin of the image.</param> /// <param name="origin">The detected origin of the image.</param>
/// <returns>The resulting bitmap of the image.</returns> /// <returns>The resulting bitmap of the image.</returns>
internal SKBitmap Decode(string path, bool forceCleanBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin) internal SKBitmap? Decode(string path, bool forceCleanBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin)
{ {
if (!File.Exists(path)) if (!File.Exists(path))
{ {
@ -348,12 +352,17 @@ namespace Jellyfin.Drawing.Skia
return resultBitmap; return resultBitmap;
} }
private SKBitmap GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin) private SKBitmap? GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin)
{ {
if (cropWhitespace) if (cropWhitespace)
{ {
using (var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin)) using (var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin))
{ {
if (bitmap == null)
{
return null;
}
return CropWhiteSpace(bitmap); return CropWhiteSpace(bitmap);
} }
} }
@ -361,13 +370,11 @@ namespace Jellyfin.Drawing.Skia
return Decode(path, forceAnalyzeBitmap, orientation, out origin); return Decode(path, forceAnalyzeBitmap, orientation, out origin);
} }
private SKBitmap GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation) private SKBitmap? GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation)
{ {
SKEncodedOrigin origin;
if (autoOrient) if (autoOrient)
{ {
var bitmap = GetBitmap(path, cropWhitespace, true, orientation, out origin); var bitmap = GetBitmap(path, cropWhitespace, true, orientation, out var origin);
if (bitmap != null && origin != SKEncodedOrigin.TopLeft) if (bitmap != null && origin != SKEncodedOrigin.TopLeft)
{ {
@ -380,7 +387,7 @@ namespace Jellyfin.Drawing.Skia
return bitmap; return bitmap;
} }
return GetBitmap(path, cropWhitespace, false, orientation, out origin); return GetBitmap(path, cropWhitespace, false, orientation, out _);
} }
private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin) private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin)
@ -517,14 +524,14 @@ namespace Jellyfin.Drawing.Skia
/// <inheritdoc/> /// <inheritdoc/>
public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
{ {
if (string.IsNullOrWhiteSpace(inputPath)) if (inputPath.Length == 0)
{ {
throw new ArgumentNullException(nameof(inputPath)); throw new ArgumentException("String can't be empty.", nameof(inputPath));
} }
if (string.IsNullOrWhiteSpace(inputPath)) if (outputPath.Length == 0)
{ {
throw new ArgumentNullException(nameof(outputPath)); throw new ArgumentException("String can't be empty.", nameof(outputPath));
} }
var skiaOutputFormat = GetImageFormat(selectedOutputFormat); var skiaOutputFormat = GetImageFormat(selectedOutputFormat);
@ -538,7 +545,7 @@ namespace Jellyfin.Drawing.Skia
{ {
if (bitmap == null) if (bitmap == null)
{ {
throw new ArgumentOutOfRangeException($"Skia unable to read image {inputPath}"); throw new InvalidDataException($"Skia unable to read image {inputPath}");
} }
var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height); var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height);

View file

@ -120,13 +120,13 @@ namespace Jellyfin.Drawing.Skia
} }
// resize to the same aspect as the original // resize to the same aspect as the original
int iWidth = (int)Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height); int iWidth = Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType)) using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
{ {
currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High); currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
// crop image // crop image
int ix = (int)Math.Abs((iWidth - iSlice) / 2); int ix = Math.Abs((iWidth - iSlice) / 2);
using (var image = SKImage.FromBitmap(resizeBitmap)) using (var image = SKImage.FromBitmap(resizeBitmap))
using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight))) using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight)))
{ {
@ -141,10 +141,10 @@ namespace Jellyfin.Drawing.Skia
return bitmap; return bitmap;
} }
private SKBitmap GetNextValidImage(string[] paths, int currentIndex, out int newIndex) private SKBitmap? GetNextValidImage(string[] paths, int currentIndex, out int newIndex)
{ {
var imagesTested = new Dictionary<int, int>(); var imagesTested = new Dictionary<int, int>();
SKBitmap bitmap = null; SKBitmap? bitmap = null;
while (imagesTested.Count < paths.Length) while (imagesTested.Count < paths.Length)
{ {
@ -153,7 +153,7 @@ namespace Jellyfin.Drawing.Skia
currentIndex = 0; currentIndex = 0;
} }
bitmap = _skiaEncoder.Decode(paths[currentIndex], false, null, out var origin); bitmap = _skiaEncoder.Decode(paths[currentIndex], false, null, out _);
imagesTested[currentIndex] = 0; imagesTested[currentIndex] = 0;

View file

@ -32,7 +32,7 @@ namespace Jellyfin.Drawing.Skia
{ {
paint.Color = SKColor.Parse("#CC00A4DC"); paint.Color = SKColor.Parse("#CC00A4DC");
paint.Style = SKPaintStyle.Fill; paint.Style = SKPaintStyle.Fill;
canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint); canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
} }
using (var paint = new SKPaint()) using (var paint = new SKPaint())
@ -61,7 +61,7 @@ namespace Jellyfin.Drawing.Skia
paint.TextSize = 18; paint.TextSize = 18;
} }
canvas.DrawText(text, (float)x, y, paint); canvas.DrawText(text, x, y, paint);
} }
} }
} }

View file

@ -1,9 +1,13 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using Emby.Drawing;
using Emby.Server.Implementations; using Emby.Server.Implementations;
using Jellyfin.Drawing.Skia;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Jellyfin.Server namespace Jellyfin.Server
@ -20,27 +24,40 @@ namespace Jellyfin.Server
/// <param name="loggerFactory">The <see cref="ILoggerFactory" /> to be used by the <see cref="CoreAppHost" />.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param> /// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param> /// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="imageEncoder">The <see cref="IImageEncoder" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param> /// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param>
public CoreAppHost( public CoreAppHost(
ServerApplicationPaths applicationPaths, ServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
StartupOptions options, StartupOptions options,
IFileSystem fileSystem, IFileSystem fileSystem,
IImageEncoder imageEncoder,
INetworkManager networkManager) INetworkManager networkManager)
: base( : base(
applicationPaths, applicationPaths,
loggerFactory, loggerFactory,
options, options,
fileSystem, fileSystem,
imageEncoder,
networkManager) networkManager)
{ {
} }
/// <inheritdoc /> /// <inheritdoc/>
public override bool CanSelfRestart => StartupOptions.RestartPath != null; protected override void RegisterServices(IServiceCollection serviceCollection)
{
// Register an image encoder
bool useSkiaEncoder = SkiaEncoder.IsNativeLibAvailable();
Type imageEncoderType = useSkiaEncoder
? typeof(SkiaEncoder)
: typeof(NullImageEncoder);
serviceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType);
// Log a warning if the Skia encoder could not be used
if (!useSkiaEncoder)
{
Logger.LogWarning($"Skia not available. Will fallback to {nameof(NullImageEncoder)}.");
}
base.RegisterServices(serviceCollection);
}
/// <inheritdoc /> /// <inheritdoc />
protected override void RestartInternal() => Program.Restart(); protected override void RestartInternal() => Program.Restart();

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{07E39F42-A2C6-4B32-AF8C-725F957A73FF}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<AssemblyName>jellyfin</AssemblyName> <AssemblyName>jellyfin</AssemblyName>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>

View file

@ -168,7 +168,6 @@ namespace Jellyfin.Server
_loggerFactory, _loggerFactory,
options, options,
new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths), new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
GetImageEncoder(appPaths),
new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>())); new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()));
try try
@ -188,14 +187,13 @@ namespace Jellyfin.Server
} }
ServiceCollection serviceCollection = new ServiceCollection(); ServiceCollection serviceCollection = new ServiceCollection();
await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false); appHost.Init(serviceCollection);
var webHost = new WebHostBuilder().ConfigureWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build(); var webHost = new WebHostBuilder().ConfigureWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build();
// Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection. // Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection.
appHost.ServiceProvider = webHost.Services; appHost.ServiceProvider = webHost.Services;
appHost.InitializeServices(); await appHost.InitializeServices().ConfigureAwait(false);
appHost.FindParts();
Migrations.MigrationRunner.Run(appHost, _loggerFactory); Migrations.MigrationRunner.Run(appHost, _loggerFactory);
try try
@ -598,25 +596,6 @@ namespace Jellyfin.Server
} }
} }
private static IImageEncoder GetImageEncoder(IApplicationPaths appPaths)
{
try
{
// Test if the native lib is available
SkiaEncoder.TestSkia();
return new SkiaEncoder(
_loggerFactory.CreateLogger<SkiaEncoder>(),
appPaths);
}
catch (Exception ex)
{
_logger.LogWarning(ex, $"Skia not available. Will fallback to {nameof(NullImageEncoder)}.");
}
return new NullImageEncoder();
}
private static void StartNewInstance(StartupOptions options) private static void StartNewInstance(StartupOptions options)
{ {
_logger.LogInformation("Starting new instance"); _logger.LogInformation("Starting new instance");

View file

@ -332,7 +332,8 @@ namespace MediaBrowser.Api.Images
var fileInfo = _fileSystem.GetFileInfo(info.Path); var fileInfo = _fileSystem.GetFileInfo(info.Path);
length = fileInfo.Length; length = fileInfo.Length;
ImageDimensions size = _imageProcessor.GetImageDimensions(item, info, true); ImageDimensions size = _imageProcessor.GetImageDimensions(item, info);
_libraryManager.UpdateImages(item);
width = size.Width; width = size.Width;
height = size.Height; height = size.Height;
@ -606,6 +607,12 @@ namespace MediaBrowser.Api.Images
IDictionary<string, string> headers, IDictionary<string, string> headers,
bool isHeadRequest) bool isHeadRequest)
{ {
if (!image.IsLocalFile)
{
item ??= _libraryManager.GetItemById(itemId);
image = await _libraryManager.ConvertImageToLocal(item, image, request.Index ?? 0).ConfigureAwait(false);
}
var options = new ImageProcessingOptions var options = new ImageProcessingOptions
{ {
CropWhiteSpace = cropwhitespace, CropWhiteSpace = cropwhitespace,

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</ProjectGuid>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />

View file

@ -42,23 +42,6 @@ namespace MediaBrowser.Api
[Authenticated] [Authenticated]
public class GetPackages : IReturn<PackageInfo[]> public class GetPackages : IReturn<PackageInfo[]>
{ {
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "PackageType", Description = "Optional package type filter (System/UserInstalled)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string PackageType { get; set; }
[ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string TargetSystems { get; set; }
[ApiMember(Name = "IsPremium", Description = "Optional. Filter by premium status", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? IsPremium { get; set; }
[ApiMember(Name = "IsAdult", Description = "Optional. Filter by package that contain adult content.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? IsAdult { get; set; }
public bool? IsAppStoreEnabled { get; set; }
} }
/// <summary> /// <summary>
@ -88,13 +71,6 @@ namespace MediaBrowser.Api
/// <value>The version.</value> /// <value>The version.</value>
[ApiMember(Name = "Version", Description = "Optional version. Defaults to latest version.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] [ApiMember(Name = "Version", Description = "Optional version. Defaults to latest version.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Version { get; set; } public string Version { get; set; }
/// <summary>
/// Gets or sets the update class.
/// </summary>
/// <value>The update class.</value>
[ApiMember(Name = "UpdateClass", Description = "Optional update class (Dev, Beta, Release). Defaults to Release.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public PackageVersionClass UpdateClass { get; set; }
} }
/// <summary> /// <summary>
@ -154,23 +130,6 @@ namespace MediaBrowser.Api
{ {
IEnumerable<PackageInfo> packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false); IEnumerable<PackageInfo> packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
if (!string.IsNullOrEmpty(request.TargetSystems))
{
var apps = request.TargetSystems.Split(',').Select(i => (PackageTargetSystem)Enum.Parse(typeof(PackageTargetSystem), i, true));
packages = packages.Where(p => apps.Contains(p.targetSystem));
}
if (request.IsAdult.HasValue)
{
packages = packages.Where(p => p.adult == request.IsAdult.Value);
}
if (request.IsAppStoreEnabled.HasValue)
{
packages = packages.Where(p => p.enableInAppStore == request.IsAppStoreEnabled.Value);
}
return ToOptimizedResult(packages.ToArray()); return ToOptimizedResult(packages.ToArray());
} }
@ -186,8 +145,7 @@ namespace MediaBrowser.Api
packages, packages,
request.Name, request.Name,
string.IsNullOrEmpty(request.AssemblyGuid) ? Guid.Empty : Guid.Parse(request.AssemblyGuid), string.IsNullOrEmpty(request.AssemblyGuid) ? Guid.Empty : Guid.Parse(request.AssemblyGuid),
string.IsNullOrEmpty(request.Version) ? null : Version.Parse(request.Version), string.IsNullOrEmpty(request.Version) ? null : Version.Parse(request.Version)).FirstOrDefault();
request.UpdateClass).FirstOrDefault();
if (package == null) if (package == null)
{ {

View file

@ -426,7 +426,7 @@ namespace MediaBrowser.Api
catch (SecurityException e) catch (SecurityException e)
{ {
// rethrow adding IP address to message // rethrow adding IP address to message
throw new SecurityException($"[{Request.RemoteIp}] {e.Message}"); throw new SecurityException($"[{Request.RemoteIp}] {e.Message}", e);
} }
} }

View file

@ -1,3 +1,5 @@
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View file

@ -2,8 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Updates;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common namespace MediaBrowser.Common
@ -48,12 +46,6 @@ namespace MediaBrowser.Common
/// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
bool CanSelfRestart { get; } bool CanSelfRestart { get; }
/// <summary>
/// Gets the version class of the system.
/// </summary>
/// <value><see cref="PackageVersionClass.Release" /> or <see cref="PackageVersionClass.Beta" />.</value>
PackageVersionClass SystemUpdateLevel { get; }
/// <summary> /// <summary>
/// Gets the application version. /// Gets the application version.
/// </summary> /// </summary>
@ -125,9 +117,7 @@ namespace MediaBrowser.Common
/// Initializes this instance. /// Initializes this instance.
/// </summary> /// </summary>
/// <param name="serviceCollection">The service collection.</param> /// <param name="serviceCollection">The service collection.</param>
/// <param name="startupConfig">The configuration to use for initialization.</param> void Init(IServiceCollection serviceCollection);
/// <returns>A task.</returns>
Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig);
/// <summary> /// <summary>
/// Creates the instance. /// Creates the instance.

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<Authors>Jellyfin Contributors</Authors> <Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Common</PackageId> <PackageId>Jellyfin.Common</PackageId>

View file

@ -67,7 +67,7 @@ namespace MediaBrowser.Common.Plugins
} }
/// <summary> /// <summary>
/// Called when just before the plugin is uninstalled from the server. /// Called just before the plugin is uninstalled from the server.
/// </summary> /// </summary>
public virtual void OnUninstalling() public virtual void OnUninstalling()
{ {
@ -101,7 +101,7 @@ namespace MediaBrowser.Common.Plugins
private readonly object _configurationSyncLock = new object(); private readonly object _configurationSyncLock = new object();
/// <summary> /// <summary>
/// The save lock. /// The configuration save lock.
/// </summary> /// </summary>
private readonly object _configurationSaveLock = new object(); private readonly object _configurationSaveLock = new object();
@ -148,7 +148,7 @@ namespace MediaBrowser.Common.Plugins
protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath); protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath);
/// <summary> /// <summary>
/// Gets or sets the plugin's configuration. /// Gets or sets the plugin configuration.
/// </summary> /// </summary>
/// <value>The configuration.</value> /// <value>The configuration.</value>
public TConfigurationType Configuration public TConfigurationType Configuration
@ -186,7 +186,7 @@ namespace MediaBrowser.Common.Plugins
public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName); public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
/// <summary> /// <summary>
/// Gets the plugin's configuration. /// Gets the plugin configuration.
/// </summary> /// </summary>
/// <value>The configuration.</value> /// <value>The configuration.</value>
BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration; BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration;

View file

@ -28,12 +28,12 @@ namespace MediaBrowser.Common.Updates
/// <summary> /// <summary>
/// Occurs when a plugin is updated. /// Occurs when a plugin is updated.
/// </summary> /// </summary>
event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated; event EventHandler<GenericEventArgs<(IPlugin, VersionInfo)>> PluginUpdated;
/// <summary> /// <summary>
/// Occurs when a plugin is installed. /// Occurs when a plugin is installed.
/// </summary> /// </summary>
event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled; event EventHandler<GenericEventArgs<VersionInfo>> PluginInstalled;
/// <summary> /// <summary>
/// Gets the completed installations. /// Gets the completed installations.
@ -64,12 +64,10 @@ namespace MediaBrowser.Common.Updates
/// </summary> /// </summary>
/// <param name="availableVersions">The available version of the plugin.</param> /// <param name="availableVersions">The available version of the plugin.</param>
/// <param name="minVersion">The minimum required version of the plugin.</param> /// <param name="minVersion">The minimum required version of the plugin.</param>
/// <param name="classification">The classification of updates.</param>
/// <returns>All compatible versions ordered from newest to oldest.</returns> /// <returns>All compatible versions ordered from newest to oldest.</returns>
IEnumerable<PackageVersionInfo> GetCompatibleVersions( IEnumerable<VersionInfo> GetCompatibleVersions(
IEnumerable<PackageVersionInfo> availableVersions, IEnumerable<VersionInfo> availableVersions,
Version minVersion = null, Version minVersion = null);
PackageVersionClass classification = PackageVersionClass.Release);
/// <summary> /// <summary>
/// Returns all compatible versions ordered from newest to oldest. /// Returns all compatible versions ordered from newest to oldest.
@ -78,21 +76,19 @@ namespace MediaBrowser.Common.Updates
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="guid">The guid of the plugin.</param> /// <param name="guid">The guid of the plugin.</param>
/// <param name="minVersion">The minimum required version of the plugin.</param> /// <param name="minVersion">The minimum required version of the plugin.</param>
/// <param name="classification">The classification.</param>
/// <returns>All compatible versions ordered from newest to oldest.</returns> /// <returns>All compatible versions ordered from newest to oldest.</returns>
IEnumerable<PackageVersionInfo> GetCompatibleVersions( IEnumerable<VersionInfo> GetCompatibleVersions(
IEnumerable<PackageInfo> availablePackages, IEnumerable<PackageInfo> availablePackages,
string name = null, string name = null,
Guid guid = default, Guid guid = default,
Version minVersion = null, Version minVersion = null);
PackageVersionClass classification = PackageVersionClass.Release);
/// <summary> /// <summary>
/// Returns the available plugin updates. /// Returns the available plugin updates.
/// </summary> /// </summary>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The available plugin updates.</returns> /// <returns>The available plugin updates.</returns>
Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default); Task<IEnumerable<VersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Installs the package. /// Installs the package.
@ -100,7 +96,7 @@ namespace MediaBrowser.Common.Updates
/// <param name="package">The package.</param> /// <param name="package">The package.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="Task" />.</returns> /// <returns><see cref="Task" />.</returns>
Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken = default); Task InstallPackage(VersionInfo package, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Uninstalls a plugin. /// Uninstalls a plugin.

View file

@ -8,6 +8,6 @@ namespace MediaBrowser.Common.Updates
{ {
public InstallationInfo InstallationInfo { get; set; } public InstallationInfo InstallationInfo { get; set; }
public PackageVersionInfo PackageVersionInfo { get; set; } public VersionInfo VersionInfo { get; set; }
} }
} }

View file

@ -7,23 +7,29 @@ namespace MediaBrowser.Controller.Authentication
/// </summary> /// </summary>
public class AuthenticationException : Exception public class AuthenticationException : Exception
{ {
/// <inheritdoc /> /// <summary>
/// Initializes a new instance of the <see cref="AuthenticationException"/> class.
/// </summary>
public AuthenticationException() : base() public AuthenticationException() : base()
{ {
} }
/// <inheritdoc /> /// <summary>
/// Initializes a new instance of the <see cref="AuthenticationException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public AuthenticationException(string message) : base(message) public AuthenticationException(string message) : base(message)
{ {
} }
/// <inheritdoc /> /// <summary>
/// Initializes a new instance of the <see cref="AuthenticationException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
public AuthenticationException(string message, Exception innerException) public AuthenticationException(string message, Exception innerException)
: base(message, innerException) : base(message, innerException)
{ {
} }
} }
} }

View file

@ -40,15 +40,6 @@ namespace MediaBrowser.Controller.Drawing
/// <returns>ImageDimensions</returns> /// <returns>ImageDimensions</returns>
ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info); ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info);
/// <summary>
/// Gets the dimensions of the image.
/// </summary>
/// <param name="item">The base item.</param>
/// <param name="info">The information.</param>
/// <param name="updateItem">Whether or not the item info should be updated.</param>
/// <returns>ImageDimensions</returns>
ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem);
/// <summary> /// <summary>
/// Gets the image cache tag. /// Gets the image cache tag.
/// </summary> /// </summary>

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<Authors>Jellyfin Contributors</Authors> <Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Controller</PackageId> <PackageId>Jellyfin.Controller</PackageId>

View file

@ -459,7 +459,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions); var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions);
var outputVideoCodec = GetVideoEncoder(state, encodingOptions); var outputVideoCodec = GetVideoEncoder(state, encodingOptions);
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
if (!hasTextSubs) if (!hasTextSubs)

View file

@ -2,20 +2,36 @@ using System;
namespace MediaBrowser.Controller.Net namespace MediaBrowser.Controller.Net
{ {
/// <summary>
/// The exception that is thrown when a user is authenticated, but not authorized to access a requested resource.
/// </summary>
public class SecurityException : Exception public class SecurityException : Exception
{ {
/// <summary>
/// Initializes a new instance of the <see cref="SecurityException"/> class.
/// </summary>
public SecurityException()
: base()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SecurityException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public SecurityException(string message) public SecurityException(string message)
: base(message) : base(message)
{ {
} }
public SecurityExceptionType SecurityExceptionType { get; set; } /// <summary>
} /// Initializes a new instance of the <see cref="SecurityException"/> class.
/// </summary>
public enum SecurityExceptionType /// <param name="message">The message that describes the error</param>
{ /// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
Unauthenticated = 0, public SecurityException(string message, Exception innerException)
ParentalControl = 1 : base(message, innerException)
{
}
} }
} }

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}</ProjectGuid>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />

View file

@ -19,7 +19,6 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Diagnostics; using System.Diagnostics;
@ -39,8 +38,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly IServerConfigurationManager _configurationManager; private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly Func<ISubtitleEncoder> _subtitleEncoder; private readonly Lazy<EncodingHelper> _encodingHelperFactory;
private readonly IConfiguration _configuration;
private readonly string _startupOptionFFmpegPath; private readonly string _startupOptionFFmpegPath;
private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2); private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
@ -48,8 +46,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly object _runningProcessesLock = new object(); private readonly object _runningProcessesLock = new object();
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>(); private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
private EncodingHelper _encodingHelper;
private string _ffmpegPath; private string _ffmpegPath;
private string _ffprobePath; private string _ffprobePath;
@ -58,23 +54,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
IServerConfigurationManager configurationManager, IServerConfigurationManager configurationManager,
IFileSystem fileSystem, IFileSystem fileSystem,
ILocalizationManager localization, ILocalizationManager localization,
Func<ISubtitleEncoder> subtitleEncoder, Lazy<EncodingHelper> encodingHelperFactory,
IConfiguration configuration,
string startupOptionsFFmpegPath) string startupOptionsFFmpegPath)
{ {
_logger = logger; _logger = logger;
_configurationManager = configurationManager; _configurationManager = configurationManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_localization = localization; _localization = localization;
_encodingHelperFactory = encodingHelperFactory;
_startupOptionFFmpegPath = startupOptionsFFmpegPath; _startupOptionFFmpegPath = startupOptionsFFmpegPath;
_subtitleEncoder = subtitleEncoder;
_configuration = configuration;
} }
private EncodingHelper EncodingHelper private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
=> LazyInitializer.EnsureInitialized(
ref _encodingHelper,
() => new EncodingHelper(this, _fileSystem, _subtitleEncoder(), _configuration));
/// <inheritdoc /> /// <inheritdoc />
public string EncoderPath => _ffmpegPath; public string EncoderPath => _ffmpegPath;

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{960295EE-4AF4-4440-A525-B4C295B01A61}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<Authors>Jellyfin Contributors</Authors> <Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Model</PackageId> <PackageId>Jellyfin.Model</PackageId>

View file

@ -8,17 +8,17 @@ namespace MediaBrowser.Model.Services
{ {
/// <summary> /// <summary>
/// Order in which Request Filters are executed. /// Order in which Request Filters are executed.
/// &lt;0 Executed before global request filters /// &lt;0 Executed before global request filters.
/// &gt;0 Executed after global request filters /// &gt;0 Executed after global request filters.
/// </summary> /// </summary>
int Priority { get; } int Priority { get; }
/// <summary> /// <summary>
/// The request filter is executed before the service. /// The request filter is executed before the service.
/// </summary> /// </summary>
/// <param name="req">The http request wrapper</param> /// <param name="req">The http request wrapper.</param>
/// <param name="res">The http response wrapper</param> /// <param name="res">The http response wrapper.</param>
/// <param name="requestDto">The request DTO</param> /// <param name="requestDto">The request DTO.</param>
void RequestFilter(IRequest req, HttpResponse res, object requestDto); void RequestFilter(IRequest req, HttpResponse res, object requestDto);
} }
} }

View file

@ -26,8 +26,6 @@ namespace MediaBrowser.Model.System
/// </summary> /// </summary>
public class SystemInfo : PublicSystemInfo public class SystemInfo : PublicSystemInfo
{ {
public PackageVersionClass SystemUpdateLevel { get; set; }
/// <summary> /// <summary>
/// Gets or sets the display name of the operating system. /// Gets or sets the display name of the operating system.
/// </summary> /// </summary>

View file

@ -1,29 +0,0 @@
namespace MediaBrowser.Model.Updates
{
/// <summary>
/// Class CheckForUpdateResult.
/// </summary>
public class CheckForUpdateResult
{
/// <summary>
/// Gets or sets a value indicating whether this instance is update available.
/// </summary>
/// <value><c>true</c> if this instance is update available; otherwise, <c>false</c>.</value>
public bool IsUpdateAvailable { get; set; }
/// <summary>
/// Gets or sets the available version.
/// </summary>
/// <value>The available version.</value>
public string AvailableVersion
{
get => Package != null ? Package.versionStr : "0.0.0.1";
set { } // need this for the serializer
}
/// <summary>
/// Get or sets package information for an available update
/// </summary>
public PackageVersionInfo Package { get; set; }
}
}

View file

@ -8,10 +8,10 @@ namespace MediaBrowser.Model.Updates
public class InstallationInfo public class InstallationInfo
{ {
/// <summary> /// <summary>
/// Gets or sets the id. /// Gets or sets the guid.
/// </summary> /// </summary>
/// <value>The id.</value> /// <value>The guid.</value>
public Guid Id { get; set; } public string Guid { get; set; }
/// <summary> /// <summary>
/// Gets or sets the name. /// Gets or sets the name.
@ -19,22 +19,10 @@ namespace MediaBrowser.Model.Updates
/// <value>The name.</value> /// <value>The name.</value>
public string Name { get; set; } public string Name { get; set; }
/// <summary>
/// Gets or sets the assembly guid.
/// </summary>
/// <value>The guid of the assembly.</value>
public string AssemblyGuid { get; set; }
/// <summary> /// <summary>
/// Gets or sets the version. /// Gets or sets the version.
/// </summary> /// </summary>
/// <value>The version.</value> /// <value>The version.</value>
public string Version { get; set; } public string Version { get; set; }
/// <summary>
/// Gets or sets the update class.
/// </summary>
/// <value>The update class.</value>
public PackageVersionClass UpdateClass { get; set; }
} }
} }

View file

@ -8,12 +8,6 @@ namespace MediaBrowser.Model.Updates
/// </summary> /// </summary>
public class PackageInfo public class PackageInfo
{ {
/// <summary>
/// The internal id of this package.
/// </summary>
/// <value>The id.</value>
public string id { get; set; }
/// <summary> /// <summary>
/// Gets or sets the name. /// Gets or sets the name.
/// </summary> /// </summary>
@ -21,59 +15,17 @@ namespace MediaBrowser.Model.Updates
public string name { get; set; } public string name { get; set; }
/// <summary> /// <summary>
/// Gets or sets the short description. /// Gets or sets a long description of the plugin containing features or helpful explanations.
/// </summary> /// </summary>
/// <value>The short description.</value> /// <value>The description.</value>
public string shortDescription { get; set; } public string description { get; set; }
/// <summary> /// <summary>
/// Gets or sets the overview. /// Gets or sets a short overview of what the plugin does.
/// </summary> /// </summary>
/// <value>The overview.</value> /// <value>The overview.</value>
public string overview { get; set; } public string overview { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is premium.
/// </summary>
/// <value><c>true</c> if this instance is premium; otherwise, <c>false</c>.</value>
public bool isPremium { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is adult only content.
/// </summary>
/// <value><c>true</c> if this instance is adult; otherwise, <c>false</c>.</value>
public bool adult { get; set; }
/// <summary>
/// Gets or sets the rich desc URL.
/// </summary>
/// <value>The rich desc URL.</value>
public string richDescUrl { get; set; }
/// <summary>
/// Gets or sets the thumb image.
/// </summary>
/// <value>The thumb image.</value>
public string thumbImage { get; set; }
/// <summary>
/// Gets or sets the preview image.
/// </summary>
/// <value>The preview image.</value>
public string previewImage { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public string type { get; set; }
/// <summary>
/// Gets or sets the target filename.
/// </summary>
/// <value>The target filename.</value>
public string targetFilename { get; set; }
/// <summary> /// <summary>
/// Gets or sets the owner. /// Gets or sets the owner.
/// </summary> /// </summary>
@ -87,90 +39,24 @@ namespace MediaBrowser.Model.Updates
public string category { get; set; } public string category { get; set; }
/// <summary> /// <summary>
/// Gets or sets the catalog tile color. /// The guid of the assembly associated with this plugin.
/// </summary>
/// <value>The owner.</value>
public string tileColor { get; set; }
/// <summary>
/// Gets or sets the feature id of this package (if premium).
/// </summary>
/// <value>The feature id.</value>
public string featureId { get; set; }
/// <summary>
/// Gets or sets the registration info for this package (if premium).
/// </summary>
/// <value>The registration info.</value>
public string regInfo { get; set; }
/// <summary>
/// Gets or sets the price for this package (if premium).
/// </summary>
/// <value>The price.</value>
public float price { get; set; }
/// <summary>
/// Gets or sets the target system for this plug-in (Server, MBTheater, MBClassic).
/// </summary>
/// <value>The target system.</value>
public PackageTargetSystem targetSystem { get; set; }
/// <summary>
/// The guid of the assembly associated with this package (if a plug-in).
/// This is used to identify the proper item for automatic updates. /// This is used to identify the proper item for automatic updates.
/// </summary> /// </summary>
/// <value>The name.</value> /// <value>The name.</value>
public string guid { get; set; } public string guid { get; set; }
/// <summary>
/// Gets or sets the total number of ratings for this package.
/// </summary>
/// <value>The total ratings.</value>
public int? totalRatings { get; set; }
/// <summary>
/// Gets or sets the average rating for this package .
/// </summary>
/// <value>The rating.</value>
public float avgRating { get; set; }
/// <summary>
/// Gets or sets whether or not this package is registered.
/// </summary>
/// <value>True if registered.</value>
public bool isRegistered { get; set; }
/// <summary>
/// Gets or sets the expiration date for this package.
/// </summary>
/// <value>Expiration Date.</value>
public DateTime expDate { get; set; }
/// <summary> /// <summary>
/// Gets or sets the versions. /// Gets or sets the versions.
/// </summary> /// </summary>
/// <value>The versions.</value> /// <value>The versions.</value>
public IReadOnlyList<PackageVersionInfo> versions { get; set; } public IReadOnlyList<VersionInfo> versions { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [enable in application store].
/// </summary>
/// <value><c>true</c> if [enable in application store]; otherwise, <c>false</c>.</value>
public bool enableInAppStore { get; set; }
/// <summary>
/// Gets or sets the installs.
/// </summary>
/// <value>The installs.</value>
public int installs { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PackageInfo"/> class. /// Initializes a new instance of the <see cref="PackageInfo"/> class.
/// </summary> /// </summary>
public PackageInfo() public PackageInfo()
{ {
versions = Array.Empty<PackageVersionInfo>(); versions = Array.Empty<VersionInfo>();
} }
} }
} }

View file

@ -1,23 +0,0 @@
namespace MediaBrowser.Model.Updates
{
/// <summary>
/// Enum PackageType.
/// </summary>
public enum PackageTargetSystem
{
/// <summary>
/// Server.
/// </summary>
Server,
/// <summary>
/// MB Theater.
/// </summary>
MBTheater,
/// <summary>
/// MB Classic.
/// </summary>
MBClassic
}
}

View file

@ -1,23 +0,0 @@
namespace MediaBrowser.Model.Updates
{
/// <summary>
/// Enum PackageVersionClass.
/// </summary>
public enum PackageVersionClass
{
/// <summary>
/// The release.
/// </summary>
Release = 0,
/// <summary>
/// The beta.
/// </summary>
Beta = 1,
/// <summary>
/// The dev.
/// </summary>
Dev = 2
}
}

View file

@ -1,96 +0,0 @@
#pragma warning disable CS1591
using System;
using System.Text.Json.Serialization;
namespace MediaBrowser.Model.Updates
{
/// <summary>
/// Class PackageVersionInfo.
/// </summary>
public class PackageVersionInfo
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string name { get; set; }
/// <summary>
/// Gets or sets the guid.
/// </summary>
/// <value>The guid.</value>
public string guid { get; set; }
/// <summary>
/// Gets or sets the version STR.
/// </summary>
/// <value>The version STR.</value>
public string versionStr { get; set; }
/// <summary>
/// The _version
/// </summary>
private Version _version;
/// <summary>
/// Gets or sets the version.
/// Had to make this an interpreted property since Protobuf can't handle Version
/// </summary>
/// <value>The version.</value>
[JsonIgnore]
public Version Version
{
get
{
if (_version == null)
{
var ver = versionStr;
_version = new Version(string.IsNullOrEmpty(ver) ? "0.0.0.1" : ver);
}
return _version;
}
}
/// <summary>
/// Gets or sets the classification.
/// </summary>
/// <value>The classification.</value>
public PackageVersionClass classification { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>The description.</value>
public string description { get; set; }
/// <summary>
/// Gets or sets the required version STR.
/// </summary>
/// <value>The required version STR.</value>
public string requiredVersionStr { get; set; }
/// <summary>
/// Gets or sets the source URL.
/// </summary>
/// <value>The source URL.</value>
public string sourceUrl { get; set; }
/// <summary>
/// Gets or sets the source URL.
/// </summary>
/// <value>The source URL.</value>
public string checksum { get; set; }
/// <summary>
/// Gets or sets the target filename.
/// </summary>
/// <value>The target filename.</value>
public string targetFilename { get; set; }
public string infoUrl { get; set; }
public string runtimes { get; set; }
}
}

View file

@ -0,0 +1,58 @@
using System;
namespace MediaBrowser.Model.Updates
{
/// <summary>
/// Class PackageVersionInfo.
/// </summary>
public class VersionInfo
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string name { get; set; }
/// <summary>
/// Gets or sets the guid.
/// </summary>
/// <value>The guid.</value>
public string guid { get; set; }
/// <summary>
/// Gets or sets the version.
/// </summary>
/// <value>The version.</value>
public Version version { get; set; }
/// <summary>
/// Gets or sets the changelog for this version.
/// </summary>
/// <value>The changelog.</value>
public string changelog { get; set; }
/// <summary>
/// Gets or sets the ABI that this version was built against.
/// </summary>
/// <value>The target ABI version.</value>
public string targetAbi { get; set; }
/// <summary>
/// Gets or sets the source URL.
/// </summary>
/// <value>The source URL.</value>
public string sourceUrl { get; set; }
/// <summary>
/// Gets or sets a checksum for the binary.
/// </summary>
/// <value>The checksum.</value>
public string checksum { get; set; }
/// <summary>
/// Gets or sets the target filename for the downloaded binary.
/// </summary>
/// <value>The target filename.</value>
public string filename { get; set; }
}
}

View file

@ -36,60 +36,51 @@ namespace MediaBrowser.Providers.Manager
/// </summary> /// </summary>
public class ProviderManager : IProviderManager, IDisposable public class ProviderManager : IProviderManager, IDisposable
{ {
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger; private readonly ILogger _logger;
/// <summary>
/// The _HTTP client
/// </summary>
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
/// <summary>
/// The _directory watchers
/// </summary>
private readonly ILibraryMonitor _libraryMonitor; private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem;
/// <summary> private readonly IServerApplicationPaths _appPaths;
/// Gets or sets the configuration manager. private readonly IJsonSerializer _json;
/// </summary> private readonly ILibraryManager _libraryManager;
/// <value>The configuration manager.</value> private readonly ISubtitleManager _subtitleManager;
private IServerConfigurationManager ConfigurationManager { get; set; } private readonly IServerConfigurationManager _configurationManager;
private IImageProvider[] ImageProviders { get; set; } private IImageProvider[] ImageProviders { get; set; }
private readonly IFileSystem _fileSystem;
private IMetadataService[] _metadataServices = { }; private IMetadataService[] _metadataServices = { };
private IMetadataProvider[] _metadataProviders = { }; private IMetadataProvider[] _metadataProviders = { };
private IEnumerable<IMetadataSaver> _savers; private IEnumerable<IMetadataSaver> _savers;
private readonly IServerApplicationPaths _appPaths;
private readonly IJsonSerializer _json;
private IExternalId[] _externalIds; private IExternalId[] _externalIds;
private readonly Func<ILibraryManager> _libraryManagerFactory;
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted; public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted;
public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted; public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted;
public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress; public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress;
private ISubtitleManager _subtitleManager;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ProviderManager" /> class. /// Initializes a new instance of the <see cref="ProviderManager" /> class.
/// </summary> /// </summary>
public ProviderManager(IHttpClient httpClient, ISubtitleManager subtitleManager, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILoggerFactory loggerFactory, IFileSystem fileSystem, IServerApplicationPaths appPaths, Func<ILibraryManager> libraryManagerFactory, IJsonSerializer json) public ProviderManager(
IHttpClient httpClient,
ISubtitleManager subtitleManager,
IServerConfigurationManager configurationManager,
ILibraryMonitor libraryMonitor,
ILogger<ProviderManager> logger,
IFileSystem fileSystem,
IServerApplicationPaths appPaths,
ILibraryManager libraryManager,
IJsonSerializer json)
{ {
_logger = loggerFactory.CreateLogger("ProviderManager"); _logger = logger;
_httpClient = httpClient; _httpClient = httpClient;
ConfigurationManager = configurationManager; _configurationManager = configurationManager;
_libraryMonitor = libraryMonitor; _libraryMonitor = libraryMonitor;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_appPaths = appPaths; _appPaths = appPaths;
_libraryManagerFactory = libraryManagerFactory; _libraryManager = libraryManager;
_json = json; _json = json;
_subtitleManager = subtitleManager; _subtitleManager = subtitleManager;
} }
@ -176,7 +167,7 @@ namespace MediaBrowser.Providers.Manager
public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken) public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{ {
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken); return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken);
} }
public Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken) public Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
@ -188,7 +179,7 @@ namespace MediaBrowser.Providers.Manager
var fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, true); var fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, true);
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken); return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
} }
public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken)
@ -273,7 +264,7 @@ namespace MediaBrowser.Providers.Manager
public IEnumerable<IImageProvider> GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions) public IEnumerable<IImageProvider> GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions)
{ {
return GetImageProviders(item, _libraryManagerFactory().GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false); return GetImageProviders(item, _libraryManager.GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false);
} }
private IEnumerable<IImageProvider> GetImageProviders(BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) private IEnumerable<IImageProvider> GetImageProviders(BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
@ -328,7 +319,7 @@ namespace MediaBrowser.Providers.Manager
private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(BaseItem item, bool includeDisabled) private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(BaseItem item, bool includeDisabled)
{ {
var options = GetMetadataOptions(item); var options = GetMetadataOptions(item);
var libraryOptions = _libraryManagerFactory().GetLibraryOptions(item); var libraryOptions = _libraryManager.GetLibraryOptions(item);
return GetImageProviders(item, libraryOptions, options, return GetImageProviders(item, libraryOptions, options,
new ImageRefreshOptions( new ImageRefreshOptions(
@ -593,7 +584,7 @@ namespace MediaBrowser.Providers.Manager
{ {
var type = item.GetType().Name; var type = item.GetType().Name;
return ConfigurationManager.Configuration.MetadataOptions return _configurationManager.Configuration.MetadataOptions
.FirstOrDefault(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) ?? .FirstOrDefault(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) ??
new MetadataOptions(); new MetadataOptions();
} }
@ -623,7 +614,7 @@ namespace MediaBrowser.Providers.Manager
/// <returns>Task.</returns> /// <returns>Task.</returns>
private void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable<IMetadataSaver> savers) private void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable<IMetadataSaver> savers)
{ {
var libraryOptions = _libraryManagerFactory().GetLibraryOptions(item); var libraryOptions = _libraryManager.GetLibraryOptions(item);
foreach (var saver in savers.Where(i => IsSaverEnabledForItem(i, item, libraryOptions, updateType, false))) foreach (var saver in savers.Where(i => IsSaverEnabledForItem(i, item, libraryOptions, updateType, false)))
{ {
@ -743,7 +734,7 @@ namespace MediaBrowser.Providers.Manager
if (!searchInfo.ItemId.Equals(Guid.Empty)) if (!searchInfo.ItemId.Equals(Guid.Empty))
{ {
referenceItem = _libraryManagerFactory().GetItemById(searchInfo.ItemId); referenceItem = _libraryManager.GetItemById(searchInfo.ItemId);
} }
return GetRemoteSearchResults<TItemType, TLookupType>(searchInfo, referenceItem, cancellationToken); return GetRemoteSearchResults<TItemType, TLookupType>(searchInfo, referenceItem, cancellationToken);
@ -771,7 +762,7 @@ namespace MediaBrowser.Providers.Manager
} }
else else
{ {
libraryOptions = _libraryManagerFactory().GetLibraryOptions(referenceItem); libraryOptions = _libraryManager.GetLibraryOptions(referenceItem);
} }
var options = GetMetadataOptions(referenceItem); var options = GetMetadataOptions(referenceItem);
@ -786,11 +777,11 @@ namespace MediaBrowser.Providers.Manager
if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataLanguage)) if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataLanguage))
{ {
searchInfo.SearchInfo.MetadataLanguage = ConfigurationManager.Configuration.PreferredMetadataLanguage; searchInfo.SearchInfo.MetadataLanguage = _configurationManager.Configuration.PreferredMetadataLanguage;
} }
if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataCountryCode)) if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataCountryCode))
{ {
searchInfo.SearchInfo.MetadataCountryCode = ConfigurationManager.Configuration.MetadataCountryCode; searchInfo.SearchInfo.MetadataCountryCode = _configurationManager.Configuration.MetadataCountryCode;
} }
var resultList = new List<RemoteSearchResult>(); var resultList = new List<RemoteSearchResult>();
@ -967,7 +958,7 @@ namespace MediaBrowser.Providers.Manager
public void OnRefreshProgress(BaseItem item, double progress) public void OnRefreshProgress(BaseItem item, double progress)
{ {
var id = item.Id; var id = item.Id;
_logger.LogInformation("OnRefreshProgress {0} {1}", id.ToString("N", CultureInfo.InvariantCulture), progress); _logger.LogDebug("OnRefreshProgress {0} {1}", id.ToString("N", CultureInfo.InvariantCulture), progress);
// TODO: Need to hunt down the conditions for this happening // TODO: Need to hunt down the conditions for this happening
_activeRefreshes.AddOrUpdate( _activeRefreshes.AddOrUpdate(
@ -1010,7 +1001,7 @@ namespace MediaBrowser.Providers.Manager
private async Task StartProcessingRefreshQueue() private async Task StartProcessingRefreshQueue()
{ {
var libraryManager = _libraryManagerFactory(); var libraryManager = _libraryManager;
if (_disposed) if (_disposed)
{ {
@ -1088,7 +1079,7 @@ namespace MediaBrowser.Providers.Manager
private async Task RefreshArtist(MusicArtist item, MetadataRefreshOptions options, CancellationToken cancellationToken) private async Task RefreshArtist(MusicArtist item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{ {
var albums = _libraryManagerFactory() var albums = _libraryManager
.GetItemList(new InternalItemsQuery .GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(MusicAlbum) }, IncludeItemTypes = new[] { nameof(MusicAlbum) },

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{442B5058-DCAF-4263-BB6A-F21E31120A1B}</ProjectGuid>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />

View file

@ -25,22 +25,22 @@ namespace MediaBrowser.Providers.Subtitles
{ {
public class SubtitleManager : ISubtitleManager public class SubtitleManager : ISubtitleManager
{ {
private ISubtitleProvider[] _subtitleProviders;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _monitor; private readonly ILibraryMonitor _monitor;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly ILocalizationManager _localization;
private ILocalizationManager _localization; private ISubtitleProvider[] _subtitleProviders;
public SubtitleManager( public SubtitleManager(
ILoggerFactory loggerFactory, ILogger<SubtitleManager> logger,
IFileSystem fileSystem, IFileSystem fileSystem,
ILibraryMonitor monitor, ILibraryMonitor monitor,
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
ILocalizationManager localizationManager) ILocalizationManager localizationManager)
{ {
_logger = loggerFactory.CreateLogger(nameof(SubtitleManager)); _logger = logger;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_monitor = monitor; _monitor = monitor;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{5624B7B5-B5A7-41D8-9F10-CC5611109619}</ProjectGuid>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{23499896-B135-4527-8574-C26E926EA99E}</ProjectGuid>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{21002819-C39A-4D3E-BE83-2A276A77FB1F}</ProjectGuid>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{DF194677-DFD3-42AF-9F75-D44D5A416478}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{462584F7-5023-4019-9EAC-B98CA458C0A0}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{28464062-0939-4AA7-9F7B-24DDDA61A7C0}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{3998657B-1CCC-49DD-A19F-275DC8495F57}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View file

@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<ProjectGuid>{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}</ProjectGuid>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View file

@ -82,11 +82,10 @@ namespace MediaBrowser.Api.Tests
loggerFactory, loggerFactory,
commandLineOpts, commandLineOpts,
new ManagedFileSystem(loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths), new ManagedFileSystem(loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
new SkiaEncoder(loggerFactory.CreateLogger<SkiaEncoder>(), appPaths),
new NetworkManager(loggerFactory.CreateLogger<NetworkManager>())); new NetworkManager(loggerFactory.CreateLogger<NetworkManager>()));
_appHosts.Add(appHost); _appHosts.Add(appHost);
var serviceCollection = new ServiceCollection(); var serviceCollection = new ServiceCollection();
appHost.InitAsync(serviceCollection, startupConfig).Wait(); appHost.Init(serviceCollection);
// Configure the web host builder // Configure the web host builder
Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths); Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths);
@ -101,8 +100,7 @@ namespace MediaBrowser.Api.Tests
// Finish initializing the app host // Finish initializing the app host
var appHost = (CoreAppHost)testServer.Services.GetRequiredService<IApplicationHost>(); var appHost = (CoreAppHost)testServer.Services.GetRequiredService<IApplicationHost>();
appHost.ServiceProvider = testServer.Services; appHost.ServiceProvider = testServer.Services;
appHost.InitializeServices(); appHost.InitializeServices().Wait();
appHost.FindParts();
appHost.RunStartupTasksAsync().Wait(); appHost.RunStartupTasksAsync().Wait();
return testServer; return testServer;