1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-18 16:24:34 -04:00

Compare commits

...

36 Commits

Author SHA1 Message Date
Qstick
0584d7676c Bump FFProbe and Newtonsoft 2023-01-02 22:02:50 -06:00
Weblate
09c42530ec Translated using Weblate (Dutch) [skip ci]
Currently translated at 95.4% (1102 of 1155 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 94.3% (1090 of 1155 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.9% (1154 of 1155 strings)

Translated using Weblate (Ukrainian) [skip ci]

Currently translated at 94.4% (1091 of 1155 strings)

Co-authored-by: Davide Palma <github@davidepalma.it>
Co-authored-by: Iagocds <cdsiago@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: andrey4korop <andrey999@i.ua>
Co-authored-by: lhquark <lhquark@gmail.com>
Co-authored-by: vyruz1986 <alex.goris@fastlikehell.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2023-01-02 21:47:27 -06:00
Mark McDowall
0697d694e0 New: Improved messaging when qBittorrent fails due to host header rejection
(cherry picked from commit 48b4cc5f3ffa0cb8eea6748db9091267216cef4f)
2023-01-02 21:46:10 -06:00
Mark McDowall
e085f6af8a Fixed: Multiple pushed releases will be processed sequentially
(cherry picked from commit 1f8e1cf582f59fe1e8dcc0fad15afeed6d9cd9d1)
2023-01-02 17:59:43 -06:00
Colin Gagnaire
7feda1c446 New: Add support for native Freebox Download Client
(cherry picked from commit fb76c237bfbb8aa43bcdd9ce34d90ea843011cee)
2022-12-27 21:20:33 +00:00
Winter
e1f83c205d Bump MonoTorrent to 2.0.7 2022-12-24 14:48:08 -06:00
Weblate
db00edd266 Translated using Weblate (Bengali) [skip ci]
Currently translated at 0.4% (5 of 1155 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.3% (1147 of 1155 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Finnish) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Dutch) [skip ci]

Currently translated at 95.4% (1102 of 1155 strings)

Translated using Weblate (Finnish) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 94.3% (1090 of 1155 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.3% (1148 of 1155 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 93.9% (1085 of 1155 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 93.9% (1085 of 1155 strings)

Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Frank van den Bosch <frank@fbtech.nl>
Co-authored-by: Freelf <freelf.me@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Mipiaceanutella <remix-polity-0l@icloud.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: benniblot <ben2004engler@gmail.com>
Co-authored-by: deepserket <deepserket@gmail.com>
Co-authored-by: hidaba <nag@hidaba.com>
Co-authored-by: ningxia <xianing7105@163.com>
Co-authored-by: saambd <me@salimrahman.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/bn/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2022-12-22 20:34:42 -06:00
Mark McDowall
d699f61f5d Fixed: Prevent unexpected data breaking Series Import
(cherry picked from commit b8714d80a1ede761042ab469110edf552a74ac6b)
2022-12-22 20:33:30 -06:00
Mark McDowall
dc1b478f2c Fixed: Only log /proc/mounts exception once per process
(cherry picked from commit ce0388ca99b7f89bd9e8971777a7995c4361d268)
2022-12-22 20:32:43 -06:00
erri120
0ca665c903 New: Parse Open Matte as Edition
To make James Cameron happy.
2022-12-18 10:36:50 -06:00
Mark McDowall
111c6a743f New: Rename Emby to Emby / Jellyfin
(cherry picked from commit ee1ee8f267079e18015829065a76a628929cf4b2)
2022-12-17 18:18:52 +00:00
Qstick
d3517532a4 Update README for DigitalOcean attribution
[common]
2022-12-17 11:31:47 -06:00
Qstick
5790ebc558 Bump version to 4.3.2 2022-12-11 19:03:47 -06:00
Mark McDowall
c11f72c098 New: IPv6 support for connections/indexers/download clients
Closes #7850

(cherry picked from commit 1b90fbcf7df2c1086da4791c6491771924b1b7aa)
2022-12-10 12:05:55 -06:00
Mark McDowall
3617bef54b Fixed: Improve Bind Address validation and help text
Closes #7849

(cherry picked from commit 6bdeafcf8c78e145595f52e885356be1210abe91)
2022-12-10 12:04:04 -06:00
Zak Saunders
a5fb01f1e6 New: Auto theme option to match OS theme
Co-authored-by: Qstick <qstick@gmail.com>
(cherry picked from commit 4ca5a213fa0fc29ed93e7e31b080728d6fa7f1f3)
2022-12-09 22:11:03 -06:00
Qstick
fa6acb7497 Simplify X-Forwarded-For handling
This happens in asp.net middleware now

Co-Authored-By: ta264 <ta264@users.noreply.github.com>

(cherry picked from commit 16e2d130e6a2e7239bcfe92187a7f990f93eff00)
2022-12-09 22:05:50 -06:00
Qstick
904259df92 New: Improve IPAddress.IsLocal method
Co-Authored-By: ta264 <ta264@users.noreply.github.com>

(cherry picked from commit fd98a179ab6fed8037c99344b34593aac24a0ac0)
2022-12-09 22:05:32 -06:00
Qstick
65c316bd6d Fixed: Smb paths fail on Kodi update
Fixes #7854
2022-12-09 22:04:50 -06:00
Qstick
3b46a08606 Fix PendingRelease Tests 2022-12-05 22:12:34 -06:00
Servarr
6ad49373d4 Automated API Docs update 2022-12-05 21:52:13 -06:00
Qstick
2a1f57c085 Fixed: Sending Webhook on upgrade if media info is unavailable
Fixes #7838

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2022-12-05 21:34:00 -06:00
Qstick
9d9065fbcd API Updates
Fixes #7833
Fixes #6785
Fixes #6787

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2022-12-05 21:34:00 -06:00
Qstick
694940452c Fixed: Loading queue when there are pending items that were added before upgrading
Fixes #7823

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2022-12-05 21:34:00 -06:00
Qstick
f5d6a79998 Fixed: Grab/remove queue actions not showing spinner
Fixes #7821
2022-12-05 21:34:00 -06:00
Qstick
4cc98a10a0 Fixed: Use route Id for PUT requests if not passed in body
Fixes #7809
2022-12-05 21:34:00 -06:00
Qstick
1751bd1a58 Fixed: Correct Attribute compare for Id validation
(cherry picked from commit 7e48ea0231272ae56c30f5f43339f0dca7a27fb3)
2022-12-05 21:20:31 -06:00
Weblate
c0caf65b69 Translated using Weblate (Dutch) [skip ci]
Currently translated at 95.2% (1100 of 1155 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Robin Flikkema <robin@robinflikkema.nl>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translation: Servarr/Radarr
2022-12-02 21:13:27 -06:00
Davo1624
cd889872de Properly parse H.265 4k as 4k quality (#7812)
* Update en.json

* Properly parse H265 for 4k quality

`.WEB-DL.4K.H265.AAC` parses properly for 4k quality
`.WEB-DL.4K.H.265.AAC` parses improperly as 480p
2022-11-29 22:00:36 -06:00
Servarr
6366e335bc Automated API Docs update 2022-11-29 21:57:01 -06:00
Qstick
41f10d098e Don't block task queue for queued update task when there are longer running tasks
Fixes #7538

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2022-11-29 21:44:03 -06:00
Qstick
b2c1698097 Fixed: False Positive HC in some cases
Fixes #7785
2022-11-29 20:52:23 -06:00
Mark McDowall
ed20487f30 Fixed: Handle Flood reporting errors for completed and stopped downloads
(cherry picked from commit f2b2eb69a3e8b271535bd92dc2f5cbfd45664daf)
2022-11-28 21:25:34 -06:00
Bruno Garcia
d1235adfc4 Sentry SDK v3.23.1
Co-authored-by: Bruno Garcia <bruno@Brunos-MacBook-Pro.local>
(cherry picked from commit de3cb07c57d762084c983336aa01b761a8e4b74a)
2022-11-28 21:30:06 +00:00
Qstick
561993e30c Fixed: Parse multiple languages for two letter cases
Fixes #7783
2022-11-25 19:16:29 -06:00
Weblate
14f8f89634 Translated using Weblate (Czech) [skip ci]
Currently translated at 90.9% (1051 of 1155 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zalhera <tobias.bechen@gmail.com>
Co-authored-by: marapavelka <mara.pavelka@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translation: Servarr/Radarr
2022-11-24 18:40:38 -06:00
71 changed files with 2833 additions and 233 deletions

View File

@@ -76,6 +76,15 @@ Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrai
* [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
* [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
## DigitalOcean
This project is also supported by DigitalOcean
<p>
<a href="https://www.digitalocean.com/">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
### License
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)

View File

@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '4.3.1'
majorVersion: '4.3.2'
minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)'

View File

@@ -75,13 +75,23 @@ class Queue extends Component {
return;
}
const nextState = {};
if (prevProps.items !== items) {
nextState.items = items;
}
const selectedIds = this.getSelectedIds();
const isPendingSelected = _.some(this.props.items, (item) => {
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
});
if (isPendingSelected !== this.state.isPendingSelected) {
this.setState({ isPendingSelected });
nextState.isPendingSelected = isPendingSelected;
}
if (!_.isEmpty(nextState)) {
this.setState(nextState);
}
}
@@ -214,26 +224,29 @@ class Queue extends Component {
<PageContentBody>
{
isRefreshing && !isAllPopulated &&
<LoadingIndicator />
isRefreshing && !isAllPopulated ?
<LoadingIndicator /> :
null
}
{
!isRefreshing && hasError &&
!isRefreshing && hasError ?
<div>
{translate('FailedToLoadQueue')}
</div>
</div> :
null
}
{
isAllPopulated && !hasError && !items.length &&
isAllPopulated && !hasError && !items.length ?
<div>
{translate('QueueIsEmpty')}
</div>
</div> :
null
}
{
isAllPopulated && !hasError && !!items.length &&
isAllPopulated && !hasError && !!items.length ?
<div>
<Table
columns={columns}
@@ -268,7 +281,8 @@ class Queue extends Component {
isFetching={isRefreshing}
{...otherProps}
/>
</div>
</div> :
null
}
</PageContentBody>

View File

@@ -225,13 +225,19 @@ class ImportMovieFooter extends Component {
body={
<ul>
{
importError.responseJSON.map((error, index) => {
return (
<li key={index}>
{error.errorMessage}
</li>
);
})
Array.isArray(importError.responseJSON) ?
importError.responseJSON.map((error, index) => {
return (
<li key={index}>
{error.errorMessage}
</li>
);
}) :
<li>
{
JSON.stringify(importError.responseJSON)
}
</li>
}
</ul>
}

View File

@@ -152,13 +152,19 @@ class ImportMovieSelectFolder extends Component {
<ul>
{
saveError.responseJSON.map((e, index) => {
return (
<li key={index}>
{e.errorMessage}
</li>
);
})
Array.isArray(saveError.responseJSON) ?
saveError.responseJSON.map((e, index) => {
return (
<li key={index}>
{e.errorMessage}
</li>
);
}) :
<li>
{
JSON.stringify(saveError.responseJSON)
}
</li>
}
</ul>
</Alert> :

View File

@@ -1,7 +1,11 @@
import * as dark from './dark';
import * as light from './light';
const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const auto = defaultDark ? { ...dark } : { ...light };
export default {
auto,
light,
dark
};

View File

@@ -0,0 +1,25 @@
using System.Globalization;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Test.ExtensionTests.StringExtensionTests
{
[TestFixture]
public class IsValidIPAddressFixture
{
[TestCase("192.168.0.1")]
[TestCase("::1")]
[TestCase("2001:db8:4006:812::200e")]
public void should_validate_ip_address(string input)
{
input.IsValidIpAddress().Should().BeTrue();
}
[TestCase("sonarr.tv")]
public void should_not_parse_non_ip_address(string input)
{
input.IsValidIpAddress().Should().BeFalse();
}
}
}

View File

@@ -1,4 +1,4 @@
using FluentAssertions;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Test.Common;
@@ -10,6 +10,7 @@ namespace NzbDrone.Common.Test.Http
[TestCase("abc://my_host.com:8080/root/api/")]
[TestCase("abc://my_host.com:8080//root/api/")]
[TestCase("abc://my_host.com:8080/root//api/")]
[TestCase("abc://[::1]:8080/root//api/")]
public void should_parse(string uri)
{
var newUri = new HttpUri(uri);

View File

@@ -7,34 +7,50 @@ namespace NzbDrone.Common.Extensions
{
public static bool IsLocalAddress(this IPAddress ipAddress)
{
if (ipAddress.IsIPv6LinkLocal)
// Map back to IPv4 if mapped to IPv6, for example "::ffff:1.2.3.4" to "1.2.3.4".
if (ipAddress.IsIPv4MappedToIPv6)
{
return true;
ipAddress = ipAddress.MapToIPv4();
}
// Checks loopback ranges for both IPv4 and IPv6.
if (IPAddress.IsLoopback(ipAddress))
{
return true;
}
// IPv4
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
byte[] bytes = ipAddress.GetAddressBytes();
switch (bytes[0])
{
case 10:
case 127:
return true;
case 172:
return bytes[1] < 32 && bytes[1] >= 16;
case 192:
return bytes[1] == 168;
default:
return false;
}
return IsLocalIPv4(ipAddress.GetAddressBytes());
}
// IPv6
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
return ipAddress.IsIPv6LinkLocal ||
ipAddress.IsIPv6UniqueLocal ||
ipAddress.IsIPv6SiteLocal;
}
return false;
}
private static bool IsLocalIPv4(byte[] ipv4Bytes)
{
// Link local (no IP assigned by DHCP): 169.254.0.0 to 169.254.255.255 (169.254.0.0/16)
bool IsLinkLocal() => ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254;
// Class A private range: 10.0.0.0 10.255.255.255 (10.0.0.0/8)
bool IsClassA() => ipv4Bytes[0] == 10;
// Class B private range: 172.16.0.0 172.31.255.255 (172.16.0.0/12)
bool IsClassB() => ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31;
// Class C private range: 192.168.0.0 192.168.255.255 (192.168.0.0/16)
bool IsClassC() => ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168;
return IsLinkLocal() || IsClassA() || IsClassC() || IsClassB();
}
}
}

View File

@@ -2,6 +2,8 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
@@ -192,5 +194,30 @@ namespace NzbDrone.Common.Extensions
.Replace("'", "%27")
.Replace("%7E", "~");
}
public static bool IsValidIpAddress(this string value)
{
if (!IPAddress.TryParse(value, out var parsedAddress))
{
return false;
}
if (parsedAddress.Equals(IPAddress.Parse("255.255.255.255")))
{
return false;
}
if (parsedAddress.IsIPv6Multicast)
{
return false;
}
return parsedAddress.AddressFamily == AddressFamily.InterNetwork || parsedAddress.AddressFamily == AddressFamily.InterNetworkV6;
}
public static string ToUrlHost(this string input)
{
return input.Contains(":") ? $"[{input}]" : input;
}
}
}

View File

@@ -8,7 +8,7 @@ namespace NzbDrone.Common.Http
{
public class HttpUri : IEquatable<HttpUri>
{
private static readonly Regex RegexUri = new Regex(@"^(?:(?<scheme>[a-z]+):)?(?://(?<host>[-_A-Z0-9.]+)(?::(?<port>[0-9]{1,5}))?)?(?<path>(?:(?:(?<=^)|/+)[^/?#\r\n]+)+/*|/+)?(?:\?(?<query>[^#\r\n]*))?(?:\#(?<fragment>.*))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex RegexUri = new Regex(@"^(?:(?<scheme>[a-z]+):)?(?://(?<host>[-_A-Z0-9.]+|\[[[A-F0-9:]+\])(?::(?<port>[0-9]{1,5}))?)?(?<path>(?:(?:(?<=^)|/+)[^/?#\r\n]+)+/*|/+)?(?:\?(?<query>[^#\r\n]*))?(?:\#(?<fragment>.*))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly string _uri;
public string FullUri => _uri;
@@ -70,6 +70,8 @@ namespace NzbDrone.Common.Http
private void Parse()
{
var parseSuccess = Uri.TryCreate(_uri, UriKind.RelativeOrAbsolute, out var uri);
var match = RegexUri.Match(_uri);
var scheme = match.Groups["scheme"];
@@ -79,7 +81,7 @@ namespace NzbDrone.Common.Http
var query = match.Groups["query"];
var fragment = match.Groups["fragment"];
if (!match.Success || (scheme.Success && !host.Success && path.Success))
if (!parseSuccess || (scheme.Success && !host.Success && path.Success))
{
throw new ArgumentException("Uri didn't match expected pattern: " + _uri);
}

View File

@@ -11,26 +11,41 @@ namespace NzbDrone.Common.Instrumentation.Sentry
{
try
{
sentryEvent.Message.Message = CleanseLogMessage.Cleanse(sentryEvent.Message.Message);
if (sentryEvent.Message is not null)
{
sentryEvent.Message.Formatted = CleanseLogMessage.Cleanse(sentryEvent.Message.Formatted);
sentryEvent.Message.Message = CleanseLogMessage.Cleanse(sentryEvent.Message.Message);
sentryEvent.Message.Params = sentryEvent.Message.Params?.Select(x => CleanseLogMessage.Cleanse(x switch
{
string str => str,
_ => x.ToString()
})).ToList();
}
if (sentryEvent.Fingerprint != null)
if (sentryEvent.Fingerprint.Any())
{
var fingerprint = sentryEvent.Fingerprint.Select(x => CleanseLogMessage.Cleanse(x)).ToList();
sentryEvent.SetFingerprint(fingerprint);
}
if (sentryEvent.Extra != null)
if (sentryEvent.Extra.Any())
{
var extras = sentryEvent.Extra.ToDictionary(x => x.Key, y => (object)CleanseLogMessage.Cleanse((string)y.Value));
var extras = sentryEvent.Extra.ToDictionary(x => x.Key, y => (object)CleanseLogMessage.Cleanse(y.Value as string));
sentryEvent.SetExtras(extras);
}
foreach (var exception in sentryEvent.SentryExceptions)
if (sentryEvent.SentryExceptions is not null)
{
exception.Value = CleanseLogMessage.Cleanse(exception.Value);
foreach (var frame in exception.Stacktrace.Frames)
foreach (var exception in sentryEvent.SentryExceptions)
{
frame.FileName = ShortenPath(frame.FileName);
exception.Value = CleanseLogMessage.Cleanse(exception.Value);
if (exception.Stacktrace is not null)
{
foreach (var frame in exception.Stacktrace.Frames)
{
frame.FileName = ShortenPath(frame.FileName);
}
}
}
}
}

View File

@@ -99,9 +99,6 @@ namespace NzbDrone.Common.Instrumentation.Sentry
o.Dsn = dsn;
o.AttachStacktrace = true;
o.MaxBreadcrumbs = 200;
o.SendDefaultPii = false;
o.Debug = false;
o.DiagnosticLevel = SentryLevel.Debug;
o.Release = BuildInfo.Release;
o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);

View File

@@ -7,10 +7,10 @@
<PackageReference Include="DryIoc.dll" Version="5.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
<PackageReference Include="Sentry" Version="3.20.1" />
<PackageReference Include="Sentry" Version="3.23.1" />
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="System.Text.Json" Version="6.0.5" />

View File

@@ -0,0 +1,367 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Download.Clients.FreeboxDownload;
using NzbDrone.Core.Download.Clients.FreeboxDownload.Responses;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.FreeboxDownloadTests
{
[TestFixture]
public class TorrentFreeboxDownloadFixture : DownloadClientFixtureBase<TorrentFreeboxDownload>
{
protected FreeboxDownloadSettings _settings;
protected FreeboxDownloadConfiguration _downloadConfiguration;
protected FreeboxDownloadTask _task;
protected string _defaultDestination = @"/some/path";
protected string _encodedDefaultDestination = "L3NvbWUvcGF0aA==";
protected string _category = "somecat";
protected string _encodedDefaultDestinationAndCategory = "L3NvbWUvcGF0aC9zb21lY2F0";
protected string _destinationDirectory = @"/path/to/media";
protected string _encodedDestinationDirectory = "L3BhdGgvdG8vbWVkaWE=";
protected OsPath _physicalPath = new OsPath("/mnt/sdb1/mydata");
protected string _downloadURL => "magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcad53426&dn=download";
[SetUp]
public void Setup()
{
Subject.Definition = new DownloadClientDefinition();
_settings = new FreeboxDownloadSettings()
{
Host = "127.0.0.1",
Port = 443,
ApiUrl = "/api/v1/",
AppId = "someid",
AppToken = "S0mEv3RY1oN9T0k3n"
};
Subject.Definition.Settings = _settings;
_downloadConfiguration = new FreeboxDownloadConfiguration()
{
DownloadDirectory = _encodedDefaultDestination
};
_task = new FreeboxDownloadTask()
{
Id = "id0",
Name = "name",
DownloadDirectory = "L3NvbWUvcGF0aA==",
InfoHash = "HASH",
QueuePosition = 1,
Status = FreeboxDownloadTaskStatus.Unknown,
Eta = 0,
Error = "none",
Type = FreeboxDownloadTaskType.Bt.ToString(),
IoPriority = FreeboxDownloadTaskIoPriority.Normal.ToString(),
StopRatio = 150,
PieceLength = 125,
CreatedTimestamp = 1665261599,
Size = 1000,
ReceivedPrct = 0,
ReceivedBytes = 0,
ReceivedRate = 0,
TransmittedPrct = 0,
TransmittedBytes = 0,
TransmittedRate = 0,
};
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
}
protected void GivenCategory()
{
_settings.Category = _category;
}
protected void GivenDestinationDirectory()
{
_settings.DestinationDirectory = _destinationDirectory;
}
protected virtual void GivenDownloadConfiguration()
{
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.GetDownloadConfiguration(It.IsAny<FreeboxDownloadSettings>()))
.Returns(_downloadConfiguration);
}
protected virtual void GivenTasks(List<FreeboxDownloadTask> torrents)
{
if (torrents == null)
{
torrents = new List<FreeboxDownloadTask>();
}
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.GetTasks(It.IsAny<FreeboxDownloadSettings>()))
.Returns(torrents);
}
protected void PrepareClientToReturnQueuedItem()
{
_task.Status = FreeboxDownloadTaskStatus.Queued;
GivenTasks(new List<FreeboxDownloadTask>
{
_task
});
}
protected void GivenSuccessfulDownload()
{
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[1000]));
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()))
.Callback(PrepareClientToReturnQueuedItem);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.AddTaskFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()))
.Callback(PrepareClientToReturnQueuedItem);
}
protected override RemoteMovie CreateRemoteMovie()
{
var movie = base.CreateRemoteMovie();
movie.Release.DownloadUrl = _downloadURL;
return movie;
}
[Test]
public void Download_with_DestinationDirectory_should_force_directory()
{
GivenDestinationDirectory();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), _encodedDestinationDirectory, It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[Test]
public void Download_with_Category_should_force_directory()
{
GivenDownloadConfiguration();
GivenCategory();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), _encodedDefaultDestinationAndCategory, It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[Test]
public void Download_without_DestinationDirectory_and_Category_should_use_default()
{
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), _encodedDefaultDestination, It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[TestCase(false, false)]
[TestCase(true, true)]
public void Download_should_pause_torrent_as_expected(bool addPausedSetting, bool toBePausedFlag)
{
_settings.AddPaused = addPausedSetting;
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), toBePausedFlag, It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[TestCase(0, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.First, true)]
[TestCase(0, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.First, true)]
[TestCase(0, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.Last, false)]
[TestCase(0, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.Last, false)]
[TestCase(22, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.First, true)]
[TestCase(22, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.First, false)]
[TestCase(22, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.Last, true)]
[TestCase(22, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.Last, false)]
public void Download_should_queue_torrent_first_as_expected(int ageDay, int olderPriority, int recentPriority, bool toBeQueuedFirstFlag)
{
_settings.OlderPriority = olderPriority;
_settings.RecentPriority = recentPriority;
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
remoteMovie.Movie.MovieMetadata.Value.PhysicalRelease = DateTime.UtcNow.AddDays(-ageDay);
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), toBeQueuedFirstFlag, It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[TestCase(0, 0)]
[TestCase(1.5, 150)]
public void Download_should_define_seed_ratio_as_expected(double? providerSeedRatio, double? expectedSeedRatio)
{
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
remoteMovie.SeedConfiguration = new TorrentSeedConfiguration();
remoteMovie.SeedConfiguration.Ratio = providerSeedRatio;
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>(), expectedSeedRatio, It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[Test]
public void GetItems_should_return_empty_list_if_no_tasks_available()
{
GivenTasks(new List<FreeboxDownloadTask>());
Subject.GetItems().Should().BeEmpty();
}
[Test]
public void GetItems_should_return_ignore_tasks_of_unknown_type()
{
_task.Status = FreeboxDownloadTaskStatus.Done;
_task.Type = "toto";
GivenTasks(new List<FreeboxDownloadTask> { _task });
Subject.GetItems().Should().BeEmpty();
}
[Test]
public void GetItems_when_destinationdirectory_is_set_should_ignore_downloads_in_wrong_folder()
{
_settings.DestinationDirectory = @"/some/path/that/will/not/match";
_task.Status = FreeboxDownloadTaskStatus.Done;
GivenTasks(new List<FreeboxDownloadTask> { _task });
Subject.GetItems().Should().BeEmpty();
}
[Test]
public void GetItems_when_category_is_set_should_ignore_downloads_in_wrong_folder()
{
_settings.Category = "somecategory";
_task.Status = FreeboxDownloadTaskStatus.Done;
GivenTasks(new List<FreeboxDownloadTask> { _task });
Subject.GetItems().Should().BeEmpty();
}
[TestCase(FreeboxDownloadTaskStatus.Downloading, false, false)]
[TestCase(FreeboxDownloadTaskStatus.Done, true, true)]
[TestCase(FreeboxDownloadTaskStatus.Seeding, false, false)]
[TestCase(FreeboxDownloadTaskStatus.Stopped, false, false)]
public void GetItems_should_return_canBeMoved_and_canBeDeleted_as_expected(FreeboxDownloadTaskStatus apiStatus, bool canMoveFilesExpected, bool canBeRemovedExpected)
{
_task.Status = apiStatus;
GivenTasks(new List<FreeboxDownloadTask>() { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().CanBeRemoved.Should().Be(canBeRemovedExpected);
items.First().CanMoveFiles.Should().Be(canMoveFilesExpected);
}
[TestCase(FreeboxDownloadTaskStatus.Stopped, DownloadItemStatus.Paused)]
[TestCase(FreeboxDownloadTaskStatus.Stopping, DownloadItemStatus.Paused)]
[TestCase(FreeboxDownloadTaskStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(FreeboxDownloadTaskStatus.Starting, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Retry, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Checking, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Error, DownloadItemStatus.Warning)]
[TestCase(FreeboxDownloadTaskStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(FreeboxDownloadTaskStatus.Done, DownloadItemStatus.Completed)]
[TestCase(FreeboxDownloadTaskStatus.Unknown, DownloadItemStatus.Downloading)]
public void GetItems_should_return_item_as_downloadItemStatus(FreeboxDownloadTaskStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_task.Status = apiStatus;
GivenTasks(new List<FreeboxDownloadTask>() { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().Status.Should().Be(expectedItemStatus);
}
[Test]
public void GetItems_should_return_decoded_destination_directory()
{
var decodedDownloadDirectory = "/that/the/path";
_task.Status = FreeboxDownloadTaskStatus.Done;
_task.DownloadDirectory = "L3RoYXQvdGhlL3BhdGg=";
GivenTasks(new List<FreeboxDownloadTask> { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().OutputPath.Should().Be(decodedDownloadDirectory);
}
[Test]
public void GetItems_should_return_message_if_tasks_in_error()
{
_task.Status = FreeboxDownloadTaskStatus.Error;
_task.Error = "internal";
GivenTasks(new List<FreeboxDownloadTask> { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().Message.Should().Be("Internal error.");
items.First().Status.Should().Be(DownloadItemStatus.Warning);
}
}
}

View File

@@ -89,6 +89,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
.With(h => h.Title = title)
.With(h => h.Release = release)
.With(h => h.Reason = reason)
.With(h => h.ParsedMovieInfo = _parsedMovieInfo)
.Build();
_heldReleases.AddRange(heldReleases);

View File

@@ -52,6 +52,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
_pending.Add(new PendingRelease
{
Id = id,
Title = "Movie.Title.2020.720p-Radarr",
ParsedMovieInfo = new ParsedMovieInfo { MovieTitles = new List<string> { title }, Year = year },
MovieId = _movie.Id
});

View File

@@ -93,6 +93,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
.With(h => h.MovieId = _movie.Id)
.With(h => h.Title = title)
.With(h => h.Release = release)
.With(h => h.ParsedMovieInfo = _parsedMovieInfo)
.Build();
Mocker.GetMock<IPendingReleaseRepository>()

View File

@@ -49,6 +49,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie.Title.1990.Ultimate.Rekall.Edition.NORDiC.REMUX.1080p.BluRay.AVC.DTS-HD.MA5.1-TWA", "Ultimate Rekall Edition")]
[TestCase("Movie.Title.1971.Signature.Edition.1080p.BluRay.FLAC.2.0.x264-TDD", "Signature Edition")]
[TestCase("Movie.1979.The.Imperial.Edition.BluRay.720p.DTS.x264-CtrlHD", "Imperial Edition")]
[TestCase("Movie.1997.Open.Matte.1080p.BluRay.x264.DTS-FGT", "Open Matte")]
public void should_parse_edition(string postTitle, string edition)
{
var parsed = Parser.Parser.ParseMovieTitle(postTitle);

View File

@@ -455,6 +455,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie.Title.2016.1080p.KORSUBS.WEBRip.x264.AAC2.0-RADARR", "KORSUBS")]
[TestCase("Movie Title 2017 HC 720p HDRiP DD5 1 x264-LEGi0N", "Generic Hardcoded Subs")]
[TestCase("Movie.Title.2017.720p.SUBBED.HDRip.V2.XViD-26k.avi", "Generic Hardcoded Subs")]
[TestCase("Movie.Title.2000.1080p.BlueRay.x264.DTS.RoSubbed-playHD", null)]
[TestCase("Movie Title! 2018 [Web][MKV][h264][480p][AAC 2.0][Softsubs]", null)]
[TestCase("Movie Title! 2019 [HorribleSubs][Web][MKV][h264][848x480][AAC 2.0][Softsubs(HorribleSubs)]", null)]
public void should_parse_hardcoded_subs(string postTitle, string sub)

View File

@@ -198,7 +198,7 @@ namespace NzbDrone.Core.Configuration
public string LogLevel => GetValue("LogLevel", "info").ToLowerInvariant();
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
public string Theme => GetValue("Theme", "light", persist: false);
public string Theme => GetValue("Theme", "auto", persist: false);
public string PostgresHost => _postgresOptions?.Host ?? GetValue("PostgresHost", string.Empty, persist: false);
public string PostgresUser => _postgresOptions?.User ?? GetValue("PostgresUser", string.Empty, persist: false);
public string PostgresPassword => _postgresOptions?.Password ?? GetValue("PostgresPassword", string.Empty, persist: false);

View File

@@ -132,22 +132,22 @@ namespace NzbDrone.Core.Download.Clients.Flood
item.RemainingTime = TimeSpan.FromSeconds(properties.Eta);
}
if (properties.Status.Contains("error"))
{
item.Status = DownloadItemStatus.Warning;
}
else if (properties.Status.Contains("seeding") || properties.Status.Contains("complete"))
if (properties.Status.Contains("seeding") || properties.Status.Contains("complete"))
{
item.Status = DownloadItemStatus.Completed;
}
else if (properties.Status.Contains("downloading"))
{
item.Status = DownloadItemStatus.Downloading;
}
else if (properties.Status.Contains("stopped"))
{
item.Status = DownloadItemStatus.Paused;
}
else if (properties.Status.Contains("error"))
{
item.Status = DownloadItemStatus.Warning;
}
else if (properties.Status.Contains("downloading"))
{
item.Status = DownloadItemStatus.Downloading;
}
if (item.Status == DownloadItemStatus.Completed)
{

View File

@@ -0,0 +1,27 @@
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public static class EncodingForBase64
{
public static string EncodeBase64(this string text)
{
if (text == null)
{
return null;
}
byte[] textAsBytes = System.Text.Encoding.UTF8.GetBytes(text);
return System.Convert.ToBase64String(textAsBytes);
}
public static string DecodeBase64(this string encodedText)
{
if (encodedText == null)
{
return null;
}
byte[] textAsBytes = System.Convert.FromBase64String(encodedText);
return System.Text.Encoding.UTF8.GetString(textAsBytes);
}
}
}

View File

@@ -0,0 +1,10 @@
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public class FreeboxDownloadException : DownloadClientException
{
public FreeboxDownloadException(string message)
: base(message)
{
}
}
}

View File

@@ -0,0 +1,8 @@
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public enum FreeboxDownloadPriority
{
Last = 0,
First = 1
}
}

View File

@@ -0,0 +1,277 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Download.Clients.FreeboxDownload.Responses;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public interface IFreeboxDownloadProxy
{
void Authenticate(FreeboxDownloadSettings settings);
string AddTaskFromUrl(string url, string directory, bool addPaused, bool addFirst, double? seedRatio, FreeboxDownloadSettings settings);
string AddTaskFromFile(string fileName, byte[] fileContent, string directory, bool addPaused, bool addFirst, double? seedRatio, FreeboxDownloadSettings settings);
void DeleteTask(string id, bool deleteData, FreeboxDownloadSettings settings);
FreeboxDownloadConfiguration GetDownloadConfiguration(FreeboxDownloadSettings settings);
List<FreeboxDownloadTask> GetTasks(FreeboxDownloadSettings settings);
}
public class FreeboxDownloadProxy : IFreeboxDownloadProxy
{
private readonly IHttpClient _httpClient;
private readonly Logger _logger;
private ICached<string> _authSessionTokenCache;
public FreeboxDownloadProxy(ICacheManager cacheManager, IHttpClient httpClient, Logger logger)
{
_httpClient = httpClient;
_logger = logger;
_authSessionTokenCache = cacheManager.GetCache<string>(GetType(), "authSessionToken");
}
public void Authenticate(FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/login").Build();
var response = ProcessRequest<FreeboxLogin>(request, settings);
if (response.Result.LoggedIn == false)
{
throw new DownloadClientAuthenticationException("Not logged");
}
}
public string AddTaskFromUrl(string url, string directory, bool addPaused, bool addFirst, double? seedRatio, FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/downloads/add").Post();
request.Headers.ContentType = "application/x-www-form-urlencoded";
request.AddFormParameter("download_url", System.Web.HttpUtility.UrlPathEncode(url));
if (!directory.IsNullOrWhiteSpace())
{
request.AddFormParameter("download_dir", directory);
}
var response = ProcessRequest<FreeboxDownloadTask>(request.Build(), settings);
SetTorrentSettings(response.Result.Id, addPaused, addFirst, seedRatio, settings);
return response.Result.Id;
}
public string AddTaskFromFile(string fileName, byte[] fileContent, string directory, bool addPaused, bool addFirst, double? seedRatio, FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/downloads/add").Post();
request.AddFormUpload("download_file", fileName, fileContent, "multipart/form-data");
if (directory.IsNotNullOrWhiteSpace())
{
request.AddFormParameter("download_dir", directory);
}
var response = ProcessRequest<FreeboxDownloadTask>(request.Build(), settings);
SetTorrentSettings(response.Result.Id, addPaused, addFirst, seedRatio, settings);
return response.Result.Id;
}
public void DeleteTask(string id, bool deleteData, FreeboxDownloadSettings settings)
{
var uri = "/downloads/" + id;
if (deleteData == true)
{
uri += "/erase";
}
var request = BuildRequest(settings).Resource(uri).Build();
request.Method = HttpMethod.Delete;
ProcessRequest<string>(request, settings);
}
public FreeboxDownloadConfiguration GetDownloadConfiguration(FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/downloads/config/").Build();
return ProcessRequest<FreeboxDownloadConfiguration>(request, settings).Result;
}
public List<FreeboxDownloadTask> GetTasks(FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/downloads/").Build();
return ProcessRequest<List<FreeboxDownloadTask>>(request, settings).Result;
}
private static string BuildCachedHeaderKey(FreeboxDownloadSettings settings)
{
return $"{settings.Host}:{settings.AppId}:{settings.AppToken}";
}
private void SetTorrentSettings(string id, bool addPaused, bool addFirst, double? seedRatio, FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/downloads/" + id).Build();
request.Method = HttpMethod.Put;
var body = new Dictionary<string, object> { };
if (addPaused)
{
body.Add("status", FreeboxDownloadTaskStatus.Stopped.ToString().ToLower());
}
if (addFirst)
{
body.Add("queue_pos", "1");
}
if (seedRatio != null)
{
// 0 means unlimited seeding
body.Add("stop_ratio", seedRatio);
}
if (body.Count == 0)
{
return;
}
request.SetContent(body.ToJson());
ProcessRequest<FreeboxDownloadTask>(request, settings);
}
private string GetSessionToken(HttpRequestBuilder requestBuilder, FreeboxDownloadSettings settings, bool force = false)
{
var sessionToken = _authSessionTokenCache.Find(BuildCachedHeaderKey(settings));
if (sessionToken == null || force)
{
_authSessionTokenCache.Remove(BuildCachedHeaderKey(settings));
_logger.Debug($"Client needs a new Session Token to reach the API with App ID '{settings.AppId}'");
// Obtaining a Session Token (from official documentation):
// To protect the app_token secret, it will never be used directly to authenticate the
// application, instead the API will provide a challenge the app will combine to its
// app_token to open a session and get a session_token.
// The validity of the session_token is limited in time and the app will have to renew
// this session_token once in a while.
// Retrieving the 'challenge' value (it changes frequently and have a limited time validity)
// needed to build password
var challengeRequest = requestBuilder.Resource("/login").Build();
challengeRequest.Method = HttpMethod.Get;
var challenge = ProcessRequest<FreeboxLogin>(challengeRequest, settings).Result.Challenge;
// The password is computed using the 'challenge' value and the 'app_token' ('App Token' setting)
var enc = System.Text.Encoding.ASCII;
var hmac = new HMACSHA1(enc.GetBytes(settings.AppToken));
hmac.Initialize();
var buffer = enc.GetBytes(challenge);
var password = System.BitConverter.ToString(hmac.ComputeHash(buffer)).Replace("-", "").ToLower();
// Both 'app_id' ('App ID' setting) and computed password are set to get a Session Token
var sessionRequest = requestBuilder.Resource("/login/session").Post().Build();
var body = new Dictionary<string, object>
{
{ "app_id", settings.AppId },
{ "password", password }
};
sessionRequest.SetContent(body.ToJson());
sessionToken = ProcessRequest<FreeboxLogin>(sessionRequest, settings).Result.SessionToken;
_authSessionTokenCache.Set(BuildCachedHeaderKey(settings), sessionToken);
_logger.Debug($"New Session Token stored in cache for App ID '{settings.AppId}', ready to reach API");
}
return sessionToken;
}
private HttpRequestBuilder BuildRequest(FreeboxDownloadSettings settings, bool authentication = true)
{
var requestBuilder = new HttpRequestBuilder(settings.UseSsl, settings.Host, settings.Port, settings.ApiUrl)
{
LogResponseContent = true
};
requestBuilder.Headers.ContentType = "application/json";
if (authentication == true)
{
requestBuilder.SetHeader("X-Fbx-App-Auth", GetSessionToken(requestBuilder, settings));
}
return requestBuilder;
}
private FreeboxResponse<T> ProcessRequest<T>(HttpRequest request, FreeboxDownloadSettings settings)
{
request.LogResponseContent = true;
request.SuppressHttpError = true;
HttpResponse response;
try
{
response = _httpClient.Execute(request);
}
catch (HttpRequestException ex)
{
throw new DownloadClientUnavailableException($"Unable to reach Freebox API. Verify 'Host', 'Port' or 'Use SSL' settings. (Error: {ex.Message})", ex);
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Unable to connect to Freebox API, please check your settings", ex);
}
if (response.StatusCode == HttpStatusCode.Forbidden || response.StatusCode == HttpStatusCode.Unauthorized)
{
_authSessionTokenCache.Remove(BuildCachedHeaderKey(settings));
var responseContent = Json.Deserialize<FreeboxResponse<FreeboxLogin>>(response.Content);
var msg = $"Authentication to Freebox API failed. Reason: {responseContent.GetErrorDescription()}";
_logger.Error(msg);
throw new DownloadClientAuthenticationException(msg);
}
else if (response.StatusCode == HttpStatusCode.NotFound)
{
throw new FreeboxDownloadException("Unable to reach Freebox API. Verify 'API URL' setting for base URL and version.");
}
else if (response.StatusCode == HttpStatusCode.OK)
{
var responseContent = Json.Deserialize<FreeboxResponse<T>>(response.Content);
if (responseContent.Success)
{
return responseContent;
}
else
{
var msg = $"Freebox API returned error: {responseContent.GetErrorDescription()}";
_logger.Error(msg);
throw new DownloadClientException(msg);
}
}
else
{
throw new DownloadClientException("Unable to connect to Freebox, please check your settings.");
}
}
}
}

View File

@@ -0,0 +1,87 @@
using System.Text.RegularExpressions;
using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
using NzbDrone.Core.Validation.Paths;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public class FreeboxDownloadSettingsValidator : AbstractValidator<FreeboxDownloadSettings>
{
public FreeboxDownloadSettingsValidator()
{
RuleFor(c => c.Host).ValidHost();
RuleFor(c => c.Port).InclusiveBetween(1, 65535);
RuleFor(c => c.ApiUrl).NotEmpty()
.WithMessage("'API URL' must not be empty.");
RuleFor(c => c.ApiUrl).ValidUrlBase();
RuleFor(c => c.AppId).NotEmpty()
.WithMessage("'App ID' must not be empty.");
RuleFor(c => c.AppToken).NotEmpty()
.WithMessage("'App Token' must not be empty.");
RuleFor(c => c.Category).Matches(@"^\.?[-a-z]*$", RegexOptions.IgnoreCase)
.WithMessage("Allowed characters a-z and -");
RuleFor(c => c.DestinationDirectory).IsValidPath()
.When(c => c.DestinationDirectory.IsNotNullOrWhiteSpace());
RuleFor(c => c.DestinationDirectory).Empty()
.When(c => c.Category.IsNotNullOrWhiteSpace())
.WithMessage("Cannot use 'Category' and 'Destination Directory' at the same time.");
RuleFor(c => c.Category).Empty()
.When(c => c.DestinationDirectory.IsNotNullOrWhiteSpace())
.WithMessage("Cannot use 'Category' and 'Destination Directory' at the same time.");
}
}
public class FreeboxDownloadSettings : IProviderConfig
{
private static readonly FreeboxDownloadSettingsValidator Validator = new FreeboxDownloadSettingsValidator();
public FreeboxDownloadSettings()
{
Host = "mafreebox.freebox.fr";
Port = 443;
UseSsl = true;
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)")]
public string Host { get; set; }
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox, HelpText = "Port used to access Freebox interface, defaults to '443'")]
public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secured connection when connecting to Freebox API")]
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/'")]
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')")]
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')")]
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")]
public string DestinationDirectory { get; set; }
[FieldDefinition(7, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads (will create a [category] subdirectory in the output directory)")]
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")]
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")]
public int OlderPriority { get; set; }
[FieldDefinition(10, Label = "Add Paused", Type = FieldType.Checkbox)]
public bool AddPaused { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
}

View File

@@ -0,0 +1,21 @@
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload.Responses
{
public class FreeboxDownloadConfiguration
{
[JsonProperty(PropertyName = "download_dir")]
public string DownloadDirectory { get; set; }
public string DecodedDownloadDirectory
{
get
{
return DownloadDirectory.DecodeBase64();
}
set
{
DownloadDirectory = value.EncodeBase64();
}
}
}
}

View File

@@ -0,0 +1,137 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using NzbDrone.Common.Serializer;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload.Responses
{
public enum FreeboxDownloadTaskType
{
Bt,
Nzb,
Http,
Ftp
}
public enum FreeboxDownloadTaskStatus
{
Unknown,
Stopped,
Queued,
Starting,
Downloading,
Stopping,
Error,
Done,
Checking,
Repairing,
Extracting,
Seeding,
Retry
}
public enum FreeboxDownloadTaskIoPriority
{
Low,
Normal,
High
}
public class FreeboxDownloadTask
{
private static readonly Dictionary<string, string> Descriptions;
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "download_dir")]
public string DownloadDirectory { get; set; }
public string DecodedDownloadDirectory
{
get
{
return DownloadDirectory.DecodeBase64();
}
set
{
DownloadDirectory = value.EncodeBase64();
}
}
[JsonProperty(PropertyName = "info_hash")]
public string InfoHash { get; set; }
[JsonProperty(PropertyName = "queue_pos")]
public int QueuePosition { get; set; }
[JsonConverter(typeof(UnderscoreStringEnumConverter), FreeboxDownloadTaskStatus.Unknown)]
public FreeboxDownloadTaskStatus Status { get; set; }
[JsonProperty(PropertyName = "eta")]
public long Eta { get; set; }
[JsonProperty(PropertyName = "error")]
public string Error { get; set; }
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[JsonProperty(PropertyName = "io_priority")]
public string IoPriority { get; set; }
[JsonProperty(PropertyName = "stop_ratio")]
public long StopRatio { get; set; }
[JsonProperty(PropertyName = "piece_length")]
public long PieceLength { get; set; }
[JsonProperty(PropertyName = "created_ts")]
public long CreatedTimestamp { get; set; }
[JsonProperty(PropertyName = "size")]
public long Size { get; set; }
[JsonProperty(PropertyName = "rx_pct")]
public long ReceivedPrct { get; set; }
[JsonProperty(PropertyName = "rx_bytes")]
public long ReceivedBytes { get; set; }
[JsonProperty(PropertyName = "rx_rate")]
public long ReceivedRate { get; set; }
[JsonProperty(PropertyName = "tx_pct")]
public long TransmittedPrct { get; set; }
[JsonProperty(PropertyName = "tx_bytes")]
public long TransmittedBytes { get; set; }
[JsonProperty(PropertyName = "tx_rate")]
public long TransmittedRate { get; set; }
static FreeboxDownloadTask()
{
Descriptions = new Dictionary<string, string>
{
{ "internal", "Internal error." },
{ "disk_full", "The disk is full." },
{ "unknown", "Unknown error." },
{ "parse_error", "Parse error." },
{ "unknown_host", "Unknown host." },
{ "timeout", "Timeout." },
{ "bad_authentication", "Invalid credentials." },
{ "connection_refused", "Remote host refused connection." },
{ "bt_tracker_error", "Unable to announce on tracker." },
{ "bt_missing_files", "Missing torrent files." },
{ "bt_file_error", "Error accessing torrent files." },
{ "missing_ctx_file", "Error accessing task context file." },
{ "nzb_no_group", "Cannot find the requested group on server." },
{ "nzb_not_found", "Article not fount on the server." },
{ "nzb_invalid_crc", "Invalid article CRC." },
{ "nzb_invalid_size", "Invalid article size." },
{ "nzb_invalid_filename", "Invalid filename." },
{ "nzb_open_failed", "Error opening." },
{ "nzb_write_failed", "Error writing." },
{ "nzb_missing_size", "Missing article size." },
{ "nzb_decode_error", "Article decoding error." },
{ "nzb_missing_segments", "Missing article segments." },
{ "nzb_error", "Other nzb error." },
{ "nzb_authentication_required", "Nzb server need authentication." }
};
}
public string GetErrorDescription()
{
if (Descriptions.ContainsKey(Error))
{
return Descriptions[Error];
}
return $"{Error} - Unknown error";
}
}
}

View File

@@ -0,0 +1,18 @@
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload.Responses
{
public class FreeboxLogin
{
[JsonProperty(PropertyName = "logged_in")]
public bool LoggedIn { get; set; }
[JsonProperty(PropertyName = "challenge")]
public string Challenge { get; set; }
[JsonProperty(PropertyName = "password_salt")]
public string PasswordSalt { get; set; }
[JsonProperty(PropertyName = "password_set")]
public bool PasswordSet { get; set; }
[JsonProperty(PropertyName = "session_token")]
public string SessionToken { get; set; }
}
}

View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload.Responses
{
public class FreeboxResponse<T>
{
private static readonly Dictionary<string, string> Descriptions;
[JsonProperty(PropertyName = "success")]
public bool Success { get; set; }
[JsonProperty(PropertyName = "msg")]
public string Message { get; set; }
[JsonProperty(PropertyName = "error_code")]
public string ErrorCode { get; set; }
[JsonProperty(PropertyName = "result")]
public T Result { get; set; }
static FreeboxResponse()
{
Descriptions = new Dictionary<string, string>
{
// Common errors
{ "invalid_request", "Your request is invalid." },
{ "invalid_api_version", "Invalid API base url or unknown API version." },
{ "internal_error", "Internal error." },
// Login API errors
{ "auth_required", "Invalid session token, or no session token sent." },
{ "invalid_token", "The app token you are trying to use is invalid or has been revoked." },
{ "pending_token", "The app token you are trying to use has not been validated by user yet." },
{ "insufficient_rights", "Your app permissions does not allow accessing this API." },
{ "denied_from_external_ip", "You are trying to get an app_token from a remote IP." },
{ "ratelimited", "Too many auth error have been made from your IP." },
{ "new_apps_denied", "New application token request has been disabled." },
{ "apps_denied", "API access from apps has been disabled." },
// Download API errors
{ "task_not_found", "No task was found with the given id." },
{ "invalid_operation", "Attempt to perform an invalid operation." },
{ "invalid_file", "Error with the download file (invalid format ?)." },
{ "invalid_url", "URL is invalid." },
{ "not_implemented", "Method not implemented." },
{ "out_of_memory", "No more memory available to perform the requested action." },
{ "invalid_task_type", "The task type is invalid." },
{ "hibernating", "The downloader is hibernating." },
{ "need_bt_stopped_done", "This action is only valid for Bittorrent task in stopped or done state." },
{ "bt_tracker_not_found", "Attempt to access an invalid tracker object." },
{ "too_many_tasks", "Too many tasks." },
{ "invalid_address", "Invalid peer address." },
{ "port_conflict", "Port conflict when setting config." },
{ "invalid_priority", "Invalid priority." },
{ "ctx_file_error", "Failed to initialize task context file (need to check disk)." },
{ "exists", "Same task already exists." },
{ "port_outside_range", "Incoming port is not available for this customer." }
};
}
public string GetErrorDescription()
{
if (Descriptions.ContainsKey(ErrorCode))
{
return Descriptions[ErrorCode];
}
return $"{ErrorCode} - Unknown error";
}
}
}

View File

@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download.Clients.FreeboxDownload.Responses;
using NzbDrone.Core.Download.Clients.QBittorrent;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public class TorrentFreeboxDownload : TorrentClientBase<FreeboxDownloadSettings>
{
private readonly IFreeboxDownloadProxy _proxy;
public TorrentFreeboxDownload(IFreeboxDownloadProxy proxy,
ITorrentFileInfoReader torrentFileInfoReader,
IHttpClient httpClient,
IConfigService configService,
INamingConfigService namingConfigService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
ICacheManager cacheManager,
Logger logger)
: base(torrentFileInfoReader, httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger)
{
_proxy = proxy;
}
public override string Name => "Freebox Download";
protected IEnumerable<FreeboxDownloadTask> GetTorrents()
{
return _proxy.GetTasks(Settings).Where(v => v.Type.ToLower() == FreeboxDownloadTaskType.Bt.ToString().ToLower());
}
public override IEnumerable<DownloadClientItem> GetItems()
{
var torrents = GetTorrents();
var queueItems = new List<DownloadClientItem>();
foreach (var torrent in torrents)
{
var outputPath = new OsPath(torrent.DecodedDownloadDirectory);
if (Settings.DestinationDirectory.IsNotNullOrWhiteSpace())
{
if (!new OsPath(Settings.DestinationDirectory).Contains(outputPath))
{
continue;
}
}
if (Settings.Category.IsNotNullOrWhiteSpace())
{
var directories = outputPath.FullPath.Split('\\', '/');
if (!directories.Contains(Settings.Category))
{
continue;
}
}
var item = new DownloadClientItem()
{
DownloadId = torrent.Id,
Category = Settings.Category,
Title = torrent.Name,
TotalSize = torrent.Size,
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
RemainingSize = (long)(torrent.Size * (double)(1 - ((double)torrent.ReceivedPrct / 10000))),
RemainingTime = torrent.Eta <= 0 ? null : TimeSpan.FromSeconds(torrent.Eta),
SeedRatio = torrent.StopRatio <= 0 ? 0 : torrent.StopRatio / 100,
OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, outputPath)
};
switch (torrent.Status)
{
case FreeboxDownloadTaskStatus.Stopped: // task is stopped, can be resumed by setting the status to downloading
case FreeboxDownloadTaskStatus.Stopping: // task is gracefully stopping
item.Status = DownloadItemStatus.Paused;
break;
case FreeboxDownloadTaskStatus.Queued: // task will start when a new download slot is available the queue position is stored in queue_pos attribute
item.Status = DownloadItemStatus.Queued;
break;
case FreeboxDownloadTaskStatus.Starting: // task is preparing to start download
case FreeboxDownloadTaskStatus.Downloading:
case FreeboxDownloadTaskStatus.Retry: // you can set a task status to retry to restart the download task.
case FreeboxDownloadTaskStatus.Checking: // checking data before lauching download.
item.Status = DownloadItemStatus.Downloading;
break;
case FreeboxDownloadTaskStatus.Error: // there was a problem with the download, you can get an error code in the error field
item.Status = DownloadItemStatus.Warning;
item.Message = torrent.GetErrorDescription();
break;
case FreeboxDownloadTaskStatus.Done: // the download is over. For bt you can resume seeding setting the status to seeding if the ratio is not reached yet
case FreeboxDownloadTaskStatus.Seeding: // download is over, the content is Change to being shared to other users. The task will automatically stop once the seed ratio has been reached
item.Status = DownloadItemStatus.Completed;
break;
case FreeboxDownloadTaskStatus.Unknown:
default: // new status in API? default to downloading
item.Message = "Unknown download state: " + torrent.Status;
_logger.Info(item.Message);
item.Status = DownloadItemStatus.Downloading;
break;
}
item.CanBeRemoved = item.CanMoveFiles = torrent.Status == FreeboxDownloadTaskStatus.Done;
queueItems.Add(item);
}
return queueItems;
}
protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
{
return _proxy.AddTaskFromUrl(magnetLink,
GetDownloadDirectory().EncodeBase64(),
ToBePaused(),
ToBeQueuedFirst(remoteMovie),
GetSeedRatio(remoteMovie),
Settings);
}
protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, byte[] fileContent)
{
return _proxy.AddTaskFromFile(filename,
fileContent,
GetDownloadDirectory().EncodeBase64(),
ToBePaused(),
ToBeQueuedFirst(remoteMovie),
GetSeedRatio(remoteMovie),
Settings);
}
public override void RemoveItem(DownloadClientItem item, bool deleteData)
{
_proxy.DeleteTask(item.DownloadId, deleteData, Settings);
}
public override DownloadClientInfo GetStatus()
{
var destDir = GetDownloadDirectory();
return new DownloadClientInfo
{
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "::1" || Settings.Host == "localhost",
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(destDir)) }
};
}
protected override void Test(List<ValidationFailure> failures)
{
try
{
_proxy.Authenticate(Settings);
}
catch (DownloadClientUnavailableException ex)
{
failures.Add(new ValidationFailure("Host", ex.Message));
failures.Add(new ValidationFailure("Port", ex.Message));
}
catch (DownloadClientAuthenticationException ex)
{
failures.Add(new ValidationFailure("AppId", ex.Message));
failures.Add(new ValidationFailure("AppToken", ex.Message));
}
catch (FreeboxDownloadException ex)
{
failures.Add(new ValidationFailure("ApiUrl", ex.Message));
}
}
private string GetDownloadDirectory()
{
if (Settings.DestinationDirectory.IsNotNullOrWhiteSpace())
{
return Settings.DestinationDirectory.TrimEnd('/');
}
var destDir = _proxy.GetDownloadConfiguration(Settings).DecodedDownloadDirectory.TrimEnd('/');
if (Settings.Category.IsNotNullOrWhiteSpace())
{
destDir = $"{destDir}/{Settings.Category}";
}
return destDir;
}
private bool ToBePaused()
{
return Settings.AddPaused;
}
private bool ToBeQueuedFirst(RemoteMovie remoteMovie)
{
var isRecentMovie = remoteMovie.Movie.MovieMetadata.Value.IsRecentMovie;
if ((isRecentMovie && Settings.RecentPriority == (int)FreeboxDownloadPriority.First) ||
(!isRecentMovie && Settings.OlderPriority == (int)FreeboxDownloadPriority.First))
{
return true;
}
return false;
}
private double? GetSeedRatio(RemoteMovie remoteMovie)
{
if (remoteMovie.SeedConfiguration == null || remoteMovie.SeedConfiguration.Ratio == null)
{
return null;
}
return remoteMovie.SeedConfiguration.Ratio.Value * 100;
}
}
}

View File

@@ -45,6 +45,11 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
return true;
}
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new DownloadClientException("Failed to connect to qBittorrent. Check your settings and qBittorrent configuration.", new HttpException(response));
}
if (response.HasHttpError)
{
throw new DownloadClientException("Failed to connect to qBittorrent, check your settings.", new HttpException(response));

View File

@@ -283,6 +283,12 @@ namespace NzbDrone.Core.Download.Pending
return null;
}
// Languages will be empty if added before upgrading to v4, reparsing the languages if they're empty will set it to Unknown or better.
if (release.ParsedMovieInfo.Languages.Empty())
{
release.ParsedMovieInfo.Languages = LanguageParser.ParseLanguages(release.Title);
}
release.RemoteMovie = new RemoteMovie
{
Movie = movie,

View File

@@ -5,5 +5,7 @@ namespace NzbDrone.Core.Download
public class ProcessMonitoredDownloadsCommand : Command
{
public override bool RequiresDiskAccess => true;
public override bool IsLongRunning => true;
}
}

View File

@@ -1,9 +1,11 @@
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Indexers
{
public class RssSyncCommand : Command
{
public override bool SendUpdatesToClient => true;
public override bool IsLongRunning => true;
}
}

View File

@@ -1 +1,7 @@
{}
{
"About": "সম্পর্কিত",
"AcceptConfirmationModal": "নিশ্চিতকরণ মডেল গ্রহণ করুন",
"Actions": "ক্রিয়াকাণ্ড",
"Activity": "কার্যকলাপ",
"Add": "যোগ করুন"
}

View File

@@ -1,9 +1,9 @@
{
"EditDelayProfile": "Upravit profil zpoždění",
"AcceptConfirmationModal": "Přijměte potvrzení Modal",
"AcceptConfirmationModal": "Přijměte potvrzovací modální okno",
"Automatic": "Automatický",
"CertificateValidation": "Ověření certifikátu",
"CertificateValidationHelpText": "Změňte, jak přísné je ověření certifikace HTTPS",
"CertificateValidationHelpText": "Změňte přísnost ověřování certifikátů HTTPS. Neměňte, pokud nerozumíte rizikům.",
"Cast": "Obsazení",
"CheckDownloadClientForDetails": "zkontrolujte stahování klienta pro více informací",
"CheckForFinishedDownloadsInterval": "Zkontrolujte interval dokončených stahování",
@@ -137,7 +137,7 @@
"Ungroup": "Oddělit skupinu",
"Unlimited": "Neomezený",
"UnsavedChanges": "Neuložené změny",
"UpdateMechanismHelpText": "Použijte vestavěný aktualizátor Radarr nebo skript",
"UpdateMechanismHelpText": "Použijte vestavěný aktualizační program Radarr nebo skript",
"UpgradeUntilQuality": "Upgradujte až do kvality",
"UseHardlinksInsteadOfCopy": "Místo kopírování použijte pevné odkazy",
"UsenetDelayTime": "Usenet Zpoždění: {0}",
@@ -157,12 +157,12 @@
"Announced": "Oznámeno",
"AvailabilityDelayHelpText": "Množství času před nebo po dostupném datu pro vyhledání filmu",
"ImportExistingMovies": "Importovat existující filmy",
"AddedToDownloadQueue": "Přidáno do stažené fronty",
"AddedToDownloadQueue": "Přidáno do fronty ke stažení",
"AddNotification": "Přidat oznámení",
"Add": "Přidat",
"AddCustomFormat": "Přidat vlastní formát",
"AddDelayProfile": "Přidat profil zpoždění",
"AddDownloadClient": "Přidat staženého klienta",
"AddDownloadClient": "Přidat klienta pro stahování",
"AddRootFolder": "Přidat kořenovou složku",
"Always": "Vždy",
"AnalyticsEnabledHelpText": "Odesílejte anonymní informace o použití a chybách na servery Radarru. To zahrnuje informace o vašem prohlížeči, které stránky Radarr WebUI používáte, hlášení chyb a také verzi operačního systému a běhového prostředí. Tyto informace použijeme k upřednostnění funkcí a oprav chyb.",
@@ -677,7 +677,7 @@
"IMDb": "IMDb",
"ImportCustomFormat": "Importujte vlastní formát",
"ImportedTo": "Importováno do",
"ImportRootPath": "Namiřte Radarr na složku obsahující všechny vaše filmy, nikoli konkrétní film. např. {0} a ne {1}. Každý film musí být navíc ve své vlastní složce v kořenové složce / složce knihovny.",
"ImportRootPath": "Nasměrujte Radarr na složku obsahující všechny vaše filmy, ne na konkrétní film. Např. {0} a ne {1}. Kromě toho musí být každý film ve vlastní složce v rámci kořenové složky nebo knihovny.",
"ImportTipsMessage": "Několik tipů, jak zajistit bezproblémový import:",
"InCinemasDate": "V kinech",
"InCinemasMsg": "Film je v kinech",
@@ -687,13 +687,13 @@
"IncludeUnmonitored": "Zahrnout Nesledováno",
"ImportMovies": "Importovat filmy",
"IndexerPriority": "Priorita indexování",
"IndexerPriorityHelpText": "Priorita indexování od 1 (nejvyšší) do 50 (nejnižší). Výchozí: 25.",
"IndexerPriorityHelpText": "Priorita indexovacího modulu od 1 (nejvyšší) do 50 (nejnižší). Výchozí: 25. Používá se při získávání verzí jako rozhodující prvek pro jinak stejné verze, Radarr bude stále používat všechny povolené indexovací moduly pro Synchronizaci RSS a vyhledávání",
"IndexerRssHealthCheckNoAvailableIndexers": "Všechny indexery podporující rss jsou dočasně nedostupné kvůli nedávným chybám indexeru",
"IndexerRssHealthCheckNoIndexers": "Nejsou k dispozici žádné indexery se zapnutou synchronizací RSS, Radarr nové verze automaticky nezachytí",
"Indexers": "Indexery",
"IndexerSearchCheckNoAutomaticMessage": "Nejsou k dispozici žádné indexery se zapnutým automatickým vyhledáváním, Radarr neposkytne žádné automatické výsledky hledání",
"IndexerSearchCheckNoAvailableIndexersMessage": "Všechny indexery podporující vyhledávání jsou dočasně nedostupné kvůli nedávným chybám indexeru",
"IndexerSearchCheckNoInteractiveMessage": "Pokud je povoleno interaktivní vyhledávání, nejsou k dispozici žádné indexery, Radarr neposkytne žádné interaktivní výsledky hledání",
"IndexerSearchCheckNoInteractiveMessage": "Při povoleném interaktivním vyhledávání, nejsou dostupné žádné indexovací moduly, Radarr neposkytne žádné interaktivní výsledky hledání",
"IndexerSettings": "Nastavení indexeru",
"IndexerStatusCheckSingleClientMessage": "Indexery nedostupné z důvodu selhání: {0}",
"InstallLatest": "Nainstalujte nejnovější",
@@ -1061,6 +1061,6 @@
"AllCollectionsHiddenDueToFilter": "Všechny filmy jsou skryty kvůli použitému filtru.",
"Collections": "Sbírka",
"MonitorMovies": "Monitorujte film",
"NoCollections": "Nebyly nalezeny žádné filmy. Chcete-li začít, budete chtít přidat nový film nebo importovat některé stávající.",
"NoCollections": "Nebyly nalezeny žádné kolekce, pro začátek budete chtít přidat nový film nebo importovat některé stávající",
"RssSyncHelpText": "Interval v minutách. Nastavením na nulu deaktivujete (tím se zastaví veškeré automatické uvolnění uvolnění)"
}

View File

@@ -329,17 +329,17 @@
"BackupRetentionHelpText": "Automatische Backups, die älter als die Aufbewahrungsfrist sind, werden automatisch gelöscht",
"Backups": "Backups",
"BindAddress": "Adresse binden",
"BindAddressHelpText": "Gültige IPv4 Adresse oder \"*\" für alle Netzwerke",
"BindAddressHelpText": "Gültige IP Adresse oder \"*\" für alle Netzwerke",
"Branch": "Git-Branch",
"BypassProxyForLocalAddresses": "Proxy für lokale Adressen umgehen",
"CertificateValidation": "Zertifikat Validierung",
"CertificateValidationHelpText": "Ändere wie streng die Validierung der HTTPS-Zertifizierung ist. Nicht anpassen, außer du kennst das Risiko.",
"CertificateValidationHelpText": "Ändere wie streng die Validierung der HTTPS-Zertifizierung ist. Ändern Sie nicht wenn Ihnen die Risiken nicht bewusst sind.",
"CertificationCountry": "Zertifizierungs Land",
"ChangeFileDate": "Erstelldatum der Datei anpassen",
"ChangeHasNotBeenSavedYet": "Änderung wurde noch nicht gespeichert",
"CheckForFinishedDownloadsInterval": "Intervall zum prüfen von fertigen Downloads",
"CleanLibraryLevel": "Mediathek aufräumen",
"ClickToChangeLanguage": "Sprache ändern ...",
"ClickToChangeLanguage": "Sprache ändern",
"ClickToChangeQuality": "Hier klicken um die Qualität zu ändern",
"ClientPriority": "Priorität",
"CloneFormatTag": "Format Tag kopieren",
@@ -516,7 +516,7 @@
"ShowTitleHelpText": "Filmtitel unter dem Plakat anzeigen",
"ShowUnknownMovieItems": "Unzugeordente Filmeinträge anzeigen",
"SkipFreeSpaceCheck": "Pürfung des freien Speichers überspringen",
"SkipFreeSpaceCheckWhenImportingHelpText": "Aktiviere diese Option, wenn es nicht möglich ist, den freien Speicherplatz des Stammverzeichnisses für Filme zu erkennen",
"SkipFreeSpaceCheckWhenImportingHelpText": "Aktiviere diese Option, wenn es Radarr nicht möglich ist, den freien Speicherplatz des Stammverzeichnisses für Filme zu erkennen",
"SorryThatMovieCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.",
"SourcePath": "Quellpfad",
"SourceRelativePath": "Relativer Quellpfad",
@@ -531,7 +531,7 @@
"TestAllIndexers": "Alle testen",
"TestAllLists": "Alle testen",
"TimeFormat": "Zeitformat",
"UpdateMechanismHelpText": "Benutze den Build-In Updater oder ein Script",
"UpdateMechanismHelpText": "Benutze Radarr's Build-In Updater oder ein Script",
"UpdateScriptPathHelpText": "Pfad zu einem benutzerdefinierten Skript, das ein extrahiertes Update-Paket übernimmt und den Rest des Update-Prozesses abwickelt",
"UpgradeAllowedHelpText": "Wenn deaktiviert wird die Qualität nicht verbessert",
"Uptime": "Laufzeit",
@@ -979,7 +979,7 @@
"EditCustomFormat": "Eigenes Format bearbeiten",
"DoNotUpgradeAutomatically": "Nicht automatisch upgraden",
"DoNotPrefer": "Nicht bevorzugen",
"DoneEditingGroups": "Gruppen bearbeiten abgechlossen",
"DoneEditingGroups": "Editieren der Gruppen abschließen",
"Donations": "Spenden",
"DockerUpdater": "aktualisiere den Docker Container um das Update zu erhalten",
"Discord": "Discord",
@@ -1063,7 +1063,7 @@
"ImportListMissingRoot": "Fehlendes Stammverzeichnis für Importlist(en): {0}",
"BypassDelayIfHighestQualityHelpText": "Verzögerung ignorieren wenn das Release die höchste aktivierte Qualität des Qualitätsprofils mit dem bevorzugtem Protokoll ist",
"BypassDelayIfHighestQuality": "Ignoriere wenn höchste Qualität",
"TaskUserAgentTooltip": "UserAgent von der App die die API aufgerufen hat",
"TaskUserAgentTooltip": "UserAgent von der App welche die API aufgerufen hat",
"Letterboxd": "Letterboxd",
"From": "von",
"NotificationTriggersHelpText": "Auslöser für diese Benachrichtigung auswählen",
@@ -1146,5 +1146,12 @@
"TotalMovies": "Filme insgesamt",
"ApplicationURL": "Anwendungs-URL",
"ApplicationUrlHelpText": "Die externe URL der Anwendung inklusive http(s)://, Port und URL-Basis",
"PreferredProtocol": "Bevorzugtes Protokoll"
"PreferredProtocol": "Bevorzugtes Protokoll",
"AreYouSureYouWantToResetQualityDefinitions": "Sicher, dass die Qualitätsdefinitionen zurückgesetzt werden sollen?",
"ResetDefinitions": "Definitionen zurücksetzen",
"ResetQualityDefinitions": "Qualitätsdefinitionen zurücksetzen",
"SettingsThemeHelpText": "Anwendungsdesign ändern, das 'Auto' Design passt sich an den Light/Dark-Mode deines Systems an. Inspiriert von Theme.Park",
"ResetTitles": "Titel zurücksetzen",
"ResetTitlesHelpText": "Definitionstitel und Werte zurücksetzen",
"SettingsTheme": "Design"
}

View File

@@ -89,7 +89,7 @@
"Backups": "Backups",
"BeforeUpdate": "Before update",
"BindAddress": "Bind Address",
"BindAddressHelpText": "Valid IPv4 address or '*' for all interfaces",
"BindAddressHelpText": "Valid IP address, localhost or '*' for all interfaces",
"Blocklist": "Blocklist",
"Blocklisted": "Blocklisted",
"BlocklistHelpText": "Prevents Radarr from automatically grabbing this release again",
@@ -928,7 +928,7 @@
"SettingsShowRelativeDates": "Show Relative Dates",
"SettingsShowRelativeDatesHelpText": "Show relative (Today/Yesterday/etc) or absolute dates",
"SettingsTheme": "Theme",
"SettingsThemeHelpText": "Change Application UI Theme, Inspired by Theme.Park",
"SettingsThemeHelpText": "Change Application UI Theme, 'Auto' Theme will use your OS Theme to set Light or Dark mode. Inspired by Theme.Park",
"SettingsTimeFormat": "Time Format",
"SettingsWeekColumnHeader": "Week Column Header",
"SettingsWeekColumnHeaderHelpText": "Shown above each column when week is the active view",

View File

@@ -100,7 +100,7 @@
"BackupIntervalHelpText": "Prowlarrin tietokannan ja asetusten automaattisen varmuuskopioinnin suoritusaikaväli.",
"AppDataDirectory": "AppData-kansio",
"BackupNow": "Varmuuskopioi nyt",
"BindAddressHelpText": "Toimiva IPv4-osoite tai '*' (tähti) kaikille yhteyksille.",
"BindAddressHelpText": "Toimiva IP-osoite, localhost tai '*' (tähti) kaikille yhteyksille.",
"Branch": "Kehityshaara",
"BuiltIn": "Sisäänrakennettu",
"CalendarOptions": "Kalenterin asetukset",
@@ -252,7 +252,7 @@
"Profiles": "Profiilit",
"ProxyType": "Välityspalvelimen tyyppi",
"PtpOldSettingsCheckMessage": "Seuraavat PassThePopcorn-tietolähteet sisältävät vanhentuneita asetuksia, jotka olisi syytä päivittää: {0}",
"QualitiesHelpText": "Listalla ylempänä olevia laatuja painotetaan. Saman ryhmän laadut ovat tasaveroisia. Valitse vain haluamasi laadut.",
"QualitiesHelpText": "Listalla ylempänä olevia laatuja painotetaan enemmän vaikkei niitä ole valittu. Samoissa ryhmissä olevat laadut ovat tasaveroisia. Valitse vain halutut laadut.",
"QualityProfileInUse": "Elokuvaan, listaan tai kokelmaan liitettyä laatuprofiilia ei voida poistaa.",
"QueueIsEmpty": "Jono on tyhjä",
"RadarrCalendarFeed": "Radarr-kalenterisyöte",
@@ -333,7 +333,7 @@
"CreateEmptyMovieFoldersHelpText": "Luo puuttuvat elokuvakansiot levyn tarkistuksen yhteydessä.",
"DeleteDownloadClient": "Poista lataustyökalu",
"ImportHeader": "Lisää Radarriin elokuvia tuomalla olemassa oleva, järjestetty kirjasto.",
"ImportMechanismHealthCheckMessage": "Ota valmiiden latausten käsittely käyttöön",
"ImportMechanismHealthCheckMessage": "Käytä valmiiden latausten käsittelyä",
"MinAvailability": "Pienin saatavuus",
"MovieIsUnmonitored": "Elokuvaa ei valvota",
"MovieNaming": "Elokuvien nimeäminen",
@@ -435,7 +435,7 @@
"StartTypingOrSelectAPathBelow": "Aloita kirjoitus tai valitse sijainti alta",
"StartupDirectory": "Käynnistyskansio",
"System": "Järjestelmä",
"SystemTimeCheckMessage": "Järjestelmän aika on heittä yli vuorokauden verran. Ajoitetut tehtävät eivät luultavasti toimi oikein ennen ajan korjausta.",
"SystemTimeCheckMessage": "Järjestelmän aika on pielessä yli vuorokauden. Ajoitetut tehtävät eivät luultavasti toimi oikein ennen sen korjausta.",
"Posters": "Julisteet",
"PosterSize": "Julisteen koko",
"TagCannotBeDeletedWhileInUse": "Tunnistetta ei voi poistaa, koska se on käytössä",
@@ -653,9 +653,9 @@
"IndexerPriority": "Tietolähteiden painotus",
"IndexerPriorityHelpText": "Tietolähteen painotus: 1 (korkein) - 50 (matalin). Oletusarvo on 25. Käytetään muutoin tasaveroisten julkaisujen sieppauspäätökseen. Kaikkia käytössä olevia tietolähteitä käytetään edelleen RSS-synkronointiin ja hakuun.",
"IndexerRssHealthCheckNoAvailableIndexers": "Kaikki RSS-tietolähteet ovat tilapaisesti tavoittamattomissa viimeaikaisten tietolähdevirheiden vuoksi.",
"IndexerRssHealthCheckNoIndexers": "Yhtään RSS-synkronointia käyttävää tietolähdettä ei ole käytettävissä, eikä Radarr tämän vuoksi sieppaa uusia julkaisuja automaattisesti.",
"IndexerRssHealthCheckNoIndexers": "Yhtään RSS-synkronointia käyttävää tietolähdettä ei ole käytettävissä, eikä uusia julkaisuja sen vuoksi siepata automaattisesti.",
"Indexers": "Tietolähteet",
"IndexerSearchCheckNoAutomaticMessage": "Ei hakemistoja, joissa automaattinen haku on käytössä, Radarr ei tarjoa automaattisia hakutuloksia",
"IndexerSearchCheckNoAutomaticMessage": "Automaattihaussa käytettäviä tietolähteitä ei ole käytettävissä, eikä automaattisia hakutuloksia ole tämän vuoksi saatavilla.",
"IndexerSearchCheckNoAvailableIndexersMessage": "Kaikki hakukelpoiset tietolähteet ovat tilapaisesti tavoittamattomissa viimeaikaisten tietolähdevirheiden vuoksi.",
"IndexerSettings": "Tietolähteiden asetukset",
"IndexerStatusCheckSingleClientMessage": "Tietolähteet eivät ole käytettävissä virheiden vuoksi: {0}",
@@ -750,7 +750,7 @@
"QualityDefinitions": "Laatumääritykset",
"QualityLimitsHelpText": "Rajoitukset suhteutetaan automaattisesti elokuvan kestoon.",
"QualityProfileDeleteConfirm": "Haluatko varmasti poistaa laatuprofiilin {0}",
"QualitySettingsSummary": "Laatumääritykset erilaisia sisältömuotoja ja teidostokokoja varten.",
"QualitySettingsSummary": "Laatumääritykset erilaisia sisältömuotoja ja tiedostokokoja varten.",
"RadarrSupportsAnyIndexer": "Radarr tukee Newznab- ja Torznab-yhteensopivien tietolähteiden ohella myös monia muita alla lueteltuja tietolähteitä.",
"RadarrSupportsCustomConditionsAgainstTheReleasePropertiesBelow": "Radarr tukee mukautettuja ehtoja perustuen julkaisujen alla oleviin ominaisuuksiin.",
"Ratings": "Arviot",
@@ -1146,5 +1146,12 @@
"TotalMovies": "Elokuvia yhteensä",
"ApplicationURL": "Sovelluksen URL-osoite",
"ApplicationUrlHelpText": "Sovelluksen ulkoinen URL-osoite, johon sisältyy http(s)://, portti ja URL-perusta.",
"PreferredProtocol": "Ensisijainen protokolla"
"PreferredProtocol": "Ensisijainen protokolla",
"ResetQualityDefinitions": "Palauta laatumääritykset",
"ResetTitles": "Palauta nimet",
"SettingsTheme": "Teema",
"SettingsThemeHelpText": "Vaihda sovelluksen käyttöliittymän ulkoasua. \"Automaattinen\" vaihtaa vaalean ja tumman tilan käyttöjärjestelmäsi teemaa vastaavaksi. Innoittanut Theme.Park.",
"AreYouSureYouWantToResetQualityDefinitions": "Haluatko varmasti palauttaa laatumääritykset?",
"ResetDefinitions": "Palauta määritykset",
"ResetTitlesHelpText": "Palauta määritysten nimet ja arvot."
}

View File

@@ -159,7 +159,7 @@
"BranchUpdateMechanism": "A külső frissítési mechanizmus által használt ágazat",
"BranchUpdate": "Ágazattípus a Radarr frissítéseihez",
"Branch": "Ágazat",
"BindAddressHelpText": "Érvényes IPv4-cím, vagy „*” minden interfészhez",
"BindAddressHelpText": "Érvényes IP-cím, localhost vagy '*' minden interfészhez",
"BindAddress": "Kapcsolási Cím",
"BeforeUpdate": "Alkalmazásfrissítés előtt",
"Backups": "Biztonsági Mentés",
@@ -838,7 +838,7 @@
"EditCustomFormat": "Egyéni Formátumok szerkesztése",
"DoNotUpgradeAutomatically": "Ne frissítsen automatikusan",
"DoNotPrefer": "Nem preferált",
"DoneEditingGroups": "Kész szerkesztő csoportok",
"DoneEditingGroups": "A csoportok szerkesztése kész",
"Donations": "Adományok",
"DockerUpdater": "A Frissítéshez frissítenie kell a Docker tárolót",
"Discord": "Discord",
@@ -965,7 +965,7 @@
"QueueIsEmpty": "A várakozási sor üres",
"QualityProfileInUse": "A filmhez, listához vagy gyűjteményhez csatolt minőségi profil nem törölhető",
"QualityLimitsHelpText": "A korlátozások automatikusan beállítódnak a film hossza szerint.",
"QualitiesHelpText": "A jobb minőség a listában jobban preferált. Ugyanazon minőségek a csoportban egyenlőek. Csak a bejelölt minőségek a kívánt minőségek",
"QualitiesHelpText": "A listán magasabb minőségek előnyösebbek, még akkor is, ha nincs bejelölve. Ugyanazon csoporton belül a tulajdonságok egyenlőek. Csak ellenőrzött minőségek szükségesek",
"Qualities": "Minőségek",
"PrioritySettings": "Prioritás: {0}",
"PreviewRenameHelpText": "Tipp: Átnevezés előnézetéhez... válassza a 'Visszavonást' majd kattintson a film címére és használja a",
@@ -1144,7 +1144,14 @@
"InstanceNameHelpText": "Példánynév a böngésző lapon és a syslog alkalmazás neve",
"RottenTomatoesRating": "Tomato Értékelés",
"TotalMovies": "Összes film",
"ApplicationUrlHelpText": "Az alkalmazás külső URL-címe, beleértve a \"http(s)://\"-t, a \"Portot\" és az \"URL-alapot\" is",
"ApplicationUrlHelpText": "Az alkalmazás külső URL-címe, beleértve a http(s)://-t, a portot és az URL-alapot",
"ApplicationURL": "Alkalmazás URL-je",
"PreferredProtocol": "Preferált protokoll"
"PreferredProtocol": "Preferált protokoll",
"AreYouSureYouWantToResetQualityDefinitions": "Biztos visszaállítod a minőségi definíciókat?",
"SettingsTheme": "Téma",
"SettingsThemeHelpText": "Változtasd meg az alkalmazás felhasználói felület témáját, az „Auto” téma az operációs rendszer témáját használja a Világos vagy Sötét mód beállításához. A Theme.Park ihlette",
"ResetDefinitions": "Definíciók visszaállítása",
"ResetQualityDefinitions": "Állítsd vissza a minőségi meghatározásokat",
"ResetTitles": "Címek visszaállítása",
"ResetTitlesHelpText": "A definíciócímek és értékek visszaállítása"
}

