diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 1bce3a041..ba0800fee 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
- majorVersion: '2.12.3'
+ majorVersion: '2.13.1'
minorVersion: $[counter('minorVersion', 1076)]
lidarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(lidarrVersion)'
diff --git a/frontend/src/Settings/MediaManagement/RootFolder/RootFolder.js b/frontend/src/Settings/MediaManagement/RootFolder/RootFolder.js
index 47e5dfcf1..dc91e4622 100644
--- a/frontend/src/Settings/MediaManagement/RootFolder/RootFolder.js
+++ b/frontend/src/Settings/MediaManagement/RootFolder/RootFolder.js
@@ -94,9 +94,9 @@ class RootFolder extends Component {
diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.css b/frontend/src/Settings/Quality/Definition/QualityDefinition.css
index e090428a1..860333725 100644
--- a/frontend/src/Settings/Quality/Definition/QualityDefinition.css
+++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.css
@@ -24,19 +24,19 @@
height: 20px;
}
-.bar {
+.track {
top: 9px;
margin: 0 5px;
height: 3px;
background-color: var(--sliderAccentColor);
box-shadow: 0 0 0 #000;
- &:nth-child(3n+1) {
+ &:nth-child(3n + 1) {
background-color: #ddd;
}
}
-.handle {
+.thumb {
top: 1px;
z-index: 0 !important;
width: 18px;
diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.css.d.ts b/frontend/src/Settings/Quality/Definition/QualityDefinition.css.d.ts
index 2b92fb212..9c9e8393a 100644
--- a/frontend/src/Settings/Quality/Definition/QualityDefinition.css.d.ts
+++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.css.d.ts
@@ -1,8 +1,6 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
- 'bar': string;
- 'handle': string;
'kilobitsPerSecond': string;
'quality': string;
'qualityDefinition': string;
@@ -10,7 +8,9 @@ interface CssExports {
'sizeLimit': string;
'sizes': string;
'slider': string;
+ 'thumb': string;
'title': string;
+ 'track': string;
}
export const cssExports: CssExports;
export default cssExports;
diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.js b/frontend/src/Settings/Quality/Definition/QualityDefinition.js
index 7d8a78737..48251abfb 100644
--- a/frontend/src/Settings/Quality/Definition/QualityDefinition.js
+++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.js
@@ -55,6 +55,27 @@ class QualityDefinition extends Component {
};
}
+ //
+ // Control
+
+ trackRenderer(props, state) {
+ return (
+
+ );
+ }
+
+ thumbRenderer(props, state) {
+ return (
+
+ );
+ }
+
//
// Listeners
@@ -174,6 +195,7 @@ class QualityDefinition extends Component {
diff --git a/frontend/src/System/Logs/Files/LogFiles.js b/frontend/src/System/Logs/Files/LogFiles.js
index 83736c617..5339a8590 100644
--- a/frontend/src/System/Logs/Files/LogFiles.js
+++ b/frontend/src/System/Logs/Files/LogFiles.js
@@ -1,8 +1,8 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
-import Link from 'Components/Link/Link';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
+import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
@@ -77,15 +77,16 @@ class LogFiles extends Component {
- Log files are located in: {location}
+ {translate('LogFilesLocation', {
+ location
+ })}
- {
- currentLogView === 'Log Files' &&
-
- The log level defaults to 'Info' and can be changed in General Settings
-
- }
+ {currentLogView === 'Log Files' ? (
+
+
+
+ ) : null}
{
diff --git a/frontend/src/System/Updates/Updates.tsx b/frontend/src/System/Updates/Updates.tsx
index ef3d20288..300ab1f99 100644
--- a/frontend/src/System/Updates/Updates.tsx
+++ b/frontend/src/System/Updates/Updates.tsx
@@ -270,7 +270,7 @@ function Updates() {
{generalSettingsError ? (
- {translate('FailedToUpdateSettings')}
+ {translate('FailedToFetchSettings')}
) : null}
diff --git a/src/Lidarr.Api.V1/RemotePathMappings/RemotePathMappingController.cs b/src/Lidarr.Api.V1/RemotePathMappings/RemotePathMappingController.cs
index 33edddff3..fae5b2388 100644
--- a/src/Lidarr.Api.V1/RemotePathMappings/RemotePathMappingController.cs
+++ b/src/Lidarr.Api.V1/RemotePathMappings/RemotePathMappingController.cs
@@ -4,6 +4,7 @@ using Lidarr.Http;
using Lidarr.Http.REST;
using Lidarr.Http.REST.Attributes;
using Microsoft.AspNetCore.Mvc;
+using NzbDrone.Common.Extensions;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Validation.Paths;
@@ -21,11 +22,19 @@ namespace Lidarr.Api.V1.RemotePathMappings
_remotePathMappingService = remotePathMappingService;
SharedValidator.RuleFor(c => c.Host)
- .NotEmpty();
+ .NotEmpty();
// We cannot use IsValidPath here, because it's a remote path, possibly other OS.
SharedValidator.RuleFor(c => c.RemotePath)
- .NotEmpty();
+ .NotEmpty();
+
+ SharedValidator.RuleFor(c => c.RemotePath)
+ .Must(remotePath => remotePath.IsNotNullOrWhiteSpace() && !remotePath.StartsWith(" "))
+ .WithMessage("Remote Path '{PropertyValue}' must not start with a space");
+
+ SharedValidator.RuleFor(c => c.RemotePath)
+ .Must(remotePath => remotePath.IsNotNullOrWhiteSpace() && !remotePath.EndsWith(" "))
+ .WithMessage("Remote Path '{PropertyValue}' must not end with a space");
SharedValidator.RuleFor(c => c.LocalPath)
.Cascade(CascadeMode.Stop)
diff --git a/src/Lidarr.Api.V1/System/Backup/BackupController.cs b/src/Lidarr.Api.V1/System/Backup/BackupController.cs
index 22f017d03..350ada72b 100644
--- a/src/Lidarr.Api.V1/System/Backup/BackupController.cs
+++ b/src/Lidarr.Api.V1/System/Backup/BackupController.cs
@@ -92,7 +92,7 @@ namespace Lidarr.Api.V1.System.Backup
}
[HttpPost("restore/upload")]
- [RequestFormLimits(MultipartBodyLengthLimit = 1000000000)]
+ [RequestFormLimits(MultipartBodyLengthLimit = 5000000000)]
public object UploadAndRestore()
{
var files = Request.Form.Files;
diff --git a/src/NzbDrone.Automation.Test/AutomationTest.cs b/src/NzbDrone.Automation.Test/AutomationTest.cs
index bcf777431..51c79539e 100644
--- a/src/NzbDrone.Automation.Test/AutomationTest.cs
+++ b/src/NzbDrone.Automation.Test/AutomationTest.cs
@@ -40,15 +40,16 @@ namespace NzbDrone.Automation.Test
var service = ChromeDriverService.CreateDefaultService();
// Timeout as windows automation tests seem to take alot longer to get going
- driver = new ChromeDriver(service, options, new TimeSpan(0, 3, 0));
+ driver = new ChromeDriver(service, options, TimeSpan.FromMinutes(3));
driver.Manage().Window.Size = new System.Drawing.Size(1920, 1080);
+ driver.Manage().Window.FullScreen();
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger(), null);
_runner.KillAll();
_runner.Start(true);
- driver.Url = "http://localhost:8686";
+ driver.Navigate().GoToUrl("http://localhost:8686");
var page = new PageBase(driver);
page.WaitForNoSpinner();
@@ -68,7 +69,7 @@ namespace NzbDrone.Automation.Test
{
try
{
- var image = ((ITakesScreenshot)driver).GetScreenshot();
+ var image = (driver as ITakesScreenshot).GetScreenshot();
image.SaveAsFile($"./{name}_test_screenshot.png", ScreenshotImageFormat.Png);
}
catch (Exception ex)
diff --git a/src/NzbDrone.Automation.Test/PageModel/PageBase.cs b/src/NzbDrone.Automation.Test/PageModel/PageBase.cs
index c9a7e8891..664ec7258 100644
--- a/src/NzbDrone.Automation.Test/PageModel/PageBase.cs
+++ b/src/NzbDrone.Automation.Test/PageModel/PageBase.cs
@@ -1,19 +1,17 @@
using System;
using System.Threading;
using OpenQA.Selenium;
-using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.UI;
namespace NzbDrone.Automation.Test.PageModel
{
public class PageBase
{
- private readonly RemoteWebDriver _driver;
+ private readonly IWebDriver _driver;
- public PageBase(RemoteWebDriver driver)
+ public PageBase(IWebDriver driver)
{
_driver = driver;
- driver.Manage().Window.Maximize();
}
public IWebElement FindByClass(string className, int timeout = 5)
diff --git a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs
index 8ca01f6ec..9d896d15c 100644
--- a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs
+++ b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs
@@ -141,7 +141,7 @@ namespace NzbDrone.Common.Http.Dispatchers
}
catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
{
- throw new WebException("Http request timed out", ex.InnerException, WebExceptionStatus.Timeout, null);
+ throw new WebException("Http request timed out", ex, WebExceptionStatus.Timeout, null);
}
}
diff --git a/src/NzbDrone.Common/Lidarr.Common.csproj b/src/NzbDrone.Common/Lidarr.Common.csproj
index 6e16836fc..2e5bacde4 100644
--- a/src/NzbDrone.Common/Lidarr.Common.csproj
+++ b/src/NzbDrone.Common/Lidarr.Common.csproj
@@ -6,7 +6,7 @@
-
+
diff --git a/src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs b/src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs
index 948ab3a54..dd501374c 100644
--- a/src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs
@@ -103,6 +103,7 @@ namespace NzbDrone.Core.Test.DiskSpace
[TestCase("/var/lib/docker")]
[TestCase("/some/place/docker/aufs")]
[TestCase("/etc/network")]
+ [TestCase("/Volumes/.timemachine/ABC123456-A1BC-12A3B45678C9/2025-05-13-181401.backup")]
public void should_not_check_diskspace_for_irrelevant_mounts(string path)
{
var mount = new Mock();
diff --git a/src/NzbDrone.Core/CustomFormats/Specifications/SizeSpecification.cs b/src/NzbDrone.Core/CustomFormats/Specifications/SizeSpecification.cs
index fe873f9ec..9e2fe766e 100644
--- a/src/NzbDrone.Core/CustomFormats/Specifications/SizeSpecification.cs
+++ b/src/NzbDrone.Core/CustomFormats/Specifications/SizeSpecification.cs
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.CustomFormats
{
RuleFor(c => c.Min).GreaterThanOrEqualTo(0);
RuleFor(c => c.Max).GreaterThan(c => c.Min);
+ RuleFor(c => c.Max).LessThanOrEqualTo(double.MaxValue);
}
}
diff --git a/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs b/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs
index 4f685c560..cb8bd50f0 100644
--- a/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs
+++ b/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.DiskSpace
private readonly IRootFolderService _rootFolderService;
private readonly Logger _logger;
- private static readonly Regex _regexSpecialDrive = new Regex("^/var/lib/(docker|rancher|kubelet)(/|$)|^/(boot|etc)(/|$)|/docker(/var)?/aufs(/|$)", RegexOptions.Compiled);
+ private static readonly Regex _regexSpecialDrive = new Regex(@"^/var/lib/(docker|rancher|kubelet)(/|$)|^/(boot|etc)(/|$)|/docker(/var)?/aufs(/|$)|/\.timemachine", RegexOptions.Compiled);
public DiskSpaceService(IDiskProvider diskProvider,
IRootFolderService rootFolderService,
@@ -38,7 +38,10 @@ namespace NzbDrone.Core.DiskSpace
var optionalRootFolders = GetFixedDisksRootPaths().Except(importantRootFolders).Distinct().ToList();
- var diskSpace = GetDiskSpace(importantRootFolders).Concat(GetDiskSpace(optionalRootFolders, true)).ToList();
+ var diskSpace = GetDiskSpace(importantRootFolders)
+ .Concat(GetDiskSpace(optionalRootFolders, true))
+ .OrderBy(d => d.Path, StringComparer.OrdinalIgnoreCase)
+ .ToList();
return diskSpace;
}
@@ -54,7 +57,7 @@ namespace NzbDrone.Core.DiskSpace
private IEnumerable GetFixedDisksRootPaths()
{
return _diskProvider.GetMounts()
- .Where(d => d.DriveType == DriveType.Fixed)
+ .Where(d => d.DriveType is DriveType.Fixed or DriveType.Network)
.Where(d => !_regexSpecialDrive.IsMatch(d.RootDirectory))
.Select(d => d.RootDirectory);
}
diff --git a/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs b/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs
index 0e13b49af..2a045f788 100644
--- a/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs
+++ b/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs
@@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -28,9 +29,10 @@ namespace NzbDrone.Core.Download.Clients.Aria2
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_proxy = proxy;
}
diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs
index 27e2560ec..a9f8c445f 100644
--- a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs
+++ b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs
@@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
@@ -30,9 +31,10 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_scanWatchFolder = scanWatchFolder;
diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs
index 279ef6fcd..71f7fc828 100644
--- a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs
+++ b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs
@@ -7,6 +7,7 @@ using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -25,8 +26,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
+ ILocalizationService localizationService,
Logger logger)
- : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger)
+ : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, localizationService, logger)
{
_scanWatchFolder = scanWatchFolder;
diff --git a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs
index 5ad0f2387..e9ad75d37 100644
--- a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs
+++ b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs
@@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -27,9 +28,10 @@ namespace NzbDrone.Core.Download.Clients.Deluge
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_proxy = proxy;
}
diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs
index 1c31bbd9a..0774d5d6a 100644
--- a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs
+++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs
@@ -11,6 +11,7 @@ using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download.Clients.DownloadStation.Proxies;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -37,9 +38,10 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_dsInfoProxy = dsInfoProxy;
_dsTaskProxySelector = dsTaskProxySelector;
diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs
index 1add52b29..fc14629f8 100644
--- a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs
+++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs
@@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download.Clients.DownloadStation.Proxies;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.ThingiProvider;
@@ -34,8 +35,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
+ ILocalizationService localizationService,
Logger logger)
- : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger)
+ : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, localizationService, logger)
{
_dsInfoProxy = dsInfoProxy;
_dsTaskProxySelector = dsTaskProxySelector;
diff --git a/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs b/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs
index f8cfeeed0..0c8802859 100644
--- a/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs
+++ b/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs
@@ -9,6 +9,7 @@ using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download.Clients.Flood.Models;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -28,9 +29,10 @@ namespace NzbDrone.Core.Download.Clients.Flood
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_proxy = proxy;
_downloadSeedConfigProvider = downloadSeedConfigProvider;
diff --git a/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs b/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs
index b83615c18..34afe472f 100644
--- a/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs
+++ b/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs
@@ -9,6 +9,7 @@ using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download.Clients.FreeboxDownload.Responses;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -25,9 +26,10 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_proxy = proxy;
}
diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs
index d97645b3c..12336c986 100644
--- a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs
+++ b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs
@@ -8,6 +8,7 @@ using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download.Clients.Hadouken.Models;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -25,9 +26,10 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_proxy = proxy;
}
diff --git a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs
index dba7b3ffb..c71e6977f 100644
--- a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs
+++ b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs
@@ -8,6 +8,7 @@ using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Validation;
@@ -24,8 +25,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
+ ILocalizationService localizationService,
Logger logger)
- : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger)
+ : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, localizationService, logger)
{
_proxy = proxy;
}
diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs
index df3e86411..29ee3718e 100644
--- a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs
+++ b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs
@@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Validation;
@@ -28,8 +29,9 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
+ ILocalizationService localizationService,
Logger logger)
- : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger)
+ : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, localizationService, logger)
{
_proxy = proxy;
}
diff --git a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs
index becc142d2..28866080e 100644
--- a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs
+++ b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs
@@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -23,8 +24,9 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
Logger logger)
- : base(configService, diskProvider, remotePathMappingService, logger)
+ : base(configService, diskProvider, remotePathMappingService, localizationService, logger)
{
_httpClient = httpClient;
}
diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs
index 75587513a..9ecf3d471 100644
--- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs
+++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs
@@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -35,9 +36,10 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
ICacheManager cacheManager,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_proxySelector = proxySelector;
diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs
index 05d565718..7f36ae891 100644
--- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs
+++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs
@@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Validation;
@@ -26,8 +27,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
+ ILocalizationService localizationService,
Logger logger)
- : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger)
+ : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, localizationService, logger)
{
_proxy = proxy;
}
diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs b/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs
index 88fdb0f41..d3963e571 100644
--- a/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs
+++ b/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs
@@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.RemotePathMappings;
@@ -24,9 +25,10 @@ namespace NzbDrone.Core.Download.Clients.Transmission
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
}
diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs
index 2dc9dd14e..e797ae48a 100644
--- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs
+++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs
@@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -28,9 +29,10 @@ namespace NzbDrone.Core.Download.Clients.Transmission
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_proxy = proxy;
}
@@ -101,7 +103,11 @@ namespace NzbDrone.Core.Download.Clients.Transmission
if (!torrent.ErrorString.IsNullOrWhiteSpace())
{
item.Status = DownloadItemStatus.Warning;
- item.Message = torrent.ErrorString;
+ item.Message = _localizationService.GetLocalizedString("DownloadClientItemErrorMessage", new Dictionary
+ {
+ { "clientName", Name },
+ { "message", torrent.ErrorString }
+ });
}
else if (torrent.TotalSize == 0)
{
diff --git a/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs b/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs
index 9f5897495..c10d5d3ba 100644
--- a/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs
+++ b/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs
@@ -5,6 +5,7 @@ using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download.Clients.Transmission;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.RemotePathMappings;
@@ -23,9 +24,10 @@ namespace NzbDrone.Core.Download.Clients.Vuze
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
}
diff --git a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs
index c68e1c15d..ff89db95c 100644
--- a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs
+++ b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs
@@ -12,6 +12,7 @@ using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download.Clients.rTorrent;
using NzbDrone.Core.Exceptions;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -35,9 +36,10 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
IRemotePathMappingService remotePathMappingService,
IDownloadSeedConfigProvider downloadSeedConfigProvider,
IRTorrentDirectoryValidator rTorrentDirectoryValidator,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_proxy = proxy;
_rTorrentDirectoryValidator = rTorrentDirectoryValidator;
diff --git a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs
index 72c7ec827..c44b908bd 100644
--- a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs
+++ b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs
@@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -29,9 +30,10 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, blocklistService, logger)
+ : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_proxy = proxy;
diff --git a/src/NzbDrone.Core/Download/DownloadClientBase.cs b/src/NzbDrone.Core/Download/DownloadClientBase.cs
index 63ccf629e..69f0a025e 100644
--- a/src/NzbDrone.Core/Download/DownloadClientBase.cs
+++ b/src/NzbDrone.Core/Download/DownloadClientBase.cs
@@ -8,6 +8,7 @@ using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.ThingiProvider;
@@ -23,6 +24,7 @@ namespace NzbDrone.Core.Download
protected readonly IConfigService _configService;
protected readonly IDiskProvider _diskProvider;
protected readonly IRemotePathMappingService _remotePathMappingService;
+ protected readonly ILocalizationService _localizationService;
protected readonly Logger _logger;
protected ResiliencePipeline RetryStrategy => new ResiliencePipelineBuilder()
@@ -77,11 +79,13 @@ namespace NzbDrone.Core.Download
protected DownloadClientBase(IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
Logger logger)
{
_configService = configService;
_diskProvider = diskProvider;
_remotePathMappingService = remotePathMappingService;
+ _localizationService = localizationService;
_logger = logger;
}
diff --git a/src/NzbDrone.Core/Download/NzbValidationService.cs b/src/NzbDrone.Core/Download/NzbValidationService.cs
index e3cbff710..ee5eae100 100644
--- a/src/NzbDrone.Core/Download/NzbValidationService.cs
+++ b/src/NzbDrone.Core/Download/NzbValidationService.cs
@@ -1,3 +1,4 @@
+using System;
using System.IO;
using System.Linq;
using System.Xml;
@@ -15,39 +16,53 @@ namespace NzbDrone.Core.Download
{
public void Validate(string filename, byte[] fileContent)
{
- var reader = new StreamReader(new MemoryStream(fileContent));
-
- using (var xmlTextReader = XmlReader.Create(reader, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, IgnoreComments = true }))
+ try
{
- var xDoc = XDocument.Load(xmlTextReader);
- var nzb = xDoc.Root;
+ var reader = new StreamReader(new MemoryStream(fileContent));
- if (nzb == null)
+ using (var xmlTextReader = XmlReader.Create(reader,
+ new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, IgnoreComments = true }))
{
- throw new InvalidNzbException("Invalid NZB: No Root element [{0}]", filename);
- }
+ var xDoc = XDocument.Load(xmlTextReader);
+ var nzb = xDoc.Root;
- // nZEDb has an bug in their error reporting code spitting out invalid http status codes
- if (nzb.Name.LocalName.Equals("error") &&
- nzb.TryGetAttributeValue("code", out var code) &&
- nzb.TryGetAttributeValue("description", out var description))
- {
- throw new InvalidNzbException("Invalid NZB: Contains indexer error: {0} - {1}", code, description);
- }
+ if (nzb == null)
+ {
+ throw new InvalidNzbException("Invalid NZB: No Root element [{0}]", filename);
+ }
- if (!nzb.Name.LocalName.Equals("nzb"))
- {
- throw new InvalidNzbException("Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}' [{1}]", nzb.Name.LocalName, filename);
- }
+ // nZEDb has an bug in their error reporting code spitting out invalid http status codes
+ if (nzb.Name.LocalName.Equals("error") &&
+ nzb.TryGetAttributeValue("code", out var code) &&
+ nzb.TryGetAttributeValue("description", out var description))
+ {
+ throw new InvalidNzbException("Invalid NZB: Contains indexer error: {0} - {1}", code, description);
+ }
- var ns = nzb.Name.Namespace;
- var files = nzb.Elements(ns + "file").ToList();
+ if (!nzb.Name.LocalName.Equals("nzb"))
+ {
+ throw new InvalidNzbException(
+ "Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}' [{1}]", nzb.Name.LocalName, filename);
+ }
- if (files.Empty())
- {
- throw new InvalidNzbException("Invalid NZB: No files [{0}]", filename);
+ var ns = nzb.Name.Namespace;
+ var files = nzb.Elements(ns + "file").ToList();
+
+ if (files.Empty())
+ {
+ throw new InvalidNzbException("Invalid NZB: No files [{0}]", filename);
+ }
}
}
+ catch (InvalidNzbException)
+ {
+ // Throw the original exception
+ throw;
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidNzbException("Invalid NZB: Unable to parse [{0}]", ex, filename);
+ }
}
}
}
diff --git a/src/NzbDrone.Core/Download/TorrentClientBase.cs b/src/NzbDrone.Core/Download/TorrentClientBase.cs
index 4e3ec11ab..cdee0e799 100644
--- a/src/NzbDrone.Core/Download/TorrentClientBase.cs
+++ b/src/NzbDrone.Core/Download/TorrentClientBase.cs
@@ -10,6 +10,7 @@ using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Indexers;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
@@ -30,9 +31,10 @@ namespace NzbDrone.Core.Download
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
+ ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
- : base(configService, diskProvider, remotePathMappingService, logger)
+ : base(configService, diskProvider, remotePathMappingService, localizationService, logger)
{
_httpClient = httpClient;
_blocklistService = blocklistService;
@@ -170,7 +172,7 @@ namespace NzbDrone.Core.Download
}
catch (HttpException ex)
{
- if (ex.Response.StatusCode == HttpStatusCode.NotFound)
+ if (ex.Response.StatusCode is HttpStatusCode.NotFound or HttpStatusCode.Gone)
{
_logger.Error(ex, "Downloading torrent file for album '{0}' failed since it no longer exists ({1})", remoteAlbum.Release.Title, torrentUrl);
throw new ReleaseUnavailableException(remoteAlbum.Release, "Downloading torrent failed", ex);
diff --git a/src/NzbDrone.Core/Download/UsenetClientBase.cs b/src/NzbDrone.Core/Download/UsenetClientBase.cs
index e14ac5a87..d92363abf 100644
--- a/src/NzbDrone.Core/Download/UsenetClientBase.cs
+++ b/src/NzbDrone.Core/Download/UsenetClientBase.cs
@@ -6,6 +6,7 @@ using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Indexers;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -24,8 +25,9 @@ namespace NzbDrone.Core.Download
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
+ ILocalizationService localizationService,
Logger logger)
- : base(configService, diskProvider, remotePathMappingService, logger)
+ : base(configService, diskProvider, remotePathMappingService, localizationService, logger)
{
_httpClient = httpClient;
_nzbValidationService = nzbValidationService;
@@ -46,6 +48,7 @@ namespace NzbDrone.Core.Download
{
var request = indexer?.GetDownloadRequest(url) ?? new HttpRequest(url);
request.RateLimitKey = remoteAlbum?.Release?.IndexerId.ToString();
+ request.AllowAutoRedirect = true;
var response = await RetryStrategy
.ExecuteAsync(static async (state, _) => await state._httpClient.GetAsync(state.request), (_httpClient, request))
@@ -57,7 +60,7 @@ namespace NzbDrone.Core.Download
}
catch (HttpException ex)
{
- if (ex.Response.StatusCode == HttpStatusCode.NotFound)
+ if (ex.Response.StatusCode is HttpStatusCode.NotFound or HttpStatusCode.Gone)
{
_logger.Error(ex, "Downloading nzb file for album '{0}' failed since it no longer exists ({1})", remoteAlbum.Release.Title, url);
throw new ReleaseUnavailableException(remoteAlbum.Release, "Downloading nzb failed", ex);
diff --git a/src/NzbDrone.Core/History/EntityHistoryService.cs b/src/NzbDrone.Core/History/EntityHistoryService.cs
index d88358ddb..4b342995a 100644
--- a/src/NzbDrone.Core/History/EntityHistoryService.cs
+++ b/src/NzbDrone.Core/History/EntityHistoryService.cs
@@ -157,7 +157,7 @@ namespace NzbDrone.Core.History
history.Data.Add("Age", message.Album.Release.Age.ToString());
history.Data.Add("AgeHours", message.Album.Release.AgeHours.ToString());
history.Data.Add("AgeMinutes", message.Album.Release.AgeMinutes.ToString());
- history.Data.Add("PublishedDate", message.Album.Release.PublishDate.ToString("s") + "Z");
+ history.Data.Add("PublishedDate", message.Album.Release.PublishDate.ToUniversalTime().ToString("s") + "Z");
history.Data.Add("DownloadClient", message.DownloadClient);
history.Data.Add("Size", message.Album.Release.Size.ToString());
history.Data.Add("DownloadUrl", message.Album.Release.DownloadUrl);
diff --git a/src/NzbDrone.Core/Indexers/FileList/FileListRequestGenerator.cs b/src/NzbDrone.Core/Indexers/FileList/FileListRequestGenerator.cs
index 37c421456..653a94f9c 100644
--- a/src/NzbDrone.Core/Indexers/FileList/FileListRequestGenerator.cs
+++ b/src/NzbDrone.Core/Indexers/FileList/FileListRequestGenerator.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.IndexerSearch.Definitions;
@@ -44,6 +45,11 @@ namespace NzbDrone.Core.Indexers.FileList
private IEnumerable GetRequest(string searchType, IEnumerable categories, string parameters)
{
+ if (categories.Empty())
+ {
+ yield break;
+ }
+
var categoriesQuery = string.Join(",", categories.Distinct());
var baseUrl = string.Format("{0}/api.php?action={1}&category={2}{3}", Settings.BaseUrl.TrimEnd('/'), searchType, categoriesQuery, parameters);
diff --git a/src/NzbDrone.Core/Indexers/FileList/FileListSettings.cs b/src/NzbDrone.Core/Indexers/FileList/FileListSettings.cs
index 2f587e6ef..398cebd38 100644
--- a/src/NzbDrone.Core/Indexers/FileList/FileListSettings.cs
+++ b/src/NzbDrone.Core/Indexers/FileList/FileListSettings.cs
@@ -13,6 +13,8 @@ namespace NzbDrone.Core.Indexers.FileList
RuleFor(c => c.Username).NotEmpty();
RuleFor(c => c.Passkey).NotEmpty();
+ RuleFor(c => c.Categories).NotEmpty();
+
RuleFor(c => c.SeedCriteria).SetValidator(_ => new SeedCriteriaSettingsValidator());
}
}
diff --git a/src/NzbDrone.Core/Lidarr.Core.csproj b/src/NzbDrone.Core/Lidarr.Core.csproj
index 9c5d16036..14b90c62a 100644
--- a/src/NzbDrone.Core/Lidarr.Core.csproj
+++ b/src/NzbDrone.Core/Lidarr.Core.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json
index 5f9c76b0c..c9271275e 100644
--- a/src/NzbDrone.Core/Localization/Core/en.json
+++ b/src/NzbDrone.Core/Localization/Core/en.json
@@ -256,6 +256,7 @@
"CreateEmptyArtistFolders": "Create empty artist folders",
"CreateEmptyArtistFoldersHelpText": "Create missing artist folders during disk scan",
"CreateGroup": "Create group",
+ "CurrentlyInstalled": "Currently Installed",
"Custom": "Custom",
"CustomFilter": "Custom Filter",
"CustomFilters": "Custom Filters",
@@ -334,8 +335,6 @@
"DeleteReleaseProfileMessageText": "Are you sure you want to delete this release profile?",
"DeleteRemotePathMapping": "Delete Remote Path Mapping",
"DeleteRemotePathMappingMessageText": "Are you sure you want to delete this remote path mapping?",
- "DeleteRootFolder": "Delete Root Folder",
- "DeleteRootFolderMessageText": "Are you sure you want to delete the root folder '{name}'?",
"DeleteSelected": "Delete Selected",
"DeleteSelectedArtists": "Delete Selected Artists",
"DeleteSelectedCustomFormats": "Delete Custom Format(s)",
@@ -383,6 +382,7 @@
"DownloadClientDelugeSettingsDirectoryCompleted": "Move When Completed Directory",
"DownloadClientDelugeSettingsDirectoryCompletedHelpText": "Optional location to move completed downloads to, leave blank to use the default Deluge location",
"DownloadClientDelugeSettingsDirectoryHelpText": "Optional location to put downloads in, leave blank to use the default Deluge location",
+ "DownloadClientItemErrorMessage": "{clientName} is reporting an error: {message}",
"DownloadClientPriorityHelpText": "Download Client Priority from 1 (Highest) to 50 (Lowest). Default: 1. Round-Robin is used for clients with the same priority.",
"DownloadClientQbittorrentSettingsContentLayout": "Content Layout",
"DownloadClientQbittorrentSettingsContentLayoutHelpText": "Whether to use qBittorrent's configured content layout, the original layout from the torrent or always create a subfolder (qBittorrent 4.3.2+)",
@@ -486,6 +486,8 @@
"ExtraFileExtensionsHelpTextsExamples": "Examples: '.sub, .nfo' or 'sub,nfo'",
"FailedDownloadHandling": "Failed Download Handling",
"FailedLoadingSearchResults": "Failed to load search results, please try again.",
+ "FailedToFetchSettings": "Failed to fetch settings",
+ "FailedToFetchUpdates": "Failed to fetch updates",
"FailedToLoadQueue": "Failed to load Queue",
"False": "False",
"FileDateHelpText": "Change file date on import/rescan",
@@ -688,6 +690,7 @@
"LocalPathHelpText": "Path that {appName} should use to access the remote path locally",
"Location": "Location",
"LogFiles": "Log Files",
+ "LogFilesLocation": "Log files are located in: {location}",
"LogLevel": "Log Level",
"LogLevelvalueTraceTraceLoggingShouldOnlyBeEnabledTemporarily": "Trace logging should only be enabled temporarily",
"LogSizeLimit": "Log Size Limit",
@@ -1024,6 +1027,8 @@
"RemoveQueueItemRemovalMethod": "Removal Method",
"RemoveQueueItemRemovalMethodHelpTextWarning": "'Remove from Download Client' will remove the download and the file(s) from the download client.",
"RemoveQueueItemsRemovalMethodHelpTextWarning": "'Remove from Download Client' will remove the downloads and the files from the download client.",
+ "RemoveRootFolder": "Remove Root Folder",
+ "RemoveRootFolderArtistsMessageText": "Are you sure you want to remove the root folder '{name}'? Files and folders will not be deleted from disk, and artists in this root folder will not be removed from {appName}.",
"RemoveSelected": "Remove Selected",
"RemoveSelectedItem": "Remove Selected Item",
"RemoveSelectedItemBlocklistMessageText": "Are you sure you want to remove the selected items from the blocklist?",
@@ -1218,6 +1223,7 @@
"TestParsing": "Test Parsing",
"TheAlbumsFilesWillBeDeleted": "The album's files will be deleted.",
"TheArtistFolderStrongpathstrongAndAllOfItsContentWillBeDeleted": "The artist folder '{0}' and all of its content will be deleted.",
+ "TheLogLevelDefault": "The log level defaults to 'Debug' and can be changed in [General Settings](/settings/general)",
"Theme": "Theme",
"ThemeHelpText": "Change Application UI Theme, 'Auto' Theme will use your OS Theme to set Light or Dark mode. Inspired by Theme.Park",
"ThereWasAnErrorLoadingThisItem": "There was an error loading this item",
diff --git a/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs b/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs
index 0dd3c3e62..1dddb8de2 100644
--- a/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs
+++ b/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs
@@ -65,6 +65,10 @@ namespace NzbDrone.Core.MediaFiles
_logger.Debug("Removing existing track file: {0}", file);
_recycleBinProvider.DeleteFile(trackFilePath, subfolder);
}
+ else
+ {
+ _logger.Warn("Existing track file missing from disk: {0}", trackFilePath);
+ }
moveFileResult.OldFiles.Add(file);
_mediaFileService.Delete(file, DeleteMediaFileReason.Upgrade);
diff --git a/src/NzbDrone.Core/Music/Utilities/ShouldRefreshAlbum.cs b/src/NzbDrone.Core/Music/Utilities/ShouldRefreshAlbum.cs
index 5dac303ce..f21c6d70c 100644
--- a/src/NzbDrone.Core/Music/Utilities/ShouldRefreshAlbum.cs
+++ b/src/NzbDrone.Core/Music/Utilities/ShouldRefreshAlbum.cs
@@ -19,26 +19,34 @@ namespace NzbDrone.Core.Music
public bool ShouldRefresh(Album album)
{
- if (album.LastInfoSync < DateTime.UtcNow.AddDays(-60))
+ try
{
- _logger.Trace("Album {0} last updated more than 60 days ago, should refresh.", album.Title);
- return true;
- }
+ if (album.LastInfoSync < DateTime.UtcNow.AddDays(-60))
+ {
+ _logger.Trace("Album {0} last updated more than 60 days ago, should refresh.", album.Title);
+ return true;
+ }
- if (album.LastInfoSync >= DateTime.UtcNow.AddHours(-12))
- {
- _logger.Trace("Album {0} last updated less than 12 hours ago, should not be refreshed.", album.Title);
+ if (album.LastInfoSync >= DateTime.UtcNow.AddHours(-12))
+ {
+ _logger.Trace("Album {0} last updated less than 12 hours ago, should not be refreshed.", album.Title);
+ return false;
+ }
+
+ if (album.ReleaseDate > DateTime.UtcNow.AddDays(-30))
+ {
+ _logger.Trace("album {0} released less than 30 days ago, should refresh.", album.Title);
+ return true;
+ }
+
+ _logger.Trace("Album {0} released long ago and recently refreshed, should not be refreshed.", album.Title);
return false;
}
-
- if (album.ReleaseDate > DateTime.UtcNow.AddDays(-30))
+ catch (Exception e)
{
- _logger.Trace("album {0} released less than 30 days ago, should refresh.", album.Title);
+ _logger.Error(e, "Unable to determine if album should refresh, will try to refresh.");
return true;
}
-
- _logger.Trace("Album {0} released long ago and recently refreshed, should not be refreshed.", album.Title);
- return false;
}
}
}
diff --git a/src/NzbDrone.Core/Music/Utilities/ShouldRefreshArtist.cs b/src/NzbDrone.Core/Music/Utilities/ShouldRefreshArtist.cs
index 495b937af..26548d757 100644
--- a/src/NzbDrone.Core/Music/Utilities/ShouldRefreshArtist.cs
+++ b/src/NzbDrone.Core/Music/Utilities/ShouldRefreshArtist.cs
@@ -22,40 +22,48 @@ namespace NzbDrone.Core.Music
public bool ShouldRefresh(Artist artist)
{
- if (artist.LastInfoSync == null)
+ try
{
- _logger.Trace("Artist {0} was just added, should refresh.", artist.Name);
- return true;
- }
+ if (artist.LastInfoSync == null)
+ {
+ _logger.Trace("Artist {0} was just added, should refresh.", artist.Name);
+ return true;
+ }
- if (artist.LastInfoSync < DateTime.UtcNow.AddDays(-30))
- {
- _logger.Trace("Artist {0} last updated more than 30 days ago, should refresh.", artist.Name);
- return true;
- }
+ if (artist.LastInfoSync < DateTime.UtcNow.AddDays(-30))
+ {
+ _logger.Trace("Artist {0} last updated more than 30 days ago, should refresh.", artist.Name);
+ return true;
+ }
- if (artist.LastInfoSync >= DateTime.UtcNow.AddHours(-12))
- {
- _logger.Trace("Artist {0} last updated less than 12 hours ago, should not be refreshed.", artist.Name);
+ if (artist.LastInfoSync >= DateTime.UtcNow.AddHours(-12))
+ {
+ _logger.Trace("Artist {0} last updated less than 12 hours ago, should not be refreshed.", artist.Name);
+ return false;
+ }
+
+ if (artist.Metadata.Value.Status == ArtistStatusType.Continuing && artist.LastInfoSync < DateTime.UtcNow.AddDays(-2))
+ {
+ _logger.Trace("Artist {0} is continuing and has not been refreshed in 2 days, should refresh.", artist.Name);
+ return true;
+ }
+
+ var lastAlbum = _albumService.GetAlbumsByArtist(artist.Id).MaxBy(e => e.ReleaseDate);
+
+ if (lastAlbum != null && lastAlbum.ReleaseDate > DateTime.UtcNow.AddDays(-30))
+ {
+ _logger.Trace("Last album in {0} aired less than 30 days ago, should refresh.", artist.Name);
+ return true;
+ }
+
+ _logger.Trace("Artist {0} ended long ago, should not be refreshed.", artist.Name);
return false;
}
-
- if (artist.Metadata.Value.Status == ArtistStatusType.Continuing && artist.LastInfoSync < DateTime.UtcNow.AddDays(-2))
+ catch (Exception e)
{
- _logger.Trace("Artist {0} is continuing and has not been refreshed in 2 days, should refresh.", artist.Name);
+ _logger.Error(e, "Unable to determine if artist should refresh, will try to refresh.");
return true;
}
-
- var lastAlbum = _albumService.GetAlbumsByArtist(artist.Id).MaxBy(e => e.ReleaseDate);
-
- if (lastAlbum != null && lastAlbum.ReleaseDate > DateTime.UtcNow.AddDays(-30))
- {
- _logger.Trace("Last album in {0} aired less than 30 days ago, should refresh.", artist.Name);
- return true;
- }
-
- _logger.Trace("Artist {0} ended long ago, should not be refreshed.", artist.Name);
- return false;
}
}
}
diff --git a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs
index 9610ef324..c657f8b16 100644
--- a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs
+++ b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs
@@ -347,7 +347,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
{
if (artist == null)
{
- return null;
+ return new List();
}
return _tagRepository.GetTags(artist.Tags)
diff --git a/src/NzbDrone.Core/Notifications/Discord/Discord.cs b/src/NzbDrone.Core/Notifications/Discord/Discord.cs
index 94714a4f0..9889a8ace 100644
--- a/src/NzbDrone.Core/Notifications/Discord/Discord.cs
+++ b/src/NzbDrone.Core/Notifications/Discord/Discord.cs
@@ -514,9 +514,9 @@ namespace NzbDrone.Core.Notifications.Discord
{
var albumTitles = string.Join(" + ", albums.Select(e => e.Title));
- var title = $"{artist.Name} - {albumTitles}";
+ var title = $"{artist.Name} - {albumTitles}".Replace("`", "\\`");
- return title.Length > 256 ? $"{title.AsSpan(0, 253)}..." : title;
+ return title.Length > 256 ? $"{title.AsSpan(0, 253).TrimEnd('\\')}..." : title;
}
}
}
diff --git a/src/NzbDrone.Core/RemotePathMappings/RemotePathMappingService.cs b/src/NzbDrone.Core/RemotePathMappings/RemotePathMappingService.cs
index b757db3f3..9ee8a0ad0 100644
--- a/src/NzbDrone.Core/RemotePathMappings/RemotePathMappingService.cs
+++ b/src/NzbDrone.Core/RemotePathMappings/RemotePathMappingService.cs
@@ -96,6 +96,11 @@ namespace NzbDrone.Core.RemotePathMappings
throw new ArgumentException("Invalid Host");
}
+ if (mapping.RemotePath.StartsWith(" "))
+ {
+ throw new ArgumentException("Remote Path must not start with a space");
+ }
+
var remotePath = new OsPath(mapping.RemotePath);
var localPath = new OsPath(mapping.LocalPath);