Translate Download Clients on the backend

(cherry picked from commit 48b12f5b00429a7cd218d23f0544641b0da62a06)

Closes #9391
This commit is contained in:
Stevie Robinson 2023-11-10 17:44:04 +01:00 committed by Bogdan
parent a8dbc97921
commit f90cdbb112
39 changed files with 494 additions and 251 deletions

View File

@ -320,7 +320,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
var result = Subject.Test(); var result = Subject.Test();
result.Errors.First().ErrorMessage.Should().Be("Old Hadouken client with unsupported API, need 5.1 or higher"); result.Errors.Count.Should().Be(1);
} }
} }
} }

View File

@ -37,7 +37,7 @@ namespace NzbDrone.Core.Download.Clients.Aria2
_proxy = proxy; _proxy = proxy;
} }
protected override string AddFromMagnetLink(RemoteMovie remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
{ {
var gid = _proxy.AddMagnet(Settings, magnetLink); var gid = _proxy.AddMagnet(Settings, magnetLink);
@ -56,7 +56,7 @@ namespace NzbDrone.Core.Download.Clients.Aria2
return hash; return hash;
} }
protected override string AddFromTorrentFile(RemoteMovie remoteEpisode, string hash, string filename, byte[] fileContent) protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, byte[] fileContent)
{ {
var gid = _proxy.AddTorrent(Settings, fileContent); var gid = _proxy.AddTorrent(Settings, fileContent);
@ -81,6 +81,7 @@ namespace NzbDrone.Core.Download.Clients.Aria2
{ {
var firstFile = torrent.Files?.FirstOrDefault(); var firstFile = torrent.Files?.FirstOrDefault();
// skip metadata download
if (firstFile?.Path?.Contains("[METADATA]") == true) if (firstFile?.Path?.Contains("[METADATA]") == true)
{ {
continue; continue;
@ -236,14 +237,19 @@ namespace NzbDrone.Core.Download.Clients.Aria2
if (new Version(version) < new Version("1.34.0")) if (new Version(version) < new Version("1.34.0"))
{ {
return new ValidationFailure(string.Empty, "Aria2 version should be at least 1.34.0. Version reported is {0}", version); return new ValidationFailure(string.Empty,
_localizationService.GetLocalizedString("DownloadClientValidationErrorVersion",
new Dictionary<string, object>
{
{ "clientName", "Aria2" }, { "requiredVersion", "1.34.0" }, { "reportedVersion", version }
}));
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to test Aria2"); _logger.Error(ex, "Failed to test Aria2");
return new NzbDroneValidationFailure("Host", "Unable to connect to Aria2") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", "Aria2" } }))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };

View File

@ -32,13 +32,13 @@ namespace NzbDrone.Core.Download.Clients.Aria2
[FieldDefinition(1, Label = "Port", Type = FieldType.Number)] [FieldDefinition(1, Label = "Port", Type = FieldType.Number)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "XML RPC Path", Type = FieldType.Textbox)] [FieldDefinition(2, Label = "XmlRpcPath", Type = FieldType.Textbox)]
public string RpcPath { get; set; } public string RpcPath { get; set; }
[FieldDefinition(3, Label = "Use SSL", Type = FieldType.Checkbox)] [FieldDefinition(3, Label = "UseSsl", Type = FieldType.Checkbox)]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(4, Label = "Secret token", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(4, Label = "SecretToken", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string SecretToken { get; set; } public string SecretToken { get; set; }
[FieldDefinition(5, Label = "Directory", Type = FieldType.Textbox, HelpText = "DownloadClientAriaSettingsDirectoryHelpText")] [FieldDefinition(5, Label = "Directory", Type = FieldType.Textbox, HelpText = "DownloadClientAriaSettingsDirectoryHelpText")]

View File

@ -83,7 +83,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
return null; return null;
} }
public override string Name => "Torrent Blackhole"; public override string Name => _localizationService.GetLocalizedString("TorrentBlackhole");
public override IEnumerable<DownloadClientItem> GetItems() public override IEnumerable<DownloadClientItem> GetItems()
{ {

View File

@ -28,23 +28,24 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
private static readonly TorrentBlackholeSettingsValidator Validator = new TorrentBlackholeSettingsValidator(); private static readonly TorrentBlackholeSettingsValidator Validator = new TorrentBlackholeSettingsValidator();
[FieldDefinition(0, Label = "Torrent Folder", Type = FieldType.Path, HelpText = "Folder in which Radarr will store the .torrent file")] [FieldDefinition(0, Label = "TorrentBlackholeTorrentFolder", Type = FieldType.Path, HelpText = "BlackholeFolderHelpText")]
[FieldToken(TokenField.HelpText, "TorrentBlackholeTorrentFolder", "extension", ".torrent")]
public string TorrentFolder { get; set; } public string TorrentFolder { get; set; }
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Radarr should import completed downloads")] [FieldDefinition(1, Label = "BlackholeWatchFolder", Type = FieldType.Path, HelpText = "BlackholeWatchFolderHelpText")]
public string WatchFolder { get; set; } public string WatchFolder { get; set; }
[DefaultValue(false)] [DefaultValue(false)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[FieldDefinition(2, Label = "Save Magnet Files", Type = FieldType.Checkbox, HelpText = "Save a .magnet file with the magnet link if no .torrent file is available (only useful if the download client supports .magnet files)")] [FieldDefinition(2, Label = "TorrentBlackholeSaveMagnetFiles", Type = FieldType.Checkbox, HelpText = "TorrentBlackholeSaveMagnetFilesHelpText")]
public bool SaveMagnetFiles { get; set; } public bool SaveMagnetFiles { get; set; }
[FieldDefinition(3, Label = "Save Magnet Files", Type = FieldType.Textbox, HelpText = "Extension to use for magnet links, defaults to '.magnet'")] [FieldDefinition(3, Label = "TorrentBlackholeSaveMagnetFilesExtension", Type = FieldType.Textbox, HelpText = "TorrentBlackholeSaveMagnetFilesExtensionHelpText")]
public string MagnetFileExtension { get; set; } public string MagnetFileExtension { get; set; }
[DefaultValue(false)] [DefaultValue(false)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[FieldDefinition(4, Label = "Read Only", Type = FieldType.Checkbox, HelpText = "Instead of moving files this will instruct Radarr to Copy or Hardlink (depending on settings/system configuration)")] [FieldDefinition(4, Label = "TorrentBlackholeSaveMagnetFilesReadOnly", Type = FieldType.Checkbox, HelpText = "TorrentBlackholeSaveMagnetFilesReadOnlyHelpText")]
public bool ReadOnly { get; set; } public bool ReadOnly { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -53,7 +53,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
return null; return null;
} }
public override string Name => "Usenet Blackhole"; public override string Name => _localizationService.GetLocalizedString("UsenetBlackhole");
public override IEnumerable<DownloadClientItem> GetItems() public override IEnumerable<DownloadClientItem> GetItems()
{ {

View File

@ -19,10 +19,11 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
{ {
private static readonly UsenetBlackholeSettingsValidator Validator = new UsenetBlackholeSettingsValidator(); private static readonly UsenetBlackholeSettingsValidator Validator = new UsenetBlackholeSettingsValidator();
[FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "Folder in which Radarr will store the .nzb file")] [FieldDefinition(0, Label = "UsenetBlackholeNzbFolder", Type = FieldType.Path, HelpText = "BlackholeFolderHelpText")]
[FieldToken(TokenField.HelpText, "UsenetBlackholeNzbFolder", "extension", ".nzb")]
public string NzbFolder { get; set; } public string NzbFolder { get; set; }
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Radarr should import completed downloads")] [FieldDefinition(1, Label = "BlackholeWatchFolder", Type = FieldType.Path, HelpText = "BlackholeWatchFolderHelpText")]
public string WatchFolder { get; set; } public string WatchFolder { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -159,7 +159,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
if (torrent.State == DelugeTorrentStatus.Error) if (torrent.State == DelugeTorrentStatus.Error)
{ {
item.Status = DownloadItemStatus.Warning; item.Status = DownloadItemStatus.Warning;
item.Message = "Deluge is reporting an error"; item.Message = _localizationService.GetLocalizedString("DownloadClientDelugeTorrentStateError");
} }
else if (torrent.IsFinished && torrent.State != DelugeTorrentStatus.Checking) else if (torrent.IsFinished && torrent.State != DelugeTorrentStatus.Checking)
{ {
@ -252,7 +252,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Password", "Authentication failed"); return new NzbDroneValidationFailure("Password", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure"));
} }
catch (WebException ex) catch (WebException ex)
{ {
@ -260,29 +260,29 @@ namespace NzbDrone.Core.Download.Clients.Deluge
switch (ex.Status) switch (ex.Status)
{ {
case WebExceptionStatus.ConnectFailure: case WebExceptionStatus.ConnectFailure:
return new NzbDroneValidationFailure("Host", "Unable to connect") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = "Please verify the hostname and port." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnectDetail")
}; };
case WebExceptionStatus.ConnectionClosed: case WebExceptionStatus.ConnectionClosed:
return new NzbDroneValidationFailure("UseSsl", "Verify SSL settings") return new NzbDroneValidationFailure("UseSsl", _localizationService.GetLocalizedString("DownloadClientValidationVerifySsl"))
{ {
DetailedDescription = "Please verify your SSL configuration on both Deluge and Radarr." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationVerifySslDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
case WebExceptionStatus.SecureChannelFailure: case WebExceptionStatus.SecureChannelFailure:
return new NzbDroneValidationFailure("UseSsl", "Unable to connect through SSL") return new NzbDroneValidationFailure("UseSsl", _localizationService.GetLocalizedString("DownloadClientValidationSslConnectFailure"))
{ {
DetailedDescription = "Radarr is unable to connect to Deluge using SSL. This problem could be computer related. Please try to configure both Radarr and Deluge to not use SSL." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationSslConnectFailureDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
default: default:
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary<string, object> { { "exception", ex.Message } }));
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to test connection"); _logger.Error(ex, "Failed to test connection");
return new NzbDroneValidationFailure("Host", "Unable to connect to Deluge") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };
@ -302,9 +302,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
if (!enabledPlugins.Contains("Label")) if (!enabledPlugins.Contains("Label"))
{ {
return new NzbDroneValidationFailure("MovieCategory", "Label plugin not activated") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginInactive"))
{ {
DetailedDescription = "You must have the Label plugin enabled in Deluge to use categories." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginInactiveDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
@ -317,9 +317,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
if (!labels.Contains(Settings.MovieCategory)) if (!labels.Contains(Settings.MovieCategory))
{ {
return new NzbDroneValidationFailure("MovieCategory", "Configuration of label failed") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginFailure"))
{ {
DetailedDescription = "Radarr was unable to add the label to Deluge." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginFailureDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
} }
@ -331,9 +331,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
if (!labels.Contains(Settings.MovieImportedCategory)) if (!labels.Contains(Settings.MovieImportedCategory))
{ {
return new NzbDroneValidationFailure("MovieImportedCategory", "Configuration of label failed") return new NzbDroneValidationFailure("MovieImportedCategory", _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginFailure"))
{ {
DetailedDescription = "Radarr was unable to add the label to Deluge." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginFailureDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
} }
@ -350,7 +350,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Unable to get torrents"); _logger.Error(ex, "Unable to get torrents");
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
} }
return null; return null;

View File

@ -35,28 +35,30 @@ namespace NzbDrone.Core.Download.Clients.Deluge
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Deluge")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Deluge")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the deluge json url, see http://[host]:[port]/[urlBase]/json")] [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientDelugeSettingsUrlBaseHelpText")]
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/json")]
public string UrlBase { get; set; } public string UrlBase { get; set; }
[FieldDefinition(4, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(4, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated non-Radarr downloads. Using a category is optional, but strongly recommended.")] [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")]
public string MovieCategory { get; set; } public string MovieCategory { get; set; }
[FieldDefinition(6, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Radarr to set after it has imported the download. Radarr will not remove the torrent if seeding has finished. Leave blank to keep same category.")] [FieldDefinition(6, Label = "PostImportCategory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsPostImportCategoryHelpText")]
public string MovieImportedCategory { get; set; } public string MovieImportedCategory { get; set; }
[FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing movies that aired within the last 14 days")] [FieldDefinition(7, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "DownloadClientSettingsRecentPriorityMovieHelpText")]
public int RecentMoviePriority { get; set; } public int RecentMoviePriority { get; set; }
[FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing movies that aired over 14 days ago")] [FieldDefinition(8, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "DownloadClientSettingsOlderPriorityMovieHelpText")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
[FieldDefinition(9, Label = "Add Paused", Type = FieldType.Checkbox)] [FieldDefinition(9, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
public bool AddPaused { get; set; } public bool AddPaused { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -36,7 +36,8 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Download Station")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Download Station")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] [FieldDefinition(3, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
@ -45,10 +46,10 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
[FieldDefinition(4, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(4, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated non-Radarr downloads. Using a category is optional, but strongly recommended. Creates a [category] subdirectory in the output directory.")] [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategorySubFolderHelpText")]
public string TvCategory { get; set; } public string TvCategory { get; set; }
[FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, HelpText = "Optional shared folder to put downloads into, leave blank to use the default Download Station location")] [FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, HelpText = "DownloadClientDownloadStationSettingsDirectory")]
public string TvDirectory { get; set; } public string TvDirectory { get; set; }
public DownloadStationSettings() public DownloadStationSettings()

View File

@ -52,7 +52,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
public override string Name => "Download Station"; public override string Name => "Download Station";
public override ProviderMessage Message => new ProviderMessage("Radarr is unable to connect to Download Station if 2-Factor Authentication is enabled on your DSM account", ProviderMessageType.Warning); public override ProviderMessage Message => new ProviderMessage(_localizationService.GetLocalizedString("DownloadClientDownloadStationProviderMessage"), ProviderMessageType.Warning);
private IDownloadStationTaskProxy DsTaskProxy => _dsTaskProxySelector.GetProxy(Settings); private IDownloadStationTaskProxy DsTaskProxy => _dsTaskProxySelector.GetProxy(Settings);
@ -226,7 +226,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
{ {
if (torrent.Status == DownloadStationTaskStatus.Extracting) if (torrent.Status == DownloadStationTaskStatus.Extracting)
{ {
return $"Extracting: {int.Parse(torrent.StatusExtra["unzip_progress"])}%"; return _localizationService.GetLocalizedString("DownloadStationStatusExtracting",
new Dictionary<string, object>
{ { "progress", int.Parse(torrent.StatusExtra["unzip_progress"]) } });
} }
if (torrent.Status == DownloadStationTaskStatus.Error) if (torrent.Status == DownloadStationTaskStatus.Error)
@ -363,9 +365,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
catch (DownloadClientAuthenticationException ex) catch (DownloadClientAuthenticationException ex)
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Username", "Authentication failure") return new NzbDroneValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure"))
{ {
DetailedDescription = $"Please verify your username and password. Also verify if the host running Radarr isn't blocked from accessing {Name} by WhiteList limitations in the {Name} configuration." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailureDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
catch (WebException ex) catch (WebException ex)
@ -374,19 +376,19 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
if (ex.Status == WebExceptionStatus.ConnectFailure) if (ex.Status == WebExceptionStatus.ConnectFailure)
{ {
return new NzbDroneValidationFailure("Host", "Unable to connect") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = "Please verify the hostname and port." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnectDetail")
}; };
} }
return new NzbDroneValidationFailure(string.Empty, $"Unknown exception: {ex.Message}"); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary<string, object> { { "exception", ex.Message } }));
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Error testing Torrent Download Station"); _logger.Error(ex, "Error testing Torrent Download Station");
return new NzbDroneValidationFailure("Host", "Unable to connect to Torrent Download Station") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };
@ -401,7 +403,12 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
if (info.MinVersion > 2 || info.MaxVersion < 2) if (info.MinVersion > 2 || info.MaxVersion < 2)
{ {
return new ValidationFailure(string.Empty, $"Download Station API version not supported, should be at least 2. It supports from {info.MinVersion} to {info.MaxVersion}"); return new ValidationFailure(string.Empty,
_localizationService.GetLocalizedString("DownloadClientDownloadStationValidationApiVersion",
new Dictionary<string, object>
{
{ "requiredVersion", 2 }, { "minVersion", info.MinVersion }, { "maxVersion", info.MaxVersion }
}));
} }
return null; return null;
@ -416,7 +423,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
} }
catch (Exception ex) catch (Exception ex)
{ {
return new NzbDroneValidationFailure(string.Empty, $"Failed to get the list of torrents: {ex.Message}"); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
} }
} }

View File

@ -48,7 +48,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
public override string Name => "Download Station"; public override string Name => "Download Station";
public override ProviderMessage Message => new ProviderMessage("Radarr is unable to connect to Download Station if 2-Factor Authentication is enabled on your DSM account", ProviderMessageType.Warning); public override ProviderMessage Message => new ProviderMessage(_localizationService.GetLocalizedString("DownloadClientDownloadStationProviderMessage"), ProviderMessageType.Warning);
private IDownloadStationTaskProxy DsTaskProxy => _dsTaskProxySelector.GetProxy(Settings); private IDownloadStationTaskProxy DsTaskProxy => _dsTaskProxySelector.GetProxy(Settings);
@ -267,9 +267,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
catch (DownloadClientAuthenticationException ex) catch (DownloadClientAuthenticationException ex)
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Username", "Authentication failure") return new NzbDroneValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure"))
{ {
DetailedDescription = $"Please verify your username and password. Also verify if the host running Radarr isn't blocked from accessing {Name} by WhiteList limitations in the {Name} configuration." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailureDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
catch (WebException ex) catch (WebException ex)
@ -278,19 +278,19 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
if (ex.Status == WebExceptionStatus.ConnectFailure) if (ex.Status == WebExceptionStatus.ConnectFailure)
{ {
return new NzbDroneValidationFailure("Host", "Unable to connect") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = "Please verify the hostname and port." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnectDetail")
}; };
} }
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary<string, object> { { "exception", ex.Message } }));
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Error testing Torrent Download Station"); _logger.Error(ex, "Error testing Torrent Download Station");
return new NzbDroneValidationFailure("Host", "Unable to connect to Usenet Download Station") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };
@ -305,7 +305,12 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
if (info.MinVersion > 2 || info.MaxVersion < 2) if (info.MinVersion > 2 || info.MaxVersion < 2)
{ {
return new ValidationFailure(string.Empty, $"Download Station API version not supported, should be at least 2. It supports from {info.MinVersion} to {info.MaxVersion}"); return new ValidationFailure(string.Empty,
_localizationService.GetLocalizedString("DownloadClientDownloadStationValidationApiVersion",
new Dictionary<string, object>
{
{ "requiredVersion", 2 }, { "minVersion", info.MinVersion }, { "maxVersion", info.MaxVersion }
}));
} }
return null; return null;
@ -317,7 +322,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
{ {
if (task.Status == DownloadStationTaskStatus.Extracting) if (task.Status == DownloadStationTaskStatus.Extracting)
{ {
return $"Extracting: {int.Parse(task.StatusExtra["unzip_progress"])}%"; return _localizationService.GetLocalizedString("DownloadStationStatusExtracting",
new Dictionary<string, object>
{ { "progress", int.Parse(task.StatusExtra["unzip_progress"]) } });
} }
if (task.Status == DownloadStationTaskStatus.Error) if (task.Status == DownloadStationTaskStatus.Error)
@ -396,7 +403,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
} }
catch (Exception ex) catch (Exception ex)
{ {
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of NZBs: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestNzbs", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
@ -84,7 +85,8 @@ namespace NzbDrone.Core.Download.Clients.Flood
} }
public override string Name => "Flood"; public override string Name => "Flood";
public override ProviderMessage Message => new ProviderMessage("Radarr will handle automatic removal of torrents based on the current seed criteria in Settings -> Indexers", ProviderMessageType.Info); public override ProviderMessage Message => new ProviderMessage(_localizationService.GetLocalizedString("DownloadClientFloodSettingsRemovalInfo"), ProviderMessageType.Info);
protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, byte[] fileContent) protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, byte[] fileContent)
{ {
_proxy.AddTorrentByFile(Convert.ToBase64String(fileContent), HandleTags(remoteMovie, Settings), Settings); _proxy.AddTorrentByFile(Convert.ToBase64String(fileContent), HandleTags(remoteMovie, Settings), Settings);
@ -223,7 +225,7 @@ namespace NzbDrone.Core.Download.Clients.Flood
if (list.ContainsKey(downloadClientItem.DownloadId)) if (list.ContainsKey(downloadClientItem.DownloadId))
{ {
_proxy.SetTorrentsTags(downloadClientItem.DownloadId, _proxy.SetTorrentsTags(downloadClientItem.DownloadId,
list[downloadClientItem.DownloadId].Tags.Concat(Settings.PostImportTags).ToHashSet(), list[downloadClientItem.DownloadId].Tags.Concat(Settings.PostImportTags).ToImmutableHashSet(),
Settings); Settings);
} }
} }

View File

@ -40,10 +40,12 @@ namespace NzbDrone.Core.Download.Clients.Flood
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Flood")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Flood")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, HelpText = "Optionally adds a prefix to Flood API, such as [protocol]://[host]:[port]/[urlBase]api")] [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, HelpText = "DownloadClientFloodSettingsUrlBaseHelpText")]
[FieldToken(TokenField.HelpText, "UrlBase", "url", "[protocol]://[host]:[port]/[urlBase]/api")]
public string UrlBase { get; set; } public string UrlBase { get; set; }
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
@ -52,19 +54,19 @@ namespace NzbDrone.Core.Download.Clients.Flood
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(6, Label = "Destination", Type = FieldType.Textbox, HelpText = "Manually specifies download destination")] [FieldDefinition(6, Label = "Destination", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDestinationHelpText")]
public string Destination { get; set; } public string Destination { get; set; }
[FieldDefinition(7, Label = "Tags", Type = FieldType.Tag, HelpText = "Initial tags of a download. To be recognized, a download must have all initial tags. This avoids conflicts with unrelated downloads.")] [FieldDefinition(7, Label = "Tags", Type = FieldType.Tag, HelpText = "DownloadClientFloodSettingsTagsHelpText")]
public IEnumerable<string> Tags { get; set; } public IEnumerable<string> Tags { get; set; }
[FieldDefinition(8, Label = "Post-Import Tags", Type = FieldType.Tag, HelpText = "Appends tags after a download is imported.", Advanced = true)] [FieldDefinition(8, Label = "DownloadClientFloodSettingsPostImportTags", Type = FieldType.Tag, HelpText = "DownloadClientFloodSettingsPostImportTagsHelpText", Advanced = true)]
public IEnumerable<string> PostImportTags { get; set; } public IEnumerable<string> PostImportTags { get; set; }
[FieldDefinition(9, Label = "Additional Tags", Type = FieldType.Select, SelectOptions = typeof(AdditionalTags), HelpText = "Adds properties of media as tags. Hints are examples.", Advanced = true)] [FieldDefinition(9, Label = "DownloadClientFloodSettingsAdditionalTags", Type = FieldType.Select, SelectOptions = typeof(AdditionalTags), HelpText = "DownloadClientFloodSettingsAdditionalTagsHelpText", Advanced = true)]
public IEnumerable<int> AdditionalTags { get; set; } public IEnumerable<int> AdditionalTags { get; set; }
[FieldDefinition(10, Label = "Add Paused", Type = FieldType.Checkbox)] [FieldDefinition(10, Label = "DownloadClientFloodSettingsAddPaused", Type = FieldType.Checkbox)]
public bool AddPaused { get; set; } public bool AddPaused { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -46,37 +46,42 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload
ApiUrl = "/api/v1/"; ApiUrl = "/api/v1/";
} }
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox, HelpText = "Hostname or host IP address of the Freebox, defaults to 'mafreebox.freebox.fr' (will only work if on same network)")] [FieldDefinition(0, Label = "Host", Type = FieldType.Textbox, HelpText = "DownloadClientFreeboxSettingsHostHelpText")]
[FieldToken(TokenField.HelpText, "Host", "url", "mafreebox.freebox.fr")]
public string Host { get; set; } public string Host { get; set; }
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox, HelpText = "Port used to access Freebox interface, defaults to '443'")] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox, HelpText = "DownloadClientFreeboxSettingsPortHelpText")]
[FieldToken(TokenField.HelpText, "Port", "port", 443)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secured connection when connecting to Freebox API")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Freebox API")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "API URL", Type = FieldType.Textbox, Advanced = true, HelpText = "Define Freebox API base URL with API version, eg http://[host]:[port]/[api_base_url]/[api_version]/, defaults to '/api/v1/'")] [FieldDefinition(3, Label = "DownloadClientFreeboxSettingsApiUrl", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientFreeboxSettingsApiUrlHelpText")]
[FieldToken(TokenField.HelpText, "DownloadClientFreeboxSettingsApiUrl", "url", "http://[host]:[port]/[api_base_url]/[api_version]/")]
[FieldToken(TokenField.HelpText, "DownloadClientFreeboxSettingsApiUrl", "defaultApiUrl", "/api/v1/")]
public string ApiUrl { get; set; } public string ApiUrl { get; set; }
[FieldDefinition(4, Label = "App ID", Type = FieldType.Textbox, HelpText = "App ID given when creating access to Freebox API (ie 'app_id')")] [FieldDefinition(4, Label = "DownloadClientFreeboxSettingsAppId", Type = FieldType.Textbox, HelpText = "DownloadClientFreeboxSettingsAppIdHelpText")]
public string AppId { get; set; } public string AppId { get; set; }
[FieldDefinition(5, Label = "App Token", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "App token retrieved when creating access to Freebox API (ie 'app_token')")] [FieldDefinition(5, Label = "DownloadClientFreeboxSettingsAppToken", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "DownloadClientFreeboxSettingsAppTokenHelpText")]
public string AppToken { get; set; } public string AppToken { get; set; }
[FieldDefinition(6, Label = "Destination Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Freebox download location")] [FieldDefinition(6, Label = "Destination", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsDestinationHelpText")]
public string DestinationDirectory { get; set; } public string DestinationDirectory { get; set; }
[FieldDefinition(7, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated non-Radarr downloads (will create a [category] subdirectory in the output directory)")] [FieldDefinition(7, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategorySubFolderHelpText")]
public string Category { get; set; } public string Category { get; set; }
[FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] [FieldDefinition(8, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "DownloadClientSettingsRecentPriorityMovieHelpText")]
public int RecentPriority { get; set; } public int RecentPriority { get; set; }
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] [FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "DownloadClientSettingsOlderPriorityMovieHelpText")]
public int OlderPriority { get; set; } public int OlderPriority { get; set; }
[FieldDefinition(10, Label = "Add Paused", Type = FieldType.Checkbox)] [FieldDefinition(10, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
public bool AddPaused { get; set; } public bool AddPaused { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -112,8 +112,9 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload
case FreeboxDownloadTaskStatus.Unknown: case FreeboxDownloadTaskStatus.Unknown:
default: // new status in API? default to downloading default: // new status in API? default to downloading
item.Message = "Unknown download state: " + torrent.Status; item.Message = _localizationService.GetLocalizedString("UnknownDownloadState",
_logger.Info(item.Message); new Dictionary<string, object> { { "state", torrent.Status } });
_logger.Info($"Unknown download state: {torrent.Status}");
item.Status = DownloadItemStatus.Downloading; item.Status = DownloadItemStatus.Downloading;
break; break;
} }

View File

@ -174,18 +174,20 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
if (version < new Version("5.1")) if (version < new Version("5.1"))
{ {
return new ValidationFailure(string.Empty, return new ValidationFailure(string.Empty,
"Old Hadouken client with unsupported API, need 5.1 or higher"); _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion",
new Dictionary<string, object>
{ { "clientName", Name }, { "requiredVersion", "5.1" }, { "reportedVersion", version } }));
} }
} }
catch (DownloadClientAuthenticationException ex) catch (DownloadClientAuthenticationException ex)
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Password", "Authentication failed"); return new NzbDroneValidationFailure("Password", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure"));
} }
catch (Exception ex) catch (Exception ex)
{ {
return new NzbDroneValidationFailure("Host", "Unable to connect to Hadouken") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect"))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };
@ -203,7 +205,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
} }
return null; return null;