View File

@@ -59,7 +59,7 @@
"AppDataLocationHealthCheckMessage": "L'aggiornamento non sarà possibile per evitare la cancellazione di AppData durante l'aggiornamento",
"Analytics": "Analitica",
"Added": "Aggiunto",
"About": "Informazioni",
"About": "Info",
"Year": "Anno",
"Week": "Settimana",
"Updates": "Aggiornamenti",
@@ -189,7 +189,7 @@
"ChooseAnotherFolder": "Scegli un'altra Cartella",
"Cast": "Cast",
"Calendar": "Calendario",
"BackupNow": "Effettua il Backup adesso",
"BackupNow": "Esegui backup ora",
"Backup": "Backup",
"All": "Tutti",
"Agenda": "Agenda",
@@ -220,12 +220,12 @@
"ChangeFileDate": "Cambiare la Data del File",
"CertificationCountryHelpText": "Seleziona il Paese per le Certificazioni dei Film",
"CertificationCountry": "Paese di Certificazione",
"CertificateValidationHelpText": "Cambia quanto è rigorosa la convalida del certificato HTTPS. Non cambiare a meno che tu non comprenda i rischi.",
"CertificateValidationHelpText": "Cambia quanto rigorosamente vengono validati i certificati HTTPS. Non cambiare senza conoscerne i rischi.",
"CertificateValidation": "Convalida del Certificato",
"Cancel": "Annulla",
"BypassProxyForLocalAddresses": "Evita il Proxy per gli Indirizzi Locali",
"Branch": "Ramo",
"BindAddressHelpText": "Indirizzo IPv4 valido o '*' per tutte le interfacce",
"BindAddressHelpText": "Indirizzo IPV4 valido o '*' per tutte le interfacce di rete",
"BindAddress": "Indirizzo di Bind",
"Backups": "I Backup",
"BackupRetentionHelpText": "I backup automatici più vecchi del periodo di conservazione verranno eliminati automaticamente",
@@ -261,7 +261,7 @@
"TotalSpace": "Spazio Totale",
"Title": "Titolo",
"Time": "Ora",
"TestAll": "Testa Tutti",
"TestAll": "Prova Tutti",
"Test": "Test",
"TableOptionsColumnsMessage": "Scegli quali colonne rendere visibili ed il loro ordine",
"TableOptions": "Opzioni della tabella",
@@ -452,7 +452,7 @@
"RadarrSupportsAnyRSSMovieListsAsWellAsTheOneStatedBelow": "Radarr supporta qualunque Lista di film RSS, cosi come le altre sotto.",
"RadarrSupportsAnyDownloadClient": "Radarr supporta qualunque client di download che usi gli standard Newznab, cosi come gli altri client sotto.",
"QuickImport": "Sposta automaticamente",
"Queued": "Messo in coda",
"Queued": "In coda",
"QualitySettings": "Impostazione di Qualità",
"QualityProfileDeleteConfirm": "Sicuro di voler cancellare il profilo di qualità {0}",
"QualityCutoffHasNotBeenMet": "Il qualità di taglio non è stata raggiunta",
@@ -713,8 +713,8 @@
"TimeFormat": "Formato orario",
"ThisConditionMatchesUsingRegularExpressions": "Questa condizione si applica usando espressione regolari. Nota che i caratteri {0} hanno significati speciali e devono essere evitati con un {1}",
"TestAllLists": "Testa tutte le liste",
"TestAllIndexers": "Testa tutti gli Indicizzatori",
"TestAllClients": "Testa Tutti i Client",
"TestAllIndexers": "Prova tutti gli indicizzatori",
"TestAllClients": "Testa tutti i client",
"TagsHelpText": "Si applica ai film con almeno un tag corrispondente",
"TagIsNotUsedAndCanBeDeleted": "L'etichetta non è in uso e può essere eliminata",
"TagCannotBeDeletedWhileInUse": "Non può essere cancellato mentre è in uso",
@@ -793,7 +793,7 @@
"MovieDetailsPreviousMovie": "Dettagli del film: film precedente",
"FocusSearchBox": "Evidenzia casella di ricerca",
"CloseCurrentModal": "Chiudi la Modale Attuale",
"AcceptConfirmationModal": "Acetta Conferma Modale",
"AcceptConfirmationModal": "Accetta Conferma Modale",
"StartSearchForMissingMovie": "Avvia ricerca film mancanti",
"StartProcessing": "Avvia Lavorazione",
"StartImport": "Avvia Importazione",
@@ -842,7 +842,7 @@
"AddQualityProfile": "Aggiungi Profilo Qualità",
"AddNotification": "Aggiungi Notifica",
"AddedToDownloadQueue": "Aggiunto alla coda di download",
"AddDownloadClient": "Aggiungi Client di Download",
"AddDownloadClient": "Aggiungi Downloader",
"AddCustomFormat": "Aggiungi Formato Personalizzato",
"Add": "Aggiungi",
"ImportLibrary": "Importazione della libreria",
@@ -1091,11 +1091,18 @@
"ClickToChangeReleaseGroup": "Clicca per cambiare gruppo di rilascio",
"DiscordUrlInSlackNotification": "Hai una notifica Discord configurata come notifica Slack. Configurala come notifica Discord per una miglior funzionalità. Le notifiche interessate sono: {0}",
"Duration": "Durata",
"InstanceNameHelpText": "Nome dell'istanza nella scheda e per il nome dell'applicazione Syslog",
"InstanceNameHelpText": "Nome istanza nella scheda e per il nome dell'app nel Syslog",
"InstanceName": "Nome Istanza",
"AllCollectionsHiddenDueToFilter": "Tutti i film sono nascosti a causa del filtro applicato.",
"Collections": "Collezione",
"MonitorMovies": "Monitora Film",
"NoCollections": "Nessun film trovato, per iniziare ti consigliamo di aggiungere un nuovo film o importarne alcuni esistenti.",
"RssSyncHelpText": "Intervallo in minuti. Imposta zero per disabilitarlo (ciò fermerà il recupero automatico di tutte le release)"
"RssSyncHelpText": "Intervallo in minuti. Imposta zero per disabilitarlo (ciò fermerà il recupero automatico di tutte le release)",
"ApplicationURL": "URL Applicazione",
"ApplicationUrlHelpText": "L'URL esterno di questa applicazione, incluso http(s)://, porta e URL base",
"CollectionOptions": "Opzioni della Collezione",
"CollectionShowDetailsHelpText": "Mostra lo stato e le proprietà della collezione",
"Started": "Iniziato",
"AreYouSureYouWantToResetQualityDefinitions": "Sei sicuro di voler ripristinare le definizioni della qualità?",
"ChooseImportMode": "Selezionare Metodo di Importazione"
}

