mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-27 17:54:34 -04:00
Compare commits
23 Commits
collection
...
v4.3.1.682
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c11f72c098 | ||
|
|
3617bef54b | ||
|
|
a5fb01f1e6 | ||
|
|
fa6acb7497 | ||
|
|
904259df92 | ||
|
|
65c316bd6d | ||
|
|
3b46a08606 | ||
|
|
6ad49373d4 | ||
|
|
2a1f57c085 | ||
|
|
9d9065fbcd | ||
|
|
694940452c | ||
|
|
f5d6a79998 | ||
|
|
4cc98a10a0 | ||
|
|
1751bd1a58 | ||
|
|
c0caf65b69 | ||
|
|
cd889872de | ||
|
|
6366e335bc | ||
|
|
41f10d098e | ||
|
|
b2c1698097 | ||
|
|
ed20487f30 | ||
|
|
d1235adfc4 | ||
|
|
561993e30c | ||
|
|
14f8f89634 |
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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>()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -5,5 +5,7 @@ namespace NzbDrone.Core.Download
|
||||
public class ProcessMonitoredDownloadsCommand : Command
|
||||
{
|
||||
public override bool RequiresDiskAccess => true;
|
||||
|
||||
public override bool IsLongRunning => true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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í)"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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?"
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
43
src/Radarr.Api.V3/System/SystemResource.cs
Normal file
43
src/Radarr.Api.V3/System/SystemResource.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
39
src/Radarr.Http/Ping/PingController.cs
Normal file
39
src/Radarr.Http/Ping/PingController.cs
Normal 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"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/Radarr.Http/Ping/PingResource.cs
Normal file
7
src/Radarr.Http/Ping/PingResource.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Radarr.Http.Ping
|
||||
{
|
||||
public class PingResource
|
||||
{
|
||||
public string Status { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user