View File

@ -39,10 +39,13 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Hadouken")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Hadouken")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the Hadouken url, e.g. http://[host]:[port]/[urlBase]/api")] [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "Hadouken")]
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")]
public string UrlBase { get; set; } public string UrlBase { get; set; }
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
@ -51,7 +54,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox)] [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")]
public string Category { get; set; } public string Category { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -174,7 +174,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
{ {
_logger.Error(ex, "Unable to connect to NZBVortex"); _logger.Error(ex, "Unable to connect to NZBVortex");
return new NzbDroneValidationFailure("Host", "Unable to connect to NZBVortex") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };
@ -192,13 +192,16 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
if (version.Major < 2 || (version.Major == 2 && version.Minor < 3)) if (version.Major < 2 || (version.Major == 2 && version.Minor < 3))
{ {
return new ValidationFailure("Host", "NZBVortex needs to be updated"); return new ValidationFailure("Host",
_localizationService.GetLocalizedString("DownloadClientValidationErrorVersion",
new Dictionary<string, object>
{ { "clientName", Name }, { "requiredVersion", "2.3" }, { "reportedVersion", version } }));
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, "Unable to connect to NZBVortex");
return new ValidationFailure("Host", "Unable to connect to NZBVortex"); return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }));
} }
return null; return null;
@ -212,7 +215,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
} }
catch (NzbVortexAuthenticationException) catch (NzbVortexAuthenticationException)
{ {
return new ValidationFailure("ApiKey", "API Key Incorrect"); return new ValidationFailure("ApiKey", _localizationService.GetLocalizedString("DownloadClientValidationApiKeyIncorrect"));
} }
return null; return null;
@ -226,9 +229,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
{ {
if (Settings.TvCategory.IsNotNullOrWhiteSpace()) if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{ {
return new NzbDroneValidationFailure("TvCategory", "Group does not exist") return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientValidationGroupMissing"))
{ {
DetailedDescription = "The Group you entered doesn't exist in NzbVortex. Go to NzbVortex to create it." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationGroupMissingDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
} }
@ -255,7 +258,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
if (filesResponse.Count > 1) if (filesResponse.Count > 1)
{ {
var message = string.Format("Download contains multiple files and is not in a job folder: {0}", outputPath); var message = _localizationService.GetLocalizedString("DownloadClientNzbVortexMultipleFilesMessage", new Dictionary<string, object> { { "outputPath", outputPath } });
queueItem.Status = DownloadItemStatus.Warning; queueItem.Status = DownloadItemStatus.Warning;
queueItem.Message = message; queueItem.Message = message;

View File

@ -1,4 +1,4 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
@ -42,19 +42,21 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the NZBVortex url, e.g. http://[host]:[port]/[urlBase]/api")] [FieldDefinition(2, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "NZBVortex")]
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")]
public string UrlBase { get; set; } public string UrlBase { get; set; }
[FieldDefinition(3, Label = "API Key", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)] [FieldDefinition(3, Label = "ApiKey", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)]
public string ApiKey { get; set; } public string ApiKey { get; set; }
[FieldDefinition(4, Label = "Group", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated non-Radarr downloads. Using a category is optional, but strongly recommended.")] [FieldDefinition(4, Label = "Group", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")]
public string TvCategory { get; set; } public string TvCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")] [FieldDefinition(5, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "DownloadClientSettingsRecentPriorityMovieHelpText")]
public int RecentMoviePriority { get; set; } public int RecentMoviePriority { get; set; }
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")] [FieldDefinition(6, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "DownloadClientSettingsOlderPriorityMovieHelpText")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -128,7 +128,13 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
historyItem.TotalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo); historyItem.TotalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo);
historyItem.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(itemDir)); historyItem.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(itemDir));
historyItem.Category = item.Category; historyItem.Category = item.Category;
historyItem.Message = $"PAR Status: {item.ParStatus} - Unpack Status: {item.UnpackStatus} - Move Status: {item.MoveStatus} - Script Status: {item.ScriptStatus} - Delete Status: {item.DeleteStatus} - Mark Status: {item.MarkStatus}"; historyItem.Message = _localizationService.GetLocalizedString("NzbgetHistoryItemMessage",
new Dictionary<string, object>
{
{ "parStatus", item.ParStatus }, { "unpackStatus", item.UnpackStatus },
{ "moveStatus", item.MoveStatus }, { "scriptStaus", item.ScriptStatus },
{ "deleteStatus", item.DeleteStatus }, { "markStatus", item.MarkStatus }
});
historyItem.Status = DownloadItemStatus.Completed; historyItem.Status = DownloadItemStatus.Completed;
historyItem.RemainingTime = TimeSpan.Zero; historyItem.RemainingTime = TimeSpan.Zero;
historyItem.CanMoveFiles = true; historyItem.CanMoveFiles = true;
@ -274,18 +280,23 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
if (Version.Parse(version) < Version.Parse("12.0")) if (Version.Parse(version) < Version.Parse("12.0"))
{ {
return new ValidationFailure(string.Empty, "NZBGet version too low, need 12.0 or higher"); return new ValidationFailure(string.Empty,
_localizationService.GetLocalizedString("DownloadClientValidationErrorVersion",
new Dictionary<string, object>
{ { "clientName", Name }, { "requiredVersion", "12.0" }, { "reportedVersion", version } }));
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
if (ex.Message.ContainsIgnoreCase("Authentication failed")) if (ex.Message.ContainsIgnoreCase("Authentication failed"))
{ {
return new ValidationFailure("Username", "Authentication failed"); return new ValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure"));
} }
_logger.Error(ex, "Unable to connect to NZBGet"); _logger.Error(ex, "Unable to connect to NZBGet");
return new ValidationFailure("Host", "Unable to connect to NZBGet"); return new ValidationFailure("Host",
_localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect",
new Dictionary<string, object> { { "clientName", Name } }));
} }
return null; return null;
@ -298,10 +309,10 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
if (!Settings.MovieCategory.IsNullOrWhiteSpace() && !categories.Any(v => v.Name == Settings.MovieCategory)) if (!Settings.MovieCategory.IsNullOrWhiteSpace() && !categories.Any(v => v.Name == Settings.MovieCategory))
{ {
return new NzbDroneValidationFailure("MovieCategory", "Category does not exist") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientValidationCategoryMissing"))
{ {
InfoLink = _proxy.GetBaseUrl(Settings), InfoLink = _proxy.GetBaseUrl(Settings),
DetailedDescription = "The category you entered doesn't exist in NZBGet. Go to NZBGet to create it." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationCategoryMissingDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
@ -315,18 +326,18 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
var keepHistory = config.GetValueOrDefault("KeepHistory", "7"); var keepHistory = config.GetValueOrDefault("KeepHistory", "7");
if (!int.TryParse(keepHistory, NumberStyles.None, CultureInfo.InvariantCulture, out var value) || value == 0) if (!int.TryParse(keepHistory, NumberStyles.None, CultureInfo.InvariantCulture, out var value) || value == 0)
{ {
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0") return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientNzbgetValidationKeepHistoryZero"))
{ {
InfoLink = _proxy.GetBaseUrl(Settings), InfoLink = _proxy.GetBaseUrl(Settings),
DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Radarr from seeing completed downloads." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientNzbgetValidationKeepHistoryZeroDetail")
}; };
} }
else if (value > 25000) else if (value > 25000)
{ {
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be less than 25000") return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientNzbgetValidationKeepHistoryOverMax"))
{ {
InfoLink = _proxy.GetBaseUrl(Settings), InfoLink = _proxy.GetBaseUrl(Settings),
DetailedDescription = "NzbGet setting KeepHistory is set too high." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientNzbgetValidationKeepHistoryOverMaxDetail")
}; };
} }

View File

@ -42,10 +42,13 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to NZBGet")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "NZBGet")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the nzbget url, e.g. http://[host]:[port]/[urlBase]/jsonrpc")] [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "NZBGet")]
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/jsonrpc")]
public string UrlBase { get; set; } public string UrlBase { get; set; }
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
@ -54,16 +57,16 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated non-Radarr downloads. Using a category is optional, but strongly recommended.")] [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")]
public string MovieCategory { get; set; } public string MovieCategory { get; set; }
[FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")] [FieldDefinition(7, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "DownloadClientSettingsRecentPriorityMovieHelpText")]
public int RecentMoviePriority { get; set; } public int RecentMoviePriority { get; set; }
[FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")] [FieldDefinition(8, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "DownloadClientSettingsOlderPriorityMovieHelpText")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
[FieldDefinition(9, Label = "Add Paused", Type = FieldType.Checkbox, HelpText = "This option requires at least NZBGet version 16.0")] [FieldDefinition(9, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox, HelpText = "DownloadClientNzbgetSettingsAddPausedHelpText")]
public bool AddPaused { get; set; } public bool AddPaused { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -19,10 +19,10 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
{ {
private static readonly PneumaticSettingsValidator Validator = new PneumaticSettingsValidator(); private static readonly PneumaticSettingsValidator Validator = new PneumaticSettingsValidator();
[FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "This folder will need to be reachable from XBMC")] [FieldDefinition(0, Label = "DownloadClientPneumaticSettingsNzbFolder", Type = FieldType.Path, HelpText = "DownloadClientPneumaticSettingsNzbFolderHelpText")]
public string NzbFolder { get; set; } public string NzbFolder { get; set; }
[FieldDefinition(1, Label = "Strm Folder", Type = FieldType.Path, HelpText = ".strm files in this folder will be import by drone")] [FieldDefinition(1, Label = "DownloadClientPneumaticSettingsStrmFolder", Type = FieldType.Path, HelpText = "DownloadClientPneumaticSettingsStrmFolderHelpText")]
public string StrmFolder { get; set; } public string StrmFolder { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -61,7 +61,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
} }
catch (DownloadClientException) catch (DownloadClientException)
{ {
_logger.Warn("Failed to set post-import torrent label \"{0}\" for {1} in qBittorrent. Does the label exist?", Settings.MovieImportedCategory, downloadClientItem.Title); _logger.Warn("Failed to set post-import torrent label \"{0}\" for {1} in qBittorrent. Does the label exist?",
Settings.MovieImportedCategory,
downloadClientItem.Title);
} }
} }
} }
@ -243,7 +245,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
{ {
case "error": // some error occurred, applies to paused torrents, warning so failed download handling isn't triggered case "error": // some error occurred, applies to paused torrents, warning so failed download handling isn't triggered
item.Status = DownloadItemStatus.Warning; item.Status = DownloadItemStatus.Warning;
item.Message = "qBittorrent is reporting an error"; item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateError");
break; break;
case "pausedDL": // torrent is paused and has NOT finished downloading case "pausedDL": // torrent is paused and has NOT finished downloading
@ -268,24 +270,24 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
case "stalledDL": // torrent is being downloaded, but no connection were made case "stalledDL": // torrent is being downloaded, but no connection were made
item.Status = DownloadItemStatus.Warning; item.Status = DownloadItemStatus.Warning;
item.Message = "The download is stalled with no connections"; item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateStalled");
break; break;
case "missingFiles": // torrent is being downloaded, but no connection were made case "missingFiles": // torrent is missing files
item.Status = DownloadItemStatus.Warning; item.Status = DownloadItemStatus.Warning;
item.Message = "The download is missing files"; item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateMissingFiles");
break; break;
case "metaDL": // torrent magnet is being downloaded case "metaDL": // torrent magnet is being downloaded
if (config.DhtEnabled) if (config.DhtEnabled)
{ {
item.Status = DownloadItemStatus.Queued; item.Status = DownloadItemStatus.Queued;
item.Message = "qBittorrent is downloading metadata"; item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateMetadata");
} }
else else
{ {
item.Status = DownloadItemStatus.Warning; item.Status = DownloadItemStatus.Warning;
item.Message = "qBittorrent cannot resolve magnet link with DHT disabled"; item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateDhtDisabled");
} }
break; break;
@ -298,8 +300,8 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
break; break;
default: // new status in API? default to downloading default: // new status in API? default to downloading
item.Message = "Unknown download state: " + torrent.State; item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateUnknown", new Dictionary<string, object> { { "state", torrent.State } });
_logger.Info(item.Message); _logger.Info($"Unknown download state: {torrent.State}");
item.Status = DownloadItemStatus.Downloading; item.Status = DownloadItemStatus.Downloading;
break; break;
} }
@ -313,7 +315,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
else else
{ {
item.Status = DownloadItemStatus.Warning; item.Status = DownloadItemStatus.Warning;
item.Message = "Unable to Import. Path matches client base download directory, it's possible 'Keep top-level folder' is disabled for this torrent or 'Torrent Content Layout' is NOT set to 'Original' or 'Create Subfolder'?"; item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStatePathError");
} }
} }
@ -417,29 +419,30 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
if (version < Version.Parse("1.5")) if (version < Version.Parse("1.5"))
{ {
// API version 5 introduced the "save_path" property in /query/torrents // API version 5 introduced the "save_path" property in /query/torrents
return new NzbDroneValidationFailure("Host", "Unsupported client version") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion",
{ new Dictionary<string, object>
DetailedDescription = "Please upgrade to qBittorrent version 3.2.4 or higher." {
}; { "clientName", Name }, { "requiredVersion", "3.2.4" }, { "reportedVersion", version }
}));
} }
else if (version < Version.Parse("1.6")) else if (version < Version.Parse("1.6"))
{ {
// API version 6 introduced support for labels // API version 6 introduced support for labels
if (Settings.MovieCategory.IsNotNullOrWhiteSpace()) if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
{ {
return new NzbDroneValidationFailure("Category", "Category is not supported") return new NzbDroneValidationFailure("Category", _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryUnsupported"))
{ {
DetailedDescription = "Labels are not supported until qBittorrent version 3.3.0. Please upgrade or try again with an empty Category." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryUnsupportedDetail")
}; };
} }
} }
else if (Settings.MovieCategory.IsNullOrWhiteSpace()) else if (Settings.MovieCategory.IsNullOrWhiteSpace())
{ {
// warn if labels are supported, but category is not provided // warn if labels are supported, but category is not provided
return new NzbDroneValidationFailure("MovieCategory", "Category is recommended") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryRecommended"))
{ {
IsWarning = true, IsWarning = true,
DetailedDescription = "Radarr will not attempt to import completed downloads without a category." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryRecommendedDetail")
}; };
} }
@ -447,18 +450,18 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
var config = Proxy.GetConfig(Settings); var config = Proxy.GetConfig(Settings);
if ((config.MaxRatioEnabled || config.MaxSeedingTimeEnabled) && (config.MaxRatioAction == QBittorrentMaxRatioAction.Remove || config.MaxRatioAction == QBittorrentMaxRatioAction.DeleteFiles)) if ((config.MaxRatioEnabled || config.MaxSeedingTimeEnabled) && (config.MaxRatioAction == QBittorrentMaxRatioAction.Remove || config.MaxRatioAction == QBittorrentMaxRatioAction.DeleteFiles))
{ {
return new NzbDroneValidationFailure(string.Empty, "qBittorrent is configured to remove torrents when they reach their Share Ratio Limit") return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationRemovesAtRatioLimit"))
{ {
DetailedDescription = "Radarr will be unable to perform Completed Download Handling as configured. You can fix this in qBittorrent ('Tools -> Options...' in the menu) by changing 'Options -> BitTorrent -> Share Ratio Limiting' from 'Remove them' to 'Pause them'." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationRemovesAtRatioLimitDetail")
}; };
} }
} }
catch (DownloadClientAuthenticationException ex) catch (DownloadClientAuthenticationException ex)
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Username", "Authentication failure") return new NzbDroneValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure"))
{ {
DetailedDescription = "Please verify your username and password." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailureDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
catch (WebException ex) catch (WebException ex)
@ -466,19 +469,19 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
_logger.Error(ex, "Unable to connect to qBittorrent"); _logger.Error(ex, "Unable to connect to qBittorrent");
if (ex.Status == WebExceptionStatus.ConnectFailure) if (ex.Status == WebExceptionStatus.ConnectFailure)
{ {
return new NzbDroneValidationFailure("Host", "Unable to connect") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = "Please verify the hostname and port." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnectDetail")
}; };
} }
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary<string, object> { { "exception", ex.Message } }));
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Unable to test qBittorrent"); _logger.Error(ex, "Unable to test qBittorrent");
return new NzbDroneValidationFailure("Host", "Unable to connect to qBittorrent") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };
@ -510,9 +513,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
if (!labels.ContainsKey(Settings.MovieCategory)) if (!labels.ContainsKey(Settings.MovieCategory))
{ {
return new NzbDroneValidationFailure("MovieCategory", "Configuration of label failed") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryAddFailure"))
{ {
DetailedDescription = "Radarr was unable to add the label to qBittorrent." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryAddFailureDetail")
}; };
} }
} }
@ -524,9 +527,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
if (!labels.ContainsKey(Settings.MovieImportedCategory)) if (!labels.ContainsKey(Settings.MovieImportedCategory))
{ {
return new NzbDroneValidationFailure("MovieImportedCategory", "Configuration of label failed") return new NzbDroneValidationFailure("MovieImportedCategory", _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryAddFailure"))
{ {
DetailedDescription = "Radarr was unable to add the label to qBittorrent." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryAddFailureDetail")
}; };
} }
} }
@ -552,18 +555,24 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
{ {
if (!recentPriorityDefault) if (!recentPriorityDefault)
{ {
return new NzbDroneValidationFailure(nameof(Settings.RecentMoviePriority), "Queueing not enabled") { DetailedDescription = "Torrent Queueing is not enabled in your qBittorrent settings. Enable it in qBittorrent or select 'Last' as priority." }; return new NzbDroneValidationFailure(nameof(Settings.RecentMoviePriority), _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationQueueingNotEnabled"))
{
DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationQueueingNotEnabledDetail")
};
} }
else if (!olderPriorityDefault) else if (!olderPriorityDefault)
{ {
return new NzbDroneValidationFailure(nameof(Settings.OlderMoviePriority), "Queueing not enabled") { DetailedDescription = "Torrent Queueing is not enabled in your qBittorrent settings. Enable it in qBittorrent or select 'Last' as priority." }; return new NzbDroneValidationFailure(nameof(Settings.OlderMoviePriority), _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationQueueingNotEnabled"))
{
DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationQueueingNotEnabledDetail")
};
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to test qBittorrent"); _logger.Error(ex, "Failed to test qBittorrent");
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary<string, object> { { "exception", ex.Message } }));
} }
return null; return null;
@ -578,7 +587,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to get torrents"); _logger.Error(ex, "Failed to get torrents");
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
} }
return null; return null;

View File

@ -1,11 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NLog; using NLog;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
using NzbDrone.Common.Http;
namespace NzbDrone.Core.Download.Clients.QBittorrent namespace NzbDrone.Core.Download.Clients.QBittorrent
{ {
public interface IQBittorrentProxy public interface IQBittorrentProxy
@ -49,7 +46,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
public QBittorrentProxySelector(QBittorrentProxyV1 proxyV1, public QBittorrentProxySelector(QBittorrentProxyV1 proxyV1,
QBittorrentProxyV2 proxyV2, QBittorrentProxyV2 proxyV2,
IHttpClient httpClient,
ICacheManager cacheManager, ICacheManager cacheManager,
Logger logger) Logger logger)
{ {

View File

@ -383,9 +383,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
throw new DownloadClientUnavailableException("Failed to connect to qBittorrent, please check your settings.", ex); throw new DownloadClientUnavailableException("Failed to connect to qBittorrent, please check your settings.", ex);
} }
// returns "Fails." on bad login
if (response.Content != "Ok.") if (response.Content != "Ok.")
{ {
// returns "Fails." on bad login
_logger.Debug("qbitTorrent authentication failed."); _logger.Debug("qbitTorrent authentication failed.");
throw new DownloadClientAuthenticationException("Failed to authenticate with qBittorrent."); throw new DownloadClientAuthenticationException("Failed to authenticate with qBittorrent.");
} }

View File

@ -447,9 +447,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
throw new DownloadClientUnavailableException("Failed to connect to qBittorrent, please check your settings.", ex); throw new DownloadClientUnavailableException("Failed to connect to qBittorrent, please check your settings.", ex);
} }
// returns "Fails." on bad login
if (response.Content != "Ok.") if (response.Content != "Ok.")
{ {
// returns "Fails." on bad login
_logger.Debug("qbitTorrent authentication failed."); _logger.Debug("qbitTorrent authentication failed.");
throw new DownloadClientAuthenticationException("Failed to authenticate with qBittorrent."); throw new DownloadClientAuthenticationException("Failed to authenticate with qBittorrent.");
} }

View File

