1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-27 17:54:34 -04:00

Compare commits

...

23 Commits

Author SHA1 Message Date
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
44 changed files with 553 additions and 163 deletions

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

@@ -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

@@ -10,7 +10,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<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

@@ -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

@@ -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

@@ -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,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

@@ -333,13 +333,13 @@
"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",
@@ -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, 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

@@ -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": "Kezelőfelület témájának módosítása, a Theme.Park jóvoltából",
"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

@@ -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.",
@@ -1107,5 +1107,11 @@
"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"
}

View File

@@ -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, inspirado no 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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -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))
{