View File

@@ -368,11 +368,11 @@
"ChangeFileDate": "Wijzig Bestandsdatum",
"CertificationCountryHelpText": "Selecteer Land voor Film Certificatie",
"CertificationCountry": "Certificatie Land",
"CertificateValidationHelpText": "Wijzig hoe strikt HTTPS certificaat validatie is",
"CertificateValidationHelpText": "Wijzig hoe strict HTTPS certificaat validatie is. Wijzig dit niet behalve als je de risico's begrijpt.",
"CertificateValidation": "Certificaat Validatie",
"BypassProxyForLocalAddresses": "Omzeil Proxy voor Lokale Adressen",
"Branch": "Branch",
"BindAddressHelpText": "Geldig IPv4 adres of '*' voor alle interfaces",
"BindAddressHelpText": "Geldig IPv4-adres of '*' voor alle interfaces",
"DeleteBackup": "Verwijder Veiligheidskopie",
"BackupIntervalHelpText": "Tussentijd voor automatische back-up",
"Backups": "Veiligheidskopieën",
@@ -806,10 +806,10 @@
"AllMoviesInPathHaveBeenImported": "Alle films in {0} zijn geïmporteerd",
"AllFiles": "Alle bestanden",
"AfterManualRefresh": "Na handmatig verversen",
"AddToDownloadQueue": "Toegevoegd aan download wachtrij",
"AddToDownloadQueue": "Toegevoegd aan downloadwachtrij",
"AddQualityProfile": "Kwaliteitsprofiel toevoegen",
"AddNotification": "Notificatie toevoegen",
"AddedToDownloadQueue": "Aan de download wachtrij toegevoegd",
"AddedToDownloadQueue": "Aan de download wachtrijtoegevoegd",
"AddDownloadClient": "Download Client Toevoegen",
"Add": "Toevoegen",
"ChmodFolderHelpTextWarning": "Dit werkt alleen als de gebruiker die Radarr draait de eigenaar is van het bestand. Het is beter om zeker te zijn dat de downloader de juiste rechten zet.",
@@ -1045,7 +1045,7 @@
"DeleteFileLabel": "Verwijder {0} Film Bestanden",
"UnableToAddRootFolder": "Kon hoofdmappen niet inladen",
"UpdateAvailable": "Nieuwe update is beschikbaar",
"From": "Van",
"From": "van",
"RemotePathMappingCheckDownloadPermissions": "Radarr kan gedownloade film {0} zien, maar niet openen. Waarschijnlijk fout met machtigingen.",
"RemotePathMappingCheckFileRemoved": "Bestand {0} is halverwege de verwerking verwijderd.",
"RemotePathMappingCheckFilesBadDockerPath": "U gebruikt docker; download client {0} gerapporteerde bestanden in {1} maar dit is geen geldig {2} pad. Controleer uw externe padtoewijzingen en download clientinstellingen.",
@@ -1107,5 +1107,14 @@
"Collections": "Collectie",
"MonitorMovies": "Bewaak Film",
"ApplicationURL": "Applicatie URL",
"ApplicationUrlHelpText": "De externe URL van deze applicatie inclusief http(s)://,Port en URL base"
"ApplicationUrlHelpText": "De externe URL van deze applicatie inclusief http(s)://,Port en URL base",
"CollectionsSelectedInterp": "{0} Collectie(s) geselecteerd",
"AreYouSureYouWantToResetQualityDefinitions": "Weet je zeker dat je de kwaliteitsdefinities wilt resetten?",
"ChooseImportMode": "Kies Importmodus",
"CollectionOptions": "Collectieopties",
"CollectionShowDetailsHelpText": "Collectie status en details weergeven",
"CollectionShowOverviewsHelpText": "Collectieoverzicht weergeven",
"EditCollection": "Bewerk collectie",
"BypassDelayIfHighestQualityHelpText": "Vertraging ignoreren wanneer een release de hoogst ingeschakelde kwaliteit heeft in het kwialiteitsprofiel van het geprefereerde protocol",
"CollectionShowPostersHelpText": "Posters van de collectie tonen"
}