@ -36,10 +36,12 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientQbittorrentSettingsUseSslHelpText")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the qBittorrent url, e.g. http://[host]:[port]/[urlBase]/api")] [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "qBittorrent")]
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")]
public string UrlBase { get; set; } public string UrlBase { get; set; }
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
@ -48,25 +50,25 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated non-Radarr downloads. Using a category is optional, but strongly recommended.")] [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")]
public string MovieCategory { get; set; } public string MovieCategory { get; set; }
[FieldDefinition(7, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Radarr to set after it has imported the download. Radarr will not remove the torrent if seeding has finished. Leave blank to keep same category.")] [FieldDefinition(7, Label = "PostImportCategory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsPostImportCategoryHelpText")]
public string MovieImportedCategory { get; set; } public string MovieImportedCategory { get; set; }
[FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing movies that released within the last 14 days")] [FieldDefinition(8, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "DownloadClientSettingsRecentPriorityMovieHelpText")]
public int RecentMoviePriority { get; set; } public int RecentMoviePriority { get; set; }
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing movies that were released over 14 days ago")] [FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "DownloadClientSettingsOlderPriorityMovieHelpText")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
[FieldDefinition(10, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "Initial state for torrents added to qBittorrent. Note that Forced Torrents do not abide by seed restrictions")] [FieldDefinition(10, Label = "DownloadClientSettingsInitialState", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "DownloadClientQbittorrentSettingsInitialStateHelpText")]
public int InitialState { get; set; } public int InitialState { get; set; }
[FieldDefinition(11, Label = "Sequential Order", Type = FieldType.Checkbox, HelpText = "Download in sequential order (qBittorrent 4.1.0+)")] [FieldDefinition(11, Label = "DownloadClientQbittorrentSettingsSequentialOrder", Type = FieldType.Checkbox, HelpText = "DownloadClientQbittorrentSettingsSequentialOrderHelpText")]
public bool SequentialOrder { get; set; } public bool SequentialOrder { get; set; }
[FieldDefinition(12, Label = "First and Last First", Type = FieldType.Checkbox, HelpText = "Download first and last pieces first (qBittorrent 4.1.0+)")] [FieldDefinition(12, Label = "DownloadClientQbittorrentSettingsFirstAndLastFirst", Type = FieldType.Checkbox, HelpText = "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText")]
public bool FirstAndLast { get; set; } public bool FirstAndLast { get; set; }
[FieldDefinition(13, Label = "DownloadClientQbittorrentSettingsContentLayout", Type = FieldType.Select, SelectOptions = typeof(QBittorrentContentLayout), HelpText = "DownloadClientQbittorrentSettingsContentLayoutHelpText")] [FieldDefinition(13, Label = "DownloadClientQbittorrentSettingsContentLayout", Type = FieldType.Select, SelectOptions = typeof(QBittorrentContentLayout), HelpText = "DownloadClientQbittorrentSettingsContentLayoutHelpText")]

View File

@ -387,15 +387,15 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
if (version == null) if (version == null)
{ {
return new ValidationFailure("Version", "Unknown Version: " + rawVersion); return new ValidationFailure("Version", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationUnknownVersion", new Dictionary<string, object> { { "rawVersion", rawVersion ?? "" } }));
} }
if (rawVersion.Equals("develop", StringComparison.InvariantCultureIgnoreCase)) if (rawVersion.Equals("develop", StringComparison.InvariantCultureIgnoreCase))
{ {
return new NzbDroneValidationFailure("Version", "SABnzbd develop version, assuming version 3.0.0 or higher.") return new NzbDroneValidationFailure("Version", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationDevelopVersion"))
{ {
IsWarning = true, IsWarning = true,
DetailedDescription = "Radarr may not be able to support new features added to SABnzbd when running develop versions." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationDevelopVersionDetail")
}; };
} }
@ -409,12 +409,17 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return null; return null;
} }
return new ValidationFailure("Version", "Version 0.7.0+ is required, but found: " + version); return new ValidationFailure("Version",
_localizationService.GetLocalizedString("DownloadClientValidationErrorVersion",
new Dictionary<string, object>
{
{ "clientName", Name }, { "requiredVersion", "0.7.0" }, { "reportedVersion", version }
}));
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Host", "Unable to connect to SABnzbd") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };
@ -431,12 +436,12 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
if (ex.Message.ContainsIgnoreCase("API Key Incorrect")) if (ex.Message.ContainsIgnoreCase("API Key Incorrect"))
{ {
return new ValidationFailure("APIKey", "API Key Incorrect"); return new ValidationFailure("APIKey", _localizationService.GetLocalizedString("DownloadClientValidationApiKeyIncorrect"));
} }
if (ex.Message.ContainsIgnoreCase("API Key Required")) if (ex.Message.ContainsIgnoreCase("API Key Required"))
{ {
return new ValidationFailure("APIKey", "API Key Required"); return new ValidationFailure("APIKey", _localizationService.GetLocalizedString("DownloadClientValidationApiKeyRequired"));
} }
throw; throw;
@ -450,10 +455,10 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
var config = _proxy.GetConfig(Settings); var config = _proxy.GetConfig(Settings);
if (config.Misc.pre_check && !HasVersion(1, 1)) if (config.Misc.pre_check && !HasVersion(1, 1))
{ {
return new NzbDroneValidationFailure("", "Disable 'Check before download' option in SABnzbd") return new NzbDroneValidationFailure("", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationCheckBeforeDownload"))
{ {
InfoLink = _proxy.GetBaseUrl(Settings, "config/switches/"), InfoLink = _proxy.GetBaseUrl(Settings, "config/switches/"),
DetailedDescription = "Using Check before download affects Radarr ability to track new downloads. Also SABnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationCheckBeforeDownloadDetail")
}; };
} }
@ -469,10 +474,10 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
if (category.Dir.EndsWith("*")) if (category.Dir.EndsWith("*"))
{ {
return new NzbDroneValidationFailure("MovieCategory", "Enable Job folders") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableJobFolders"))
{ {
InfoLink = _proxy.GetBaseUrl(Settings, "config/categories/"), InfoLink = _proxy.GetBaseUrl(Settings, "config/categories/"),
DetailedDescription = "Radarr prefers each download to have a separate folder. With * appended to the Folder/Path SABnzbd will not create these job folders. Go to SABnzbd to fix it." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableJobFoldersDetail")
}; };
} }
} }
@ -480,10 +485,10 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
if (!Settings.MovieCategory.IsNullOrWhiteSpace()) if (!Settings.MovieCategory.IsNullOrWhiteSpace())
{ {
return new NzbDroneValidationFailure("MovieCategory", "Category does not exist") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientValidationCategoryMissing"))
{ {
InfoLink = _proxy.GetBaseUrl(Settings, "config/categories/"), InfoLink = _proxy.GetBaseUrl(Settings, "config/categories/"),
DetailedDescription = "The category you entered doesn't exist in SABnzbd. Go to SABnzbd to create it." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationCategoryMissingDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
} }
@ -491,37 +496,37 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
// New in SABnzbd 4.1, but on older versions this will be empty and not apply // New in SABnzbd 4.1, but on older versions this will be empty and not apply
if (config.Sorters.Any(s => s.is_active && ContainsCategory(s.sort_cats, Settings.MovieCategory))) if (config.Sorters.Any(s => s.is_active && ContainsCategory(s.sort_cats, Settings.MovieCategory)))
{ {
return new NzbDroneValidationFailure("MovieCategory", "Disable TV Sorting") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableTvSorting"))
{ {
InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"), InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"),
DetailedDescription = "You must disable sorting for the category Radarr uses to prevent import issues. Go to Sabnzbd to fix it." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableTvSortingDetail")
}; };
} }
if (config.Misc.enable_tv_sorting && ContainsCategory(config.Misc.tv_categories, Settings.MovieCategory)) if (config.Misc.enable_tv_sorting && ContainsCategory(config.Misc.tv_categories, Settings.MovieCategory))
{ {
return new NzbDroneValidationFailure("MovieCategory", "Disable TV Sorting") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableTvSorting"))
{ {
InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"), InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"),
DetailedDescription = "You must disable SABnzbd TV Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableTvSortingDetail")
}; };
} }
if (config.Misc.enable_movie_sorting && ContainsCategory(config.Misc.movie_categories, Settings.MovieCategory)) if (config.Misc.enable_movie_sorting && ContainsCategory(config.Misc.movie_categories, Settings.MovieCategory))
{ {
return new NzbDroneValidationFailure("MovieCategory", "Disable Movie Sorting") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableMovieSorting"))
{ {
InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"), InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"),
DetailedDescription = "You must disable SABnzbd Movie Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableMovieSortingDetail")
}; };
} }
if (config.Misc.enable_date_sorting && ContainsCategory(config.Misc.date_categories, Settings.MovieCategory)) if (config.Misc.enable_date_sorting && ContainsCategory(config.Misc.date_categories, Settings.MovieCategory))
{ {
return new NzbDroneValidationFailure("MovieCategory", "Disable Date Sorting") return new NzbDroneValidationFailure("MovieCategory", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableDateSorting"))
{ {
InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"), InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"),
DetailedDescription = "You must disable SABnzbd Date Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableDateSortingDetail")
}; };
} }

View File

