aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/azure-pipelines-abi.yml1
-rw-r--r--.ci/azure-pipelines-main.yml1
-rw-r--r--.ci/azure-pipelines-package.yml1
-rw-r--r--.ci/azure-pipelines-test.yml1
-rw-r--r--.github/workflows/codeql-analysis.yml3
-rw-r--r--.github/workflows/openapi.yml2
-rw-r--r--Directory.Build.props5
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs27
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs2
-rw-r--r--Emby.Dlna/Emby.Dlna.csproj2
-rw-r--r--Emby.Dlna/Service/BaseControlHandler.cs2
-rw-r--r--Emby.Drawing/ImageProcessor.cs2
-rw-r--r--Emby.Naming/Common/NamingOptions.cs2
-rw-r--r--Emby.Naming/Emby.Naming.csproj2
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs3
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs2
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj12
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs2
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs43
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs14
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs37
-rw-r--r--Emby.Server.Implementations/Library/Validators/PeopleValidator.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs8
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs12
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/et.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json6
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs8
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs8
-rw-r--r--Jellyfin.Api/Controllers/ClientLogController.cs149
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs8
-rw-r--r--Jellyfin.Api/Controllers/HlsSegmentController.cs6
-rw-r--r--Jellyfin.Api/Controllers/ImageByNameController.cs6
-rw-r--r--Jellyfin.Api/Controllers/SubtitleController.cs2
-rw-r--r--Jellyfin.Api/Helpers/ClassMigrationHelper.cs2
-rw-r--r--Jellyfin.Api/Helpers/StreamingHelpers.cs2
-rw-r--r--Jellyfin.Api/Helpers/TranscodingJobHelper.cs2
-rw-r--r--Jellyfin.Api/Jellyfin.Api.csproj4
-rw-r--r--Jellyfin.Api/Models/ClientLogDtos/ClientLogDocumentResponseDto.cs22
-rw-r--r--Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs30
-rw-r--r--Jellyfin.Data/Jellyfin.Data.csproj4
-rw-r--r--Jellyfin.Server.Implementations/Events/EventManager.cs2
-rw-r--r--Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj8
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj9
-rw-r--r--Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs2
-rw-r--r--Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs2
-rw-r--r--Jellyfin.Server/Program.cs50
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj6
-rw-r--r--MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs55
-rw-r--r--MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs31
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs4
-rw-r--r--MediaBrowser.Controller/IO/FileData.cs4
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceManager.cs7
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj8
-rw-r--r--MediaBrowser.Controller/Providers/ImageRefreshOptions.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ItemInfo.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs2
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj4
-rw-r--r--MediaBrowser.Model/ClientLog/ClientLogEvent.cs75
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs5
-rw-r--r--MediaBrowser.Model/Dlna/DlnaMaps.cs14
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs2
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj6
-rw-r--r--MediaBrowser.Model/MediaInfo/AudioCodec.cs27
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs16
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs58
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleFormat.cs2
-rw-r--r--MediaBrowser.Model/Net/NetworkShareType.cs33
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs4
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs6
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj6
-rw-r--r--MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs4
-rw-r--r--MediaBrowser.XbmcMetadata/EntryPoint.cs2
-rw-r--r--deployment/Dockerfile.centos.amd642
-rw-r--r--deployment/Dockerfile.fedora.amd642
-rw-r--r--deployment/Dockerfile.ubuntu.amd642
-rw-r--r--deployment/Dockerfile.ubuntu.arm642
-rw-r--r--deployment/Dockerfile.ubuntu.armhf2
-rw-r--r--jellyfin.ruleset6
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj4
-rw-r--r--tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs18
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs32
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj4
-rw-r--r--tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj4
90 files changed, 668 insertions, 335 deletions
diff --git a/.ci/azure-pipelines-abi.yml b/.ci/azure-pipelines-abi.yml
index 31f861f63..cf74a4201 100644
--- a/.ci/azure-pipelines-abi.yml
+++ b/.ci/azure-pipelines-abi.yml
@@ -34,7 +34,6 @@ jobs:
inputs:
packageType: sdk
version: ${{ parameters.DotNetSdkVersion }}
- includePreviewVersions: true
- task: DotNetCoreCLI@2
displayName: 'Install ABI CompatibilityChecker Tool'
diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml
index 1086d51d2..b7112ba24 100644
--- a/.ci/azure-pipelines-main.yml
+++ b/.ci/azure-pipelines-main.yml
@@ -54,7 +54,6 @@ jobs:
inputs:
packageType: sdk
version: ${{ parameters.DotNetSdkVersion }}
- includePreviewVersions: true
- task: DotNetCoreCLI@2
displayName: 'Publish Server'
diff --git a/.ci/azure-pipelines-package.yml b/.ci/azure-pipelines-package.yml
index 4abe52b43..e227d5fe6 100644
--- a/.ci/azure-pipelines-package.yml
+++ b/.ci/azure-pipelines-package.yml
@@ -199,7 +199,6 @@ jobs:
inputs:
packageType: 'sdk'
version: '6.0.x'
- includePreviewVersions: true
- task: DotNetCoreCLI@2
displayName: 'Build Stable Nuget packages'
diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml
index 80a5732ee..cc94dc2c5 100644
--- a/.ci/azure-pipelines-test.yml
+++ b/.ci/azure-pipelines-test.yml
@@ -41,7 +41,6 @@ jobs:
inputs:
packageType: sdk
version: ${{ parameters.DotNetSdkVersion }}
- includePreviewVersions: true
- task: SonarCloudPrepare@1
displayName: 'Prepare analysis on SonarCloud'
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index e07d913b5..ea1d30cdf 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -25,8 +25,7 @@ jobs:
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
- include-prerelease: true
-
+
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml
index ea9188f1b..3e9346840 100644
--- a/.github/workflows/openapi.yml
+++ b/.github/workflows/openapi.yml
@@ -20,7 +20,6 @@ jobs:
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
- include-prerelease: true
- name: Generate openapi.json
run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests"
- name: Upload openapi.json
@@ -45,7 +44,6 @@ jobs:
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
- include-prerelease: true
- name: Generate openapi.json
run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests"
- name: Upload openapi.json
diff --git a/Directory.Build.props b/Directory.Build.props
index b899999ef..d243cde2b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -3,10 +3,13 @@
<PropertyGroup>
<Nullable>enable</Nullable>
- <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)/jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ </PropertyGroup>
+
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
</PropertyGroup>
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index ac336e5dc..34e5b8a36 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -291,9 +291,9 @@ namespace Emby.Dlna.ContentDirectory
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<Features xmlns=\"urn:schemas-upnp-org:av:avs\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:schemas-upnp-org:av:avs http://www.upnp.org/schemas/av/avs.xsd\">"
+ "<Feature name=\"samsung.com_BASICVIEW\" version=\"1\">"
- + "<container id=\"I\" type=\"object.item.imageItem\"/>"
- + "<container id=\"A\" type=\"object.item.audioItem\"/>"
- + "<container id=\"V\" type=\"object.item.videoItem\"/>"
+ + "<container id=\"0\" type=\"object.item.imageItem\"/>"
+ + "<container id=\"0\" type=\"object.item.audioItem\"/>"
+ + "<container id=\"0\" type=\"object.item.videoItem\"/>"
+ "</Feature>"
+ "</Features>";
}
@@ -800,6 +800,11 @@ namespace Emby.Dlna.ContentDirectory
}
};
+ if (limit.HasValue)
+ {
+ list = list.Take(limit.Value).ToList();
+ }
+
return new QueryResult<ServerItem>
{
Items = list,
@@ -884,6 +889,11 @@ namespace Emby.Dlna.ContentDirectory
}
};
+ if (limit.HasValue)
+ {
+ array = array.Take(limit.Value).ToArray();
+ }
+
return new QueryResult<ServerItem>
{
Items = array,
@@ -1010,6 +1020,11 @@ namespace Emby.Dlna.ContentDirectory
}
};
+ if (limit.HasValue)
+ {
+ list = list.Take(limit.Value).ToList();
+ }
+
return new QueryResult<ServerItem>
{
Items = list,
@@ -1037,7 +1052,7 @@ namespace Emby.Dlna.ContentDirectory
};
query.IsResumable = true;
- query.Limit = 10;
+ query.Limit = query.Limit ?? 10;
var result = _libraryManager.GetItemsResult(query);
@@ -1451,7 +1466,7 @@ namespace Emby.Dlna.ContentDirectory
new LatestItemsQuery
{
UserId = user.Id,
- Limit = 50,
+ Limit = query.Limit ?? 50,
IncludeItemTypes = new[] { nameof(Episode) },
ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = false
@@ -1476,7 +1491,7 @@ namespace Emby.Dlna.ContentDirectory
new LatestItemsQuery
{
UserId = user.Id,
- Limit = 50,
+ Limit = query.Limit ?? 50,
IncludeItemTypes = new[] { nameof(Movie) },
ParentId = parent?.Id ?? Guid.Empty,
GroupItems = true
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 0a84f30c4..b00e1c98a 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -729,7 +729,7 @@ namespace Emby.Dlna.Didl
{
if (item.PremiereDate.HasValue)
{
- AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NsDc);
+ AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture), NsDc);
}
}
diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj
index c8332e44e..7fdbd44f0 100644
--- a/Emby.Dlna/Emby.Dlna.csproj
+++ b/Emby.Dlna/Emby.Dlna.csproj
@@ -72,7 +72,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
</ItemGroup>
</Project>
diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs
index 581e4a286..780aad9c1 100644
--- a/Emby.Dlna/Service/BaseControlHandler.cs
+++ b/Emby.Dlna/Service/BaseControlHandler.cs
@@ -64,7 +64,7 @@ namespace Emby.Dlna.Service
requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false);
}
- Logger.LogDebug("Received control request {0}", requestInfo.LocalName);
+ Logger.LogDebug("Received control request {LocalName}, params: {@Headers}", requestInfo.LocalName, requestInfo.Headers);
var settings = new XmlWriterSettings
{
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index ac73cfa42..3f75e4fc7 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -26,7 +26,7 @@ namespace Emby.Drawing
public sealed class ImageProcessor : IImageProcessor, IDisposable
{
// Increment this when there's a change requiring caches to be invalidated
- private const string Version = "3";
+ private const char Version = '3';
private static readonly HashSet<string> _transparentImageTypes
= new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
index 5ddcf37fe..7bc9fbce8 100644
--- a/Emby.Naming/Common/NamingOptions.cs
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CA1819
+
using System;
using System.Linq;
using System.Text.RegularExpressions;
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
index e9e9edda6..4c5dcdafc 100644
--- a/Emby.Naming/Emby.Naming.csproj
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -38,7 +38,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0" PrivateAssets="All" />
</ItemGroup>
<!-- Code Analyzers-->
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 4f2fdfd3c..73919f306 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -56,6 +56,7 @@ using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Chapters;
+using MediaBrowser.Controller.ClientEvent;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
@@ -665,7 +666,7 @@ namespace Emby.Server.Implementations
serviceCollection.AddScoped<MediaInfoHelper>();
serviceCollection.AddScoped<AudioHelper>();
serviceCollection.AddScoped<DynamicHlsHelper>();
-
+ serviceCollection.AddScoped<IClientEventLogger, ClientEventLogger>();
serviceCollection.AddSingleton<IDirectoryService, DirectoryService>();
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 9287f5272..c6b32a52c 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -497,7 +497,7 @@ namespace Emby.Server.Implementations.Dto
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error getting {imageType} image info for {path}", image.Type, image.Path);
+ _logger.LogError(ex, "Error getting {ImageType} image info for {Path}", image.Type, image.Path);
return null;
}
}
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index c1ce4b557..042b8f71a 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -25,12 +25,12 @@
<ItemGroup>
<PackageReference Include="DiscUtils.Udf" Version="0.16.13" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0-rc.2*" />
- <PackageReference Include="Mono.Nat" Version="3.0.1" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
+ <PackageReference Include="Mono.Nat" Version="3.0.2" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.2" />
<PackageReference Include="sharpcompress" Version="0.30.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index e9d069cd3..7ebc800b9 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -276,7 +276,7 @@ namespace Emby.Server.Implementations.IO
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error watching path: {path}", path);
+ _logger.LogError(ex, "Error watching path: {Path}", path);
}
});
}
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index eeee28842..777cd2cd4 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -23,6 +21,11 @@ namespace Emby.Server.Implementations.IO
private readonly string _tempPath;
private static readonly bool _isEnvironmentCaseInsensitive = OperatingSystem.IsWindows();
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ManagedFileSystem"/> class.
+ /// </summary>
+ /// <param name="logger">The <see cref="ILogger"/> instance to use.</param>
+ /// <param name="applicationPaths">The <see cref="IApplicationPaths"/> instance to use.</param>
public ManagedFileSystem(
ILogger<ManagedFileSystem> logger,
IApplicationPaths applicationPaths)
@@ -31,6 +34,7 @@ namespace Emby.Server.Implementations.IO
_tempPath = applicationPaths.TempDirectory;
}
+ /// <inheritdoc />
public virtual void AddShortcutHandler(IShortcutHandler handler)
{
_shortcutHandlers.Add(handler);
@@ -72,6 +76,7 @@ namespace Emby.Server.Implementations.IO
return handler?.Resolve(filename);
}
+ /// <inheritdoc />
public virtual string MakeAbsolutePath(string folderPath, string filePath)
{
// path is actually a stream
@@ -358,11 +363,13 @@ namespace Emby.Server.Implementations.IO
return GetCreationTimeUtc(GetFileSystemInfo(path));
}
+ /// <inheritdoc />
public virtual DateTime GetCreationTimeUtc(FileSystemMetadata info)
{
return info.CreationTimeUtc;
}
+ /// <inheritdoc />
public virtual DateTime GetLastWriteTimeUtc(FileSystemMetadata info)
{
return info.LastWriteTimeUtc;
@@ -397,6 +404,7 @@ namespace Emby.Server.Implementations.IO
return GetLastWriteTimeUtc(GetFileSystemInfo(path));
}
+ /// <inheritdoc />
public virtual void SetHidden(string path, bool isHidden)
{
if (!OperatingSystem.IsWindows())
@@ -421,6 +429,7 @@ namespace Emby.Server.Implementations.IO
}
}
+ /// <inheritdoc />
public virtual void SetAttributes(string path, bool isHidden, bool readOnly)
{
if (!OperatingSystem.IsWindows())
@@ -444,7 +453,7 @@ namespace Emby.Server.Implementations.IO
if (readOnly)
{
- attributes = attributes | FileAttributes.ReadOnly;
+ attributes |= FileAttributes.ReadOnly;
}
else
{
@@ -453,7 +462,7 @@ namespace Emby.Server.Implementations.IO
if (isHidden)
{
- attributes = attributes | FileAttributes.Hidden;
+ attributes |= FileAttributes.Hidden;
}
else
{
@@ -498,6 +507,7 @@ namespace Emby.Server.Implementations.IO
File.Copy(temp1, file2, true);
}
+ /// <inheritdoc />
public virtual bool ContainsSubPath(string parentPath, string path)
{
if (string.IsNullOrEmpty(parentPath))
@@ -515,6 +525,7 @@ namespace Emby.Server.Implementations.IO
_isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
}
+ /// <inheritdoc />
public virtual string NormalizePath(string path)
{
if (string.IsNullOrEmpty(path))
@@ -530,6 +541,7 @@ namespace Emby.Server.Implementations.IO
return Path.TrimEndingDirectorySeparator(path);
}
+ /// <inheritdoc />
public virtual bool AreEqual(string path1, string path2)
{
if (path1 == null && path2 == null)
@@ -548,6 +560,7 @@ namespace Emby.Server.Implementations.IO
_isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
}
+ /// <inheritdoc />
public virtual string GetFileNameWithoutExtension(FileSystemMetadata info)
{
if (info.IsDirectory)
@@ -558,11 +571,11 @@ namespace Emby.Server.Implementations.IO
return Path.GetFileNameWithoutExtension(info.FullName);
}
+ /// <inheritdoc />
public virtual bool IsPathFile(string path)
{
- // Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\
- if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 &&
- !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
+ if (path.Contains("://", StringComparison.OrdinalIgnoreCase)
+ && !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -570,17 +583,23 @@ namespace Emby.Server.Implementations.IO
return true;
}
+ /// <inheritdoc />
public virtual void DeleteFile(string path)
{
SetAttributes(path, false, false);
File.Delete(path);
}
+ /// <inheritdoc />
public virtual List<FileSystemMetadata> GetDrives()
{
// check for ready state to avoid waiting for drives to timeout
// some drives on linux have no actual size or are used for other purposes
- return DriveInfo.GetDrives().Where(d => d.IsReady && d.TotalSize != 0 && d.DriveType != DriveType.Ram)
+ return DriveInfo.GetDrives()
+ .Where(
+ d => (d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network || d.DriveType == DriveType.Removable)
+ && d.IsReady
+ && d.TotalSize != 0)
.Select(d => new FileSystemMetadata
{
Name = d.Name,
@@ -589,16 +608,19 @@ namespace Emby.Server.Implementations.IO
}).ToList();
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
{
return ToMetadata(new DirectoryInfo(path).EnumerateDirectories("*", GetEnumerationOptions(recursive)));
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
{
return GetFiles(path, null, false, recursive);
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string>? extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
{
var enumerationOptions = GetEnumerationOptions(recursive);
@@ -629,6 +651,7 @@ namespace Emby.Server.Implementations.IO
return ToMetadata(files);
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
{
var directoryInfo = new DirectoryInfo(path);
@@ -642,16 +665,19 @@ namespace Emby.Server.Implementations.IO
return infos.Select(GetFileSystemMetadata);
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
{
return Directory.EnumerateDirectories(path, "*", GetEnumerationOptions(recursive));
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetFilePaths(string path, bool recursive = false)
{
return GetFilePaths(path, null, false, recursive);
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetFilePaths(string path, string[]? extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
{
var enumerationOptions = GetEnumerationOptions(recursive);
@@ -682,6 +708,7 @@ namespace Emby.Server.Implementations.IO
return files;
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
{
return Directory.EnumerateFileSystemEntries(path, "*", GetEnumerationOptions(recursive));
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 1326f60fe..2dbb569c6 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -492,7 +492,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error in {resolver} resolving {path}", resolver.GetType().Name, args.Path);
+ _logger.LogError(ex, "Error in {Resolver} resolving {Path}", resolver.GetType().Name, args.Path);
return null;
}
}
@@ -799,7 +799,7 @@ namespace Emby.Server.Implementations.Library
{
var userRootPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
- _logger.LogDebug("Creating userRootPath at {path}", userRootPath);
+ _logger.LogDebug("Creating userRootPath at {Path}", userRootPath);
Directory.CreateDirectory(userRootPath);
var newItemId = GetNewItemId(userRootPath, typeof(UserRootFolder));
@@ -810,7 +810,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error creating UserRootFolder {path}", newItemId);
+ _logger.LogError(ex, "Error creating UserRootFolder {Path}", newItemId);
}
if (tmpItem == null)
@@ -827,7 +827,7 @@ namespace Emby.Server.Implementations.Library
}
_userRootFolder = tmpItem;
- _logger.LogDebug("Setting userRootFolder: {folder}", _userRootFolder);
+ _logger.LogDebug("Setting userRootFolder: {Folder}", _userRootFolder);
}
}
}
@@ -1213,7 +1213,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error resolving shortcut file {file}", i);
+ _logger.LogError(ex, "Error resolving shortcut file {File}", i);
return null;
}
})
@@ -1698,7 +1698,7 @@ namespace Emby.Server.Implementations.Library
if (video == null)
{
- _logger.LogError("Intro resolver returned null for {path}.", info.Path);
+ _logger.LogError("Intro resolver returned null for {Path}.", info.Path);
}
else
{
@@ -1717,7 +1717,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error resolving path {path}.", info.Path);
+ _logger.LogError(ex, "Error resolving path {Path}.", info.Path);
}
}
else
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 351fced34..972d4ebbb 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -45,6 +45,7 @@ namespace Emby.Server.Implementations.Library
private readonly IMediaEncoder _mediaEncoder;
private readonly ILocalizationManager _localizationManager;
private readonly IApplicationPaths _appPaths;
+ private readonly IDirectoryService _directoryService;
private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
@@ -61,7 +62,8 @@ namespace Emby.Server.Implementations.Library
ILogger<MediaSourceManager> logger,
IFileSystem fileSystem,
IUserDataManager userDataManager,
- IMediaEncoder mediaEncoder)
+ IMediaEncoder mediaEncoder,
+ IDirectoryService directoryService)
{
_itemRepo = itemRepo;
_userManager = userManager;
@@ -72,6 +74,7 @@ namespace Emby.Server.Implementations.Library
_mediaEncoder = mediaEncoder;
_localizationManager = localizationManager;
_appPaths = applicationPaths;
+ _directoryService = directoryService;
}
public void AddParts(IEnumerable<IMediaSourceProvider> providers)
@@ -106,16 +109,6 @@ namespace Emby.Server.Implementations.Library
return false;
}
- public List<MediaStream> GetMediaStreams(string mediaSourceId)
- {
- var list = GetMediaStreams(new MediaStreamQuery
- {
- ItemId = new Guid(mediaSourceId)
- });
-
- return GetMediaStreamsForItem(list);
- }
-
public List<MediaStream> GetMediaStreams(Guid itemId)
{
var list = GetMediaStreams(new MediaStreamQuery
@@ -161,7 +154,7 @@ namespace Emby.Server.Implementations.Library
if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio || i.Type == MediaStreamType.Video))
{
await item.RefreshMetadata(
- new MetadataRefreshOptions(new DirectoryService(_fileSystem))
+ new MetadataRefreshOptions(_directoryService)
{
EnableRemoteContentProbe = true,
MetadataRefreshMode = MetadataRefreshMode.FullRefresh
@@ -212,6 +205,7 @@ namespace Emby.Server.Implementations.Library
return SortMediaSources(list);
}
+ /// <inheritdoc />>
public MediaProtocol GetPathProtocol(string path)
{
if (path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase))
@@ -258,7 +252,7 @@ namespace Emby.Server.Implementations.Library
{
if (path != null)
{
- if (path.IndexOf(".m3u", StringComparison.OrdinalIgnoreCase) != -1)
+ if (path.Contains(".m3u", StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -297,7 +291,7 @@ namespace Emby.Server.Implementations.Library
catch (Exception ex)
{
_logger.LogError(ex, "Error getting media sources");
- return new List<MediaSourceInfo>();
+ return Enumerable.Empty<MediaSourceInfo>();
}
}
@@ -494,14 +488,11 @@ namespace Emby.Server.Implementations.Library
_liveStreamSemaphore.Release();
}
- // TODO: Don't hardcode this
- const bool isAudio = false;
-
try
{
if (mediaSource.MediaStreams.Any(i => i.Index != -1) || !mediaSource.SupportsProbing)
{
- AddMediaInfo(mediaSource, isAudio);
+ AddMediaInfo(mediaSource);
}
else
{
@@ -509,14 +500,14 @@ namespace Emby.Server.Implementations.Library
string cacheKey = request.OpenToken;
await new LiveStreamHelper(_mediaEncoder, _logger, _appPaths)
- .AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken)
+ .AddMediaInfoWithProbe(mediaSource, false, cacheKey, true, cancellationToken)
.ConfigureAwait(false);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error probing live tv stream");
- AddMediaInfo(mediaSource, isAudio);
+ AddMediaInfo(mediaSource);
}
// TODO: @bond Fix
@@ -536,7 +527,7 @@ namespace Emby.Server.Implementations.Library
return new Tuple<LiveStreamResponse, IDirectStreamProvider>(new LiveStreamResponse(clone), liveStream as IDirectStreamProvider);
}
- private static void AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio)
+ private static void AddMediaInfo(MediaSourceInfo mediaSource)
{
mediaSource.DefaultSubtitleStreamIndex = null;
@@ -855,9 +846,7 @@ namespace Emby.Server.Implementations.Library
return (provider, keyId);
}
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
+ /// <inheritdoc />
public void Dispose()
{
Dispose(true);
diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
index 8739a9e1b..8a9a4b865 100644
--- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.Library.Validators
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error validating IBN entry {person}", person);
+ _logger.LogError(ex, "Error validating IBN entry {Person}", person);
}
// Update progress
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 8045e814c..367f3cb9e 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -1308,16 +1308,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
await recorder.Record(directStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
recordingStatus = RecordingStatus.Completed;
- _logger.LogInformation("Recording completed: {recordPath}", recordPath);
+ _logger.LogInformation("Recording completed: {RecordPath}", recordPath);
}
catch (OperationCanceledException)
{
- _logger.LogInformation("Recording stopped: {recordPath}", recordPath);
+ _logger.LogInformation("Recording stopped: {RecordPath}", recordPath);
recordingStatus = RecordingStatus.Completed;
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error recording to {recordPath}", recordPath);
+ _logger.LogError(ex, "Error recording to {RecordPath}", recordPath);
recordingStatus = RecordingStatus.Error;
}
@@ -1404,7 +1404,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error deleting 0-byte failed recording file {path}", path);
+ _logger.LogError(ex, "Error deleting 0-byte failed recording file {Path}", path);
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 835028b92..8688688e9 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -225,13 +225,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
try
{
- _logger.LogInformation("Stopping ffmpeg recording process for {path}", _targetPath);
+ _logger.LogInformation("Stopping ffmpeg recording process for {Path}", _targetPath);
_process.StandardInput.WriteLine("q");
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error stopping recording transcoding job for {path}", _targetPath);
+ _logger.LogError(ex, "Error stopping recording transcoding job for {Path}", _targetPath);
}
if (_hasExited)
@@ -241,7 +241,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try
{
- _logger.LogInformation("Calling recording process.WaitForExit for {path}", _targetPath);
+ _logger.LogInformation("Calling recording process.WaitForExit for {Path}", _targetPath);
if (_process.WaitForExit(10000))
{
@@ -250,7 +250,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error waiting for recording process to exit for {path}", _targetPath);
+ _logger.LogError(ex, "Error waiting for recording process to exit for {Path}", _targetPath);
}
if (_hasExited)
@@ -260,13 +260,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try
{
- _logger.LogInformation("Killing ffmpeg recording process for {path}", _targetPath);
+ _logger.LogInformation("Killing ffmpeg recording process for {Path}", _targetPath);
_process.Kill();
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error killing recording transcoding job for {path}", _targetPath);
+ _logger.LogError(ex, "Error killing recording transcoding job for {Path}", _targetPath);
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index 21e1409ac..598e3f88a 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -393,7 +393,7 @@ namespace Emby.Server.Implementations.LiveTv
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error getting image info for {name}", info.Name);
+ _logger.LogError(ex, "Error getting image info for {Name}", info.Name);
}
return null;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index ea1a28fe8..a41b63f28 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1054,7 +1054,7 @@ namespace Emby.Server.Implementations.LiveTv
{
cancellationToken.ThrowIfCancellationRequested();
- _logger.LogDebug("Refreshing guide from {name}", service.Name);
+ _logger.LogDebug("Refreshing guide from {Name}", service.Name);
try
{
@@ -1135,7 +1135,7 @@ namespace Emby.Server.Implementations.LiveTv
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error getting channel information for {name}", channelInfo.Item2.Name);
+ _logger.LogError(ex, "Error getting channel information for {Name}", channelInfo.Item2.Name);
}
numComplete++;
@@ -1248,7 +1248,7 @@ namespace Emby.Server.Implementations.LiveTv
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error getting programs for channel {name}", currentChannel.Name);
+ _logger.LogError(ex, "Error getting programs for channel {Name}", currentChannel.Name);
}
numComplete++;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index 96c1bc3f3..9fba17ca3 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -82,7 +82,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
Directory.CreateDirectory(Path.GetDirectoryName(TempFilePath));
- Logger.LogInformation("Opening HDHR UDP Live stream from {host}", uri.Host);
+ Logger.LogInformation("Opening HDHR UDP Live stream from {Host}", uri.Host);
var remoteAddress = IPAddress.Parse(uri.Host);
IPAddress localAddress = null;
diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json
index c3596ecf1..e5405e515 100644
--- a/Emby.Server.Implementations/Localization/Core/et.json
+++ b/Emby.Server.Implementations/Localization/Core/et.json
@@ -17,7 +17,7 @@
"TaskCleanLogsDescription": "Kustutab logifailid, mis on vanemad kui {0} päeva.",
"TaskCleanLogs": "Puhasta logikataloog",
"TaskRefreshLibraryDescription": "Otsib meedikogust uusi faile ja värskendab metaandmeid.",
- "Collections": "Kollektsioonid",
+ "Collections": "Kogumikud",
"TaskRefreshLibrary": "Skaneeri meediakogu",
"TaskRefreshChapterImagesDescription": "Loob peatükkidega videote jaoks pisipildid.",
"TaskRefreshChapterImages": "Eralda peatükipildid",
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 36f4e3e7c..2d7163275 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -31,7 +31,7 @@
"ItemRemovedWithName": "{0} - изъято из медиатеки",
"LabelIpAddressValue": "IP-адрес: {0}",
"LabelRunningTimeValue": "Длительность: {0}",
- "Latest": "Последнее",
+ "Latest": "Крайнее",
"MessageApplicationUpdated": "Jellyfin Server был обновлён",
"MessageApplicationUpdatedTo": "Jellyfin Server был обновлён до {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Конфигурация сервера (раздел {0}) была обновлена",
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index 33aa0eea0..548e395a9 100644
--- a/Emby.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -62,11 +62,11 @@
"PluginUninstalledWithName": "{0} đã được gỡ bỏ",
"PluginInstalledWithName": "{0} đã được cài đặt",
"Plugin": "Plugin",
- "NotificationOptionVideoPlaybackStopped": "Phát lại video đã dừng",
+ "NotificationOptionVideoPlaybackStopped": "Đã dừng phát lại video",
"NotificationOptionVideoPlayback": "Đã bắt đầu phát lại video",
"NotificationOptionUserLockedOut": "Người dùng bị khóa",
"NotificationOptionTaskFailed": "Lỗi tác vụ đã lên lịch",
- "NotificationOptionServerRestartRequired": "Yêu cầu khởi động lại Server",
+ "NotificationOptionServerRestartRequired": "Yêu cầu khởi động lại máy chủ",
"NotificationOptionPluginUpdateInstalled": "Cập nhật Plugin đã được cài đặt",
"NotificationOptionPluginUninstalled": "Đã gỡ bỏ Plugin",
"NotificationOptionPluginInstalled": "Đã cài đặt Plugin",
@@ -75,7 +75,7 @@
"NotificationOptionInstallationFailed": "Cài đặt thất bại",
"NotificationOptionCameraImageUploaded": "Đã tải lên hình ảnh máy ảnh",
"NotificationOptionAudioPlaybackStopped": "Phát lại âm thanh đã dừng",
- "NotificationOptionAudioPlayback": "Phát lại âm thanh đã bắt đầu",
+ "NotificationOptionAudioPlayback": "Đã bắt đầu phát lại âm thanh",
"NotificationOptionApplicationUpdateInstalled": "Bản cập nhật ứng dụng đã được cài đặt",
"NotificationOptionApplicationUpdateAvailable": "Bản cập nhật ứng dụng hiện sẵn có",
"NewVersionIsAvailable": "Một phiên bản mới của Jellyfin Server sẵn có để tải.",
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index a575b260c..0941902fc 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -161,11 +161,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
catch (UnauthorizedAccessException ex)
{
- _logger.LogError(ex, "Error deleting directory {path}", directory);
+ _logger.LogError(ex, "Error deleting directory {Path}", directory);
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting directory {path}", directory);
+ _logger.LogError(ex, "Error deleting directory {Path}", directory);
}
}
}
@@ -179,11 +179,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
catch (UnauthorizedAccessException ex)
{
- _logger.LogError(ex, "Error deleting file {path}", path);
+ _logger.LogError(ex, "Error deleting file {Path}", path);
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting file {path}", path);
+ _logger.LogError(ex, "Error deleting file {Path}", path);
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
index b13fc7fc6..099d781cd 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
@@ -141,11 +141,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
catch (UnauthorizedAccessException ex)
{
- _logger.LogError(ex, "Error deleting directory {path}", directory);
+ _logger.LogError(ex, "Error deleting directory {Path}", directory);
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting directory {path}", directory);
+ _logger.LogError(ex, "Error deleting directory {Path}", directory);
}
}
}
@@ -159,11 +159,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
catch (UnauthorizedAccessException ex)
{
- _logger.LogError(ex, "Error deleting file {path}", path);
+ _logger.LogError(ex, "Error deleting file {Path}", path);
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting file {path}", path);
+ _logger.LogError(ex, "Error deleting file {Path}", path);
}
}
}
diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs
new file mode 100644
index 000000000..95d07c930
--- /dev/null
+++ b/Jellyfin.Api/Controllers/ClientLogController.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Net.Mime;
+using System.Threading.Tasks;
+using Jellyfin.Api.Attributes;
+using Jellyfin.Api.Constants;
+using Jellyfin.Api.Helpers;
+using Jellyfin.Api.Models.ClientLogDtos;
+using MediaBrowser.Controller.ClientEvent;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.ClientLog;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Jellyfin.Api.Controllers
+{
+ /// <summary>
+ /// Client log controller.
+ /// </summary>
+ [Authorize(Policy = Policies.DefaultAuthorization)]
+ public class ClientLogController : BaseJellyfinApiController
+ {
+ private const int MaxDocumentSize = 1_000_000;
+ private readonly IClientEventLogger _clientEventLogger;
+ private readonly IServerConfigurationManager _serverConfigurationManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientLogController"/> class.
+ /// </summary>
+ /// <param name="clientEventLogger">Instance of the <see cref="IClientEventLogger"/> interface.</param>
+ /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
+ public ClientLogController(
+ IClientEventLogger clientEventLogger,
+ IServerConfigurationManager serverConfigurationManager)
+ {
+ _clientEventLogger = clientEventLogger;
+ _serverConfigurationManager = serverConfigurationManager;
+ }
+
+ /// <summary>
+ /// Post event from client.
+ /// </summary>
+ /// <param name="clientLogEventDto">The client log dto.</param>
+ /// <response code="204">Event logged.</response>
+ /// <response code="403">Event logging disabled.</response>
+ /// <returns>Submission status.</returns>
+ [HttpPost]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ public ActionResult LogEvent([FromBody] ClientLogEventDto clientLogEventDto)
+ {
+ if (!_serverConfigurationManager.Configuration.AllowClientLogUpload)
+ {
+ return Forbid();
+ }
+
+ var (clientName, clientVersion, userId, deviceId) = GetRequestInformation();
+ Log(clientLogEventDto, userId, clientName, clientVersion, deviceId);
+ return NoContent();
+ }
+
+ /// <summary>
+ /// Bulk post events from client.
+ /// </summary>
+ /// <param name="clientLogEventDtos">The list of client log dtos.</param>
+ /// <response code="204">All events logged.</response>
+ /// <response code="403">Event logging disabled.</response>
+ /// <returns>Submission status.</returns>
+ [HttpPost("Bulk")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ public ActionResult LogEvents([FromBody] ClientLogEventDto[] clientLogEventDtos)
+ {
+ if (!_serverConfigurationManager.Configuration.AllowClientLogUpload)
+ {
+ return Forbid();
+ }
+
+ var (clientName, clientVersion, userId, deviceId) = GetRequestInformation();
+ foreach (var dto in clientLogEventDtos)
+ {
+ Log(dto, userId, clientName, clientVersion, deviceId);
+ }
+
+ return NoContent();
+ }
+
+ /// <summary>
+ /// Upload a document.
+ /// </summary>
+ /// <response code="200">Document saved.</response>
+ /// <response code="403">Event logging disabled.</response>
+ /// <response code="413">Upload size too large.</response>
+ /// <returns>Create response.</returns>
+ [HttpPost("Document")]
+ [ProducesResponseType(typeof(ClientLogDocumentResponseDto), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
+ [AcceptsFile(MediaTypeNames.Text.Plain)]
+ [RequestSizeLimit(MaxDocumentSize)]
+ public async Task<ActionResult<ClientLogDocumentResponseDto>> LogFile()
+ {
+ if (!_serverConfigurationManager.Configuration.AllowClientLogUpload)
+ {
+ return Forbid();
+ }
+
+ if (Request.ContentLength > MaxDocumentSize)
+ {
+ // Manually validate to return proper status code.
+ return StatusCode(StatusCodes.Status413PayloadTooLarge, $"Payload must be less than {MaxDocumentSize:N0} bytes");
+ }
+
+ var (clientName, clientVersion, _, _) = GetRequestInformation();
+ var fileName = await _clientEventLogger.WriteDocumentAsync(clientName, clientVersion, Request.Body)
+ .ConfigureAwait(false);
+ return Ok(new ClientLogDocumentResponseDto(fileName));
+ }
+
+ private void Log(
+ ClientLogEventDto dto,
+ Guid userId,
+ string clientName,
+ string clientVersion,
+ string deviceId)
+ {
+ _clientEventLogger.Log(new ClientLogEvent(
+ dto.Timestamp,
+ dto.Level,
+ userId,
+ clientName,
+ clientVersion,
+ deviceId,
+ dto.Message));
+ }
+
+ private (string ClientName, string ClientVersion, Guid UserId, string DeviceId) GetRequestInformation()
+ {
+ var clientName = ClaimHelpers.GetClient(HttpContext.User) ?? "unknown-client";
+ var clientVersion = ClaimHelpers.GetIsApiKey(HttpContext.User)
+ ? "apikey"
+ : ClaimHelpers.GetVersion(HttpContext.User) ?? "unknown-version";
+ var userId = ClaimHelpers.GetUserId(HttpContext.User) ?? Guid.Empty;
+ var deviceId = ClaimHelpers.GetDeviceId(HttpContext.User) ?? "unknown-device-id";
+
+ return (clientName, clientVersion, userId, deviceId);
+ }
+ }
+}
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 42e82dd5b..049fd503b 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -1794,7 +1794,7 @@ namespace Jellyfin.Api.Controllers
return;
}
- _logger.LogDebug("Deleting partial HLS file {path}", path);
+ _logger.LogDebug("Deleting partial HLS file {Path}", path);
try
{
@@ -1802,15 +1802,15 @@ namespace Jellyfin.Api.Controllers
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting partial stream file(s) {path}", path);
+ _logger.LogError(ex, "Error deleting partial stream file(s) {Path}", path);
var task = Task.Delay(100);
- Task.WaitAll(task);
+ task.Wait();
DeleteFile(path, retryCount + 1);
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error deleting partial stream file(s) {path}", path);
+ _logger.LogError(ex, "Error deleting partial stream file(s) {Path}", path);
}
}
diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs
index 71caa0fe0..7325dca0a 100644
--- a/Jellyfin.Api/Controllers/HlsSegmentController.cs
+++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs
@@ -64,7 +64,7 @@ namespace Jellyfin.Api.Controllers
var transcodePath = _serverConfigurationManager.GetTranscodePath();
file = Path.GetFullPath(Path.Combine(transcodePath, file));
var fileDir = Path.GetDirectoryName(file);
- if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath))
+ if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath, StringComparison.InvariantCulture))
{
return BadRequest("Invalid segment.");
}
@@ -90,7 +90,7 @@ namespace Jellyfin.Api.Controllers
var transcodePath = _serverConfigurationManager.GetTranscodePath();
file = Path.GetFullPath(Path.Combine(transcodePath, file));
var fileDir = Path.GetDirectoryName(file);
- if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath) || Path.GetExtension(file) != ".m3u8")
+ if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath, StringComparison.InvariantCulture) || Path.GetExtension(file) != ".m3u8")
{
return BadRequest("Invalid segment.");
}
@@ -144,7 +144,7 @@ namespace Jellyfin.Api.Controllers
file = Path.GetFullPath(Path.Combine(transcodeFolderPath, file));
var fileDir = Path.GetDirectoryName(file);
- if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodeFolderPath))
+ if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodeFolderPath, StringComparison.InvariantCulture))
{
return BadRequest("Invalid segment.");
}
diff --git a/Jellyfin.Api/Controllers/ImageByNameController.cs b/Jellyfin.Api/Controllers/ImageByNameController.cs
index 99ab7f232..89bbf22c9 100644
--- a/Jellyfin.Api/Controllers/ImageByNameController.cs
+++ b/Jellyfin.Api/Controllers/ImageByNameController.cs
@@ -82,7 +82,7 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- if (!path.StartsWith(_applicationPaths.GeneralPath))
+ if (!path.StartsWith(_applicationPaths.GeneralPath, StringComparison.InvariantCulture))
{
return BadRequest("Invalid image path.");
}
@@ -177,7 +177,7 @@ namespace Jellyfin.Api.Controllers
if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path))
{
- if (!path.StartsWith(basePath))
+ if (!path.StartsWith(basePath, StringComparison.InvariantCulture))
{
return BadRequest("Invalid image path.");
}
@@ -196,7 +196,7 @@ namespace Jellyfin.Api.Controllers
if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path))
{
- if (!path.StartsWith(basePath))
+ if (!path.StartsWith(basePath, StringComparison.InvariantCulture))
{
return BadRequest("Invalid image path.");
}
diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs
index db8307f28..16acedcf3 100644
--- a/Jellyfin.Api/Controllers/SubtitleController.cs
+++ b/Jellyfin.Api/Controllers/SubtitleController.cs
@@ -528,7 +528,7 @@ namespace Jellyfin.Api.Controllers
if (fontFile != null && fileSize != null && fileSize > 0)
{
- _logger.LogDebug("Fallback font size is {fileSize} Bytes", fileSize);
+ _logger.LogDebug("Fallback font size is {FileSize} Bytes", fileSize);
return PhysicalFile(fontFile.FullName, MimeTypes.GetMimeType(fontFile.FullName));
}
else
diff --git a/Jellyfin.Api/Helpers/ClassMigrationHelper.cs b/Jellyfin.Api/Helpers/ClassMigrationHelper.cs
index a911a3324..76fb27bcc 100644
--- a/Jellyfin.Api/Helpers/ClassMigrationHelper.cs
+++ b/Jellyfin.Api/Helpers/ClassMigrationHelper.cs
@@ -19,7 +19,7 @@ namespace Jellyfin.Api.Helpers
// If any this null throw an exception.
if (source == null || destination == null)
{
- throw new Exception("Source or/and Destination Objects are null");
+ throw new ArgumentException("Source or/and Destination Objects are null");
}
// Getting the Types of the objects.
diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs
index 4fc791665..1b8f24c27 100644
--- a/Jellyfin.Api/Helpers/StreamingHelpers.cs
+++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs
@@ -148,7 +148,7 @@ namespace Jellyfin.Api.Helpers
mediaSource = string.IsNullOrEmpty(streamingRequest.MediaSourceId)
? mediaSources[0]
- : mediaSources.Find(i => string.Equals(i.Id, streamingRequest.MediaSourceId, StringComparison.InvariantCulture));
+ : mediaSources.Find(i => string.Equals(i.Id, streamingRequest.MediaSourceId, StringComparison.Ordinal));
if (mediaSource == null && Guid.Parse(streamingRequest.MediaSourceId) == streamingRequest.Id)
{
diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
index 07d0b5543..f435bbf00 100644
--- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
+++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
@@ -283,6 +283,7 @@ namespace Jellyfin.Api.Helpers
lock (job.ProcessLock!)
{
+ #pragma warning disable CA1849 // Can't await in lock block
job.TranscodingThrottler?.Stop().GetAwaiter().GetResult();
var process = job.Process;
@@ -308,6 +309,7 @@ namespace Jellyfin.Api.Helpers
{
}
}
+ #pragma warning restore CA1849
}
if (delete(job.Path!))
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index 57480b2f3..a3598edfa 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -13,8 +13,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.2.3" />
</ItemGroup>
diff --git a/Jellyfin.Api/Models/ClientLogDtos/ClientLogDocumentResponseDto.cs b/Jellyfin.Api/Models/ClientLogDtos/ClientLogDocumentResponseDto.cs
new file mode 100644
index 000000000..44509a9c0
--- /dev/null
+++ b/Jellyfin.Api/Models/ClientLogDtos/ClientLogDocumentResponseDto.cs
@@ -0,0 +1,22 @@
+namespace Jellyfin.Api.Models.ClientLogDtos
+{
+ /// <summary>
+ /// Client log document response dto.
+ /// </summary>
+ public class ClientLogDocumentResponseDto
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientLogDocumentResponseDto"/> class.
+ /// </summary>
+ /// <param name="fileName">The file name.</param>
+ public ClientLogDocumentResponseDto(string fileName)
+ {
+ FileName = fileName;
+ }
+
+ /// <summary>
+ /// Gets the resulting filename.
+ /// </summary>
+ public string FileName { get; }
+ }
+}
diff --git a/Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs b/Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs
new file mode 100644
index 000000000..9bf9be0a4
--- /dev/null
+++ b/Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs
@@ -0,0 +1,30 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Api.Models.ClientLogDtos
+{
+ /// <summary>
+ /// The client log dto.
+ /// </summary>
+ public class ClientLogEventDto
+ {
+ /// <summary>
+ /// Gets or sets the event timestamp.
+ /// </summary>
+ [Required]
+ public DateTime Timestamp { get; set; }
+
+ /// <summary>
+ /// Gets or sets the log level.
+ /// </summary>
+ [Required]
+ public LogLevel Level { get; set; }
+
+ /// <summary>
+ /// Gets or sets the log message.
+ /// </summary>
+ [Required]
+ public string Message { get; set; } = string.Empty;
+ }
+}
diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj
index 2de53e7c8..58dd945c6 100644
--- a/Jellyfin.Data/Jellyfin.Data.csproj
+++ b/Jellyfin.Data/Jellyfin.Data.csproj
@@ -24,7 +24,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0" PrivateAssets="All" />
</ItemGroup>
<!-- Code analysers-->
@@ -35,7 +35,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
diff --git a/Jellyfin.Server.Implementations/Events/EventManager.cs b/Jellyfin.Server.Implementations/Events/EventManager.cs
index 8c5d8f2ce..7f7c4750d 100644
--- a/Jellyfin.Server.Implementations/Events/EventManager.cs
+++ b/Jellyfin.Server.Implementations/Events/EventManager.cs
@@ -57,7 +57,7 @@ namespace Jellyfin.Server.Implementations.Events
}
catch (Exception e)
{
- _logger.LogError(e, "Uncaught exception in EventConsumer {type}: ", service.GetType());
+ _logger.LogError(e, "Uncaught exception in EventConsumer {Type}: ", service.GetType());
}
}
}
diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
index e26cf093b..73ee69424 100644
--- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
+++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
@@ -19,13 +19,13 @@
<ItemGroup>
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0-rc.2*">
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0-rc.2*">
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 49d979e11..045ed6a2b 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -31,10 +31,10 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
- <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="6.0.0" />
<PackageReference Include="prometheus-net" Version="5.0.1" />
<PackageReference Include="prometheus-net.AspNetCore" Version="5.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
@@ -44,6 +44,7 @@
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Graylog" Version="2.2.2" />
+ <PackageReference Include="Serilog.Sinks.Map" Version="1.0.2" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.7" />
</ItemGroup>
diff --git a/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs b/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs
index 74874da1b..da9b69136 100644
--- a/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs
+++ b/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs
@@ -68,7 +68,7 @@ namespace Jellyfin.Server.Middleware
if (_enableWarning && watch.ElapsedMilliseconds > _warningThreshold)
{
_logger.LogWarning(
- "Slow HTTP Response from {url} to {remoteIp} in {elapsed:g} with Status Code {statusCode}",
+ "Slow HTTP Response from {Url} to {RemoteIp} in {Elapsed:g} with Status Code {StatusCode}",
context.Request.GetDisplayUrl(),
context.GetNormalizedRemoteIp(),
watch.Elapsed,
diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs
index e4d2937e7..2f1d79157 100644
--- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs
+++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs
@@ -51,7 +51,7 @@ namespace Jellyfin.Server.Middleware
return;
}
- if (!key.Contains('='))
+ if (!key.Contains('=', StringComparison.Ordinal))
{
_store = value;
return;
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 5f848be9e..6e4c2280b 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -13,6 +13,7 @@ using Emby.Server.Implementations;
using Jellyfin.Server.Implementations;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.ClientEvent;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Model.IO;
using Microsoft.AspNetCore.Hosting;
@@ -25,6 +26,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Serilog;
using Serilog.Extensions.Logging;
+using Serilog.Filters;
using SQLitePCL;
using ConfigurationExtensions = MediaBrowser.Controller.Extensions.ConfigurationExtensions;
using ILogger = Microsoft.Extensions.Logging.ILogger;
@@ -596,22 +598,46 @@ namespace Jellyfin.Server
{
// Serilog.Log is used by SerilogLoggerFactory when no logger is specified
Log.Logger = new LoggerConfiguration()
- .ReadFrom.Configuration(configuration)
- .Enrich.FromLogContext()
- .Enrich.WithThreadId()
+ .WriteTo.Logger(lc =>
+ lc.ReadFrom.Configuration(configuration)
+ .Enrich.FromLogContext()
+ .Enrich.WithThreadId()
+ .Filter.ByExcluding(Matching.FromSource<ClientEventLogger>()))
+ .WriteTo.Logger(lc =>
+ lc.WriteTo.Map(
+ "ClientName",
+ (clientName, wt)
+ => wt.File(
+ Path.Combine(appPaths.LogDirectoryPath, "log_" + clientName + "_.log"),
+ rollingInterval: RollingInterval.Day,
+ outputTemplate: "{Message:l}{NewLine}{Exception}",
+ encoding: Encoding.UTF8))
+ .Filter.ByIncludingOnly(Matching.FromSource<ClientEventLogger>()))
.CreateLogger();
}
catch (Exception ex)
{
Log.Logger = new LoggerConfiguration()
- .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
- .WriteTo.Async(x => x.File(
- Path.Combine(appPaths.LogDirectoryPath, "log_.log"),
- rollingInterval: RollingInterval.Day,
- outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}",
- encoding: Encoding.UTF8))
- .Enrich.FromLogContext()
- .Enrich.WithThreadId()
+ .WriteTo.Logger(lc =>
+ lc.WriteTo.Async(x => x.File(
+ Path.Combine(appPaths.LogDirectoryPath, "log_.log"),
+ rollingInterval: RollingInterval.Day,
+ outputTemplate: "{Message:l}{NewLine}{Exception}",
+ encoding: Encoding.UTF8))
+ .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
+ .Enrich.FromLogContext()
+ .Enrich.WithThreadId())
+ .WriteTo.Logger(lc =>
+ lc
+ .WriteTo.Map(
+ "ClientName",
+ (clientName, wt)
+ => wt.File(
+ Path.Combine(appPaths.LogDirectoryPath, "log_" + clientName + "_.log"),
+ rollingInterval: RollingInterval.Day,
+ outputTemplate: "{Message:l}{NewLine}{Exception}",
+ encoding: Encoding.UTF8))
+ .Filter.ByIncludingOnly(Matching.FromSource<ClientEventLogger>()))
.CreateLogger();
Log.Logger.Fatal(ex, "Failed to create/read logger configuration");
@@ -649,7 +675,7 @@ namespace Jellyfin.Server
private static string NormalizeCommandLineArgument(string arg)
{
- if (!arg.Contains(" ", StringComparison.OrdinalIgnoreCase))
+ if (!arg.Contains(' ', StringComparison.Ordinal))
{
return arg;
}
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 9c8ce4ac5..441f06f69 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -19,9 +19,9 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs
new file mode 100644
index 000000000..82b5b4593
--- /dev/null
+++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs
@@ -0,0 +1,55 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using MediaBrowser.Model.ClientLog;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.Controller.ClientEvent
+{
+ /// <inheritdoc />
+ public class ClientEventLogger : IClientEventLogger
+ {
+ private const string LogString = "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level}] [{ClientName}:{ClientVersion}]: UserId: {UserId} DeviceId: {DeviceId}{NewLine}{Message}";
+ private readonly ILogger<ClientEventLogger> _logger;
+ private readonly IServerApplicationPaths _applicationPaths;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientEventLogger"/> class.
+ /// </summary>
+ /// <param name="logger">Instance of the <see cref="ILogger{ClientEventLogger}"/> interface.</param>
+ /// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
+ public ClientEventLogger(
+ ILogger<ClientEventLogger> logger,
+ IServerApplicationPaths applicationPaths)
+ {
+ _logger = logger;
+ _applicationPaths = applicationPaths;
+ }
+
+ /// <inheritdoc />
+ public void Log(ClientLogEvent clientLogEvent)
+ {
+ _logger.Log(
+ LogLevel.Critical,
+ LogString,
+ clientLogEvent.Timestamp,
+ clientLogEvent.Level.ToString(),
+ clientLogEvent.ClientName,
+ clientLogEvent.ClientVersion,
+ clientLogEvent.UserId ?? Guid.Empty,
+ clientLogEvent.DeviceId,
+ Environment.NewLine,
+ clientLogEvent.Message);
+ }
+
+ /// <inheritdoc />
+ public async Task<string> WriteDocumentAsync(string clientName, string clientVersion, Stream fileContents)
+ {
+ var fileName = $"upload_{clientName}_{clientVersion}_{DateTime.UtcNow:yyyyMMddHHmmss}_{Guid.NewGuid():N}.log";
+ var logFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, fileName);
+ await using var fileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None);
+ await fileContents.CopyToAsync(fileStream).ConfigureAwait(false);
+ return fileName;
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs
new file mode 100644
index 000000000..34968d493
--- /dev/null
+++ b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs
@@ -0,0 +1,31 @@
+using System.IO;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.ClientLog;
+
+namespace MediaBrowser.Controller.ClientEvent
+{
+ /// <summary>
+ /// The client event logger.
+ /// </summary>
+ public interface IClientEventLogger
+ {
+ /// <summary>
+ /// Logs the event from the client.
+ /// </summary>
+ /// <param name="clientLogEvent">The client log event.</param>
+ void Log(ClientLogEvent clientLogEvent);
+
+ /// <summary>
+ /// Writes a file to the log directory.
+ /// </summary>
+ /// <param name="clientName">The client name writing the document.</param>
+ /// <param name="clientVersion">The client version writing the document.</param>
+ /// <param name="fileContents">The file contents to write.</param>
+ /// <returns>The created file name.</returns>
+ Task<string> WriteDocumentAsync(
+ string clientName,
+ string clientVersion,
+ Stream fileContents);
+ }
+}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 63749b1f3..a76ca2305 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -1452,7 +1452,7 @@ namespace MediaBrowser.Controller.Entities
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error refreshing owned items for {path}", Path ?? Name);
+ Logger.LogError(ex, "Error refreshing owned items for {Path}", Path ?? Name);
}
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 18b4ec3c6..ffd1c7f0a 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -303,7 +303,7 @@ namespace MediaBrowser.Controller.Entities
if (dictionary.ContainsKey(id))
{
Logger.LogError(
- "Found folder containing items with duplicate id. Path: {path}, Child Name: {ChildName}",
+ "Found folder containing items with duplicate id. Path: {Path}, Child Name: {ChildName}",
Path ?? Name,
child.Path ?? child.Name);
}
@@ -1013,6 +1013,7 @@ namespace MediaBrowser.Controller.Entities
items = CollapseBoxSetItemsIfNeeded(items, query, this, user, ConfigurationManager, CollectionManager);
}
+ #pragma warning disable CA1309
if (!string.IsNullOrEmpty(query.NameStartsWithOrGreater))
{
items = items.Where(i => string.Compare(query.NameStartsWithOrGreater, i.SortName, StringComparison.InvariantCultureIgnoreCase) < 1);
@@ -1027,6 +1028,7 @@ namespace MediaBrowser.Controller.Entities
{
items = items.Where(i => string.Compare(query.NameLessThan, i.SortName, StringComparison.InvariantCultureIgnoreCase) == 1);
}
+ #pragma warning restore CA1309
// This must be the last filter
if (!string.IsNullOrEmpty(query.AdjacentTo))
diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs
index b8a0bf331..2429ac42d 100644
--- a/MediaBrowser.Controller/IO/FileData.cs
+++ b/MediaBrowser.Controller/IO/FileData.cs
@@ -69,7 +69,7 @@ namespace MediaBrowser.Controller.IO
if (string.IsNullOrEmpty(newPath))
{
// invalid shortcut - could be old or target could just be unavailable
- logger.LogWarning("Encountered invalid shortcut: " + fullName);
+ logger.LogWarning("Encountered invalid shortcut: {Path}", fullName);
continue;
}
@@ -83,7 +83,7 @@ namespace MediaBrowser.Controller.IO
}
catch (Exception ex)
{
- logger.LogError(ex, "Error resolving shortcut from {path}", fullName);
+ logger.LogError(ex, "Error resolving shortcut from {Path}", fullName);
}
}
else if (flattenFolderDepth > 0 && isDirectory)
diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
index e802796d3..f1758a9d8 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
@@ -33,13 +33,6 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Gets the media streams.
/// </summary>
- /// <param name="mediaSourceId">The media source identifier.</param>
- /// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
- List<MediaStream> GetMediaStreams(string mediaSourceId);
-
- /// <summary>
- /// Gets the media streams.
- /// </summary>
/// <param name="query">The query.</param>
/// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
List<MediaStream> GetMediaStreams(MediaStreamQuery query);
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index d37880865..1996335fe 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -15,10 +15,10 @@
<ItemGroup>
<PackageReference Include="Diacritics" Version="3.3.4" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
- <PackageReference Include="System.Threading.Tasks.Dataflow" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0" PrivateAssets="All" />
+ <PackageReference Include="System.Threading.Tasks.Dataflow" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
index 2ac4c728b..08d129a82 100644
--- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CA1819, CS1591
using System;
diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs
index b8dd416a2..3a97127ea 100644
--- a/MediaBrowser.Controller/Providers/ItemInfo.cs
+++ b/MediaBrowser.Controller/Providers/ItemInfo.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
index 5a36c1663..80eb45423 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
@@ -412,7 +412,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
var actors = reader.ReadInnerXml();
- if (actors.Contains("<", StringComparison.Ordinal))
+ if (actors.Contains('<', StringComparison.Ordinal))
{
// This is one of the mis-named "Actors" full nodes created by MB2
// Create a reader and pass it to the persons node processor
diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
index 5e7af23fc..1a8b5bb4e 100644
--- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
@@ -144,7 +144,7 @@ namespace MediaBrowser.LocalMetadata.Savers
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error setting hidden attribute on {path}", path);
+ Logger.LogError(ex, "Error setting hidden attribute on {Path}", path);
}
}
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index c1fd8e5fb..6bb8bcdab 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -24,8 +24,8 @@
<ItemGroup>
<PackageReference Include="BDInfo" Version="0.7.6.1" />
<PackageReference Include="libse" Version="3.6.2" />
- <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-rc.2*" />
- <PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
+ <PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
<PackageReference Include="UTF.Unknown" Version="2.5.0" />
</ItemGroup>
diff --git a/MediaBrowser.Model/ClientLog/ClientLogEvent.cs b/MediaBrowser.Model/ClientLog/ClientLogEvent.cs
new file mode 100644
index 000000000..21087b564
--- /dev/null
+++ b/MediaBrowser.Model/ClientLog/ClientLogEvent.cs
@@ -0,0 +1,75 @@
+using System;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.Model.ClientLog
+{
+ /// <summary>
+ /// The client log event.
+ /// </summary>
+ public class ClientLogEvent
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientLogEvent"/> class.
+ /// </summary>
+ /// <param name="timestamp">The log timestamp.</param>
+ /// <param name="level">The log level.</param>
+ /// <param name="userId">The user id.</param>
+ /// <param name="clientName">The client name.</param>
+ /// <param name="clientVersion">The client version.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="message">The message.</param>
+ public ClientLogEvent(
+ DateTime timestamp,
+ LogLevel level,
+ Guid? userId,
+ string clientName,
+ string clientVersion,
+ string deviceId,
+ string message)
+ {
+ Timestamp = timestamp;
+ UserId = userId;
+ ClientName = clientName;
+ ClientVersion = clientVersion;
+ DeviceId = deviceId;
+ Message = message;
+ Level = level;
+ }
+
+ /// <summary>
+ /// Gets the event timestamp.
+ /// </summary>
+ public DateTime Timestamp { get; }
+
+ /// <summary>
+ /// Gets the log level.
+ /// </summary>
+ public LogLevel Level { get; }
+
+ /// <summary>
+ /// Gets the user id.
+ /// </summary>
+ public Guid? UserId { get; }
+
+ /// <summary>
+ /// Gets the client name.
+ /// </summary>
+ public string ClientName { get; }
+
+ /// <summary>
+ /// Gets the client version.
+ /// </summary>
+ public string ClientVersion { get; }
+
+ ///
+ /// <summary>
+ /// Gets the device id.
+ /// </summary>
+ public string DeviceId { get; }
+
+ /// <summary>
+ /// Gets the log message.
+ /// </summary>
+ public string Message { get; }
+ }
+}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index d1e999666..b79d18abd 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -459,5 +459,10 @@ namespace MediaBrowser.Model.Configuration
/// Gets or sets a value indicating whether older plugins should automatically be deleted from the plugin folder.
/// </summary>
public bool RemoveOldPlugins { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether clients should be allowed to upload logs.
+ /// </summary>
+ public bool AllowClientLogUpload { get; set; } = true;
}
}
diff --git a/MediaBrowser.Model/Dlna/DlnaMaps.cs b/MediaBrowser.Model/Dlna/DlnaMaps.cs
index 95cd0ac27..4613bc542 100644
--- a/MediaBrowser.Model/Dlna/DlnaMaps.cs
+++ b/MediaBrowser.Model/Dlna/DlnaMaps.cs
@@ -6,20 +6,6 @@ namespace MediaBrowser.Model.Dlna
{
public static class DlnaMaps
{
- private static readonly string DefaultStreaming =
- FlagsToString(DlnaFlags.StreamingTransferMode |
- DlnaFlags.BackgroundTransferMode |
- DlnaFlags.ConnectionStall |
- DlnaFlags.ByteBasedSeek |
- DlnaFlags.DlnaV15);
-
- private static readonly string DefaultInteractive =
- FlagsToString(DlnaFlags.InteractiveTransferMode |
- DlnaFlags.BackgroundTransferMode |
- DlnaFlags.ConnectionStall |
- DlnaFlags.ByteBasedSeek |
- DlnaFlags.DlnaV15);
-
public static string FlagsToString(DlnaFlags flags)
{
return string.Format(CultureInfo.InvariantCulture, "{0:X8}{1:D24}", (ulong)flags, 0);
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 4414415a2..cf8465067 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -794,7 +794,7 @@ namespace MediaBrowser.Model.Dlna
}
// strip spaces to avoid having to encode h264 profile names
- list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", string.Empty)));
+ list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", string.Empty, StringComparison.Ordinal)));
}
if (!item.IsDirectStream)
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 16bc4adf8..85947b3de 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -29,10 +29,10 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
- <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0" PrivateAssets="All" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
<PackageReference Include="System.Globalization" Version="4.3.0" />
- <PackageReference Include="System.Text.Json" Version="6.0.0-rc.2*" />
+ <PackageReference Include="System.Text.Json" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Model/MediaInfo/AudioCodec.cs b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
index 8b17757b8..7b83b1b9d 100644
--- a/MediaBrowser.Model/MediaInfo/AudioCodec.cs
+++ b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
@@ -1,13 +1,11 @@
#pragma warning disable CS1591
+using System;
+
namespace MediaBrowser.Model.MediaInfo
{
public static class AudioCodec
{
- public const string AAC = "aac";
- public const string MP3 = "mp3";
- public const string AC3 = "ac3";
-
public static string GetFriendlyName(string codec)
{
if (codec.Length == 0)
@@ -15,17 +13,20 @@ namespace MediaBrowser.Model.MediaInfo
return codec;
}
- switch (codec.ToLowerInvariant())
+ if (string.Equals(codec, "ac3", StringComparison.OrdinalIgnoreCase))
+ {
+ return "Dolby Digital";
+ }
+ else if (string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
{
- case "ac3":
- return "Dolby Digital";
- case "eac3":
- return "Dolby Digital+";
- case "dca":
- return "DTS";
- default:
- return codec.ToUpperInvariant();
+ return "Dolby Digital+";
}
+ else if (string.Equals(codec, "dca", StringComparison.OrdinalIgnoreCase))
+ {
+ return "DTS";
+ }
+
+ return codec.ToUpperInvariant();
}
}
}
diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
index 36a240706..24eab1a74 100644
--- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
+++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
@@ -16,22 +16,6 @@ namespace MediaBrowser.Model.MediaInfo
DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
}
- public LiveStreamRequest(AudioOptions options)
- {
- MaxStreamingBitrate = options.MaxBitrate;
- ItemId = options.ItemId;
- DeviceProfile = options.Profile;
- MaxAudioChannels = options.MaxAudioChannels;
-
- DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
-
- if (options is VideoOptions videoOptions)
- {
- AudioStreamIndex = videoOptions.AudioStreamIndex;
- SubtitleStreamIndex = videoOptions.SubtitleStreamIndex;
- }
- }
-
public string OpenToken { get; set; }
public Guid UserId { get; set; }
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
deleted file mode 100644
index ecd9b8834..000000000
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Model.MediaInfo
-{
- public class PlaybackInfoRequest
- {
- public PlaybackInfoRequest()
- {
- EnableDirectPlay = true;
- EnableDirectStream = true;
- EnableTranscoding = true;
- AllowVideoStreamCopy = true;
- AllowAudioStreamCopy = true;
- IsPlayback = true;
- DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
- }
-
- public Guid Id { get; set; }
-
- public Guid UserId { get; set; }
-
- public long? MaxStreamingBitrate { get; set; }
-
- public long? StartTimeTicks { get; set; }
-
- public int? AudioStreamIndex { get; set; }
-
- public int? SubtitleStreamIndex { get; set; }
-
- public int? MaxAudioChannels { get; set; }
-
- public string MediaSourceId { get; set; }
-
- public string LiveStreamId { get; set; }
-
- public DeviceProfile DeviceProfile { get; set; }
-
- public bool EnableDirectPlay { get; set; }
-
- public bool EnableDirectStream { get; set; }
-
- public bool EnableTranscoding { get; set; }
-
- public bool AllowVideoStreamCopy { get; set; }
-
- public bool AllowAudioStreamCopy { get; set; }
-
- public bool IsPlayback { get; set; }
-
- public bool AutoOpenLiveStream { get; set; }
-
- public MediaProtocol[] DirectPlayProtocols { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
index 2bd45695a..9bc5c31f6 100644
--- a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
+++ b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
@@ -8,8 +8,6 @@ namespace MediaBrowser.Model.MediaInfo
public const string SSA = "ssa";
public const string ASS = "ass";
public const string VTT = "vtt";
- public const string SUB = "sub";
- public const string SMI = "smi";
public const string TTML = "ttml";
}
}
diff --git a/MediaBrowser.Model/Net/NetworkShareType.cs b/MediaBrowser.Model/Net/NetworkShareType.cs
deleted file mode 100644
index 5d985f85d..000000000
--- a/MediaBrowser.Model/Net/NetworkShareType.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Enum NetworkShareType.
- /// </summary>
- public enum NetworkShareType
- {
- /// <summary>
- /// Disk share.
- /// </summary>
- Disk,
-
- /// <summary>
- /// Printer share.
- /// </summary>
- Printer,
-
- /// <summary>
- /// Device share.
- /// </summary>
- Device,
-
- /// <summary>
- /// IPC share.
- /// </summary>
- Ipc,
-
- /// <summary>
- /// Special share.
- /// </summary>
- Special
- }
-}
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index 1022a3fae..5e1985611 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -215,7 +215,7 @@ namespace MediaBrowser.Providers.Manager
catch (Exception ex)
{
result.ErrorMessage = ex.Message;
- _logger.LogError(ex, "Error in {provider}", provider.Name);
+ _logger.LogError(ex, "Error in {Provider}", provider.Name);
}
}
@@ -331,7 +331,7 @@ namespace MediaBrowser.Providers.Manager
catch (Exception ex)
{
result.ErrorMessage = ex.Message;
- _logger.LogError(ex, "Error in {provider}", provider.Name);
+ _logger.LogError(ex, "Error in {Provider}", provider.Name);
}
}
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index ffb3baeb1..90d14a973 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -713,7 +713,7 @@ namespace MediaBrowser.Providers.Manager
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error in {provider}", provider.Name);
+ Logger.LogError(ex, "Error in {Provider}", provider.Name);
// If a local provider fails, consider that a failure
refreshResult.ErrorMessage = ex.Message;
@@ -785,7 +785,7 @@ namespace MediaBrowser.Providers.Manager
catch (Exception ex)
{
refreshResult.ErrorMessage = ex.Message;
- Logger.LogError(ex, "Error in {provider}", provider.Name);
+ Logger.LogError(ex, "Error in {Provider}", provider.Name);
}
}
@@ -837,7 +837,7 @@ namespace MediaBrowser.Providers.Manager
{
refreshResult.Failures++;
refreshResult.ErrorMessage = ex.Message;
- Logger.LogError(ex, "Error in {provider}", provider.Name);
+ Logger.LogError(ex, "Error in {Provider}", provider.Name);
}
}
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 9d0a6944b..b42112111 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -16,9 +16,9 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="OptimizedPriorityQueue" Version="5.0.0" />
<PackageReference Include="PlaylistsNET" Version="1.1.3" />
diff --git a/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs b/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs
index c41d36d76..9b63971a9 100644
--- a/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs
@@ -125,7 +125,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (attachmentStream != null)
{
- return await ExtractAttachment(item, cancellationToken, attachmentStream, mediaSource);
+ return await ExtractAttachment(item, attachmentStream, mediaSource, cancellationToken);
}
// Fall back to EmbeddedImage streams
@@ -169,7 +169,7 @@ namespace MediaBrowser.Providers.MediaInfo
};
}
- private async Task<DynamicImageResponse> ExtractAttachment(Video item, CancellationToken cancellationToken, MediaAttachment attachmentStream, MediaSourceInfo mediaSource)
+ private async Task<DynamicImageResponse> ExtractAttachment(Video item, MediaAttachment attachmentStream, MediaSourceInfo mediaSource, CancellationToken cancellationToken)
{
var extension = string.IsNullOrEmpty(attachmentStream.MimeType)
? Path.GetExtension(attachmentStream.FileName)
diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs
index d02aea556..935ff5f59 100644
--- a/MediaBrowser.XbmcMetadata/EntryPoint.cs
+++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs
@@ -71,7 +71,7 @@ namespace MediaBrowser.XbmcMetadata
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error saving metadata for {path}", item.Path ?? item.Name);
+ _logger.LogError(ex, "Error saving metadata for {Path}", item.Path ?? item.Name);
}
}
}
diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64
index 78f051e4f..3967a165d 100644
--- a/deployment/Dockerfile.centos.amd64
+++ b/deployment/Dockerfile.centos.amd64
@@ -13,7 +13,7 @@ RUN yum update -yq \
&& yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget
# Install DotNET SDK
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/20283373-1d83-4879-8278-0afb7fd4035e/56f204f174743b29a656499ad0fc93c3/dotnet-sdk-6.0.100-rc.2.21505.57-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/17b6759f-1af0-41bc-ab12-209ba0377779/e8d02195dbf1434b940e0f05ae086453/dotnet-sdk-6.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64
index 14eeb6eed..bc40a8059 100644
--- a/deployment/Dockerfile.fedora.amd64
+++ b/deployment/Dockerfile.fedora.amd64
@@ -12,7 +12,7 @@ RUN dnf update -yq \
&& dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget
# Install DotNET SDK
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/20283373-1d83-4879-8278-0afb7fd4035e/56f204f174743b29a656499ad0fc93c3/dotnet-sdk-6.0.100-rc.2.21505.57-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/17b6759f-1af0-41bc-ab12-209ba0377779/e8d02195dbf1434b940e0f05ae086453/dotnet-sdk-6.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64
index 8733be89c..c1b541c59 100644
--- a/deployment/Dockerfile.ubuntu.amd64
+++ b/deployment/Dockerfile.ubuntu.amd64
@@ -17,7 +17,7 @@ RUN apt-get update -yqq \
libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
# Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/20283373-1d83-4879-8278-0afb7fd4035e/56f204f174743b29a656499ad0fc93c3/dotnet-sdk-6.0.100-rc.2.21505.57-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/17b6759f-1af0-41bc-ab12-209ba0377779/e8d02195dbf1434b940e0f05ae086453/dotnet-sdk-6.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64
index 6ae0d53cc..6aa98a289 100644
--- a/deployment/Dockerfile.ubuntu.arm64
+++ b/deployment/Dockerfile.ubuntu.arm64
@@ -16,7 +16,7 @@ RUN apt-get update -yqq \
mmv build-essential lsb-release
# Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/20283373-1d83-4879-8278-0afb7fd4035e/56f204f174743b29a656499ad0fc93c3/dotnet-sdk-6.0.100-rc.2.21505.57-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/17b6759f-1af0-41bc-ab12-209ba0377779/e8d02195dbf1434b940e0f05ae086453/dotnet-sdk-6.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf
index 154388148..cc9d8dc79 100644
--- a/deployment/Dockerfile.ubuntu.armhf
+++ b/deployment/Dockerfile.ubuntu.armhf
@@ -16,7 +16,7 @@ RUN apt-get update -yqq \
mmv build-essential lsb-release
# Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/20283373-1d83-4879-8278-0afb7fd4035e/56f204f174743b29a656499ad0fc93c3/dotnet-sdk-6.0.100-rc.2.21505.57-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/17b6759f-1af0-41bc-ab12-209ba0377779/e8d02195dbf1434b940e0f05ae086453/dotnet-sdk-6.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index dfb991170..3bced438c 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -42,6 +42,8 @@
<Rule Id="CA1305" Action="Error" />
<!-- error on CA1725: Parameter names should match base declaration -->
<Rule Id="CA1725" Action="Error" />
+ <!-- error on CA1725: Call async methods when in an async method -->
+ <Rule Id="CA1727" Action="Error" />
<!-- error on CA2016: Forward the CancellationToken parameter to methods that take one
or pass in 'CancellationToken.None' explicitly to indicate intentionally not propagating the token -->
<Rule Id="CA2016" Action="Error" />
@@ -77,6 +79,8 @@
<Rule Id="CA1822" Action="Info" />
<!-- disable warning CA2000: Dispose objects before losing scope -->
<Rule Id="CA2000" Action="Info" />
+ <!-- disable warning CA2253: Named placeholders should not be numeric values -->
+ <Rule Id="CA2253" Action="Info" />
<!-- disable warning CA5394: Do not use insecure randomness -->
<Rule Id="CA5394" Action="Info" />
@@ -90,6 +94,8 @@
<Rule Id="CA1303" Action="None" />
<!-- disable warning CA1308: Normalize strings to uppercase -->
<Rule Id="CA1308" Action="None" />
+ <!-- disable warning CA1848: Use the LoggerMessage delegates -->
+ <Rule Id="CA1848" Action="None" />
<!-- disable warning CA2101: Specify marshaling for P/Invoke string arguments -->
<Rule Id="CA2101" Action="None" />
<!-- disable warning CA2234: Pass System.Uri objects instead of strings -->
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index 57ec86316..2aced0669 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -15,8 +15,8 @@
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
diff --git a/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs b/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
index 9f73ed7fc..2ba5c47d7 100644
--- a/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
+++ b/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
@@ -231,12 +231,12 @@ namespace Jellyfin.Providers.Tests.Manager
.ReturnsAsync(imageResponse);
var refreshOptions = forceRefresh
- ? new ImageRefreshOptions(null)
+ ? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
{
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllImages = true
}
- : new ImageRefreshOptions(null);
+ : new ImageRefreshOptions(Mock.Of<IDirectoryService>());
var itemImageProvider = GetItemImageProvider(null, new Mock<IFileSystem>());
var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { dynamicProvider.Object }, refreshOptions, CancellationToken.None);
@@ -286,7 +286,7 @@ namespace Jellyfin.Providers.Tests.Manager
dynamicProvider.Setup(rp => rp.GetImage(item, imageType, It.IsAny<CancellationToken>()))
.ReturnsAsync(imageResponse);
- var refreshOptions = new ImageRefreshOptions(null);
+ var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>());
var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
providerManager.Setup(pm => pm.SaveImage(item, It.IsAny<Stream>(), It.IsAny<string>(), imageType, null, It.IsAny<CancellationToken>()))
@@ -323,12 +323,12 @@ namespace Jellyfin.Providers.Tests.Manager
.Returns(new[] { imageType });
var refreshOptions = forceRefresh
- ? new ImageRefreshOptions(null)
+ ? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
{
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllImages = true
}
- : new ImageRefreshOptions(null);
+ : new ImageRefreshOptions(Mock.Of<IDirectoryService>());
var remoteInfo = new RemoteImageInfo[imageCount];
for (int i = 0; i < imageCount; i++)
@@ -393,12 +393,12 @@ namespace Jellyfin.Providers.Tests.Manager
});
var refreshOptions = fullRefresh
- ? new ImageRefreshOptions(null)
+ ? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
{
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllImages = true
}
- : new ImageRefreshOptions(null);
+ : new ImageRefreshOptions(Mock.Of<IDirectoryService>());
var remoteInfo = new RemoteImageInfo[targetImageCount];
for (int i = 0; i < targetImageCount; i++)
@@ -442,7 +442,7 @@ namespace Jellyfin.Providers.Tests.Manager
remoteProvider.Setup(rp => rp.GetSupportedImages(item))
.Returns(new[] { imageType });
- var refreshOptions = new ImageRefreshOptions(null);
+ var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>());
// populate remote with double the required images to verify count is trimmed to the library option count
var remoteInfoCount = imageCount * 2;
@@ -487,7 +487,7 @@ namespace Jellyfin.Providers.Tests.Manager
remoteProvider.Setup(rp => rp.GetSupportedImages(item))
.Returns(new[] { imageType });
- var refreshOptions = new ImageRefreshOptions(null)
+ var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>())
{
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllImages = true
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs
new file mode 100644
index 000000000..8ed3d8b94
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs
@@ -0,0 +1,32 @@
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Emby.Server.Implementations.IO;
+using Emby.Server.Implementations.Library;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.MediaInfo;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Library
+{
+ public class MediaSourceManagerTests
+ {
+ private readonly MediaSourceManager _mediaSourceManager;
+
+ public MediaSourceManagerTests()
+ {
+ IFixture fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
+ fixture.Inject<IFileSystem>(fixture.Create<ManagedFileSystem>());
+ _mediaSourceManager = fixture.Create<MediaSourceManager>();
+ }
+
+ [Theory]
+ [InlineData(@"C:\mydir\myfile.ext", MediaProtocol.File)]
+ [InlineData("/mydir/myfile.ext", MediaProtocol.File)]
+ [InlineData("file:///mydir/myfile.ext", MediaProtocol.File)]
+ [InlineData("http://example.com/stream.m3u8", MediaProtocol.Http)]
+ [InlineData("https://example.com/stream.m3u8", MediaProtocol.Http)]
+ [InlineData("rtsp://media.example.com:554/twister/audiotrack", MediaProtocol.Rtsp)]
+ public void GetPathProtocol_ValidArg_Correct(string path, MediaProtocol expected)
+ => Assert.Equal(expected, _mediaSourceManager.GetPathProtocol(path));
+ }
+}
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs
index 827365363..3396a94e5 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs
@@ -40,7 +40,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(MediaTypeNames.Text.Html, response.Content.Headers.ContentType?.MediaType);
StreamReader reader = new StreamReader(typeof(TestPlugin).Assembly.GetManifestResourceStream("Jellyfin.Server.Integration.Tests.TestPage.html")!);
- Assert.Equal(await response.Content.ReadAsStringAsync(), reader.ReadToEnd());
+ Assert.Equal(await response.Content.ReadAsStringAsync().ConfigureAwait(false), await reader.ReadToEndAsync().ConfigureAwait(false));
}
[Fact]
diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
index 889220d86..5b884cddf 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
+++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
@@ -9,8 +9,8 @@
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
index 3daa45e56..29d7646a6 100644
--- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
+++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
@@ -10,8 +10,8 @@
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />