diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index c84df5d64..c68f45ee7 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -2,7 +2,7 @@
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
{
"name": "Sonarr",
- "image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0",
+ "image": "mcr.microsoft.com/devcontainers/dotnet:1-10.0",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"nodeGypDependencies": true,
diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml
index 6eb368e80..eaa17c36e 100644
--- a/.github/actions/build/action.yml
+++ b/.github/actions/build/action.yml
@@ -23,6 +23,12 @@ runs:
- name: Setup .NET
uses: actions/setup-dotnet@v5
+ - name: Setup NuGet registry source
+ shell: bash
+ if: ${{ startsWith(inputs.runtime, 'freebsd') }}
+ run:
+ dotnet nuget add source --configfile src/NuGet.Config --name gh-openur https://nuget.pkg.github.com/openur/index.json --username ${{ github.repository_owner }} --password ${{ github.token }} --store-password-in-clear-text
+
- name: Setup Environment Variables
id: variables
shell: bash
@@ -86,7 +92,7 @@ runs:
echo "Building Sonarr for $runtime, Platform: $platform"
- dotnet msbuild -restore $slnFile -p:SelfContained=True -p:Configuration=Release -p:Platform=$platform -p:RuntimeIdentifiers=$runtime -p:EnableWindowsTargeting=true -t:PublishAllRids
+ dotnet msbuild -restore $slnFile -p:SelfContained=true -p:Configuration=Release -p:Platform=$platform -p:RuntimeIdentifiers=$runtime -p:EnableWindowsTargeting=true -t:PublishAllRids
- name: Package
shell: bash
diff --git a/.github/actions/package/package.sh b/.github/actions/package/package.sh
index 4c3c8a8ba..ffbf7cd30 100755
--- a/.github/actions/package/package.sh
+++ b/.github/actions/package/package.sh
@@ -3,7 +3,7 @@
outputFolder=_output
artifactsFolder=_artifacts
uiFolder="$outputFolder/UI"
-framework="${FRAMEWORK:=net8.0}"
+framework="${FRAMEWORK:=net10.0}"
rm -rf $artifactsFolder
mkdir $artifactsFolder
diff --git a/.github/workflows/build_v5.yml b/.github/workflows/build_v5.yml
index 30844f4c7..fb0f90f73 100644
--- a/.github/workflows/build_v5.yml
+++ b/.github/workflows/build_v5.yml
@@ -19,7 +19,7 @@ concurrency:
cancel-in-progress: true
env:
- FRAMEWORK: net8.0
+ FRAMEWORK: net10.0
RAW_BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
SONARR_MAJOR_VERSION: 5
VERSION: 5.0.0
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 4e4c3440d..adcc1ee11 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build dotnet",
// If you have changed target frameworks, make sure to update the program path.
- "program": "${workspaceFolder}/_output/net8.0/Sonarr",
+ "program": "${workspaceFolder}/_output/net10.0/Sonarr",
"args": [],
"cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
diff --git a/global.json b/global.json
index 13d185e52..936a420a5 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
{
"sdk": {
- "version": "8.0.405"
+ "version": "10.0.101"
}
}
diff --git a/package.json b/package.json
index 3755bcfc4..5e6d2f157 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
"@fortawesome/free-solid-svg-icons": "6.7.2",
"@fortawesome/react-fontawesome": "0.2.2",
"@juggle/resize-observer": "3.4.0",
- "@microsoft/signalr": "8.0.7",
+ "@microsoft/signalr": "10.0.0",
"@sentry/browser": "7.119.1",
"@tanstack/react-query": "5.61.0",
"@types/node": "20.16.11",
diff --git a/scripts/docs.sh b/scripts/docs.sh
index db2bfe9eb..fdc8309b4 100755
--- a/scripts/docs.sh
+++ b/scripts/docs.sh
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
-FRAMEWORK="net8.0"
+FRAMEWORK="net10.0"
PLATFORM=$1
ARCHITECTURE="${2:-x64}"
@@ -38,7 +38,7 @@ dotnet clean $slnFile -c Release
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
dotnet new tool-manifest
-dotnet tool install --version 8.0.0 Swashbuckle.AspNetCore.Cli
+dotnet tool install --version 10.0.1 Swashbuckle.AspNetCore.Cli
# Remove the openapi.json file so we can check if it was created
rm $outputFile
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 4754cda5b..5109e2557 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -123,7 +123,7 @@
-
+
diff --git a/src/NuGet.Config b/src/NuGet.Config
index e07e77531..940e4c6cf 100644
--- a/src/NuGet.Config
+++ b/src/NuGet.Config
@@ -3,7 +3,6 @@
-
diff --git a/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj b/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj
index fa8b5eb40..f2457a4e8 100644
--- a/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj
+++ b/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
diff --git a/src/NzbDrone.Automation.Test/Sonarr.Automation.Test.csproj b/src/NzbDrone.Automation.Test/Sonarr.Automation.Test.csproj
index 9281846c1..8b028efe4 100644
--- a/src/NzbDrone.Automation.Test/Sonarr.Automation.Test.csproj
+++ b/src/NzbDrone.Automation.Test/Sonarr.Automation.Test.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
diff --git a/src/NzbDrone.Common.Test/Sonarr.Common.Test.csproj b/src/NzbDrone.Common.Test/Sonarr.Common.Test.csproj
index 025c769c0..85f910b8e 100644
--- a/src/NzbDrone.Common.Test/Sonarr.Common.Test.csproj
+++ b/src/NzbDrone.Common.Test/Sonarr.Common.Test.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
diff --git a/src/NzbDrone.Common/Http/HttpClient.cs b/src/NzbDrone.Common/Http/HttpClient.cs
index f042a5fd7..dee92460b 100644
--- a/src/NzbDrone.Common/Http/HttpClient.cs
+++ b/src/NzbDrone.Common/Http/HttpClient.cs
@@ -59,7 +59,6 @@ namespace NzbDrone.Common.Http
_httpDispatcher = httpDispatcher;
_logger = logger;
- ServicePointManager.DefaultConnectionLimit = 12;
_cookieContainerCache = cacheManager.GetCache(typeof(HttpClient));
}
diff --git a/src/NzbDrone.Common/Sonarr.Common.csproj b/src/NzbDrone.Common/Sonarr.Common.csproj
index d9ab36fd5..b313109b2 100644
--- a/src/NzbDrone.Common/Sonarr.Common.csproj
+++ b/src/NzbDrone.Common/Sonarr.Common.csproj
@@ -1,28 +1,25 @@
- net8.0
+ net10.0
ISMUSL
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
-
+
-
-
+
diff --git a/src/NzbDrone.Console/Sonarr.Console.csproj b/src/NzbDrone.Console/Sonarr.Console.csproj
index 957bf09eb..ef4838971 100644
--- a/src/NzbDrone.Console/Sonarr.Console.csproj
+++ b/src/NzbDrone.Console/Sonarr.Console.csproj
@@ -1,7 +1,7 @@
Exe
- net8.0
+ net10.0
..\NzbDrone.Host\Sonarr.ico
diff --git a/src/NzbDrone.Core.Test/Sonarr.Core.Test.csproj b/src/NzbDrone.Core.Test/Sonarr.Core.Test.csproj
index 43c8ddba9..56177aa94 100644
--- a/src/NzbDrone.Core.Test/Sonarr.Core.Test.csproj
+++ b/src/NzbDrone.Core.Test/Sonarr.Core.Test.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
diff --git a/src/NzbDrone.Core/Datastore/WhereBuilder.cs b/src/NzbDrone.Core/Datastore/WhereBuilder.cs
index 969364e4f..ba0abca09 100644
--- a/src/NzbDrone.Core/Datastore/WhereBuilder.cs
+++ b/src/NzbDrone.Core/Datastore/WhereBuilder.cs
@@ -1,9 +1,25 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq.Expressions;
using Dapper;
-namespace NzbDrone.Core.Datastore
+namespace NzbDrone.Core.Datastore;
+
+public abstract class WhereBuilder : ExpressionVisitor
{
- public abstract class WhereBuilder : ExpressionVisitor
+ public DynamicParameters Parameters { get; protected set; }
+
+ protected static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression result)
{
- public DynamicParameters Parameters { get; protected set; }
+ if (expression is MethodCallExpression { Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType }, Arguments: [var unwrapped] }
+ && implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
+ && (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
+ {
+ result = unwrapped;
+ return true;
+ }
+
+ result = null;
+ return false;
}
}
diff --git a/src/NzbDrone.Core/Datastore/WhereBuilderPostgres.cs b/src/NzbDrone.Core/Datastore/WhereBuilderPostgres.cs
index fe951228b..553b8fea3 100644
--- a/src/NzbDrone.Core/Datastore/WhereBuilderPostgres.cs
+++ b/src/NzbDrone.Core/Datastore/WhereBuilderPostgres.cs
@@ -298,15 +298,27 @@ namespace NzbDrone.Core.Datastore
}
else
{
- // Static method
- // Must be Enumerable.Contains(source, item)
- if (body.Method.DeclaringType != typeof(Enumerable) || body.Arguments.Count != 2)
+ // "MemoryExtensions.Contains" has an optional 3rd ComparisonType parameter, match only when it's null.
+ if (body.Method.DeclaringType == typeof(MemoryExtensions)
+ && body.Method.Name == nameof(MemoryExtensions.Contains)
+ && body.Arguments is [var spanArg, var valueArg, ..]
+ && (body.Arguments.Count is 2 || (body.Arguments.Count is 3 && body.Arguments[2] is ConstantExpression { Value: null }))
+ && TryUnwrapSpanImplicitCast(spanArg, out var unwrappedSpanArg))
+ {
+ list = unwrappedSpanArg;
+ item = valueArg;
+ }
+
+ // Static method. Must be Enumerable.Contains(source, item)
+ else if (body.Method.DeclaringType == typeof(Enumerable) && body.Arguments.Count == 2)
+ {
+ list = body.Arguments[0];
+ item = body.Arguments[1];
+ }
+ else
{
throw new NotSupportedException("Unexpected form of Enumerable.Contains");
}
-
- list = body.Arguments[0];
- item = body.Arguments[1];
}
_sb.Append('(');
diff --git a/src/NzbDrone.Core/Datastore/WhereBuilderSqlite.cs b/src/NzbDrone.Core/Datastore/WhereBuilderSqlite.cs
index b91c9e61e..5cca447de 100644
--- a/src/NzbDrone.Core/Datastore/WhereBuilderSqlite.cs
+++ b/src/NzbDrone.Core/Datastore/WhereBuilderSqlite.cs
@@ -298,15 +298,27 @@ namespace NzbDrone.Core.Datastore
}
else
{
- // Static method
- // Must be Enumerable.Contains(source, item)
- if (body.Method.DeclaringType != typeof(Enumerable) || body.Arguments.Count != 2)
+ // "MemoryExtensions.Contains" has an optional 3rd ComparisonType parameter, match only when it's null.
+ if (body.Method.DeclaringType == typeof(MemoryExtensions)
+ && body.Method.Name == nameof(MemoryExtensions.Contains)
+ && body.Arguments is [var spanArg, var valueArg, ..]
+ && (body.Arguments.Count is 2 || (body.Arguments.Count is 3 && body.Arguments[2] is ConstantExpression { Value: null }))
+ && TryUnwrapSpanImplicitCast(spanArg, out var unwrappedSpanArg))
+ {
+ list = unwrappedSpanArg;
+ item = valueArg;
+ }
+
+ // Static method. Must be Enumerable.Contains(source, item)
+ else if (body.Method.DeclaringType == typeof(Enumerable) && body.Arguments.Count == 2)
+ {
+ list = body.Arguments[0];
+ item = body.Arguments[1];
+ }
+ else
{
throw new NotSupportedException("Unexpected form of Enumerable.Contains");
}
-
- list = body.Arguments[0];
- item = body.Arguments[1];
}
_sb.Append('(');
diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs
index 5a48dc3a1..7a647f7e0 100644
--- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs
+++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs
@@ -94,7 +94,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
{
item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta);
}
- catch (OverflowException)
+ catch (Exception ex) when (ex is OverflowException or ArgumentOutOfRangeException)
{
item.RemainingTime = TimeSpan.FromMilliseconds(torrent.Eta);
}
diff --git a/src/NzbDrone.Core/Sonarr.Core.csproj b/src/NzbDrone.Core/Sonarr.Core.csproj
index 95d383318..e53adcdaf 100644
--- a/src/NzbDrone.Core/Sonarr.Core.csproj
+++ b/src/NzbDrone.Core/Sonarr.Core.csproj
@@ -1,32 +1,30 @@
- net8.0
+ net10.0
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
+
diff --git a/src/NzbDrone.Host.Test/Sonarr.Host.Test.csproj b/src/NzbDrone.Host.Test/Sonarr.Host.Test.csproj
index 92066b20c..92d6cc86d 100644
--- a/src/NzbDrone.Host.Test/Sonarr.Host.Test.csproj
+++ b/src/NzbDrone.Host.Test/Sonarr.Host.Test.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
diff --git a/src/NzbDrone.Host/Bootstrap.cs b/src/NzbDrone.Host/Bootstrap.cs
index 4ee6db7f7..f8e7ad196 100644
--- a/src/NzbDrone.Host/Bootstrap.cs
+++ b/src/NzbDrone.Host/Bootstrap.cs
@@ -286,7 +286,7 @@ namespace NzbDrone.Host
}
else if (type == X509ContentType.Pkcs12)
{
- certificate = new X509Certificate2(cert, password, X509KeyStorageFlags.DefaultKeySet);
+ certificate = X509CertificateLoader.LoadPkcs12FromFile(cert, password, X509KeyStorageFlags.DefaultKeySet);
}
else
{
diff --git a/src/NzbDrone.Host/Sonarr.Host.csproj b/src/NzbDrone.Host/Sonarr.Host.csproj
index fc458acc7..3d5cdc46c 100644
--- a/src/NzbDrone.Host/Sonarr.Host.csproj
+++ b/src/NzbDrone.Host/Sonarr.Host.csproj
@@ -1,13 +1,12 @@
- net8.0
+ net10.0
Library
-
-
-
-
+
+
+
diff --git a/src/NzbDrone.Host/Startup.cs b/src/NzbDrone.Host/Startup.cs
index 7a09f72e7..fc1c7e957 100644
--- a/src/NzbDrone.Host/Startup.cs
+++ b/src/NzbDrone.Host/Startup.cs
@@ -13,7 +13,7 @@ using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi;
using NLog.Extensions.Logging;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
@@ -35,7 +35,7 @@ using Sonarr.Http.ClientSchema;
using Sonarr.Http.ErrorManagement;
using Sonarr.Http.Frontend;
using Sonarr.Http.Middleware;
-using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
+using IPNetwork = System.Net.IPNetwork;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace NzbDrone.Host
@@ -64,11 +64,11 @@ namespace NzbDrone.Host
services.Configure(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost;
- options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8));
- options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12));
- options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("192.168.0.0"), 16));
- options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7));
- options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("fe80::"), 10));
+ options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8));
+ options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12));
+ options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("192.168.0.0"), 16));
+ options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7));
+ options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("fe80::"), 10));
});
services.AddRouting(options => options.LowercaseUrls = true);
@@ -139,18 +139,13 @@ namespace NzbDrone.Host
Scheme = "apiKey",
Description = "Apikey passed as header",
In = ParameterLocation.Header,
- Reference = new OpenApiReference
- {
- Type = ReferenceType.SecurityScheme,
- Id = "X-Api-Key"
- },
};
c.AddSecurityDefinition("X-Api-Key", apiKeyHeader);
- c.AddSecurityRequirement(new OpenApiSecurityRequirement
+ c.AddSecurityRequirement(document => new OpenApiSecurityRequirement
{
- { apiKeyHeader, Array.Empty() }
+ [new OpenApiSecuritySchemeReference(apiKeyHeader.Name, document)] = new List(),
});
var apikeyQuery = new OpenApiSecurityScheme
@@ -160,11 +155,6 @@ namespace NzbDrone.Host
Scheme = "apiKey",
Description = "Apikey passed as query parameter",
In = ParameterLocation.Query,
- Reference = new OpenApiReference
- {
- Type = ReferenceType.SecurityScheme,
- Id = "apikey"
- },
};
c.AddServer(new OpenApiServer
@@ -179,9 +169,9 @@ namespace NzbDrone.Host
c.AddSecurityDefinition("apikey", apikeyQuery);
- c.AddSecurityRequirement(new OpenApiSecurityRequirement
+ c.AddSecurityRequirement(document => new OpenApiSecurityRequirement
{
- { apikeyQuery, Array.Empty() }
+ [new OpenApiSecuritySchemeReference(apikeyQuery.Name, document)] = new List(),
});
c.DescribeAllParametersInCamelCase();
diff --git a/src/NzbDrone.Integration.Test/Sonarr.Integration.Test.csproj b/src/NzbDrone.Integration.Test/Sonarr.Integration.Test.csproj
index 3794d2368..fcb20be32 100644
--- a/src/NzbDrone.Integration.Test/Sonarr.Integration.Test.csproj
+++ b/src/NzbDrone.Integration.Test/Sonarr.Integration.Test.csproj
@@ -1,10 +1,10 @@
- net8.0
+ net10.0
Library
-
+
diff --git a/src/NzbDrone.Libraries.Test/Sonarr.Libraries.Test.csproj b/src/NzbDrone.Libraries.Test/Sonarr.Libraries.Test.csproj
index 3d6862b6a..8034fca5c 100644
--- a/src/NzbDrone.Libraries.Test/Sonarr.Libraries.Test.csproj
+++ b/src/NzbDrone.Libraries.Test/Sonarr.Libraries.Test.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
diff --git a/src/NzbDrone.Mono.Test/Sonarr.Mono.Test.csproj b/src/NzbDrone.Mono.Test/Sonarr.Mono.Test.csproj
index cd1a3678b..31d01ebc0 100644
--- a/src/NzbDrone.Mono.Test/Sonarr.Mono.Test.csproj
+++ b/src/NzbDrone.Mono.Test/Sonarr.Mono.Test.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0