1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-18 21:35:51 -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
46 changed files with 589 additions and 243 deletions
+23 -9
View File
@@ -75,13 +75,23 @@ class Queue extends Component {
return; return;
} }
const nextState = {};
if (prevProps.items !== items) {
nextState.items = items;
}
const selectedIds = this.getSelectedIds(); const selectedIds = this.getSelectedIds();
const isPendingSelected = _.some(this.props.items, (item) => { const isPendingSelected = _.some(this.props.items, (item) => {
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay'; return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
}); });
if (isPendingSelected !== this.state.isPendingSelected) { 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> <PageContentBody>
{ {
isRefreshing && !isAllPopulated && isRefreshing && !isAllPopulated ?
<LoadingIndicator /> <LoadingIndicator /> :
null
} }
{ {
!isRefreshing && hasError && !isRefreshing && hasError ?
<div> <div>
{translate('FailedToLoadQueue')} {translate('FailedToLoadQueue')}
</div> </div> :
null
} }
{ {
isAllPopulated && !hasError && !items.length && isAllPopulated && !hasError && !items.length ?
<div> <div>
{translate('QueueIsEmpty')} {translate('QueueIsEmpty')}
</div> </div> :
null
} }
{ {
isAllPopulated && !hasError && !!items.length && isAllPopulated && !hasError && !!items.length ?
<div> <div>
<Table <Table
columns={columns} columns={columns}
@@ -268,7 +281,8 @@ class Queue extends Component {
isFetching={isRefreshing} isFetching={isRefreshing}
{...otherProps} {...otherProps}
/> />
</div> </div> :
null
} }
</PageContentBody> </PageContentBody>
+4
View File
@@ -1,7 +1,11 @@
import * as dark from './dark'; import * as dark from './dark';
import * as light from './light'; import * as light from './light';
const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const auto = defaultDark ? { ...dark } : { ...light };
export default { export default {
auto,
light, light,
dark 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 NUnit.Framework;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Test.Common; 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://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) public void should_parse(string uri)
{ {
var newUri = new HttpUri(uri); var newUri = new HttpUri(uri);
@@ -7,34 +7,50 @@ namespace NzbDrone.Common.Extensions
{ {
public static bool IsLocalAddress(this IPAddress ipAddress) 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)) if (IPAddress.IsLoopback(ipAddress))
{ {
return true; return true;
} }
// IPv4
if (ipAddress.AddressFamily == AddressFamily.InterNetwork) if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{ {
byte[] bytes = ipAddress.GetAddressBytes(); return IsLocalIPv4(ipAddress.GetAddressBytes());
switch (bytes[0]) }
{
case 10: // IPv6
case 127: if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
return true; {
case 172: return ipAddress.IsIPv6LinkLocal ||
return bytes[1] < 32 && bytes[1] >= 16; ipAddress.IsIPv6UniqueLocal ||
case 192: ipAddress.IsIPv6SiteLocal;
return bytes[1] == 168;
default:
return false;
}
} }
return false; 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.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@@ -192,5 +194,30 @@ namespace NzbDrone.Common.Extensions
.Replace("'", "%27") .Replace("'", "%27")
.Replace("%7E", "~"); .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;
}
} }
} }
+4 -2
View File
@@ -8,7 +8,7 @@ namespace NzbDrone.Common.Http
{ {
public class HttpUri : IEquatable<HttpUri> 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; private readonly string _uri;
public string FullUri => _uri; public string FullUri => _uri;
@@ -70,6 +70,8 @@ namespace NzbDrone.Common.Http
private void Parse() private void Parse()
{ {
var parseSuccess = Uri.TryCreate(_uri, UriKind.RelativeOrAbsolute, out var uri);
var match = RegexUri.Match(_uri); var match = RegexUri.Match(_uri);
var scheme = match.Groups["scheme"]; var scheme = match.Groups["scheme"];
@@ -79,7 +81,7 @@ namespace NzbDrone.Common.Http
var query = match.Groups["query"]; var query = match.Groups["query"];
var fragment = match.Groups["fragment"]; 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); throw new ArgumentException("Uri didn't match expected pattern: " + _uri);
} }
@@ -11,26 +11,41 @@ namespace NzbDrone.Common.Instrumentation.Sentry
{ {
try 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(); var fingerprint = sentryEvent.Fingerprint.Select(x => CleanseLogMessage.Cleanse(x)).ToList();
sentryEvent.SetFingerprint(fingerprint); 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); sentryEvent.SetExtras(extras);
} }
foreach (var exception in sentryEvent.SentryExceptions) if (sentryEvent.SentryExceptions is not null)
{ {
exception.Value = CleanseLogMessage.Cleanse(exception.Value); foreach (var exception in sentryEvent.SentryExceptions)
foreach (var frame in exception.Stacktrace.Frames)
{ {
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.Dsn = dsn;
o.AttachStacktrace = true; o.AttachStacktrace = true;
o.MaxBreadcrumbs = 200; o.MaxBreadcrumbs = 200;
o.SendDefaultPii = false;
o.Debug = false;
o.DiagnosticLevel = SentryLevel.Debug;
o.Release = BuildInfo.Release; o.Release = BuildInfo.Release;
o.BeforeSend = x => SentryCleanser.CleanseEvent(x); o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x); o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
+1 -1
View File
@@ -10,7 +10,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NLog" Version="5.0.1" /> <PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" /> <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="NLog.Targets.Syslog" Version="7.0.0" />
<PackageReference Include="SharpZipLib" Version="1.3.3" /> <PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="System.Text.Json" Version="6.0.5" /> <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.Title = title)
.With(h => h.Release = release) .With(h => h.Release = release)
.With(h => h.Reason = reason) .With(h => h.Reason = reason)
.With(h => h.ParsedMovieInfo = _parsedMovieInfo)
.Build(); .Build();
_heldReleases.AddRange(heldReleases); _heldReleases.AddRange(heldReleases);
@@ -52,6 +52,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
_pending.Add(new PendingRelease _pending.Add(new PendingRelease
{ {
Id = id, Id = id,
Title = "Movie.Title.2020.720p-Radarr",
ParsedMovieInfo = new ParsedMovieInfo { MovieTitles = new List<string> { title }, Year = year }, ParsedMovieInfo = new ParsedMovieInfo { MovieTitles = new List<string> { title }, Year = year },
MovieId = _movie.Id MovieId = _movie.Id
}); });
@@ -93,6 +93,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
.With(h => h.MovieId = _movie.Id) .With(h => h.MovieId = _movie.Id)
.With(h => h.Title = title) .With(h => h.Title = title)
.With(h => h.Release = release) .With(h => h.Release = release)
.With(h => h.ParsedMovieInfo = _parsedMovieInfo)
.Build(); .Build();
Mocker.GetMock<IPendingReleaseRepository>() 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.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 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.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! 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)] [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) 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 LogLevel => GetValue("LogLevel", "info").ToLowerInvariant();
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false); 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 PostgresHost => _postgresOptions?.Host ?? GetValue("PostgresHost", string.Empty, persist: false);
public string PostgresUser => _postgresOptions?.User ?? GetValue("PostgresUser", 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); 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); item.RemainingTime = TimeSpan.FromSeconds(properties.Eta);
} }
if (properties.Status.Contains("error")) if (properties.Status.Contains("seeding") || properties.Status.Contains("complete"))
{
item.Status = DownloadItemStatus.Warning;
}
else if (properties.Status.Contains("seeding") || properties.Status.Contains("complete"))
{ {
item.Status = DownloadItemStatus.Completed; item.Status = DownloadItemStatus.Completed;
} }
else if (properties.Status.Contains("downloading"))
{
item.Status = DownloadItemStatus.Downloading;
}
else if (properties.Status.Contains("stopped")) else if (properties.Status.Contains("stopped"))
{ {
item.Status = DownloadItemStatus.Paused; 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) if (item.Status == DownloadItemStatus.Completed)
{ {
@@ -283,6 +283,12 @@ namespace NzbDrone.Core.Download.Pending
return null; 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 release.RemoteMovie = new RemoteMovie
{ {
Movie = movie, Movie = movie,
@@ -5,5 +5,7 @@ namespace NzbDrone.Core.Download
public class ProcessMonitoredDownloadsCommand : Command public class ProcessMonitoredDownloadsCommand : Command
{ {
public override bool RequiresDiskAccess => true; public override bool RequiresDiskAccess => true;
public override bool IsLongRunning => true;
} }
} }
+3 -1
View File
@@ -1,9 +1,11 @@
using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Indexers namespace NzbDrone.Core.Indexers
{ {
public class RssSyncCommand : Command public class RssSyncCommand : Command
{ {
public override bool SendUpdatesToClient => true; public override bool SendUpdatesToClient => true;
public override bool IsLongRunning => true;
} }
} }
+9 -9
View File
@@ -1,9 +1,9 @@
{ {
"EditDelayProfile": "Upravit profil zpoždění", "EditDelayProfile": "Upravit profil zpoždění",
"AcceptConfirmationModal": "Přijměte potvrzení Modal", "AcceptConfirmationModal": "Přijměte potvrzovací modální okno",
"Automatic": "Automatický", "Automatic": "Automatický",
"CertificateValidation": "Ověření certifikátu", "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í", "Cast": "Obsazení",
"CheckDownloadClientForDetails": "zkontrolujte stahování klienta pro více informací", "CheckDownloadClientForDetails": "zkontrolujte stahování klienta pro více informací",
"CheckForFinishedDownloadsInterval": "Zkontrolujte interval dokončených stahování", "CheckForFinishedDownloadsInterval": "Zkontrolujte interval dokončených stahování",
@@ -137,7 +137,7 @@
"Ungroup": "Oddělit skupinu", "Ungroup": "Oddělit skupinu",
"Unlimited": "Neomezený", "Unlimited": "Neomezený",
"UnsavedChanges": "Neuložené změny", "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", "UpgradeUntilQuality": "Upgradujte až do kvality",
"UseHardlinksInsteadOfCopy": "Místo kopírování použijte pevné odkazy", "UseHardlinksInsteadOfCopy": "Místo kopírování použijte pevné odkazy",
"UsenetDelayTime": "Usenet Zpoždění: {0}", "UsenetDelayTime": "Usenet Zpoždění: {0}",
@@ -157,12 +157,12 @@
"Announced": "Oznámeno", "Announced": "Oznámeno",
"AvailabilityDelayHelpText": "Množství času před nebo po dostupném datu pro vyhledání filmu", "AvailabilityDelayHelpText": "Množství času před nebo po dostupném datu pro vyhledání filmu",
"ImportExistingMovies": "Importovat existující filmy", "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í", "AddNotification": "Přidat oznámení",
"Add": "Přidat", "Add": "Přidat",
"AddCustomFormat": "Přidat vlastní formát", "AddCustomFormat": "Přidat vlastní formát",
"AddDelayProfile": "Přidat profil zpoždění", "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", "AddRootFolder": "Přidat kořenovou složku",
"Always": "Vždy", "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.", "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", "IMDb": "IMDb",
"ImportCustomFormat": "Importujte vlastní formát", "ImportCustomFormat": "Importujte vlastní formát",
"ImportedTo": "Importováno do", "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:", "ImportTipsMessage": "Několik tipů, jak zajistit bezproblémový import:",
"InCinemasDate": "V kinech", "InCinemasDate": "V kinech",
"InCinemasMsg": "Film je v kinech", "InCinemasMsg": "Film je v kinech",
@@ -687,13 +687,13 @@
"IncludeUnmonitored": "Zahrnout Nesledováno", "IncludeUnmonitored": "Zahrnout Nesledováno",
"ImportMovies": "Importovat filmy", "ImportMovies": "Importovat filmy",
"IndexerPriority": "Priorita indexování", "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", "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í", "IndexerRssHealthCheckNoIndexers": "Nejsou k dispozici žádné indexery se zapnutou synchronizací RSS, Radarr nové verze automaticky nezachytí",
"Indexers": "Indexery", "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í", "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", "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", "IndexerSettings": "Nastavení indexeru",
"IndexerStatusCheckSingleClientMessage": "Indexery nedostupné z důvodu selhání: {0}", "IndexerStatusCheckSingleClientMessage": "Indexery nedostupné z důvodu selhání: {0}",
"InstallLatest": "Nainstalujte nejnovější", "InstallLatest": "Nainstalujte nejnovější",
@@ -1061,6 +1061,6 @@
"AllCollectionsHiddenDueToFilter": "Všechny filmy jsou skryty kvůli použitému filtru.", "AllCollectionsHiddenDueToFilter": "Všechny filmy jsou skryty kvůli použitému filtru.",
"Collections": "Sbírka", "Collections": "Sbírka",
"MonitorMovies": "Monitorujte film", "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í)" "RssSyncHelpText": "Interval v minutách. Nastavením na nulu deaktivujete (tím se zastaví veškeré automatické uvolnění uvolnění)"
} }
+13 -6
View File
@@ -333,13 +333,13 @@
"Branch": "Git-Branch", "Branch": "Git-Branch",
"BypassProxyForLocalAddresses": "Proxy für lokale Adressen umgehen", "BypassProxyForLocalAddresses": "Proxy für lokale Adressen umgehen",
"CertificateValidation": "Zertifikat Validierung", "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", "CertificationCountry": "Zertifizierungs Land",
"ChangeFileDate": "Erstelldatum der Datei anpassen", "ChangeFileDate": "Erstelldatum der Datei anpassen",
"ChangeHasNotBeenSavedYet": "Änderung wurde noch nicht gespeichert", "ChangeHasNotBeenSavedYet": "Änderung wurde noch nicht gespeichert",
"CheckForFinishedDownloadsInterval": "Intervall zum prüfen von fertigen Downloads", "CheckForFinishedDownloadsInterval": "Intervall zum prüfen von fertigen Downloads",
"CleanLibraryLevel": "Mediathek aufräumen", "CleanLibraryLevel": "Mediathek aufräumen",
"ClickToChangeLanguage": "Sprache ändern ...", "ClickToChangeLanguage": "Sprache ändern",
"ClickToChangeQuality": "Hier klicken um die Qualität zu ändern", "ClickToChangeQuality": "Hier klicken um die Qualität zu ändern",
"ClientPriority": "Priorität", "ClientPriority": "Priorität",
"CloneFormatTag": "Format Tag kopieren", "CloneFormatTag": "Format Tag kopieren",
@@ -516,7 +516,7 @@
"ShowTitleHelpText": "Filmtitel unter dem Plakat anzeigen", "ShowTitleHelpText": "Filmtitel unter dem Plakat anzeigen",
"ShowUnknownMovieItems": "Unzugeordente Filmeinträge anzeigen", "ShowUnknownMovieItems": "Unzugeordente Filmeinträge anzeigen",
"SkipFreeSpaceCheck": "Pürfung des freien Speichers überspringen", "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.", "SorryThatMovieCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.",
"SourcePath": "Quellpfad", "SourcePath": "Quellpfad",
"SourceRelativePath": "Relativer Quellpfad", "SourceRelativePath": "Relativer Quellpfad",
@@ -531,7 +531,7 @@
"TestAllIndexers": "Alle testen", "TestAllIndexers": "Alle testen",
"TestAllLists": "Alle testen", "TestAllLists": "Alle testen",
"TimeFormat": "Zeitformat", "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", "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", "UpgradeAllowedHelpText": "Wenn deaktiviert wird die Qualität nicht verbessert",
"Uptime": "Laufzeit", "Uptime": "Laufzeit",
@@ -1063,7 +1063,7 @@
"ImportListMissingRoot": "Fehlendes Stammverzeichnis für Importlist(en): {0}", "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", "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", "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", "Letterboxd": "Letterboxd",
"From": "von", "From": "von",
"NotificationTriggersHelpText": "Auslöser für diese Benachrichtigung auswählen", "NotificationTriggersHelpText": "Auslöser für diese Benachrichtigung auswählen",
@@ -1146,5 +1146,12 @@
"TotalMovies": "Filme insgesamt", "TotalMovies": "Filme insgesamt",
"ApplicationURL": "Anwendungs-URL", "ApplicationURL": "Anwendungs-URL",
"ApplicationUrlHelpText": "Die externe URL der Anwendung inklusive http(s)://, Port und URL-Basis", "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"
} }
+2 -2
View File
@@ -89,7 +89,7 @@
"Backups": "Backups", "Backups": "Backups",
"BeforeUpdate": "Before update", "BeforeUpdate": "Before update",
"BindAddress": "Bind Address", "BindAddress": "Bind Address",
"BindAddressHelpText": "Valid IPv4 address or '*' for all interfaces", "BindAddressHelpText": "Valid IP address, localhost or '*' for all interfaces",
"Blocklist": "Blocklist", "Blocklist": "Blocklist",
"Blocklisted": "Blocklisted", "Blocklisted": "Blocklisted",
"BlocklistHelpText": "Prevents Radarr from automatically grabbing this release again", "BlocklistHelpText": "Prevents Radarr from automatically grabbing this release again",
@@ -928,7 +928,7 @@
"SettingsShowRelativeDates": "Show Relative Dates", "SettingsShowRelativeDates": "Show Relative Dates",
"SettingsShowRelativeDatesHelpText": "Show relative (Today/Yesterday/etc) or absolute dates", "SettingsShowRelativeDatesHelpText": "Show relative (Today/Yesterday/etc) or absolute dates",
"SettingsTheme": "Theme", "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", "SettingsTimeFormat": "Time Format",
"SettingsWeekColumnHeader": "Week Column Header", "SettingsWeekColumnHeader": "Week Column Header",
"SettingsWeekColumnHeaderHelpText": "Shown above each column when week is the active view", "SettingsWeekColumnHeaderHelpText": "Shown above each column when week is the active view",
+9 -2
View File
@@ -1144,7 +1144,14 @@
"InstanceNameHelpText": "Példánynév a böngésző lapon és a syslog alkalmazás neve", "InstanceNameHelpText": "Példánynév a böngésző lapon és a syslog alkalmazás neve",
"RottenTomatoesRating": "Tomato Értékelés", "RottenTomatoesRating": "Tomato Értékelés",
"TotalMovies": "Összes film", "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", "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"
} }
+11 -5
View File
@@ -368,11 +368,11 @@
"ChangeFileDate": "Wijzig Bestandsdatum", "ChangeFileDate": "Wijzig Bestandsdatum",
"CertificationCountryHelpText": "Selecteer Land voor Film Certificatie", "CertificationCountryHelpText": "Selecteer Land voor Film Certificatie",
"CertificationCountry": "Certificatie Land", "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", "CertificateValidation": "Certificaat Validatie",
"BypassProxyForLocalAddresses": "Omzeil Proxy voor Lokale Adressen", "BypassProxyForLocalAddresses": "Omzeil Proxy voor Lokale Adressen",
"Branch": "Branch", "Branch": "Branch",
"BindAddressHelpText": "Geldig IPv4 adres of '*' voor alle interfaces", "BindAddressHelpText": "Geldig IPv4-adres of '*' voor alle interfaces",
"DeleteBackup": "Verwijder Veiligheidskopie", "DeleteBackup": "Verwijder Veiligheidskopie",
"BackupIntervalHelpText": "Tussentijd voor automatische back-up", "BackupIntervalHelpText": "Tussentijd voor automatische back-up",
"Backups": "Veiligheidskopieën", "Backups": "Veiligheidskopieën",
@@ -806,10 +806,10 @@
"AllMoviesInPathHaveBeenImported": "Alle films in {0} zijn geïmporteerd", "AllMoviesInPathHaveBeenImported": "Alle films in {0} zijn geïmporteerd",
"AllFiles": "Alle bestanden", "AllFiles": "Alle bestanden",
"AfterManualRefresh": "Na handmatig verversen", "AfterManualRefresh": "Na handmatig verversen",
"AddToDownloadQueue": "Toegevoegd aan download wachtrij", "AddToDownloadQueue": "Toegevoegd aan downloadwachtrij",
"AddQualityProfile": "Kwaliteitsprofiel toevoegen", "AddQualityProfile": "Kwaliteitsprofiel toevoegen",
"AddNotification": "Notificatie toevoegen", "AddNotification": "Notificatie toevoegen",
"AddedToDownloadQueue": "Aan de download wachtrij toegevoegd", "AddedToDownloadQueue": "Aan de download wachtrijtoegevoegd",
"AddDownloadClient": "Download Client Toevoegen", "AddDownloadClient": "Download Client Toevoegen",
"Add": "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.", "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", "Collections": "Collectie",
"MonitorMovies": "Bewaak Film", "MonitorMovies": "Bewaak Film",
"ApplicationURL": "Applicatie URL", "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"
} }
+10 -3
View File
@@ -1144,7 +1144,14 @@
"CollectionShowPostersHelpText": "Mostrar pôsteres de itens da coleção", "CollectionShowPostersHelpText": "Mostrar pôsteres de itens da coleção",
"RottenTomatoesRating": "Avaliação Tomato", "RottenTomatoesRating": "Avaliação Tomato",
"TotalMovies": "Total de Filmes", "TotalMovies": "Total de Filmes",
"ApplicationURL": "URL da Aplicação", "ApplicationURL": "URL do Aplicativo",
"ApplicationUrlHelpText": "O URL externa deste aplicativo, incluindo http(s)://, porta e base de URL", "ApplicationUrlHelpText": "A URL externa deste aplicativo, incluindo http(s)://, porta e base da URL",
"PreferredProtocol": "Protocolo Preferido" "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 RequiresDiskAccess => false;
public virtual bool IsExclusive => false; public virtual bool IsExclusive => false;
public virtual bool IsTypeExclusive => false; public virtual bool IsTypeExclusive => false;
public virtual bool IsLongRunning => false;
public string Name { get; private set; } public string Name { get; private set; }
public DateTime? LastExecutionTime { get; 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)); 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) var localItem = queuedCommands.OrderByDescending(c => c.Priority)
.ThenBy(c => c.QueuedAt) .ThenBy(c => c.QueuedAt)
.FirstOrDefault(); .FirstOrDefault();
@@ -45,7 +45,39 @@ namespace NzbDrone.Core.Movies
public List<MovieMetadata> GetMoviesWithCollections() public List<MovieMetadata> GetMoviesWithCollections()
{ {
return Query(x => x.CollectionTmdbId > 0); var movieDictionary = new Dictionary<int, MovieMetadata>();
var builder = new SqlBuilder(_database.DatabaseType)
.LeftJoin<MovieMetadata, MovieTranslation>((mm, t) => mm.Id == t.MovieMetadataId)
.Where<MovieMetadata>(x => x.CollectionTmdbId > 0);
_ = _database.QueryJoined<MovieMetadata, MovieTranslation>(
builder,
(metadata, translation) =>
{
MovieMetadata movieEntry;
if (!movieDictionary.TryGetValue(metadata.Id, out movieEntry))
{
movieEntry = metadata;
movieDictionary.Add(movieEntry.Id, movieEntry);
}
if (translation != null)
{
movieEntry.Translations.Add(translation);
}
else
{
// Add a translation to avoid filename builder making another call thinking translations are not loaded
// Optimize this later by pulling translations with metadata always
movieEntry.Translations.Add(new MovieTranslation { Title = movieEntry.Title, Language = Language.English });
}
return movieEntry;
});
return movieDictionary.Values.ToList();
} }
public List<MovieMetadata> GetMoviesByCollectionTmdbId(int collectionId) public List<MovieMetadata> GetMoviesByCollectionTmdbId(int collectionId)
@@ -1,5 +1,6 @@
using FluentValidation; using FluentValidation;
using Newtonsoft.Json; using Newtonsoft.Json;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
@@ -43,7 +44,7 @@ namespace NzbDrone.Core.Notifications.Emby
public bool UpdateLibrary { get; set; } public bool UpdateLibrary { get; set; }
[JsonIgnore] [JsonIgnore]
public string Address => $"{Host}:{Port}"; public string Address => $"{Host.ToUrlHost()}:{Port}";
public bool IsValid => !string.IsNullOrWhiteSpace(Host) && Port > 0; 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 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) .Accept(HttpAccept.Json)
.AddQueryParam("X-Plex-Client-Identifier", _configService.PlexClientIdentifier) .AddQueryParam("X-Plex-Client-Identifier", _configService.PlexClientIdentifier)
.AddQueryParam("X-Plex-Product", BuildInfo.AppName) .AddQueryParam("X-Plex-Product", BuildInfo.AppName)
@@ -21,7 +21,11 @@ namespace NzbDrone.Core.Notifications.Webhook
IndexerFlags = movieFile.IndexerFlags.ToString(); IndexerFlags = movieFile.IndexerFlags.ToString();
Size = movieFile.Size; Size = movieFile.Size;
DateAdded = movieFile.DateAdded; DateAdded = movieFile.DateAdded;
MediaInfo = new WebhookMovieFileMediaInfo(movieFile);
if (movieFile.MediaInfo != null)
{
MediaInfo = new WebhookMovieFileMediaInfo(movieFile);
}
} }
public int Id { get; set; } public int Id { get; set; }
@@ -86,7 +86,6 @@ namespace NzbDrone.Core.Notifications.Xbmc
if (moviePath != null) 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); _logger.Debug("Updating movie {0} (Path: {1}) on XBMC host: {2}", movie, moviePath, settings.Address);
} }
else else
@@ -1,6 +1,7 @@
using System.ComponentModel; using System.ComponentModel;
using FluentValidation; using FluentValidation;
using Newtonsoft.Json; using Newtonsoft.Json;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
@@ -58,7 +59,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
public bool AlwaysUpdate { get; set; } public bool AlwaysUpdate { get; set; }
[JsonIgnore] [JsonIgnore]
public string Address => string.Format("{0}:{1}", Host, Port); public string Address => $"{Host.ToUrlHost()}:{Port}";
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {
+23 -19
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.Extensions.FileSystemGlobbing;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation;
@@ -197,31 +198,34 @@ namespace NzbDrone.Core.Parser
} }
// Case sensitive // 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()) if (match.Groups["czech"].Captures.Cast<Capture>().Any())
{ {
languages.Add(Language.Czech); languages.Add(Language.Czech);
} }
if (caseSensitiveMatch.Groups["polish"].Captures.Cast<Capture>().Any()) if (match.Groups["polish"].Captures.Cast<Capture>().Any())
{ {
languages.Add(Language.Polish); languages.Add(Language.Polish);
} }
if (caseSensitiveMatch.Groups["bulgarian"].Captures.Cast<Capture>().Any()) if (match.Groups["bulgarian"].Captures.Cast<Capture>().Any())
{ {
languages.Add(Language.Bulgarian); languages.Add(Language.Bulgarian);
} }
if (caseSensitiveMatch.Groups["slovak"].Captures.Cast<Capture>().Any()) if (match.Groups["slovak"].Captures.Cast<Capture>().Any())
{ {
languages.Add(Language.Slovak); languages.Add(Language.Slovak);
}
} }
var matches = LanguageRegex.Matches(title); var matches = LanguageRegex.Matches(title);
+2 -2
View File
@@ -56,7 +56,7 @@ namespace NzbDrone.Core.Parser
private static readonly Regex RealRegex = new Regex(@"\b(?<real>REAL)\b", private static readonly Regex RealRegex = new Regex(@"\b(?<real>REAL)\b",
RegexOptions.Compiled); 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); RegexOptions.Compiled | RegexOptions.IgnoreCase);
// Handle cases where no resolution is in the release name; assume if UHD then 4k // 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 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); RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
public static QualityModel ParseQuality(string name) public static QualityModel ParseQuality(string name)
+3 -19
View File
@@ -1,30 +1,14 @@
using System.Net;
using System.Net.Sockets;
using FluentValidation; using FluentValidation;
using FluentValidation.Validators; using FluentValidation.Validators;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Validation namespace NzbDrone.Core.Validation
{ {
public static class IpValidation 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 => return ruleBuilder.Must(x => x.IsValidIpAddress()).WithMessage("Must contain wildcard (*) or a valid IP Address");
{
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");
} }
public static IRuleBuilderOptions<T, string> NotListenAllIp4Address<T>(this IRuleBuilder<T, string> ruleBuilder) public static IRuleBuilderOptions<T, string> NotListenAllIp4Address<T>(this IRuleBuilder<T, string> ruleBuilder)
@@ -1,11 +1,15 @@
using System;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using FluentValidation; using FluentValidation;
using FluentValidation.Validators; using FluentValidation.Validators;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Validation namespace NzbDrone.Core.Validation
{ {
public static class RuleBuilderExtensions 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) public static IRuleBuilderOptions<T, int> ValidId<T>(this IRuleBuilder<T, int> ruleBuilder)
{ {
return ruleBuilder.SetValidator(new GreaterThanValidator(0)); 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) public static IRuleBuilderOptions<T, string> ValidHost<T>(this IRuleBuilder<T, string> ruleBuilder)
{ {
ruleBuilder.SetValidator(new NotEmptyValidator(null)); 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) public static IRuleBuilderOptions<T, string> ValidRootUrl<T>(this IRuleBuilder<T, string> ruleBuilder)
{ {
ruleBuilder.SetValidator(new NotEmptyValidator(null)); 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") public static IRuleBuilderOptions<T, string> ValidUrlBase<T>(this IRuleBuilder<T, string> ruleBuilder, string example = "/radarr")
@@ -2,16 +2,13 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Collections; using NzbDrone.Core.Movies.Collections;
using NzbDrone.Core.Movies.Commands; using NzbDrone.Core.Movies.Commands;
using NzbDrone.Core.Movies.Events; using NzbDrone.Core.Movies.Events;
using NzbDrone.Core.Movies.Translations;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.SignalR; using NzbDrone.SignalR;
using Radarr.Http; using Radarr.Http;
@@ -29,8 +26,6 @@ namespace Radarr.Api.V3.Collections
private readonly IMovieCollectionService _collectionService; private readonly IMovieCollectionService _collectionService;
private readonly IMovieService _movieService; private readonly IMovieService _movieService;
private readonly IMovieMetadataService _movieMetadataService; private readonly IMovieMetadataService _movieMetadataService;
private readonly IMovieTranslationService _movieTranslationService;
private readonly IConfigService _configService;
private readonly IBuildFileNames _fileNameBuilder; private readonly IBuildFileNames _fileNameBuilder;
private readonly INamingConfigService _namingService; private readonly INamingConfigService _namingService;
private readonly IManageCommandQueue _commandQueueManager; private readonly IManageCommandQueue _commandQueueManager;
@@ -39,8 +34,6 @@ namespace Radarr.Api.V3.Collections
IMovieCollectionService collectionService, IMovieCollectionService collectionService,
IMovieService movieService, IMovieService movieService,
IMovieMetadataService movieMetadataService, IMovieMetadataService movieMetadataService,
IMovieTranslationService movieTranslationService,
IConfigService configService,
IBuildFileNames fileNameBuilder, IBuildFileNames fileNameBuilder,
INamingConfigService namingService, INamingConfigService namingService,
IManageCommandQueue commandQueueManager) IManageCommandQueue commandQueueManager)
@@ -49,8 +42,6 @@ namespace Radarr.Api.V3.Collections
_collectionService = collectionService; _collectionService = collectionService;
_movieService = movieService; _movieService = movieService;
_movieMetadataService = movieMetadataService; _movieMetadataService = movieMetadataService;
_movieTranslationService = movieTranslationService;
_configService = configService;
_fileNameBuilder = fileNameBuilder; _fileNameBuilder = fileNameBuilder;
_namingService = namingService; _namingService = namingService;
_commandQueueManager = commandQueueManager; _commandQueueManager = commandQueueManager;
@@ -141,29 +132,16 @@ namespace Radarr.Api.V3.Collections
private IEnumerable<CollectionResource> MapToResource(List<MovieCollection> collections) private IEnumerable<CollectionResource> MapToResource(List<MovieCollection> collections)
{ {
var configLanguage = (Language)_configService.MovieInfoLanguage;
// Avoid calling for naming spec on every movie in filenamebuilder // Avoid calling for naming spec on every movie in filenamebuilder
var namingConfig = _namingService.GetConfig(); var namingConfig = _namingService.GetConfig();
var collectionMovies = _movieMetadataService.GetMoviesWithCollections();
var allCollectionMovies = _movieMetadataService.GetMoviesWithCollections()
.GroupBy(x => x.CollectionTmdbId)
.ToDictionary(x => x.Key, x => (IEnumerable<MovieMetadata>)x);
var translations = _movieTranslationService.GetAllTranslationsForLanguage(configLanguage);
var tdict = translations.ToDictionary(x => x.MovieMetadataId);
foreach (var collection in collections) foreach (var collection in collections)
{ {
var resource = collection.ToResource(); var resource = collection.ToResource();
allCollectionMovies.TryGetValue(collection.TmdbId, out var collectionMovies); foreach (var movie in collectionMovies.Where(m => m.CollectionTmdbId == collection.TmdbId))
foreach (var movie in collectionMovies)
{ {
var translation = GetTranslationFromDict(tdict, movie, configLanguage);
movie.Translations.Add(translation);
var movieResource = movie.ToResource(); var movieResource = movie.ToResource();
movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { MovieMetadata = movie }, namingConfig); movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { MovieMetadata = movie }, namingConfig);
@@ -177,17 +155,11 @@ namespace Radarr.Api.V3.Collections
private CollectionResource MapToResource(MovieCollection collection) private CollectionResource MapToResource(MovieCollection collection)
{ {
var resource = collection.ToResource(); var resource = collection.ToResource();
var namingConfig = _namingService.GetConfig();
foreach (var movie in _movieMetadataService.GetMoviesByCollectionTmdbId(collection.TmdbId)) foreach (var movie in _movieMetadataService.GetMoviesByCollectionTmdbId(collection.TmdbId))
{ {
var translations = _movieTranslationService.GetAllTranslationsForMovieMetadata(movie.Id);
var translation = GetMovieTranslation(translations, movie, (Language)_configService.MovieInfoLanguage);
movie.Translations.Add(translation);
var movieResource = movie.ToResource(); var movieResource = movie.ToResource();
movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { MovieMetadata = movie }, namingConfig); movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { MovieMetadata = movie });
resource.Movies.Add(movieResource); resource.Movies.Add(movieResource);
} }
@@ -195,54 +167,6 @@ namespace Radarr.Api.V3.Collections
return resource; return resource;
} }
private MovieTranslation GetMovieTranslation(List<MovieTranslation> translations, MovieMetadata movie, Language configLanguage)
{
if (configLanguage == Language.Original)
{
return new MovieTranslation
{
Title = movie.OriginalTitle,
Overview = movie.Overview
};
}
var translation = translations.FirstOrDefault(t => t.Language == configLanguage && t.MovieMetadataId == movie.Id);
if (translation == null)
{
translation = new MovieTranslation
{
Title = movie.Title,
Language = Language.English
};
}
return translation;
}
private MovieTranslation GetTranslationFromDict(Dictionary<int, MovieTranslation> translations, MovieMetadata movie, Language configLanguage)
{
if (configLanguage == Language.Original)
{
return new MovieTranslation
{
Title = movie.OriginalTitle,
Overview = movie.Overview
};
}
if (!translations.TryGetValue(movie.Id, out var translation))
{
translation = new MovieTranslation
{
Title = movie.Title,
Language = Language.English
};
}
return translation;
}
[NonAction] [NonAction]
public void Handle(CollectionAddedEvent message) public void Handle(CollectionAddedEvent message)
{ {
@@ -33,9 +33,9 @@ namespace Radarr.Api.V3.Config
_userService = userService; _userService = userService;
SharedValidator.RuleFor(c => c.BindAddress) SharedValidator.RuleFor(c => c.BindAddress)
.ValidIp4Address() .ValidIpAddress()
.NotListenAllIp4Address() .NotListenAllIp4Address()
.When(c => c.BindAddress != "*"); .When(c => c.BindAddress != "*" && c.BindAddress != "localhost");
SharedValidator.RuleFor(c => c.Port).ValidPort(); SharedValidator.RuleFor(c => c.Port).ValidPort();
+2 -2
View File
@@ -54,9 +54,9 @@ namespace Radarr.Api.V3.System
} }
[HttpGet("status")] [HttpGet("status")]
public object GetStatus() public SystemResource GetStatus()
{ {
return new return new SystemResource
{ {
AppName = BuildInfo.AppName, AppName = BuildInfo.AppName,
InstanceName = _configFileProvider.InstanceName, InstanceName = _configFileProvider.InstanceName,
@@ -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; }
}
}
+181 -1
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}": { "/api/v3/qualitydefinition/{id}": {
"put": { "put": {
"tags": [ "tags": [
@@ -7635,7 +7654,24 @@
], ],
"responses": { "responses": {
"200": { "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", "type": "boolean",
"readOnly": true "readOnly": true
}, },
"isLongRunning": {
"type": "boolean",
"readOnly": true
},
"name": { "name": {
"type": "string", "type": "string",
"nullable": true, "nullable": true,
@@ -9017,6 +9057,13 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"DatabaseType": {
"enum": [
"sqLite",
"postgreSQL"
],
"type": "string"
},
"DelayProfileResource": { "DelayProfileResource": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -11349,6 +11396,16 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"PingResource": {
"type": "object",
"properties": {
"status": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"ProfileFormatItemResource": { "ProfileFormatItemResource": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -12107,6 +12164,14 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"RuntimeMode": {
"enum": [
"console",
"service",
"tray"
],
"type": "string"
},
"SelectOption": { "SelectOption": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -12164,6 +12229,121 @@
], ],
"type": "string" "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": { "TMDbCountryCode": {
"enum": [ "enum": [
"au", "au",
@@ -162,39 +162,7 @@ namespace Radarr.Http.Extensions
remoteIP = remoteIP.MapToIPv4(); remoteIP = remoteIP.MapToIPv4();
} }
var remoteAddress = remoteIP.ToString(); return 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;
} }
public static void DisableCache(this IHeaderDictionary headers) public static void DisableCache(this IHeaderDictionary headers)
+39
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"
});
}
}
}
+7
View File
@@ -0,0 +1,7 @@
namespace Radarr.Http.Ping
{
public class PingResource
{
public string Status { get; set; }
}
}
+7 -1
View File
@@ -69,12 +69,18 @@ namespace Radarr.Http.REST
foreach (var resource in resourceArgs) 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); ValidateResource(resource, skipValidate, skipShared);
} }
} }
var attributes = descriptor.MethodInfo.CustomAttributes; 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)) if (context.ActionArguments.TryGetValue("id", out var idObj))
{ {