View File

@@ -19,7 +19,7 @@
"IndexerStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a falhas",
"IndexersSettingsSummary": "Indexadores e restrições de lançamento",
"IndexerSettings": "Configurações do indexador",
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa interativa ativada, o Radarr não fornecerá nenhum resultado de pesquisa interativa",
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa interativa habilitada, o Radarr não fornecerá nenhum resultado de pesquisa interativo",
"IndexerSearchCheckNoAvailableIndexersMessage": "Todos os indexadores com capacidade de pesquisa estão temporariamente indisponíveis devido a erros recentes do indexador",
"IndexerSearchCheckNoAutomaticMessage": "Nenhum indexador disponível com a Pesquisa automática habilitada, o Radarr não fornecerá nenhum resultado de pesquisa automática",
"Indexers": "Indexadores",
@@ -84,7 +84,7 @@
"HealthNoIssues": "Nenhum problema com sua configuração",
"Health": "Integridade",
"HaveNotAddedMovies": "Você ainda não adicionou nenhum filme, deseja importar alguns ou todos os seus filmes primeiro?",
"HardlinkCopyFiles": "Vínculo real/Copiar arquivos",
"HardlinkCopyFiles": "Hardlink/Copiar arquivos",
"Group": "Grupo",
"GrabSelected": "Obter selecionado",
"GrabReleaseMessageText": "O Radarr não conseguiu determinar a qual filme este lançamento está relacionado. O Radarr pode não conseguir importar automaticamente este lançamento. Quer obter \"{0}\"?",
@@ -174,7 +174,7 @@
"EditListExclusion": "Editar exclusão da lista",
"Edition": "Edição",
"EditIndexer": "Editar indexador",
"EditGroups": "Editar grupos",
"EditGroups": "Editar Grupos",
"EditDelayProfile": "Editar perfil de atraso",
"EditCustomFormat": "Editar formato personalizado",
"Edit": "Editar",
@@ -202,7 +202,7 @@
"DownloadClient": "Cliente de download",
"DoNotUpgradeAutomatically": "Não atualizar automaticamente",
"DoNotPrefer": "Não preferir",
"DoneEditingGroups": "Edição de grupos concluída",
"DoneEditingGroups": "Concluído a Edição de Grupos",
"Donations": "Doações",
"DockerUpdater": "atualizar o contêiner do Docker para receber a atualização",
"Docker": "Docker",
@@ -340,7 +340,7 @@
"BranchUpdateMechanism": "Ramificação usada pelo mecanismo de atualização externo",
"BranchUpdate": "Ramificação para atualização do Radarr",
"Branch": "Ramo",
"BindAddressHelpText": "Endereço IPv4 Válido ou '*' para todas as interfaces",
"BindAddressHelpText": "Endereço IP válido, localhost ou '*' para todas as interfaces",
"BindAddress": "Fixar Endereço",
"BeforeUpdate": "Antes da atualização",
"Backups": "Backups",
@@ -484,7 +484,7 @@
"AddMovies": "Adicionar filmes",
"AddMovie": "Adicionar filme",
"AddListExclusion": "Adicionar exclusão à lista",
"AddList": "Adicionar à Lista",
"AddList": "Adicionar Lista",
"AddingTag": "Adicionando etiqueta",
"AddIndexer": "Adicionar indexador",
"AddImportExclusionHelpText": "Impedir a adição do filme ao Radarr por listas",
@@ -752,7 +752,7 @@
"Restrictions": "Restrições",
"RestoreBackup": "Restaurar backup",
"Restore": "Restaurar",
"RestartRequiredHelpTextWarning": "Requer reinicio para fazer efeito",
"RestartRequiredHelpTextWarning": "Requer reinicialização para ter efeito",
"RestartRadarr": "Reiniciar o Radarr",
"RestartNow": "Reiniciar agora",
"Restart": "Reiniciar",
@@ -854,7 +854,7 @@
"QualityDefinitions": "Definições de qualidade",
"QualityCutoffHasNotBeenMet": "Limite de qualidade não atingido",
"Quality": "Qualidade",
"QualitiesHelpText": "As qualidades mais altas na lista são mais preferidas, mesmo que não estejam marcadas. As qualidades dentro do mesmo grupo são iguais. Apenas qualidades marcadas são desejadas",
"QualitiesHelpText": "As qualidades mais altas na lista são mais preferidas, mesmo que não sejam verificadas. As qualidades dentro do mesmo grupo são iguais. Somente qualidades verificadas são desejadas",
"Qualities": "Qualidades",
"PublishedDate": "Data de publicação",
"PtpOldSettingsCheckMessage": "As configurações dos seguintes indexadores do PassThePopcorn são obsoletas e devem ser atualizadas: {0}",
@@ -868,7 +868,7 @@
"Proxy": "Proxy",
"ProtocolHelpText": "Escolha que protocolo(s) utilizar e qual o preferido ao escolher entre versões iguais",
"Protocol": "Protocolo",
"Proper": "Proper",
"Proper": "Apropriado",
"Progress": "Progresso",
"ProfilesSettingsSummary": "Perfis de qualidade, idioma e atraso",
"Profiles": "Perfis",
@@ -923,7 +923,7 @@
"OnRenameHelpText": "Ao renomear",
"OnRename": "Ao Renomear",
"OnlyUsenet": "Apenas Usenet",
"OnlyTorrent": "Apenas torrents",
"OnlyTorrent": "Apenas Torrents",
"OnLatestVersion": "A versão mais recente do Radarr já está instalada",
"OnImport": "Ao importar",
"OnHealthIssueHelpText": "Ao ter problema de integridade",
@@ -1053,7 +1053,7 @@
"RemotePathMappingCheckImportFailed": "O Radarr não conseguiu importar um filme. Verifique os logs para saber mais.",
"RemotePathMappingCheckFileRemoved": "O arquivo {0} foi removido no meio do processamento.",
"RemotePathMappingCheckDownloadPermissions": "O Radarr pode ver, mas não pode acessar o filme baixado {0}. Provável erro de permissões.",
"RemotePathMappingCheckGenericPermissions": "O cliente de download {0} coloca downloads em {1} mas o Radarr não pode ver este diretório. Pode ser necessário ajustar as permissões da pasta.",
"RemotePathMappingCheckGenericPermissions": "O cliente de download {0} coloca os downloads em {1}, mas o Radarr não pode ver este diretório. Pode ser necessário ajustar as permissões da pasta.",
"RemotePathMappingCheckWrongOSPath": "O cliente de download remoto {0} coloca downloads em {1}, mas este não é um caminho {2} válido. Revise seus mapeamentos de caminho remoto e baixe as configurações do cliente.",
"RemotePathMappingCheckLocalWrongOSPath": "O cliente de download local {0} coloca downloads em {1}, mas este não é um caminho {2} válido. Revise as configurações do seu cliente de download.",
"RemotePathMappingCheckLocalFolderMissing": "O cliente de download remoto {0} coloca downloads em {1}, mas esse diretório parece não existir. Mapeamento de caminho remoto provavelmente ausente ou incorreto.",
@@ -1087,7 +1087,7 @@
"OnApplicationUpdateHelpText": "Na Atualização do Aplicativo",
"DiscordUrlInSlackNotification": "Você tem uma notificação do Discord configurado como uma notificação do Slack. Definir isso como uma notificação do Discord para melhor funcionalidade. Com efeito, notificações são: {0}",
"AnnouncedMsg": "Filme foi anunciado",
"IndexerDownloadClientHelpText": "Especificar em que cliente de download é usado para baixar deste indexador",
"IndexerDownloadClientHelpText": "Especificar qual cliente de download é utilizado para baixar a partir deste indexador",
"LocalPath": "Caminho Local",
"ManualImportSetReleaseGroup": "Importar Manual - Definir Grupo de Lançamento",
"SelectLanguages": "Selecionar Idiomas",
@@ -1144,7 +1144,14 @@
"CollectionShowPostersHelpText": "Mostrar pôsteres de itens da coleção",
"RottenTomatoesRating": "Avaliação Tomato",
"TotalMovies": "Total de Filmes",
"ApplicationURL": "URL da Aplicação",
"ApplicationUrlHelpText": "O URL externa deste aplicativo, incluindo http(s)://, porta e base de URL",
"PreferredProtocol": "Protocolo Preferido"
"ApplicationURL": "URL do Aplicativo",
"ApplicationUrlHelpText": "A URL externa deste aplicativo, incluindo http(s)://, porta e base da URL",
"PreferredProtocol": "Protocolo Preferido",
"SettingsThemeHelpText": "Alterar o tema da interface do usuário do aplicativo, o tema 'Auto' usará o tema do sistema operacional para definir o modo Claro ou Escuro. Inspirado por Theme.Park",
"ResetDefinitions": "Redefinir definições",
"ResetQualityDefinitions": "Redefinir Definições de Qualidade",
"ResetTitles": "Redefinir Títulos",
"ResetTitlesHelpText": "Redefinir títulos de definição, bem como valores",
"SettingsTheme": "Tema",
"AreYouSureYouWantToResetQualityDefinitions": "Tem certeza de que deseja redefinir as definições de qualidade?"
}

