diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 4cd19dc838..7a92d40889 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -11,9 +11,6 @@ pr: trigger: batch: true - branches: - include: - - master jobs: - job: main_build @@ -71,28 +68,28 @@ jobs: - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact Naming' - condition: eq(variables['BuildConfiguration'], 'Release') + condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) inputs: PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll' artifactName: 'Jellyfin.Naming' - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact Controller' - condition: eq(variables['BuildConfiguration'], 'Release') + condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) inputs: PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll' artifactName: 'Jellyfin.Controller' - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact Model' - condition: eq(variables['BuildConfiguration'], 'Release') + condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) inputs: PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll' artifactName: 'Jellyfin.Model' - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact Common' - condition: eq(variables['BuildConfiguration'], 'Release') + condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) inputs: PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll' artifactName: 'Jellyfin.Common' @@ -102,7 +99,7 @@ jobs: pool: vmImage: ubuntu-16.04 dependsOn: main_build - condition: succeeded() + condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber']) # Only execute if the pullrequest numer is defined. (So not for normal CI builds) strategy: matrix: Naming: @@ -121,16 +118,28 @@ jobs: steps: - checkout: none - - task: NuGetCommand@2 - displayName: 'Download $(NugetPackageName)' + - task: DownloadBuildArtifacts@0 + displayName: Download the Reference Assembly Build Artifact inputs: - command: custom - arguments: 'install $(NugetPackageName) -OutputDirectory $(System.ArtifactsDirectory)/packages -ExcludeVersion -DirectDownload' + buildType: 'specific' # Options: current, specific + project: $(System.TeamProjectId) # Required when buildType == Specific + pipeline: $(System.DefinitionId) # Required when buildType == Specific, not sure if this will take a name too + #specificBuildWithTriggering: false # Optional + buildVersionToDownload: 'latestFromBranch' # Required when buildType == Specific# Options: latest, latestFromBranch, specific + allowPartiallySucceededBuilds: false # Optional + branchName: '$(System.PullRequest.TargetBranch)' # Required when buildType == Specific && BuildVersionToDownload == LatestFromBranch + #buildId: # Required when buildType == Specific && BuildVersionToDownload == Specific + #tags: # Optional + downloadType: 'single' # Options: single, specific + artifactName: '$(NugetPackageName)'# Required when downloadType == Single + #itemPattern: '**' # Optional + downloadPath: '$(System.ArtifactsDirectory)/current-artifacts' + #parallelizationLimit: '8' # Optional - task: CopyFiles@2 displayName: Copy Nuget Assembly to current-release folder inputs: - sourceFolder: $(System.ArtifactsDirectory)/packages/$(NugetPackageName) # Optional + sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional contents: '**/*.dll' targetFolder: $(System.ArtifactsDirectory)/current-release cleanTargetFolder: true # Optional @@ -138,7 +147,7 @@ jobs: flattenFolders: true # Optional - task: DownloadBuildArtifacts@0 - displayName: Download the Assembly Build Artifact + displayName: Download the New Assembly Build Artifact inputs: buildType: 'current' # Options: current, specific allowPartiallySucceededBuilds: false # Optional diff --git a/.drone.yml b/.drone.yml index 7705f4f936..87c8e414e9 100644 --- a/.drone.yml +++ b/.drone.yml @@ -28,84 +28,3 @@ steps: commands: - dotnet publish "Jellyfin.Server" --configuration Release --output "../ci/ci-release" ---- - -kind: pipeline -name: check-abi - -steps: -- name: submodules - image: docker:git - commands: - - git submodule update --init --recursive - -- name: build - image: microsoft/dotnet:2-sdk - commands: - - dotnet publish "Jellyfin.Server" --configuration Release --output "../ci/ci-release" - -- name: clone-dotnet-compat - image: docker:git - commands: - - git clone --depth 1 https://github.com/EraYaN/dotnet-compatibility ci/dotnet-compatibility - -- name: build-dotnet-compat - image: microsoft/dotnet:2-sdk - commands: - - dotnet publish "ci/dotnet-compatibility/CompatibilityCheckerCoreCLI" --configuration Release --output "../../ci-tools" - -- name: download-last-nuget-release-common - image: plugins/download - settings: - source: https://www.nuget.org/api/v2/package/Jellyfin.Common - destination: ci/Jellyfin.Common.nupkg - -- name: download-last-nuget-release-model - image: plugins/download - settings: - source: https://www.nuget.org/api/v2/package/Jellyfin.Model - destination: ci/Jellyfin.Model.nupkg - -- name: download-last-nuget-release-controller - image: plugins/download - settings: - source: https://www.nuget.org/api/v2/package/Jellyfin.Controller - destination: ci/Jellyfin.Controller.nupkg - -- name: download-last-nuget-release-naming - image: plugins/download - settings: - source: https://www.nuget.org/api/v2/package/Jellyfin.Naming - destination: ci/Jellyfin.Naming.nupkg - -- name: extract-downloaded-nuget-packages - image: garthk/unzip - commands: - - unzip -j ci/Jellyfin.Common.nupkg "*.dll" -d ci/nuget-packages - - unzip -j ci/Jellyfin.Model.nupkg "*.dll" -d ci/nuget-packages - - unzip -j ci/Jellyfin.Controller.nupkg "*.dll" -d ci/nuget-packages - - unzip -j ci/Jellyfin.Naming.nupkg "*.dll" -d ci/nuget-packages - -- name: run-dotnet-compat-common - image: microsoft/dotnet:2-runtime - err_ignore: true - commands: - - dotnet ci/ci-tools/CompatibilityCheckerCoreCLI.dll ci/nuget-packages/MediaBrowser.Common.dll ci/ci-release/MediaBrowser.Common.dll - -- name: run-dotnet-compat-model - image: microsoft/dotnet:2-runtime - err_ignore: true - commands: - - dotnet ci/ci-tools/CompatibilityCheckerCoreCLI.dll ci/nuget-packages/MediaBrowser.Model.dll ci/ci-release/MediaBrowser.Model.dll - -- name: run-dotnet-compat-controller - image: microsoft/dotnet:2-runtime - err_ignore: true - commands: - - dotnet ci/ci-tools/CompatibilityCheckerCoreCLI.dll ci/nuget-packages/MediaBrowser.Controller.dll ci/ci-release/MediaBrowser.Controller.dll - -- name: run-dotnet-compat-naming - image: microsoft/dotnet:2-runtime - err_ignore: true - commands: - - dotnet ci/ci-tools/CompatibilityCheckerCoreCLI.dll ci/nuget-packages/Emby.Naming.dll ci/ci-release/Emby.Naming.dll diff --git a/BDInfo/Properties/AssemblyInfo.cs b/BDInfo/Properties/AssemblyInfo.cs index 788cf73666..f65c7036a4 100644 --- a/BDInfo/Properties/AssemblyInfo.cs +++ b/BDInfo/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2016 CinemaSquid. Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2016 CinemaSquid. Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Dockerfile b/Dockerfile index 91a4f5a2d2..5794bdde1e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ RUN apt-get update \ libfontconfig1 \ && apt-get clean autoclean \ && apt-get autoremove \ - && rm -rf /var/lib/{apt,dpkg,cache,log} \ + && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media COPY --from=ffmpeg / / @@ -31,5 +31,4 @@ VOLUME /cache /config /media ENTRYPOINT dotnet /jellyfin/jellyfin.dll \ --datadir /config \ --cachedir /cache \ - --ffmpeg /usr/local/bin/ffmpeg \ - --ffprobe /usr/local/bin/ffprobe + --ffmpeg /usr/local/bin/ffmpeg diff --git a/Dockerfile.arm b/Dockerfile.arm index 42f0354a32..1497da0ef7 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -25,6 +25,7 @@ FROM microsoft/dotnet:${DOTNET_VERSION}-runtime-stretch-slim-arm32v7 COPY --from=qemu_extract qemu-arm-static /usr/bin RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ + && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media COPY --from=builder /jellyfin /jellyfin @@ -39,5 +40,4 @@ VOLUME /cache /config /media ENTRYPOINT dotnet /jellyfin/jellyfin.dll \ --datadir /config \ --cachedir /cache \ - --ffmpeg /usr/bin/ffmpeg \ - --ffprobe /usr/bin/ffprobe + --ffmpeg /usr/bin/ffmpeg diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index d3103d3893..f4658a055c 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -26,6 +26,7 @@ FROM microsoft/dotnet:${DOTNET_VERSION}-runtime-stretch-slim-arm64v8 COPY --from=qemu_extract qemu-aarch64-static /usr/bin RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ + && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media COPY --from=builder /jellyfin /jellyfin @@ -40,5 +41,4 @@ VOLUME /cache /config /media ENTRYPOINT dotnet /jellyfin/jellyfin.dll \ --datadir /config \ --cachedir /cache \ - --ffmpeg /usr/bin/ffmpeg \ - --ffprobe /usr/bin/ffprobe + --ffmpeg /usr/bin/ffmpeg diff --git a/DvdLib/Properties/AssemblyInfo.cs b/DvdLib/Properties/AssemblyInfo.cs index 5fc055d1f0..6acd571d68 100644 --- a/DvdLib/Properties/AssemblyInfo.cs +++ b/DvdLib/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Emby.Dlna/ConnectionManager/ConnectionManager.cs b/Emby.Dlna/ConnectionManager/ConnectionManager.cs index e138b91d6a..83011fbabd 100644 --- a/Emby.Dlna/ConnectionManager/ConnectionManager.cs +++ b/Emby.Dlna/ConnectionManager/ConnectionManager.cs @@ -2,7 +2,6 @@ using Emby.Dlna.Service; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace Emby.Dlna.ConnectionManager @@ -12,15 +11,13 @@ namespace Emby.Dlna.ConnectionManager private readonly IDlnaManager _dlna; private readonly ILogger _logger; private readonly IServerConfigurationManager _config; - protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory; - public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient) : base(logger, httpClient) { _dlna = dlna; _config = config; _logger = logger; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } public string GetServiceXml() @@ -33,7 +30,7 @@ namespace Emby.Dlna.ConnectionManager var profile = _dlna.GetProfile(request.Headers) ?? _dlna.GetDefaultProfile(); - return new ControlHandler(_config, _logger, XmlReaderSettingsFactory, profile).ProcessControlRequest(request); + return new ControlHandler(_config, _logger, profile).ProcessControlRequest(request); } } } diff --git a/Emby.Dlna/ConnectionManager/ControlHandler.cs b/Emby.Dlna/ConnectionManager/ControlHandler.cs index 16211c61f4..2e11047487 100644 --- a/Emby.Dlna/ConnectionManager/ControlHandler.cs +++ b/Emby.Dlna/ConnectionManager/ControlHandler.cs @@ -4,7 +4,6 @@ using Emby.Dlna.Service; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace Emby.Dlna.ConnectionManager @@ -32,7 +31,8 @@ namespace Emby.Dlna.ConnectionManager }; } - public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory, DeviceProfile profile) : base(config, logger, xmlReaderSettingsFactory) + public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile) + : base(config, logger) { _profile = profile; } diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs index 867e6112fd..5175898ab7 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs +++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs @@ -11,7 +11,6 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.TV; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace Emby.Dlna.ContentDirectory @@ -28,7 +27,6 @@ namespace Emby.Dlna.ContentDirectory private readonly IMediaSourceManager _mediaSourceManager; private readonly IUserViewManager _userViewManager; private readonly IMediaEncoder _mediaEncoder; - protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory; private readonly ITVSeriesManager _tvSeriesManager; public ContentDirectory(IDlnaManager dlna, @@ -38,7 +36,12 @@ namespace Emby.Dlna.ContentDirectory IServerConfigurationManager config, IUserManager userManager, ILogger logger, - IHttpClient httpClient, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager) + IHttpClient httpClient, + ILocalizationManager localization, + IMediaSourceManager mediaSourceManager, + IUserViewManager userViewManager, + IMediaEncoder mediaEncoder, + ITVSeriesManager tvSeriesManager) : base(logger, httpClient) { _dlna = dlna; @@ -51,7 +54,6 @@ namespace Emby.Dlna.ContentDirectory _mediaSourceManager = mediaSourceManager; _userViewManager = userViewManager; _mediaEncoder = mediaEncoder; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; _tvSeriesManager = tvSeriesManager; } @@ -94,7 +96,6 @@ namespace Emby.Dlna.ContentDirectory _mediaSourceManager, _userViewManager, _mediaEncoder, - XmlReaderSettingsFactory, _tvSeriesManager) .ProcessControlRequest(request); } diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 84f38ff769..4f8c89e485 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -25,7 +25,6 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace Emby.Dlna.ContentDirectory @@ -51,8 +50,22 @@ namespace Emby.Dlna.ContentDirectory private readonly DeviceProfile _profile; - public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager) - : base(config, logger, xmlReaderSettingsFactory) + public ControlHandler( + ILogger logger, + ILibraryManager libraryManager, + DeviceProfile profile, + string serverAddress, + string accessToken, + IImageProcessor imageProcessor, + IUserDataManager userDataManager, + User user, int systemUpdateId, + IServerConfigurationManager config, + ILocalizationManager localization, + IMediaSourceManager mediaSourceManager, + IUserViewManager userViewManager, + IMediaEncoder mediaEncoder, + ITVSeriesManager tvSeriesManager) + : base(config, logger) { _libraryManager = libraryManager; _userDataManager = userDataManager; diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 57ed0097a0..5fbe70dedb 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; @@ -20,10 +19,10 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Net; using MediaBrowser.Model.System; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; using Rssdp; using Rssdp.Infrastructure; +using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Dlna.Main { @@ -48,9 +47,8 @@ namespace Emby.Dlna.Main private readonly IDeviceDiscovery _deviceDiscovery; private SsdpDevicePublisher _Publisher; - + private readonly ISocketFactory _socketFactory; - private readonly IEnvironmentInfo _environmentInfo; private readonly INetworkManager _networkManager; private ISsdpCommunicationsServer _communicationsServer; @@ -76,10 +74,8 @@ namespace Emby.Dlna.Main IDeviceDiscovery deviceDiscovery, IMediaEncoder mediaEncoder, ISocketFactory socketFactory, - IEnvironmentInfo environmentInfo, INetworkManager networkManager, IUserViewManager userViewManager, - IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager) { _config = config; @@ -96,11 +92,11 @@ namespace Emby.Dlna.Main _deviceDiscovery = deviceDiscovery; _mediaEncoder = mediaEncoder; _socketFactory = socketFactory; - _environmentInfo = environmentInfo; _networkManager = networkManager; _logger = loggerFactory.CreateLogger("Dlna"); - ContentDirectory = new ContentDirectory.ContentDirectory(dlnaManager, + ContentDirectory = new ContentDirectory.ContentDirectory( + dlnaManager, userDataManager, imageProcessor, libraryManager, @@ -112,12 +108,11 @@ namespace Emby.Dlna.Main mediaSourceManager, userViewManager, mediaEncoder, - xmlReaderSettingsFactory, tvSeriesManager); - ConnectionManager = new ConnectionManager.ConnectionManager(dlnaManager, config, _logger, httpClient, xmlReaderSettingsFactory); + ConnectionManager = new ConnectionManager.ConnectionManager(dlnaManager, config, _logger, httpClient); - MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(_logger, httpClient, config, xmlReaderSettingsFactory); + MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(_logger, httpClient, config); Current = this; } @@ -169,8 +164,8 @@ namespace Emby.Dlna.Main { if (_communicationsServer == null) { - var enableMultiSocketBinding = _environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows || - _environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Linux; + var enableMultiSocketBinding = OperatingSystem.Id == OperatingSystemId.Windows || + OperatingSystem.Id == OperatingSystemId.Linux; _communicationsServer = new SsdpCommunicationsServer(_config, _socketFactory, _networkManager, _logger, enableMultiSocketBinding) { @@ -230,7 +225,7 @@ namespace Emby.Dlna.Main try { - _Publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion, _config.GetDlnaConfiguration().SendOnlyMatchedHost); + _Publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost); _Publisher.LogFunction = LogMessage; _Publisher.SupportPnpRootDevice = false; diff --git a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs index ae8175f4a2..7381e52582 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs +++ b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Emby.Dlna.Service; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace Emby.Dlna.MediaReceiverRegistrar @@ -36,8 +35,8 @@ namespace Emby.Dlna.MediaReceiverRegistrar }; } - public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) - : base(config, logger, xmlReaderSettingsFactory) + public ControlHandler(IServerConfigurationManager config, ILogger logger) + : base(config, logger) { } } diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs index e47143c16a..b565cb631e 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs +++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs @@ -1,7 +1,6 @@ using Emby.Dlna.Service; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace Emby.Dlna.MediaReceiverRegistrar @@ -9,13 +8,11 @@ namespace Emby.Dlna.MediaReceiverRegistrar public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar { private readonly IServerConfigurationManager _config; - protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory; - public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config) : base(logger, httpClient) { _config = config; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } public string GetServiceXml() @@ -27,7 +24,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar { return new ControlHandler( _config, - Logger, XmlReaderSettingsFactory) + Logger) .ProcessControlRequest(request); } } diff --git a/Emby.Dlna/Properties/AssemblyInfo.cs b/Emby.Dlna/Properties/AssemblyInfo.cs index 9d3a22c970..a2c1e0db8b 100644 --- a/Emby.Dlna/Properties/AssemblyInfo.cs +++ b/Emby.Dlna/Properties/AssemblyInfo.cs @@ -8,8 +8,8 @@ using System.Resources; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 5f78674b88..067d5fa43f 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -7,7 +7,6 @@ using System.Xml; using Emby.Dlna.Didl; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Extensions; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace Emby.Dlna.Service @@ -18,13 +17,11 @@ namespace Emby.Dlna.Service protected readonly IServerConfigurationManager Config; protected readonly ILogger _logger; - protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory; - protected BaseControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + protected BaseControlHandler(IServerConfigurationManager config, ILogger logger) { Config = config; _logger = logger; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } public ControlResponse ProcessControlRequest(ControlRequest request) @@ -61,11 +58,13 @@ namespace Emby.Dlna.Service using (var streamReader = new StreamReader(request.InputXml)) { - var readerSettings = XmlReaderSettingsFactory.Create(false); - - readerSettings.CheckCharacters = false; - readerSettings.IgnoreProcessingInstructions = true; - readerSettings.IgnoreComments = true; + var readerSettings = new XmlReaderSettings() + { + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; using (var reader = XmlReader.Create(streamReader, readerSettings)) { diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index faaeb5af84..6d209d8d01 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -180,6 +180,12 @@ namespace Emby.Drawing var supportedImageInfo = await GetSupportedImage(originalImagePath, dateModified).ConfigureAwait(false); originalImagePath = supportedImageInfo.path; + + if (!File.Exists(originalImagePath)) + { + return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); + } + dateModified = supportedImageInfo.dateModified; bool requiresTransparency = TransparentImageTypes.Contains(Path.GetExtension(originalImagePath)); @@ -265,8 +271,6 @@ namespace Emby.Drawing { // If it fails for whatever reason, return the original image _logger.LogError(ex, "Error encoding image"); - - // Just spit out the original file if all the options are default return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } finally diff --git a/Emby.Drawing/Properties/AssemblyInfo.cs b/Emby.Drawing/Properties/AssemblyInfo.cs index 8dfefe0af8..281008e370 100644 --- a/Emby.Drawing/Properties/AssemblyInfo.cs +++ b/Emby.Drawing/Properties/AssemblyInfo.cs @@ -8,8 +8,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs index 943caa3e62..2f0003be88 100644 --- a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs +++ b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs @@ -7,6 +7,7 @@ using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.IO; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; +using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace IsoMounter { @@ -17,7 +18,6 @@ namespace IsoMounter #region Private Fields - private readonly IEnvironmentInfo EnvironmentInfo; private readonly bool ExecutablesAvailable; private readonly ILogger _logger; private readonly string MountCommand; @@ -30,10 +30,8 @@ namespace IsoMounter #region Constructor(s) - public LinuxIsoManager(ILogger logger, IEnvironmentInfo environment, IProcessFactory processFactory) + public LinuxIsoManager(ILogger logger, IProcessFactory processFactory) { - - EnvironmentInfo = environment; _logger = logger; ProcessFactory = processFactory; @@ -109,7 +107,7 @@ namespace IsoMounter public bool CanMount(string path) { - if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Linux) + if (OperatingSystem.Id != OperatingSystemId.Linux) { return false; } @@ -118,7 +116,7 @@ namespace IsoMounter Name, path, Path.GetExtension(path), - EnvironmentInfo.OperatingSystem, + OperatingSystem.Name, ExecutablesAvailable ); diff --git a/Emby.IsoMounting/IsoMounter/Properties/AssemblyInfo.cs b/Emby.IsoMounting/IsoMounter/Properties/AssemblyInfo.cs index d60eccc2ee..5956fc3b31 100644 --- a/Emby.IsoMounting/IsoMounter/Properties/AssemblyInfo.cs +++ b/Emby.IsoMounting/IsoMounter/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Emby.Naming/Properties/AssemblyInfo.cs b/Emby.Naming/Properties/AssemblyInfo.cs index 15311570b8..f26e0ba794 100644 --- a/Emby.Naming/Properties/AssemblyInfo.cs +++ b/Emby.Naming/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Emby.Notifications/Properties/AssemblyInfo.cs b/Emby.Notifications/Properties/AssemblyInfo.cs index fd70375515..5c82c90c47 100644 --- a/Emby.Notifications/Properties/AssemblyInfo.cs +++ b/Emby.Notifications/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Emby.Photos/Properties/AssemblyInfo.cs b/Emby.Photos/Properties/AssemblyInfo.cs index 262125d388..a3bb4362a9 100644 --- a/Emby.Photos/Properties/AssemblyInfo.cs +++ b/Emby.Photos/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 739f687678..98cd97c318 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -30,13 +30,10 @@ namespace Emby.Server.Implementations.Activity public class ActivityLogEntryPoint : IServerEntryPoint { private readonly IInstallationManager _installationManager; - - //private readonly ILogger _logger; private readonly ISessionManager _sessionManager; private readonly ITaskManager _taskManager; private readonly IActivityManager _activityManager; private readonly ILocalizationManager _localization; - private readonly ILibraryManager _libraryManager; private readonly ISubtitleManager _subManager; private readonly IUserManager _userManager; @@ -61,41 +58,37 @@ namespace Emby.Server.Implementations.Activity public Task RunAsync() { - _taskManager.TaskCompleted += _taskManager_TaskCompleted; + _taskManager.TaskCompleted += OnTaskCompleted; - _installationManager.PluginInstalled += _installationManager_PluginInstalled; - _installationManager.PluginUninstalled += _installationManager_PluginUninstalled; - _installationManager.PluginUpdated += _installationManager_PluginUpdated; - _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed; + _installationManager.PluginInstalled += OnPluginInstalled; + _installationManager.PluginUninstalled += OnPluginUninstalled; + _installationManager.PluginUpdated += OnPluginUpdated; + _installationManager.PackageInstallationFailed += OnPackageInstallationFailed; - _sessionManager.SessionStarted += _sessionManager_SessionStarted; - _sessionManager.AuthenticationFailed += _sessionManager_AuthenticationFailed; - _sessionManager.AuthenticationSucceeded += _sessionManager_AuthenticationSucceeded; - _sessionManager.SessionEnded += _sessionManager_SessionEnded; + _sessionManager.SessionStarted += OnSessionStarted; + _sessionManager.AuthenticationFailed += OnAuthenticationFailed; + _sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded; + _sessionManager.SessionEnded += OnSessionEnded; - _sessionManager.PlaybackStart += _sessionManager_PlaybackStart; - _sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped; + _sessionManager.PlaybackStart += OnPlaybackStart; + _sessionManager.PlaybackStopped += OnPlaybackStopped; - //_subManager.SubtitlesDownloaded += _subManager_SubtitlesDownloaded; - _subManager.SubtitleDownloadFailure += _subManager_SubtitleDownloadFailure; + _subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure; - _userManager.UserCreated += _userManager_UserCreated; - _userManager.UserPasswordChanged += _userManager_UserPasswordChanged; - _userManager.UserDeleted += _userManager_UserDeleted; - _userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated; - _userManager.UserLockedOut += _userManager_UserLockedOut; + _userManager.UserCreated += OnUserCreated; + _userManager.UserPasswordChanged += OnUserPasswordChanged; + _userManager.UserDeleted += OnUserDeleted; + _userManager.UserPolicyUpdated += OnUserPolicyUpdated; + _userManager.UserLockedOut += OnUserLockedOut; - //_config.ConfigurationUpdated += _config_ConfigurationUpdated; - //_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; + _deviceManager.CameraImageUploaded += OnCameraImageUploaded; - _deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded; - - _appHost.ApplicationUpdated += _appHost_ApplicationUpdated; + _appHost.ApplicationUpdated += OnApplicationUpdated; return Task.CompletedTask; } - void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs e) + private void OnCameraImageUploaded(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -104,7 +97,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _userManager_UserLockedOut(object sender, GenericEventArgs e) + private void OnUserLockedOut(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -114,7 +107,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _subManager_SubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e) + private void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -125,7 +118,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e) + private void OnPlaybackStopped(object sender, PlaybackStopEventArgs e) { var item = e.MediaInfo; @@ -146,7 +139,7 @@ namespace Emby.Server.Implementations.Activity return; } - var user = e.Users.First(); + var user = e.Users[0]; CreateLogEntry(new ActivityLogEntry { @@ -156,7 +149,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e) + private void OnPlaybackStart(object sender, PlaybackProgressEventArgs e) { var item = e.MediaInfo; @@ -232,7 +225,7 @@ namespace Emby.Server.Implementations.Activity return null; } - void _sessionManager_SessionEnded(object sender, SessionEventArgs e) + private void OnSessionEnded(object sender, SessionEventArgs e) { string name; var session = e.SessionInfo; @@ -258,7 +251,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs e) + private void OnAuthenticationSucceeded(object sender, GenericEventArgs e) { var user = e.Argument.User; @@ -271,7 +264,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _sessionManager_AuthenticationFailed(object sender, GenericEventArgs e) + private void OnAuthenticationFailed(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -282,7 +275,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _appHost_ApplicationUpdated(object sender, GenericEventArgs e) + private void OnApplicationUpdated(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -292,25 +285,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) - { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format(_localization.GetLocalizedString("MessageNamedServerConfigurationUpdatedWithValue"), e.Key), - Type = "NamedConfigurationUpdated" - }); - } - - void _config_ConfigurationUpdated(object sender, EventArgs e) - { - CreateLogEntry(new ActivityLogEntry - { - Name = _localization.GetLocalizedString("MessageServerConfigurationUpdated"), - Type = "ServerConfigurationUpdated" - }); - } - - void _userManager_UserPolicyUpdated(object sender, GenericEventArgs e) + private void OnUserPolicyUpdated(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -320,7 +295,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _userManager_UserDeleted(object sender, GenericEventArgs e) + private void OnUserDeleted(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -329,7 +304,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _userManager_UserPasswordChanged(object sender, GenericEventArgs e) + private void OnUserPasswordChanged(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -339,7 +314,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _userManager_UserCreated(object sender, GenericEventArgs e) + private void OnUserCreated(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -349,18 +324,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _subManager_SubtitlesDownloaded(object sender, SubtitleDownloadEventArgs e) - { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format(_localization.GetLocalizedString("SubtitlesDownloadedForItem"), Notifications.Notifications.GetItemName(e.Item)), - Type = "SubtitlesDownloaded", - ItemId = e.Item.Id.ToString("N"), - ShortOverview = string.Format(_localization.GetLocalizedString("ProviderValue"), e.Provider) - }); - } - - void _sessionManager_SessionStarted(object sender, SessionEventArgs e) + private void OnSessionStarted(object sender, SessionEventArgs e) { string name; var session = e.SessionInfo; @@ -386,7 +350,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _installationManager_PluginUpdated(object sender, GenericEventArgs> e) + private void OnPluginUpdated(object sender, GenericEventArgs> e) { CreateLogEntry(new ActivityLogEntry { @@ -397,7 +361,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _installationManager_PluginUninstalled(object sender, GenericEventArgs e) + private void OnPluginUninstalled(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -406,7 +370,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _installationManager_PluginInstalled(object sender, GenericEventArgs e) + private void OnPluginInstalled(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -416,7 +380,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e) + private void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e) { var installationInfo = e.InstallationInfo; @@ -429,7 +393,7 @@ namespace Emby.Server.Implementations.Activity }); } - void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e) + private void OnTaskCompleted(object sender, TaskCompletionEventArgs e) { var result = e.Result; var task = e.Task; @@ -468,48 +432,36 @@ namespace Emby.Server.Implementations.Activity } private void CreateLogEntry(ActivityLogEntry entry) - { - try - { - _activityManager.Create(entry); - } - catch - { - // Logged at lower levels - } - } + => _activityManager.Create(entry); public void Dispose() { - _taskManager.TaskCompleted -= _taskManager_TaskCompleted; + _taskManager.TaskCompleted -= OnTaskCompleted; - _installationManager.PluginInstalled -= _installationManager_PluginInstalled; - _installationManager.PluginUninstalled -= _installationManager_PluginUninstalled; - _installationManager.PluginUpdated -= _installationManager_PluginUpdated; - _installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed; + _installationManager.PluginInstalled -= OnPluginInstalled; + _installationManager.PluginUninstalled -= OnPluginUninstalled; + _installationManager.PluginUpdated -= OnPluginUpdated; + _installationManager.PackageInstallationFailed -= OnPackageInstallationFailed; - _sessionManager.SessionStarted -= _sessionManager_SessionStarted; - _sessionManager.AuthenticationFailed -= _sessionManager_AuthenticationFailed; - _sessionManager.AuthenticationSucceeded -= _sessionManager_AuthenticationSucceeded; - _sessionManager.SessionEnded -= _sessionManager_SessionEnded; + _sessionManager.SessionStarted -= OnSessionStarted; + _sessionManager.AuthenticationFailed -= OnAuthenticationFailed; + _sessionManager.AuthenticationSucceeded -= OnAuthenticationSucceeded; + _sessionManager.SessionEnded -= OnSessionEnded; - _sessionManager.PlaybackStart -= _sessionManager_PlaybackStart; - _sessionManager.PlaybackStopped -= _sessionManager_PlaybackStopped; + _sessionManager.PlaybackStart -= OnPlaybackStart; + _sessionManager.PlaybackStopped -= OnPlaybackStopped; - _subManager.SubtitleDownloadFailure -= _subManager_SubtitleDownloadFailure; + _subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure; - _userManager.UserCreated -= _userManager_UserCreated; - _userManager.UserPasswordChanged -= _userManager_UserPasswordChanged; - _userManager.UserDeleted -= _userManager_UserDeleted; - _userManager.UserPolicyUpdated -= _userManager_UserPolicyUpdated; - _userManager.UserLockedOut -= _userManager_UserLockedOut; + _userManager.UserCreated -= OnUserCreated; + _userManager.UserPasswordChanged -= OnUserPasswordChanged; + _userManager.UserDeleted -= OnUserDeleted; + _userManager.UserPolicyUpdated -= OnUserPolicyUpdated; + _userManager.UserLockedOut -= OnUserLockedOut; - _config.ConfigurationUpdated -= _config_ConfigurationUpdated; - _config.NamedConfigurationUpdated -= _config_NamedConfigurationUpdated; + _deviceManager.CameraImageUploaded -= OnCameraImageUploaded; - _deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded; - - _appHost.ApplicationUpdated -= _appHost_ApplicationUpdated; + _appHost.ApplicationUpdated -= OnApplicationUpdated; } /// @@ -531,6 +483,7 @@ namespace Emby.Server.Implementations.Activity values.Add(CreateValueString(years, "year")); days = days % DaysInYear; } + // Number of months if (days >= DaysInMonth) { @@ -538,25 +491,39 @@ namespace Emby.Server.Implementations.Activity values.Add(CreateValueString(months, "month")); days = days % DaysInMonth; } + // Number of days if (days >= 1) + { values.Add(CreateValueString(days, "day")); + } + // Number of hours if (span.Hours >= 1) + { values.Add(CreateValueString(span.Hours, "hour")); + } // Number of minutes if (span.Minutes >= 1) + { values.Add(CreateValueString(span.Minutes, "minute")); + } + // Number of seconds (include when 0 if no other components included) if (span.Seconds >= 1 || values.Count == 0) + { values.Add(CreateValueString(span.Seconds, "second")); + } // Combine values into string var builder = new StringBuilder(); for (int i = 0; i < values.Count; i++) { if (builder.Length > 0) + { builder.Append(i == values.Count - 1 ? " and " : ", "); + } + builder.Append(values[i]); } // Return result diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs index 65cdccfa5d..00cfa0c9a9 100644 --- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs +++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs @@ -17,12 +17,14 @@ namespace Emby.Server.Implementations.AppBase string programDataPath, string logDirectoryPath, string configurationDirectoryPath, - string cacheDirectoryPath) + string cacheDirectoryPath, + string webDirectoryPath) { ProgramDataPath = programDataPath; LogDirectoryPath = logDirectoryPath; ConfigurationDirectoryPath = configurationDirectoryPath; CachePath = cacheDirectoryPath; + WebPath = webDirectoryPath; DataPath = Path.Combine(ProgramDataPath, "data"); } @@ -33,6 +35,12 @@ namespace Emby.Server.Implementations.AppBase /// The program data path. public string ProgramDataPath { get; private set; } + /// + /// Gets the path to the web UI resources folder + /// + /// The web UI resources path. + public string WebPath { get; set; } + /// /// Gets the path to the system folder /// diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f7d9bad1b0..eaea8844d4 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -34,10 +34,8 @@ using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Library; using Emby.Server.Implementations.LiveTv; using Emby.Server.Implementations.Localization; -using Emby.Server.Implementations.Middleware; using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Playlists; -using Emby.Server.Implementations.Reflection; using Emby.Server.Implementations.ScheduledTasks; using Emby.Server.Implementations.Security; using Emby.Server.Implementations.Serialization; @@ -45,7 +43,6 @@ using Emby.Server.Implementations.Session; using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; -using Emby.Server.Implementations.Xml; using MediaBrowser.Api; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; @@ -93,13 +90,11 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; -using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Updates; -using MediaBrowser.Model.Xml; using MediaBrowser.Providers.Chapters; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Subtitles; @@ -115,6 +110,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using ServiceStack; +using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Server.Implementations { @@ -143,12 +139,8 @@ namespace Emby.Server.Implementations return false; } - if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) - { - return true; - } - - if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX) + if (OperatingSystem.Id == OperatingSystemId.Windows + || OperatingSystem.Id == OperatingSystemId.Darwin) { return true; } @@ -168,7 +160,7 @@ namespace Emby.Server.Implementations public event EventHandler> ApplicationUpdated; /// - /// Gets or sets a value indicating whether this instance has changes that require the entire application to restart. + /// Gets a value indicating whether this instance has changes that require the entire application to restart. /// /// true if this instance has pending application restart; otherwise, false. public bool HasPendingRestart { get; private set; } @@ -182,7 +174,7 @@ namespace Emby.Server.Implementations protected ILogger Logger { get; set; } /// - /// Gets or sets the plugins. + /// Gets the plugins. /// /// The plugins. public IPlugin[] Plugins { get; protected set; } @@ -194,13 +186,13 @@ namespace Emby.Server.Implementations public ILoggerFactory LoggerFactory { get; protected set; } /// - /// Gets the application paths. + /// Gets or sets the application paths. /// /// The application paths. protected ServerApplicationPaths ApplicationPaths { get; set; } /// - /// Gets all concrete types. + /// Gets or sets all concrete types. /// /// All concrete types. public Type[] AllConcreteTypes { get; protected set; } @@ -208,7 +200,7 @@ namespace Emby.Server.Implementations /// /// The disposable parts /// - protected readonly List DisposableParts = new List(); + protected readonly List _disposableParts = new List(); /// /// Gets the configuration manager. @@ -218,8 +210,6 @@ namespace Emby.Server.Implementations public IFileSystem FileSystemManager { get; set; } - protected IEnvironmentInfo EnvironmentInfo { get; set; } - public PackageVersionClass SystemUpdateLevel { get @@ -239,15 +229,6 @@ namespace Emby.Server.Implementations /// The server configuration manager. public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager; - /// - /// Gets the configuration manager. - /// - /// IConfigurationManager. - protected IConfigurationManager GetConfigurationManager() - { - return new ServerConfigurationManager(ApplicationPaths, LoggerFactory, XmlSerializer, FileSystemManager); - } - protected virtual IResourceFileManager CreateResourceFileManager() { return new ResourceFileManager(HttpResultFactory, LoggerFactory, FileSystemManager); @@ -258,27 +239,33 @@ namespace Emby.Server.Implementations /// /// The user manager. public IUserManager UserManager { get; set; } + /// /// Gets or sets the library manager. /// /// The library manager. internal ILibraryManager LibraryManager { get; set; } + /// /// Gets or sets the directory watchers. /// /// The directory watchers. private ILibraryMonitor LibraryMonitor { get; set; } + /// /// Gets or sets the provider manager. /// /// The provider manager. private IProviderManager ProviderManager { get; set; } + /// /// Gets or sets the HTTP server. /// /// The HTTP server. private IHttpServer HttpServer { get; set; } + private IDtoService DtoService { get; set; } + public IImageProcessor ImageProcessor { get; set; } /// @@ -286,6 +273,7 @@ namespace Emby.Server.Implementations /// /// The media encoder. private IMediaEncoder MediaEncoder { get; set; } + private ISubtitleEncoder SubtitleEncoder { get; set; } private ISessionManager SessionManager { get; set; } @@ -295,6 +283,7 @@ namespace Emby.Server.Implementations public LocalizationManager LocalizationManager { get; set; } private IEncodingManager EncodingManager { get; set; } + private IChannelManager ChannelManager { get; set; } /// @@ -302,20 +291,29 @@ namespace Emby.Server.Implementations /// /// The user data repository. private IUserDataManager UserDataManager { get; set; } + private IUserRepository UserRepository { 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; } + private IPlaylistManager PlaylistManager { get; set; } private readonly IConfiguration _configuration; @@ -331,32 +329,40 @@ namespace Emby.Server.Implementations /// /// The zip client. protected IZipClient ZipClient { get; private set; } + protected IHttpResultFactory HttpResultFactory { get; private set; } + protected IAuthService AuthService { get; private set; } - public IStartupOptions StartupOptions { get; private set; } + public IStartupOptions StartupOptions { get; } internal IImageEncoder ImageEncoder { get; private set; } protected IProcessFactory ProcessFactory { get; private set; } + protected ICryptoProvider CryptographyProvider = new CryptographyProvider(); 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; } /// /// Initializes a new instance of the class. /// - public ApplicationHost(ServerApplicationPaths applicationPaths, + public ApplicationHost( + ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IStartupOptions options, IFileSystem fileSystem, - IEnvironmentInfo environmentInfo, IImageEncoder imageEncoder, INetworkManager networkManager, IConfiguration configuration) @@ -370,13 +376,12 @@ namespace Emby.Server.Implementations NetworkManager = networkManager; networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; - EnvironmentInfo = environmentInfo; ApplicationPaths = applicationPaths; LoggerFactory = loggerFactory; FileSystemManager = fileSystem; - ConfigurationManager = GetConfigurationManager(); + ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, XmlSerializer, FileSystemManager); Logger = LoggerFactory.CreateLogger("App"); @@ -415,7 +420,7 @@ namespace Emby.Server.Implementations _validAddressResults.Clear(); } - public string ApplicationVersion => typeof(ApplicationHost).Assembly.GetName().Version.ToString(3); + public string ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3); /// /// Gets the current application user agent @@ -423,14 +428,23 @@ namespace Emby.Server.Implementations /// The application user agent. public string ApplicationUserAgent => Name.Replace(' ','-') + "/" + ApplicationVersion; + /// + /// Gets the email address for use within a comment section of a user agent field. + /// Presently used to provide contact information to MusicBrainz service. + /// + public string ApplicationUserAgentAddress { get; } = "team@jellyfin.org"; + private string _productName; + /// /// Gets the current application name /// /// The application name. - public string ApplicationProductName => _productName ?? (_productName = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName); + public string ApplicationProductName + => _productName ?? (_productName = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName); private DeviceId _deviceId; + public string SystemId { get @@ -461,15 +475,15 @@ namespace Emby.Server.Implementations /// /// Creates an instance of type and resolves all constructor dependencies /// - /// The type. - /// System.Object. + /// /// The type + /// T public T CreateInstance() => ActivatorUtilities.CreateInstance(_serviceProvider); /// /// Creates the instance safe. /// - /// The type information. + /// The type. /// System.Object. protected object CreateInstanceSafe(Type type) { @@ -488,14 +502,14 @@ namespace Emby.Server.Implementations /// /// Resolves this instance. /// - /// + /// The type /// ``0. public T Resolve() => _serviceProvider.GetService(); /// /// Gets the export types. /// - /// + /// The type /// IEnumerable{Type}. public IEnumerable GetExportTypes() { @@ -507,22 +521,22 @@ namespace Emby.Server.Implementations /// /// Gets the exports. /// - /// + /// The type /// if set to true [manage lifetime]. /// IEnumerable{``0}. public IEnumerable GetExports(bool manageLifetime = true) { var parts = GetExportTypes() - .Select(x => CreateInstanceSafe(x)) + .Select(CreateInstanceSafe) .Where(i => i != null) .Cast() .ToList(); // Convert to list so this isn't executed for each iteration if (manageLifetime) { - lock (DisposableParts) + lock (_disposableParts) { - DisposableParts.AddRange(parts.OfType()); + _disposableParts.AddRange(parts.OfType()); } } @@ -532,7 +546,7 @@ namespace Emby.Server.Implementations /// /// Runs the startup tasks. /// - public async Task RunStartupTasks() + public async Task RunStartupTasksAsync() { Logger.LogInformation("Running startup tasks"); @@ -542,29 +556,20 @@ namespace Emby.Server.Implementations MediaEncoder.SetFFmpegPath(); - //if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath)) - //{ - // if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted) - // { - // ServerConfigurationManager.Configuration.IsStartupWizardCompleted = false; - // ServerConfigurationManager.SaveConfiguration(); - // } - //} - Logger.LogInformation("ServerId: {0}", SystemId); - var entryPoints = GetExports(); + var entryPoints = GetExports().ToList(); var stopWatch = new Stopwatch(); stopWatch.Start(); - await Task.WhenAll(StartEntryPoints(entryPoints, true)); + await Task.WhenAll(StartEntryPoints(entryPoints, true)).ConfigureAwait(false); Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed); Logger.LogInformation("Core startup complete"); HttpServer.GlobalResponse = null; stopWatch.Restart(); - await Task.WhenAll(StartEntryPoints(entryPoints, false)); + await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); Logger.LogInformation("Executed all post-startup entry points in {Elapsed:g}", stopWatch.Elapsed); stopWatch.Stop(); } @@ -584,7 +589,7 @@ namespace Emby.Server.Implementations } } - public async Task Init(IServiceCollection serviceCollection) + public async Task InitAsync(IServiceCollection serviceCollection) { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; @@ -614,14 +619,14 @@ namespace Emby.Server.Implementations SetHttpLimit(); - await RegisterResources(serviceCollection); + await RegisterResources(serviceCollection).ConfigureAwait(false); FindParts(); string contentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath; if (string.IsNullOrEmpty(contentRoot)) { - contentRoot = Path.Combine(ServerConfigurationManager.ApplicationPaths.ApplicationResourcesPath, "jellyfin-web", "src"); + contentRoot = ServerConfigurationManager.ApplicationPaths.WebPath; } var host = new WebHostBuilder() @@ -629,7 +634,7 @@ namespace Emby.Server.Implementations { options.ListenAnyIP(HttpPort); - if (EnableHttps) + if (EnableHttps && Certificate != null) { options.ListenAnyIP(HttpsPort, listenOptions => { listenOptions.UseHttps(Certificate); }); } @@ -651,7 +656,7 @@ namespace Emby.Server.Implementations }) .Build(); - await host.StartAsync(); + await host.StartAsync().ConfigureAwait(false); } private async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func next) @@ -700,14 +705,14 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(ApplicationPaths); + serviceCollection.AddSingleton(_configuration); + serviceCollection.AddSingleton(JsonSerializer); serviceCollection.AddSingleton(LoggerFactory); serviceCollection.AddLogging(); serviceCollection.AddSingleton(Logger); - serviceCollection.AddSingleton(EnvironmentInfo); - serviceCollection.AddSingleton(FileSystemManager); serviceCollection.AddSingleton(); @@ -749,17 +754,12 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(ServerConfigurationManager); - var assemblyInfo = new AssemblyInfo(); - serviceCollection.AddSingleton(assemblyInfo); - - LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LoggerFactory); - await LocalizationManager.LoadAll(); + LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory); + await LocalizationManager.LoadAll().ConfigureAwait(false); serviceCollection.AddSingleton(LocalizationManager); serviceCollection.AddSingleton(new BdInfoExaminer(FileSystemManager)); - serviceCollection.AddSingleton(new XmlReaderSettingsFactory()); - UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager); serviceCollection.AddSingleton(UserDataManager); @@ -770,7 +770,7 @@ namespace Emby.Server.Implementations var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LoggerFactory, JsonSerializer, ApplicationPaths, FileSystemManager); serviceCollection.AddSingleton(displayPreferencesRepo); - ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, JsonSerializer, LoggerFactory, assemblyInfo); + ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, JsonSerializer, LoggerFactory); serviceCollection.AddSingleton(ItemRepository); AuthenticationRepository = GetAuthenticationRepository(); @@ -786,7 +786,7 @@ namespace Emby.Server.Implementations var musicManager = new MusicManager(LibraryManager); serviceCollection.AddSingleton(new MusicManager(LibraryManager)); - LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager, EnvironmentInfo); + LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager); serviceCollection.AddSingleton(LibraryMonitor); serviceCollection.AddSingleton(new SearchEngine(LoggerFactory, LibraryManager, UserManager)); @@ -794,16 +794,19 @@ namespace Emby.Server.Implementations CertificateInfo = GetCertificateInfo(true); Certificate = GetCertificate(CertificateInfo); - HttpServer = new HttpListenerHost(this, + HttpServer = new HttpListenerHost( + this, LoggerFactory, ServerConfigurationManager, _configuration, NetworkManager, JsonSerializer, XmlSerializer, - CreateHttpListener()); + CreateHttpListener()) + { + GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading") + }; - HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); serviceCollection.AddSingleton(HttpServer); ImageProcessor = GetImageProcessor(); @@ -862,7 +865,6 @@ namespace Emby.Server.Implementations LoggerFactory, JsonSerializer, StartupOptions.FFmpegPath, - StartupOptions.FFprobePath, ServerConfigurationManager, FileSystemManager, () => SubtitleEncoder, @@ -907,7 +909,7 @@ namespace Emby.Server.Implementations public virtual string PackageRuntime => "netcore"; - public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths, EnvironmentInfo.EnvironmentInfo environmentInfo) + public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths) { // Distinct these to prevent users from reporting problems that aren't actually problems var commandLineArgs = Environment @@ -915,12 +917,14 @@ namespace Emby.Server.Implementations .Distinct(); logger.LogInformation("Arguments: {Args}", commandLineArgs); - logger.LogInformation("Operating system: {OS} {OSVersion}", environmentInfo.OperatingSystemName, environmentInfo.OperatingSystemVersion); - logger.LogInformation("Architecture: {Architecture}", environmentInfo.SystemArchitecture); + // FIXME: @bond this logs the kernel version, not the OS version + logger.LogInformation("Operating system: {OS} {OSVersion}", OperatingSystem.Name, Environment.OSVersion.Version); + logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture); logger.LogInformation("64-Bit Process: {Is64Bit}", Environment.Is64BitProcess); logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive); logger.LogInformation("Processor count: {ProcessorCount}", Environment.ProcessorCount); logger.LogInformation("Program data path: {ProgramDataPath}", appPaths.ProgramDataPath); + logger.LogInformation("Web resources path: {WebPath}", appPaths.WebPath); logger.LogInformation("Application directory: {ApplicationPath}", appPaths.ProgramSystemPath); } @@ -957,7 +961,7 @@ namespace Emby.Server.Implementations var password = string.IsNullOrWhiteSpace(info.Password) ? null : info.Password; var localCert = new X509Certificate2(certificateLocation, password); - //localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; + // localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; if (!localCert.HasPrivateKey) { Logger.LogError("No private key included in SSL cert {CertificateLocation}.", certificateLocation); @@ -1058,13 +1062,15 @@ namespace Emby.Server.Implementations HttpServer.Init(GetExports(false), GetExports(), GetUrlPrefixes()); - LibraryManager.AddParts(GetExports(), + LibraryManager.AddParts( + GetExports(), GetExports(), GetExports(), GetExports(), GetExports()); - ProviderManager.AddParts(GetExports(), + ProviderManager.AddParts( + GetExports(), GetExports(), GetExports(), GetExports(), @@ -1145,6 +1151,7 @@ namespace Emby.Server.Implementations } private CertificateInfo CertificateInfo { get; set; } + protected X509Certificate2 Certificate { get; private set; } private IEnumerable GetUrlPrefixes() @@ -1155,7 +1162,7 @@ namespace Emby.Server.Implementations { var prefixes = new List { - "http://"+i+":" + HttpPort + "/" + "http://" + i + ":" + HttpPort + "/" }; if (CertificateInfo != null) @@ -1184,30 +1191,12 @@ namespace Emby.Server.Implementations // Generate self-signed cert var certHost = GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns); var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N") + ".pfx"); - var password = "embycert"; - - //if (generateCertificate) - //{ - // if (!File.Exists(certPath)) - // { - // FileSystemManager.CreateDirectory(FileSystemManager.GetDirectoryName(certPath)); - - // try - // { - // CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, password, Logger); - // } - // catch (Exception ex) - // { - // Logger.LogError(ex, "Error creating ssl cert"); - // return null; - // } - // } - //} + const string Password = "embycert"; return new CertificateInfo { Path = certPath, - Password = password + Password = Password }; } @@ -1242,9 +1231,9 @@ namespace Emby.Server.Implementations requiresRestart = true; } - var currentCertPath = CertificateInfo == null ? null : CertificateInfo.Path; + var currentCertPath = CertificateInfo?.Path; var newCertInfo = GetCertificateInfo(false); - var newCertPath = newCertInfo == null ? null : newCertInfo.Path; + var newCertPath = newCertInfo?.Path; if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase)) { @@ -1377,6 +1366,7 @@ namespace Emby.Server.Implementations /// /// Gets the system status. /// + /// The cancellation token /// SystemInfo. public async Task GetSystemInfo(CancellationToken cancellationToken) { @@ -1393,6 +1383,7 @@ namespace Emby.Server.Implementations CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(), Id = SystemId, ProgramDataPath = ApplicationPaths.ProgramDataPath, + WebPath = ApplicationPaths.WebPath, LogPath = ApplicationPaths.LogDirectoryPath, ItemsByNamePath = ApplicationPaths.InternalMetadataPath, InternalMetadataPath = ApplicationPaths.InternalMetadataPath, @@ -1400,8 +1391,8 @@ namespace Emby.Server.Implementations HttpServerPortNumber = HttpPort, SupportsHttps = SupportsHttps, HttpsPortNumber = HttpsPort, - OperatingSystem = EnvironmentInfo.OperatingSystem.ToString(), - OperatingSystemDisplayName = EnvironmentInfo.OperatingSystemName, + OperatingSystem = OperatingSystem.Id.ToString(), + OperatingSystemDisplayName = OperatingSystem.Name, CanSelfRestart = CanSelfRestart, CanLaunchWebBrowser = CanLaunchWebBrowser, WanAddress = wanAddress, @@ -1411,7 +1402,7 @@ namespace Emby.Server.Implementations LocalAddress = localAddress, SupportsLibraryMonitor = true, EncoderLocation = MediaEncoder.EncoderLocation, - SystemArchitecture = EnvironmentInfo.SystemArchitecture, + SystemArchitecture = RuntimeInformation.OSArchitecture, SystemUpdateLevel = SystemUpdateLevel, PackageName = StartupOptions.PackageName }; @@ -1435,7 +1426,7 @@ namespace Emby.Server.Implementations { Version = ApplicationVersion, Id = SystemId, - OperatingSystem = EnvironmentInfo.OperatingSystem.ToString(), + OperatingSystem = OperatingSystem.Id.ToString(), WanAddress = wanAddress, ServerName = FriendlyName, LocalAddress = localAddress @@ -1470,19 +1461,19 @@ namespace Emby.Server.Implementations public async Task GetWanApiUrl(CancellationToken cancellationToken) { - const string url = "http://ipv4.icanhazip.com"; + const string Url = "http://ipv4.icanhazip.com"; try { using (var response = await HttpClient.Get(new HttpRequestOptions { - Url = url, + Url = Url, LogErrorResponseBody = false, LogErrors = false, LogRequest = false, TimeoutMs = 10000, BufferContent = false, CancellationToken = cancellationToken - })) + }).ConfigureAwait(false)) { return GetLocalApiUrl(response.ReadToEnd().Trim()); } @@ -1570,10 +1561,12 @@ namespace Emby.Server.Implementations { return result; } + return null; } private readonly ConcurrentDictionary _validAddressResults = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private async Task IsIpAddressValidAsync(IpAddressInfo address, CancellationToken cancellationToken) { if (address.Equals(IpAddressInfo.Loopback) || @@ -1590,26 +1583,26 @@ namespace Emby.Server.Implementations return cachedResult; } - var logPing = false; - #if DEBUG - logPing = true; + const bool LogPing = true; +#else + const bool LogPing = false; #endif try { - using (var response = await HttpClient.SendAsync(new HttpRequestOptions - { - Url = apiUrl, - LogErrorResponseBody = false, - LogErrors = logPing, - LogRequest = logPing, - TimeoutMs = 5000, - BufferContent = false, + using (var response = await HttpClient.SendAsync( + new HttpRequestOptions + { + Url = apiUrl, + LogErrorResponseBody = false, + LogErrors = LogPing, + LogRequest = LogPing, + TimeoutMs = 5000, + BufferContent = false, - CancellationToken = cancellationToken - - }, "POST").ConfigureAwait(false)) + CancellationToken = cancellationToken + }, "POST").ConfigureAwait(false)) { using (var reader = new StreamReader(response.Content)) { @@ -1674,6 +1667,7 @@ namespace Emby.Server.Implementations public event EventHandler HasUpdateAvailableChanged; private bool _hasUpdateAvailable; + public bool HasUpdateAvailable { get => _hasUpdateAvailable; @@ -1734,7 +1728,7 @@ namespace Emby.Server.Implementations var process = ProcessFactory.Create(new ProcessOptions { FileName = url, - //EnableRaisingEvents = true, + EnableRaisingEvents = true, UseShellExecute = true, ErrorDialog = false }); @@ -1769,26 +1763,25 @@ namespace Emby.Server.Implementations { Logger.LogInformation("Application has been updated to version {0}", package.versionStr); - ApplicationUpdated?.Invoke(this, new GenericEventArgs - { - Argument = package - }); + ApplicationUpdated?.Invoke( + this, + new GenericEventArgs() + { + Argument = package + }); NotifyPendingRestart(); } - private bool _disposed; + private bool _disposed = false; + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - if (!_disposed) - { - _disposed = true; - - Dispose(true); - } + Dispose(true); + GC.SuppressFinalize(this); } /// @@ -1797,14 +1790,19 @@ namespace Emby.Server.Implementations /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool dispose) { + if (_disposed) + { + return; + } + if (dispose) { var type = GetType(); Logger.LogInformation("Disposing {Type}", type.Name); - var parts = DisposableParts.Distinct().Where(i => i.GetType() != type).ToList(); - DisposableParts.Clear(); + var parts = _disposableParts.Distinct().Where(i => i.GetType() != type).ToList(); + _disposableParts.Clear(); foreach (var part in parts) { @@ -1820,6 +1818,8 @@ namespace Emby.Server.Implementations } } } + + _disposed = true; } } diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index 30bfd87498..9bc60972a1 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -6,7 +6,8 @@ namespace Emby.Server.Implementations { public static readonly Dictionary Configuration = new Dictionary { - {"HttpListenerHost:DefaultRedirectPath", "web/index.html"} + {"HttpListenerHost:DefaultRedirectPath", "web/index.html"}, + {"MusicBrainz:BaseUrl", "https://www.musicbrainz.org"} }; } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 06f6563a32..3ae63279b6 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -24,7 +24,6 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; @@ -65,8 +64,7 @@ namespace Emby.Server.Implementations.Data IServerConfigurationManager config, IServerApplicationHost appHost, IJsonSerializer jsonSerializer, - ILoggerFactory loggerFactory, - IAssemblyInfo assemblyInfo) + ILoggerFactory loggerFactory) : base(loggerFactory.CreateLogger(nameof(SqliteItemRepository))) { if (config == null) @@ -82,7 +80,7 @@ namespace Emby.Server.Implementations.Data _appHost = appHost; _config = config; _jsonSerializer = jsonSerializer; - _typeMapper = new TypeMapper(assemblyInfo); + _typeMapper = new TypeMapper(); DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db"); } diff --git a/Emby.Server.Implementations/Data/TypeMapper.cs b/Emby.Server.Implementations/Data/TypeMapper.cs index 37c952e88e..0e67affbfc 100644 --- a/Emby.Server.Implementations/Data/TypeMapper.cs +++ b/Emby.Server.Implementations/Data/TypeMapper.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Linq; -using MediaBrowser.Model.Reflection; namespace Emby.Server.Implementations.Data { @@ -10,16 +9,13 @@ namespace Emby.Server.Implementations.Data /// public class TypeMapper { - private readonly IAssemblyInfo _assemblyInfo; - /// /// This holds all the types in the running assemblies so that we can de-serialize properly when we don't have strong types /// private readonly ConcurrentDictionary _typeMap = new ConcurrentDictionary(); - public TypeMapper(IAssemblyInfo assemblyInfo) + public TypeMapper() { - _assemblyInfo = assemblyInfo; } /// @@ -45,8 +41,7 @@ namespace Emby.Server.Implementations.Data /// Type. private Type LookupType(string typeName) { - return _assemblyInfo - .GetCurrentAssemblies() + return AppDomain.CurrentDomain.GetAssemblies() .Select(a => a.GetType(typeName)) .FirstOrDefault(t => t != null); } diff --git a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs index 2c4ef170dc..175a8f3ce4 100644 --- a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs +++ b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs @@ -9,14 +9,14 @@ namespace Emby.Server.Implementations.Diagnostics { public class CommonProcess : IProcess { - public event EventHandler Exited; - - private readonly ProcessOptions _options; private readonly Process _process; + private bool _disposed = false; + private bool _hasExited; + public CommonProcess(ProcessOptions options) { - _options = options; + StartInfo = options; var startInfo = new ProcessStartInfo { @@ -27,10 +27,10 @@ namespace Emby.Server.Implementations.Diagnostics CreateNoWindow = options.CreateNoWindow, RedirectStandardError = options.RedirectStandardError, RedirectStandardInput = options.RedirectStandardInput, - RedirectStandardOutput = options.RedirectStandardOutput + RedirectStandardOutput = options.RedirectStandardOutput, + ErrorDialog = options.ErrorDialog }; - startInfo.ErrorDialog = options.ErrorDialog; if (options.IsHidden) { @@ -45,11 +45,22 @@ namespace Emby.Server.Implementations.Diagnostics if (options.EnableRaisingEvents) { _process.EnableRaisingEvents = true; - _process.Exited += _process_Exited; + _process.Exited += OnProcessExited; } } - private bool _hasExited; + public event EventHandler Exited; + + public ProcessOptions StartInfo { get; } + + public StreamWriter StandardInput => _process.StandardInput; + + public StreamReader StandardError => _process.StandardError; + + public StreamReader StandardOutput => _process.StandardOutput; + + public int ExitCode => _process.ExitCode; + private bool HasExited { get @@ -72,25 +83,6 @@ namespace Emby.Server.Implementations.Diagnostics } } - private void _process_Exited(object sender, EventArgs e) - { - _hasExited = true; - if (Exited != null) - { - Exited(this, e); - } - } - - public ProcessOptions StartInfo => _options; - - public StreamWriter StandardInput => _process.StandardInput; - - public StreamReader StandardError => _process.StandardError; - - public StreamReader StandardOutput => _process.StandardOutput; - - public int ExitCode => _process.ExitCode; - public void Start() { _process.Start(); @@ -108,7 +100,7 @@ namespace Emby.Server.Implementations.Diagnostics public Task WaitForExitAsync(int timeMs) { - //Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true. + // Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true. if (HasExited) { @@ -130,7 +122,29 @@ namespace Emby.Server.Implementations.Diagnostics public void Dispose() { - _process?.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + _process?.Dispose(); + } + + _disposed = true; + } + + private void OnProcessExited(object sender, EventArgs e) + { + _hasExited = true; + Exited?.Invoke(this, e); } } } diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 39e9ed3756..2c79624524 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -13,7 +13,6 @@ - @@ -47,6 +46,21 @@ false + + true + + + + + + + + + + + ../jellyfin.ruleset + + diff --git a/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs b/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs deleted file mode 100644 index c8104150d6..0000000000 --- a/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using MediaBrowser.Model.System; - -namespace Emby.Server.Implementations.EnvironmentInfo -{ - public class EnvironmentInfo : IEnvironmentInfo - { - public EnvironmentInfo(MediaBrowser.Model.System.OperatingSystem operatingSystem) - { - OperatingSystem = operatingSystem; - } - - public MediaBrowser.Model.System.OperatingSystem OperatingSystem { get; private set; } - - public string OperatingSystemName - { - get - { - switch (OperatingSystem) - { - case MediaBrowser.Model.System.OperatingSystem.Android: return "Android"; - case MediaBrowser.Model.System.OperatingSystem.BSD: return "BSD"; - case MediaBrowser.Model.System.OperatingSystem.Linux: return "Linux"; - case MediaBrowser.Model.System.OperatingSystem.OSX: return "macOS"; - case MediaBrowser.Model.System.OperatingSystem.Windows: return "Windows"; - default: throw new Exception($"Unknown OS {OperatingSystem}"); - } - } - } - - public string OperatingSystemVersion => Environment.OSVersion.Version.ToString() + " " + Environment.OSVersion.ServicePack.ToString(); - - public Architecture SystemArchitecture => RuntimeInformation.OSArchitecture; - } -} diff --git a/Emby.Server.Implementations/HttpServer/StreamWriter.cs b/Emby.Server.Implementations/HttpServer/StreamWriter.cs index 66a13e20df..cf30bbc326 100644 --- a/Emby.Server.Implementations/HttpServer/StreamWriter.cs +++ b/Emby.Server.Implementations/HttpServer/StreamWriter.cs @@ -5,7 +5,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Emby.Server.Implementations.HttpServer diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index d473425115..df4dc41b99 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -10,8 +10,8 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.IO; using MediaBrowser.Model.System; -using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; +using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Server.Implementations.IO { @@ -127,7 +127,6 @@ namespace Emby.Server.Implementations.IO private IServerConfigurationManager ConfigurationManager { get; set; } private readonly IFileSystem _fileSystem; - private readonly IEnvironmentInfo _environmentInfo; /// /// Initializes a new instance of the class. @@ -136,14 +135,12 @@ namespace Emby.Server.Implementations.IO ILoggerFactory loggerFactory, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, - IFileSystem fileSystem, - IEnvironmentInfo environmentInfo) + IFileSystem fileSystem) { LibraryManager = libraryManager; Logger = loggerFactory.CreateLogger(GetType().Name); ConfigurationManager = configurationManager; _fileSystem = fileSystem; - _environmentInfo = environmentInfo; } private bool IsLibraryMonitorEnabled(BaseItem item) @@ -267,7 +264,7 @@ namespace Emby.Server.Implementations.IO return; } - if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows) + if (OperatingSystem.Id != OperatingSystemId.Windows) { if (path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase) || path.StartsWith("smb://", StringComparison.OrdinalIgnoreCase)) { @@ -276,12 +273,6 @@ namespace Emby.Server.Implementations.IO } } - if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Android) - { - // causing crashing - return; - } - // Already being watched if (_fileSystemWatchers.ContainsKey(path)) { diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 421592fada..47cea7269d 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -8,6 +8,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; +using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Server.Implementations.IO { @@ -24,22 +25,19 @@ namespace Emby.Server.Implementations.IO private readonly string _tempPath; - private readonly IEnvironmentInfo _environmentInfo; private readonly bool _isEnvironmentCaseInsensitive; public ManagedFileSystem( ILoggerFactory loggerFactory, - IEnvironmentInfo environmentInfo, IApplicationPaths applicationPaths) { Logger = loggerFactory.CreateLogger("FileSystem"); _supportsAsyncFileStreams = true; _tempPath = applicationPaths.TempDirectory; - _environmentInfo = environmentInfo; - SetInvalidFileNameChars(environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows); + SetInvalidFileNameChars(OperatingSystem.Id == OperatingSystemId.Windows); - _isEnvironmentCaseInsensitive = environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows; + _isEnvironmentCaseInsensitive = OperatingSystem.Id == OperatingSystemId.Windows; } public virtual void AddShortcutHandler(IShortcutHandler handler) @@ -468,7 +466,7 @@ namespace Emby.Server.Implementations.IO public virtual void SetHidden(string path, bool isHidden) { - if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows) + if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows) { return; } @@ -492,7 +490,7 @@ namespace Emby.Server.Implementations.IO public virtual void SetReadOnly(string path, bool isReadOnly) { - if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows) + if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows) { return; } @@ -516,7 +514,7 @@ namespace Emby.Server.Implementations.IO public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly) { - if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows) + if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows) { return; } @@ -801,7 +799,7 @@ namespace Emby.Server.Implementations.IO public virtual void SetExecutable(string path) { - if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX) + if (OperatingSystem.Id == MediaBrowser.Model.System.OperatingSystemId.Darwin) { RunProcess("chmod", "+x \"" + path + "\"", Path.GetDirectoryName(path)); } diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/Emby.Server.Implementations/IO/StreamHelper.cs index 09cf4d4a3e..d02cd84a03 100644 --- a/Emby.Server.Implementations/IO/StreamHelper.cs +++ b/Emby.Server.Implementations/IO/StreamHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -8,168 +9,213 @@ namespace Emby.Server.Implementations.IO { public class StreamHelper : IStreamHelper { + private const int StreamCopyToBufferSize = 81920; + public async Task CopyToAsync(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken) { - byte[] buffer = new byte[bufferSize]; - int read; - while ((read = source.Read(buffer, 0, buffer.Length)) != 0) + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try { - cancellationToken.ThrowIfCancellationRequested(); - - await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false); - - if (onStarted != null) + int read; + while ((read = await source.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) != 0) { - onStarted(); - onStarted = null; + cancellationToken.ThrowIfCancellationRequested(); + + await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false); + + if (onStarted != null) + { + onStarted(); + onStarted = null; + } } } + finally + { + ArrayPool.Shared.Return(buffer); + } } public async Task CopyToAsync(Stream source, Stream destination, int bufferSize, int emptyReadLimit, CancellationToken cancellationToken) { - byte[] buffer = new byte[bufferSize]; - - if (emptyReadLimit <= 0) + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try { - int read; - while ((read = source.Read(buffer, 0, buffer.Length)) != 0) + if (emptyReadLimit <= 0) + { + int read; + while ((read = await source.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) != 0) + { + cancellationToken.ThrowIfCancellationRequested(); + + await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false); + } + + return; + } + + var eofCount = 0; + + while (eofCount < emptyReadLimit) { cancellationToken.ThrowIfCancellationRequested(); - await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false); - } + var bytesRead = await source.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); - return; + if (bytesRead == 0) + { + eofCount++; + await Task.Delay(50, cancellationToken).ConfigureAwait(false); + } + else + { + eofCount = 0; + + await destination.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false); + } + } } - - var eofCount = 0; - - while (eofCount < emptyReadLimit) + finally { - cancellationToken.ThrowIfCancellationRequested(); - - var bytesRead = source.Read(buffer, 0, buffer.Length); - - if (bytesRead == 0) - { - eofCount++; - await Task.Delay(50, cancellationToken).ConfigureAwait(false); - } - else - { - eofCount = 0; - - await destination.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false); - } + ArrayPool.Shared.Return(buffer); } } - const int StreamCopyToBufferSize = 81920; public async Task CopyToAsync(Stream source, Stream destination, CancellationToken cancellationToken) { - var array = new byte[StreamCopyToBufferSize]; - int bytesRead; - int totalBytesRead = 0; - - while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) + byte[] buffer = ArrayPool.Shared.Rent(StreamCopyToBufferSize); + try { - var bytesToWrite = bytesRead; + int totalBytesRead = 0; - if (bytesToWrite > 0) + int bytesRead; + while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) { - await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + var bytesToWrite = bytesRead; - totalBytesRead += bytesRead; + if (bytesToWrite > 0) + { + await destination.WriteAsync(buffer, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + + totalBytesRead += bytesRead; + } } - } - return totalBytesRead; + return totalBytesRead; + } + finally + { + ArrayPool.Shared.Return(buffer); + } } public async Task CopyToAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken) { - var array = new byte[StreamCopyToBufferSize]; - int bytesRead; - int totalBytesRead = 0; - - while ((bytesRead = source.Read(array, 0, array.Length)) != 0) + byte[] buffer = ArrayPool.Shared.Rent(StreamCopyToBufferSize); + try { - var bytesToWrite = bytesRead; + int bytesRead; + int totalBytesRead = 0; - if (bytesToWrite > 0) + while ((bytesRead = source.Read(buffer, 0, buffer.Length)) != 0) { - await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + var bytesToWrite = bytesRead; - totalBytesRead += bytesRead; + if (bytesToWrite > 0) + { + await destination.WriteAsync(buffer, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + + totalBytesRead += bytesRead; + } } - } - return totalBytesRead; + return totalBytesRead; + } + finally + { + ArrayPool.Shared.Return(buffer); + } } public async Task CopyToAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken) { - var array = new byte[StreamCopyToBufferSize]; - int bytesRead; - - while ((bytesRead = source.Read(array, 0, array.Length)) != 0) + byte[] buffer = ArrayPool.Shared.Rent(StreamCopyToBufferSize); + try { - var bytesToWrite = Math.Min(bytesRead, copyLength); + int bytesRead; - if (bytesToWrite > 0) + while ((bytesRead = source.Read(buffer, 0, buffer.Length)) != 0) { - await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); - } + var bytesToWrite = Math.Min(bytesRead, copyLength); - copyLength -= bytesToWrite; + if (bytesToWrite > 0) + { + await destination.WriteAsync(buffer, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + } - if (copyLength <= 0) - { - break; + copyLength -= bytesToWrite; + + if (copyLength <= 0) + { + break; + } } } + finally + { + ArrayPool.Shared.Return(buffer); + } } public async Task CopyToAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken) { - var array = new byte[StreamCopyToBufferSize]; - int bytesRead; - - while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) + byte[] buffer = ArrayPool.Shared.Rent(StreamCopyToBufferSize); + try { - var bytesToWrite = Math.Min(bytesRead, copyLength); + int bytesRead; - if (bytesToWrite > 0) + while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) { - await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); - } + var bytesToWrite = Math.Min(bytesRead, copyLength); - copyLength -= bytesToWrite; + if (bytesToWrite > 0) + { + await destination.WriteAsync(buffer, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + } - if (copyLength <= 0) - { - break; + copyLength -= bytesToWrite; + + if (copyLength <= 0) + { + break; + } } } + finally + { + ArrayPool.Shared.Return(buffer); + } } public async Task CopyUntilCancelled(Stream source, Stream target, int bufferSize, CancellationToken cancellationToken) { - byte[] buffer = new byte[bufferSize]; - - while (!cancellationToken.IsCancellationRequested) + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try { - var bytesRead = await CopyToAsyncInternal(source, target, buffer, cancellationToken).ConfigureAwait(false); - - //var position = fs.Position; - //_logger.LogDebug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); - - if (bytesRead == 0) + while (!cancellationToken.IsCancellationRequested) { - await Task.Delay(100).ConfigureAwait(false); + var bytesRead = await CopyToAsyncInternal(source, target, buffer, cancellationToken).ConfigureAwait(false); + + if (bytesRead == 0) + { + await Task.Delay(100).ConfigureAwait(false); + } } } + finally + { + ArrayPool.Shared.Return(buffer); + } } private static async Task CopyToAsyncInternal(Stream source, Stream destination, byte[] buffer, CancellationToken cancellationToken) diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs index 24aaa76c02..6e915de3d1 100644 --- a/Emby.Server.Implementations/IStartupOptions.cs +++ b/Emby.Server.Implementations/IStartupOptions.cs @@ -7,11 +7,6 @@ namespace Emby.Server.Implementations /// string FFmpegPath { get; } - /// - /// --ffprobe - /// - string FFprobePath { get; } - /// /// --service /// diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6591d54c56..1673e37776 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -58,22 +58,23 @@ namespace Emby.Server.Implementations.Library private ILibraryPostScanTask[] PostscanTasks { get; set; } /// - /// Gets the intro providers. + /// Gets or sets the intro providers. /// /// The intro providers. private IIntroProvider[] IntroProviders { get; set; } /// - /// Gets the list of entity resolution ignore rules + /// Gets or sets the list of entity resolution ignore rules /// /// The entity resolution ignore rules. private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; } /// - /// Gets the list of currently registered entity resolvers + /// Gets or sets the list of currently registered entity resolvers /// /// The entity resolvers enumerable. private IItemResolver[] EntityResolvers { get; set; } + private IMultiItemResolver[] MultiItemResolvers { get; set; } /// @@ -83,7 +84,7 @@ namespace Emby.Server.Implementations.Library private IBaseItemComparer[] Comparers { get; set; } /// - /// Gets the active item repository + /// Gets or sets the active item repository /// /// The item repository. public IItemRepository ItemRepository { get; set; } @@ -133,12 +134,14 @@ namespace Emby.Server.Implementations.Library private readonly Func _providerManagerFactory; private readonly Func _userviewManager; public bool IsScanRunning { get; private set; } + private IServerApplicationHost _appHost; /// /// The _library items cache /// private readonly ConcurrentDictionary _libraryItemsCache; + /// /// Gets the library items cache. /// @@ -150,7 +153,8 @@ namespace Emby.Server.Implementations.Library /// /// Initializes a new instance of the class. /// - /// The logger. + /// The application host + /// The logger factory. /// The task manager. /// The user manager. /// The configuration manager. @@ -167,6 +171,7 @@ namespace Emby.Server.Implementations.Library Func providerManagerFactory, Func userviewManager) { + _appHost = appHost; _logger = loggerFactory.CreateLogger(nameof(LibraryManager)); _taskManager = taskManager; _userManager = userManager; @@ -176,7 +181,7 @@ namespace Emby.Server.Implementations.Library _fileSystem = fileSystem; _providerManagerFactory = providerManagerFactory; _userviewManager = userviewManager; - _appHost = appHost; + _libraryItemsCache = new ConcurrentDictionary(); ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated; @@ -191,8 +196,9 @@ namespace Emby.Server.Implementations.Library /// The resolvers. /// The intro providers. /// The item comparers. - /// The postscan tasks. - public void AddParts(IEnumerable rules, + /// The post scan tasks. + public void AddParts( + IEnumerable rules, IEnumerable resolvers, IEnumerable introProviders, IEnumerable itemComparers, @@ -203,24 +209,19 @@ namespace Emby.Server.Implementations.Library MultiItemResolvers = EntityResolvers.OfType().ToArray(); IntroProviders = introProviders.ToArray(); Comparers = itemComparers.ToArray(); - - PostscanTasks = postscanTasks.OrderBy(i => - { - var hasOrder = i as IHasOrder; - - return hasOrder == null ? 0 : hasOrder.Order; - - }).ToArray(); + PostscanTasks = postscanTasks.ToArray(); } /// /// The _root folder /// private volatile AggregateFolder _rootFolder; + /// /// The _root folder sync lock /// private readonly object _rootFolderSyncLock = new object(); + /// /// Gets the root folder. /// @@ -239,11 +240,13 @@ namespace Emby.Server.Implementations.Library } } } + return _rootFolder; } } private bool _wizardCompleted; + /// /// Records the configuration values. /// @@ -258,7 +261,7 @@ namespace Emby.Server.Implementations.Library /// /// The sender. /// The instance containing the event data. - void ConfigurationUpdated(object sender, EventArgs e) + private void ConfigurationUpdated(object sender, EventArgs e) { var config = ConfigurationManager.Configuration; @@ -335,12 +338,14 @@ namespace Emby.Server.Implementations.Library // channel no longer installed } } + options.DeleteFileLocation = false; } if (item is LiveTvProgram) { - _logger.LogDebug("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}", + _logger.LogDebug( + "Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}", item.GetType().Name, item.Name ?? "Unknown name", item.Path ?? string.Empty, @@ -348,7 +353,8 @@ namespace Emby.Server.Implementations.Library } else { - _logger.LogInformation("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}", + _logger.LogInformation( + "Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}", item.GetType().Name, item.Name ?? "Unknown name", item.Path ?? string.Empty, @@ -488,12 +494,13 @@ namespace Emby.Server.Implementations.Library { throw new ArgumentNullException(nameof(key)); } + if (type == null) { throw new ArgumentNullException(nameof(type)); } - if (key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath)) + if (key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal)) { // Try to normalize paths located underneath program-data in an attempt to make them more portable key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length) @@ -511,13 +518,11 @@ namespace Emby.Server.Implementations.Library return key.GetMD5(); } - public BaseItem ResolvePath(FileSystemMetadata fileInfo, - Folder parent = null) - { - return ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent); - } + public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null) + => ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent); - private BaseItem ResolvePath(FileSystemMetadata fileInfo, + private BaseItem ResolvePath( + FileSystemMetadata fileInfo, IDirectoryService directoryService, IItemResolver[] resolvers, Folder parent = null, @@ -572,7 +577,7 @@ namespace Emby.Server.Implementations.Library { _logger.LogError(ex, "Error in GetFilteredFileSystemEntries isPhysicalRoot: {0} IsVf: {1}", isPhysicalRoot, isVf); - files = new FileSystemMetadata[] { }; + files = Array.Empty(); } else { @@ -600,13 +605,7 @@ namespace Emby.Server.Implementations.Library } public bool IgnoreFile(FileSystemMetadata file, BaseItem parent) - { - if (EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent))) - { - return true; - } - return false; - } + => EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent)); public List NormalizeRootPathList(IEnumerable paths) { @@ -646,7 +645,8 @@ namespace Emby.Server.Implementations.Library return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers); } - public IEnumerable ResolvePaths(IEnumerable files, + public IEnumerable ResolvePaths( + IEnumerable files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, @@ -672,6 +672,7 @@ namespace Emby.Server.Implementations.Library { ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService); } + items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions)); return items; } @@ -681,7 +682,8 @@ namespace Emby.Server.Implementations.Library return ResolveFileList(fileList, directoryService, parent, collectionType, resolvers, libraryOptions); } - private IEnumerable ResolveFileList(IEnumerable fileList, + private IEnumerable ResolveFileList( + IEnumerable fileList, IDirectoryService directoryService, Folder parent, string collectionType, @@ -766,6 +768,7 @@ namespace Emby.Server.Implementations.Library private volatile UserRootFolder _userRootFolder; private readonly object _syncLock = new object(); + public Folder GetUserRootFolder() { if (_userRootFolder == null) @@ -810,8 +813,6 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(path)); } - //_logger.LogInformation("FindByPath {0}", path); - var query = new InternalItemsQuery { Path = path, @@ -885,7 +886,6 @@ namespace Emby.Server.Implementations.Library /// /// The value. /// Task{Year}. - /// public Year GetYear(int value) { if (value <= 0) @@ -1027,20 +1027,25 @@ namespace Emby.Server.Implementations.Library private async Task ValidateTopLibraryFolders(CancellationToken cancellationToken) { - var rootChildren = RootFolder.Children.ToList(); - rootChildren = GetUserRootFolder().Children.ToList(); - await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false); // Start by just validating the children of the root, but go no further - await RootFolder.ValidateChildren(new SimpleProgress(), cancellationToken, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), recursive: false); + await RootFolder.ValidateChildren( + new SimpleProgress(), + cancellationToken, + new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), + recursive: false).ConfigureAwait(false); await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); - await GetUserRootFolder().ValidateChildren(new SimpleProgress(), cancellationToken, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), recursive: false).ConfigureAwait(false); + await GetUserRootFolder().ValidateChildren( + new SimpleProgress(), + cancellationToken, + new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), + recursive: false).ConfigureAwait(false); // Quickly scan CollectionFolders for changes - foreach (var folder in GetUserRootFolder().Children.OfType().ToList()) + foreach (var folder in GetUserRootFolder().Children.OfType()) { await folder.RefreshMetadata(cancellationToken).ConfigureAwait(false); } @@ -1204,7 +1209,7 @@ namespace Emby.Server.Implementations.Library private string GetCollectionType(string path) { return _fileSystem.GetFilePaths(path, new[] { ".collection" }, true, false) - .Select(i => Path.GetFileNameWithoutExtension(i)) + .Select(Path.GetFileNameWithoutExtension) .FirstOrDefault(i => !string.IsNullOrEmpty(i)); } @@ -1218,7 +1223,7 @@ namespace Emby.Server.Implementations.Library { if (id == Guid.Empty) { - throw new ArgumentException(nameof(id), "Guid can't be empty"); + throw new ArgumentException("Guid can't be empty", nameof(id)); } if (LibraryItemsCache.TryGetValue(id, out BaseItem item)) @@ -1386,17 +1391,7 @@ namespace Emby.Server.Implementations.Library var parents = query.AncestorIds.Select(i => GetItemById(i)).ToList(); - if (parents.All(i => - { - if (i is ICollectionFolder || i is UserView) - { - return true; - } - - //_logger.LogDebug("Query requires ancestor query due to type: " + i.GetType().Name); - return false; - - })) + if (parents.All(i => i is ICollectionFolder || i is UserView)) { // Optimize by querying against top level views query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray(); @@ -1452,17 +1447,7 @@ namespace Emby.Server.Implementations.Library private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List parents) { - if (parents.All(i => - { - if (i is ICollectionFolder || i is UserView) - { - return true; - } - - //_logger.LogDebug("Query requires ancestor query due to type: " + i.GetType().Name); - return false; - - })) + if (parents.All(i => i is ICollectionFolder || i is UserView)) { // Optimize by querying against top level views query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray(); @@ -1511,11 +1496,9 @@ namespace Emby.Server.Implementations.Library private IEnumerable GetTopParentIdsForQuery(BaseItem item, User user) { - var view = item as UserView; - - if (view != null) + if (item is UserView view) { - if (string.Equals(view.ViewType, CollectionType.LiveTv)) + if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.Ordinal)) { return new[] { view.Id }; } @@ -1528,8 +1511,10 @@ namespace Emby.Server.Implementations.Library { return GetTopParentIdsForQuery(displayParent, user); } + return Array.Empty(); } + if (!view.ParentId.Equals(Guid.Empty)) { var displayParent = GetItemById(view.ParentId); @@ -1537,6 +1522,7 @@ namespace Emby.Server.Implementations.Library { return GetTopParentIdsForQuery(displayParent, user); } + return Array.Empty(); } @@ -1550,11 +1536,11 @@ namespace Emby.Server.Implementations.Library .Where(i => user.IsFolderGrouped(i.Id)) .SelectMany(i => GetTopParentIdsForQuery(i, user)); } + return Array.Empty(); } - var collectionFolder = item as CollectionFolder; - if (collectionFolder != null) + if (item is CollectionFolder collectionFolder) { return collectionFolder.PhysicalFolderIds; } @@ -1564,6 +1550,7 @@ namespace Emby.Server.Implementations.Library { return new[] { topParent.Id }; } + return Array.Empty(); } @@ -1760,19 +1747,16 @@ namespace Emby.Server.Implementations.Library { var comparer = Comparers.FirstOrDefault(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase)); - if (comparer != null) + // If it requires a user, create a new one, and assign the user + if (comparer is IUserBaseItemComparer) { - // If it requires a user, create a new one, and assign the user - if (comparer is IUserBaseItemComparer) - { - var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType()); + var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType()); - userComparer.User = user; - userComparer.UserManager = _userManager; - userComparer.UserDataRepository = _userDataRepository; + userComparer.User = user; + userComparer.UserManager = _userManager; + userComparer.UserDataRepository = _userDataRepository; - return userComparer; - } + return userComparer; } return comparer; @@ -1783,7 +1767,6 @@ namespace Emby.Server.Implementations.Library /// /// The item. /// The parent item. - /// Task. public void CreateItem(BaseItem item, BaseItem parent) { CreateItems(new[] { item }, parent, CancellationToken.None); @@ -1793,20 +1776,23 @@ namespace Emby.Server.Implementations.Library /// Creates the items. /// /// The items. + /// The parent item /// The cancellation token. - /// Task. public void CreateItems(IEnumerable items, BaseItem parent, CancellationToken cancellationToken) { - ItemRepository.SaveItems(items, cancellationToken); + // Don't iterate multiple times + var itemsList = items.ToList(); - foreach (var item in items) + ItemRepository.SaveItems(itemsList, cancellationToken); + + foreach (var item in itemsList) { RegisterItem(item); } if (ItemAdded != null) { - foreach (var item in items) + foreach (var item in itemsList) { // With the live tv guide this just creates too much noise if (item.SourceType != SourceType.Library) @@ -1816,11 +1802,13 @@ namespace Emby.Server.Implementations.Library try { - ItemAdded(this, new ItemChangeEventArgs - { - Item = item, - Parent = parent ?? item.GetParent() - }); + ItemAdded( + this, + new ItemChangeEventArgs + { + Item = item, + Parent = parent ?? item.GetParent() + }); } catch (Exception ex) { @@ -1842,7 +1830,10 @@ namespace Emby.Server.Implementations.Library /// public void UpdateItems(IEnumerable items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) { - foreach (var item in items) + // Don't iterate multiple times + var itemsList = items.ToList(); + + foreach (var item in itemsList) { if (item.IsFileProtocol) { @@ -1854,14 +1845,11 @@ namespace Emby.Server.Implementations.Library RegisterItem(item); } - //var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name; - //_logger.LogDebug("Saving {0} to database.", logName); - - ItemRepository.SaveItems(items, cancellationToken); + ItemRepository.SaveItems(itemsList, cancellationToken); if (ItemUpdated != null) { - foreach (var item in items) + foreach (var item in itemsList) { // With the live tv guide this just creates too much noise if (item.SourceType != SourceType.Library) @@ -1871,12 +1859,14 @@ namespace Emby.Server.Implementations.Library try { - ItemUpdated(this, new ItemChangeEventArgs - { - Item = item, - Parent = parent, - UpdateReason = updateReason - }); + ItemUpdated( + this, + new ItemChangeEventArgs + { + Item = item, + Parent = parent, + UpdateReason = updateReason + }); } catch (Exception ex) { @@ -1890,9 +1880,9 @@ namespace Emby.Server.Implementations.Library /// Updates the item. /// /// The item. + /// The parent item. /// The update reason. /// The cancellation token. - /// Task. public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) { UpdateItems(new [] { item }, parent, updateReason, cancellationToken); @@ -1902,17 +1892,20 @@ namespace Emby.Server.Implementations.Library /// Reports the item removed. /// /// The item. + /// The parent item. public void ReportItemRemoved(BaseItem item, BaseItem parent) { if (ItemRemoved != null) { try { - ItemRemoved(this, new ItemChangeEventArgs - { - Item = item, - Parent = parent - }); + ItemRemoved( + this, + new ItemChangeEventArgs + { + Item = item, + Parent = parent + }); } catch (Exception ex) { @@ -2038,8 +2031,7 @@ namespace Emby.Server.Implementations.Library public string GetConfiguredContentType(BaseItem item, bool inheritConfiguredPath) { - var collectionFolder = item as ICollectionFolder; - if (collectionFolder != null) + if (item is ICollectionFolder collectionFolder) { return collectionFolder.CollectionType; } @@ -2049,13 +2041,11 @@ namespace Emby.Server.Implementations.Library private string GetContentTypeOverride(string path, bool inherit) { - var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path) || (inherit && !string.IsNullOrEmpty(i.Name) && _fileSystem.ContainsSubPath(i.Name, path))); - if (nameValuePair != null) - { - return nameValuePair.Value; - } - - return null; + var nameValuePair = ConfigurationManager.Configuration.ContentTypes + .FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path) + || (inherit && !string.IsNullOrEmpty(i.Name) + && _fileSystem.ContainsSubPath(i.Name, path))); + return nameValuePair?.Value; } private string GetTopFolderContentType(BaseItem item) @@ -2072,6 +2062,7 @@ namespace Emby.Server.Implementations.Library { break; } + item = parent; } @@ -2083,9 +2074,9 @@ namespace Emby.Server.Implementations.Library } private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24); - //private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromMinutes(1); - public UserView GetNamedView(User user, + public UserView GetNamedView( + User user, string name, string viewType, string sortName) @@ -2093,13 +2084,15 @@ namespace Emby.Server.Implementations.Library return GetNamedView(user, name, Guid.Empty, viewType, sortName); } - public UserView GetNamedView(string name, + public UserView GetNamedView( + string name, string viewType, string sortName) { - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, - "views", - _fileSystem.GetValidFilename(viewType)); + var path = Path.Combine( + ConfigurationManager.ApplicationPaths.InternalMetadataPath, + "views", + _fileSystem.GetValidFilename(viewType)); var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView)); @@ -2135,7 +2128,8 @@ namespace Emby.Server.Implementations.Library return item; } - public UserView GetNamedView(User user, + public UserView GetNamedView( + User user, string name, Guid parentId, string viewType, @@ -2164,10 +2158,10 @@ namespace Emby.Server.Implementations.Library Name = name, ViewType = viewType, ForcedSortName = sortName, - UserId = user.Id + UserId = user.Id, + DisplayParentId = parentId }; - item.DisplayParentId = parentId; CreateItem(item, null); @@ -2184,20 +2178,24 @@ namespace Emby.Server.Implementations.Library if (refresh) { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) - { - // Need to force save to increment DateLastSaved - ForceSave = true + _providerManagerFactory().QueueRefresh( + item.Id, + new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) + { + // Need to force save to increment DateLastSaved + ForceSave = true - }, RefreshPriority.Normal); + }, + RefreshPriority.Normal); } return item; } - public UserView GetShadowView(BaseItem parent, - string viewType, - string sortName) + public UserView GetShadowView( + BaseItem parent, + string viewType, + string sortName) { if (parent == null) { @@ -2248,18 +2246,21 @@ namespace Emby.Server.Implementations.Library if (refresh) { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) - { - // Need to force save to increment DateLastSaved - ForceSave = true - - }, RefreshPriority.Normal); + _providerManagerFactory().QueueRefresh( + item.Id, + new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) + { + // Need to force save to increment DateLastSaved + ForceSave = true + }, + RefreshPriority.Normal); } return item; } - public UserView GetNamedView(string name, + public UserView GetNamedView( + string name, Guid parentId, string viewType, string sortName, @@ -2322,17 +2323,21 @@ namespace Emby.Server.Implementations.Library if (refresh) { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) - { - // Need to force save to increment DateLastSaved - ForceSave = true - }, RefreshPriority.Normal); + _providerManagerFactory().QueueRefresh( + item.Id, + new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) + { + // Need to force save to increment DateLastSaved + ForceSave = true + }, + RefreshPriority.Normal); } return item; } - public void AddExternalSubtitleStreams(List streams, + public void AddExternalSubtitleStreams( + List streams, string videoPath, string[] files) { @@ -2436,6 +2441,7 @@ namespace Emby.Server.Implementations.Library { changed = true; } + episode.IndexNumber = episodeInfo.EpisodeNumber; } @@ -2445,6 +2451,7 @@ namespace Emby.Server.Implementations.Library { changed = true; } + episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber; } @@ -2454,6 +2461,7 @@ namespace Emby.Server.Implementations.Library { changed = true; } + episode.ParentIndexNumber = episodeInfo.SeasonNumber; } } @@ -2483,6 +2491,7 @@ namespace Emby.Server.Implementations.Library private NamingOptions _namingOptions; private string[] _videoFileExtensions; + private NamingOptions GetNamingOptionsInternal() { if (_namingOptions == null) @@ -2679,7 +2688,7 @@ namespace Emby.Server.Implementations.Library var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase); var changed = false; - if (!string.Equals(newPath, path)) + if (!string.Equals(newPath, path, StringComparison.Ordinal)) { if (to.IndexOf('/') != -1) { @@ -2803,6 +2812,7 @@ namespace Emby.Server.Implementations.Library { continue; } + throw; } } @@ -2907,6 +2917,7 @@ namespace Emby.Server.Implementations.Library } private const string ShortcutFileExtension = ".mblink"; + public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo) { AddMediaPathInternal(virtualFolderName, pathInfo, true); @@ -2923,7 +2934,7 @@ namespace Emby.Server.Implementations.Library if (string.IsNullOrWhiteSpace(path)) { - throw new ArgumentNullException(nameof(path)); + throw new ArgumentException(nameof(path)); } if (!Directory.Exists(path)) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index f424bdf5c4..58b3b6a69f 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -33,7 +33,6 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; @@ -58,7 +57,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private readonly IProviderManager _providerManager; private readonly IMediaEncoder _mediaEncoder; private readonly IProcessFactory _processFactory; - private readonly IAssemblyInfo _assemblyInfo; private IMediaSourceManager _mediaSourceManager; public static EmbyTV Current; @@ -74,7 +72,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public EmbyTV(IServerApplicationHost appHost, IStreamHelper streamHelper, IMediaSourceManager mediaSourceManager, - IAssemblyInfo assemblyInfo, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, @@ -101,7 +98,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _processFactory = processFactory; _liveTvManager = (LiveTvManager)liveTvManager; _jsonSerializer = jsonSerializer; - _assemblyInfo = assemblyInfo; _mediaSourceManager = mediaSourceManager; _streamHelper = streamHelper; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 8774371d52..7f426ea31f 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -151,7 +151,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun }); } - private static int RtpHeaderBytes = 12; + private const int RtpHeaderBytes = 12; + private async Task CopyTo(MediaBrowser.Model.Net.ISocket udpClient, string file, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) { var bufferSize = 81920; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index 1f8ca276e7..ece2cbd547 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public string OriginalStreamId { get; set; } public bool EnableStreamSharing { get; set; } - public string UniqueId { get; private set; } + public string UniqueId { get; } protected readonly IFileSystem FileSystem; protected readonly IServerApplicationPaths AppPaths; @@ -31,12 +31,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts protected readonly ILogger Logger; protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource(); - public string TunerHostId { get; private set; } + public string TunerHostId { get; } public DateTime DateOpened { get; protected set; } - public Func OnClose { get; set; } - public LiveStream(MediaSourceInfo mediaSource, TunerHostInfo tuner, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths) { OriginalMediaSource = mediaSource; @@ -76,26 +74,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts LiveStreamCancellationTokenSource.Cancel(); - if (OnClose != null) - { - return CloseWithExternalFn(); - } - return Task.CompletedTask; } - private async Task CloseWithExternalFn() - { - try - { - await OnClose().ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error closing live stream"); - } - } - protected Stream GetInputStream(string path, bool allowAsyncFileRead) { var fileOpenOptions = FileOpenOptions.SequentialScan; @@ -113,27 +94,26 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return DeleteTempFiles(GetStreamFilePaths()); } - protected async Task DeleteTempFiles(List paths, int retryCount = 0) + protected async Task DeleteTempFiles(IEnumerable paths, int retryCount = 0) { if (retryCount == 0) { - Logger.LogInformation("Deleting temp files {0}", string.Join(", ", paths.ToArray())); + Logger.LogInformation("Deleting temp files {0}", paths); } var failedFiles = new List(); foreach (var path in paths) { + if (!File.Exists(path)) + { + continue; + } + try { FileSystem.DeleteFile(path); } - catch (DirectoryNotFoundException) - { - } - catch (FileNotFoundException) - { - } catch (Exception ex) { Logger.LogError(ex, "Error deleting file {path}", path); @@ -157,8 +137,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token).Token; - var allowAsync = false; - // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 + // use non-async filestream on windows along with read due to https://github.com/dotnet/corefx/issues/6039 + var allowAsync = Environment.OSVersion.Platform != PlatformID.Win32NT; bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10; @@ -181,28 +161,24 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts Logger.LogInformation("Live Stream ended."); } - private Tuple GetNextFile(string currentFile) + private (string file, bool isLastFile) GetNextFile(string currentFile) { var files = GetStreamFilePaths(); - //logger.LogInformation("Live stream files: {0}", string.Join(", ", files.ToArray())); - if (string.IsNullOrEmpty(currentFile)) { - return new Tuple(files.Last(), true); + return (files.Last(), true); } var nextIndex = files.FindIndex(i => string.Equals(i, currentFile, StringComparison.OrdinalIgnoreCase)) + 1; var isLastFile = nextIndex == files.Count - 1; - return new Tuple(files.ElementAtOrDefault(nextIndex), isLastFile); + return (files.ElementAtOrDefault(nextIndex), isLastFile); } private async Task CopyFile(string path, bool seekFile, int emptyReadLimit, bool allowAsync, Stream stream, CancellationToken cancellationToken) { - //logger.LogInformation("Opening live stream file {0}. Empty read limit: {1}", path, emptyReadLimit); - using (var inputStream = (FileStream)GetInputStream(path, allowAsync)) { if (seekFile) @@ -218,7 +194,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts private void TrySeek(FileStream stream, long offset) { - //logger.LogInformation("TrySeek live stream"); + if (!stream.CanSeek) + { + return; + } + try { stream.Seek(offset, SeekOrigin.End); diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index c01bb0c501..dc73ba6b34 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -1,97 +1,97 @@ { - "Albums": "Albums", - "AppDeviceValues": "App: {0}, Device: {1}", - "Application": "Application", - "Artists": "Artists", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "Books": "Books", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", - "Channels": "Channels", - "ChapterNameValue": "Chapter {0}", - "Collections": "Collections", - "DeviceOfflineWithName": "{0} has disconnected", - "DeviceOnlineWithName": "{0} is connected", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "Favorites": "Favorites", - "Folders": "Folders", - "Genres": "Genres", - "HeaderAlbumArtists": "Album Artists", - "HeaderCameraUploads": "Camera Uploads", - "HeaderContinueWatching": "Continue Watching", - "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderFavoriteArtists": "Favorite Artists", - "HeaderFavoriteEpisodes": "Favorite Episodes", - "HeaderFavoriteShows": "Favorite Shows", - "HeaderFavoriteSongs": "Favorite Songs", - "HeaderLiveTV": "Live TV", - "HeaderNextUp": "Next Up", - "HeaderRecordingGroups": "Recording Groups", - "HomeVideos": "Home videos", - "Inherit": "Inherit", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "LabelRunningTimeValue": "Running time: {0}", - "Latest": "Latest", - "MessageApplicationUpdated": "Jellyfin Server has been updated", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", - "MessageServerConfigurationUpdated": "Server configuration has been updated", - "MixedContent": "Mixed content", - "Movies": "Movies", - "Music": "Music", - "MusicVideos": "Music videos", - "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", - "NotificationOptionAudioPlayback": "Audio playback started", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", - "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionInstallationFailed": "Installation failure", - "NotificationOptionNewLibraryContent": "New content added", - "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Server restart required", - "NotificationOptionTaskFailed": "Scheduled task failure", - "NotificationOptionUserLockedOut": "User locked out", - "NotificationOptionVideoPlayback": "Video playback started", - "NotificationOptionVideoPlaybackStopped": "Video playback stopped", - "Photos": "Photos", - "Playlists": "Playlists", + "Albums": "Álbumes", + "AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}", + "Application": "Aplicación", + "Artists": "Artistas", + "AuthenticationSucceededWithUserName": "{0} autenticado correctamente", + "Books": "Libros", + "CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}", + "Channels": "Canales", + "ChapterNameValue": "Capítulo {0}", + "Collections": "Colecciones", + "DeviceOfflineWithName": "{0} se ha desconectado", + "DeviceOnlineWithName": "{0} está conectado", + "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión desde {0}", + "Favorites": "Favoritos", + "Folders": "Carpetas", + "Genres": "Géneros", + "HeaderAlbumArtists": "Artistas de álbumes", + "HeaderCameraUploads": "Subidas de cámara", + "HeaderContinueWatching": "Continuar viendo", + "HeaderFavoriteAlbums": "Álbumes favoritos", + "HeaderFavoriteArtists": "Artistas favoritos", + "HeaderFavoriteEpisodes": "Episodios favoritos", + "HeaderFavoriteShows": "Programas favoritos", + "HeaderFavoriteSongs": "Canciones favoritas", + "HeaderLiveTV": "TV en vivo", + "HeaderNextUp": "Continuar Viendo", + "HeaderRecordingGroups": "Grupos de grabación", + "HomeVideos": "Videos caseros", + "Inherit": "Heredar", + "ItemAddedWithName": "{0} se ha añadido a la biblioteca", + "ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca", + "LabelIpAddressValue": "Dirección IP: {0}", + "LabelRunningTimeValue": "Tiempo de funcionamiento: {0}", + "Latest": "Últimos", + "MessageApplicationUpdated": "El servidor Jellyfin fue actualizado", + "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "Fue actualizada la sección {0} de la configuración del servidor", + "MessageServerConfigurationUpdated": "Fue actualizada la configuración del servidor", + "MixedContent": "Contenido mixto", + "Movies": "Películas", + "Music": "Música", + "MusicVideos": "Videos musicales", + "NameInstallFailed": "{0} error de instalación", + "NameSeasonNumber": "Temporada {0}", + "NameSeasonUnknown": "Temporada desconocida", + "NewVersionIsAvailable": "Disponible una nueva versión de Jellyfin para descargar.", + "NotificationOptionApplicationUpdateAvailable": "Actualización de la aplicación disponible", + "NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada", + "NotificationOptionAudioPlayback": "Se inició la reproducción de audio", + "NotificationOptionAudioPlaybackStopped": "Se detuvo la reproducción de audio", + "NotificationOptionCameraImageUploaded": "Imagen de la cámara cargada", + "NotificationOptionInstallationFailed": "Error de instalación", + "NotificationOptionNewLibraryContent": "Nuevo contenido añadido", + "NotificationOptionPluginError": "Error en plugin", + "NotificationOptionPluginInstalled": "Plugin instalado", + "NotificationOptionPluginUninstalled": "Plugin desinstalado", + "NotificationOptionPluginUpdateInstalled": "Actualización del complemento instalada", + "NotificationOptionServerRestartRequired": "Se requiere reinicio del servidor", + "NotificationOptionTaskFailed": "Error de tarea programada", + "NotificationOptionUserLockedOut": "Usuario bloqueado", + "NotificationOptionVideoPlayback": "Se inició la reproducción de video", + "NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida", + "Photos": "Fotos", + "Playlists": "Listas de reproducción", "Plugin": "Plugin", - "PluginInstalledWithName": "{0} was installed", - "PluginUninstalledWithName": "{0} was uninstalled", - "PluginUpdatedWithName": "{0} was updated", - "ProviderValue": "Provider: {0}", - "ScheduledTaskFailedWithName": "{0} failed", - "ScheduledTaskStartedWithName": "{0} started", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", + "PluginInstalledWithName": "{0} fue instalado", + "PluginUninstalledWithName": "{0} fue desinstalado", + "PluginUpdatedWithName": "{0} fue actualizado", + "ProviderValue": "Proveedor: {0}", + "ScheduledTaskFailedWithName": "{0} falló", + "ScheduledTaskStartedWithName": "{0} iniciada", + "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado", "Shows": "Series", - "Songs": "Songs", - "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", + "Songs": "Canciones", + "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", - "Sync": "Sync", - "System": "System", - "TvShows": "TV Shows", - "User": "User", - "UserCreatedWithName": "User {0} has been created", - "UserDeletedWithName": "User {0} has been deleted", - "UserDownloadingItemWithValues": "{0} is downloading {1}", - "UserLockedOutWithName": "User {0} has been locked out", - "UserOfflineFromDevice": "{0} has disconnected from {1}", - "UserOnlineFromDevice": "{0} is online from {1}", - "UserPasswordChangedWithName": "Password has been changed for user {0}", - "UserPolicyUpdatedWithName": "User policy has been updated for {0}", - "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}", - "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", - "ValueSpecialEpisodeName": "Special - {0}", - "VersionNumber": "Version {0}" + "SubtitleDownloadFailureFromForItem": "Fallo de descarga de subtítulos desde {0} para {1}", + "SubtitlesDownloadedForItem": "Descargar subtítulos para {0}", + "Sync": "Sincronizar", + "System": "Sistema", + "TvShows": "Series de TV", + "User": "Usuario", + "UserCreatedWithName": "El usuario {0} ha sido creado", + "UserDeletedWithName": "El usuario {0} ha sido borrado", + "UserDownloadingItemWithValues": "{0} está descargando {1}", + "UserLockedOutWithName": "El usuario {0} ha sido bloqueado", + "UserOfflineFromDevice": "{0} se ha desconectado de {1}", + "UserOnlineFromDevice": "{0} está en línea desde {1}", + "UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}", + "UserPolicyUpdatedWithName": "Actualizada política de usuario para {0}", + "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}", + "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}", + "ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia", + "ValueSpecialEpisodeName": "Especial - {0}", + "VersionNumber": "Versión {0}" } diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json index 7202be9f56..4b4db39a8b 100644 --- a/Emby.Server.Implementations/Localization/Core/fr-CA.json +++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json @@ -1,97 +1,97 @@ { "Albums": "Albums", - "AppDeviceValues": "App: {0}, Device: {1}", + "AppDeviceValues": "Application : {0}, Appareil : {1}", "Application": "Application", - "Artists": "Artists", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "Books": "Books", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", - "Channels": "Channels", - "ChapterNameValue": "Chapter {0}", + "Artists": "Artistes", + "AuthenticationSucceededWithUserName": "{0} s'est authentifié avec succès", + "Books": "Livres", + "CameraImageUploadedFrom": "Une nouvelle image de caméra a été téléchargée depuis {0}", + "Channels": "Chaînes", + "ChapterNameValue": "Chapitre {0}", "Collections": "Collections", - "DeviceOfflineWithName": "{0} has disconnected", - "DeviceOnlineWithName": "{0} is connected", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "Favorites": "Favorites", - "Folders": "Folders", + "DeviceOfflineWithName": "{0} s'est déconnecté", + "DeviceOnlineWithName": "{0} est connecté", + "FailedLoginAttemptWithUserName": "Échec d'une tentative de connexion de {0}", + "Favorites": "Favoris", + "Folders": "Dossiers", "Genres": "Genres", - "HeaderAlbumArtists": "Album Artists", - "HeaderCameraUploads": "Camera Uploads", + "HeaderAlbumArtists": "Artistes de l'album", + "HeaderCameraUploads": "Photos transférées", "HeaderContinueWatching": "Continuer à regarder", - "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderFavoriteArtists": "Favorite Artists", - "HeaderFavoriteEpisodes": "Favorite Episodes", - "HeaderFavoriteShows": "Favorite Shows", - "HeaderFavoriteSongs": "Favorite Songs", - "HeaderLiveTV": "Live TV", + "HeaderFavoriteAlbums": "Albums favoris", + "HeaderFavoriteArtists": "Artistes favoris", + "HeaderFavoriteEpisodes": "Épisodes favoris", + "HeaderFavoriteShows": "Séries favorites", + "HeaderFavoriteSongs": "Chansons favorites", + "HeaderLiveTV": "TV en direct", "HeaderNextUp": "À Suivre", - "HeaderRecordingGroups": "Recording Groups", - "HomeVideos": "Home videos", - "Inherit": "Inherit", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "LabelRunningTimeValue": "Running time: {0}", - "Latest": "Latest", - "MessageApplicationUpdated": "Jellyfin Server has been updated", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", - "MessageServerConfigurationUpdated": "Server configuration has been updated", - "MixedContent": "Mixed content", - "Movies": "Movies", - "Music": "Music", - "MusicVideos": "Music videos", - "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", - "NotificationOptionAudioPlayback": "Audio playback started", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", - "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionInstallationFailed": "Installation failure", - "NotificationOptionNewLibraryContent": "New content added", - "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Server restart required", - "NotificationOptionTaskFailed": "Scheduled task failure", - "NotificationOptionUserLockedOut": "User locked out", - "NotificationOptionVideoPlayback": "Video playback started", - "NotificationOptionVideoPlaybackStopped": "Video playback stopped", + "HeaderRecordingGroups": "Groupes d'enregistrements", + "HomeVideos": "Vidéos personnelles", + "Inherit": "Hériter", + "ItemAddedWithName": "{0} a été ajouté à la médiathèque", + "ItemRemovedWithName": "{0} a été supprimé de la médiathèque", + "LabelIpAddressValue": "Adresse IP : {0}", + "LabelRunningTimeValue": "Durée : {0}", + "Latest": "Derniers", + "MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour", + "MessageApplicationUpdatedTo": "Le serveur Jellyfin a été mis à jour vers la version {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour", + "MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour", + "MixedContent": "Contenu mixte", + "Movies": "Films", + "Music": "Musique", + "MusicVideos": "Vidéos musicales", + "NameInstallFailed": "{0} échec d'installation", + "NameSeasonNumber": "Saison {0}", + "NameSeasonUnknown": "Saison Inconnue", + "NewVersionIsAvailable": "Une nouvelle version du serveur Jellyfin est disponible au téléchargement.", + "NotificationOptionApplicationUpdateAvailable": "Mise à jour de l'application disponible", + "NotificationOptionApplicationUpdateInstalled": "Mise à jour de l'application installée", + "NotificationOptionAudioPlayback": "Lecture audio démarrée", + "NotificationOptionAudioPlaybackStopped": "Lecture audio arrêtée", + "NotificationOptionCameraImageUploaded": "L'image de l'appareil photo a été transférée", + "NotificationOptionInstallationFailed": "Échec d'installation", + "NotificationOptionNewLibraryContent": "Nouveau contenu ajouté", + "NotificationOptionPluginError": "Erreur d'extension", + "NotificationOptionPluginInstalled": "Extension installée", + "NotificationOptionPluginUninstalled": "Extension désinstallée", + "NotificationOptionPluginUpdateInstalled": "Mise à jour d'extension installée", + "NotificationOptionServerRestartRequired": "Un redémarrage du serveur est requis", + "NotificationOptionTaskFailed": "Échec de tâche planifiée", + "NotificationOptionUserLockedOut": "Utilisateur verrouillé", + "NotificationOptionVideoPlayback": "Lecture vidéo démarrée", + "NotificationOptionVideoPlaybackStopped": "Lecture vidéo arrêtée", "Photos": "Photos", - "Playlists": "Playlists", - "Plugin": "Plugin", - "PluginInstalledWithName": "{0} was installed", - "PluginUninstalledWithName": "{0} was uninstalled", - "PluginUpdatedWithName": "{0} was updated", - "ProviderValue": "Provider: {0}", - "ScheduledTaskFailedWithName": "{0} failed", - "ScheduledTaskStartedWithName": "{0} started", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", - "Shows": "Series", - "Songs": "Songs", - "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", + "Playlists": "Listes de lecture", + "Plugin": "Extension", + "PluginInstalledWithName": "{0} a été installé", + "PluginUninstalledWithName": "{0} a été désinstallé", + "PluginUpdatedWithName": "{0} a été mis à jour", + "ProviderValue": "Fournisseur : {0}", + "ScheduledTaskFailedWithName": "{0} a échoué", + "ScheduledTaskStartedWithName": "{0} a commencé", + "ServerNameNeedsToBeRestarted": "{0} doit être redémarré", + "Shows": "Émissions", + "Songs": "Chansons", + "StartupEmbyServerIsLoading": "Le serveur Jellyfin est en cours de chargement. Veuillez réessayer dans quelques instants.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", - "Sync": "Sync", - "System": "System", - "TvShows": "TV Shows", - "User": "User", - "UserCreatedWithName": "User {0} has been created", - "UserDeletedWithName": "User {0} has been deleted", - "UserDownloadingItemWithValues": "{0} is downloading {1}", - "UserLockedOutWithName": "User {0} has been locked out", - "UserOfflineFromDevice": "{0} has disconnected from {1}", - "UserOnlineFromDevice": "{0} is online from {1}", - "UserPasswordChangedWithName": "Password has been changed for user {0}", - "UserPolicyUpdatedWithName": "User policy has been updated for {0}", - "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}", - "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", + "SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}", + "SubtitlesDownloadedForItem": "Les sous-titres de {0} ont été téléchargés", + "Sync": "Synchroniser", + "System": "Système", + "TvShows": "Séries Télé", + "User": "Utilisateur", + "UserCreatedWithName": "L'utilisateur {0} a été créé", + "UserDeletedWithName": "L'utilisateur {0} a été supprimé", + "UserDownloadingItemWithValues": "{0} est en train de télécharger {1}", + "UserLockedOutWithName": "L'utilisateur {0} a été verrouillé", + "UserOfflineFromDevice": "{0} s'est déconnecté depuis {1}", + "UserOnlineFromDevice": "{0} s'est connecté depuis {1}", + "UserPasswordChangedWithName": "Le mot de passe pour l'utilisateur {0} a été modifié", + "UserPolicyUpdatedWithName": "La politique de l'utilisateur a été mise à jour pour {0}", + "UserStartedPlayingItemWithValues": "{0} est en train de lire {1} sur {2}", + "UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}", + "ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque", "ValueSpecialEpisodeName": "Spécial - {0}", "VersionNumber": "Version {0}" } diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 52afb4e492..e434b7605b 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -44,7 +44,7 @@ "NameInstallFailed": "{0} échec d'installation", "NameSeasonNumber": "Saison {0}", "NameSeasonUnknown": "Saison Inconnue", - "NewVersionIsAvailable": "Une nouvelle version d'Jellyfin Serveur est disponible au téléchargement.", + "NewVersionIsAvailable": "Une nouvelle version de Jellyfin Serveur est disponible au téléchargement.", "NotificationOptionApplicationUpdateAvailable": "Mise à jour de l'application disponible", "NotificationOptionApplicationUpdateInstalled": "Mise à jour de l'application installée", "NotificationOptionAudioPlayback": "Lecture audio démarrée", @@ -89,7 +89,7 @@ "UserOnlineFromDevice": "{0} s'est connecté depuis {1}", "UserPasswordChangedWithName": "Le mot de passe pour l'utilisateur {0} a été modifié", "UserPolicyUpdatedWithName": "La politique de l'utilisateur a été mise à jour pour {0}", - "UserStartedPlayingItemWithValues": "{0} est entrain de lire {1} sur {2}", + "UserStartedPlayingItemWithValues": "{0} est en train de lire {1} sur {2}", "UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}", "ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre librairie", "ValueSpecialEpisodeName": "Spécial - {0}", diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index fff1d1f0ec..0ed998c4bd 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -1,8 +1,8 @@ { - "Albums": "Albums", + "Albums": "אלבומים", "AppDeviceValues": "App: {0}, Device: {1}", - "Application": "Application", - "Artists": "Artists", + "Application": "אפליקציה", + "Artists": "אמנים", "AuthenticationSucceededWithUserName": "{0} successfully authenticated", "Books": "ספרים", "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index a5f1e8f94d..357883cd37 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -34,17 +34,17 @@ "LabelRunningTimeValue": "Durata: {0}", "Latest": "Più recenti", "MessageApplicationUpdated": "Il Server Jellyfin è stato aggiornato", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", + "MessageApplicationUpdatedTo": "Jellyfin Server è stato aggiornato a {0}", "MessageNamedServerConfigurationUpdatedWithValue": "La sezione {0} della configurazione server è stata aggiornata", "MessageServerConfigurationUpdated": "La configurazione del server è stata aggiornata", "MixedContent": "Contenuto misto", "Movies": "Film", "Music": "Musica", "MusicVideos": "Video musicali", - "NameInstallFailed": "{0} installation failed", + "NameInstallFailed": "{0} installazione fallita", "NameSeasonNumber": "Stagione {0}", "NameSeasonUnknown": "Stagione sconosciuto", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "NewVersionIsAvailable": "Una nuova versione di Jellyfin Server è disponibile per il download.", "NotificationOptionApplicationUpdateAvailable": "Aggiornamento dell'applicazione disponibile", "NotificationOptionApplicationUpdateInstalled": "Aggiornamento dell'applicazione installato", "NotificationOptionAudioPlayback": "La riproduzione audio è iniziata", @@ -70,12 +70,12 @@ "ProviderValue": "Provider: {0}", "ScheduledTaskFailedWithName": "{0} fallito", "ScheduledTaskStartedWithName": "{0} avviati", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", + "ServerNameNeedsToBeRestarted": "{0} deve essere riavviato", "Shows": "Programmi", "Songs": "Canzoni", "StartupEmbyServerIsLoading": "Jellyfin server si sta avviando. Per favore riprova più tardi.", "SubtitleDownloadFailureForItem": "Impossibile scaricare i sottotitoli per {0}", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", + "SubtitleDownloadFailureFromForItem": "Impossibile scaricare i sottotitoli da {0} per {1}", "SubtitlesDownloadedForItem": "Sottotitoli scaricati per {0}", "Sync": "Sincronizza", "System": "Sistema", @@ -91,7 +91,7 @@ "UserPolicyUpdatedWithName": "La politica dell'utente è stata aggiornata per {0}", "UserStartedPlayingItemWithValues": "{0} ha avviato la riproduzione di {1}", "UserStoppedPlayingItemWithValues": "{0} ha interrotto la riproduzione di {1}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", + "ValueHasBeenAddedToLibrary": "{0} è stato aggiunto alla tua libreria multimediale", "ValueSpecialEpisodeName": "Speciale - {0}", "VersionNumber": "Versione {0}" } diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json index 658d168e9f..23841f37d6 100644 --- a/Emby.Server.Implementations/Localization/Core/kk.json +++ b/Emby.Server.Implementations/Localization/Core/kk.json @@ -3,15 +3,15 @@ "AppDeviceValues": "Qoldanba: {0}, Qurylǵy: {1}", "Application": "Qoldanba", "Artists": "Oryndaýshylar", - "AuthenticationSucceededWithUserName": "{0} túpnusqalyǵyn rastalýy sátti", + "AuthenticationSucceededWithUserName": "{0} túpnusqalyq rastalýy sátti aıaqtaldy", "Books": "Kitaptar", - "CameraImageUploadedFrom": "Jańa sýret {0} kamerasynan júktep alyndy", + "CameraImageUploadedFrom": "{0} kamerasynan jańa sýret júktep alyndy", "Channels": "Arnalar", "ChapterNameValue": "{0}-sahna", "Collections": "Jıyntyqtar", "DeviceOfflineWithName": "{0} ajyratylǵan", "DeviceOnlineWithName": "{0} qosylǵan", - "FailedLoginAttemptWithUserName": "{0} tarapynan kirý áreketi sátsiz", + "FailedLoginAttemptWithUserName": "{0} tarapynan kirý áreketi sátsiz aıaqtaldy", "Favorites": "Tańdaýlylar", "Folders": "Qaltalar", "Genres": "Janrlar", @@ -28,13 +28,13 @@ "HeaderRecordingGroups": "Jazba toptary", "HomeVideos": "Úılik beıneler", "Inherit": "Muraǵa ıelený", - "ItemAddedWithName": "{0} tasyǵyshhanaǵa ústelindi", + "ItemAddedWithName": "{0} tasyǵyshhanaǵa ústeldi", "ItemRemovedWithName": "{0} tasyǵyshhanadan alastaldy", "LabelIpAddressValue": "IP-mekenjaıy: {0}", "LabelRunningTimeValue": "Oınatý ýaqyty: {0}", "Latest": "Eń keıingi", "MessageApplicationUpdated": "Jellyfin Serveri jańartyldy", - "MessageApplicationUpdatedTo": "Jellyfin Serveri {0} deńgeıge jańartyldy", + "MessageApplicationUpdatedTo": "Jellyfin Serveri {0} nusqasyna jańartyldy", "MessageNamedServerConfigurationUpdatedWithValue": "Server teńsheliminiń {0} bólimi jańartyldy", "MessageServerConfigurationUpdated": "Server teńshelimi jańartyldy", "MixedContent": "Aralas mazmun", diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index aaedf08505..dbc9c4c4b8 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -5,7 +5,7 @@ "Artists": "Artistas", "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", "Books": "Livros", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", + "CameraImageUploadedFrom": "Uma nova imagem da câmera foi submetida de {0}", "Channels": "Canais", "ChapterNameValue": "Capítulo {0}", "Collections": "Coletâneas", @@ -30,21 +30,21 @@ "Inherit": "Herdar", "ItemAddedWithName": "{0} foi adicionado à biblioteca", "ItemRemovedWithName": "{0} foi removido da biblioteca", - "LabelIpAddressValue": "Endereço ip: {0}", + "LabelIpAddressValue": "Endereço IP: {0}", "LabelRunningTimeValue": "Tempo de execução: {0}", "Latest": "Recente", "MessageApplicationUpdated": "O servidor Jellyfin foi atualizado", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", + "MessageApplicationUpdatedTo": "O Servidor Jellyfin foi atualizado para {0}", "MessageNamedServerConfigurationUpdatedWithValue": "A seção {0} da configuração do servidor foi atualizada", "MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada", "MixedContent": "Conteúdo misto", "Movies": "Filmes", "Music": "Música", "MusicVideos": "Vídeos musicais", - "NameInstallFailed": "{0} installation failed", + "NameInstallFailed": "A instalação de {0} falhou", "NameSeasonNumber": "Temporada {0}", "NameSeasonUnknown": "Temporada Desconhecida", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "NewVersionIsAvailable": "Uma nova versão do servidor Jellyfin está disponível para download.", "NotificationOptionApplicationUpdateAvailable": "Atualização de aplicativo disponível", "NotificationOptionApplicationUpdateInstalled": "Atualização de aplicativo instalada", "NotificationOptionAudioPlayback": "Reprodução de áudio iniciada", @@ -70,12 +70,12 @@ "ProviderValue": "Provedor: {0}", "ScheduledTaskFailedWithName": "{0} falhou", "ScheduledTaskStartedWithName": "{0} iniciada", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", + "ServerNameNeedsToBeRestarted": "O servidor {0} precisa ser reiniciado", "Shows": "Séries", "Songs": "Músicas", "StartupEmbyServerIsLoading": "O Servidor Jellyfin está carregando. Por favor tente novamente em breve.", "SubtitleDownloadFailureForItem": "Download de legendas falhou para {0}", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", + "SubtitleDownloadFailureFromForItem": "Houve um problema ao baixar as legendas de {0} para {1}", "SubtitlesDownloadedForItem": "Legendas baixadas para {0}", "Sync": "Sincronizar", "System": "Sistema", @@ -91,7 +91,7 @@ "UserPolicyUpdatedWithName": "A política de usuário foi atualizada para {0}", "UserStartedPlayingItemWithValues": "{0} iniciou a reprodução de {1}", "UserStoppedPlayingItemWithValues": "{0} parou de reproduzir {1}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", + "ValueHasBeenAddedToLibrary": "{0} foi adicionado a sua biblioteca", "ValueSpecialEpisodeName": "Especial - {0}", "VersionNumber": "Versão {0}" } diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index e850257d4a..b50ff4706e 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -1,62 +1,62 @@ { - "Albums": "Albums", - "AppDeviceValues": "App: {0}, Device: {1}", - "Application": "Application", - "Artists": "Artists", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "Books": "Books", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", - "Channels": "Channels", - "ChapterNameValue": "Chapter {0}", - "Collections": "Collections", + "Albums": "Albumi", + "AppDeviceValues": "Aplikacija: {0}, Naprava: {1}", + "Application": "Aplikacija", + "Artists": "Izvajalci", + "AuthenticationSucceededWithUserName": "{0} preverjanje uspešno", + "Books": "Knjige", + "CameraImageUploadedFrom": "Nova fotografija je bila naložena z {0}", + "Channels": "Kanali", + "ChapterNameValue": "Poglavje {0}", + "Collections": "Zbirke", "DeviceOfflineWithName": "{0} has disconnected", - "DeviceOnlineWithName": "{0} is connected", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "Favorites": "Favorites", - "Folders": "Folders", - "Genres": "Genres", - "HeaderAlbumArtists": "Album Artists", - "HeaderCameraUploads": "Camera Uploads", - "HeaderContinueWatching": "Continue Watching", - "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderFavoriteArtists": "Favorite Artists", - "HeaderFavoriteEpisodes": "Favorite Episodes", - "HeaderFavoriteShows": "Favorite Shows", - "HeaderFavoriteSongs": "Favorite Songs", - "HeaderLiveTV": "Live TV", - "HeaderNextUp": "Next Up", - "HeaderRecordingGroups": "Recording Groups", - "HomeVideos": "Home videos", - "Inherit": "Inherit", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "LabelRunningTimeValue": "Running time: {0}", - "Latest": "Latest", - "MessageApplicationUpdated": "Jellyfin Server has been updated", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", + "DeviceOnlineWithName": "{0} je povezan", + "FailedLoginAttemptWithUserName": "Neuspešen poskus prijave z {0}", + "Favorites": "Priljubljeni", + "Folders": "Mape", + "Genres": "Zvrsti", + "HeaderAlbumArtists": "Izvajalci albuma", + "HeaderCameraUploads": "Posnetki kamere", + "HeaderContinueWatching": "Nadaljuj gledanje", + "HeaderFavoriteAlbums": "Priljubljeni albumi", + "HeaderFavoriteArtists": "Priljubljeni izvajalci", + "HeaderFavoriteEpisodes": "Priljubljene epizode", + "HeaderFavoriteShows": "Priljubljene serije", + "HeaderFavoriteSongs": "Priljubljene pesmi", + "HeaderLiveTV": "TV v živo", + "HeaderNextUp": "Sledi", + "HeaderRecordingGroups": "Zbirke posnetkov", + "HomeVideos": "Domači posnetki", + "Inherit": "Podeduj", + "ItemAddedWithName": "{0} je dodan v knjižnico", + "ItemRemovedWithName": "{0} je bil odstranjen iz knjižnice", + "LabelIpAddressValue": "IP naslov: {0}", + "LabelRunningTimeValue": "Čas trajanja: {0}", + "Latest": "Najnovejše", + "MessageApplicationUpdated": "Jellyfin strežnik je bil posodobljen", + "MessageApplicationUpdatedTo": "Jellyfin strežnik je bil posodobljen na {0}", "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", - "MessageServerConfigurationUpdated": "Server configuration has been updated", - "MixedContent": "Mixed content", - "Movies": "Movies", - "Music": "Music", - "MusicVideos": "Music videos", - "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", - "NotificationOptionAudioPlayback": "Audio playback started", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", - "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionInstallationFailed": "Installation failure", - "NotificationOptionNewLibraryContent": "New content added", - "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Server restart required", + "MessageServerConfigurationUpdated": "Nastavitve strežnika so bile posodobljene", + "MixedContent": "Razne vsebine", + "Movies": "Filmi", + "Music": "Glasba", + "MusicVideos": "Glasbeni posnetki", + "NameInstallFailed": "{0} namestitev neuspešna", + "NameSeasonNumber": "Sezona {0}", + "NameSeasonUnknown": "Season neznana", + "NewVersionIsAvailable": "Nova razničica Jellyfin strežnika je na voljo za prenos.", + "NotificationOptionApplicationUpdateAvailable": "Posodobitev aplikacije je na voljo", + "NotificationOptionApplicationUpdateInstalled": "Posodobitev aplikacije je bila nameščena", + "NotificationOptionAudioPlayback": "Predvajanje zvoka začeto", + "NotificationOptionAudioPlaybackStopped": "Predvajanje zvoka zaustavljeno", + "NotificationOptionCameraImageUploaded": "Posnetek kamere naložen", + "NotificationOptionInstallationFailed": "Napaka pri nameščanju", + "NotificationOptionNewLibraryContent": "Nove vsebine dodane", + "NotificationOptionPluginError": "Napaka dodatka", + "NotificationOptionPluginInstalled": "Dodatek nameščen", + "NotificationOptionPluginUninstalled": "Dodatek odstranjen", + "NotificationOptionPluginUpdateInstalled": "Posodobitev dodatka nameščena", + "NotificationOptionServerRestartRequired": "Potreben je ponovni zagon strežnika", "NotificationOptionTaskFailed": "Scheduled task failure", "NotificationOptionUserLockedOut": "User locked out", "NotificationOptionVideoPlayback": "Video playback started", diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index 495f82db6d..9e00eba62f 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -1,12 +1,12 @@ { - "Albums": "Albums", - "AppDeviceValues": "App: {0}, Device: {1}", - "Application": "Application", - "Artists": "Artists", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "Books": "Books", + "Albums": "Albümler", + "AppDeviceValues": "Uygulama: {0}, Aygıt: {1}", + "Application": "Uygulama", + "Artists": "Sanatçılar", + "AuthenticationSucceededWithUserName": "{0} başarı ile giriş yaptı", + "Books": "Kitaplar", "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", - "Channels": "Channels", + "Channels": "Kanallar", "ChapterNameValue": "Chapter {0}", "Collections": "Collections", "DeviceOfflineWithName": "{0} has disconnected", @@ -17,8 +17,8 @@ "Genres": "Genres", "HeaderAlbumArtists": "Album Artists", "HeaderCameraUploads": "Camera Uploads", - "HeaderContinueWatching": "Continue Watching", - "HeaderFavoriteAlbums": "Favorite Albums", + "HeaderContinueWatching": "İzlemeye Devam Et", + "HeaderFavoriteAlbums": "Favori Albümler", "HeaderFavoriteArtists": "Favorite Artists", "HeaderFavoriteEpisodes": "Favorite Episodes", "HeaderFavoriteShows": "Favori Showlar", @@ -30,21 +30,21 @@ "Inherit": "Inherit", "ItemAddedWithName": "{0} was added to the library", "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "LabelRunningTimeValue": "Running time: {0}", + "LabelIpAddressValue": "Ip adresi: {0}", + "LabelRunningTimeValue": "Çalışma süresi: {0}", "Latest": "Latest", - "MessageApplicationUpdated": "Jellyfin Server has been updated", + "MessageApplicationUpdated": "Jellyfin Sunucusu güncellendi", "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", "MessageServerConfigurationUpdated": "Server configuration has been updated", "MixedContent": "Mixed content", "Movies": "Movies", - "Music": "Music", - "MusicVideos": "Music videos", - "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "Music": "Müzik", + "MusicVideos": "Müzik videoları", + "NameInstallFailed": "{0} kurulum başarısız", + "NameSeasonNumber": "Sezon {0}", + "NameSeasonUnknown": "Bilinmeyen Sezon", + "NewVersionIsAvailable": "Jellyfin Sunucusunun yeni bir versiyonu indirmek için hazır.", "NotificationOptionApplicationUpdateAvailable": "Application update available", "NotificationOptionApplicationUpdateInstalled": "Application update installed", "NotificationOptionAudioPlayback": "Audio playback started", diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 8910a6bce8..6f7d362d3b 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -34,14 +34,14 @@ "LabelRunningTimeValue": "运行时间:{0}", "Latest": "最新", "MessageApplicationUpdated": "Jellyfin 服务器已更新", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", + "MessageApplicationUpdatedTo": "Jellyfin Server 的版本已更新为 {0}", "MessageNamedServerConfigurationUpdatedWithValue": "服务器配置 {0} 部分已更新", "MessageServerConfigurationUpdated": "服务器配置已更新", "MixedContent": "混合内容", "Movies": "电影", "Music": "音乐", "MusicVideos": "音乐视频", - "NameInstallFailed": "{0} installation failed", + "NameInstallFailed": "{0} 安装失败", "NameSeasonNumber": "季 {0}", "NameSeasonUnknown": "未知季", "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", @@ -70,7 +70,7 @@ "ProviderValue": "提供商:{0}", "ScheduledTaskFailedWithName": "{0} 已失败", "ScheduledTaskStartedWithName": "{0} 已开始", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", + "ServerNameNeedsToBeRestarted": "{0} 需要重新启动", "Shows": "节目", "Songs": "歌曲", "StartupEmbyServerIsLoading": "Jellyfin 服务器加载中。请稍后再试。", diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 762649b716..8c49b64055 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -11,7 +11,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; @@ -35,7 +34,6 @@ namespace Emby.Server.Implementations.Localization private readonly Dictionary> _allParentalRatings = new Dictionary>(StringComparer.OrdinalIgnoreCase); - private readonly IFileSystem _fileSystem; private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; @@ -44,40 +42,38 @@ namespace Emby.Server.Implementations.Localization /// Initializes a new instance of the class. /// /// The configuration manager. - /// The file system. /// The json serializer. + /// The logger factory public LocalizationManager( IServerConfigurationManager configurationManager, - IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILoggerFactory loggerFactory) { _configurationManager = configurationManager; - _fileSystem = fileSystem; _jsonSerializer = jsonSerializer; _logger = loggerFactory.CreateLogger(nameof(LocalizationManager)); } public async Task LoadAll() { - const string ratingsResource = "Emby.Server.Implementations.Localization.Ratings."; + const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; // Extract from the assembly foreach (var resource in _assembly.GetManifestResourceNames()) { - if (!resource.StartsWith(ratingsResource)) + if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) { continue; } - string countryCode = resource.Substring(ratingsResource.Length, 2); + string countryCode = resource.Substring(RatingsResource.Length, 2); var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); using (var str = _assembly.GetManifestResourceStream(resource)) using (var reader = new StreamReader(str)) { string line; - while ((line = await reader.ReadLineAsync()) != null) + while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) { if (string.IsNullOrWhiteSpace(line)) { @@ -102,7 +98,7 @@ namespace Emby.Server.Implementations.Localization _allParentalRatings[countryCode] = dict; } - await LoadCultures(); + await LoadCultures().ConfigureAwait(false); } public string NormalizeFormKD(string text) @@ -121,14 +117,14 @@ namespace Emby.Server.Implementations.Localization { List list = new List(); - const string path = "Emby.Server.Implementations.Localization.iso6392.txt"; + const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; - using (var stream = _assembly.GetManifestResourceStream(path)) + using (var stream = _assembly.GetManifestResourceStream(ResourcePath)) using (var reader = new StreamReader(stream)) { while (!reader.EndOfStream) { - var line = await reader.ReadLineAsync(); + var line = await reader.ReadLineAsync().ConfigureAwait(false); if (string.IsNullOrWhiteSpace(line)) { @@ -154,11 +150,11 @@ namespace Emby.Server.Implementations.Localization string[] threeletterNames; if (string.IsNullOrWhiteSpace(parts[1])) { - threeletterNames = new [] { parts[0] }; + threeletterNames = new[] { parts[0] }; } else { - threeletterNames = new [] { parts[0], parts[1] }; + threeletterNames = new[] { parts[0], parts[1] }; } list.Add(new CultureDto @@ -218,6 +214,7 @@ namespace Emby.Server.Implementations.Localization /// Gets the ratings. /// /// The country code. + /// The ratings private Dictionary GetRatings(string countryCode) { _allParentalRatings.TryGetValue(countryCode, out var value); @@ -227,9 +224,12 @@ namespace Emby.Server.Implementations.Localization private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; + /// /// /// Gets the rating level. /// + /// Rating field + /// The rating level> public int? GetRatingLevel(string rating) { if (string.IsNullOrEmpty(rating)) @@ -301,6 +301,7 @@ namespace Emby.Server.Implementations.Localization { culture = _configurationManager.Configuration.UICulture; } + if (string.IsNullOrEmpty(culture)) { culture = DefaultCulture; @@ -346,8 +347,8 @@ namespace Emby.Server.Implementations.Localization var namespaceName = GetType().Namespace + "." + prefix; - await CopyInto(dictionary, namespaceName + "." + baseFilename); - await CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture)); + await CopyInto(dictionary, namespaceName + "." + baseFilename).ConfigureAwait(false); + await CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture)).ConfigureAwait(false); return dictionary; } @@ -359,7 +360,7 @@ namespace Emby.Server.Implementations.Localization // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain if (stream != null) { - var dict = await _jsonSerializer.DeserializeFromStreamAsync>(stream); + var dict = await _jsonSerializer.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); foreach (var key in dict.Keys) { diff --git a/Emby.Server.Implementations/Net/DisposableManagedObjectBase.cs b/Emby.Server.Implementations/Net/DisposableManagedObjectBase.cs deleted file mode 100644 index 304b445651..0000000000 --- a/Emby.Server.Implementations/Net/DisposableManagedObjectBase.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; - -namespace Emby.Server.Implementations.Net -{ - /// - /// Correclty implements the interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an property. - /// - public abstract class DisposableManagedObjectBase : IDisposable - { - - #region Public Methods - - /// - /// Override this method and dispose any objects you own the lifetime of if disposing is true; - /// - /// True if managed objects should be disposed, if false, only unmanaged resources should be released. - protected abstract void Dispose(bool disposing); - - - //TODO Remove and reimplement using the IsDisposed property directly. - /// - /// Throws an if the property is true. - /// - /// - /// Thrown if the property is true. - /// - protected virtual void ThrowIfDisposed() - { - if (IsDisposed) throw new ObjectDisposedException(GetType().Name); - } - - #endregion - - #region Public Properties - - /// - /// Sets or returns a boolean indicating whether or not this instance has been disposed. - /// - /// - public bool IsDisposed - { - get; - private set; - } - - #endregion - - #region IDisposable Members - - /// - /// Disposes this object instance and all internally managed resources. - /// - /// - /// Sets the property to true. Does not explicitly throw an exception if called multiple times, but makes no promises about behaviour of derived classes. - /// - /// - public void Dispose() - { - IsDisposed = true; - - Dispose(true); - } - - #endregion - } -} diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs index 6beb14f558..492f48abe8 100644 --- a/Emby.Server.Implementations/Net/SocketFactory.cs +++ b/Emby.Server.Implementations/Net/SocketFactory.cs @@ -4,7 +4,6 @@ using System.Net; using System.Net.Sockets; using Emby.Server.Implementations.Networking; using MediaBrowser.Model.Net; -using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Net { @@ -19,7 +18,10 @@ namespace Emby.Server.Implementations.Net public ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort) { - if (remotePort < 0) throw new ArgumentException("remotePort cannot be less than zero.", nameof(remotePort)); + if (remotePort < 0) + { + throw new ArgumentException("remotePort cannot be less than zero.", nameof(remotePort)); + } var addressFamily = remoteAddress.AddressFamily == IpAddressFamily.InterNetwork ? AddressFamily.InterNetwork @@ -42,8 +44,7 @@ namespace Emby.Server.Implementations.Net } catch { - if (retVal != null) - retVal.Dispose(); + retVal?.Dispose(); throw; } @@ -55,7 +56,10 @@ namespace Emby.Server.Implementations.Net /// An integer specifying the local port to bind the acceptSocket to. public ISocket CreateUdpSocket(int localPort) { - if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort)); + if (localPort < 0) + { + throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort)); + } var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); try @@ -65,8 +69,7 @@ namespace Emby.Server.Implementations.Net } catch { - if (retVal != null) - retVal.Dispose(); + retVal?.Dispose(); throw; } @@ -74,7 +77,10 @@ namespace Emby.Server.Implementations.Net public ISocket CreateUdpBroadcastSocket(int localPort) { - if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort)); + if (localPort < 0) + { + throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort)); + } var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); try @@ -86,8 +92,7 @@ namespace Emby.Server.Implementations.Net } catch { - if (retVal != null) - retVal.Dispose(); + retVal?.Dispose(); throw; } @@ -99,7 +104,10 @@ namespace Emby.Server.Implementations.Net /// An implementation of the interface used by RSSDP components to perform acceptSocket operations. public ISocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort) { - if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort)); + if (localPort < 0) + { + throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort)); + } var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); try @@ -114,8 +122,7 @@ namespace Emby.Server.Implementations.Net } catch { - if (retVal != null) - retVal.Dispose(); + retVal?.Dispose(); throw; } @@ -130,10 +137,25 @@ namespace Emby.Server.Implementations.Net /// public ISocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort) { - if (ipAddress == null) throw new ArgumentNullException(nameof(ipAddress)); - if (ipAddress.Length == 0) throw new ArgumentException("ipAddress cannot be an empty string.", nameof(ipAddress)); - if (multicastTimeToLive <= 0) throw new ArgumentException("multicastTimeToLive cannot be zero or less.", nameof(multicastTimeToLive)); - if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort)); + if (ipAddress == null) + { + throw new ArgumentNullException(nameof(ipAddress)); + } + + if (ipAddress.Length == 0) + { + throw new ArgumentException("ipAddress cannot be an empty string.", nameof(ipAddress)); + } + + if (multicastTimeToLive <= 0) + { + throw new ArgumentException("multicastTimeToLive cannot be zero or less.", nameof(multicastTimeToLive)); + } + + if (localPort < 0) + { + throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort)); + } var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); @@ -172,87 +194,13 @@ namespace Emby.Server.Implementations.Net } catch { - if (retVal != null) - retVal.Dispose(); + retVal?.Dispose(); throw; } } public Stream CreateNetworkStream(ISocket socket, bool ownsSocket) - { - var netSocket = (UdpSocket)socket; - - return new SocketStream(netSocket.Socket, ownsSocket); - } + => new NetworkStream(((UdpSocket)socket).Socket, ownsSocket); } - - public class SocketStream : Stream - { - private readonly Socket _socket; - - public SocketStream(Socket socket, bool ownsSocket) - { - _socket = socket; - } - - public override void Flush() - { - } - - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => true; - - public override long Length => throw new NotImplementedException(); - - public override long Position - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - _socket.Send(buffer, offset, count, SocketFlags.None); - } - - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - return _socket.BeginSend(buffer, offset, count, SocketFlags.None, callback, state); - } - - public override void EndWrite(IAsyncResult asyncResult) - { - _socket.EndSend(asyncResult); - } - - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotImplementedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - return _socket.Receive(buffer, offset, count, SocketFlags.None); - } - - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - return _socket.BeginReceive(buffer, offset, count, SocketFlags.None, callback, state); - } - - public override int EndRead(IAsyncResult asyncResult) - { - return _socket.EndReceive(asyncResult); - } - } - } diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs index d488554860..6c55085c83 100644 --- a/Emby.Server.Implementations/Net/UdpSocket.cs +++ b/Emby.Server.Implementations/Net/UdpSocket.cs @@ -11,12 +11,15 @@ namespace Emby.Server.Implementations.Net // THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS // Be careful to check any changes compile and work for all platform projects it is shared in. - public sealed class UdpSocket : DisposableManagedObjectBase, ISocket + public sealed class UdpSocket : ISocket, IDisposable { - private Socket _Socket; - private int _LocalPort; + private Socket _socket; + private int _localPort; + private bool _disposed = false; - public Socket Socket => _Socket; + public Socket Socket => _socket; + + public IpAddressInfo LocalIPAddress { get; } private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs() { @@ -35,11 +38,11 @@ namespace Emby.Server.Implementations.Net { if (socket == null) throw new ArgumentNullException(nameof(socket)); - _Socket = socket; - _LocalPort = localPort; + _socket = socket; + _localPort = localPort; LocalIPAddress = NetworkManager.ToIpAddressInfo(ip); - _Socket.Bind(new IPEndPoint(ip, _LocalPort)); + _socket.Bind(new IPEndPoint(ip, _localPort)); InitReceiveSocketAsyncEventArgs(); } @@ -101,32 +104,26 @@ namespace Emby.Server.Implementations.Net { if (socket == null) throw new ArgumentNullException(nameof(socket)); - _Socket = socket; - _Socket.Connect(NetworkManager.ToIPEndPoint(endPoint)); + _socket = socket; + _socket.Connect(NetworkManager.ToIPEndPoint(endPoint)); InitReceiveSocketAsyncEventArgs(); } - public IpAddressInfo LocalIPAddress - { - get; - private set; - } - public IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback) { ThrowIfDisposed(); EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); - return _Socket.BeginReceiveFrom(buffer, offset, count, SocketFlags.None, ref receivedFromEndPoint, callback, buffer); + return _socket.BeginReceiveFrom(buffer, offset, count, SocketFlags.None, ref receivedFromEndPoint, callback, buffer); } public int Receive(byte[] buffer, int offset, int count) { ThrowIfDisposed(); - return _Socket.Receive(buffer, 0, buffer.Length, SocketFlags.None); + return _socket.Receive(buffer, 0, buffer.Length, SocketFlags.None); } public SocketReceiveResult EndReceive(IAsyncResult result) @@ -136,7 +133,7 @@ namespace Emby.Server.Implementations.Net var sender = new IPEndPoint(IPAddress.Any, 0); var remoteEndPoint = (EndPoint)sender; - var receivedBytes = _Socket.EndReceiveFrom(result, ref remoteEndPoint); + var receivedBytes = _socket.EndReceiveFrom(result, ref remoteEndPoint); var buffer = (byte[])result.AsyncState; @@ -236,37 +233,42 @@ namespace Emby.Server.Implementations.Net var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint); - return _Socket.BeginSendTo(buffer, offset, size, SocketFlags.None, ipEndPoint, callback, state); + return _socket.BeginSendTo(buffer, offset, size, SocketFlags.None, ipEndPoint, callback, state); } public int EndSendTo(IAsyncResult result) { ThrowIfDisposed(); - return _Socket.EndSendTo(result); + return _socket.EndSendTo(result); } - protected override void Dispose(bool disposing) + private void ThrowIfDisposed() { - if (disposing) + if (_disposed) { - var socket = _Socket; - if (socket != null) - socket.Dispose(); - - var tcs = _currentReceiveTaskCompletionSource; - if (tcs != null) - { - tcs.TrySetCanceled(); - } - var sendTcs = _currentSendTaskCompletionSource; - if (sendTcs != null) - { - sendTcs.TrySetCanceled(); - } + throw new ObjectDisposedException(nameof(UdpSocket)); } } + public void Dispose() + { + if (_disposed) + { + return; + } + + _socket?.Dispose(); + _currentReceiveTaskCompletionSource?.TrySetCanceled(); + _currentSendTaskCompletionSource?.TrySetCanceled(); + + _socket = null; + _currentReceiveTaskCompletionSource = null; + _currentSendTaskCompletionSource = null; + + _disposed = true; + } + private static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint) { if (endpoint == null) diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index ace93ebdee..c102f9eb55 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -7,11 +7,11 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading.Tasks; using MediaBrowser.Common.Net; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; +using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Server.Implementations.Networking { @@ -22,14 +22,12 @@ namespace Emby.Server.Implementations.Networking public event EventHandler NetworkChanged; public Func LocalSubnetsFn { get; set; } - public NetworkManager( - ILoggerFactory loggerFactory, - IEnvironmentInfo environment) + public NetworkManager(ILoggerFactory loggerFactory) { Logger = loggerFactory.CreateLogger(nameof(NetworkManager)); // In FreeBSD these events cause a crash - if (environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.BSD) + if (OperatingSystem.Id != OperatingSystemId.BSD) { try { diff --git a/Emby.Server.Implementations/Properties/AssemblyInfo.cs b/Emby.Server.Implementations/Properties/AssemblyInfo.cs index 79ba9374ce..a1933f66ef 100644 --- a/Emby.Server.Implementations/Properties/AssemblyInfo.cs +++ b/Emby.Server.Implementations/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] [assembly: AssemblyProduct("Jellyfin Server")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Emby.Server.Implementations/Reflection/AssemblyInfo.cs b/Emby.Server.Implementations/Reflection/AssemblyInfo.cs deleted file mode 100644 index 9d16fe43f6..0000000000 --- a/Emby.Server.Implementations/Reflection/AssemblyInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using MediaBrowser.Model.Reflection; - -namespace Emby.Server.Implementations.Reflection -{ - public class AssemblyInfo : IAssemblyInfo - { - public Stream GetManifestResourceStream(Type type, string resource) - { - return type.Assembly.GetManifestResourceStream(resource); - } - - public string[] GetManifestResourceNames(Type type) - { - return type.Assembly.GetManifestResourceNames(); - } - - public Assembly[] GetCurrentAssemblies() - { - return AppDomain.CurrentDomain.GetAssemblies(); - } - } -} diff --git a/Emby.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs index 05f6469ece..adaf23234f 100644 --- a/Emby.Server.Implementations/ServerApplicationPaths.cs +++ b/Emby.Server.Implementations/ServerApplicationPaths.cs @@ -17,11 +17,13 @@ namespace Emby.Server.Implementations string programDataPath, string logDirectoryPath, string configurationDirectoryPath, - string cacheDirectoryPath) + string cacheDirectoryPath, + string webDirectoryPath) : base(programDataPath, logDirectoryPath, configurationDirectoryPath, - cacheDirectoryPath) + cacheDirectoryPath, + webDirectoryPath) { } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 03e7b26545..985748caf2 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -116,14 +116,14 @@ namespace Emby.Server.Implementations.Session _authRepo = authRepo; _deviceManager = deviceManager; _mediaSourceManager = mediaSourceManager; - _deviceManager.DeviceOptionsUpdated += _deviceManager_DeviceOptionsUpdated; + _deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated; } - private void _deviceManager_DeviceOptionsUpdated(object sender, GenericEventArgs> e) + private void OnDeviceManagerDeviceOptionsUpdated(object sender, GenericEventArgs> e) { foreach (var session in Sessions) { - if (string.Equals(session.DeviceId, e.Argument.Item1)) + if (string.Equals(session.DeviceId, e.Argument.Item1, StringComparison.Ordinal)) { if (!string.IsNullOrWhiteSpace(e.Argument.Item2.CustomName)) { @@ -138,11 +138,29 @@ namespace Emby.Server.Implementations.Session } } - private bool _disposed; + private bool _disposed = false; + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + // TODO: dispose stuff + } + + _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated; + _disposed = true; - _deviceManager.DeviceOptionsUpdated -= _deviceManager_DeviceOptionsUpdated; } public void CheckDisposed() @@ -157,7 +175,7 @@ namespace Emby.Server.Implementations.Session /// Gets all connections. /// /// All connections. - public IEnumerable Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate).ToList(); + public IEnumerable Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate); private void OnSessionStarted(SessionInfo info) { @@ -171,20 +189,27 @@ namespace Emby.Server.Implementations.Session } } - EventHelper.QueueEventIfNotNull(SessionStarted, this, new SessionEventArgs - { - SessionInfo = info - - }, _logger); + EventHelper.QueueEventIfNotNull( + SessionStarted, + this, + new SessionEventArgs + { + SessionInfo = info + }, + _logger); } private void OnSessionEnded(SessionInfo info) { - EventHelper.QueueEventIfNotNull(SessionEnded, this, new SessionEventArgs - { - SessionInfo = info + EventHelper.QueueEventIfNotNull( + SessionEnded, + this, + new SessionEventArgs + { + SessionInfo = info - }, _logger); + }, + _logger); info.Dispose(); } @@ -192,9 +217,6 @@ namespace Emby.Server.Implementations.Session public void UpdateDeviceName(string sessionId, string deviceName) { var session = GetSession(sessionId); - - var key = GetSessionKey(session.Client, session.DeviceId); - if (session != null) { session.DeviceName = deviceName; @@ -210,10 +232,10 @@ namespace Emby.Server.Implementations.Session /// Name of the device. /// The remote end point. /// The user. - /// Task. + /// SessionInfo. /// user - /// - public SessionInfo LogSessionActivity(string appName, + public SessionInfo LogSessionActivity( + string appName, string appVersion, string deviceId, string deviceName, @@ -226,10 +248,12 @@ namespace Emby.Server.Implementations.Session { throw new ArgumentNullException(nameof(appName)); } + if (string.IsNullOrEmpty(appVersion)) { throw new ArgumentNullException(nameof(appVersion)); } + if (string.IsNullOrEmpty(deviceId)) { throw new ArgumentNullException(nameof(deviceId)); @@ -260,10 +284,12 @@ namespace Emby.Server.Implementations.Session if ((activityDate - lastActivityDate).TotalSeconds > 10) { - SessionActivity?.Invoke(this, new SessionEventArgs - { - SessionInfo = session - }); + SessionActivity?.Invoke( + this, + new SessionEventArgs + { + SessionInfo = session + }); } return session; @@ -304,6 +330,7 @@ namespace Emby.Server.Implementations.Session /// /// Updates the now playing item id. /// + /// Task. private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem, bool updateLastCheckInTime) { if (string.IsNullOrEmpty(info.MediaSourceId)) @@ -418,7 +445,7 @@ namespace Emby.Server.Implementations.Session }); sessionInfo.UserId = user == null ? Guid.Empty : user.Id; - sessionInfo.UserName = user == null ? null : user.Name; + sessionInfo.UserName = user?.Name; sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary); sessionInfo.RemoteEndPoint = remoteEndPoint; sessionInfo.Client = appName; @@ -432,7 +459,7 @@ namespace Emby.Server.Implementations.Session if (user == null) { - sessionInfo.AdditionalUsers = new SessionUserInfo[] { }; + sessionInfo.AdditionalUsers = Array.Empty(); } return sessionInfo; @@ -449,9 +476,9 @@ namespace Emby.Server.Implementations.Session ServerId = _appHost.SystemId }; - var username = user == null ? null : user.Name; + var username = user?.Name; - sessionInfo.UserId = user == null ? Guid.Empty : user.Id; + sessionInfo.UserId = user?.Id ?? Guid.Empty; sessionInfo.UserName = username; sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary); sessionInfo.RemoteEndPoint = remoteEndPoint; @@ -508,6 +535,7 @@ namespace Emby.Server.Implementations.Session _idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); } } + private void StopIdleCheckTimer() { if (_idleTimer != null) @@ -539,9 +567,9 @@ namespace Emby.Server.Implementations.Session Item = session.NowPlayingItem, ItemId = session.NowPlayingItem == null ? Guid.Empty : session.NowPlayingItem.Id, SessionId = session.Id, - MediaSourceId = session.PlayState == null ? null : session.PlayState.MediaSourceId, - PositionTicks = session.PlayState == null ? null : session.PlayState.PositionTicks - }); + MediaSourceId = session.PlayState?.MediaSourceId, + PositionTicks = session.PlayState?.PositionTicks + }).ConfigureAwait(false); } catch (Exception ex) { @@ -616,18 +644,22 @@ namespace Emby.Server.Implementations.Session // Nothing to save here // Fire events to inform plugins - EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs - { - Item = libraryItem, - Users = users, - MediaSourceId = info.MediaSourceId, - MediaInfo = info.Item, - DeviceName = session.DeviceName, - ClientName = session.Client, - DeviceId = session.DeviceId, - Session = session + EventHelper.QueueEventIfNotNull( + PlaybackStart, + this, + new PlaybackProgressEventArgs + { + Item = libraryItem, + Users = users, + MediaSourceId = info.MediaSourceId, + MediaInfo = info.Item, + DeviceName = session.DeviceName, + ClientName = session.Client, + DeviceId = session.DeviceId, + Session = session - }, _logger); + }, + _logger); StartIdleCheckTimer(); } @@ -667,6 +699,7 @@ namespace Emby.Server.Implementations.Session /// /// Used to report playback progress for an item /// + /// Task. public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated) { CheckDisposed(); @@ -695,21 +728,23 @@ namespace Emby.Server.Implementations.Session } } - PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs - { - Item = libraryItem, - Users = users, - PlaybackPositionTicks = session.PlayState.PositionTicks, - MediaSourceId = session.PlayState.MediaSourceId, - MediaInfo = info.Item, - DeviceName = session.DeviceName, - ClientName = session.Client, - DeviceId = session.DeviceId, - IsPaused = info.IsPaused, - PlaySessionId = info.PlaySessionId, - IsAutomated = isAutomated, - Session = session - }); + PlaybackProgress?.Invoke( + this, + new PlaybackProgressEventArgs + { + Item = libraryItem, + Users = users, + PlaybackPositionTicks = session.PlayState.PositionTicks, + MediaSourceId = session.PlayState.MediaSourceId, + MediaInfo = info.Item, + DeviceName = session.DeviceName, + ClientName = session.Client, + DeviceId = session.DeviceId, + IsPaused = info.IsPaused, + PlaySessionId = info.PlaySessionId, + IsAutomated = isAutomated, + Session = session + }); if (!isAutomated) { @@ -830,8 +865,7 @@ namespace Emby.Server.Implementations.Session { MediaSourceInfo mediaSource = null; - var hasMediaSources = libraryItem as IHasMediaSources; - if (hasMediaSources != null) + if (libraryItem is IHasMediaSources hasMediaSources) { mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); } @@ -848,7 +882,8 @@ namespace Emby.Server.Implementations.Session { var msString = info.PositionTicks.HasValue ? (info.PositionTicks.Value / 10000).ToString(CultureInfo.InvariantCulture) : "unknown"; - _logger.LogInformation("Playback stopped reported by app {0} {1} playing {2}. Stopped at {3} ms", + _logger.LogInformation( + "Playback stopped reported by app {0} {1} playing {2}. Stopped at {3} ms", session.Client, session.ApplicationVersion, info.Item.Name, @@ -887,20 +922,24 @@ namespace Emby.Server.Implementations.Session } } - EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs - { - Item = libraryItem, - Users = users, - PlaybackPositionTicks = info.PositionTicks, - PlayedToCompletion = playedToCompletion, - MediaSourceId = info.MediaSourceId, - MediaInfo = info.Item, - DeviceName = session.DeviceName, - ClientName = session.Client, - DeviceId = session.DeviceId, - Session = session + EventHelper.QueueEventIfNotNull( + PlaybackStopped, + this, + new PlaybackStopEventArgs + { + Item = libraryItem, + Users = users, + PlaybackPositionTicks = info.PositionTicks, + PlayedToCompletion = playedToCompletion, + MediaSourceId = info.MediaSourceId, + MediaInfo = info.Item, + DeviceName = session.DeviceName, + ClientName = session.Client, + DeviceId = session.DeviceId, + Session = session - }, _logger); + }, + _logger); } private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed) @@ -936,11 +975,10 @@ namespace Emby.Server.Implementations.Session /// The session identifier. /// if set to true [throw on missing]. /// SessionInfo. - /// + /// sessionId private SessionInfo GetSession(string sessionId, bool throwOnMissing = true) { - var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId)); - + var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId, StringComparison.Ordinal)); if (session == null && throwOnMissing) { throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId)); @@ -952,7 +990,7 @@ namespace Emby.Server.Implementations.Session private SessionInfo GetSessionToRemoteControl(string sessionId) { // Accept either device id or session id - var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId)); + var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId, StringComparison.Ordinal)); if (session == null) { @@ -1061,10 +1099,12 @@ namespace Emby.Server.Implementations.Session var series = episode.Series; if (series != null) { - var episodes = series.GetEpisodes(user, new DtoOptions(false) - { - EnableImages = false - }) + var episodes = series.GetEpisodes( + user, + new DtoOptions(false) + { + EnableImages = false + }) .Where(i => !i.IsVirtualItem) .SkipWhile(i => i.Id != episode.Id) .ToList(); @@ -1100,9 +1140,7 @@ namespace Emby.Server.Implementations.Session return new List(); } - var byName = item as IItemByName; - - if (byName != null) + if (item is IItemByName byName) { return byName.GetTaggedItems(new InternalItemsQuery(user) { @@ -1152,7 +1190,7 @@ namespace Emby.Server.Implementations.Session if (item == null) { - _logger.LogError("A non-existant item Id {0} was passed into TranslateItemForInstantMix", id); + _logger.LogError("A non-existent item Id {0} was passed into TranslateItemForInstantMix", id); return new List(); } @@ -1163,13 +1201,15 @@ namespace Emby.Server.Implementations.Session { var generalCommand = new GeneralCommand { - Name = GeneralCommandType.DisplayContent.ToString() + Name = GeneralCommandType.DisplayContent.ToString(), + Arguments = + { + ["ItemId"] = command.ItemId, + ["ItemName"] = command.ItemName, + ["ItemType"] = command.ItemType + } }; - generalCommand.Arguments["ItemId"] = command.ItemId; - generalCommand.Arguments["ItemName"] = command.ItemName; - generalCommand.Arguments["ItemType"] = command.ItemType; - return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken); } @@ -1410,7 +1450,8 @@ namespace Emby.Server.Implementations.Session var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName); - var session = LogSessionActivity(request.App, + var session = LogSessionActivity( + request.App, request.AppVersion, request.DeviceId, request.DeviceName, @@ -1454,9 +1495,9 @@ namespace Emby.Server.Implementations.Session { Logout(auth); } - catch + catch (Exception ex) { - + _logger.LogError(ex, "Error while logging out."); } } } @@ -1572,7 +1613,8 @@ namespace Emby.Server.Implementations.Session ReportCapabilities(session, capabilities, true); } - private void ReportCapabilities(SessionInfo session, + private void ReportCapabilities( + SessionInfo session, ClientCapabilities capabilities, bool saveCapabilities) { @@ -1580,10 +1622,12 @@ namespace Emby.Server.Implementations.Session if (saveCapabilities) { - CapabilitiesChanged?.Invoke(this, new SessionEventArgs - { - SessionInfo = session - }); + CapabilitiesChanged?.Invoke( + this, + new SessionEventArgs + { + SessionInfo = session + }); try { diff --git a/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs b/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs index f38ed848ee..95b7912fbb 100644 --- a/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs +++ b/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs @@ -63,6 +63,28 @@ public sealed class HttpPostedFile : IDisposable _position = offset; } + public override bool CanRead => true; + + public override bool CanSeek => true; + + public override bool CanWrite => false; + + public override long Length => _end - _offset; + + public override long Position + { + get => _position - _offset; + set + { + if (value > Length) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + _position = Seek(value, SeekOrigin.Begin); + } + } + public override void Flush() { } @@ -178,27 +200,5 @@ public sealed class HttpPostedFile : IDisposable { throw new NotSupportedException(); } - - public override bool CanRead => true; - - public override bool CanSeek => true; - - public override bool CanWrite => false; - - public override long Length => _end - _offset; - - public override long Position - { - get => _position - _offset; - set - { - if (value > Length) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - - _position = Seek(value, SeekOrigin.Begin); - } - } } } diff --git a/Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs b/Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs index 535f123f90..0804b01fca 100644 --- a/Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs +++ b/Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs @@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Sorting { var audio = x as IHasAlbumArtist; - return audio != null ? audio.AlbumArtists.FirstOrDefault() : null; + return audio?.AlbumArtists.FirstOrDefault(); } /// diff --git a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs index 46e0dd9186..504b6d2838 100644 --- a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs +++ b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs @@ -18,17 +18,17 @@ namespace Emby.Server.Implementations.Sorting return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase); } - private static string GetValue(BaseItem item) - { - var hasSeries = item as IHasSeries; - - return hasSeries != null ? hasSeries.FindSeriesSortName() : null; - } - /// /// Gets the name. /// /// The name. public string Name => ItemSortBy.SeriesSortName; + + private static string GetValue(BaseItem item) + { + var hasSeries = item as IHasSeries; + + return hasSeries?.FindSeriesSortName(); + } } } diff --git a/Emby.Server.Implementations/Xml/XmlReaderSettingsFactory.cs b/Emby.Server.Implementations/Xml/XmlReaderSettingsFactory.cs deleted file mode 100644 index 308922e6d1..0000000000 --- a/Emby.Server.Implementations/Xml/XmlReaderSettingsFactory.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Xml; -using MediaBrowser.Model.Xml; - -namespace Emby.Server.Implementations.Xml -{ - public class XmlReaderSettingsFactory : IXmlReaderSettingsFactory - { - public XmlReaderSettings Create(bool enableValidation) - { - var settings = new XmlReaderSettings(); - - if (!enableValidation) - { - settings.ValidationType = ValidationType.None; - } - - return settings; - } - } -} diff --git a/Emby.XmlTv/Emby.XmlTv/Properties/AssemblyInfo.cs b/Emby.XmlTv/Emby.XmlTv/Properties/AssemblyInfo.cs index ff2efb0781..7beec09cbd 100644 --- a/Emby.XmlTv/Emby.XmlTv/Properties/AssemblyInfo.cs +++ b/Emby.XmlTv/Emby.XmlTv/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Jellyfin.Drawing.Skia/Properties/AssemblyInfo.cs b/Jellyfin.Drawing.Skia/Properties/AssemblyInfo.cs index ea1c457f6c..e7db09449d 100644 --- a/Jellyfin.Drawing.Skia/Properties/AssemblyInfo.cs +++ b/Jellyfin.Drawing.Skia/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs index dfdf398710..7d404ce644 100644 --- a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs +++ b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs @@ -77,21 +77,18 @@ namespace Jellyfin.Drawing.Skia { canvas.Clear(SKColors.Black); + // number of images used in the thumbnail + var iCount = 3; + // determine sizes for each image that will composited into the final image - var iSlice = Convert.ToInt32(width * 0.23475); - int iTrans = Convert.ToInt32(height * .25); - int iHeight = Convert.ToInt32(height * .70); - var horizontalImagePadding = Convert.ToInt32(width * 0.0125); - var verticalSpacing = Convert.ToInt32(height * 0.01111111111111111111111111111111); + var iSlice = Convert.ToInt32(width / iCount); + int iHeight = Convert.ToInt32(height * 1.00); int imageIndex = 0; - - for (int i = 0; i < 4; i++) + for (int i = 0; i < iCount; i++) { - using (var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex)) { imageIndex = newIndex; - if (currentBitmap == null) { continue; @@ -108,44 +105,7 @@ namespace Jellyfin.Drawing.Skia using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight))) { // draw image onto canvas - canvas.DrawImage(subset ?? image, (horizontalImagePadding * (i + 1)) + (iSlice * i), verticalSpacing); - - if (subset == null) - { - continue; - } - // create reflection of image below the drawn image - using (var croppedBitmap = SKBitmap.FromImage(subset)) - using (var reflectionBitmap = new SKBitmap(croppedBitmap.Width, croppedBitmap.Height / 2, croppedBitmap.ColorType, croppedBitmap.AlphaType)) - { - // resize to half height - currentBitmap.ScalePixels(reflectionBitmap, SKFilterQuality.High); - - using (var flippedBitmap = new SKBitmap(reflectionBitmap.Width, reflectionBitmap.Height, reflectionBitmap.ColorType, reflectionBitmap.AlphaType)) - using (var flippedCanvas = new SKCanvas(flippedBitmap)) - { - // flip image vertically - var matrix = SKMatrix.MakeScale(1, -1); - matrix.SetScaleTranslate(1, -1, 0, flippedBitmap.Height); - flippedCanvas.SetMatrix(matrix); - flippedCanvas.DrawBitmap(reflectionBitmap, 0, 0); - flippedCanvas.ResetMatrix(); - - // create gradient to make image appear as a reflection - var remainingHeight = height - (iHeight + (2 * verticalSpacing)); - flippedCanvas.ClipRect(SKRect.Create(reflectionBitmap.Width, remainingHeight)); - using (var gradient = new SKPaint()) - { - gradient.IsAntialias = true; - gradient.BlendMode = SKBlendMode.SrcOver; - gradient.Shader = SKShader.CreateLinearGradient(new SKPoint(0, 0), new SKPoint(0, remainingHeight), new[] { new SKColor(0, 0, 0, 128), new SKColor(0, 0, 0, 208), new SKColor(0, 0, 0, 240), new SKColor(0, 0, 0, 255) }, null, SKShaderTileMode.Clamp); - flippedCanvas.DrawPaint(gradient); - } - - // finally draw reflection onto canvas - canvas.DrawBitmap(flippedBitmap, (horizontalImagePadding * (i + 1)) + (iSlice * i), iHeight + (2 * verticalSpacing)); - } - } + canvas.DrawImage(subset ?? image, iSlice * i, 0); } } } diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 17259c7370..8e6ed7a7e5 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -3,7 +3,6 @@ using System.Reflection; using Emby.Server.Implementations; using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.IO; -using MediaBrowser.Model.System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -16,7 +15,6 @@ namespace Jellyfin.Server ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, - IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, MediaBrowser.Common.Net.INetworkManager networkManager, IConfiguration configuration) @@ -25,7 +23,6 @@ namespace Jellyfin.Server loggerFactory, options, fileSystem, - environmentInfo, imageEncoder, networkManager, configuration) diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index bd670df527..9346a2d254 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -12,7 +12,8 @@ latest - SA1600;CS1591 + SA1600;SA1601;CS1591 + true @@ -23,10 +24,6 @@ - - true - - diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 0ef1711d4c..82a76c6378 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -12,7 +12,6 @@ using System.Threading.Tasks; using CommandLine; using Emby.Drawing; using Emby.Server.Implementations; -using Emby.Server.Implementations.EnvironmentInfo; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Networking; using Jellyfin.Drawing.Skia; @@ -46,7 +45,6 @@ namespace Jellyfin.Server const string pattern = @"^(-[^-\s]{2})"; // Match -xx, not -x, not --xx, not xx const string substitution = @"-$1"; // Prepend with additional single-hyphen var regex = new Regex(pattern); - for (var i = 0; i < args.Length; i++) { args[i] = regex.Replace(args[i], substitution); @@ -54,9 +52,7 @@ namespace Jellyfin.Server // Parse the command line arguments and either start the app or exit indicating error await Parser.Default.ParseArguments(args) - .MapResult( - options => StartApp(options), - errs => Task.FromResult(0)).ConfigureAwait(false); + .MapResult(StartApp, _ => Task.CompletedTask).ConfigureAwait(false); } public static void Shutdown() @@ -119,31 +115,29 @@ namespace Jellyfin.Server _logger.LogInformation("Jellyfin version: {Version}", Assembly.GetEntryAssembly().GetName().Version); - EnvironmentInfo environmentInfo = new EnvironmentInfo(GetOperatingSystem()); - ApplicationHost.LogEnvironmentInfo(_logger, appPaths, environmentInfo); + ApplicationHost.LogEnvironmentInfo(_logger, appPaths); SQLitePCL.Batteries_V2.Init(); // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); - var fileSystem = new ManagedFileSystem(_loggerFactory, environmentInfo, appPaths); + var fileSystem = new ManagedFileSystem(_loggerFactory, appPaths); using (var appHost = new CoreAppHost( appPaths, _loggerFactory, options, fileSystem, - environmentInfo, new NullImageEncoder(), - new NetworkManager(_loggerFactory, environmentInfo), + new NetworkManager(_loggerFactory), appConfig)) { - await appHost.Init(new ServiceCollection()).ConfigureAwait(false); + await appHost.InitAsync(new ServiceCollection()).ConfigureAwait(false); appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager); - await appHost.RunStartupTasks().ConfigureAwait(false); + await appHost.RunStartupTasksAsync().ConfigureAwait(false); try { @@ -174,15 +168,14 @@ namespace Jellyfin.Server { // dataDir // IF --datadir - // ELSE IF $JELLYFIN_DATA_PATH + // ELSE IF $JELLYFIN_DATA_DIR // ELSE IF windows, use <%APPDATA%>/jellyfin // ELSE IF $XDG_DATA_HOME then use $XDG_DATA_HOME/jellyfin // ELSE use $HOME/.local/share/jellyfin var dataDir = options.DataDir; - if (string.IsNullOrEmpty(dataDir)) { - dataDir = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH"); + dataDir = Environment.GetEnvironmentVariable("JELLYFIN_DATA_DIR"); if (string.IsNullOrEmpty(dataDir)) { @@ -191,8 +184,6 @@ namespace Jellyfin.Server } } - Directory.CreateDirectory(dataDir); - // configDir // IF --configdir // ELSE IF $JELLYFIN_CONFIG_DIR @@ -236,7 +227,6 @@ namespace Jellyfin.Server // ELSE IF XDG_CACHE_HOME, use $XDG_CACHE_HOME/jellyfin // ELSE HOME/.cache/jellyfin var cacheDir = options.CacheDir; - if (string.IsNullOrEmpty(cacheDir)) { cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR"); @@ -264,13 +254,29 @@ namespace Jellyfin.Server } } + // webDir + // IF --webdir + // ELSE IF $JELLYFIN_WEB_DIR + // ELSE use /jellyfin-web + var webDir = options.WebDir; + + if (string.IsNullOrEmpty(webDir)) + { + webDir = Environment.GetEnvironmentVariable("JELLYFIN_WEB_DIR"); + + if (string.IsNullOrEmpty(webDir)) + { + // Use default location under ResourcesPath + webDir = Path.Combine(AppContext.BaseDirectory, "jellyfin-web", "src"); + } + } + // logDir // IF --logdir // ELSE IF $JELLYFIN_LOG_DIR // ELSE IF --datadir, use /log (assume portable run) // ELSE /log var logDir = options.LogDir; - if (string.IsNullOrEmpty(logDir)) { logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR"); @@ -285,6 +291,7 @@ namespace Jellyfin.Server // Ensure the main folders exist before we continue try { + Directory.CreateDirectory(dataDir); Directory.CreateDirectory(logDir); Directory.CreateDirectory(configDir); Directory.CreateDirectory(cacheDir); @@ -296,7 +303,7 @@ namespace Jellyfin.Server Environment.Exit(1); } - return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir); + return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir, webDir); } private static async Task CreateConfiguration(IApplicationPaths appPaths) @@ -364,36 +371,6 @@ namespace Jellyfin.Server return new NullImageEncoder(); } - private static MediaBrowser.Model.System.OperatingSystem GetOperatingSystem() - { - switch (Environment.OSVersion.Platform) - { - case PlatformID.MacOSX: - return MediaBrowser.Model.System.OperatingSystem.OSX; - case PlatformID.Win32NT: - return MediaBrowser.Model.System.OperatingSystem.Windows; - case PlatformID.Unix: - default: - { - string osDescription = RuntimeInformation.OSDescription; - if (osDescription.Contains("linux", StringComparison.OrdinalIgnoreCase)) - { - return MediaBrowser.Model.System.OperatingSystem.Linux; - } - else if (osDescription.Contains("darwin", StringComparison.OrdinalIgnoreCase)) - { - return MediaBrowser.Model.System.OperatingSystem.OSX; - } - else if (osDescription.Contains("bsd", StringComparison.OrdinalIgnoreCase)) - { - return MediaBrowser.Model.System.OperatingSystem.BSD; - } - - throw new Exception($"Can't resolve OS with description: '{osDescription}'"); - } - } - } - private static void StartNewInstance(StartupOptions options) { _logger.LogInformation("Starting new instance"); diff --git a/Jellyfin.Server/Properties/AssemblyInfo.cs b/Jellyfin.Server/Properties/AssemblyInfo.cs index 2959cdf1fe..5de1e653d9 100644 --- a/Jellyfin.Server/Properties/AssemblyInfo.cs +++ b/Jellyfin.Server/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs index c8cdb984db..8296d414ef 100644 --- a/Jellyfin.Server/StartupOptions.cs +++ b/Jellyfin.Server/StartupOptions.cs @@ -11,6 +11,9 @@ namespace Jellyfin.Server [Option('d', "datadir", Required = false, HelpText = "Path to use for the data folder (database files, etc.).")] public string DataDir { get; set; } + [Option('w', "webdir", Required = false, HelpText = "Path to the Jellyfin web UI resources.")] + public string WebDir { get; set; } + [Option('C', "cachedir", Required = false, HelpText = "Path to use for caching.")] public string CacheDir { get; set; } @@ -23,9 +26,6 @@ namespace Jellyfin.Server [Option("ffmpeg", Required = false, HelpText = "Path to external FFmpeg executable to use in place of default found in PATH.")] public string FFmpegPath { get; set; } - [Option("ffprobe", Required = false, HelpText = "(deprecated) Option has no effect and shall be removed in next release.")] - public string FFprobePath { get; set; } - [Option("service", Required = false, HelpText = "Run as headless service.")] public bool IsService { get; set; } diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 69673a49c1..49f8c6ace0 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -165,6 +165,7 @@ namespace MediaBrowser.Api { options.ImageTypeLimit = hasDtoOptions.ImageTypeLimit.Value; } + if (hasDtoOptions.EnableUserData.HasValue) { options.EnableUserData = hasDtoOptions.EnableUserData.Value; @@ -307,7 +308,7 @@ namespace MediaBrowser.Api return pathInfo[index]; } - private string[] Parse(string pathUri) + private static string[] Parse(string pathUri) { var actionParts = pathUri.Split(new[] { "://" }, StringSplitOptions.None); @@ -329,38 +330,32 @@ namespace MediaBrowser.Api /// protected BaseItem GetItemByName(string name, string type, ILibraryManager libraryManager, DtoOptions dtoOptions) { - BaseItem item; - - if (type.IndexOf("Person", StringComparison.OrdinalIgnoreCase) == 0) + if (type.Equals("Person", StringComparison.OrdinalIgnoreCase)) { - item = GetPerson(name, libraryManager, dtoOptions); + return GetPerson(name, libraryManager, dtoOptions); } - else if (type.IndexOf("Artist", StringComparison.OrdinalIgnoreCase) == 0) + else if (type.Equals("Artist", StringComparison.OrdinalIgnoreCase)) { - item = GetArtist(name, libraryManager, dtoOptions); + return GetArtist(name, libraryManager, dtoOptions); } - else if (type.IndexOf("Genre", StringComparison.OrdinalIgnoreCase) == 0) + else if (type.Equals("Genre", StringComparison.OrdinalIgnoreCase)) { - item = GetGenre(name, libraryManager, dtoOptions); + return GetGenre(name, libraryManager, dtoOptions); } - else if (type.IndexOf("MusicGenre", StringComparison.OrdinalIgnoreCase) == 0) + else if (type.Equals("MusicGenre", StringComparison.OrdinalIgnoreCase)) { - item = GetMusicGenre(name, libraryManager, dtoOptions); + return GetMusicGenre(name, libraryManager, dtoOptions); } - else if (type.IndexOf("Studio", StringComparison.OrdinalIgnoreCase) == 0) + else if (type.Equals("Studio", StringComparison.OrdinalIgnoreCase)) { - item = GetStudio(name, libraryManager, dtoOptions); + return GetStudio(name, libraryManager, dtoOptions); } - else if (type.IndexOf("Year", StringComparison.OrdinalIgnoreCase) == 0) + else if (type.Equals("Year", StringComparison.OrdinalIgnoreCase)) { - item = libraryManager.GetYear(int.Parse(name)); - } - else - { - throw new ArgumentException(); + return libraryManager.GetYear(int.Parse(name)); } - return item; + throw new ArgumentException("Invalid type", nameof(type)); } } } diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 88ed734569..e41ad540ad 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -23,7 +23,6 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; -using MediaBrowser.Model.System; using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.LiveTv @@ -695,29 +694,36 @@ namespace MediaBrowser.Api.LiveTv private readonly IHttpClient _httpClient; private readonly ILibraryManager _libraryManager; private readonly IDtoService _dtoService; - private readonly IFileSystem _fileSystem; private readonly IAuthorizationContext _authContext; private readonly ISessionContext _sessionContext; - private readonly IEnvironmentInfo _environment; - private ICryptoProvider _cryptographyProvider; - private IStreamHelper _streamHelper; - private IMediaSourceManager _mediaSourceManager; + private readonly ICryptoProvider _cryptographyProvider; + private readonly IStreamHelper _streamHelper; + private readonly IMediaSourceManager _mediaSourceManager; - public LiveTvService(ICryptoProvider crypto, IMediaSourceManager mediaSourceManager, IStreamHelper streamHelper, ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext, IEnvironmentInfo environment) + public LiveTvService( + ICryptoProvider crypto, + IMediaSourceManager mediaSourceManager, + IStreamHelper streamHelper, + ILiveTvManager liveTvManager, + IUserManager userManager, + IServerConfigurationManager config, + IHttpClient httpClient, + ILibraryManager libraryManager, + IDtoService dtoService, + IAuthorizationContext authContext, + ISessionContext sessionContext) { + _cryptographyProvider = crypto; + _mediaSourceManager = mediaSourceManager; + _streamHelper = streamHelper; _liveTvManager = liveTvManager; _userManager = userManager; _config = config; _httpClient = httpClient; _libraryManager = libraryManager; _dtoService = dtoService; - _fileSystem = fileSystem; _authContext = authContext; _sessionContext = sessionContext; - _environment = environment; - _cryptographyProvider = crypto; - _streamHelper = streamHelper; - _mediaSourceManager = mediaSourceManager; } public object Get(GetTunerHostTypes request) @@ -731,7 +737,7 @@ namespace MediaBrowser.Api.LiveTv var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId); var folders = _liveTvManager.GetRecordingFolders(user); - var returnArray = _dtoService.GetBaseItemDtos(folders.ToArray(), new DtoOptions(), user); + var returnArray = _dtoService.GetBaseItemDtos(folders, new DtoOptions(), user); var result = new QueryResult { @@ -756,7 +762,7 @@ namespace MediaBrowser.Api.LiveTv [HeaderNames.ContentType] = Model.Net.MimeTypes.GetMimeType(path) }; - return new ProgressiveFileCopier(_fileSystem, _streamHelper, path, outputHeaders, Logger, _environment) + return new ProgressiveFileCopier(_streamHelper, path, outputHeaders, Logger) { AllowEndOfFile = false }; @@ -779,7 +785,7 @@ namespace MediaBrowser.Api.LiveTv [HeaderNames.ContentType] = Model.Net.MimeTypes.GetMimeType("file." + request.Container) }; - return new ProgressiveFileCopier(directStreamProvider, _streamHelper, outputHeaders, Logger, _environment) + return new ProgressiveFileCopier(directStreamProvider, _streamHelper, outputHeaders, Logger) { AllowEndOfFile = false }; diff --git a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs index 8412bf66b9..4c608d9a33 100644 --- a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs +++ b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Threading; @@ -5,60 +6,39 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; -using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.LiveTv { public class ProgressiveFileCopier : IAsyncStreamWriter, IHasHeaders { - private readonly IFileSystem _fileSystem; private readonly ILogger _logger; private readonly string _path; private readonly Dictionary _outputHeaders; - const int StreamCopyToBufferSize = 81920; - - public long StartPosition { get; set; } public bool AllowEndOfFile = true; private readonly IDirectStreamProvider _directStreamProvider; - private readonly IEnvironmentInfo _environment; private IStreamHelper _streamHelper; - public ProgressiveFileCopier(IFileSystem fileSystem, IStreamHelper streamHelper, string path, Dictionary outputHeaders, ILogger logger, IEnvironmentInfo environment) + public ProgressiveFileCopier(IStreamHelper streamHelper, string path, Dictionary outputHeaders, ILogger logger) { - _fileSystem = fileSystem; _path = path; _outputHeaders = outputHeaders; _logger = logger; - _environment = environment; _streamHelper = streamHelper; } - public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, IStreamHelper streamHelper, Dictionary outputHeaders, ILogger logger, IEnvironmentInfo environment) + public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, IStreamHelper streamHelper, Dictionary outputHeaders, ILogger logger) { _directStreamProvider = directStreamProvider; _outputHeaders = outputHeaders; _logger = logger; - _environment = environment; _streamHelper = streamHelper; } public IDictionary Headers => _outputHeaders; - private Stream GetInputStream(bool allowAsyncFileRead) - { - var fileOpenOptions = FileOpenOptions.SequentialScan; - - if (allowAsyncFileRead) - { - fileOpenOptions |= FileOpenOptions.Asynchronous; - } - - return _fileSystem.GetFileStream(_path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions); - } - public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken) { if (_directStreamProvider != null) @@ -67,28 +47,23 @@ namespace MediaBrowser.Api.LiveTv return; } - var eofCount = 0; + var fileOptions = FileOptions.SequentialScan; // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 - var allowAsyncFileRead = true; - - using (var inputStream = GetInputStream(allowAsyncFileRead)) + if (Environment.OSVersion.Platform != PlatformID.Win32NT) { - if (StartPosition > 0) - { - inputStream.Position = StartPosition; - } + fileOptions |= FileOptions.Asynchronous; + } + using (var inputStream = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, fileOptions)) + { var emptyReadLimit = AllowEndOfFile ? 20 : 100; - + var eofCount = 0; while (eofCount < emptyReadLimit) { int bytesRead; bytesRead = await _streamHelper.CopyToAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false); - //var position = fs.Position; - //_logger.LogDebug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); - if (bytesRead == 0) { eofCount++; diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index 48b4e2f24e..dfe4b2b8e9 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -3,7 +3,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; -using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; @@ -11,7 +10,6 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; -using MediaBrowser.Model.System; namespace MediaBrowser.Api.Playback.Progressive { @@ -46,8 +44,7 @@ namespace MediaBrowser.Api.Playback.Progressive IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IJsonSerializer jsonSerializer, - IAuthorizationContext authorizationContext, - IEnvironmentInfo environmentInfo) + IAuthorizationContext authorizationContext) : base(httpClient, serverConfig, userManager, @@ -60,8 +57,7 @@ namespace MediaBrowser.Api.Playback.Progressive deviceManager, mediaSourceManager, jsonSerializer, - authorizationContext, - environmentInfo) + authorizationContext) { } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index f43f26b2f3..a2c20e38fd 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -8,15 +7,12 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; -using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Services; -using MediaBrowser.Model.System; using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.Playback.Progressive @@ -26,7 +22,6 @@ namespace MediaBrowser.Api.Playback.Progressive /// public abstract class BaseProgressiveStreamingService : BaseStreamingService { - protected readonly IEnvironmentInfo EnvironmentInfo; protected IHttpClient HttpClient { get; private set; } public BaseProgressiveStreamingService( @@ -42,8 +37,7 @@ namespace MediaBrowser.Api.Playback.Progressive IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IJsonSerializer jsonSerializer, - IAuthorizationContext authorizationContext, - IEnvironmentInfo environmentInfo) + IAuthorizationContext authorizationContext) : base(serverConfig, userManager, libraryManager, @@ -57,7 +51,6 @@ namespace MediaBrowser.Api.Playback.Progressive jsonSerializer, authorizationContext) { - EnvironmentInfo = environmentInfo; HttpClient = httpClient; } @@ -157,7 +150,7 @@ namespace MediaBrowser.Api.Playback.Progressive // TODO: Don't hardcode this outputHeaders[HeaderNames.ContentType] = Model.Net.MimeTypes.GetMimeType("file.ts"); - return new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, EnvironmentInfo, CancellationToken.None) + return new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, CancellationToken.None) { AllowEndOfFile = false }; @@ -203,7 +196,7 @@ namespace MediaBrowser.Api.Playback.Progressive }; - return new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, EnvironmentInfo, CancellationToken.None) + return new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, CancellationToken.None) { AllowEndOfFile = false }; @@ -397,7 +390,7 @@ namespace MediaBrowser.Api.Playback.Progressive outputHeaders[item.Key] = item.Value; } - return new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, EnvironmentInfo, CancellationToken.None); + return new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, CancellationToken.None); } finally { diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index 3dd9de2a1b..6609120655 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -8,6 +8,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; +using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace MediaBrowser.Api.Playback.Progressive { @@ -27,9 +28,8 @@ namespace MediaBrowser.Api.Playback.Progressive public bool AllowEndOfFile = true; private readonly IDirectStreamProvider _directStreamProvider; - private readonly IEnvironmentInfo _environment; - public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary outputHeaders, TranscodingJob job, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken) + public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken) { _fileSystem = fileSystem; _path = path; @@ -37,17 +37,15 @@ namespace MediaBrowser.Api.Playback.Progressive _job = job; _logger = logger; _cancellationToken = cancellationToken; - _environment = environment; } - public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary outputHeaders, TranscodingJob job, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken) + public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken) { _directStreamProvider = directStreamProvider; _outputHeaders = outputHeaders; _job = job; _logger = logger; _cancellationToken = cancellationToken; - _environment = environment; } public IDictionary Headers => _outputHeaders; @@ -79,7 +77,7 @@ namespace MediaBrowser.Api.Playback.Progressive var eofCount = 0; // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 - var allowAsyncFileRead = _environment.OperatingSystem != Model.System.OperatingSystem.Windows; + var allowAsyncFileRead = OperatingSystem.Id != OperatingSystemId.Windows; using (var inputStream = GetInputStream(allowAsyncFileRead)) { diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index bf15cc756c..ab19fdc261 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -3,7 +3,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; -using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; @@ -11,7 +10,6 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; -using MediaBrowser.Model.System; namespace MediaBrowser.Api.Playback.Progressive { @@ -83,8 +81,7 @@ namespace MediaBrowser.Api.Playback.Progressive IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IJsonSerializer jsonSerializer, - IAuthorizationContext authorizationContext, - IEnvironmentInfo environmentInfo) + IAuthorizationContext authorizationContext) : base(httpClient, serverConfig, userManager, @@ -97,8 +94,7 @@ namespace MediaBrowser.Api.Playback.Progressive deviceManager, mediaSourceManager, jsonSerializer, - authorizationContext, - environmentInfo) + authorizationContext) { } diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index f97e88e986..b3d8bfe59f 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -18,7 +18,6 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; -using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback @@ -93,7 +92,6 @@ namespace MediaBrowser.Api.Playback IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, INetworkManager networkManager, - IEnvironmentInfo environmentInfo, ILoggerFactory loggerFactory) { HttpClient = httpClient; @@ -112,7 +110,6 @@ namespace MediaBrowser.Api.Playback AuthorizationContext = authorizationContext; ImageProcessor = imageProcessor; NetworkManager = networkManager; - EnvironmentInfo = environmentInfo; _loggerFactory = loggerFactory; _logger = loggerFactory.CreateLogger(nameof(UniversalAudioService)); } @@ -133,7 +130,6 @@ namespace MediaBrowser.Api.Playback protected IAuthorizationContext AuthorizationContext { get; private set; } protected IImageProcessor ImageProcessor { get; private set; } protected INetworkManager NetworkManager { get; private set; } - protected IEnvironmentInfo EnvironmentInfo { get; private set; } private ILoggerFactory _loggerFactory; private ILogger _logger; @@ -338,8 +334,7 @@ namespace MediaBrowser.Api.Playback DeviceManager, MediaSourceManager, JsonSerializer, - AuthorizationContext, - EnvironmentInfo) + AuthorizationContext) { Request = Request }; diff --git a/MediaBrowser.Api/Properties/AssemblyInfo.cs b/MediaBrowser.Api/Properties/AssemblyInfo.cs index f867230311..35bcbea5cd 100644 --- a/MediaBrowser.Api/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Api/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs index 7a8455ff26..a30f8adfed 100644 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -87,11 +87,6 @@ namespace MediaBrowser.Api.UserLibrary /// System.Object. public object Get(GetArtists request) { - if (string.IsNullOrWhiteSpace(request.IncludeItemTypes)) - { - //request.IncludeItemTypes = "Audio,MusicVideo"; - } - return GetResultSlim(request); } @@ -102,11 +97,6 @@ namespace MediaBrowser.Api.UserLibrary /// System.Object. public object Get(GetAlbumArtists request) { - if (string.IsNullOrWhiteSpace(request.IncludeItemTypes)) - { - //request.IncludeItemTypes = "Audio,MusicVideo"; - } - var result = GetResultSlim(request); return ToOptimizedResult(result); diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs index cb4e8bf5f0..fd11bf904f 100644 --- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs +++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs @@ -11,6 +11,12 @@ namespace MediaBrowser.Common.Configuration /// The program data path. string ProgramDataPath { get; } + /// + /// Gets the path to the web UI resources folder + /// + /// The web UI resources path. + string WebPath { get; } + /// /// Gets the path to the program system folder /// diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 3a4098612d..2925a3efd9 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Events; @@ -72,6 +71,12 @@ namespace MediaBrowser.Common /// The application user agent. string ApplicationUserAgent { get; } + /// + /// Gets the email address for use within a comment section of a user agent field. + /// Presently used to provide contact information to MusicBrainz service. + /// + string ApplicationUserAgentAddress { get; } + /// /// Gets the exports. /// @@ -107,7 +112,7 @@ namespace MediaBrowser.Common /// /// Inits this instance. /// - Task Init(IServiceCollection serviceCollection); + Task InitAsync(IServiceCollection serviceCollection); /// /// Creates the instance. diff --git a/MediaBrowser.Common/Properties/AssemblyInfo.cs b/MediaBrowser.Common/Properties/AssemblyInfo.cs index 1a8fdb618d..538e89fd1c 100644 --- a/MediaBrowser.Common/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Common/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs b/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs new file mode 100644 index 0000000000..09d974db6c --- /dev/null +++ b/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Common.Providers +{ + public class SubtitleConfigurationFactory : IConfigurationFactory + { + public IEnumerable GetConfigurations() + { + yield return new ConfigurationStore() + { + Key = "subtitles", + ConfigurationType = typeof(SubtitleOptions) + }; + } + } +} diff --git a/MediaBrowser.Common/System/OperatingSystem.cs b/MediaBrowser.Common/System/OperatingSystem.cs new file mode 100644 index 0000000000..640821d4d7 --- /dev/null +++ b/MediaBrowser.Common/System/OperatingSystem.cs @@ -0,0 +1,72 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using MediaBrowser.Model.System; + +namespace MediaBrowser.Common.System +{ + public static class OperatingSystem + { + // We can't use Interlocked.CompareExchange for enums + private static int _id = int.MaxValue; + + public static OperatingSystemId Id + { + get + { + if (_id == int.MaxValue) + { + Interlocked.CompareExchange(ref _id, (int)GetId(), int.MaxValue); + } + + return (OperatingSystemId)_id; + } + } + + public static string Name + { + get + { + switch (Id) + { + case OperatingSystemId.BSD: return "BSD"; + case OperatingSystemId.Linux: return "Linux"; + case OperatingSystemId.Darwin: return "macOS"; + case OperatingSystemId.Windows: return "Windows"; + default: throw new Exception($"Unknown OS {Id}"); + } + } + } + + private static OperatingSystemId GetId() + { + switch (Environment.OSVersion.Platform) + { + // On .NET Core `MacOSX` got replaced by `Unix`, this case should never be hit. + case PlatformID.MacOSX: + return OperatingSystemId.Darwin; + case PlatformID.Win32NT: + return OperatingSystemId.Windows; + case PlatformID.Unix: + default: + { + string osDescription = RuntimeInformation.OSDescription; + if (osDescription.IndexOf("linux", StringComparison.OrdinalIgnoreCase) != -1) + { + return OperatingSystemId.Linux; + } + else if (osDescription.IndexOf("darwin", StringComparison.OrdinalIgnoreCase) != -1) + { + return OperatingSystemId.Darwin; + } + else if (osDescription.IndexOf("bsd", StringComparison.OrdinalIgnoreCase) != -1) + { + return OperatingSystemId.BSD; + } + + throw new Exception($"Can't resolve OS with description: '{osDescription}'"); + } + } + } + } +} diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 154b9470ef..e20641c99a 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.Controller.Entities /// /// The supported image extensions /// - public static readonly IReadOnlyList SupportedImageExtensions + public static readonly string[] SupportedImageExtensions = new [] { ".png", ".jpg", ".jpeg", ".tbn", ".gif" }; private static readonly List _supportedExtensions = new List(SupportedImageExtensions) diff --git a/MediaBrowser.Controller/Properties/AssemblyInfo.cs b/MediaBrowser.Controller/Properties/AssemblyInfo.cs index 007a1d739b..60e7923091 100644 --- a/MediaBrowser.Controller/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Controller/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index 38458e34c8..59c8f4da50 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -10,7 +10,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.LocalMetadata.Parsers @@ -30,19 +29,14 @@ namespace MediaBrowser.LocalMetadata.Parsers private Dictionary _validProviderIds; - protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; } - protected IFileSystem FileSystem { get; private set; } - /// /// Initializes a new instance of the class. /// /// The logger. - public BaseItemXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) + public BaseItemXmlParser(ILogger logger, IProviderManager providerManager) { Logger = logger; ProviderManager = providerManager; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; - FileSystem = fileSystem; } /// @@ -64,11 +58,13 @@ namespace MediaBrowser.LocalMetadata.Parsers throw new ArgumentException("The metadata file was empty or null.", nameof(metadataFile)); } - var settings = XmlReaderSettingsFactory.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; + var settings = new XmlReaderSettings() + { + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; _validProviderIds = _validProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -103,29 +99,24 @@ namespace MediaBrowser.LocalMetadata.Parsers item.ResetPeople(); using (var fileStream = File.OpenRead(metadataFile)) + using (var streamReader = new StreamReader(fileStream, encoding)) + using (var reader = XmlReader.Create(streamReader, settings)) { - using (var streamReader = new StreamReader(fileStream, encoding)) + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + FetchDataFromXmlNode(reader, item); + } + else { - reader.MoveToContent(); reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - FetchDataFromXmlNode(reader, item); - } - else - { - reader.Read(); - } - } } } } diff --git a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs index e595e9d068..127334625d 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs @@ -3,8 +3,6 @@ using System.Xml; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.LocalMetadata.Parsers @@ -87,7 +85,8 @@ namespace MediaBrowser.LocalMetadata.Parsers item.Item.LinkedChildren = list.ToArray(); } - public BoxSetXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem) + public BoxSetXmlParser(ILogger logger, IProviderManager providerManager) + : base(logger, providerManager) { } } diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs index ff69cb0237..5608a0be90 100644 --- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs @@ -3,8 +3,6 @@ using System.Xml; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.LocalMetadata.Parsers @@ -95,7 +93,8 @@ namespace MediaBrowser.LocalMetadata.Parsers item.LinkedChildren = list.ToArray(); } - public PlaylistXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem) + public PlaylistXmlParser(ILogger logger, IProviderManager providerManager) + : base(logger, providerManager) { } } diff --git a/MediaBrowser.LocalMetadata/Properties/AssemblyInfo.cs b/MediaBrowser.LocalMetadata/Properties/AssemblyInfo.cs index 3eac837081..580cef9da6 100644 --- a/MediaBrowser.LocalMetadata/Properties/AssemblyInfo.cs +++ b/MediaBrowser.LocalMetadata/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs index 539f818c65..2e303efab6 100644 --- a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.LocalMetadata.Providers @@ -16,19 +15,17 @@ namespace MediaBrowser.LocalMetadata.Providers { private readonly ILogger _logger; private readonly IProviderManager _providerManager; - protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; } - public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _providerManager = providerManager; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) { - new BoxSetXmlParser(_logger, _providerManager, XmlReaderSettingsFactory, FileSystem).Fetch(result, path, cancellationToken); + new BoxSetXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs index 442a18cb98..d111ae9ba8 100644 --- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.LocalMetadata.Providers @@ -13,19 +12,17 @@ namespace MediaBrowser.LocalMetadata.Providers { private readonly ILogger _logger; private readonly IProviderManager _providerManager; - protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; } - public PlaylistXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public PlaylistXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _providerManager = providerManager; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) { - new PlaylistXmlParser(_logger, _providerManager, XmlReaderSettingsFactory, FileSystem).Fetch(result, path, cancellationToken); + new PlaylistXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index 438b842525..30a33b729a 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -14,7 +14,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.LocalMetadata.Savers @@ -23,7 +22,7 @@ namespace MediaBrowser.LocalMetadata.Savers { private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public BaseXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public BaseXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) { FileSystem = fileSystem; ConfigurationManager = configurationManager; @@ -31,7 +30,6 @@ namespace MediaBrowser.LocalMetadata.Savers UserManager = userManager; UserDataManager = userDataManager; Logger = logger; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } protected IFileSystem FileSystem { get; private set; } @@ -40,9 +38,6 @@ namespace MediaBrowser.LocalMetadata.Savers protected IUserManager UserManager { get; private set; } protected IUserDataManager UserDataManager { get; private set; } protected ILogger Logger { get; private set; } - protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; } - - protected ItemUpdateType MinimumUpdateType => ItemUpdateType.MetadataDownload; public string Name => XmlProviderUtils.Name; diff --git a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs index b4d5440a52..ea939e33b1 100644 --- a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs @@ -5,7 +5,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.LocalMetadata.Savers @@ -31,7 +30,8 @@ namespace MediaBrowser.LocalMetadata.Savers return Path.Combine(item.Path, "collection.xml"); } - public BoxSetXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory) + public BoxSetXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) + : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) { } } diff --git a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs index 09bb6d8f72..35a431fa48 100644 --- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs @@ -5,7 +5,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.LocalMetadata.Savers @@ -49,7 +48,8 @@ namespace MediaBrowser.LocalMetadata.Savers return Path.Combine(path, "playlist.xml"); } - public PlaylistXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory) + public PlaylistXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) + : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) { } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 94beda3dbb..f454c27235 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -51,7 +51,6 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly IProcessFactory _processFactory; private readonly int DefaultImageExtractionTimeoutMs; private readonly string StartupOptionFFmpegPath; - private readonly string StartupOptionFFprobePath; private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1); private readonly List _runningProcesses = new List(); @@ -60,7 +59,6 @@ namespace MediaBrowser.MediaEncoding.Encoder ILoggerFactory loggerFactory, IJsonSerializer jsonSerializer, string startupOptionsFFmpegPath, - string startupOptionsFFprobePath, IServerConfigurationManager configurationManager, IFileSystem fileSystem, Func subtitleEncoder, @@ -71,7 +69,6 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger = loggerFactory.CreateLogger(nameof(MediaEncoder)); _jsonSerializer = jsonSerializer; StartupOptionFFmpegPath = startupOptionsFFmpegPath; - StartupOptionFFprobePath = startupOptionsFFprobePath; ConfigurationManager = configurationManager; FileSystem = fileSystem; SubtitleEncoder = subtitleEncoder; @@ -86,12 +83,6 @@ namespace MediaBrowser.MediaEncoding.Encoder /// public void SetFFmpegPath() { - // ToDo - Finalise removal of the --ffprobe switch - if (!string.IsNullOrEmpty(StartupOptionFFprobePath)) - { - _logger.LogWarning("--ffprobe switch is deprecated and shall be removed in the next release"); - } - // 1) Custom path stored in config/encoding xml file under tag takes precedence if (!ValidatePath(ConfigurationManager.GetConfiguration("encoding").EncoderAppPath, FFmpegLocation.Custom)) { diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 68b8bd4fae..e4757543ea 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -14,7 +14,6 @@ - diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 5099ccb2aa..2cca410a82 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -246,52 +246,49 @@ namespace MediaBrowser.MediaEncoding.Probing // \n\n\n\n\tcast\n\t\n\t\t\n\t\t\tname\n\t\t\tBlender Foundation\n\t\t\n\t\t\n\t\t\tname\n\t\t\tJanus Bager Kristensen\n\t\t\n\t\n\tdirectors\n\t\n\t\t\n\t\t\tname\n\t\t\tSacha Goedegebure\n\t\t\n\t\n\tstudio\n\tBlender Foundation\n\n\n using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml))) + using (var streamReader = new StreamReader(stream)) { - using (var streamReader = new StreamReader(stream)) + try { - try + using (var reader = XmlReader.Create(streamReader)) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader)) - { - reader.MoveToContent(); - reader.Read(); + reader.MoveToContent(); + reader.Read(); - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) { - if (reader.NodeType == XmlNodeType.Element) + switch (reader.Name) { - switch (reader.Name) - { - case "dict": - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subtree = reader.ReadSubtree()) - { - ReadFromDictNode(subtree, info); - } - break; - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); + case "dict": + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subtree = reader.ReadSubtree()) + { + ReadFromDictNode(subtree, info); + } + break; + default: + reader.Skip(); + break; } } + else + { + reader.Read(); + } } } - catch (XmlException) - { - // I've seen probe examples where the iTunMOVI value is just "<" - // So we should not allow this to fail the entire probing operation - } + } + catch (XmlException) + { + // I've seen probe examples where the iTunMOVI value is just "<" + // So we should not allow this to fail the entire probing operation } } } diff --git a/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs index 6ecdf89bc1..a9491374bd 100644 --- a/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs +++ b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/MediaBrowser.MediaEncoding/Subtitles/ConfigurationExtension.cs b/MediaBrowser.MediaEncoding/Subtitles/ConfigurationExtension.cs deleted file mode 100644 index 92544f4f62..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/ConfigurationExtension.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Providers; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - public static class ConfigurationExtension - { - public static SubtitleOptions GetSubtitleConfiguration(this IConfigurationManager manager) - { - return manager.GetConfiguration("subtitles"); - } - } - - public class SubtitleConfigurationFactory : IConfigurationFactory - { - public IEnumerable GetConfigurations() - { - return new List - { - new ConfigurationStore - { - Key = "subtitles", - ConfigurationType = typeof (SubtitleOptions) - } - }; - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs deleted file mode 100644 index a7e3f61972..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs +++ /dev/null @@ -1,347 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Security; -using MediaBrowser.Controller.Subtitles; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using Microsoft.Extensions.Logging; -using OpenSubtitlesHandler; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - public class OpenSubtitleDownloader : ISubtitleProvider, IDisposable - { - private readonly ILogger _logger; - private readonly IHttpClient _httpClient; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - private readonly IServerConfigurationManager _config; - - private readonly IJsonSerializer _json; - private readonly IFileSystem _fileSystem; - - public OpenSubtitleDownloader(ILoggerFactory loggerFactory, IHttpClient httpClient, IServerConfigurationManager config, IJsonSerializer json, IFileSystem fileSystem) - { - _logger = loggerFactory.CreateLogger(GetType().Name); - _httpClient = httpClient; - _config = config; - _json = json; - _fileSystem = fileSystem; - - _config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating; - - Utilities.HttpClient = httpClient; - OpenSubtitles.SetUserAgent("jellyfin"); - } - - private const string PasswordHashPrefix = "h:"; - void _config_NamedConfigurationUpdating(object sender, ConfigurationUpdateEventArgs e) - { - if (!string.Equals(e.Key, "subtitles", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var options = (SubtitleOptions)e.NewConfiguration; - - if (options != null && - !string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash) && - !options.OpenSubtitlesPasswordHash.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase)) - { - options.OpenSubtitlesPasswordHash = EncodePassword(options.OpenSubtitlesPasswordHash); - } - } - - private static string EncodePassword(string password) - { - var bytes = Encoding.UTF8.GetBytes(password); - return PasswordHashPrefix + Convert.ToBase64String(bytes); - } - - private static string DecodePassword(string password) - { - if (password == null || - !password.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase)) - { - return string.Empty; - } - - var bytes = Convert.FromBase64String(password.Substring(2)); - return Encoding.UTF8.GetString(bytes, 0, bytes.Length); - } - - public string Name => "Open Subtitles"; - - private SubtitleOptions GetOptions() - { - return _config.GetSubtitleConfiguration(); - } - - public IEnumerable SupportedMediaTypes - { - get - { - var options = GetOptions(); - - if (string.IsNullOrWhiteSpace(options.OpenSubtitlesUsername) || - string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash)) - { - return new VideoContentType[] { }; - } - - return new[] { VideoContentType.Episode, VideoContentType.Movie }; - } - } - - public Task GetSubtitles(string id, CancellationToken cancellationToken) - { - return GetSubtitlesInternal(id, GetOptions(), cancellationToken); - } - - private DateTime _lastRateLimitException; - private async Task GetSubtitlesInternal(string id, - SubtitleOptions options, - CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(id)) - { - throw new ArgumentNullException(nameof(id)); - } - - var idParts = id.Split(new[] { '-' }, 3); - - var format = idParts[0]; - var language = idParts[1]; - var ossId = idParts[2]; - - var downloadsList = new[] { int.Parse(ossId, _usCulture) }; - - await Login(cancellationToken).ConfigureAwait(false); - - if ((DateTime.UtcNow - _lastRateLimitException).TotalHours < 1) - { - throw new RateLimitExceededException("OpenSubtitles rate limit reached"); - } - - var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false); - - if ((resultDownLoad.Status ?? string.Empty).IndexOf("407", StringComparison.OrdinalIgnoreCase) != -1) - { - _lastRateLimitException = DateTime.UtcNow; - throw new RateLimitExceededException("OpenSubtitles rate limit reached"); - } - - if (!(resultDownLoad is MethodResponseSubtitleDownload)) - { - throw new Exception("Invalid response type"); - } - - var results = ((MethodResponseSubtitleDownload)resultDownLoad).Results; - - _lastRateLimitException = DateTime.MinValue; - - if (results.Count == 0) - { - var msg = string.Format("Subtitle with Id {0} was not found. Name: {1}. Status: {2}. Message: {3}", - ossId, - resultDownLoad.Name ?? string.Empty, - resultDownLoad.Status ?? string.Empty, - resultDownLoad.Message ?? string.Empty); - - throw new ResourceNotFoundException(msg); - } - - var data = Convert.FromBase64String(results.First().Data); - - return new SubtitleResponse - { - Format = format, - Language = language, - - Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data))) - }; - } - - private DateTime _lastLogin; - private async Task Login(CancellationToken cancellationToken) - { - if ((DateTime.UtcNow - _lastLogin).TotalSeconds < 60) - { - return; - } - - var options = GetOptions(); - - var user = options.OpenSubtitlesUsername ?? string.Empty; - var password = DecodePassword(options.OpenSubtitlesPasswordHash); - - var loginResponse = await OpenSubtitles.LogInAsync(user, password, "en", cancellationToken).ConfigureAwait(false); - - if (!(loginResponse is MethodResponseLogIn)) - { - throw new Exception("Authentication to OpenSubtitles failed."); - } - - _lastLogin = DateTime.UtcNow; - } - - public async Task> GetSupportedLanguages(CancellationToken cancellationToken) - { - await Login(cancellationToken).ConfigureAwait(false); - - var result = OpenSubtitles.GetSubLanguages("en"); - if (!(result is MethodResponseGetSubLanguages)) - { - _logger.LogError("Invalid response type"); - return new List(); - } - - var results = ((MethodResponseGetSubLanguages)result).Languages; - - return results.Select(i => new NameIdPair - { - Name = i.LanguageName, - Id = i.SubLanguageID - }); - } - - private string NormalizeLanguage(string language) - { - // Problem with Greek subtitle download #1349 - if (string.Equals(language, "gre", StringComparison.OrdinalIgnoreCase)) - { - - return "ell"; - } - - return language; - } - - public async Task> Search(SubtitleSearchRequest request, CancellationToken cancellationToken) - { - var imdbIdText = request.GetProviderId(MetadataProviders.Imdb); - long imdbId = 0; - - switch (request.ContentType) - { - case VideoContentType.Episode: - if (!request.IndexNumber.HasValue || !request.ParentIndexNumber.HasValue || string.IsNullOrEmpty(request.SeriesName)) - { - _logger.LogDebug("Episode information missing"); - return new List(); - } - break; - case VideoContentType.Movie: - if (string.IsNullOrEmpty(request.Name)) - { - _logger.LogDebug("Movie name missing"); - return new List(); - } - if (string.IsNullOrWhiteSpace(imdbIdText) || !long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId)) - { - _logger.LogDebug("Imdb id missing"); - return new List(); - } - break; - } - - if (string.IsNullOrEmpty(request.MediaPath)) - { - _logger.LogDebug("Path Missing"); - return new List(); - } - - await Login(cancellationToken).ConfigureAwait(false); - - var subLanguageId = NormalizeLanguage(request.Language); - string hash; - - using (var fileStream = File.OpenRead(request.MediaPath)) - { - hash = Utilities.ComputeHash(fileStream); - } - var fileInfo = _fileSystem.GetFileInfo(request.MediaPath); - var movieByteSize = fileInfo.Length; - var searchImdbId = request.ContentType == VideoContentType.Movie ? imdbId.ToString(_usCulture) : ""; - var subtitleSearchParameters = request.ContentType == VideoContentType.Episode - ? new List { - new SubtitleSearchParameters(subLanguageId, - query: request.SeriesName, - season: request.ParentIndexNumber.Value.ToString(_usCulture), - episode: request.IndexNumber.Value.ToString(_usCulture)) - } - : new List { - new SubtitleSearchParameters(subLanguageId, imdbid: searchImdbId), - new SubtitleSearchParameters(subLanguageId, query: request.Name, imdbid: searchImdbId) - }; - var parms = new List { - new SubtitleSearchParameters( subLanguageId, - movieHash: hash, - movieByteSize: movieByteSize, - imdbid: searchImdbId ), - }; - parms.AddRange(subtitleSearchParameters); - var result = await OpenSubtitles.SearchSubtitlesAsync(parms.ToArray(), cancellationToken).ConfigureAwait(false); - if (!(result is MethodResponseSubtitleSearch)) - { - _logger.LogError("Invalid response type"); - return new List(); - } - - Predicate mediaFilter = - x => - request.ContentType == VideoContentType.Episode - ? !string.IsNullOrEmpty(x.SeriesSeason) && !string.IsNullOrEmpty(x.SeriesEpisode) && - int.Parse(x.SeriesSeason, _usCulture) == request.ParentIndexNumber && - int.Parse(x.SeriesEpisode, _usCulture) == request.IndexNumber - : !string.IsNullOrEmpty(x.IDMovieImdb) && long.Parse(x.IDMovieImdb, _usCulture) == imdbId; - - var results = ((MethodResponseSubtitleSearch)result).Results; - - // Avoid implicitly captured closure - var hasCopy = hash; - - return results.Where(x => x.SubBad == "0" && mediaFilter(x) && (!request.IsPerfectMatch || string.Equals(x.MovieHash, hash, StringComparison.OrdinalIgnoreCase))) - .OrderBy(x => (string.Equals(x.MovieHash, hash, StringComparison.OrdinalIgnoreCase) ? 0 : 1)) - .ThenBy(x => Math.Abs(long.Parse(x.MovieByteSize, _usCulture) - movieByteSize)) - .ThenByDescending(x => int.Parse(x.SubDownloadsCnt, _usCulture)) - .ThenByDescending(x => double.Parse(x.SubRating, _usCulture)) - .Select(i => new RemoteSubtitleInfo - { - Author = i.UserNickName, - Comment = i.SubAuthorComment, - CommunityRating = float.Parse(i.SubRating, _usCulture), - DownloadCount = int.Parse(i.SubDownloadsCnt, _usCulture), - Format = i.SubFormat, - ProviderName = Name, - ThreeLetterISOLanguageName = i.SubLanguageID, - - Id = i.SubFormat + "-" + i.SubLanguageID + "-" + i.IDSubtitleFile, - - Name = i.SubFileName, - DateCreated = DateTime.Parse(i.SubAddDate, _usCulture), - IsHashMatch = i.MovieHash == hasCopy - - }).Where(i => !string.Equals(i.Format, "sub", StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Format, "idx", StringComparison.OrdinalIgnoreCase)); - } - - public void Dispose() - { - _config.NamedConfigurationUpdating -= _config_NamedConfigurationUpdating; - } - } -} diff --git a/MediaBrowser.Model/Net/HttpResponse.cs b/MediaBrowser.Model/Net/HttpResponse.cs deleted file mode 100644 index 286b1c0afd..0000000000 --- a/MediaBrowser.Model/Net/HttpResponse.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; - -namespace MediaBrowser.Model.Net -{ - public class HttpResponse : IDisposable - { - /// - /// Gets or sets the type of the content. - /// - /// The type of the content. - public string ContentType { get; set; } - - /// - /// Gets or sets the response URL. - /// - /// The response URL. - public string ResponseUrl { get; set; } - - /// - /// Gets or sets the content. - /// - /// The content. - public Stream Content { get; set; } - - /// - /// Gets or sets the status code. - /// - /// The status code. - public HttpStatusCode StatusCode { get; set; } - - /// - /// Gets or sets the length of the content. - /// - /// The length of the content. - public long? ContentLength { get; set; } - - /// - /// Gets or sets the headers. - /// - /// The headers. - public Dictionary Headers { get; set; } - - private readonly IDisposable _disposable; - - public HttpResponse(IDisposable disposable) - { - _disposable = disposable; - } - public HttpResponse() - { - } - - public void Dispose() - { - if (_disposable != null) - { - _disposable.Dispose(); - } - } - } -} diff --git a/MediaBrowser.Model/Net/IAcceptSocket.cs b/MediaBrowser.Model/Net/IAcceptSocket.cs deleted file mode 100644 index 2b21d3e660..0000000000 --- a/MediaBrowser.Model/Net/IAcceptSocket.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace MediaBrowser.Model.Net -{ - public class SocketCreateException : Exception - { - public SocketCreateException(string errorCode, Exception originalException) - : base(errorCode, originalException) - { - ErrorCode = errorCode; - } - - public string ErrorCode { get; private set; } - } -} diff --git a/MediaBrowser.Model/Properties/AssemblyInfo.cs b/MediaBrowser.Model/Properties/AssemblyInfo.cs index e78719e35f..f99e9ece96 100644 --- a/MediaBrowser.Model/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Model/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/MediaBrowser.Model/Reflection/IAssemblyInfo.cs b/MediaBrowser.Model/Reflection/IAssemblyInfo.cs deleted file mode 100644 index 5c4536c1c1..0000000000 --- a/MediaBrowser.Model/Reflection/IAssemblyInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.IO; -using System.Reflection; - -namespace MediaBrowser.Model.Reflection -{ - public interface IAssemblyInfo - { - Stream GetManifestResourceStream(Type type, string resource); - string[] GetManifestResourceNames(Type type); - - Assembly[] GetCurrentAssemblies(); - } -} diff --git a/MediaBrowser.Model/System/IEnvironmentInfo.cs b/MediaBrowser.Model/System/IEnvironmentInfo.cs deleted file mode 100644 index 3ffcc7de14..0000000000 --- a/MediaBrowser.Model/System/IEnvironmentInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Runtime.InteropServices; - -namespace MediaBrowser.Model.System -{ - public interface IEnvironmentInfo - { - OperatingSystem OperatingSystem { get; } - string OperatingSystemName { get; } - string OperatingSystemVersion { get; } - Architecture SystemArchitecture { get; } - } - - public enum OperatingSystem - { - Windows, - Linux, - OSX, - BSD, - Android - } -} diff --git a/MediaBrowser.Model/System/OperatingSystemId.cs b/MediaBrowser.Model/System/OperatingSystemId.cs new file mode 100644 index 0000000000..e81dd4213f --- /dev/null +++ b/MediaBrowser.Model/System/OperatingSystemId.cs @@ -0,0 +1,10 @@ +namespace MediaBrowser.Model.System +{ + public enum OperatingSystemId + { + Windows, + Linux, + Darwin, + BSD + } +} diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 6482f2c840..222c10798a 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -83,6 +83,12 @@ namespace MediaBrowser.Model.System /// The program data path. public string ProgramDataPath { get; set; } + /// + /// Gets or sets the web UI resources path. + /// + /// The web UI resources path. + public string WebPath { get; set; } + /// /// Gets or sets the items by name path. /// diff --git a/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs b/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs deleted file mode 100644 index b39325958b..0000000000 --- a/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Xml; - -namespace MediaBrowser.Model.Xml -{ - public interface IXmlReaderSettingsFactory - { - XmlReaderSettings Create(bool enableValidation); - } -} diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index f26087fda3..860ea13cf6 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -942,10 +942,7 @@ namespace MediaBrowser.Providers.Manager _activeRefreshes[id] = 0; } - if (RefreshStarted != null) - { - RefreshStarted(this, new GenericEventArgs(item)); - } + RefreshStarted?.Invoke(this, new GenericEventArgs(item)); } public void OnRefreshComplete(BaseItem item) @@ -956,10 +953,7 @@ namespace MediaBrowser.Providers.Manager _activeRefreshes.Remove(item.Id); } - if (RefreshCompleted != null) - { - RefreshCompleted(this, new GenericEventArgs(item)); - } + RefreshCompleted?.Invoke(this, new GenericEventArgs(item)); } public double? GetRefreshProgress(Guid id) @@ -986,10 +980,7 @@ namespace MediaBrowser.Providers.Manager { _activeRefreshes[id] = progress; - if (RefreshProgress != null) - { - RefreshProgress(this, new GenericEventArgs>(new Tuple(item, progress))); - } + RefreshProgress?.Invoke(this, new GenericEventArgs>(new Tuple(item, progress))); } else { @@ -1079,17 +1070,14 @@ namespace MediaBrowser.Providers.Manager await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); // Collection folders don't validate their children so we'll have to simulate that here - var collectionFolder = item as CollectionFolder; - if (collectionFolder != null) + if (item is CollectionFolder collectionFolder) { await RefreshCollectionFolderChildren(options, collectionFolder, cancellationToken).ConfigureAwait(false); } else { - var folder = item as Folder; - - if (folder != null) + if (item is Folder folder) { await folder.ValidateChildren(new SimpleProgress(), cancellationToken, options).ConfigureAwait(false); } @@ -1098,16 +1086,11 @@ namespace MediaBrowser.Providers.Manager private async Task RefreshCollectionFolderChildren(MetadataRefreshOptions options, CollectionFolder collectionFolder, CancellationToken cancellationToken) { - foreach (var child in collectionFolder.GetPhysicalFolders().ToList()) + foreach (var child in collectionFolder.GetPhysicalFolders()) { await child.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); - if (child.IsFolder) - { - var folder = (Folder)child; - - await folder.ValidateChildren(new SimpleProgress(), cancellationToken, options, true).ConfigureAwait(false); - } + await child.ValidateChildren(new SimpleProgress(), cancellationToken, options, true).ConfigureAwait(false); } } @@ -1116,20 +1099,18 @@ namespace MediaBrowser.Providers.Manager var albums = _libraryManagerFactory() .GetItemList(new InternalItemsQuery { - IncludeItemTypes = new[] { typeof(MusicAlbum).Name }, + IncludeItemTypes = new[] { nameof(MusicAlbum) }, ArtistIds = new[] { item.Id }, DtoOptions = new DtoOptions(false) { EnableImages = false } }) - .OfType() - .ToList(); + .OfType(); var musicArtists = albums .Select(i => i.MusicArtist) - .Where(i => i != null) - .ToList(); + .Where(i => i != null); var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new SimpleProgress(), cancellationToken, options, true)); diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 52a52efdc0..cfbb85ea6b 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -11,6 +11,7 @@ + diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs index e4bb52217c..3797f9039a 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Globalization; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -15,7 +15,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Xml; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Music @@ -28,17 +28,42 @@ namespace MediaBrowser.Providers.Music private readonly IApplicationHost _appHost; private readonly ILogger _logger; private readonly IJsonSerializer _json; - private readonly IXmlReaderSettingsFactory _xmlSettings; + private Stopwatch _stopWatchMusicBrainz = new Stopwatch(); - public static string MusicBrainzBaseUrl = "https://www.musicbrainz.org"; + public readonly string MusicBrainzBaseUrl; - public MusicBrainzAlbumProvider(IHttpClient httpClient, IApplicationHost appHost, ILogger logger, IJsonSerializer json, IXmlReaderSettingsFactory xmlSettings) + /// + /// The Jellyfin user-agent is unrestricted but source IP must not exceed + /// one request per second, therefore we rate limit to avoid throttling. + /// Be prudent, use a value slightly above the minimun required. + /// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting + /// + private const long MusicBrainzQueryIntervalMs = 1050u; + + /// + /// For each single MB lookup/search, this is the maximum number of + /// attempts that shall be made whilst receiving a 503 Server + /// Unavailable (indicating throttled) response. + /// + private const uint MusicBrainzQueryAttempts = 5u; + + public MusicBrainzAlbumProvider( + IHttpClient httpClient, + IApplicationHost appHost, + ILogger logger, + IJsonSerializer json, + IConfiguration configuration) { _httpClient = httpClient; _appHost = appHost; _logger = logger; _json = json; - _xmlSettings = xmlSettings; + + MusicBrainzBaseUrl = configuration["MusicBrainz:BaseUrl"]; + + // Use a stopwatch to ensure we don't exceed the MusicBrainz rate limit + _stopWatchMusicBrainz.Start(); + Current = this; } @@ -47,9 +72,7 @@ namespace MediaBrowser.Providers.Music var releaseId = searchInfo.GetReleaseId(); var releaseGroupId = searchInfo.GetReleaseGroupId(); - string url = null; - var isNameSearch = false; - bool forceMusicBrainzProper = false; + string url; if (!string.IsNullOrEmpty(releaseId)) { @@ -58,7 +81,6 @@ namespace MediaBrowser.Providers.Music else if (!string.IsNullOrEmpty(releaseGroupId)) { url = string.Format("/ws/2/release?release-group={0}", releaseGroupId); - forceMusicBrainzProper = true; } else { @@ -72,8 +94,6 @@ namespace MediaBrowser.Providers.Music } else { - isNameSearch = true; - // I'm sure there is a better way but for now it resolves search for 12" Mixes var queryName = searchInfo.Name.Replace("\"", string.Empty); @@ -85,7 +105,7 @@ namespace MediaBrowser.Providers.Music if (!string.IsNullOrWhiteSpace(url)) { - using (var response = await GetMusicBrainzResponse(url, isNameSearch, forceMusicBrainzProper, cancellationToken).ConfigureAwait(false)) + using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) { using (var stream = response.Content) { @@ -94,18 +114,20 @@ namespace MediaBrowser.Providers.Music } } - return new List(); + return Enumerable.Empty(); } - private List GetResultsFromResponse(Stream stream) + private IEnumerable GetResultsFromResponse(Stream stream) { using (var oReader = new StreamReader(stream, Encoding.UTF8)) { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; + var settings = new XmlReaderSettings() + { + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; using (var reader = XmlReader.Create(oReader, settings)) { @@ -141,7 +163,7 @@ namespace MediaBrowser.Providers.Music return result; - }).ToList(); + }); } } } @@ -239,23 +261,21 @@ namespace MediaBrowser.Providers.Music WebUtility.UrlEncode(albumName), artistId); - using (var response = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) + using (var stream = response.Content) + using (var oReader = new StreamReader(stream, Encoding.UTF8)) { - using (var stream = response.Content) + var settings = new XmlReaderSettings() { - using (var oReader = new StreamReader(stream, Encoding.UTF8)) - { - var settings = _xmlSettings.Create(false); + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - using (var reader = XmlReader.Create(oReader, settings)) - { - return ReleaseResult.Parse(reader).FirstOrDefault(); - } - } + using (var reader = XmlReader.Create(oReader, settings)) + { + return ReleaseResult.Parse(reader).FirstOrDefault(); } } } @@ -266,23 +286,21 @@ namespace MediaBrowser.Providers.Music WebUtility.UrlEncode(albumName), WebUtility.UrlEncode(artistName)); - using (var response = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) + using (var stream = response.Content) + using (var oReader = new StreamReader(stream, Encoding.UTF8)) { - using (var stream = response.Content) + var settings = new XmlReaderSettings() { - using (var oReader = new StreamReader(stream, Encoding.UTF8)) - { - var settings = _xmlSettings.Create(false); + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - using (var reader = XmlReader.Create(oReader, settings)) - { - return ReleaseResult.Parse(reader).FirstOrDefault(); - } - } + using (var reader = XmlReader.Create(oReader, settings)) + { + return ReleaseResult.Parse(reader).FirstOrDefault(); } } } @@ -297,7 +315,7 @@ namespace MediaBrowser.Providers.Music public List> Artists = new List>(); - public static List Parse(XmlReader reader) + public static IEnumerable Parse(XmlReader reader) { reader.MoveToContent(); reader.Read(); @@ -334,13 +352,11 @@ namespace MediaBrowser.Providers.Music } } - return new List(); + return Enumerable.Empty(); } - private static List ParseReleaseList(XmlReader reader) + private static IEnumerable ParseReleaseList(XmlReader reader) { - var list = new List(); - reader.MoveToContent(); reader.Read(); @@ -365,7 +381,7 @@ namespace MediaBrowser.Providers.Music var release = ParseRelease(subReader, releaseId); if (release != null) { - list.Add(release); + yield return release; } } break; @@ -382,8 +398,6 @@ namespace MediaBrowser.Providers.Music reader.Read(); } } - - return list; } private static ReleaseResult ParseRelease(XmlReader reader, string releaseId) @@ -548,7 +562,7 @@ namespace MediaBrowser.Providers.Music return (null, null); } - private static ValueTuple ParseArtistArtistCredit(XmlReader reader, string artistId) + private static (string name, string id) ParseArtistArtistCredit(XmlReader reader, string artistId) { reader.MoveToContent(); reader.Read(); @@ -582,34 +596,32 @@ namespace MediaBrowser.Providers.Music } } - return new ValueTuple(name, artistId); + return (name, artistId); } private async Task GetReleaseIdFromReleaseGroupId(string releaseGroupId, CancellationToken cancellationToken) { var url = string.Format("/ws/2/release?release-group={0}", releaseGroupId); - using (var response = await GetMusicBrainzResponse(url, true, true, cancellationToken).ConfigureAwait(false)) + using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) + using (var stream = response.Content) + using (var oReader = new StreamReader(stream, Encoding.UTF8)) { - using (var stream = response.Content) + var settings = new XmlReaderSettings() { - using (var oReader = new StreamReader(stream, Encoding.UTF8)) + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; + + using (var reader = XmlReader.Create(oReader, settings)) + { + var result = ReleaseResult.Parse(reader).FirstOrDefault(); + + if (result != null) { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - using (var reader = XmlReader.Create(oReader, settings)) - { - var result = ReleaseResult.Parse(reader).FirstOrDefault(); - - if (result != null) - { - return result.ReleaseId; - } - } + return result.ReleaseId; } } } @@ -627,57 +639,55 @@ namespace MediaBrowser.Providers.Music { var url = string.Format("/ws/2/release-group/?query=reid:{0}", releaseEntryId); - using (var response = await GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false)) + using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) + using (var stream = response.Content) + using (var oReader = new StreamReader(stream, Encoding.UTF8)) { - using (var stream = response.Content) + var settings = new XmlReaderSettings() { - using (var oReader = new StreamReader(stream, Encoding.UTF8)) + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; + + using (var reader = XmlReader.Create(oReader, settings)) + { + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - using (var reader = XmlReader.Create(oReader, settings)) + if (reader.NodeType == XmlNodeType.Element) { - reader.MoveToContent(); - reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) + switch (reader.Name) { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) + case "release-group-list": { - case "release-group-list": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subReader = reader.ReadSubtree()) - { - return GetFirstReleaseGroupId(subReader); - } - } - default: - { - reader.Skip(); - break; - } + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subReader = reader.ReadSubtree()) + { + return GetFirstReleaseGroupId(subReader); + } + } + default: + { + reader.Skip(); + break; } - } - else - { - reader.Read(); - } } - return null; + } + else + { + reader.Read(); } } + return null; } } } @@ -714,37 +724,56 @@ namespace MediaBrowser.Providers.Music return null; } - internal Task GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken) - { - return GetMusicBrainzResponse(url, isSearch, false, cancellationToken); - } - /// - /// Gets the music brainz response. + /// Makes request to MusicBrainz server and awaits a response. + /// A 503 Service Unavailable response indicates throttling to maintain a rate limit. + /// A number of retries shall be made in order to try and satisfy the request before + /// giving up and returning null. /// - internal async Task GetMusicBrainzResponse(string url, bool isSearch, bool forceMusicBrainzProper, CancellationToken cancellationToken) + internal async Task GetMusicBrainzResponse(string url, CancellationToken cancellationToken) { - var urlInfo = new MbzUrl(MusicBrainzBaseUrl, 1000); - var throttleMs = urlInfo.throttleMs; - - if (throttleMs > 0) - { - // MusicBrainz is extremely adamant about limiting to one request per second - _logger.LogDebug("Throttling MusicBrainz by {0}ms", throttleMs.ToString(CultureInfo.InvariantCulture)); - await Task.Delay(throttleMs, cancellationToken).ConfigureAwait(false); - } - - url = urlInfo.url.TrimEnd('/') + url; - var options = new HttpRequestOptions { - Url = url, + Url = MusicBrainzBaseUrl.TrimEnd('/') + url, CancellationToken = cancellationToken, - UserAgent = _appHost.ApplicationUserAgent, - BufferContent = throttleMs > 0 + // MusicBrainz request a contact email address is supplied, as comment, in user agent field: + // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent + UserAgent = string.Format("{0} ( {1} )", _appHost.ApplicationUserAgent, _appHost.ApplicationUserAgentAddress), + BufferContent = false }; - return await _httpClient.SendAsync(options, "GET").ConfigureAwait(false); + HttpResponseInfo response; + var attempts = 0u; + + do + { + attempts++; + + if (_stopWatchMusicBrainz.ElapsedMilliseconds < MusicBrainzQueryIntervalMs) + { + // MusicBrainz is extremely adamant about limiting to one request per second + var delayMs = MusicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds; + await Task.Delay((int)delayMs, cancellationToken).ConfigureAwait(false); + } + + // Write time since last request to debug log as evidence we're meeting rate limit + // requirement, before resetting stopwatch back to zero. + _logger.LogDebug("GetMusicBrainzResponse: Time since previous request: {0} ms", _stopWatchMusicBrainz.ElapsedMilliseconds); + _stopWatchMusicBrainz.Restart(); + + response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false); + + // We retry a finite number of times, and only whilst MB is indcating 503 (throttling) + } + while (attempts < MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable); + + // Log error if unable to query MB database due to throttling + if (attempts == MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable ) + { + _logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, options.Url); + } + + return response; } public int Order => 0; @@ -753,17 +782,5 @@ namespace MediaBrowser.Providers.Music { throw new NotImplementedException(); } - - internal class MbzUrl - { - internal MbzUrl(string url, int throttleMs) - { - this.url = url; - this.throttleMs = throttleMs; - } - - public string url { get; set; } - public int throttleMs { get; set; } - } } } diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs index 8d10834c32..59280df897 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs @@ -13,17 +13,14 @@ using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.Music { public class MusicBrainzArtistProvider : IRemoteMetadataProvider { - private readonly IXmlReaderSettingsFactory _xmlSettings; - - public MusicBrainzArtistProvider(IXmlReaderSettingsFactory xmlSettings) + public MusicBrainzArtistProvider() { - _xmlSettings = xmlSettings; + } public async Task> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) @@ -34,7 +31,7 @@ namespace MediaBrowser.Providers.Music { var url = string.Format("/ws/2/artist/?query=arid:{0}", musicBrainzId); - using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false)) + using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) { using (var stream = response.Content) { @@ -49,11 +46,11 @@ namespace MediaBrowser.Providers.Music var url = string.Format("/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch)); - using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) { using (var stream = response.Content) { - var results = GetResultsFromResponse(stream); + var results = GetResultsFromResponse(stream).ToList(); if (results.Count > 0) { @@ -67,7 +64,7 @@ namespace MediaBrowser.Providers.Music // Try again using the search with accent characters url url = string.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); - using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) { using (var stream = response.Content) { @@ -77,18 +74,20 @@ namespace MediaBrowser.Providers.Music } } - return new List(); + return Enumerable.Empty(); } - private List GetResultsFromResponse(Stream stream) + private IEnumerable GetResultsFromResponse(Stream stream) { using (var oReader = new StreamReader(stream, Encoding.UTF8)) { - var settings = _xmlSettings.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; + var settings = new XmlReaderSettings() + { + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; using (var reader = XmlReader.Create(oReader, settings)) { @@ -127,15 +126,13 @@ namespace MediaBrowser.Providers.Music } } - return new List(); + return Enumerable.Empty(); } } } - private List ParseArtistList(XmlReader reader) + private IEnumerable ParseArtistList(XmlReader reader) { - var list = new List(); - reader.MoveToContent(); reader.Read(); @@ -160,7 +157,7 @@ namespace MediaBrowser.Providers.Music var artist = ParseArtist(subReader, mbzId); if (artist != null) { - list.Add(artist); + yield return artist; } } break; @@ -177,8 +174,6 @@ namespace MediaBrowser.Providers.Music reader.Read(); } } - - return list; } private RemoteSearchResult ParseArtist(XmlReader reader, string artistId) @@ -278,7 +273,7 @@ namespace MediaBrowser.Providers.Music /// /// The name. /// System.String. - private string UrlEncode(string name) + private static string UrlEncode(string name) { return WebUtility.UrlEncode(name); } diff --git a/MediaBrowser.Providers/Properties/AssemblyInfo.cs b/MediaBrowser.Providers/Properties/AssemblyInfo.cs index d2b13fc89d..f1c46899ce 100644 --- a/MediaBrowser.Providers/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Providers/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 0a2975e0f9..752c0941d0 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index afbd838e4b..d5b0b6fd89 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -8,7 +8,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.TV.TheTVDB; using Microsoft.Extensions.Logging; @@ -18,7 +17,6 @@ namespace MediaBrowser.Providers.TV public class SeriesMetadataService : MetadataService { private readonly ILocalizationManager _localization; - private readonly IXmlReaderSettingsFactory _xmlSettings; private readonly TvDbClientManager _tvDbClientManager; public SeriesMetadataService( @@ -29,13 +27,11 @@ namespace MediaBrowser.Providers.TV IUserDataManager userDataManager, ILibraryManager libraryManager, ILocalizationManager localization, - IXmlReaderSettingsFactory xmlSettings, TvDbClientManager tvDbClientManager ) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) { _localization = localization; - _xmlSettings = xmlSettings; _tvDbClientManager = tvDbClientManager; } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index b256f26675..5474a7c398 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -192,6 +192,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB Type = PersonType.Director }); } + foreach (var person in episode.GuestStars) { var index = person.IndexOf('('); @@ -212,6 +213,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB Role = role }); } + foreach (var writer in episode.Writers) { result.AddPerson(new PersonInfo diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 9c24e4c987..5ea73dfbf5 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -23,7 +23,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB { internal static TvdbSeriesProvider Current { get; private set; } private readonly IHttpClient _httpClient; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localizationManager; @@ -146,6 +145,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB private async Task GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken) { + TvDbResponse result = null; try @@ -334,7 +334,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB result.ResultLanguage = metadataLanguage; series.AirDays = TVUtils.GetAirDays(tvdbSeries.AirsDayOfWeek); series.AirTime = tvdbSeries.AirsTime; - series.CommunityRating = (float?)tvdbSeries.SiteRating; series.SetProviderId(MetadataProviders.Imdb, tvdbSeries.ImdbId); series.SetProviderId(MetadataProviders.Zap2It, tvdbSeries.Zap2itId); diff --git a/MediaBrowser.Tests/Properties/AssemblyInfo.cs b/MediaBrowser.Tests/Properties/AssemblyInfo.cs index 9bc2a19f21..1bd3ef5d64 100644 --- a/MediaBrowser.Tests/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Tests/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] +[assembly: AssemblyProduct("Jellyfin System")] [assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 531978e1dd..58ab2d27b5 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -13,7 +13,6 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Plugins; -using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; @@ -117,20 +116,26 @@ namespace MediaBrowser.WebDashboard.Api private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; private readonly IJsonSerializer _jsonSerializer; - private readonly IAssemblyInfo _assemblyInfo; private IResourceFileManager _resourceFileManager; /// /// Initializes a new instance of the class. /// - public DashboardService(IServerApplicationHost appHost, IResourceFileManager resourceFileManager, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, IAssemblyInfo assemblyInfo, ILogger logger, IHttpResultFactory resultFactory) + public DashboardService( + IServerApplicationHost appHost, + IResourceFileManager resourceFileManager, + IServerConfigurationManager serverConfigurationManager, + IFileSystem fileSystem, + ILocalizationManager localization, + IJsonSerializer jsonSerializer, + ILogger logger, + IHttpResultFactory resultFactory) { _appHost = appHost; _serverConfigurationManager = serverConfigurationManager; _fileSystem = fileSystem; _localization = localization; _jsonSerializer = jsonSerializer; - _assemblyInfo = assemblyInfo; _logger = logger; _resultFactory = resultFactory; _resourceFileManager = resourceFileManager; @@ -149,7 +154,7 @@ namespace MediaBrowser.WebDashboard.Api return _serverConfigurationManager.Configuration.DashboardSourcePath; } - return Path.Combine(_serverConfigurationManager.ApplicationPaths.ApplicationResourcesPath, "jellyfin-web", "src"); + return _serverConfigurationManager.ApplicationPaths.WebPath; } } @@ -187,7 +192,7 @@ namespace MediaBrowser.WebDashboard.Api if (altPage != null) { plugin = altPage.Item2; - stream = _assemblyInfo.GetManifestResourceStream(plugin.GetType(), altPage.Item1.EmbeddedResourcePath); + stream = plugin.GetType().Assembly.GetManifestResourceStream(altPage.Item1.EmbeddedResourcePath); isJs = string.Equals(Path.GetExtension(altPage.Item1.EmbeddedResourcePath), ".js", StringComparison.OrdinalIgnoreCase); isTemplate = altPage.Item1.EmbeddedResourcePath.EndsWith(".template.html"); diff --git a/MediaBrowser.WebDashboard/Properties/AssemblyInfo.cs b/MediaBrowser.WebDashboard/Properties/AssemblyInfo.cs index 416ce4826a..584d490216 100644 --- a/MediaBrowser.WebDashboard/Properties/AssemblyInfo.cs +++ b/MediaBrowser.WebDashboard/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index f20dbbb6ea..5896497abb 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -13,8 +13,6 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Extensions; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using MediaBrowser.XbmcMetadata.Configuration; using MediaBrowser.XbmcMetadata.Savers; using Microsoft.Extensions.Logging; @@ -28,9 +26,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// The logger /// protected ILogger Logger { get; private set; } - protected IFileSystem FileSystem { get; private set; } protected IProviderManager ProviderManager { get; private set; } - protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; } private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IConfigurationManager _config; @@ -39,13 +35,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// /// Initializes a new instance of the class. /// - public BaseNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public BaseNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) { Logger = logger; _config = config; ProviderManager = providerManager; - FileSystem = fileSystem; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } /// @@ -68,12 +62,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers throw new ArgumentException("The metadata file was empty or null.", nameof(metadataFile)); } - var settings = XmlReaderSettingsFactory.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - _validProviderIds = _validProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); var idInfos = ProviderManager.GetExternalIdInfos(item.Item); @@ -92,7 +80,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers _validProviderIds.Add("tmdbcolid", "TmdbCollection"); _validProviderIds.Add("imdb_id", "Imdb"); - Fetch(item, metadataFile, settings, cancellationToken); + Fetch(item, metadataFile, GetXmlReaderSettings(), cancellationToken); } protected virtual bool SupportsUrlAfterClosingXmlTag => false; @@ -109,31 +97,26 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!SupportsUrlAfterClosingXmlTag) { using (var fileStream = File.OpenRead(metadataFile)) + using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) + using (var reader = XmlReader.Create(streamReader, settings)) { - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) + item.ResetPeople(); + + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + FetchDataFromXmlNode(reader, item); + } + else { - item.ResetPeople(); - - reader.MoveToContent(); reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - FetchDataFromXmlNode(reader, item); - } - else - { - reader.Read(); - } - } } } } @@ -141,81 +124,76 @@ namespace MediaBrowser.XbmcMetadata.Parsers } using (var fileStream = File.OpenRead(metadataFile)) + using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) { - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) + item.ResetPeople(); + + // Need to handle a url after the xml data + // http://kodi.wiki/view/NFO_files/movies + + var xml = streamReader.ReadToEnd(); + + // Find last closing Tag + // Need to do this in two steps to account for random > characters after the closing xml + var index = xml.LastIndexOf(@"', index); + } - // Need to handle a url after the xml data - // http://kodi.wiki/view/NFO_files/movies + if (index != -1) + { + var endingXml = xml.Substring(index); - var xml = streamReader.ReadToEnd(); + ParseProviderLinks(item.Item, endingXml); - // Find last closing Tag - // Need to do this in two steps to account for random > characters after the closing xml - var index = xml.LastIndexOf(@"', index); - } - - if (index != -1) - { - var endingXml = xml.Substring(index); - - ParseProviderLinks(item.Item, endingXml); - - // If the file is just an imdb url, don't go any further - if (index == 0) - { - return; - } - - xml = xml.Substring(0, index + 1); - } - else - { - // If the file is just an Imdb url, handle that - - ParseProviderLinks(item.Item, xml); - return; } - // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions - try + xml = xml.Substring(0, index + 1); + } + else + { + // If the file is just an Imdb url, handle that + + ParseProviderLinks(item.Item, xml); + + return; + } + + // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions + try + { + using (var stringReader = new StringReader(xml)) + using (var reader = XmlReader.Create(stringReader, settings)) { - using (var stringReader = new StringReader(xml)) + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(stringReader, settings)) + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + FetchDataFromXmlNode(reader, item); + } + else { - reader.MoveToContent(); reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - FetchDataFromXmlNode(reader, item); - } - else - { - reader.Read(); - } - } } } } - catch (XmlException) - { + } + catch (XmlException) + { - } } } } @@ -920,6 +898,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers }; } + internal XmlReaderSettings GetXmlReaderSettings() + => new XmlReaderSettings() + { + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; + /// /// Used to split names of comma or pipe delimeted genres and people /// @@ -935,19 +922,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers value = value.Trim().Trim(separator); - return string.IsNullOrWhiteSpace(value) ? Array.Empty() : Split(value, separator, StringSplitOptions.RemoveEmptyEntries); - } - - /// - /// Provides an additional overload for string.split - /// - /// The val. - /// The separators. - /// The options. - /// System.String[][]. - private string[] Split(string val, char[] separators, StringSplitOptions options) - { - return val.Split(separators, options); + return string.IsNullOrWhiteSpace(value) ? Array.Empty() : value.Split(separator, StringSplitOptions.RemoveEmptyEntries); } } } diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index a8cb6ac40d..7f42240766 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -8,8 +8,6 @@ using System.Xml; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.XbmcMetadata.Parsers @@ -29,53 +27,48 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected override void Fetch(MetadataResult item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken) { using (var fileStream = File.OpenRead(metadataFile)) + using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) { - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) + item.ResetPeople(); + + var xml = streamReader.ReadToEnd(); + + var srch = ""; + var index = xml.IndexOf(srch, StringComparison.OrdinalIgnoreCase); + + if (index != -1) { - item.ResetPeople(); + xml = xml.Substring(0, index + srch.Length); + } - var xml = streamReader.ReadToEnd(); - - var srch = ""; - var index = xml.IndexOf(srch, StringComparison.OrdinalIgnoreCase); - - if (index != -1) + // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions + try + { + using (var stringReader = new StringReader(xml)) + using (var reader = XmlReader.Create(stringReader, settings)) { - xml = xml.Substring(0, index + srch.Length); - } + reader.MoveToContent(); + reader.Read(); - // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions - try - { - using (var stringReader = new StringReader(xml)) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(stringReader, settings)) + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + FetchDataFromXmlNode(reader, item); + } + else { - reader.MoveToContent(); reader.Read(); - - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - FetchDataFromXmlNode(reader, item); - } - else - { - reader.Read(); - } - } } } } - catch (XmlException) - { + } + catch (XmlException) + { - } } } } @@ -220,7 +213,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } - public EpisodeNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(logger, config, providerManager, fileSystem, xmlReaderSettingsFactory) + public EpisodeNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) + : base(logger, config, providerManager) { } } diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs index a3e48d30d9..0c4de9f339 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs @@ -7,8 +7,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.XbmcMetadata.Parsers @@ -126,14 +124,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions try { - var settings = XmlReaderSettingsFactory.Create(false); - - settings.CheckCharacters = false; - settings.IgnoreProcessingInstructions = true; - settings.IgnoreComments = true; - - // Use XmlReader for best performance - using (var reader = XmlReader.Create(stringReader, settings)) + using (var reader = XmlReader.Create(stringReader, GetXmlReaderSettings())) { reader.MoveToContent(); reader.Read(); @@ -167,7 +158,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } - public MovieNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(logger, config, providerManager, fileSystem, xmlReaderSettingsFactory) + public MovieNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) + : base(logger, config, providerManager) { } } diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs index 17f36d82d7..882f3a9d3a 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs @@ -3,8 +3,6 @@ using System.Xml; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.XbmcMetadata.Parsers @@ -42,7 +40,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } - public SeasonNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(logger, config, providerManager, fileSystem, xmlReaderSettingsFactory) + public SeasonNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) + : base(logger, config, providerManager) { } } diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs index 700656b652..b0f25ae64e 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs @@ -5,8 +5,6 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.XbmcMetadata.Parsers @@ -94,7 +92,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } - public SeriesNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(logger, config, providerManager, fileSystem, xmlReaderSettingsFactory) + public SeriesNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) + : base(logger, config, providerManager) { } } diff --git a/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs b/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs index 11e513f1be..b3e2f27179 100644 --- a/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs +++ b/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs index 1e22bf3582..6e6a22794d 100644 --- a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using MediaBrowser.XbmcMetadata.Parsers; using Microsoft.Extensions.Logging; @@ -15,20 +14,18 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly ILogger _logger; private readonly IConfigurationManager _config; private readonly IProviderManager _providerManager; - protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; } - public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _config = config; _providerManager = providerManager; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) { - new BaseNfoParser(_logger, _config, _providerManager, FileSystem, XmlReaderSettingsFactory).Fetch(result, path, cancellationToken); + new BaseNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs index ad811b4754..20abfc7f3a 100644 --- a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using MediaBrowser.XbmcMetadata.Parsers; using Microsoft.Extensions.Logging; @@ -15,20 +14,18 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly ILogger _logger; private readonly IConfigurationManager _config; private readonly IProviderManager _providerManager; - protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; } - public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _config = config; _providerManager = providerManager; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) { - new BaseNfoParser(_logger, _config, _providerManager, FileSystem, XmlReaderSettingsFactory).Fetch(result, path, cancellationToken); + new BaseNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs index 94f104f61b..28a0514d57 100644 --- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using MediaBrowser.XbmcMetadata.Parsers; using MediaBrowser.XbmcMetadata.Savers; using Microsoft.Extensions.Logging; @@ -17,15 +16,13 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly ILogger _logger; private readonly IConfigurationManager _config; private readonly IProviderManager _providerManager; - protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; } - public BaseVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public BaseVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _config = config; _providerManager = providerManager; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) @@ -34,7 +31,7 @@ namespace MediaBrowser.XbmcMetadata.Providers { Item = result.Item }; - new MovieNfoParser(_logger, _config, _providerManager, FileSystem, XmlReaderSettingsFactory).Fetch(tmpItem, path, cancellationToken); + new MovieNfoParser(_logger, _config, _providerManager).Fetch(tmpItem, path, cancellationToken); result.Item = (T)tmpItem.Item; result.People = tmpItem.People; diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs index bf05f0f38b..f90f283cf3 100644 --- a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs @@ -5,7 +5,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using MediaBrowser.XbmcMetadata.Parsers; using Microsoft.Extensions.Logging; @@ -16,22 +15,20 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly ILogger _logger; private readonly IConfigurationManager _config; private readonly IProviderManager _providerManager; - protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; } - public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) + public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _config = config; _providerManager = providerManager; - XmlReaderSettingsFactory = xmlReaderSettingsFactory; } protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) { var images = new List(); - new EpisodeNfoParser(_logger, _config, _providerManager, FileSystem, XmlReaderSettingsFactory).Fetch(result, images, path, cancellationToken); + new EpisodeNfoParser(_logger, _config, _providerManager).Fetch(result, images, path, cancellationToken); result.Images = images; } diff --git a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs index 77b3b3781b..d21164c022 100644 --- a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs @@ -3,28 +3,30 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Xml; using Microsoft.Extensions.Logging; namespace MediaBrowser.XbmcMetadata.Providers { public class MovieNfoProvider : BaseVideoNfoProvider { - public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, logger, config, providerManager, xmlReaderSettingsFactory) + public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) + : base(fileSystem, logger, config, providerManager) { } } public class MusicVideoNfoProvider : BaseVideoNfoProvider { - public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, logger, config, providerManager, xmlReaderSettingsFactory) + public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) + : base(fileSystem, logger, config, providerManager) { } } public class VideoNfoProvider : BaseVideoNfoProvider