@ -1,4 +1,4 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
@ -51,13 +51,16 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Sabnzbd")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Sabnzbd")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the Sabnzbd url, e.g. http://[host]:[port]/[urlBase]/api")] [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "Sabnzbd")]
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")]
public string UrlBase { get; set; } public string UrlBase { get; set; }
[FieldDefinition(4, Label = "API Key", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)] [FieldDefinition(4, Label = "ApiKey", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)]
public string ApiKey { get; set; } public string ApiKey { get; set; }
[FieldDefinition(5, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] [FieldDefinition(5, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
@ -66,13 +69,13 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
[FieldDefinition(6, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(6, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(7, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated non-Radarr downloads. Using a category is optional, but strongly recommended.")] [FieldDefinition(7, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")]
public string MovieCategory { get; set; } public string MovieCategory { get; set; }
[FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")] [FieldDefinition(8, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "DownloadClientSettingsRecentPriorityMovieHelpText")]
public int RecentMoviePriority { get; set; } public int RecentMoviePriority { get; set; }
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")] [FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "DownloadClientSettingsOlderPriorityMovieHelpText")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
@ -38,7 +39,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
if (version < new Version(2, 40)) if (version < new Version(2, 40))
{ {
return new ValidationFailure(string.Empty, "Transmission version not supported, should be 2.40 or higher."); return new ValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion", new Dictionary<string, object> { { "clientName", Name }, { "requiredVersion", "2.40" }, { "reportedVersion", version } }));
} }
return null; return null;

View File

@ -176,6 +176,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
public override DownloadClientInfo GetStatus() public override DownloadClientInfo GetStatus()
{ {
string destDir; string destDir;
if (Settings.MovieDirectory.IsNotNullOrWhiteSpace()) if (Settings.MovieDirectory.IsNotNullOrWhiteSpace())
{ {
destDir = Settings.MovieDirectory; destDir = Settings.MovieDirectory;
@ -187,7 +188,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
if (Settings.MovieCategory.IsNotNullOrWhiteSpace()) if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
{ {
destDir = string.Format("{0}/{1}", destDir, Settings.MovieCategory); destDir = $"{destDir}/{Settings.MovieCategory}";
} }
} }
@ -273,16 +274,16 @@ namespace NzbDrone.Core.Download.Clients.Transmission
catch (DownloadClientAuthenticationException ex) catch (DownloadClientAuthenticationException ex)
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Username", "Authentication failure") return new NzbDroneValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure"))
{ {
DetailedDescription = string.Format("Please verify your username and password. Also verify if the host running Radarr isn't blocked from accessing {0} by WhiteList limitations in the {0} configuration.", Name) DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailureDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
catch (DownloadClientUnavailableException ex) catch (DownloadClientUnavailableException ex)
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Host", "Unable to connect to Transmission") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };
@ -291,7 +292,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
{ {
_logger.Error(ex, "Failed to test"); _logger.Error(ex, "Failed to test");
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary<string, object> { { "exception", ex.Message } }));
} }
} }
@ -306,7 +307,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to get torrents"); _logger.Error(ex, "Failed to get torrents");
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
} }
return null; return null;

View File

@ -41,10 +41,14 @@ namespace NzbDrone.Core.Download.Clients.Transmission
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Transmission")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Transmission")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the transmission rpc url, eg http://[host]:[port]/[urlBase]/rpc, defaults to '/transmission/'")] [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientTransmissionSettingsUrlBaseHelpText")]
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "Transmission")]
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/rpc")]
[FieldToken(TokenField.HelpText, "UrlBase", "defaultUrl", "/transmission/")]
public string UrlBase { get; set; } public string UrlBase { get; set; }
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
@ -53,19 +57,19 @@ namespace NzbDrone.Core.Download.Clients.Transmission
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated non-Radarr downloads. Using a category is optional, but strongly recommended. Creates a [category] subdirectory in the output directory.")] [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategorySubFolderHelpText")]
public string MovieCategory { get; set; } public string MovieCategory { get; set; }
[FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Transmission location")] [FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientTransmissionSettingsDirectoryHelpText")]
public string MovieDirectory { get; set; } public string MovieDirectory { get; set; }
[FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")] [FieldDefinition(8, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "DownloadClientSettingsRecentPriorityMovieHelpText")]
public int RecentMoviePriority { get; set; } public int RecentMoviePriority { get; set; }
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")] [FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "DownloadClientSettingsOlderPriorityMovieHelpText")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
[FieldDefinition(10, Label = "Add Paused", Type = FieldType.Checkbox)] [FieldDefinition(10, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
public bool AddPaused { get; set; } public bool AddPaused { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -61,7 +61,7 @@ namespace NzbDrone.Core.Download.Clients.Vuze
if (!int.TryParse(versionString, out var version) || version < MINIMUM_SUPPORTED_PROTOCOL_VERSION) if (!int.TryParse(versionString, out var version) || version < MINIMUM_SUPPORTED_PROTOCOL_VERSION)
{ {
{ {
return new ValidationFailure(string.Empty, "Protocol version not supported, use Vuze 5.0.0.0 or higher with Vuze Web Remote plugin."); return new ValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientVuzeValidationErrorVersion"));
} }
} }

View File

@ -49,7 +49,8 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
public override void MarkItemAsImported(DownloadClientItem downloadClientItem) public override void MarkItemAsImported(DownloadClientItem downloadClientItem)
{ {
// Set post-import label // Set post-import label
if (Settings.MovieImportedCategory.IsNotNullOrWhiteSpace() && Settings.MovieImportedCategory != Settings.MovieCategory) if (Settings.MovieImportedCategory.IsNotNullOrWhiteSpace() &&
Settings.MovieImportedCategory != Settings.MovieCategory)
{ {
try try
{ {
@ -57,7 +58,10 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Warn(ex, "Failed to set torrent post-import label \"{0}\" for {1} in rTorrent. Does the label exist?", Settings.MovieImportedCategory, downloadClientItem.Title); _logger.Warn(ex,
"Failed to set torrent post-import label \"{0}\" for {1} in rTorrent. Does the label exist?",
Settings.MovieImportedCategory,
downloadClientItem.Title);
} }
} }
@ -68,7 +72,10 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Warn(ex, "Failed to set torrent post-import view \"{0}\" for {1} in rTorrent.", _imported_view, downloadClientItem.Title); _logger.Warn(ex,
"Failed to set torrent post-import view \"{0}\" for {1} in rTorrent.",
_imported_view,
downloadClientItem.Title);
} }
} }
@ -112,7 +119,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
public override string Name => "rTorrent"; public override string Name => "rTorrent";
public override ProviderMessage Message => new ProviderMessage($"rTorrent will not pause torrents when they meet the seed criteria. Radarr will handle automatic removal of torrents based on the current seed criteria in Settings->Indexers only when Remove Completed is enabled. After importing it will also set \"{_imported_view}\" as an rTorrent view, which can be used in rTorrent scripts to customize behavior.", ProviderMessageType.Info); public override ProviderMessage Message => new ProviderMessage(_localizationService.GetLocalizedString("DownloadClientRTorrentProviderMessage", new Dictionary<string, object> { { "importedView", _imported_view } }), ProviderMessageType.Info);
public override IEnumerable<DownloadClientItem> GetItems() public override IEnumerable<DownloadClientItem> GetItems()
{ {
@ -246,14 +253,19 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
if (new Version(version) < new Version("0.9.0")) if (new Version(version) < new Version("0.9.0"))
{ {
return new ValidationFailure(string.Empty, "rTorrent version should be at least 0.9.0. Version reported is {0}", version); return new ValidationFailure(string.Empty,
_localizationService.GetLocalizedString("DownloadClientValidationErrorVersion",
new Dictionary<string, object>
{
{ "clientName", Name }, { "requiredVersion", "0.9.0" }, { "reportedVersion", version }
}));
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to test rTorrent"); _logger.Error(ex, "Failed to test rTorrent");
return new NzbDroneValidationFailure("Host", "Unable to connect to rTorrent") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };
@ -271,7 +283,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to get torrents"); _logger.Error(ex, "Failed to get torrents");
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
} }
return null; return null;

View File

@ -37,10 +37,13 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to rTorrent")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "rTorrent")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Url Path", Type = FieldType.Textbox, HelpText = "Path to the XMLRPC endpoint, see http(s)://[host]:[port]/[urlPath]. This is usually RPC2 or [path to ruTorrent]/plugins/rpc/rpc.php when using ruTorrent.")] [FieldDefinition(3, Label = "DownloadClientRTorrentSettingsUrlPath", Type = FieldType.Textbox, HelpText = "DownloadClientRTorrentSettingsUrlPathHelpText")]
[FieldToken(TokenField.HelpText, "DownloadClientRTorrentSettingsUrlPath", "url", "http(s)://[host]:[port]/[urlPath]")]
[FieldToken(TokenField.HelpText, "DownloadClientRTorrentSettingsUrlPath", "url2", "/plugins/rpc/rpc.php")]
public string UrlBase { get; set; } public string UrlBase { get; set; }
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
@ -49,22 +52,22 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated non-Radarr downloads. Using a category is optional, but strongly recommended.")] [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")]
public string MovieCategory { get; set; } public string MovieCategory { get; set; }
[FieldDefinition(7, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Radarr to set after it has imported the download. Radarr will not remove the torrent if seeding has finished. Leave blank to keep same category.")] [FieldDefinition(7, Label = "PostImportCategory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsPostImportCategoryHelpText")]
public string MovieImportedCategory { get; set; } public string MovieImportedCategory { get; set; }
[FieldDefinition(8, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default rTorrent location")] [FieldDefinition(8, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientRTorrentSettingsDirectoryHelpText")]
public string MovieDirectory { get; set; } public string MovieDirectory { get; set; }
[FieldDefinition(9, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing movies that released within the last 14 days")] [FieldDefinition(9, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "DownloadClientSettingsRecentPriorityMovieHelpText")]
public int RecentMoviePriority { get; set; } public int RecentMoviePriority { get; set; }
[FieldDefinition(10, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing movies that were released over 14 days ago")] [FieldDefinition(10, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "DownloadClientSettingsOlderPriorityMovieHelpText")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
[FieldDefinition(11, Label = "Add Stopped", Type = FieldType.Checkbox, HelpText = "Enabling will add torrents and magnets to rTorrent in a stopped state. This may break magnet files.")] [FieldDefinition(11, Label = "DownloadClientRTorrentSettingsAddStopped", Type = FieldType.Checkbox, HelpText = "DownloadClientRTorrentSettingsAddStoppedHelpText")]
public bool AddStopped { get; set; } public bool AddStopped { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -89,10 +89,10 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
_proxy.SetTorrentLabel(hash, Settings.MovieCategory, Settings); _proxy.SetTorrentLabel(hash, Settings.MovieCategory, Settings);
} }
var isRecentEpisode = remoteMovie.Movie.MovieMetadata.Value.IsRecentMovie; var isRecentMovie = remoteMovie.Movie.MovieMetadata.Value.IsRecentMovie;
if ((isRecentEpisode && Settings.RecentMoviePriority == (int)UTorrentPriority.First) || if ((isRecentMovie && Settings.RecentMoviePriority == (int)UTorrentPriority.First) ||
(!isRecentEpisode && Settings.OlderMoviePriority == (int)UTorrentPriority.First)) (!isRecentMovie && Settings.OlderMoviePriority == (int)UTorrentPriority.First))
{ {
_proxy.MoveTorrentToTopInQueue(hash, Settings); _proxy.MoveTorrentToTopInQueue(hash, Settings);
} }
@ -145,7 +145,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
if (torrent.Status.HasFlag(UTorrentTorrentStatus.Error)) if (torrent.Status.HasFlag(UTorrentTorrentStatus.Error))
{ {
item.Status = DownloadItemStatus.Warning; item.Status = DownloadItemStatus.Warning;
item.Message = "uTorrent is reporting an error"; item.Message = _localizationService.GetLocalizedString("DownloadClientUTorrentTorrentStateError");
} }
else if (torrent.Status.HasFlag(UTorrentTorrentStatus.Loaded) && else if (torrent.Status.HasFlag(UTorrentTorrentStatus.Loaded) &&
torrent.Status.HasFlag(UTorrentTorrentStatus.Checked) && torrent.Remaining == 0 && torrent.Progress == 1.0) torrent.Status.HasFlag(UTorrentTorrentStatus.Checked) && torrent.Remaining == 0 && torrent.Progress == 1.0)
@ -268,15 +268,20 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
if (version < 25406) if (version < 25406)
{ {
return new ValidationFailure(string.Empty, "Old uTorrent client with unsupported API, need 3.0 or higher"); return new ValidationFailure(string.Empty,
_localizationService.GetLocalizedString("DownloadClientValidationErrorVersion",
new Dictionary<string, object>
{
{ "clientName", Name }, { "requiredVersion", "3.0" }, { "reportedVersion", version }
}));
} }
} }
catch (DownloadClientAuthenticationException ex) catch (DownloadClientAuthenticationException ex)
{ {
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Username", "Authentication failure") return new NzbDroneValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure"))
{ {
DetailedDescription = "Please verify your username and password." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailureDetail", new Dictionary<string, object> { { "clientName", Name } })
}; };
} }
catch (WebException ex) catch (WebException ex)
@ -284,19 +289,19 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
_logger.Error(ex, "Unable to connect to uTorrent"); _logger.Error(ex, "Unable to connect to uTorrent");
if (ex.Status == WebExceptionStatus.ConnectFailure) if (ex.Status == WebExceptionStatus.ConnectFailure)
{ {
return new NzbDroneValidationFailure("Host", "Unable to connect") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = "Please verify the hostname and port." DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnectDetail")
}; };
} }
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary<string, object> { { "exception", ex.Message } }));
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to test uTorrent"); _logger.Error(ex, "Failed to test uTorrent");
return new NzbDroneValidationFailure("Host", "Unable to connect to uTorrent") return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary<string, object> { { "clientName", Name } }))
{ {
DetailedDescription = ex.Message DetailedDescription = ex.Message
}; };
@ -314,7 +319,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to get torrents"); _logger.Error(ex, "Failed to get torrents");
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
} }
return null; return null;

View File