View File

@@ -218,5 +218,889 @@
"ImportCustomFormat": "Додати свій формат",
"DeleteRestrictionHelpText": "Ви впевнені, що хочете видалити цей профіль затримки?",
"AllCollectionsHiddenDueToFilter": "Всі фільми заховані відповідно до фільтра.",
"Collections": "Колекція"
"Collections": "Колекція",
"CollectionOptions": "Параметри колекції",
"CollectionShowDetailsHelpText": "Показати статус і властивості колекції",
"CollectionShowOverviewsHelpText": "Показати огляд колекції",
"CollectionShowPostersHelpText": "Показати плакати предметів колекції",
"DeleteList": "Видалити список",
"DeleteMovieFolderHelpText": "Видалити папку з фільмами та її вміст",
"DeleteImportListExclusion": "Видалити виключення зі списку імпорту",
"DeleteIndexer": "Видалити індексатор",
"DeleteMovieFolderLabel": "Видалити папку з фільмами",
"DeleteSelectedMovie": "Видалити вибрані фільм(и)",
"Enable": "Увімкнути",
"EnableAutomaticAdd": "Увімкнути автоматичне додавання",
"EnableAutomaticSearchHelpText": "Використовуватиметься, коли автоматичний пошук виконується через інтерфейс користувача або Radarr",
"Ended": "Завершено",
"Fixed": "Виправлено",
"FocusSearchBox": "Перейти до вікна пошуку",
"Filters": "Фільтри",
"FirstDayOfWeek": "Перший день тижня",
"IgnoreDeletedMovies": "Відключити моніторинг видалених фільмів",
"IgnoredPlaceHolder": "Додайте нове обмеження",
"ImportedTo": "Імпортовано в",
"ImportErrors": "Помилки імпорту",
"Imported": "Імпортні",
"IndexersSettingsSummary": "Індексатори та обмеження випуску",
"LogLevelTraceHelpTextWarning": "Журнал трасування слід увімкнути лише тимчасово",
"LogLevel": "Рівень журналу",
"Monday": "Понеділок",
"MonitoredHelpText": "Завантажте фільм, якщо є",
"MoreDetails": "Детальніше",
"MountCheckMessage": "Монтування, що містить шлях до фільму, монтується лише для читання: ",
"MovieIsRecommend": "Фільм рекомендовано на основі останнього додавання",
"MovieIsOnImportExclusionList": "Фільм у списку винятків для імпорту",
"MultiLanguage": "Багатомовність",
"NoLeaveIt": "Ні, залиште це",
"NoLimitForAnyRuntime": "Немає обмежень для будь-якого часу виконання",
"NoCollections": "Колекції не знайдено. Щоб почати, ви захочете додати новий фільм або імпортувати кілька наявних",
"NoEventsFound": "Подій не знайдено",
"NoHistory": "Без історії",
"None": "Жодного",
"OnHealthIssue": "Про питання здоров'я",
"RemotePathMappingCheckDownloadPermissions": "Radarr може бачити, але не має доступу до завантаженого фільму {0}. Ймовірна помилка дозволів.",
"Remove": "Видалити",
"RemotePathMappingCheckWrongOSPath": "Клієнт віддаленого завантаження {0} розміщує завантаження в {1}, але це недійсний шлях {2}. Перегляньте свої віддалені відображення шляхів і завантажте налаштування клієнта.",
"ReplaceWithSpaceDashSpace": "Замінити на Пробіл Тире Пробіл",
"RequiredHelpText": "Ця умова {0} має збігатися, щоб користувацький формат застосовувався. В іншому випадку достатньо одного збігу {1}.",
"RequiredRestrictionHelpText": "Реліз має містити принаймні один із цих термінів (незалежно від регістру)",
"RequiredRestrictionPlaceHolder": "Додайте нове обмеження",
"Required": "Обов'язковий",
"SelectLanguage": "Оберіть мову",
"UnableToLoadDownloadClients": "Не вдалося завантажити клієнти для завантаження",
"OnApplicationUpdate": "Оновлення програми",
"OutputPath": "Вихідний шлях",
"Overview": "Огляд",
"ProxyType": "Тип проксі",
"RecyclingBin": "Сміттєвий кошик",
"Result": "Результат",
"Retention": "Утримання",
"RetryingDownloadInterp": "Повторна спроба завантажити {0} о {1}",
"Settings": "Налаштування",
"ShowSizeOnDisk": "Показати розмір на диску",
"ShowTitle": "Показати назву",
"ShowStudio": "Показати Студію",
"UnableToLoadCustomFormats": "Не вдалося завантажити спеціальні формати",
"Auto": "Авто",
"Downloading": "Завантаження",
"EnableColorImpairedMode": "Увімкнути режим із порушенням кольору",
"Duration": "Тривалість",
"EnableHelpText": "Увімкнути створення файлу метаданих для цього типу метаданих",
"Error": "Помилка",
"ErrorLoadingContents": "Помилка завантаження вмісту",
"ErrorLoadingPreviews": "Помилка завантаження попереднього перегляду",
"ErrorRestoringBackup": "Помилка відновлення резервної копії",
"Events": "Події",
"FailedLoadingSearchResults": "Не вдалося завантажити результати пошуку, повторіть спробу.",
"FailedToLoadMovieFromAPI": "Не вдалося завантажити фільм із API",
"FileDateHelpText": "Змінити дату файлу під час імпорту/повторного сканування",
"Files": "Файли",
"Filter": "Фільтр",
"Folders": "Папки",
"FollowPerson": "Слідкуйте за особою",
"From": "від",
"HideAdvanced": "Сховати додаткові",
"History": "Історія",
"MinimumAge": "Мінімальний вік",
"MinimumAvailability": "Мінімальна доступність",
"MinimumCustomFormatScore": "Мінімальна оцінка спеціального формату",
"MovieDetailsPreviousMovie": "Відомості про фільм: попередній фільм",
"MovieEditor": "Редактор фільмів",
"PtpOldSettingsCheckMessage": "Наведені нижче індексатори PassThePopcorn мають застарілі налаштування та їх потрібно оновити: {0}",
"ProxyPasswordHelpText": "Вам потрібно лише ввести ім’я користувача та пароль, якщо вони потрібні. В іншому випадку залиште їх порожніми.",
"PublishedDate": "Дата публікації",
"QualityDefinitions": "Визначення якості",
"QualityLimitsHelpText": "Обмеження автоматично регулюються для тривалості фільму.",
"RemotePathMappingCheckFilesGenericPermissions": "Завантажте файли звітів клієнта {0} в {1}, але Radarr не бачить цей каталог. Можливо, вам знадобиться налаштувати дозволи для папки.",
"RescanMovieFolderAfterRefresh": "Перескануйте папку фільму після оновлення",
"Reset": "Скинути",
"ResetAPIKey": "Скинути ключ API",
"Restart": "Перезавантажити",
"RestartRadarr": "Перезавантажити Radarr",
"RestartRequiredHelpTextWarning": "Щоб набуло чинності, потрібно перезапустити",
"RestoreBackup": "Відновлення резервної копії",
"Restrictions": "Обмеження",
"RootFolder": "Коренева папка",
"RootFolderCheckMultipleMessage": "Відсутні кілька кореневих папок: {0}",
"Runtime": "Час виконання",
"Search": "Пошук",
"SearchAll": "Пошук у всіх",
"UsenetDelay": "Затримка Usenet",
"Downloaded": "Завантажено",
"Existing": "Існуючий",
"ImportFailed": "Помилка імпорту: {0}",
"Importing": "Імпорт",
"InCinemas": "У кінотеатрах",
"IncludeRadarrRecommendations": "Включити рекомендації Radarr",
"Indexer": "Індексатор",
"Language": "Мова",
"MetadataSettingsSummary": "Створюйте файли метаданих, коли фільми імпортуються або оновлюються",
"Month": "Місяць",
"NoMoveFilesSelf": " Ні, я сам перенесу файли",
"Sunday": "Неділя",
"Tasks": "Задачі",
"Today": "Сьогодні",
"Tomorrow": "Завтра",
"TorrentDelay": "Затримка торрента",
"OnMovieAddedHelpText": "Фільм додано",
"OnMovieAdded": "У фільмі додано",
"OnMovieFileDelete": "Видаленні відеофайлу",
"OnMovieFileDeleteForUpgrade": "Видалити відеофайл для оновлення",
"OnMovieDelete": "Фільм видалено",
"OnRename": "При перейменуванні",
"OnMovieFileDeleteHelpText": "Видаленні відеофайлу",
"OnRenameHelpText": "При перейменуванні",
"OpenThisModal": "Відкрийте цей модальний вікно",
"PageSize": "Розмір сторінки",
"PageSizeHelpText": "Кількість елементів для показу на кожній сторінці",
"Password": "Пароль",
"Path": "Шлях",
"OriginalTitle": "Оригінальна назва",
"Presets": "Предустановки",
"PreviewRename": "Попередній перегляд Перейменування",
"PreviewRenameHelpText": "Порада: щоб переглянути перейменування... виберіть «Скасувати», потім клацніть назву будь-якого фільму та скористайтеся",
"Priority": "Пріоритет",
"Profiles": "Профілі",
"ProcessingFolders": "Обробка папок",
"Proxy": "Проксі",
"ProxyBypassFilterHelpText": "Використовуйте «,» як роздільник і «*». як символ підстановки для субдоменів",
"ProtocolHelpText": "Виберіть протокол(и) для використання та який із них є кращим під час вибору між однаковими випусками",
"ProxyUsernameHelpText": "Вам потрібно лише ввести ім’я користувача та пароль, якщо вони потрібні. В іншому випадку залиште їх порожніми.",
"QualitySettings": "Налаштування якості",
"QualitySettingsSummary": "Якісні розміри та найменування",
"QualityProfiles": "Профілі якості",
"Queue": "Черга",
"Queued": "У черзі",
"RadarrSupportsAnyIndexer": "Radarr підтримує будь-який індексатор, який використовує стандарт Newznab, а також інші індексатори, перелічені нижче.",
"RadarrSupportsAnyDownloadClient": "Radarr підтримує багато популярних торрент-клієнтів і клієнтів для завантаження через Usenet.",
"RadarrSupportsAnyRSSMovieListsAsWellAsTheOneStatedBelow": "Radarr підтримує будь-які списки фільмів RSS, а також наведений нижче.",
"RecentChanges": "Останні зміни",
"Reason": "Причина",
"RecentFolders": "Останні папки",
"RecycleBinCleanupDaysHelpText": "Встановіть значення 0, щоб вимкнути автоматичне очищення",
"RecyclingBinCleanup": "Очищення сміттєвого кошика",
"RefreshInformationAndScanDisk": "Оновити інформацію та сканувати диск",
"RefreshCollections": "Оновити колекції",
"RefreshLists": "Оновити списки",
"RefreshMonitoredIntervalHelpText": "Як часто оновлювати відстежувані завантаження з клієнтів завантаження, мінімум 1 хвилина",
"ReleaseDates": "Дати випуску",
"ReleasedMsg": "Фільм виходить",
"ReleaseGroup": "Група випуску",
"ReleaseRejected": "Реліз відхилено",
"RemoveFailed": "Не вдалося видалити",
"RemoveDownloadsAlert": "Параметри видалення переміщено до окремих налаштувань клієнта завантаження в таблиці вище.",
"RetentionHelpText": "Лише Usenet: встановіть нуль, щоб налаштувати необмежену утримку",
"Seconds": "Секунди",
"Security": "Безпека",
"SelectMovie": "Виберіть Фільм",
"SetPermissionsLinuxHelpTextWarning": "Якщо ви не впевнені, що ці налаштування роблять, не змінюйте їх.",
"SetTags": "Встановити теги",
"SettingsFirstDayOfWeek": "Перший день тижня",
"SettingsRemotePathMappingRemotePathHelpText": "Кореневий шлях до каталогу, до якого має доступ клієнт завантаження",
"SettingsRuntimeFormat": "Формат виконання",
"SettingsWeekColumnHeader": "Заголовок стовпця тижня",
"SettingsShowRelativeDatesHelpText": "Показати відносні (сьогодні/вчора/тощо) або абсолютні дати",
"ShowMovieInformation": "Показати інформацію про фільм",
"ShowMovieInformationHelpText": "Показати жанри фільмів і сертифікати",
"ShownClickToHide": "Показано, натисніть, щоб приховати",
"ShowOverview": "Показати огляд",
"ShowMonitoredHelpText": "Показати відстежуваний статус під плакатом",
"ShowSearchHelpText": "Показувати кнопку пошуку при наведенні",
"Size": "Розмір",
"StartupDirectory": "Каталог запуску",
"ThisConditionMatchesUsingRegularExpressions": "Ця умова відповідає використанню регулярних виразів. Зауважте, що символи {0} мають особливі значення та потребують екранування за допомогою {1}",
"UILanguage": "Мова інтерфейсу користувача",
"UnableToAddANewCustomFormatPleaseTryAgain": "Не вдалося додати новий спеціальний формат, спробуйте ще раз.",
"UnableToAddANewDownloadClientPleaseTryAgain": "Не вдається додати новий клієнт для завантаження, повторіть спробу.",
"UnableToAddANewIndexerPleaseTryAgain": "Не вдалося додати новий індексатор, спробуйте ще раз.",
"UnableToAddANewListExclusionPleaseTryAgain": "Не вдається додати нове виключення зі списку, повторіть спробу.",
"UnableToAddANewListPleaseTryAgain": "Не вдалося додати новий список, спробуйте ще раз.",
"UnableToLoadIndexers": "Не вдалося завантажити індексатори",
"UnableToLoadQualities": "Неможливо завантажити якості",
"UpperCase": "Великі літери",
"TotalMovies": "Всього фільмів",
"TotalSpace": "Загальний простір",
"UISettings": "Налаштування інтерфейсу користувача",
"UILanguageHelpText": "Мова, яку Radarr використовуватиме для інтерфейсу користувача",
"UILanguageHelpTextWarning": "Потрібно перезавантажити браузер",
"UnableToLoadBackups": "Не вдалося завантажити резервні копії",
"UnableToLoadBlocklist": "Не вдалося завантажити список блокувань",
"UnableToLoadCollections": "Не вдалося завантажити колекції",
"UnableToLoadAltTitle": "Не вдалося завантажити альтернативні назви.",
"UnableToLoadNamingSettings": "Не вдалося завантажити налаштування імен",
"UnableToLoadRestrictions": "Не вдалося завантажити обмеження",
"UnableToLoadTheCalendar": "Неможливо завантажити календар",
"UnableToLoadTags": "Не вдалося завантажити теги",
"Unreleased": "Недоступний",
"UnsavedChanges": "Незбережені зміни",
"Indexers": "Індексатори",
"ListTagsHelpText": "Буде додано елементи списку тегів",
"ListUpdateInterval": "Інтервал оновлення списку",
"LoadingMovieCreditsFailed": "Не вдалося завантажити титри фільму",
"MustContain": "Має містити",
"PreferUsenet": "Віддавайте перевагу Usenet",
"SSLCertPathHelpText": "Шлях до файлу pfx",
"StartProcessing": "Почати обробку",
"Waiting": "Очікування",
"WaitingToImport": "Очікування імпорту",
"WaitingToProcess": "Очікування обробки",
"VisitGithubCustomFormatsAphrodite": "Відвідайте вікі для отримання додаткової інформації: ",
"EnableAutoHelpText": "Якщо ввімкнено, фільми автоматично додаватимуться до Radarr із цього списку",
"Enabled": "Увімкнено",
"EnabledHelpText": "Увімкнути цей список для використання в Radarr",
"EnableMediaInfoHelpText": "Отримайте з файлів інформацію про відео, таку як роздільна здатність, час виконання та кодек. Це вимагає, щоб Radarr читав частини файлу, що може спричинити високу дискову або мережеву активність під час сканування.",
"ExtraFileExtensionsHelpTexts1": "Розділений комами список додаткових файлів для імпорту (.nfo буде імпортовано як .nfo-orig)",
"FileNameTokens": "Маркери імен файлів",
"FolderMoveRenameWarning": "Це також перейменує папку з фільмами відповідно до формату папки з фільмами в налаштуваннях.",
"IndexerSearchCheckNoAutomaticMessage": "Немає доступних індексаторів із увімкненим автоматичним пошуком, Radarr не надаватиме автоматичних результатів пошуку",
"HiddenClickToShow": "Приховано, натисніть, щоб показати",
"HomePage": "Домашня сторінка",
"IgnoredHelpText": "Випуск буде відхилено, якщо він містить один або кілька термінів (незалежно від регістру)",
"ImportExtraFilesHelpText": "Імпортуйте відповідні додаткові файли (субтитри, nfo тощо) після імпортування файлу фільму",
"ImportListMissingRoot": "Відсутня коренева папка для списків імпорту: {0}",
"IncludeRecommendationsHelpText": "Включіть рекомендовані Radarr фільми в режим Discovery",
"IndexerLongTermStatusCheckAllClientMessage": "Усі індексатори недоступні через збої більше 6 годин",
"DestinationRelativePath": "Відносний шлях призначення",
"EditCollection": "Редагувати колекцію",
"ForMoreInformationOnTheIndividualDownloadClients": "Щоб отримати додаткові відомості про окремі клієнти для завантаження, натисніть кнопки додаткових відомостей.",
"IndexerTagHelpText": "Використовуйте цей індексатор лише для фільмів із принаймні одним відповідним тегом. Залиште поле порожнім для використання з усіма фільмами.",
"Info": "Інформація",
"InstanceName": "Ім'я екземпляра",
"InstanceNameHelpText": "Ім’я екземпляра на вкладці та ім’я програми Syslog",
"KeepAndUnmonitorMovie": "Зберегти та скасувати моніторинг фільму",
"LaunchBrowserHelpText": " Відкрийте веб-браузер і перейдіть на домашню сторінку Radarr під час запуску програми.",
"ListSyncLevelHelpText": "Фільми з бібліотеки буде видалено або відстежуватись не буде, якщо їх немає у вашому списку",
"LocalPath": "Місцевий шлях",
"LowerCase": "Малі літери",
"MaximumLimits": "Максимальні обмеження",
"MinimumAgeHelpText": "Тільки Usenet: мінімальний вік NZB у хвилинах до їх захоплення. Використовуйте це, щоб дати новим випускам час для поширення до вашого провайдера usenet.",
"MinimumFreeSpace": "Мінімальний вільний простір",
"MinimumLimits": "Мінімальні обмеження",
"MovieCollectionMissingRoot": "Відсутня коренева папка для колекції фільмів: {0}",
"MoveFolders1": "Бажаєте перемістити папки з фільмами до \"{0}\"?",
"MoveFolders2": "Бажаєте перемістити файли фільму з \"{0}\" до \"{1}\"?",
"MovieFolderFormat": "Формат папки фільму",
"MovieIndexScrollTop": "Індекс фільму: прокрутка вгору",
"MovieInfoLanguageHelpText": "Мова, яку Radarr використовуватиме для інформації про фільм в інтерфейсі користувача",
"MovieIsDownloadingInterp": "Фільм завантажується - {0}% {1}",
"NextExecution": "Наступне виконання",
"OnMovieFileDeleteForUpgradeHelpText": "Видалити відеофайл для оновлення",
"OrganizeModalDisabled": "Перейменування вимкнено, перейменовувати нічого",
"OrganizeModalSuccess": "Успіх! Мою роботу виконано, немає файлів для перейменування.",
"OrganizeSelectedMovies": "Упорядкуйте вибрані фільми",
"PendingChangesMessage": "У вас є незбережені зміни. Ви впевнені, що бажаєте залишити цю сторінку?",
"PosterSize": "Розмір плаката",
"PreferredProtocol": "Переважний протокол",
"PriorityHelpText": "Надайте пріоритет кільком клієнтам завантаження. Round-Robin використовується для клієнтів з однаковим пріоритетом.",
"ProxyCheckBadRequestMessage": "Не вдалося перевірити проксі. Код стану: {0}",
"ProxyCheckResolveIpMessage": "Не вдалося визначити IP-адресу для налаштованого проксі-сервера {0}",
"QualitiesHelpText": "Якості, які стоять вище в списку, є кращими, навіть якщо не позначено. Якості в одній групі рівні. Потрібні тільки перевірені якості",
"QualityProfileInUse": "Неможливо видалити профіль якості, доданий до фільму, списку або колекції",
"RemotePathMappingCheckBadDockerPath": "Ви використовуєте docker; клієнт завантаження {0} розміщує завантаження в {1}, але це недійсний шлях {2}. Перегляньте свої віддалені відображення шляхів і завантажте налаштування клієнта.",
"RemotePathMappingCheckRemoteDownloadClient": "Клієнт віддаленого завантаження {0} повідомив про файли в {1}, але цей каталог, здається, не існує. Ймовірно, відсутнє відображення віддаленого шляху.",
"RemoveSelected": "Видалити вибране",
"ReplaceIllegalCharactersHelpText": "Замінити недопустимі символи. Якщо не позначено, Radarr видалить їх",
"RescanAfterRefreshHelpTextWarning": "Radarr не визначатиме автоматично зміни файлів, якщо не встановлено значення «Завжди»",
"RestartReloadNote": "Примітка: Radarr автоматично перезапуститься та перезавантажить інтерфейс під час процесу відновлення.",
"RottenTomatoesRating": "Рейтинг томатів",
"RssSyncHelpText": "Інтервал у хвилинах. Встановіть нуль, щоб вимкнути (це зупинить автоматичне захоплення звільнення)",
"ScriptPath": "Шлях сценарію",
"SearchFailedPleaseTryAgainLater": "Помилка пошуку, спробуйте пізніше.",
"SearchOnAddCollectionHelpText": "Шукайте фільми в цій колекції після додавання в бібліотеку",
"SelectReleaseGroup": "Виберіть Release Group",
"SendAnonymousUsageData": "Надсилати анонімні дані про використання",
"SettingsEnableColorImpairedMode": "Увімкнути режим із порушенням кольору",
"SettingsEnableColorImpairedModeHelpText": "Змінений стиль, щоб користувачі з вадами кольору могли краще розрізняти кольорову кодовану інформацію",
"SettingsThemeHelpText": "Змініть тему інтерфейсу додатка, тема «Авто» використовуватиме вашу тему ОС, щоб установити світлий або темний режим. Натхненний Theme.Park",
"SettingsWeekColumnHeaderHelpText": "Відображається над кожним стовпцем, коли тиждень є активним переглядом",
"ShowCertification": "Показати сертифікат",
"ShowCollectionDetails": "Показати статус колекції",
"ShowPath": "Показати шлях",
"ShowQualityProfileHelpText": "Покажіть якісний профіль під плакатом",
"ShowTitleHelpText": "Показати назву фільму під постером",
"SkipFreeSpaceCheck": "Пропустити перевірку вільного місця",
"SkipFreeSpaceCheckWhenImportingHelpText": "Використовуйте, коли Radarr не може виявити вільне місце в кореневій папці фільму",
"SqliteVersionCheckUpgradeRequiredMessage": "Наразі встановлена версія SQLite {0} більше не підтримується. Оновіть SQLite принаймні до версії {1}.",
"TheLogLevelDefault": "Рівень журналу за замовчуванням має значення «Інформація», і його можна змінити",
"ThisCannotBeCancelled": "Це не можна скасувати після запуску без вимкнення всіх ваших індексаторів.",
"TorrentDelayHelpText": "Затримка в хвилинах, щоб зачекати, перш ніж захопити торрент",
"UISettingsSummary": "Параметри календаря, дати та кольору",
"UnableToAddANewNotificationPleaseTryAgain": "Не вдалося додати нове сповіщення, спробуйте ще раз.",
"UnableToAddANewRemotePathMappingPleaseTryAgain": "Не вдалося додати нове зіставлення віддаленого шляху, спробуйте ще раз.",
"UnableToLoadDownloadClientOptions": "Не вдалося завантажити параметри клієнта для завантаження",
"UnableToLoadListExclusions": "Неможливо завантажити список винятків",
"UnableToLoadManualImportItems": "Не вдалося завантажити елементи ручного імпорту",
"UnableToLoadMovies": "Неможливо завантажити фільми",
"UnableToLoadQualityProfiles": "Не вдалося завантажити профілі якості",
"UnableToLoadResultsIntSearch": "Неможливо завантажити результати для цього пошуку фільмів. Спробуйте ще раз пізніше",
"Unmonitored": "Неконтрольований",
"UnmonitoredHelpText": "Включайте неконтрольовані фільми в канал iCal",
"IndexerJackettAll": "Індексатори, які використовують непідтримувану кінцеву точку Jackett 'all': {0}",
"UpdateAutomaticallyHelpText": "Автоматичне завантаження та встановлення оновлень. Ви все ще зможете встановити з System: Updates",
"UpdateCheckStartupNotWritableMessage": "Неможливо встановити оновлення, оскільки папка запуску \"{0}\" не може бути записана користувачем \"{1}\".",
"UpdateCheckUINotWritableMessage": "Неможливо встановити оновлення, оскільки папка інтерфейсу користувача \"{0}\" не може бути записана користувачем \"{1}\".",
"UpgradeUntilCustomFormatScore": "Оновлення до оцінки спеціального формату",
"UpgradeUntilThisQualityIsMetOrExceeded": "Оновлюйте, поки ця якість не буде досягнута або перевищена",
"UsenetDelayHelpText": "Затримка в хвилинах, щоб зачекати, перш ніж отримати випуск від Usenet",
"VersionUpdateText": "Встановлено версію Radarr {0}. Щоб отримати останні зміни, потрібно перезавантажити Radarr.",
"WhitelistedHardcodedSubsHelpText": "Встановлені тут теги субтитрів не вважатимуться жорстко закодованими",
"DockerUpdater": "оновіть контейнер docker, щоб отримати оновлення",
"EnableAutomaticSearch": "Увімкнути автоматичний пошук",
"Images": "Зображення",
"IMDb": "IMDb",
"Import": "Імпорт",
"LastUsed": "Останнє використання",
"Lists": "Списки",
"NoVideoFilesFoundSelectedFolder": "У вибраній папці не знайдено відеофайлів",
"Released": "Випуск",
"SourcePath": "Вихідний шлях",
"UnableToLoadUISettings": "Не вдалося завантажити налаштування інтерфейсу користувача",
"WouldYouLikeToRestoreBackup": "Бажаєте відновити резервну копію {0}?",
"IndexerFlags": "Прапори індексатора",
"OnMovieDeleteHelpText": "Фільм видалено",
"TagDetails": "Деталі тегу - {0}",
"DiscordUrlInSlackNotification": "Ви налаштували сповіщення Discord як сповіщення Slack. Налаштуйте це як сповіщення Discord для кращої роботи. Сповіщення, на які впливає: {0}",
"Missing": "Відсутня",
"SettingsTimeFormat": "Формат часу",
"iCalLink": "iCal Link",
"InCinemasDate": "У кінотеатрах Дата",
"IndexerSettings": "Налаштування індексатора",
"List": "Список",
"ListExclusions": "Список виключень",
"ListSettings": "Параметри списку",
"LinkHere": "тут",
"Links": "Посилання",
"ListsSettingsSummary": "Імпорт списків, список виключень",
"OrganizeConfirm": "Ви впевнені, що бажаєте впорядкувати всі файли у {0} вибраних фільмах?",
"OrganizeModalAllPathsRelative": "Усі шляхи відносяться до:",
"Options": "Опції",
"Organize": "Організувати",
"OrganizeAndRename": "Організувати та перейменувати",
"OverviewOptions": "Параметри огляду",
"PackageVersion": "Версія пакета",
"Quality": "Якість",
"Time": "Час",
"UsenetDelayTime": "Затримка Usenet: {0}",
"ChooseImportMode": "Виберіть режим імпорту",
"DeleteHeader": "Видалити - {0}",
"IncludeCustomFormatWhenRenaming": "Включати спеціальний формат під час перейменування",
"IncludeCustomFormatWhenRenamingHelpText": "Включіть у формат перейменування {Custom Formats}",
"MaximumSizeHelpText": "Максимальний розмір випуску для захоплення в МБ. Встановіть нуль, щоб встановити необмежений",
"Ungroup": "Розгрупувати",
"InteractiveImport": "Інтерактивний імпорт",
"Large": "Великий",
"LastDuration": "Остання тривалість",
"Scheduled": "За розкладом",
"UpdateSelected": "Оновити вибране",
"PreferTorrent": "Віддаю перевагу Torrent",
"PrioritySettings": "Пріоритет: {0}",
"ProxyCheckFailedToTestMessage": "Не вдалося перевірити проксі: {0}",
"Qualities": "Якості",
"QualityCutoffHasNotBeenMet": "Порогова якість не досягнута",
"QualityOrLangCutoffHasNotBeenMet": "Обрізання якості чи мови не виконано",
"QualityProfile": "Профіль якості",
"QueueIsEmpty": "Черга порожня",
"QuickImport": "Рухатися автоматично",
"RadarrCalendarFeed": "Стрічка календаря Radarr",
"RadarrSupportsCustomConditionsAgainstTheReleasePropertiesBelow": "Radarr підтримує спеціальні умови щодо наведених нижче властивостей випуску.",
"RadarrTags": "Теги Radar",
"RadarrUpdated": "Radarr оновлено",
"Rating": "Рейтинг",
"Ratings": "Рейтинги",
"Real": "Справжня",
"Refresh": "Оновити",
"RefreshAndScan": "Оновити та сканувати",
"RelativePath": "Відносний шлях",
"SearchForMissing": "Розшук зниклих",
"SearchForMovie": "Пошук фільму",
"SearchMissing": "Пошук відсутній",
"StartImport": "Розпочати імпорт",
"System": "Система",
"AreYouSureYouWantToResetQualityDefinitions": "Ви впевнені, що бажаєте скинути визначення якості?",
"CollectionsSelectedInterp": "Вибрано колекцій: {0}",
"DeleteNotification": "Видалити сповіщення",
"DeleteFileLabel": "Видалити файл фільму {0}",
"DeleteFilesHelpText": "Видалити файли фільмів і папку фільмів",
"DeleteFilesLabel": "Видалити файли фільмів {0}",
"DeleteQualityProfile": "Видалити профіль якості",
"DeleteRestriction": "Видалити обмеження",
"DownloadedAndMonitored": "Завантажено (Відстежується)",
"DownloadClientUnavailable": "Клієнт завантажувача недоступний",
"DownloadedButNotMonitored": "Завантажено (Не відстежується)",
"DownloadFailed": "Помилка завантаження",
"DownloadFailedCheckDownloadClientForMoreDetails": "Помилка завантаження: перевірте клієнт завантаження, щоб дізнатися більше",
"DownloadFailedInterp": "Помилка завантаження: {0}",
"DownloadWarning": "Попередження про завантаження: {0}",
"DownloadWarningCheckDownloadClientForMoreDetails": "Попередження про завантаження: перевірте клієнт завантаження, щоб дізнатися більше",
"Edit": "Редагувати",
"EditGroups": "Редагувати групи",
"EditIndexer": "Редагувати індексатор",
"Edition": "Видання",
"EditMovie": "Редагувати фільм",
"EditMovieFile": "Редагувати файл фільму",
"EditPerson": "Редагувати особу",
"DeleteSelectedMovieFiles": "Видалити вибрані файли фільмів",
"DeleteSelectedMovieFilesMessage": "Ви впевнені, що бажаєте видалити вибрані файли фільмів?",
"DeleteTag": "Видалити тег",
"DeleteTheMovieFolder": "Папку фільму \"{0}\" і весь її вміст буде видалено.",
"DestinationPath": "Шлях призначення",
"DetailedProgressBar": "Детальний індикатор прогресу",
"DetailedProgressBarHelpText": "Показати текст на панелі виконання",
"Details": "Подробиці",
"Donations": "Пожертви",
"EnableCompletedDownloadHandlingHelpText": "Автоматично імпортувати завершені завантаження з клієнта завантажень",
"EnableAutomaticSearchHelpTextWarning": "Буде використано, коли використовується інтерактивний пошук",
"EnableColorImpairedModeHelpText": "Змінений стиль, щоб користувачі з вадами кольору могли краще розрізняти кольорову кодовану інформацію",
"EnableInteractiveSearch": "Увімкнути інтерактивний пошук",
"EnableInteractiveSearchHelpText": "Буде використано, коли використовується інтерактивний пошук",
"EnableInteractiveSearchHelpTextWarning": "Пошук не підтримується цим індексатором",
"EnableRSS": "Увімкнути RSS",
"EnableSSL": "Увімкнути SSL",
"EnableSslHelpText": " Щоб набуло чинності, потрібно перезапустити роботу від імені адміністратора",
"EventType": "Тип події",
"Exception": "Виняток",
"Excluded": "Виключено",
"ExcludeMovie": "Виключити фільм",
"ExcludeTitle": "Виключити {0}? Це не дозволить Radarr автоматично додавати через синхронізацію списку.",
"ExistingMovies": "Існуючі фільми",
"ExistingTag": "Існуючий тег",
"Extension": "Розширення",
"ExternalUpdater": "Radarr налаштовано на використання зовнішнього механізму оновлення",
"ExtraFileExtensionsHelpTexts2": "Приклади: '.sub, .nfo' або 'sub,nfo'",
"Failed": "Не вдалося",
"FailedDownloadHandling": "Помилка обробки завантаження",
"FailedToLoadQueue": "Не вдалося завантажити чергу",
"FeatureRequests": "Запити щодо функцій",
"FileManagement": "Керування файлами",
"Filename": "Ім'я файлу",
"FileNames": "Імена файлів",
"FileWasDeletedByUpgrade": "Файл видалено, щоб імпортувати оновлення",
"FileWasDeletedByViaUI": "Файл видалено через інтерфейс користувача",
"FilterPlaceHolder": "Пошук фільмів",
"Folder": "Папка",
"Forecast": "Прогноз",
"Formats": "Формати",
"ForMoreInformationOnTheIndividualImportListsClinkOnTheInfoButtons": "Щоб отримати додаткову інформацію про окремі списки імпорту, натисніть інформаційні кнопки.",
"ForMoreInformationOnTheIndividualIndexers": "Щоб отримати додаткову інформацію про окремих індексаторів, натисніть кнопки інформації.",
"FreeSpace": "Вільний простір",
"General": "Загальний",
"Host": "Хост",
"Hostname": "Ім'я хоста",
"Hours": "Години",
"HttpHttps": "HTTP(S)",
"ICalFeed": "Канал iCal",
"ICalHttpUrlHelpText": "Скопіюйте цю URL-адресу до своїх клієнтів або натисніть, щоб підписатися, якщо ваш браузер підтримує веб-канал",
"Ignored": "Ігнорується",
"IgnoredAddresses": "Ігноровані адреси",
"IllRestartLater": "Я перезапущу пізніше",
"ImportExistingMovies": "Імпорт наявних фільмів",
"ImportExtraFiles": "Імпорт додаткових файлів",
"ImportFailedInterp": "Помилка імпорту: {0}",
"ImportHeader": "Імпортуйте існуючу організовану бібліотеку, щоб додати фільми до Radarr",
"ImportIncludeQuality": "Переконайтеся, що назви ваших файлів містять якість. напр. {0}",
"ImportLibrary": "Імпорт бібліотеки",
"ImportListMultipleMissingRoots": "Кілька кореневих папок відсутні для списків імпорту: {0}",
"IndexerLongTermStatusCheckSingleClientMessage": "Індексатори недоступні через збої більше 6 годин: {0}",
"ImdbRating": "Рейтинг IMDb",
"ImdbVotes": "Голоси IMDb",
"IndexerDownloadClientHelpText": "Укажіть, який клієнт завантаження використовується для захоплення з цього індексатора",
"IndexerRssHealthCheckNoAvailableIndexers": "Усі індексатори з підтримкою rss тимчасово недоступні через нещодавні помилки індексатора",
"IndexerPriorityHelpText": "Пріоритет індексатора від 1 (найвищий) до 50 (найнижчий). За замовчуванням: 25. Використовується під час захоплення випусків як тай-брейку для інших однакових випусків, Radarr все одно використовуватиме всі ввімкнені індексатори для RSS-синхронізації та пошуку",
"IndexerRssHealthCheckNoIndexers": "Немає доступних індексаторів із увімкненою синхронізацією RSS, Radarr не збиратиме нові випуски автоматично",
"IndexerSearchCheckNoInteractiveMessage": "Немає доступних індексаторів, коли ввімкнено інтерактивний пошук, Radarr не надаватиме жодних інтерактивних результатів пошуку",
"IndexerStatusCheckAllClientMessage": "Усі індексатори недоступні через збої",
"IndexerStatusCheckSingleClientMessage": "Індексатори недоступні через помилки: {0}",
"InteractiveImportErrLanguage": "Для кожного вибраного файлу необхідно вибрати мову",
"InteractiveImportErrMovie": "Фільм потрібно вибрати для кожного вибраного файлу",
"InteractiveImportErrQuality": "Для кожного вибраного файлу необхідно вибрати якість",
"InteractiveSearch": "Інтерактивний пошук",
"Interval": "Інтервал",
"InvalidFormat": "Недійсний формат",
"KeyboardShortcuts": "Гарячі клавіши",
"LanguageHelpText": "Мова для релізів",
"Languages": "Мови",
"LastExecution": "Останнє виконання",
"LastWriteTime": "Час останнього запису",
"Level": "Рівень",
"ListSyncLevelHelpTextWarning": "Файли фільмів буде остаточно видалено, це може призвести до видалення вашої бібліотеки, якщо ваші списки порожні",
"LoadingMovieExtraFilesFailed": "Не вдалося завантажити додаткові файли фільму",
"LoadingMovieFilesFailed": "Не вдалося завантажити файли фільму",
"Local": "Місцевий",
"Location": "Місцезнаходження",
"LogFiles": "Файли журналів",
"LogOnly": "Лише журнал",
"Logs": "Журнали",
"Manual": "Інструкція",
"ManualImport": "Імпорт вручну",
"ManualImportSelectLanguage": "Імпорт вручну - виберіть мову",
"ManualImportSelectMovie": "Імпорт вручну - виберіть фільм",
"ManualImportSelectQuality": " Імпорт вручну - Виберіть якість",
"ManualImportSetReleaseGroup": "Імпорт вручну - встановити групу випуску",
"MappedDrivesRunningAsService": "Підключені мережеві диски недоступні під час роботи як служби Windows. Щоб отримати додаткову інформацію, перегляньте FAQ",
"MarkAsFailed": "Позначити як помилку",
"MarkAsFailedMessageText": "Ви впевнені, що бажаєте позначити \"{0}\" як невдале?",
"MassMovieSearch": "Масовий пошук фільмів",
"Max": "Максимальний",
"MaximumSize": "Максимальний розмір",
"Mechanism": "Механізм",
"MediaInfo": "Медіа інформація",
"MediaManagementSettingsSummary": "Налаштування іменування та керування файлами",
"Medium": "Середній",
"MIA": "MIA",
"MinAvailability": "Мінімальна доступність",
"MinFormatScoreHelpText": "Мінімальна оцінка спеціального формату, дозволена для завантаження",
"MinimumFreeSpaceWhenImportingHelpText": "Заборонити імпорт, якщо він залишить менше, ніж цей обсяг доступного дискового простору",
"Minutes": "Хвилин",
"MinutesHundredTwenty": "120 хвилин: {0}",
"MinutesNinety": "90 хвилин: {0}",
"MinutesSixty": "60 хвилин: {0}",
"MissingFromDisk": "Radarr не зміг знайти файл на диску, тому файл було від’єднано від фільму в базі даних",
"MissingMonitoredAndConsideredAvailable": "Відсутній (відстежується)",
"Mode": "Режим",
"Months": "Місяці",
"More": "Більше",
"MoveFiles": "Перемістити файли",
"MovieChat": "Кіночат",
"MovieCollectionMultipleMissingRoots": "Кілька кореневих папок відсутні для колекцій фільмів: {0}",
"MovieDetailsNextMovie": "Подробиці про фільм: наступний фільм",
"MovieAlreadyExcluded": "Фільм уже виключено",
"MovieAndCollection": "Фільм і колекція",
"MovieExcludedFromAutomaticAdd": "Фільм виключено з автоматичного додавання",
"MovieFiles": "Файли фільмів",
"MovieFilesTotaling": "Підсумок файлів фільмів",
"MovieID": "ID фільму",
"MovieIndex": "Індекс фільму",
"MovieIndexScrollBottom": "Індекс фільму: прокрутка внизу",
"MovieInfoLanguage": "Мова інформації про фільм",
"MovieInfoLanguageHelpTextWarning": "Потрібно перезавантажити браузер",
"MovieInvalidFormat": "Фільм: недійсний формат",
"MovieIsDownloading": "Фільм завантажується",
"MovieIsMonitored": "Фільм відстежується",
"MovieIsUnmonitored": "Фільм без моніторингу",
"MovieNaming": "Назва фільму",
"MovieOnly": "Тільки кіно",
"MoviesSelectedInterp": "Вибрано фільмів: {0}",
"MovieTitle": "Назва фільму",
"MustNotContain": "Не повинен містити",
"Name": "Ім'я",
"NamingSettings": "Налаштування імен",
"Negate": "Заперечувати",
"Negated": "Заперечено",
"NoLogFiles": "Немає файлів журналу",
"NoMatchFound": "Збігів не знайдено!",
"NoResultsFound": "Нічого не знайдено",
"NoTagsHaveBeenAddedYet": "Теги ще не додано",
"NotAvailable": "Недоступний",
"NotificationTriggers": "Тригери сповіщень",
"NotificationTriggersHelpText": "Виберіть, які події мають викликати це сповіщення",
"NotMonitored": "Не контролюється",
"OAuthPopupMessage": "Ваш браузер блокує спливаючі вікна",
"Ok": "Гаразд",
"OnApplicationUpdateHelpText": "Оновлення програми",
"OnDownloadHelpText": "При імпорті",
"OnHealthIssueHelpText": "Про питання здоров'я",
"OnImport": "При імпорті",
"OnLatestVersion": "Остання версія Radarr вже встановлена",
"OnlyTorrent": "Тільки торрент",
"OnlyUsenet": "Тільки Usenet",
"OnUpgrade": "При оновленні",
"OnUpgradeHelpText": "При оновленні",
"OpenBrowserOnStart": "Відкрийте браузер при запуску",
"OrganizeModalNamingPattern": "Шаблон іменування:",
"Original": "Оригінал",
"OriginalLanguage": "Мова оригіналу",
"Paused": "Призупинено",
"Pending": "В очікуванні",
"PendingChangesDiscardChanges": "Відкинути зміни та залишити",
"PendingChangesStayReview": "Залишайтеся та переглядайте зміни",
"Permissions": "Дозволи",
"PhysicalRelease": "Фізичний реліз",
"PhysicalReleaseDate": "Дата фізичного випуску",
"Port": "Порт",
"PortNumber": "Номер порту",
"PosterOptions": "Параметри плаката",
"Posters": "Плакати",
"PreferAndUpgrade": "Віддайте перевагу та оновіть",
"PreferIndexerFlags": "Віддавати перевагу прапорцям індексатора",
"PreferIndexerFlagsHelpText": "Пріоритезуйте випуски за допомогою спеціальних прапорців",
"Preferred": "Бажано",
"PreferredSize": "Бажаний розмір",
"ProfilesSettingsSummary": "Профілі якості, мови та затримки",
"Progress": "Прогрес",
"Proper": "Належний",
"Protocol": "Протокол",
"ReadTheWikiForMoreInformation": "Читайте Wiki для отримання додаткової інформації",
"RecycleBinCleanupDaysHelpTextWarning": "Файли в кошику, старші за вибрану кількість днів, будуть очищені автоматично",
"RecycleBinHelpText": "Файли фільмів потраплять сюди після видалення, а не назавжди",
"Redownload": "Повторне завантаження",
"RejectionCount": "Кількість відмов",
"ReleaseBranchCheckOfficialBranchMessage": "Гілка {0} не є дійсною гілкою випуску Radarr, ви не отримуватимете оновлення",
"ReleaseStatus": "Статус випуску",
"ReleaseTitle": "Назва випуску",
"ReleaseWillBeProcessedInterp": "Випуск буде оброблено {0}",
"Reload": "Перезавантажити",
"RemotePath": "Віддалений шлях",
"RemotePathMappingCheckDockerFolderMissing": "Ви використовуєте docker; клієнт завантаження {0} розміщує завантаження в {1}, але цей каталог не існує всередині контейнера. Перегляньте свої віддалені відображення шляхів і налаштування обсягу контейнера.",
"RemotePathMappingCheckFileRemoved": "Файл {0} видалено під час обробки.",
"RemotePathMappingCheckFilesBadDockerPath": "Ви використовуєте docker; завантажити клієнтські {0} звітні файли в {1}, але це недійсний {2} шлях. Перегляньте свої віддалені відображення шляхів і завантажте налаштування клієнта.",
"RemotePathMappingCheckFilesLocalWrongOSPath": "Локальний клієнт завантаження {0} повідомив про файли в {1}, але це недійсний шлях {2}. Перегляньте налаштування клієнта завантаження.",
"RemotePathMappingCheckFilesWrongOSPath": "Клієнт віддаленого завантаження {0} повідомив про файли в {1}, але це недійсний шлях {2}. Перегляньте свої віддалені відображення шляхів і завантажте налаштування клієнта.",
"RemotePathMappingCheckFolderPermissions": "Radarr може бачити, але не має доступу до каталогу завантажень {0}. Ймовірна помилка дозволів.",
"RemotePathMappingCheckGenericPermissions": "Клієнт завантаження {0} розміщує завантаження в {1}, але Radarr не бачить цей каталог. Можливо, вам знадобиться налаштувати дозволи для папки.",
"RemotePathMappingCheckImportFailed": "Radarr не вдалося імпортувати фільм. Подробиці перевірте у своїх журналах.",
"RemotePathMappingCheckLocalFolderMissing": "Клієнт віддаленого завантаження {0} розміщує завантаження в {1}, але цей каталог не існує. Ймовірно, віддалений шлях відсутній або неправильний.",
"RemotePathMappingCheckLocalWrongOSPath": "Локальний клієнт завантаження {0} розміщує завантаження в {1}, але це недійсний шлях {2}. Перегляньте налаштування клієнта завантаження.",
"RemotePathMappings": "Віддалені відображення шляхів",
"RemoveCompleted": "Видалення завершено",
"RemoveCompletedDownloadsHelpText": "Видалити імпортовані завантаження з історії клієнта завантажень",
"RemovedFromTaskQueue": "Видалено з черги завдань",
"RemovedMovieCheckMultipleMessage": "Фільми {0} видалено з TMDb",
"RemovedMovieCheckSingleMessage": "Фільм {0} видалено з TMDb",
"RemoveFromDownloadClient": "Видалити з клієнта завантаження",
"Reorder": "Змінити порядок",
"Replace": "Замінити",
"ReplaceIllegalCharacters": "Замінити неприпустимі символи",
"ReplaceWithDash": "Замінити тире",
"ReplaceWithSpaceDash": "Замінити пробілом",
"RescanAfterRefreshHelpText": "Перескануйте папку фільму після оновлення фільму",
"ResetDefinitions": "Скинути визначення",
"ResetQualityDefinitions": "Скинути визначення якості",
"ResetTitles": "Скинути заголовки",
"ResetTitlesHelpText": "Скинути заголовки визначень, а також значення",
"RestartNow": "Перезавантажити зараз",
"Restore": "Відновлення",
"RootFolderCheckSingleMessage": "Відсутня коренева папка: {0}",
"RootFolders": "Кореневі папки",
"RSSIsNotSupportedWithThisIndexer": "Цей індексатор не підтримує RSS",
"RSSSync": "Синхронізація RSS",
"RSSSyncInterval": "Інтервал синхронізації RSS",
"RSSSyncIntervalHelpTextWarning": "Це стосується всіх індексаторів, будь ласка, дотримуйтеся встановлених ними правил",
"Save": "Зберегти",
"SaveChanges": "Зберегти зміни",
"SaveSettings": "Зберегти зміни",
"Score": "Оцінка",
"Script": "Сценарій",
"ScrollMovies": "Прокрутка фільмів",
"SearchCutoffUnmet": "Обрізка пошуку не виконана",
"SearchFiltered": "Пошук відфільтровано",
"SearchMovie": "Пошук фільму",
"SearchOnAdd": "Шукати на Додати",
"SearchOnAddHelpText": "Шукайте фільми в цьому списку після додавання в бібліотеку",
"SearchSelected": "Пошук вибрано",
"SelectAll": "Вибрати все",
"SelectDotDot": "Виберіть...",
"SelectFolder": "Виберіть папку",
"SelectLanguages": "Виберіть Мови",
"SelectQuality": "Виберіть Якість",
"SetPermissions": "Встановити дозволи",
"SetPermissionsLinuxHelpText": "Чи слід запускати chmod, коли файли імпортуються/перейменовуються?",
"SetReleaseGroup": "Встановити групу випуску",
"SettingsLongDateFormat": "Довгий формат дати",
"SettingsRemotePathMappingLocalPathHelpText": "Шлях, який має використовувати Radarr для локального доступу до віддаленого шляху",
"SettingsRemotePathMappingRemotePath": "Віддалений шлях",
"SettingsShortDateFormat": "Короткий формат дати",
"SettingsShowRelativeDates": "Показати відносні дати",
"SettingsTheme": "Тема",
"ShouldMonitorHelpText": "Чи слід додавати фільми чи колекції, додані до цього списку, як контрольовані",
"ShowAdvanced": "Показати Додатково",
"ShowAsAllDayEvents": "Показати як події на весь день",
"ShowCinemaRelease": "Показати дату виходу в кіно",
"showCinemaReleaseHelpText": "Покажіть дату виходу в кіно під афішею",
"ShowCutoffUnmetIconHelpText": "Показувати піктограму для файлів, коли обмеження не досягнуто",
"ShowDateAdded": "Показати дату додавання",
"ShowMonitored": "Показати Моніторинг",
"ShowPosters": "Показати плакати",
"ShowRatings": "Показати рейтинги",
"ShowReleaseDate": "Показати дату випуску",
"ShowReleaseDateHelpText": "Показати дату випуску під плакатом",
"ShowSearch": "Показати пошук",
"ShowUnknownMovieItems": "Показати невідомі елементи фільму",
"ShowYear": "Показати рік",
"Shutdown": "Вимкнення",
"SizeLimit": "Обмеження розміру",
"SizeOnDisk": "Розмір на диску",
"Small": "Маленький",
"SorryThatMovieCannotBeFound": "Вибачте, цей фільм неможливо знайти.",
"Sort": "Сортування",
"Source": "Джерело",
"SourceRelativePath": "Відносний шлях джерела",
"SourceTitle": "Назва джерела",
"SSLCertPasswordHelpText": "Пароль для файлу pfx",
"SSLPort": "Порт SSL",
"StandardMovieFormat": "Стандартний формат фільму",
"Started": "Розпочато",
"StartSearchForMissingMovie": "Почніть пошук зниклого фільму",
"StartTypingOrSelectAPathBelow": "Почніть вводити текст або виберіть шлях нижче",
"Status": "Статус",
"Studio": "Студія",
"Style": "Стиль",
"SubfolderWillBeCreatedAutomaticallyInterp": "Вкладена папка \"{0}\" буде створена автоматично",
"SuggestTranslationChange": "Запропонуйте зміну перекладу",
"SystemTimeCheckMessage": "Системний час вимкнено більш ніж на 1 день. Заплановані завдання можуть не працювати належним чином, доки час не буде виправлено",
"Table": "Таблиця",
"TableOptions": "Параметри таблиці",
"TableOptionsColumnsMessage": "Виберіть, які стовпці відображаються та в якому порядку вони відображаються",
"TagCannotBeDeletedWhileInUse": "Неможливо видалити під час використання",
"TagIsNotUsedAndCanBeDeleted": "Тег не використовується і може бути видалений",
"Tags": "Теги",
"TagsHelpText": "Застосовується до фільмів із принаймні одним відповідним тегом",
"TagsSettingsSummary": "Перегляньте всі теги та те, як вони використовуються. Невикористані теги можна видалити",
"TestAll": "Перевірити все",
"TestAllClients": "Перевірте всіх клієнтів",
"TestAllIndexers": "Перевірити всі індексатори",
"TestAllLists": "Перевірити всі списки",
"TimeFormat": "Формат часу",
"Timeleft": "Час залишився",
"Title": "Назва",
"Titles": "Назви",
"TmdbIdHelpText": "Ідентифікатор TMDb фільму, який потрібно виключити",
"TmdbRating": "Рейтинг TMDb",
"TmdbVotes": "Голоси TMDb",
"TorrentDelayTime": "Затримка торрента: {0}",
"Torrents": "Торренти",
"Trace": "Трасувати",
"Trailer": "Трейлер",
"Trigger": "Тригер",
"Type": "Тип",
"UnableToAddANewConditionPleaseTryAgain": "Не вдалося додати нову умову, спробуйте ще раз.",
"UnableToAddANewQualityProfilePleaseTryAgain": "Не вдалося додати новий профіль якості, спробуйте ще раз.",
"UnableToAddRootFolder": "Не вдалося додати кореневу папку",
"UnableToImportCheckLogs": "Завантажено неможливо імпортувати: подробиці перевірте в журналах",
"UnableToLoadDelayProfiles": "Неможливо завантажити профілі затримки",
"UnableToLoadGeneralSettings": "Не вдалося завантажити загальні налаштування",
"UnableToLoadHistory": "Не вдалося завантажити історію",
"UnableToLoadIndexerOptions": "Не вдалося завантажити параметри індексатора",
"UnableToLoadLanguages": "Не вдалося завантажити мови",
"UnableToLoadListOptions": "Не вдалося завантажити параметри списку",
"UnableToLoadLists": "Не вдалося завантажити списки",
"UnableToLoadMediaManagementSettings": "Не вдалося завантажити налаштування керування медіафайлами",
"UnableToLoadMetadata": "Не вдалося завантажити метадані",
"UnableToLoadNotifications": "Не вдалося завантажити сповіщення",
"UnableToLoadQualityDefinitions": "Не вдалося завантажити визначення якості",
"UnableToLoadRemotePathMappings": "Неможливо завантажити віддалені відображення шляхів",
"UnableToLoadRootFolders": "Не вдалося завантажити кореневі папки",
"UnableToUpdateRadarrDirectly": "Неможливо оновити Radarr безпосередньо,",
"Unavailable": "Недоступний",
"Unlimited": "Необмежений",
"UnmappedFilesOnly": "Лише незіставлені файли",
"UnmappedFolders": "Невідповідні папки",
"UnselectAll": "Скасувати вибір усіх",
"UpdateAll": "Оновити все",
"UpdateCheckStartupTranslocationMessage": "Неможливо встановити оновлення, оскільки папка запуску \"{0}\" знаходиться в папці переміщення програми.",
"UpdateMechanismHelpText": "Використовуйте вбудований засіб оновлення Radarr або скрипт",
"UpdateScriptPathHelpText": "Шлях до спеціального сценарію, який приймає витягнутий пакет оновлення та обробляє решту процесу оновлення",
"UpgradeAllowedHelpText": "Якщо відключені якості не будуть покращені",
"UpgradesAllowed": "Якщо відключені якості не будуть покращені",
"UpgradeUntilQuality": "Оновлення до якості",
"Uptime": "Час роботи",
"URLBase": "URL-адреса",
"UrlBaseHelpText": "Для підтримки зворотного проксі-сервера значення за умовчанням порожнє",
"UseHardlinksInsteadOfCopy": "Використовуйте жорсткі посилання замість копіювати",
"UsenetDisabled": "Usenet вимкнено",
"UseProxy": "Використовуйте проксі",
"Username": "Ім'я користувача",
"Version": "Версія",
"VideoCodec": "Відеокодек",
"View": "Переглянути",
"Wanted": "Розшукується",
"Warn": "Попередити",
"Week": "Тиждень",
"WeekColumnHeader": "Заголовок стовпця тижня",
"Weeks": "Тижнів",
"WhatsNew": "Що нового?",
"WhitelistedSubtitleTags": "Теги субтитрів із білого списку",
"Year": "Рік",
"YesCancel": "Так, скасувати",
"YesMoveFiles": "Так, перемістити файли",
"Yesterday": "Вчора",
"ApplicationURL": "URL програми",
"ApplicationUrlHelpText": "Зовнішня URL-адреса цієї програми, включаючи http(s)://, порт і базу URL-адрес",
"ImportRootPath": "Наведіть Radarr на папку з усіма вашими фільмами, а не на певний фільм. напр. {0}, а не {1}. Крім того, кожен фільм має бути у власній папці в кореневій папці/теці бібліотеки.",
"ImportTipsMessage": "Кілька порад, щоб забезпечити безперешкодне імпортування:",
"InCinemasMsg": "Фільм у кінотеатрах",
"IncludeHealthWarningsHelpText": "Включайте попередження про здоров’я",
"IncludeUnknownMovieItemsHelpText": "Показати елементи без фільму в черзі. Це може включати видалені фільми чи будь-що інше в категорії Radarr",
"IndexerPriority": "Пріоритет індексатора",
"ImportListStatusCheckAllClientMessage": "Усі списки недоступні через помилки",
"ImportListStatusCheckSingleClientMessage": "Списки недоступні через помилки: {0}",
"ImportListSyncIntervalHelpText": "Як часто Radarr синхронізує ваші списки. Мінімальне значення 6 годин",
"ImportMechanismHealthCheckMessage": "Увімкнути обробку завершених завантажень",
"ImportMovies": "Імпорт фільмів",
"ImportNotForDownloads": "Не використовуйте для імпортування завантажень із вашого клієнта завантаження, це лише для існуючих упорядкованих бібліотек, а не для несортованих файлів.",
"IndexerSearchCheckNoAvailableIndexersMessage": "Усі індексатори з можливістю пошуку тимчасово недоступні через нещодавні помилки індексатора",
"InstallLatest": "Встановити останній",
"MegabytesPerMinute": "Мегабайт за хвилину",
"Message": "Повідомлення",
"MovieTitleHelpText": "Назва фільму, який потрібно виключити (може бути будь-яким значущим)",
"MovieYear": "Фільм рік",
"MovieYearHelpText": "Рік фільму виключити",
"NegateHelpText": "Якщо позначено, настроюваний формат не застосовуватиметься, якщо ця умова {0} збігається.",
"Never": "Ніколи",
"New": "Новий",
"NoAltTitle": "Без альтернативних назв.",
"NoBackupsAreAvailable": "Немає резервних копій",
"NoChange": "Без змін",
"NoChanges": "Жодних змін",
"NoLinks": "Немає посилань",
"NoListRecommendations": "Елементів списку чи рекомендацій не знайдено. Щоб почати, ви захочете додати новий фільм, імпортувати кілька наявних або додати список.",
"NoMinimumForAnyRuntime": "Немає мінімуму для будь-якого часу виконання",
"NoMoviesExist": "Фільми не знайдено. Щоб почати, ви захочете додати новий фільм або імпортувати кілька наявних.",
"NoUpdatesAreAvailable": "Немає оновлень",
"Test": "Тест",
"TorrentsDisabled": "Торренти вимкнено",
"TotalFileSize": "Загальний розмір файлу",
"DownloadClientCheckDownloadingToRoot": "Клієнт завантаження {0} розміщує завантаження в кореневій папці {1}. Ви не повинні завантажувати в кореневу папку.",
"DigitalRelease": "Цифровий випуск",
"Disabled": "Вимкнено",
"DeleteEmptyFoldersHelpText": "Видаляти порожні папки фільмів під час сканування диска та під час видалення файлів фільмів",
"DeleteFile": "Видалити файл",
"Discord": "Discord",
"Docker": "Docker",
"DownloadClientCheckNoneAvailableMessage": "Немає доступного клієнта для завантаження",
"DownloadClientCheckUnableToCommunicateMessage": "Неможливо зв'язатися з {0}.",
"DiskSpace": "Дисковий простір",
"DoneEditingGroups": "Редагування груп завершено",
"DoNotPrefer": "Не віддавати перевагу",
"DoNotUpgradeAutomatically": "Не оновлювати автоматично",
"Download": "Завантажити",
"DownloadClient": "Клієнт завантажувача",
"DownloadClients": "Клієнти завантажувачів",
"DownloadClientSettings": "Налаштування клієнта завантажувача",
"DownloadClientsSettingsSummary": "Клієнти завантаження, обробка завантажень і віддалені відображення шляхів",
"DownloadClientStatusCheckAllClientMessage": "Усі клієнти завантаження недоступні через збої",
"DownloadClientStatusCheckSingleClientMessage": "Завантаження клієнтів недоступне через помилки: {0}",
"Genres": "Жанри",
"Global": "Глобальний",
"GoToInterp": "Перейти до {0}",
"Group": "Група",
"HardlinkCopyFiles": "Жорстке посилання/Копіювати файли",
"HaveNotAddedMovies": "Ви ще не додали жодного фільму. Бажаєте спершу імпортувати деякі або всі свої фільми?",
"Health": "Здоров'я",
"HealthNoIssues": "Немає проблем із вашою конфігурацією",
"GeneralSettings": "Загальні налаштування",
"GeneralSettingsSummary": "Порт, SSL, ім’я користувача/пароль, проксі, аналітика та оновлення",
"MoreInfo": "Більше інформації",
"RefreshMovie": "Оновити фільм",
"RegularExpressionsCanBeTested": "Регулярні вирази можна перевірити ",
"RemoveSelectedItems": "Видалити вибрані елементи",
"RemovingTag": "Видалення мітки",
"Renamed": "Перейменовано",
"RenameFiles": "Перейменування файлів",
"RenameMovies": "Перейменувати фільми",
"RenameMoviesHelpText": "Radarr використовуватиме існуючу назву файлу, якщо перейменування вимкнено",
"RemoveFailedDownloadsHelpText": "Видаліть невдалі завантаження з історії завантажень клієнта",
"RemoveFilter": "Видалити фільтр",
"RemoveFromBlocklist": "Видалити зі списку блокувань",
"RemoveFromQueue": "Видалити з черги",
"RemoveHelpTextWarning": "Видалення видалить завантаження та файл(и) із клієнта завантаження.",
"RemoveMovieAndDeleteFiles": "Видалити фільм і видалити файли",
"RemoveMovieAndKeepFiles": "Видалити фільм і зберегти файли",
"RemoveRootFolder": "Видалити кореневу папку",
"RemoveSelectedItem": "Видалити вибраний елемент",
"SettingsRemotePathMappingHostHelpText": "Той самий хост, який ви вказали для віддаленого клієнта завантаження",
"SettingsRemotePathMappingLocalPath": "Місцевий шлях",
"ShowGenres": "Показати жанри",
"UpdateAvailable": "Доступне нове оновлення",
"Updates": "Оновлення"
}

