aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/codeql-analysis.yml6
-rw-r--r--CONTRIBUTORS.md1
-rw-r--r--Directory.Packages.props20
-rw-r--r--Emby.Dlna/PlayTo/DlnaHttpClient.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketConnection.cs28
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketManager.cs3
-rw-r--r--Emby.Server.Implementations/Images/BaseFolderImageProvider.cs1
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/au.csv6
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/de.csv5
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs5
-rw-r--r--Jellyfin.Api/Controllers/SubtitleController.cs5
-rw-r--r--Jellyfin.Api/Controllers/UserController.cs2
-rw-r--r--Jellyfin.Api/Models/SubtitleDtos/UploadSubtitleDto.cs6
-rw-r--r--Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs18
-rw-r--r--Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs16
-rw-r--r--Jellyfin.Networking/Extensions/NetworkExtensions.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs11
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs12
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs21
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs5
-rw-r--r--Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs7
-rw-r--r--MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs60
-rw-r--r--MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs38
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs4
-rw-r--r--MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs2
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketConnection.cs17
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleResponse.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs51
-rw-r--r--MediaBrowser.Providers/Subtitles/SubtitleManager.cs5
-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--tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj7
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkParseTests.cs6
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs8
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs2
38 files changed, 289 insertions, 104 deletions
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index e1ee15c74..fa32c1c0e 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -27,11 +27,11 @@ jobs:
dotnet-version: '7.0.x'
- name: Initialize CodeQL
- uses: github/codeql-action/init@489225d82a57396c6f426a40e66d461b16b3461d # v2.20.4
+ uses: github/codeql-action/init@0ba4244466797eb048eb91a6cd43d5c03ca8bd05 # v2.21.2
with:
languages: ${{ matrix.language }}
queries: +security-extended
- name: Autobuild
- uses: github/codeql-action/autobuild@489225d82a57396c6f426a40e66d461b16b3461d # v2.20.4
+ uses: github/codeql-action/autobuild@0ba4244466797eb048eb91a6cd43d5c03ca8bd05 # v2.21.2
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@489225d82a57396c6f426a40e66d461b16b3461d # v2.20.4
+ uses: github/codeql-action/analyze@0ba4244466797eb048eb91a6cd43d5c03ca8bd05 # v2.21.2
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index dfb61df0a..d5a87d269 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -166,6 +166,7 @@
- [RealGreenDragon](https://github.com/RealGreenDragon)
- [ipitio](https://github.com/ipitio)
- [TheTyrius](https://github.com/TheTyrius)
+ - [tallbl0nde](https://github.com/tallbl0nde)
# Emby Contributors
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 08a61cc93..de347f3a0 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -23,14 +23,14 @@
<PackageVersion Include="libse" Version="3.6.13" />
<PackageVersion Include="LrcParser" Version="2023.524.0" />
<PackageVersion Include="MetaBrainz.MusicBrainz" Version="5.0.0" />
- <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="7.0.8" />
+ <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="7.0.9" />
<PackageVersion Include="Microsoft.AspNetCore.HttpOverrides" Version="2.2.0" />
- <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.8" />
+ <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.9" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
- <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.8" />
- <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.8" />
- <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.8" />
- <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.8" />
+ <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.9" />
+ <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.9" />
+ <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.9" />
+ <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.9" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
@@ -39,8 +39,8 @@
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
- <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.8" />
- <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.8" />
+ <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.9" />
+ <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.9" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
@@ -54,7 +54,7 @@
<PackageVersion Include="NEbml" Version="0.11.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="PlaylistsNET" Version="1.4.0" />
- <PackageVersion Include="prometheus-net.AspNetCore" Version="8.0.0" />
+ <PackageVersion Include="prometheus-net.AspNetCore" Version="8.0.1" />
<PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.0" />
<PackageVersion Include="prometheus-net" Version="8.0.1" />
<PackageVersion Include="Serilog.AspNetCore" Version="7.0.0" />
@@ -63,7 +63,7 @@
<PackageVersion Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
- <PackageVersion Include="Serilog.Sinks.Graylog" Version="3.0.1" />
+ <PackageVersion Include="Serilog.Sinks.Graylog" Version="3.0.2" />
<PackageVersion Include="SerilogAnalyzer" Version="0.15.0" />
<PackageVersion Include="SharpFuzz" Version="2.1.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.3" />
diff --git a/Emby.Dlna/PlayTo/DlnaHttpClient.cs b/Emby.Dlna/PlayTo/DlnaHttpClient.cs
index 8454c1afd..220aa1a8d 100644
--- a/Emby.Dlna/PlayTo/DlnaHttpClient.cs
+++ b/Emby.Dlna/PlayTo/DlnaHttpClient.cs
@@ -57,6 +57,7 @@ namespace Emby.Dlna.PlayTo
response.EnsureSuccessStatusCode();
await using MemoryStream ms = new MemoryStream();
await response.Content.CopyToAsync(ms, cancellationToken).ConfigureAwait(false);
+ ms.Position = 0;
try
{
return await XDocument.LoadAsync(
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
index fd7653a32..7f620d666 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
@@ -12,6 +12,7 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Net.WebSocketMessages;
using MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
using MediaBrowser.Model.Session;
+using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.HttpServer
@@ -43,14 +44,17 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="socket">The socket.</param>
+ /// <param name="authorizationInfo">The authorization information.</param>
/// <param name="remoteEndPoint">The remote end point.</param>
public WebSocketConnection(
ILogger<WebSocketConnection> logger,
WebSocket socket,
+ AuthorizationInfo authorizationInfo,
IPAddress? remoteEndPoint)
{
_logger = logger;
_socket = socket;
+ AuthorizationInfo = authorizationInfo;
RemoteEndPoint = remoteEndPoint;
_jsonOptions = JsonDefaults.Options;
@@ -60,30 +64,22 @@ namespace Emby.Server.Implementations.HttpServer
/// <inheritdoc />
public event EventHandler<EventArgs>? Closed;
- /// <summary>
- /// Gets the remote end point.
- /// </summary>
+ /// <inheritdoc />
+ public AuthorizationInfo AuthorizationInfo { get; }
+
+ /// <inheritdoc />
public IPAddress? RemoteEndPoint { get; }
- /// <summary>
- /// Gets or sets the receive action.
- /// </summary>
- /// <value>The receive action.</value>
+ /// <inheritdoc />
public Func<WebSocketMessageInfo, Task>? OnReceive { get; set; }
- /// <summary>
- /// Gets the last activity date.
- /// </summary>
- /// <value>The last activity date.</value>
+ /// <inheritdoc />
public DateTime LastActivityDate { get; private set; }
/// <inheritdoc />
public DateTime LastKeepAliveDate { get; set; }
- /// <summary>
- /// Gets the state.
- /// </summary>
- /// <value>The state.</value>
+ /// <inheritdoc />
public WebSocketState State => _socket.State;
/// <inheritdoc />
@@ -101,7 +97,7 @@ namespace Emby.Server.Implementations.HttpServer
}
/// <inheritdoc />
- public async Task ProcessAsync(CancellationToken cancellationToken = default)
+ public async Task ReceiveAsync(CancellationToken cancellationToken = default)
{
var pipe = new Pipe();
var writer = pipe.Writer;
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
index ecfb242f6..52f14b0b1 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
@@ -51,6 +51,7 @@ namespace Emby.Server.Implementations.HttpServer
using var connection = new WebSocketConnection(
_loggerFactory.CreateLogger<WebSocketConnection>(),
webSocket,
+ authorizationInfo,
context.GetNormalizedRemoteIP())
{
OnReceive = ProcessWebSocketMessageReceived
@@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.HttpServer
await Task.WhenAll(tasks).ConfigureAwait(false);
- await connection.ProcessAsync().ConfigureAwait(false);
+ await connection.ReceiveAsync().ConfigureAwait(false);
_logger.LogInformation("WS {IP} closed", context.Connection.RemoteIpAddress);
}
catch (Exception ex) // Otherwise ASP.Net will ignore the exception
diff --git a/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs b/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs
index 84c21931c..539d4a63a 100644
--- a/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs
@@ -31,6 +31,7 @@ namespace Emby.Server.Implementations.Images
return _libraryManager.GetItemList(new InternalItemsQuery
{
Parent = item,
+ Recursive = true,
DtoOptions = new DtoOptions(true),
ImageTypes = new ImageType[] { ImageType.Primary },
OrderBy = new (string, SortOrder)[]
diff --git a/Emby.Server.Implementations/Localization/Ratings/au.csv b/Emby.Server.Implementations/Localization/Ratings/au.csv
index 4ab808ae9..688125917 100644
--- a/Emby.Server.Implementations/Localization/Ratings/au.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/au.csv
@@ -4,10 +4,14 @@ G,0
M,15
MA,15
MA15+,15
+MA 15+,15
PG,16
16+,16
R,18
R18+,18
-X18+,18
+R 18+,18
18+,18
+X18+,1000
+X 18+,1000
X,1000
+RC,1001
diff --git a/Emby.Server.Implementations/Localization/Ratings/de.csv b/Emby.Server.Implementations/Localization/Ratings/de.csv
index d633a5dab..f6181575e 100644
--- a/Emby.Server.Implementations/Localization/Ratings/de.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/de.csv
@@ -1,12 +1,17 @@
Educational,0
Infoprogramm,0
FSK-0,0
+FSK 0,0
0,0
FSK-6,6
+FSK 6,6
6,6
FSK-12,12
+FSK 12,12
12,12
FSK-16,16
+FSK 16,16
16,16
FSK-18,18
+FSK 18,18
18,18
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 5f6dc93fb..03ff96b19 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -24,6 +24,7 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Authentication;
using MediaBrowser.Controller.Events.Session;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
@@ -1462,7 +1463,7 @@ namespace Emby.Server.Implementations.Session
if (user is null)
{
- await _eventManager.PublishAsync(new GenericEventArgs<AuthenticationRequest>(request)).ConfigureAwait(false);
+ await _eventManager.PublishAsync(new AuthenticationRequestEventArgs(request)).ConfigureAwait(false);
throw new AuthenticationException("Invalid username or password entered.");
}
@@ -1498,7 +1499,7 @@ namespace Emby.Server.Implementations.Session
ServerId = _appHost.SystemId
};
- await _eventManager.PublishAsync(new GenericEventArgs<AuthenticationResult>(returnResult)).ConfigureAwait(false);
+ await _eventManager.PublishAsync(new AuthenticationResultEventArgs(returnResult)).ConfigureAwait(false);
return returnResult;
}
diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs
index b3e9d6297..7d02550b6 100644
--- a/Jellyfin.Api/Controllers/SubtitleController.cs
+++ b/Jellyfin.Api/Controllers/SubtitleController.cs
@@ -90,7 +90,7 @@ public class SubtitleController : BaseJellyfinApiController
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult<Task> DeleteSubtitle(
+ public async Task<ActionResult> DeleteSubtitle(
[FromRoute, Required] Guid itemId,
[FromRoute, Required] int index)
{
@@ -101,7 +101,7 @@ public class SubtitleController : BaseJellyfinApiController
return NotFound();
}
- _subtitleManager.DeleteSubtitles(item, index);
+ await _subtitleManager.DeleteSubtitles(item, index).ConfigureAwait(false);
return NoContent();
}
@@ -416,6 +416,7 @@ public class SubtitleController : BaseJellyfinApiController
Format = body.Format,
Language = body.Language,
IsForced = body.IsForced,
+ IsHearingImpaired = body.IsHearingImpaired,
Stream = memoryStream
}).ConfigureAwait(false);
_providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.High);
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index 4f61af35e..1be40111d 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -494,7 +494,7 @@ public class UserController : BaseJellyfinApiController
var isLocal = HttpContext.IsLocal()
|| _networkManager.IsInLocalNetwork(ip);
- if (isLocal)
+ if (!isLocal)
{
_logger.LogWarning("Password reset process initiated from outside the local network with IP: {IP}", ip);
}
diff --git a/Jellyfin.Api/Models/SubtitleDtos/UploadSubtitleDto.cs b/Jellyfin.Api/Models/SubtitleDtos/UploadSubtitleDto.cs
index 3c903ea6b..2c45e704b 100644
--- a/Jellyfin.Api/Models/SubtitleDtos/UploadSubtitleDto.cs
+++ b/Jellyfin.Api/Models/SubtitleDtos/UploadSubtitleDto.cs
@@ -26,6 +26,12 @@ public class UploadSubtitleDto
public bool IsForced { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether the subtitle is for hearing impaired.
+ /// </summary>
+ [Required]
+ public bool IsHearingImpaired { get; set; }
+
+ /// <summary>
/// Gets or sets the subtitle data.
/// </summary>
[Required]
diff --git a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
index 4a5e0ecd4..5b90d65d8 100644
--- a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
+++ b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
@@ -1,6 +1,8 @@
using System;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
+using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Session;
@@ -9,7 +11,7 @@ using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.WebSocketListeners;
/// <summary>
-/// Class SessionInfoWebSocketListener.
+/// Class ActivityLogWebSocketListener.
/// </summary>
public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<ActivityLogEntry[], WebSocketListenerState>
{
@@ -56,6 +58,20 @@ public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<Activi
base.Dispose(dispose);
}
+ /// <summary>
+ /// Starts sending messages over an activity log web socket.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ protected override void Start(WebSocketMessageInfo message)
+ {
+ if (!message.Connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator))
+ {
+ throw new AuthenticationException("Only admin users can retrieve the activity log.");
+ }
+
+ base.Start(message);
+ }
+
private async void OnEntryCreated(object? sender, GenericEventArgs<ActivityLogEntry> e)
{
await SendData(true).ConfigureAwait(false);
diff --git a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs
index 0d8bf205c..b403ff46d 100644
--- a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs
+++ b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
@@ -66,6 +68,20 @@ public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnume
base.Dispose(dispose);
}
+ /// <summary>
+ /// Starts sending messages over a session info web socket.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ protected override void Start(WebSocketMessageInfo message)
+ {
+ if (!message.Connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator))
+ {
+ throw new AuthenticationException("Only admin users can subscribe to session information.");
+ }
+
+ base.Start(message);
+ }
+
private async void OnSessionManagerSessionActivity(object? sender, SessionEventArgs e)
{
await SendData(false).ConfigureAwait(false);
diff --git a/Jellyfin.Networking/Extensions/NetworkExtensions.cs b/Jellyfin.Networking/Extensions/NetworkExtensions.cs
index d55f78135..e45fa3bcb 100644
--- a/Jellyfin.Networking/Extensions/NetworkExtensions.cs
+++ b/Jellyfin.Networking/Extensions/NetworkExtensions.cs
@@ -104,7 +104,7 @@ public static partial class NetworkExtensions
Span<byte> bytes = stackalloc byte[mask.AddressFamily == AddressFamily.InterNetwork ? Network.IPv4MaskBytes : Network.IPv6MaskBytes];
if (!mask.TryWriteBytes(bytes, out var bytesWritten))
{
- Console.WriteLine("Unable to write address bytes, only {bytesWritten} bytes written.");
+ Console.WriteLine("Unable to write address bytes, only ${bytesWritten} bytes written.");
}
var zeroed = false;
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs
index f899b4497..b5f18d983 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs
@@ -2,9 +2,8 @@
using System.Globalization;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
-using Jellyfin.Data.Events;
using MediaBrowser.Controller.Events;
-using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.Events.Authentication;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Globalization;
using Microsoft.Extensions.Logging;
@@ -14,7 +13,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security
/// <summary>
/// Creates an entry in the activity log when there is a failed login attempt.
/// </summary>
- public class AuthenticationFailedLogger : IEventConsumer<GenericEventArgs<AuthenticationRequest>>
+ public class AuthenticationFailedLogger : IEventConsumer<AuthenticationRequestEventArgs>
{
private readonly ILocalizationManager _localizationManager;
private readonly IActivityManager _activityManager;
@@ -31,13 +30,13 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security
}
/// <inheritdoc />
- public async Task OnEvent(GenericEventArgs<AuthenticationRequest> eventArgs)
+ public async Task OnEvent(AuthenticationRequestEventArgs eventArgs)
{
await _activityManager.CreateAsync(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localizationManager.GetLocalizedString("FailedLoginAttemptWithUserName"),
- eventArgs.Argument.Username),
+ eventArgs.Username),
"AuthenticationFailed",
Guid.Empty)
{
@@ -45,7 +44,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localizationManager.GetLocalizedString("LabelIpAddressValue"),
- eventArgs.Argument.RemoteEndPoint),
+ eventArgs.RemoteEndPoint),
}).ConfigureAwait(false);
}
}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs
index 8b0bd84c6..2ee5b4e88 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs
@@ -2,8 +2,8 @@
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Events;
-using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Authentication;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Globalization;
@@ -12,7 +12,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security
/// <summary>
/// Creates an entry in the activity log when there is a successful login attempt.
/// </summary>
- public class AuthenticationSucceededLogger : IEventConsumer<GenericEventArgs<AuthenticationResult>>
+ public class AuthenticationSucceededLogger : IEventConsumer<AuthenticationResultEventArgs>
{
private readonly ILocalizationManager _localizationManager;
private readonly IActivityManager _activityManager;
@@ -29,20 +29,20 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security
}
/// <inheritdoc />
- public async Task OnEvent(GenericEventArgs<AuthenticationResult> eventArgs)
+ public async Task OnEvent(AuthenticationResultEventArgs eventArgs)
{
await _activityManager.CreateAsync(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localizationManager.GetLocalizedString("AuthenticationSucceededWithUserName"),
- eventArgs.Argument.User.Name),
+ eventArgs.User.Name),
"AuthenticationSucceeded",
- eventArgs.Argument.User.Id)
+ eventArgs.User.Id)
{
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localizationManager.GetLocalizedString("LabelIpAddressValue"),
- eventArgs.Argument.SessionInfo.RemoteEndPoint),
+ eventArgs.SessionInfo?.RemoteEndPoint),
}).ConfigureAwait(false);
}
}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
index aeb62e814..27726a57a 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
@@ -58,15 +58,18 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session
var user = eventArgs.Users[0];
await _activityManager.CreateAsync(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localizationManager.GetLocalizedString("UserStartedPlayingItemWithValues"),
- user.Username,
- GetItemName(eventArgs.MediaInfo),
- eventArgs.DeviceName),
- GetPlaybackNotificationType(eventArgs.MediaInfo.MediaType),
- user.Id))
- .ConfigureAwait(false);
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserStartedPlayingItemWithValues"),
+ user.Username,
+ GetItemName(eventArgs.MediaInfo),
+ eventArgs.DeviceName),
+ GetPlaybackNotificationType(eventArgs.MediaInfo.MediaType),
+ user.Id)
+ {
+ ItemId = eventArgs.Item?.Id.ToString("N", CultureInfo.InvariantCulture),
+ })
+ .ConfigureAwait(false);
}
private static string GetItemName(BaseItemDto item)
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
index dd7290fb8..6b16477aa 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
@@ -73,7 +73,10 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session
GetItemName(item),
eventArgs.DeviceName),
notificationType,
- user.Id))
+ user.Id)
+ {
+ ItemId = eventArgs.Item?.Id.ToString("N", CultureInfo.InvariantCulture),
+ })
.ConfigureAwait(false);
}
diff --git a/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs
index 5d558189b..9a473de52 100644
--- a/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs
+++ b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs
@@ -8,12 +8,11 @@ using Jellyfin.Server.Implementations.Events.Consumers.System;
using Jellyfin.Server.Implementations.Events.Consumers.Updates;
using Jellyfin.Server.Implementations.Events.Consumers.Users;
using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Authentication;
using MediaBrowser.Controller.Events.Session;
using MediaBrowser.Controller.Events.Updates;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.DependencyInjection;
@@ -35,8 +34,8 @@ namespace Jellyfin.Server.Implementations.Events
collection.AddScoped<IEventConsumer<SubtitleDownloadFailureEventArgs>, SubtitleDownloadFailureLogger>();
// Security consumers
- collection.AddScoped<IEventConsumer<GenericEventArgs<AuthenticationRequest>>, AuthenticationFailedLogger>();
- collection.AddScoped<IEventConsumer<GenericEventArgs<AuthenticationResult>>, AuthenticationSucceededLogger>();
+ collection.AddScoped<IEventConsumer<AuthenticationRequestEventArgs>, AuthenticationFailedLogger>();
+ collection.AddScoped<IEventConsumer<AuthenticationResultEventArgs>, AuthenticationSucceededLogger>();
// Session consumers
collection.AddScoped<IEventConsumer<PlaybackStartEventArgs>, PlaybackStartLogger>();
diff --git a/MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs b/MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs
new file mode 100644
index 000000000..2143c6998
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs
@@ -0,0 +1,60 @@
+using System;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.Events.Authentication;
+
+/// <summary>
+/// A class representing an authentication result event.
+/// </summary>
+public class AuthenticationRequestEventArgs : EventArgs
+{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthenticationRequestEventArgs"/> class.
+ /// </summary>
+ /// <param name="request">The <see cref="AuthenticationRequest"/>.</param>
+ public AuthenticationRequestEventArgs(AuthenticationRequest request)
+ {
+ Username = request.Username;
+ UserId = request.UserId;
+ App = request.App;
+ AppVersion = request.AppVersion;
+ DeviceId = request.DeviceId;
+ DeviceName = request.DeviceName;
+ RemoteEndPoint = request.RemoteEndPoint;
+ }
+
+ /// <summary>
+ /// Gets or sets the user name.
+ /// </summary>
+ public string? Username { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ public Guid? UserId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the app.
+ /// </summary>
+ public string? App { get; set; }
+
+ /// <summary>
+ /// Gets or sets the app version.
+ /// </summary>
+ public string? AppVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the device id.
+ /// </summary>
+ public string? DeviceId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the device name.
+ /// </summary>
+ public string? DeviceName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the remote endpoint.
+ /// </summary>
+ public string? RemoteEndPoint { get; set; }
+}
diff --git a/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs b/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs
new file mode 100644
index 000000000..357ef9406
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs
@@ -0,0 +1,38 @@
+using System;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Dto;
+
+namespace MediaBrowser.Controller.Events.Authentication;
+
+/// <summary>
+/// A class representing an authentication result event.
+/// </summary>
+public class AuthenticationResultEventArgs : EventArgs
+{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthenticationResultEventArgs"/> class.
+ /// </summary>
+ /// <param name="result">The <see cref="AuthenticationResult"/>.</param>
+ public AuthenticationResultEventArgs(AuthenticationResult result)
+ {
+ User = result.User;
+ SessionInfo = result.SessionInfo;
+ ServerId = result.ServerId;
+ }
+
+ /// <summary>
+ /// Gets or sets the user.
+ /// </summary>
+ public UserDto User { get; set; }
+
+ /// <summary>
+ /// Gets or sets the session information.
+ /// </summary>
+ public SessionInfo? SessionInfo { get; set; }
+
+ /// <summary>
+ /// Gets or sets the server id.
+ /// </summary>
+ public string? ServerId { get; set; }
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 750713694..a702e1003 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -1457,8 +1457,8 @@ namespace MediaBrowser.Controller.MediaEncoding
args += keyFrameArg + gopArg;
}
- // global_header produced by AMD VA-API encoder causes non-playable fMP4 on iOS
- if (codec.Contains("vaapi", StringComparison.OrdinalIgnoreCase)
+ // global_header produced by AMD HEVC VA-API encoder causes non-playable fMP4 on iOS
+ if (string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)
&& _mediaEncoder.IsVaapiDeviceAmd)
{
args += " -flags:v -global_header";
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
index 8f38d4976..e0942e490 100644
--- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
+++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
@@ -96,7 +96,7 @@ namespace MediaBrowser.Controller.Net
/// Starts sending messages over a web socket.
/// </summary>
/// <param name="message">The message.</param>
- private void Start(WebSocketMessageInfo message)
+ protected virtual void Start(WebSocketMessageInfo message)
{
var vals = message.Data.Split(',');
diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
index 79f0846b4..bba5a6b85 100644
--- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs
+++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Net;
using System.Net.WebSockets;
@@ -9,6 +7,9 @@ using MediaBrowser.Controller.Net.WebSocketMessages;
namespace MediaBrowser.Controller.Net
{
+ /// <summary>
+ /// Interface for WebSocket connections.
+ /// </summary>
public interface IWebSocketConnection : IAsyncDisposable, IDisposable
{
/// <summary>
@@ -41,6 +42,11 @@ namespace MediaBrowser.Controller.Net
WebSocketState State { get; }
/// <summary>
+ /// Gets the authorization information.
+ /// </summary>
+ public AuthorizationInfo AuthorizationInfo { get; }
+
+ /// <summary>
/// Gets the remote end point.
/// </summary>
/// <value>The remote end point.</value>
@@ -65,6 +71,11 @@ namespace MediaBrowser.Controller.Net
/// <exception cref="ArgumentNullException">The message is null.</exception>
Task SendAsync<T>(OutboundWebSocketMessage<T> message, CancellationToken cancellationToken);
- Task ProcessAsync(CancellationToken cancellationToken = default);
+ /// <summary>
+ /// Receives a message asynchronously.
+ /// </summary>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task ReceiveAsync(CancellationToken cancellationToken = default);
}
}
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs
index 85b3e6fbd..51c29c7a2 100644
--- a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs
+++ b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs
@@ -14,6 +14,8 @@ namespace MediaBrowser.Controller.Subtitles
public bool IsForced { get; set; }
+ public bool IsHearingImpaired { get; set; }
+
public Stream Stream { get; set; }
}
}
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 7d655240b..aeb08cea3 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -1,5 +1,4 @@
#nullable disable
-#pragma warning disable CS1591
using System;
using System.Collections.Generic;
@@ -20,6 +19,9 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.MediaEncoding.Probing
{
+ /// <summary>
+ /// Class responsible for normalizing FFprobe output.
+ /// </summary>
public class ProbeResultNormalizer
{
// When extracting subtitles, the maximum length to consider (to avoid invalid filenames)
@@ -36,6 +38,11 @@ namespace MediaBrowser.MediaEncoding.Probing
private string[] _splitWhiteList;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProbeResultNormalizer"/> class.
+ /// </summary>
+ /// <param name="logger">The <see cref="ILogger{ProbeResultNormalizer}"/> for use with the <see cref="ProbeResultNormalizer"/> instance.</param>
+ /// <param name="localization">The <see cref="ILocalizationManager"/> for use with the <see cref="ProbeResultNormalizer"/> instance.</param>
public ProbeResultNormalizer(ILogger logger, ILocalizationManager localization)
{
_logger = logger;
@@ -73,6 +80,15 @@ namespace MediaBrowser.MediaEncoding.Probing
"Smith/Kotzen",
};
+ /// <summary>
+ /// Transforms a FFprobe response into its <see cref="MediaInfo"/> equivalent.
+ /// </summary>
+ /// <param name="data">The <see cref="InternalMediaInfoResult"/>.</param>
+ /// <param name="videoType">The <see cref="VideoType"/>.</param>
+ /// <param name="isAudio">A boolean indicating whether the media is audio.</param>
+ /// <param name="path">Path to media file.</param>
+ /// <param name="protocol">Path media protocol.</param>
+ /// <returns>The <see cref="MediaInfo"/>.</returns>
public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType? videoType, bool isAudio, string path, MediaProtocol protocol)
{
var info = new MediaInfo
@@ -252,25 +268,30 @@ namespace MediaBrowser.MediaEncoding.Probing
return null;
}
- // Handle MPEG-1 container
- if (string.Equals(format, "mpegvideo", StringComparison.OrdinalIgnoreCase))
+ // Input can be a list of multiple, comma-delimited formats - each of them needs to be checked
+ var splitFormat = format.Split(',');
+ for (var i = 0; i < splitFormat.Length; i++)
{
- return "mpeg";
- }
+ // Handle MPEG-1 container
+ if (string.Equals(splitFormat[i], "mpegvideo", StringComparison.OrdinalIgnoreCase))
+ {
+ splitFormat[i] = "mpeg";
+ }
- // Handle MPEG-2 container
- if (string.Equals(format, "mpeg", StringComparison.OrdinalIgnoreCase))
- {
- return "ts";
- }
+ // Handle MPEG-2 container
+ else if (string.Equals(splitFormat[i], "mpeg", StringComparison.OrdinalIgnoreCase))
+ {
+ splitFormat[i] = "ts";
+ }
- // Handle matroska container
- if (string.Equals(format, "matroska", StringComparison.OrdinalIgnoreCase))
- {
- return "mkv";
+ // Handle matroska container
+ else if (string.Equals(splitFormat[i], "matroska", StringComparison.OrdinalIgnoreCase))
+ {
+ splitFormat[i] = "mkv";
+ }
}
- return format;
+ return string.Join(',', splitFormat);
}
private int? GetEstimatedAudioBitrate(string codec, int? channels)
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
index 0c01c5031..87fd2a3cd 100644
--- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -200,6 +200,11 @@ namespace MediaBrowser.Providers.Subtitles
saveFileName += ".forced";
}
+ if (response.IsHearingImpaired)
+ {
+ saveFileName += ".sdh";
+ }
+
saveFileName += "." + response.Format.ToLowerInvariant();
if (saveInMediaFolder)
diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64
index 771675519..da986a07e 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/87a55ae3-917d-449e-a4e8-776f82976e91/03380e598c326c2f9465d262c6a88c45/dotnet-sdk-7.0.305-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/0be7a87e-3a3f-4500-8301-49ccd6f24887/e9e36f35dbaf6625fec3e18f5c2b613f/dotnet-sdk-7.0.306-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 c552f06b0..09f93d41b 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 make
# Install DotNET SDK
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/87a55ae3-917d-449e-a4e8-776f82976e91/03380e598c326c2f9465d262c6a88c45/dotnet-sdk-7.0.305-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/0be7a87e-3a3f-4500-8301-49ccd6f24887/e9e36f35dbaf6625fec3e18f5c2b613f/dotnet-sdk-7.0.306-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 30100d20d..9910773da 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/87a55ae3-917d-449e-a4e8-776f82976e91/03380e598c326c2f9465d262c6a88c45/dotnet-sdk-7.0.305-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/0be7a87e-3a3f-4500-8301-49ccd6f24887/e9e36f35dbaf6625fec3e18f5c2b613f/dotnet-sdk-7.0.306-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 bac2adfaf..aa69b27f4 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/87a55ae3-917d-449e-a4e8-776f82976e91/03380e598c326c2f9465d262c6a88c45/dotnet-sdk-7.0.305-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/0be7a87e-3a3f-4500-8301-49ccd6f24887/e9e36f35dbaf6625fec3e18f5c2b613f/dotnet-sdk-7.0.306-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 37a1ed5ff..bb8597b41 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/87a55ae3-917d-449e-a4e8-776f82976e91/03380e598c326c2f9465d262c6a88c45/dotnet-sdk-7.0.305-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget -q https://download.visualstudio.microsoft.com/download/pr/0be7a87e-3a3f-4500-8301-49ccd6f24887/e9e36f35dbaf6625fec3e18f5c2b613f/dotnet-sdk-7.0.306-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/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
index 4b4bdd2a5..3747db3bb 100644
--- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
+++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
@@ -18,12 +18,7 @@
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="../../Emby.Server.Implementations/Emby.Server.Implementations.csproj" />
- <ProjectReference Include="../../MediaBrowser.Common/MediaBrowser.Common.csproj" />
+ <ProjectReference Include="../../Jellyfin.Networking/Jellyfin.Networking.csproj" />
</ItemGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
- <DefineConstants>DEBUG</DefineConstants>
- </PropertyGroup>
-
</Project>
diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
index cb8092ae9..731cbbafb 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
+++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
@@ -97,7 +97,7 @@ namespace Jellyfin.Networking.Tests
/// Checks if IPv4 address is within a defined subnet.
/// </summary>
/// <param name="netMask">Network mask.</param>
- /// <param name="IPAddress">IP Address.</param>
+ /// <param name="ipAddress">IP Address.</param>
[Theory]
[InlineData("192.168.5.85/24", "192.168.5.1")]
[InlineData("192.168.5.85/24", "192.168.5.254")]
@@ -211,7 +211,7 @@ namespace Jellyfin.Networking.Tests
if (nm.TryParseInterface(result, out var resultObj))
{
- result = resultObj.First().Address.ToString();
+ result = resultObj[0].Address.ToString();
var intf = nm.GetBindAddress(source, out _);
Assert.Equal(intf, result);
@@ -270,7 +270,7 @@ namespace Jellyfin.Networking.Tests
if (nm.TryParseInterface(result, out IReadOnlyList<IPData>? resultObj) && resultObj is not null)
{
// Parse out IPAddresses so we can do a string comparison (ignore subnet masks).
- result = resultObj.First().Address.ToString();
+ result = resultObj[0].Address.ToString();
}
var intf = nm.GetBindAddress(source, out int? _);
diff --git a/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs b/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs
index f01611819..22667ee82 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs
@@ -13,7 +13,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer
[Fact]
public void DeserializeWebSocketMessage_SingleSegment_Success()
{
- var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!);
+ var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!, null!);
var bytes = File.ReadAllBytes("Test Data/HttpServer/ForceKeepAlive.json");
con.DeserializeWebSocketMessage(new ReadOnlySequence<byte>(bytes), out var bytesConsumed);
Assert.Equal(109, bytesConsumed);
@@ -23,7 +23,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer
public void DeserializeWebSocketMessage_MultipleSegments_Success()
{
const int SplitPos = 64;
- var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!);
+ var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!, null!);
var bytes = File.ReadAllBytes("Test Data/HttpServer/ForceKeepAlive.json");
var seg1 = new BufferSegment(new Memory<byte>(bytes, 0, SplitPos));
var seg2 = seg1.Append(new Memory<byte>(bytes, SplitPos, bytes.Length - SplitPos));
@@ -34,7 +34,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer
[Fact]
public void DeserializeWebSocketMessage_ValidPartial_Success()
{
- var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!);
+ var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!, null!);
var bytes = File.ReadAllBytes("Test Data/HttpServer/ValidPartial.json");
con.DeserializeWebSocketMessage(new ReadOnlySequence<byte>(bytes), out var bytesConsumed);
Assert.Equal(109, bytesConsumed);
@@ -43,7 +43,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer
[Fact]
public void DeserializeWebSocketMessage_Partial_ThrowJsonException()
{
- var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!);
+ var con = new WebSocketConnection(new NullLogger<WebSocketConnection>(), null!, null!, null!);
var bytes = File.ReadAllBytes("Test Data/HttpServer/Partial.json");
Assert.Throws<JsonException>(() => con.DeserializeWebSocketMessage(new ReadOnlySequence<byte>(bytes), out var bytesConsumed));
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
index 7fabe9904..09e4709da 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
@@ -100,7 +100,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
await localizationManager.LoadAll();
var ratings = localizationManager.GetParentalRatings().ToList();
- Assert.Equal(19, ratings.Count);
+ Assert.Equal(24, ratings.Count);
var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal));
Assert.NotNull(fsk);