@ -34,10 +34,13 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to uTorrent")] [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "uTorrent")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the uTorrent url, e.g. http://[host]:[port]/[urlBase]/api")] [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "uTorrent")]
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")]
public string UrlBase { get; set; } public string UrlBase { get; set; }
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
@ -46,19 +49,20 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated non-Radarr downloads. Using a category is optional, but strongly recommended.")] [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")]
public string MovieCategory { get; set; } public string MovieCategory { get; set; }
[FieldDefinition(7, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Radarr to set after it has imported the download. Radarr will not remove the torrent if seeding has finished. Leave blank to keep same category.")] [FieldDefinition(7, Label = "PostImportCategory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsPostImportCategoryHelpText")]
public string MovieImportedCategory { get; set; } public string MovieImportedCategory { get; set; }
[FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing movies that aired within the last 21 days")] [FieldDefinition(8, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "DownloadClientSettingsRecentPriorityMovieHelpText")]
public int RecentMoviePriority { get; set; } public int RecentMoviePriority { get; set; }
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing movies that aired over 21 days ago")] [FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "DownloadClientSettingsOlderPriorityMovieHelpText")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
[FieldDefinition(10, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(UTorrentState), HelpText = "Initial state for torrents added to uTorrent")] [FieldDefinition(10, Label = "DownloadClientSettingsInitialState", Type = FieldType.Select, SelectOptions = typeof(UTorrentState), HelpText = "DownloadClientSettingsInitialStateHelpText")]
[FieldToken(TokenField.HelpText, "DownloadClientSettingsInitialState", "clientName", "uTorrent")]
public int IntialState { get; set; } public int IntialState { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -114,6 +114,9 @@
"BeforeUpdate": "Before update", "BeforeUpdate": "Before update",
"BindAddress": "Bind Address", "BindAddress": "Bind Address",
"BindAddressHelpText": "Valid IP address, localhost or '*' for all interfaces", "BindAddressHelpText": "Valid IP address, localhost or '*' for all interfaces",
"BlackholeFolderHelpText": "Folder in which {appName} will store the {extension} file",
"BlackholeWatchFolder": "Watch Folder",
"BlackholeWatchFolderHelpText": "Folder from which {appName} should import completed downloads",
"Blocklist": "Blocklist", "Blocklist": "Blocklist",
"BlocklistAndSearch": "Blocklist and Search", "BlocklistAndSearch": "Blocklist and Search",
"BlocklistAndSearchHint": "Start a search for a replacement after blocklisting", "BlocklistAndSearchHint": "Start a search for a replacement after blocklisting",
@ -146,6 +149,7 @@
"CancelProcessing": "Cancel Processing", "CancelProcessing": "Cancel Processing",
"CantFindMovie": "Why can't I find my movie?", "CantFindMovie": "Why can't I find my movie?",
"Cast": "Cast", "Cast": "Cast",
"Category": "Category",
"CertValidationNoLocal": "Disabled for Local Addresses", "CertValidationNoLocal": "Disabled for Local Addresses",
"CertificateValidation": "Certificate Validation", "CertificateValidation": "Certificate Validation",
"CertificateValidationHelpText": "Change how strict HTTPS certification validation is. Do not change unless you understand the risks.", "CertificateValidationHelpText": "Change how strict HTTPS certification validation is. Do not change unless you understand the risks.",
@ -308,12 +312,14 @@
"DeletedReasonManual": "File was deleted by via UI", "DeletedReasonManual": "File was deleted by via UI",
"DeletedReasonMissingFromDisk": "{appName} was unable to find the file on disk so the file was unlinked from the movie in the database", "DeletedReasonMissingFromDisk": "{appName} was unable to find the file on disk so the file was unlinked from the movie in the database",
"DeletedReasonUpgrade": "File was deleted to import an upgrade", "DeletedReasonUpgrade": "File was deleted to import an upgrade",
"Destination": "Destination",
"DestinationPath": "Destination Path", "DestinationPath": "Destination Path",
"DestinationRelativePath": "Destination Relative Path", "DestinationRelativePath": "Destination Relative Path",
"DetailedProgressBar": "Detailed Progress Bar", "DetailedProgressBar": "Detailed Progress Bar",
"DetailedProgressBarHelpText": "Show text on progress bar", "DetailedProgressBarHelpText": "Show text on progress bar",
"Details": "Details", "Details": "Details",
"DigitalRelease": "Digital Release", "DigitalRelease": "Digital Release",
"Directory": "Directory",
"Disabled": "Disabled", "Disabled": "Disabled",
"DisabledForLocalAddresses": "Disabled for Local Addresses", "DisabledForLocalAddresses": "Disabled for Local Addresses",
"Discord": "Discord", "Discord": "Discord",
@ -335,23 +341,139 @@
"DownloadClientCheckDownloadingToRoot": "Download client {downloadClientName} places downloads in the root folder {path}. You should not download to a root folder.", "DownloadClientCheckDownloadingToRoot": "Download client {downloadClientName} places downloads in the root folder {path}. You should not download to a root folder.",
"DownloadClientCheckNoneAvailableMessage": "No download client is available", "DownloadClientCheckNoneAvailableMessage": "No download client is available",
"DownloadClientCheckUnableToCommunicateMessage": "Unable to communicate with {downloadClientName}. {errorMessage}", "DownloadClientCheckUnableToCommunicateMessage": "Unable to communicate with {downloadClientName}. {errorMessage}",
"DownloadClientDelugeSettingsUrlBaseHelpText": "Adds a prefix to the deluge json url, see {url}",
"DownloadClientDelugeTorrentStateError": "Deluge is reporting an error",
"DownloadClientDelugeValidationLabelPluginFailure": "Configuration of label failed",
"DownloadClientDelugeValidationLabelPluginFailureDetail": "{appName} was unable to add the label to {clientName}.",
"DownloadClientDelugeValidationLabelPluginInactive": "Label plugin not activated",
"DownloadClientDelugeValidationLabelPluginInactiveDetail": "You must have the Label plugin enabled in {clientName} to use categories.",
"DownloadClientDownloadStationProviderMessage": "{appName} is unable to connect to Download Station if 2-Factor Authentication is enabled on your DSM account",
"DownloadClientDownloadStationSettingsDirectory": "Optional shared folder to put downloads into, leave blank to use the default Download Station location",
"DownloadClientDownloadStationValidationApiVersion": "Download Station API version not supported, should be at least {requiredVersion}. It supports from {minVersion} to {maxVersion}",
"DownloadClientDownloadStationValidationFolderMissing": "Folder does not exist", "DownloadClientDownloadStationValidationFolderMissing": "Folder does not exist",
"DownloadClientDownloadStationValidationFolderMissingDetail": "The folder '{downloadDir}' does not exist, it must be created manually inside the Shared Folder '{sharedFolder}'.", "DownloadClientDownloadStationValidationFolderMissingDetail": "The folder '{downloadDir}' does not exist, it must be created manually inside the Shared Folder '{sharedFolder}'.",
"DownloadClientDownloadStationValidationNoDefaultDestination": "No default destination", "DownloadClientDownloadStationValidationNoDefaultDestination": "No default destination",
"DownloadClientDownloadStationValidationNoDefaultDestinationDetail": "You must login into your Diskstation as {username} and manually set it up into DownloadStation settings under BT/HTTP/FTP/NZB -> Location.", "DownloadClientDownloadStationValidationNoDefaultDestinationDetail": "You must login into your Diskstation as {username} and manually set it up into DownloadStation settings under BT/HTTP/FTP/NZB -> Location.",
"DownloadClientDownloadStationValidationSharedFolderMissing": "Shared folder does not exist", "DownloadClientDownloadStationValidationSharedFolderMissing": "Shared folder does not exist",
"DownloadClientDownloadStationValidationSharedFolderMissingDetail": "The Diskstation does not have a Shared Folder with the name '{sharedFolder}', are you sure you specified it correctly?", "DownloadClientDownloadStationValidationSharedFolderMissingDetail": "The Diskstation does not have a Shared Folder with the name '{sharedFolder}', are you sure you specified it correctly?",
"DownloadClientFloodSettingsAddPaused": "Add Paused",
"DownloadClientFloodSettingsAdditionalTags": "Additional Tags",
"DownloadClientFloodSettingsAdditionalTagsHelpText": "Adds properties of media as tags. Hints are examples.",
"DownloadClientFloodSettingsPostImportTags": "Post-Import Tags",
"DownloadClientFloodSettingsPostImportTagsHelpText": "Appends tags after a download is imported.",
"DownloadClientFloodSettingsRemovalInfo": "{appName} will handle automatic removal of torrents based on the current seed criteria in Settings -> Indexers",
"DownloadClientFloodSettingsStartOnAdd": "Start on Add",
"DownloadClientFloodSettingsTagsHelpText": "Initial tags of a download. To be recognized, a download must have all initial tags. This avoids conflicts with unrelated downloads.",
"DownloadClientFloodSettingsUrlBaseHelpText": "Adds a prefix to the Flood API, such as {url}",
"DownloadClientFreeboxApiError": "Freebox API returned error: {errorDescription}",
"DownloadClientFreeboxAuthenticationError": "Authentication to Freebox API failed. Reason: {errorDescription}",
"DownloadClientFreeboxNotLoggedIn": "Not logged in",
"DownloadClientFreeboxSettingsApiUrl": "API URL",
"DownloadClientFreeboxSettingsApiUrlHelpText": "Define Freebox API base URL with API version, eg '{url}', defaults to '{defaultApiUrl}'",
"DownloadClientFreeboxSettingsAppId": "App ID",
"DownloadClientFreeboxSettingsAppIdHelpText": "App ID given when creating access to Freebox API (ie 'app_id')",
"DownloadClientFreeboxSettingsAppToken": "App Token",
"DownloadClientFreeboxSettingsAppTokenHelpText": "App token retrieved when creating access to Freebox API (ie 'app_token')",
"DownloadClientFreeboxSettingsHostHelpText": "Hostname or host IP address of the Freebox, defaults to '{url}' (will only work if on same network)",
"DownloadClientFreeboxSettingsPortHelpText": "Port used to access Freebox interface, defaults to '{port}'",
"DownloadClientFreeboxUnableToReachFreebox": "Unable to reach Freebox API. Verify 'Host', 'Port' or 'Use SSL' settings. (Error: {exceptionMessage})",
"DownloadClientFreeboxUnableToReachFreeboxApi": "Unable to reach Freebox API. Verify 'API URL' setting for base URL and version.",
"DownloadClientNzbVortexMultipleFilesMessage": "Download contains multiple files and is not in a job folder: {outputPath}",
"DownloadClientNzbgetSettingsAddPausedHelpText": "This option requires at least NzbGet version 16.0",
"DownloadClientNzbgetValidationKeepHistoryOverMax": "NzbGet setting KeepHistory should be less than 25000",
"DownloadClientNzbgetValidationKeepHistoryOverMaxDetail": "NzbGet setting KeepHistory is set too high.",
"DownloadClientNzbgetValidationKeepHistoryZero": "NzbGet setting KeepHistory should be greater than 0",
"DownloadClientNzbgetValidationKeepHistoryZeroDetail": "NzbGet setting KeepHistory is set to 0. Which prevents {appName} from seeing completed downloads.",
"DownloadClientPneumaticSettingsNzbFolder": "Nzb Folder",
"DownloadClientPneumaticSettingsNzbFolderHelpText": "This folder will need to be reachable from XBMC",
"DownloadClientPneumaticSettingsStrmFolder": "Strm Folder",
"DownloadClientPneumaticSettingsStrmFolderHelpText": ".strm files in this folder will be import by drone",
"DownloadClientPriorityHelpText": "Download Client Priority from 1 (Highest) to 50 (Lowest). Default: 1. Round-Robin is used for clients with the same priority.", "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", "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+)", "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Whether to use qBittorrent's configured content layout, the original layout from the torrent or always create a subfolder (qBittorrent 4.3.2+)",
"DownloadClientQbittorrentSettingsFirstAndLastFirst": "First and Last First",
"DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Download first and last pieces first (qBittorrent 4.1.0+)",
"DownloadClientQbittorrentSettingsInitialStateHelpText": "Initial state for torrents added to qBittorrent. Note that Forced Torrents do not abide by seed restrictions",
"DownloadClientQbittorrentSettingsSequentialOrder": "Sequential Order",
"DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Download in sequential order (qBittorrent 4.1.0+)",
"DownloadClientQbittorrentSettingsUseSslHelpText": "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.",
"DownloadClientQbittorrentTorrentStateDhtDisabled": "qBittorrent cannot resolve magnet link with DHT disabled",
"DownloadClientQbittorrentTorrentStateError": "qBittorrent is reporting an error",
"DownloadClientQbittorrentTorrentStateMetadata": "qBittorrent is downloading metadata",
"DownloadClientQbittorrentTorrentStatePathError": "Unable to Import. Path matches client base download directory, it's possible 'Keep top-level folder' is disabled for this torrent or 'Torrent Content Layout' is NOT set to 'Original' or 'Create Subfolder'?",
"DownloadClientQbittorrentTorrentStateStalled": "The download is stalled with no connections",
"DownloadClientQbittorrentTorrentStateUnknown": "Unknown download state: {state}",
"DownloadClientQbittorrentValidationCategoryAddFailure": "Configuration of category failed",
"DownloadClientQbittorrentValidationCategoryAddFailureDetail": "{appName} was unable to add the label to qBittorrent.",
"DownloadClientQbittorrentValidationCategoryRecommended": "Category is recommended",
"DownloadClientQbittorrentValidationCategoryRecommendedDetail": "{appName} will not attempt to import completed downloads without a category.",
"DownloadClientQbittorrentValidationCategoryUnsupported": "Category is not supported",
"DownloadClientQbittorrentValidationCategoryUnsupportedDetail": "Categories are not supported until qBittorrent version 3.3.0. Please upgrade or try again with an empty Category.",
"DownloadClientQbittorrentValidationQueueingNotEnabled": "Queueing Not Enabled",
"DownloadClientQbittorrentValidationQueueingNotEnabledDetail": "Torrent Queueing is not enabled in your qBittorrent settings. Enable it in qBittorrent or select 'Last' as priority.",
"DownloadClientQbittorrentValidationRemovesAtRatioLimit": "qBittorrent is configured to remove torrents when they reach their Share Ratio Limit",
"DownloadClientQbittorrentValidationRemovesAtRatioLimitDetail": "{appName} will be unable to perform Completed Download Handling as configured. You can fix this in qBittorrent ('Tools -> Options...' in the menu) by changing 'Options -> BitTorrent -> Share Ratio Limiting' from 'Remove them' to 'Pause them'",
"DownloadClientRTorrentProviderMessage": "rTorrent will not pause torrents when they meet the seed criteria. {appName} will handle automatic removal of torrents based on the current seed criteria in Settings->Indexers only when Remove Completed is enabled. After importing it will also set {importedView} as an rTorrent view, which can be used in rTorrent scripts to customize behavior.",
"DownloadClientRTorrentSettingsAddStopped": "Add Stopped",
"DownloadClientRTorrentSettingsAddStoppedHelpText": "Enabling will add torrents and magnets to rTorrent in a stopped state. This may break magnet files.",
"DownloadClientRTorrentSettingsDirectoryHelpText": "Optional location to put downloads in, leave blank to use the default rTorrent location",
"DownloadClientRTorrentSettingsUrlPath": "Url Path",
"DownloadClientRTorrentSettingsUrlPathHelpText": "Path to the XMLRPC endpoint, see {url}. This is usually RPC2 or [path to ruTorrent]{url2} when using ruTorrent.",
"DownloadClientRemovesCompletedDownloadsHealthCheckMessage": "Download client {downloadClientName} is set to remove completed downloads. This can result in downloads being removed from your client before {appName} can import them.", "DownloadClientRemovesCompletedDownloadsHealthCheckMessage": "Download client {downloadClientName} is set to remove completed downloads. This can result in downloads being removed from your client before {appName} can import them.",
"DownloadClientSabnzbdValidationCheckBeforeDownload": "Disable 'Check before download' option in Sabnbzd",
"DownloadClientSabnzbdValidationCheckBeforeDownloadDetail": "Using 'Check before download' affects {appName} ability to track new downloads. Also Sabnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective.",
"DownloadClientSabnzbdValidationDevelopVersion": "Sabnzbd develop version, assuming version 3.0.0 or higher.",
"DownloadClientSabnzbdValidationDevelopVersionDetail": "{appName} may not be able to support new features added to SABnzbd when running develop versions.",
"DownloadClientSabnzbdValidationEnableDisableDateSorting": "Disable Date Sorting",
"DownloadClientSabnzbdValidationEnableDisableDateSortingDetail": "You must disable Date sorting for the category {appName} uses to prevent import issues. Go to Sabnzbd to fix it.",
"DownloadClientSabnzbdValidationEnableDisableMovieSorting": "Disable Movie Sorting",
"DownloadClientSabnzbdValidationEnableDisableMovieSortingDetail": "You must disable Movie sorting for the category {appName} uses to prevent import issues. Go to Sabnzbd to fix it.",
"DownloadClientSabnzbdValidationEnableDisableTvSorting": "Disable TV Sorting",
"DownloadClientSabnzbdValidationEnableDisableTvSortingDetail": "You must disable TV sorting for the category {appName} uses to prevent import issues. Go to Sabnzbd to fix it.",
"DownloadClientSabnzbdValidationEnableJobFolders": "Enable Job folders",
"DownloadClientSabnzbdValidationEnableJobFoldersDetail": "{appName} prefers each download to have a separate folder. With * appended to the Folder/Path Sabnzbd will not create these job folders. Go to Sabnzbd to fix it.",
"DownloadClientSabnzbdValidationUnknownVersion": "Unknown Version: {rawVersion}",
"DownloadClientSettings": "Download Client Settings", "DownloadClientSettings": "Download Client Settings",
"DownloadClientSettingsAddPaused": "Add Paused",
"DownloadClientSettingsCategoryHelpText": "Adding a category specific to {appName} avoids conflicts with unrelated non-{appName} downloads. Using a category is optional, but strongly recommended.",
"DownloadClientSettingsCategorySubFolderHelpText": "Adding a category specific to {appName} avoids conflicts with unrelated non-{appName} downloads. Using a category is optional, but strongly recommended. Creates a [category] subdirectory in the output directory.",
"DownloadClientSettingsDestinationHelpText": "Manually specifies download destination, leave blank to use the default",
"DownloadClientSettingsInitialState": "Initial State",
"DownloadClientSettingsInitialStateHelpText": "Initial state for torrents added to {clientName}",
"DownloadClientSettingsOlderPriority": "Older Priority",
"DownloadClientSettingsOlderPriorityMovieHelpText": "Priority to use when grabbing movies that aired over 21 days ago",
"DownloadClientSettingsPostImportCategoryHelpText": "Category for {appName} to set after it has imported the download. {appName} will not remove torrents in that category even if seeding finished. Leave blank to keep same category.",
"DownloadClientSettingsRecentPriority": "Recent Priority",
"DownloadClientSettingsRecentPriorityMovieHelpText": "Priority to use when grabbing movies that aired within the last 21 days",
"DownloadClientSettingsUrlBaseHelpText": "Adds a prefix to the {clientName} url, such as {url}",
"DownloadClientSettingsUseSslHelpText": "Use secure connection when connection to {clientName}",
"DownloadClientSortingCheckMessage": "Download client {downloadClientName} has {sortingMode} sorting enabled for {appName}'s category. You should disable sorting in your download client to avoid import issues.", "DownloadClientSortingCheckMessage": "Download client {downloadClientName} has {sortingMode} sorting enabled for {appName}'s category. You should disable sorting in your download client to avoid import issues.",
"DownloadClientStatusCheckAllClientMessage": "All download clients are unavailable due to failures", "DownloadClientStatusCheckAllClientMessage": "All download clients are unavailable due to failures",
"DownloadClientStatusCheckSingleClientMessage": "Download clients unavailable due to failures: {downloadClientNames}", "DownloadClientStatusCheckSingleClientMessage": "Download clients unavailable due to failures: {downloadClientNames}",
"DownloadClientTagHelpText": "Only use this download client for movies with at least one matching tag. Leave blank to use with all movies.", "DownloadClientTagHelpText": "Only use this download client for movies with at least one matching tag. Leave blank to use with all movies.",
"DownloadClientTransmissionSettingsDirectoryHelpText": "Optional location to put downloads in, leave blank to use the default Transmission location",
"DownloadClientTransmissionSettingsUrlBaseHelpText": "Adds a prefix to the {clientName} rpc url, eg {url}, defaults to '{defaultUrl}'",
"DownloadClientUTorrentTorrentStateError": "uTorrent is reporting an error",
"DownloadClientUnavailable": "Download client is unavailable", "DownloadClientUnavailable": "Download client is unavailable",
"DownloadClientValidationApiKeyIncorrect": "API Key Incorrect",
"DownloadClientValidationApiKeyRequired": "API Key Required",
"DownloadClientValidationAuthenticationFailure": "Authentication Failure",
"DownloadClientValidationAuthenticationFailureDetail": "Please verify your username and password. Also verify if the host running {appName} isn't blocked from accessing {clientName} by WhiteList limitations in the {clientName} configuration.",
"DownloadClientValidationCategoryMissing": "Category does not exist",
"DownloadClientValidationCategoryMissingDetail": "The category you entered doesn't exist in {clientName}. Create it in {clientName} first.",
"DownloadClientValidationErrorVersion": "{clientName} version should be at least {requiredVersion}. Version reported is {reportedVersion}",
"DownloadClientValidationGroupMissing": "Group does not exist",
"DownloadClientValidationGroupMissingDetail": "The group you entered doesn't exist in {clientName}. Create it in {clientName} first.",
"DownloadClientValidationSslConnectFailure": "Unable to connect through SSL",
"DownloadClientValidationSslConnectFailureDetail": "{appName} is unable to connect to {clientName} using SSL. This problem could be computer related. Please try to configure both {appName} and {clientName} to not use SSL.",
"DownloadClientValidationTestNzbs": "Failed to get the list of NZBs: {exceptionMessage}",
"DownloadClientValidationTestTorrents": "Failed to get the list of torrents: {exceptionMessage}",
"DownloadClientValidationUnableToConnect": "Unable to connect to {clientName}",
"DownloadClientValidationUnableToConnectDetail": "Please verify the hostname and port.",
"DownloadClientValidationUnknownException": "Unknown exception: {exception}", "DownloadClientValidationUnknownException": "Unknown exception: {exception}",
"DownloadClientValidationVerifySsl": "Verify SSL settings",
"DownloadClientValidationVerifySslDetail": "Please verify your SSL configuration on both {clientName} and {appName}",
"DownloadClientVuzeValidationErrorVersion": "Protocol version not supported, use Vuze 5.0.0.0 or higher with Vuze Web Remote plugin.",
"DownloadClients": "Download Clients", "DownloadClients": "Download Clients",
"DownloadClientsLoadError": "Unable to load download clients", "DownloadClientsLoadError": "Unable to load download clients",
"DownloadClientsSettingsSummary": "Download clients, download handling and remote path mappings", "DownloadClientsSettingsSummary": "Download clients, download handling and remote path mappings",
@ -361,6 +483,7 @@
"DownloadPropersAndRepacksHelpText1": "Whether or not to automatically upgrade to Propers/Repacks", "DownloadPropersAndRepacksHelpText1": "Whether or not to automatically upgrade to Propers/Repacks",
"DownloadPropersAndRepacksHelpText2": "Use 'Do not Prefer' to sort by custom format score over Propers/Repacks", "DownloadPropersAndRepacksHelpText2": "Use 'Do not Prefer' to sort by custom format score over Propers/Repacks",
"DownloadPropersAndRepacksHelpTextWarning": "Use custom formats for automatic upgrades to Propers/Repacks", "DownloadPropersAndRepacksHelpTextWarning": "Use custom formats for automatic upgrades to Propers/Repacks",
"DownloadStationStatusExtracting": "Extracting: {progress}%",
"DownloadWarning": "Download warning: {warningMessage}", "DownloadWarning": "Download warning: {warningMessage}",
"Downloaded": "Downloaded", "Downloaded": "Downloaded",
"DownloadedAndMonitored": "Downloaded (Monitored)", "DownloadedAndMonitored": "Downloaded (Monitored)",
@ -980,6 +1103,7 @@
"NotificationsValidationUnableToConnectToService": "Unable to connect to {serviceName}", "NotificationsValidationUnableToConnectToService": "Unable to connect to {serviceName}",
"NotificationsValidationUnableToSendTestMessage": "Unable to send test message: {exceptionMessage}", "NotificationsValidationUnableToSendTestMessage": "Unable to send test message: {exceptionMessage}",
"NotificationsValidationUnableToSendTestMessageApiResponse": "Unable to send test message. Response from API: {error}", "NotificationsValidationUnableToSendTestMessageApiResponse": "Unable to send test message. Response from API: {error}",
"NzbgetHistoryItemMessage": "PAR Status: {parStatus} - Unpack Status: {unpackStatus} - Move Status: {moveStatus} - Script Status: {scriptStatus} - Delete Status: {deleteStatus} - Mark Status: {markStatus}",
"OAuthPopupMessage": "Pop-ups are being blocked by your browser", "OAuthPopupMessage": "Pop-ups are being blocked by your browser",
"Ok": "Ok", "Ok": "Ok",
"OnApplicationUpdate": "On Application Update", "OnApplicationUpdate": "On Application Update",
@ -1057,6 +1181,7 @@
"PopularityIndex": "Current Popularity Index", "PopularityIndex": "Current Popularity Index",
"Port": "Port", "Port": "Port",
"PortNumber": "Port Number", "PortNumber": "Port Number",
"PostImportCategory": "Post-Import Category",
"PosterOptions": "Poster Options", "PosterOptions": "Poster Options",
"PosterSize": "Poster Size", "PosterSize": "Poster Size",
"Posters": "Posters", "Posters": "Posters",
@ -1286,6 +1411,7 @@
"SearchOnAddHelpText": "Search for movies on this list when added to library", "SearchOnAddHelpText": "Search for movies on this list when added to library",
"SearchSelected": "Search Selected", "SearchSelected": "Search Selected",
"Seconds": "Seconds", "Seconds": "Seconds",
"SecretToken": "Secret Token",
"Security": "Security", "Security": "Security",
"Seeders": "Seeders", "Seeders": "Seeders",
"SelectAll": "Select All", "SelectAll": "Select All",
@ -1434,6 +1560,14 @@
"TmdbVotes": "TMDb Votes", "TmdbVotes": "TMDb Votes",
"Today": "Today", "Today": "Today",
"Tomorrow": "Tomorrow", "Tomorrow": "Tomorrow",
"TorrentBlackhole": "Torrent Blackhole",
"TorrentBlackholeSaveMagnetFiles": "Save Magnet Files",
"TorrentBlackholeSaveMagnetFilesExtension": "Save Magnet Files Extension",
"TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Extension to use for magnet links, defaults to '.magnet'",
"TorrentBlackholeSaveMagnetFilesHelpText": "Save the magnet link if no .torrent file is available (only useful if the download client supports magnets saved to a file)",
"TorrentBlackholeSaveMagnetFilesReadOnly": "Read Only",
"TorrentBlackholeSaveMagnetFilesReadOnlyHelpText": "Instead of moving files this will instruct {appName} to Copy or Hardlink (depending on settings/system configuration)",
"TorrentBlackholeTorrentFolder": "Torrent Folder",
"TorrentDelay": "Torrent Delay", "TorrentDelay": "Torrent Delay",
"TorrentDelayHelpText": "Delay in minutes to wait before grabbing a torrent", "TorrentDelayHelpText": "Delay in minutes to wait before grabbing a torrent",
"TorrentDelayTime": "Torrent Delay: {0}", "TorrentDelayTime": "Torrent Delay: {0}",
@ -1493,6 +1627,7 @@
"Unavailable": "Unavailable", "Unavailable": "Unavailable",
"Ungroup": "Ungroup", "Ungroup": "Ungroup",
"Unknown": "Unknown", "Unknown": "Unknown",
"UnknownDownloadState": "Unknown download state: {state}",
"UnknownEventTooltip": "Unknown event", "UnknownEventTooltip": "Unknown event",
"Unlimited": "Unlimited", "Unlimited": "Unlimited",
"UnmappedFilesOnly": "Unmapped Files Only", "UnmappedFilesOnly": "Unmapped Files Only",
@ -1525,7 +1660,10 @@
"UseHardlinksInsteadOfCopy": "Use Hardlinks instead of Copy", "UseHardlinksInsteadOfCopy": "Use Hardlinks instead of Copy",
"UseProxy": "Use Proxy", "UseProxy": "Use Proxy",
"UseScriptImportHelpText": "Copy files for importing using a script (ex. for transcoding)", "UseScriptImportHelpText": "Copy files for importing using a script (ex. for transcoding)",
"UseSsl": "Use SSL",
"Usenet": "Usenet", "Usenet": "Usenet",
"UsenetBlackhole": "Usenet Blackhole",
"UsenetBlackholeNzbFolder": "Nzb Folder",
"UsenetDelay": "Usenet Delay", "UsenetDelay": "Usenet Delay",
"UsenetDelayHelpText": "Delay in minutes to wait before grabbing a release from Usenet", "UsenetDelayHelpText": "Delay in minutes to wait before grabbing a release from Usenet",
"UsenetDelayTime": "Usenet Delay: {0}", "UsenetDelayTime": "Usenet Delay: {0}",
@ -1551,6 +1689,7 @@
"WhySearchesCouldBeFailing": "Click here to find out why searches could be failing", "WhySearchesCouldBeFailing": "Click here to find out why searches could be failing",
"Wiki": "Wiki", "Wiki": "Wiki",
"WouldYouLikeToRestoreBackup": "Would you like to restore the backup '{name}'?", "WouldYouLikeToRestoreBackup": "Would you like to restore the backup '{name}'?",
"XmlRpcPath": "XML RPC Path",
"Year": "Year", "Year": "Year",
"YesCancel": "Yes, Cancel", "YesCancel": "Yes, Cancel",
"YesMoveFiles": "Yes, Move the Files", "YesMoveFiles": "Yes, Move the Files",