View File

@@ -636,7 +636,7 @@
"InstallLatest": "安装最新版",
"IndexerStatusCheckSingleClientMessage": "搜刮器因错误不可用:{0}",
"IndexerStatusCheckAllClientMessage": "所有搜刮器都因错误不可用",
"IndexerSearchCheckNoInteractiveMessage": "没有任何索引器开启了手动搜索因此Radarr 不会提供任何手动搜索的结果",
"IndexerSearchCheckNoInteractiveMessage": "没有任何索引器开启了手动搜索,因此 Radarr 不会提供任何手动搜索的结果",
"IndexerRssHealthCheckNoIndexers": "没有任何索引器开启了RSS同步Radarr不会自动抓取新发布的影片",
"IndexerLongTermStatusCheckSingleClientMessage": "由于故障6小时下列索引器都已不可用{0}",
"IndexerLongTermStatusCheckAllClientMessage": "由于故障超过6小时所有索引器均不可用",
@@ -1016,7 +1016,7 @@
"ChmodGroupHelpText": "组名称或GID。对于远程文件系统请使用GID。",
"MinAvailability": "最小可用性",
"RequiredHelpText": "此{0}条件必须匹配才能应用自定义格式。否则,单个{1}匹配就足够了。",
"QualitiesHelpText": "列表中较高的质量更可取。同组内的质量相同。只需要检查质量",
"QualitiesHelpText": "即使未选中,列表中的质量排序越高优先级也越高。同组内的质量优先级相同。质量只有选中才标记为需要",
"DownloadPropersAndRepacksHelpText2": "使用“不喜欢”按Propers / Repacks中的自定义格式分数排序",
"LookingForReleaseProfiles1": "寻找发行档案?尝试",
"QualityLimitsHelpText": "影片运行时会自动调整限制。",
@@ -1072,7 +1072,7 @@
"IndexerTagHelpText": "仅对至少有一个匹配标记的电影使用此索引器。留空则适用于所有电影。",
"RemotePathMappingCheckFileRemoved": "文件{0} 在处理的过程中被部分删除。",
"RemotePathMappingCheckFilesGenericPermissions": "下载{1}中客户端{0}报告的文件但Radarr无法看到此目录。您可能需要调整文件夹的权限。",
"RemotePathMappingCheckGenericPermissions": "下载客户端{0}将下载放置在{1}中但Radarr无法看到此目录。您可能需要调整文件夹的权限。",
"RemotePathMappingCheckGenericPermissions": "下载客户端{0}将下载放置在{1}中,但 Radarr 无法看到此目录。您可能需要调整文件夹的权限。",
"UpdateAvailable": "有新的更新可用",
"Letterboxd": "Letterboxd",
"RemoveSelectedItem": "删除所选项目",
@@ -1143,8 +1143,15 @@
"OnMovieAdded": "电影添加时",
"OnMovieAddedHelpText": "电影添加时",
"TotalMovies": "电影总数",
"ApplicationUrlHelpText": "此应用的外部URL包含 http(s)://端口和基本URL",
"ApplicationUrlHelpText": "此应用的外部URL包含 http(s)://端口和基本URL",
"ApplicationURL": "程序URL",
"BindAddressHelpText": "有效的 IPv4 地址或以'*'代表所有接口",
"PreferredProtocol": "首选协议"
"BindAddressHelpText": "有效的 IP 地址localhost或以'*'代表所有地址",
"PreferredProtocol": "首选协议",
"AreYouSureYouWantToResetQualityDefinitions": "确定要重置质量定义吗?",
"SettingsThemeHelpText": "更改程序界面主题,“自动”主题将根据您的操作系统主题来设置浅色或深色模式。",
"ResetDefinitions": "重置定义",
"ResetQualityDefinitions": "重置质量定义",
"ResetTitles": "重置标题",
"ResetTitlesHelpText": "重置定义标题与参数值",
"SettingsTheme": "主题"
}

View File

@@ -27,6 +27,7 @@ namespace NzbDrone.Core.Messaging.Commands
public virtual bool RequiresDiskAccess => false;
public virtual bool IsExclusive => false;
public virtual bool IsTypeExclusive => false;
public virtual bool IsLongRunning => false;
public string Name { get; private set; }
public DateTime? LastExecutionTime { get; set; }

View File

@@ -176,6 +176,11 @@ namespace NzbDrone.Core.Messaging.Commands
queuedCommands = queuedCommands.Where(c => !exclusiveTypes.Any(x => x == c.Body.Name));
}
if (startedCommands.Any(x => x.Body.IsLongRunning))
{
queuedCommands = queuedCommands.Where(c => !c.Body.IsExclusive);
}
var localItem = queuedCommands.OrderByDescending(c => c.Priority)
.ThenBy(c => c.QueuedAt)
.FirstOrDefault();

View File

@@ -16,7 +16,7 @@ namespace NzbDrone.Core.Notifications.Emby
}
public override string Link => "https://emby.media/";
public override string Name => "Emby";
public override string Name => "Emby / Jellyfin";
public override void OnGrab(GrabMessage grabMessage)
{

View File

@@ -1,5 +1,6 @@
using FluentValidation;
using Newtonsoft.Json;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
@@ -43,7 +44,7 @@ namespace NzbDrone.Core.Notifications.Emby
public bool UpdateLibrary { get; set; }
[JsonIgnore]
public string Address => $"{Host}:{Port}";
public string Address => $"{Host.ToUrlHost()}:{Port}";
public bool IsValid => !string.IsNullOrWhiteSpace(Host) && Port > 0;

View File

@@ -163,7 +163,7 @@ namespace NzbDrone.Core.Notifications.Plex.Server
{
var scheme = settings.UseSsl ? "https" : "http";
var requestBuilder = new HttpRequestBuilder($"{scheme}://{settings.Host}:{settings.Port}")
var requestBuilder = new HttpRequestBuilder($"{scheme}://{settings.Host.ToUrlHost()}:{settings.Port}")
.Accept(HttpAccept.Json)
.AddQueryParam("X-Plex-Client-Identifier", _configService.PlexClientIdentifier)
.AddQueryParam("X-Plex-Product", BuildInfo.AppName)

View File

@@ -21,7 +21,11 @@ namespace NzbDrone.Core.Notifications.Webhook
IndexerFlags = movieFile.IndexerFlags.ToString();
Size = movieFile.Size;
DateAdded = movieFile.DateAdded;
MediaInfo = new WebhookMovieFileMediaInfo(movieFile);
if (movieFile.MediaInfo != null)
{
MediaInfo = new WebhookMovieFileMediaInfo(movieFile);
}
}
public int Id { get; set; }

View File

@@ -86,7 +86,6 @@ namespace NzbDrone.Core.Notifications.Xbmc
if (moviePath != null)
{
moviePath = new OsPath(moviePath).Directory.FullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
_logger.Debug("Updating movie {0} (Path: {1}) on XBMC host: {2}", movie, moviePath, settings.Address);
}
else

View File

@@ -1,6 +1,7 @@
using System.ComponentModel;
using FluentValidation;
using Newtonsoft.Json;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
@@ -58,7 +59,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
public bool AlwaysUpdate { get; set; }
[JsonIgnore]
public string Address => string.Format("{0}:{1}", Host, Port);
public string Address => $"{Host.ToUrlHost()}:{Port}";
public NzbDroneValidationResult Validate()
{

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.Extensions.FileSystemGlobbing;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation;
@@ -197,31 +198,34 @@ namespace NzbDrone.Core.Parser
}
// Case sensitive
var caseSensitiveMatch = CaseSensitiveLanguageRegex.Match(title);
var caseSensitiveMatchs = CaseSensitiveLanguageRegex.Matches(title);
if (caseSensitiveMatch.Groups["lithuanian"].Captures.Cast<Capture>().Any())
foreach (Match match in caseSensitiveMatchs)
{
languages.Add(Language.Lithuanian);
}
if (match.Groups["lithuanian"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Lithuanian);
}
if (caseSensitiveMatch.Groups["czech"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Czech);
}
if (match.Groups["czech"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Czech);
}
if (caseSensitiveMatch.Groups["polish"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Polish);
}
if (match.Groups["polish"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Polish);
}
if (caseSensitiveMatch.Groups["bulgarian"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Bulgarian);
}
if (match.Groups["bulgarian"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Bulgarian);
}
if (caseSensitiveMatch.Groups["slovak"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Slovak);
if (match.Groups["slovak"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Slovak);
}
}
var matches = LanguageRegex.Matches(title);

View File

@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Parser
{
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(Parser));
private static readonly Regex EditionRegex = new Regex(@"\(?\b(?<edition>(((Recut.|Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Extended|Despecialized|(Special|Rouge|Final|Assembly|Imperial|Diamond|Signature|Hunter|Rekall)(?=(.(Cut|Edition|Version)))|\d{2,3}(th)?.Anniversary)(?:.(Cut|Edition|Version))?(.(Extended|Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit))?|((Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit|Restored|((2|3|4)in1))))))\b\)?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex EditionRegex = new Regex(@"\(?\b(?<edition>(((Recut.|Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Extended|Despecialized|(Special|Rouge|Final|Assembly|Imperial|Diamond|Signature|Hunter|Rekall)(?=(.(Cut|Edition|Version)))|\d{2,3}(th)?.Anniversary)(?:.(Cut|Edition|Version))?(.(Extended|Uncensored|Remastered|Unrated|Uncut|Open.?Matte|IMAX|Fan.?Edit))?|((Uncensored|Remastered|Unrated|Uncut|Open?.Matte|IMAX|Fan.?Edit|Restored|((2|3|4)in1))))))\b\)?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex ReportEditionRegex = new Regex(@"^.+?" + EditionRegex, RegexOptions.Compiled | RegexOptions.IgnoreCase);

View File

@@ -56,7 +56,7 @@ namespace NzbDrone.Core.Parser
private static readonly Regex RealRegex = new Regex(@"\b(?<real>REAL)\b",
RegexOptions.Compiled);
private static readonly Regex ResolutionRegex = new Regex(@"\b(?:(?<R360p>360p)|(?<R480p>480p|640x480|848x480)|(?<R540p>540p)|(?<R576p>576p)|(?<R720p>720p|1280x720|960p)|(?<R1080p>1080p|1920x1080|1440p|FHD|1080i|4kto1080p)|(?<R2160p>2160p|3840x2160|4k[-_. ](?:UHD|HEVC|BD|H265)|(?:UHD|HEVC|BD|H265)[-_. ]4k))\b",
private static readonly Regex ResolutionRegex = new Regex(@"\b(?:(?<R360p>360p)|(?<R480p>480p|640x480|848x480)|(?<R540p>540p)|(?<R576p>576p)|(?<R720p>720p|1280x720|960p)|(?<R1080p>1080p|1920x1080|1440p|FHD|1080i|4kto1080p)|(?<R2160p>2160p|3840x2160|4k[-_. ](?:UHD|HEVC|BD|H\.?265)|(?:UHD|HEVC|BD|H\.?265)[-_. ]4k))\b",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
// Handle cases where no resolution is in the release name; assume if UHD then 4k
@@ -75,7 +75,7 @@ namespace NzbDrone.Core.Parser
private static readonly Regex RemuxRegex = new Regex(@"(?:[_. \[]|\d{4}p-)(?<remux>(?:(BD|UHD)[-_. ]?)?Remux)\b|(?<remux>(?:(BD|UHD)[-_. ]?)?Remux[_. ]\d{4}p)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex HardcodedSubsRegex = new Regex(@"\b(?<hcsub>(\w+(?<!(SOFT|HORRIBLE))SUBS?)\b)|(?<hc>(HC|SUBBED))\b",
private static readonly Regex HardcodedSubsRegex = new Regex(@"\b((?<hcsub>(\w+(?<!SOFT|HORRIBLE)SUBS?))|(?<hc>(HC|SUBBED)))\b",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
public static QualityModel ParseQuality(string name)

View File

@@ -8,7 +8,7 @@
<PackageReference Include="MailKit" Version="2.15.0" />
<PackageReference Include="Npgsql" Version="6.0.3" />
<PackageReference Include="Servarr.FFMpegCore" Version="4.7.0-26" />
<PackageReference Include="Servarr.FFprobe" Version="5.0.1.93" />
<PackageReference Include="Servarr.FFprobe" Version="5.1.2.106" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
@@ -18,10 +18,10 @@
<PackageReference Include="Servarr.FluentMigrator.Runner.Postgres" Version="3.3.2.9" />
<PackageReference Include="FluentValidation" Version="8.6.2" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
<PackageReference Include="MonoTorrent" Version="2.0.5" />
<PackageReference Include="MonoTorrent" Version="2.0.7" />
<PackageReference Include="System.Text.Json" Version="6.0.5" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,30 +1,14 @@
using System.Net;
using System.Net.Sockets;
using FluentValidation;
using FluentValidation.Validators;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Validation
{
public static class IpValidation
{
public static IRuleBuilderOptions<T, string> ValidIp4Address<T>(this IRuleBuilder<T, string> ruleBuilder)
public static IRuleBuilderOptions<T, string> ValidIpAddress<T>(this IRuleBuilder<T, string> ruleBuilder)
{
return ruleBuilder.Must(x =>
{
IPAddress parsedAddress;
if (!IPAddress.TryParse(x, out parsedAddress))
{
return false;
}
if (parsedAddress.Equals(IPAddress.Parse("255.255.255.255")))
{
return false;
}
return parsedAddress.AddressFamily == AddressFamily.InterNetwork;
}).WithMessage("Must contain wildcard (*) or a valid IPv4 Address");
return ruleBuilder.Must(x => x.IsValidIpAddress()).WithMessage("Must contain wildcard (*) or a valid IP Address");
}
public static IRuleBuilderOptions<T, string> NotListenAllIp4Address<T>(this IRuleBuilder<T, string> ruleBuilder)

View File

@@ -1,11 +1,15 @@
using System;
using System.Text.RegularExpressions;
using FluentValidation;
using FluentValidation.Validators;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Validation
{
public static class RuleBuilderExtensions
{
private static readonly Regex HostRegex = new Regex("^[-_a-z0-9.]+$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static IRuleBuilderOptions<T, int> ValidId<T>(this IRuleBuilder<T, int> ruleBuilder)
{
return ruleBuilder.SetValidator(new GreaterThanValidator(0));
@@ -24,13 +28,15 @@ namespace NzbDrone.Core.Validation
public static IRuleBuilderOptions<T, string> ValidHost<T>(this IRuleBuilder<T, string> ruleBuilder)
{
ruleBuilder.SetValidator(new NotEmptyValidator(null));
return ruleBuilder.SetValidator(new RegularExpressionValidator("^[-_a-z0-9.]+$", RegexOptions.IgnoreCase)).WithMessage("must be valid Host without http://");
return ruleBuilder.Must(x => HostRegex.IsMatch(x) || x.IsValidIpAddress()).WithMessage("must be valid Host without http://");
}
public static IRuleBuilderOptions<T, string> ValidRootUrl<T>(this IRuleBuilder<T, string> ruleBuilder)
{
ruleBuilder.SetValidator(new NotEmptyValidator(null));
return ruleBuilder.SetValidator(new RegularExpressionValidator("^https?://[-_a-z0-9.]+", RegexOptions.IgnoreCase)).WithMessage("must be valid URL that starts with http(s)://");
return ruleBuilder.Must(x => x.IsValidUrl() && x.StartsWith("http", StringComparison.InvariantCultureIgnoreCase)).WithMessage("must be valid URL that starts with http(s)://");
}
public static IRuleBuilderOptions<T, string> ValidUrlBase<T>(this IRuleBuilder<T, string> ruleBuilder, string example = "/radarr")

View File

@@ -27,6 +27,8 @@ namespace NzbDrone.Mono.Disk
private static Dictionary<string, bool> _fileSystems;
private bool _hasLoggedProcMountFailure = false;
public ProcMountProvider(Logger logger)
{
_logger = logger;
@@ -45,7 +47,11 @@ namespace NzbDrone.Mono.Disk
}
catch (Exception ex)
{
_logger.Debug(ex, "Failed to retrieve mounts from {0}", PROC_MOUNTS_FILENAME);
if (!_hasLoggedProcMountFailure)
{
_logger.Debug(ex, "Failed to retrieve mounts from {0}", PROC_MOUNTS_FILENAME);
_hasLoggedProcMountFailure = true;
}
}
return new List<IMount>();

View File

@@ -33,9 +33,9 @@ namespace Radarr.Api.V3.Config
_userService = userService;
SharedValidator.RuleFor(c => c.BindAddress)
.ValidIp4Address()
.ValidIpAddress()
.NotListenAllIp4Address()
.When(c => c.BindAddress != "*");
.When(c => c.BindAddress != "*" && c.BindAddress != "localhost");
SharedValidator.RuleFor(c => c.Port).ValidPort();

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using FluentValidation;
using FluentValidation.Results;
using Microsoft.AspNetCore.Mvc;
@@ -23,6 +24,8 @@ namespace Radarr.Api.V3.Indexers
private readonly IIndexerFactory _indexerFactory;
private readonly Logger _logger;
private static readonly object PushLock = new object();
public ReleasePushController(IMakeDownloadDecision downloadDecisionMaker,
IProcessDownloadDecisions downloadDecisionProcessor,
IIndexerFactory indexerFactory,
@@ -54,8 +57,13 @@ namespace Radarr.Api.V3.Indexers
ResolveIndexer(info);
var decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
_downloadDecisionProcessor.ProcessDecisions(decisions);
List<DownloadDecision> decisions;
lock (PushLock)
{
decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
_downloadDecisionProcessor.ProcessDecisions(decisions);
}
var firstDecision = decisions.FirstOrDefault();

View File

@@ -54,9 +54,9 @@ namespace Radarr.Api.V3.System
}
[HttpGet("status")]
public object GetStatus()
public SystemResource GetStatus()
{
return new
return new SystemResource
{
AppName = BuildInfo.AppName,
InstanceName = _configFileProvider.InstanceName,

View File

@@ -0,0 +1,43 @@
using System;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Update;
namespace Radarr.Api.V3.System
{
public class SystemResource
{
public string AppName { get; set; }
public string InstanceName { get; set; }
public string Version { get; set; }
public DateTime BuildTime { get; set; }
public bool IsDebug { get; set; }
public bool IsProduction { get; set; }
public bool IsAdmin { get; set; }
public bool IsUserInteractive { get; set; }
public string StartupPath { get; set; }
public string AppData { get; set; }
public string OsName { get; set; }
public string OsVersion { get; set; }
public bool IsNetCore { get; set; }
public bool IsLinux { get; set; }
public bool IsOsx { get; set; }
public bool IsWindows { get; set; }
public bool IsDocker { get; set; }
public RuntimeMode Mode { get; set; }
public string Branch { get; set; }
public DatabaseType DatabaseType { get; set; }
public Version DatabaseVersion { get; set; }
public AuthenticationType Authentication { get; set; }
public int MigrationVersion { get; set; }
public string UrlBase { get; set; }
public Version RuntimeVersion { get; set; }
public string RuntimeName { get; set; }
public DateTime StartTime { get; set; }
public string PackageVersion { get; set; }
public string PackageAuthor { get; set; }
public UpdateMechanism PackageUpdateMechanism { get; set; }
public string PackageUpdateMechanismMessage { get; set; }
}
}

View File

@@ -5955,6 +5955,25 @@
}
}
},
"/ping": {
"get": {
"tags": [
"Ping"
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PingResource"
}
}
}
}
}
}
},
"/api/v3/qualitydefinition/{id}": {
"put": {
"tags": [
@@ -7635,7 +7654,24 @@
],
"responses": {
"200": {
"description": "Success"
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/SystemResource"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/SystemResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/SystemResource"
}
}
}
}
}
}
@@ -8731,6 +8767,10 @@
"type": "boolean",
"readOnly": true
},
"isLongRunning": {
"type": "boolean",
"readOnly": true
},
"name": {
"type": "string",
"nullable": true,
@@ -9017,6 +9057,13 @@
},
"additionalProperties": false
},
"DatabaseType": {
"enum": [
"sqLite",
"postgreSQL"
],
"type": "string"
},
"DelayProfileResource": {
"type": "object",
"properties": {
@@ -11349,6 +11396,16 @@
},
"additionalProperties": false
},
"PingResource": {
"type": "object",
"properties": {
"status": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"ProfileFormatItemResource": {
"type": "object",
"properties": {
@@ -12107,6 +12164,14 @@
},
"additionalProperties": false
},
"RuntimeMode": {
"enum": [
"console",
"service",
"tray"
],
"type": "string"
},
"SelectOption": {
"type": "object",
"properties": {
@@ -12164,6 +12229,121 @@
],
"type": "string"
},
"SystemResource": {
"type": "object",
"properties": {
"appName": {
"type": "string",
"nullable": true
},
"instanceName": {
"type": "string",
"nullable": true
},
"version": {
"type": "string",
"nullable": true
},
"buildTime": {
"type": "string",
"format": "date-time"
},
"isDebug": {
"type": "boolean"
},
"isProduction": {
"type": "boolean"
},
"isAdmin": {
"type": "boolean"
},
"isUserInteractive": {
"type": "boolean"
},
"startupPath": {
"type": "string",
"nullable": true
},
"appData": {
"type": "string",
"nullable": true
},
"osName": {
"type": "string",
"nullable": true
},
"osVersion": {
"type": "string",
"nullable": true
},
"isNetCore": {
"type": "boolean"
},
"isLinux": {
"type": "boolean"
},
"isOsx": {
"type": "boolean"
},
"isWindows": {
"type": "boolean"
},
"isDocker": {
"type": "boolean"
},
"mode": {
"$ref": "#/components/schemas/RuntimeMode"
},
"branch": {
"type": "string",
"nullable": true
},
"databaseType": {
"$ref": "#/components/schemas/DatabaseType"
},
"databaseVersion": {
"$ref": "#/components/schemas/Version"
},
"authentication": {
"$ref": "#/components/schemas/AuthenticationType"
},
"migrationVersion": {
"type": "integer",
"format": "int32"
},
"urlBase": {
"type": "string",
"nullable": true
},
"runtimeVersion": {
"$ref": "#/components/schemas/Version"
},
"runtimeName": {
"type": "string",
"nullable": true
},
"startTime": {
"type": "string",
"format": "date-time"
},
"packageVersion": {
"type": "string",
"nullable": true
},
"packageAuthor": {
"type": "string",
"nullable": true
},
"packageUpdateMechanism": {
"$ref": "#/components/schemas/UpdateMechanism"
},
"packageUpdateMechanismMessage": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"TMDbCountryCode": {
"enum": [
"au",

View File

@@ -162,39 +162,7 @@ namespace Radarr.Http.Extensions
remoteIP = remoteIP.MapToIPv4();
}
var remoteAddress = remoteIP.ToString();
// Only check if forwarded by a local network reverse proxy
if (remoteIP.IsLocalAddress())
{
var realIPHeader = request.Headers["X-Real-IP"];
if (realIPHeader.Any())
{
return realIPHeader.First().ToString();
}
var forwardedForHeader = request.Headers["X-Forwarded-For"];
if (forwardedForHeader.Any())
{
// Get the first address that was forwarded by a local IP to prevent remote clients faking another proxy
foreach (var forwardedForAddress in forwardedForHeader.SelectMany(v => v.Split(',')).Select(v => v.Trim()).Reverse())
{
if (!IPAddress.TryParse(forwardedForAddress, out remoteIP))
{
return remoteAddress;
}
if (!remoteIP.IsLocalAddress())
{
return forwardedForAddress;
}
remoteAddress = forwardedForAddress;
}
}
}
return remoteAddress;
return remoteIP.ToString();
}
public static void DisableCache(this IHeaderDictionary headers)

View File

@@ -0,0 +1,39 @@
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Core.Configuration;
namespace Radarr.Http.Ping
{
public class PingController : Controller
{
private readonly IConfigRepository _configRepository;
public PingController(IConfigRepository configRepository)
{
_configRepository = configRepository;
}
[HttpGet("/ping")]
[Produces("application/json")]
public ActionResult<PingResource> GetStatus()
{
try
{
_configRepository.All();
}
catch (Exception)
{
return StatusCode(StatusCodes.Status500InternalServerError, new PingResource
{
Status = "Error"
});
}
return StatusCode(StatusCodes.Status200OK, new PingResource
{
Status = "OK"
});
}
}
}

View File

@@ -0,0 +1,7 @@
namespace Radarr.Http.Ping
{
public class PingResource
{
public string Status { get; set; }
}
}

View File

@@ -69,12 +69,18 @@ namespace Radarr.Http.REST
foreach (var resource in resourceArgs)
{
// Map route Id to body resource if not set in request
if (Request.Method == "PUT" && resource.Id == 0 && context.RouteData.Values.TryGetValue("id", out var routeId))
{
resource.Id = Convert.ToInt32(routeId);
}
ValidateResource(resource, skipValidate, skipShared);
}
}
var attributes = descriptor.MethodInfo.CustomAttributes;
if (attributes.Any(x => VALIDATE_ID_ATTRIBUTES.Contains(x.GetType())) && !skipValidate)
if (attributes.Any(x => VALIDATE_ID_ATTRIBUTES.Contains(x.AttributeType)) && !skipValidate)
{
if (context.ActionArguments.TryGetValue("id", out var idObj))
{