1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-03-23 17:14:18 -04:00

Compare commits

..

12 Commits

Author SHA1 Message Date
Mark McDowall
dedd3af2f1 New: Parse Chinese season packs and multi-episode releases
Closes #8042
2025-09-01 19:08:27 -07:00
Stevie Robinson
1610e54650 correct migration number and fix for postgres 2025-09-01 15:08:37 -07:00
Mark McDowall
c40fbeed50 New: Parse Chinese and English titles as separate titles
Closes #8035
2025-09-01 15:08:31 -07:00
Mark McDowall
b57e7e2db0 New: Parse releases using Temporada as the season
Closes #8030
2025-09-01 15:08:31 -07:00
Mark McDowall
478866b2bb New: Changing icon during import to blue
Closes #7992
2025-09-01 15:08:24 -07:00
Mark McDowall
ae201f5299 Use react-query for queue UI
New: Season packs and multi-episode releases will show as a single item in the queue
Closes #6537
2025-09-01 15:08:19 -07:00
Mark McDowall
642f4f97bc Add v5 queue endpoints 2025-09-01 15:08:19 -07:00
Alexander WB
37cb978f18 New: RQBit download client
Co-authored-by: Mark Mendoza <markolo25@gmail.com>
2025-09-01 15:08:03 -07:00
康小广
7fdc4d6638 Follow redirects when fetching Custom Lists 2025-09-01 14:59:34 -07:00
grapexy
309b55fe38 New: Georgian language 2025-09-01 14:58:40 -07:00
oxfordllama
d6f265c7b5 Subtitles indexer flag to indicate BTN releases with subtitles 2025-09-01 14:58:19 -07:00
Weblate
e757dca038 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: ArLab1 <arnaud.laberge@hotmail.com>
Co-authored-by: Gallyam Biktashev <gallyamb@gmail.com>
Co-authored-by: GkhnGRBZ <gkhn.gurbuz@hotmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Hugoren Martinako <aumpfbahn@gmail.com>
Co-authored-by: NanderTGA <nander.roobaert@gmail.com>
Co-authored-by: ReDFiRe <wwsoft@abv.bg>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Xoores <servarr-35466@xoores.cz>
Co-authored-by: fordas <fordas15@gmail.com>
Co-authored-by: mrchonks <chonkstv@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/tr/
Translation: Servarr/Sonarr
2025-09-01 14:56:39 -07:00
44 changed files with 1308 additions and 67 deletions

View File

@@ -90,7 +90,7 @@ function QueueStatus(props: QueueStatusProps) {
if (trackedDownloadState === 'importing') {
title += ` - ${translate('Importing')}`;
iconKind = kinds.PURPLE;
iconKind = kinds.PRIMARY;
}
if (trackedDownloadState === 'failedPending') {

View File

@@ -26,6 +26,10 @@
color: var(--warningColor);
}
.primary {
color: var(--primaryColor);
}
.purple {
color: var(--purple);
}

View File

@@ -6,6 +6,7 @@ interface CssExports {
'disabled': string;
'info': string;
'pink': string;
'primary': string;
'purple': string;
'success': string;
'warning': string;

View File

@@ -390,5 +390,12 @@ namespace NzbDrone.Common.Http
return this;
}
public virtual HttpRequestBuilder AllowRedirect(bool allowAutoRedirect = true)
{
AllowAutoRedirect = allowAutoRedirect;
return this;
}
}
}

View File

@@ -0,0 +1,234 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.RQBit;
using NzbDrone.Core.MediaFiles.TorrentInfo;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.RQBitTests
{
[TestFixture]
public class RQBitFixture : DownloadClientFixtureBase<RQBit>
{
protected RQBitTorrent _queued;
protected RQBitTorrent _downloading;
protected RQBitTorrent _failed;
protected RQBitTorrent _completed;
[SetUp]
public void Setup()
{
Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new RQbitSettings
{
Host = "127.0.0.1",
Port = 3030,
UseSsl = false
};
_queued = new RQBitTorrent
{
Hash = "HASH",
IsFinished = false,
IsActive = false,
Name = _title,
TotalSize = 1000,
RemainingSize = 1000,
Path = "somepath"
};
_downloading = new RQBitTorrent
{
Hash = "HASH",
IsFinished = false,
IsActive = true,
Name = _title,
TotalSize = 1000,
RemainingSize = 100,
Path = "somepath"
};
_failed = new RQBitTorrent
{
Hash = "HASH",
IsFinished = false,
IsActive = false,
Name = _title,
TotalSize = 1000,
RemainingSize = 1000,
Path = "somepath"
};
_completed = new RQBitTorrent
{
Hash = "HASH",
IsFinished = true,
IsActive = false,
Name = _title,
TotalSize = 1000,
RemainingSize = 0,
Path = "somepath"
};
Mocker.GetMock<ITorrentFileInfoReader>()
.Setup(s => s.GetHashFromTorrentFile(It.IsAny<byte[]>()))
.Returns("CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951");
}
protected void GivenSuccessfulDownload()
{
Mocker.GetMock<IRQbitProxy>()
.Setup(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<RQbitSettings>()))
.Returns("CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951");
Mocker.GetMock<IRQbitProxy>()
.Setup(s => s.AddTorrentFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<RQbitSettings>()))
.Returns("CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951");
}
protected virtual void GivenTorrents(List<RQBitTorrent> torrents)
{
if (torrents == null)
{
torrents = new List<RQBitTorrent>();
}
Mocker.GetMock<IRQbitProxy>()
.Setup(s => s.GetTorrents(It.IsAny<RQbitSettings>()))
.Returns(torrents);
}
protected void PrepareClientToReturnQueuedItem()
{
GivenTorrents(new List<RQBitTorrent>
{
_queued
});
}
protected void PrepareClientToReturnDownloadingItem()
{
GivenTorrents(new List<RQBitTorrent>
{
_downloading
});
}
protected void PrepareClientToReturnFailedItem()
{
GivenTorrents(new List<RQBitTorrent>
{
_failed
});
}
protected void PrepareClientToReturnCompletedItem()
{
GivenTorrents(new List<RQBitTorrent>
{
_completed
});
}
[Test]
public void queued_item_should_have_required_properties()
{
PrepareClientToReturnQueuedItem();
var item = Subject.GetItems().Single();
VerifyPaused(item);
}
[Test]
public void downloading_item_should_have_required_properties()
{
PrepareClientToReturnDownloadingItem();
var item = Subject.GetItems().Single();
VerifyDownloading(item);
}
[Test]
public void failed_item_should_have_required_properties()
{
PrepareClientToReturnFailedItem();
var item = Subject.GetItems().Single();
VerifyPaused(item);
}
[Test]
public void completed_download_should_have_required_properties()
{
PrepareClientToReturnCompletedItem();
var item = Subject.GetItems().Single();
VerifyCompleted(item);
}
[Test]
public async Task Download_should_return_unique_id()
{
GivenSuccessfulDownload();
var remoteEpisode = CreateRemoteEpisode();
var id = await Subject.Download(remoteEpisode, CreateIndexer());
id.Should().NotBeNullOrEmpty();
}
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
{
GivenSuccessfulDownload();
var remoteEpisode = CreateRemoteEpisode();
remoteEpisode.Release.DownloadUrl = magnetUrl;
var id = await Subject.Download(remoteEpisode, CreateIndexer());
id.Should().Be(expectedHash);
}
[Test]
public void should_return_status_with_outputdirs()
{
var result = Subject.GetStatus();
result.IsLocalhost.Should().BeTrue();
}
[Test]
public void GetItems_should_ignore_torrents_with_empty_path()
{
var torrents = new List<RQBitTorrent>
{
new RQBitTorrent { Name = "Test1", Hash = "Hash1", Path = "" },
new RQBitTorrent { Name = "Test2", Hash = "Hash2", Path = "/valid/path" }
};
GivenTorrents(torrents);
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().Title.Should().Be("Test2");
}
[Test]
public void GetItems_should_ignore_torrents_with_relative_path()
{
var torrents = new List<RQBitTorrent>
{
new RQBitTorrent { Name = "Test1", Hash = "Hash1", Path = "./relative/path" },
new RQBitTorrent { Name = "Test2", Hash = "Hash2", Path = "/absolute/path" }
};
GivenTorrents(torrents);
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().Title.Should().Be("Test2");
}
}
}

View File

@@ -27,6 +27,7 @@
"TvrageID":"4055",
"ImdbID":"0320037",
"InfoHash":"123",
"Tags": ["Subtitles"],
"DownloadURL":"https:\/\/broadcasthe.net\/torrents.php?action=download&id=123&authkey=123&torrent_pass=123"
},
"1234":{
@@ -54,8 +55,9 @@
"TvrageID":"38472",
"ImdbID":"2377081",
"InfoHash":"1234",
"Tags": [],
"DownloadURL":"https:\/\/broadcasthe.net\/torrents.php?action=download&id=1234&authkey=1234&torrent_pass=1234"
}},
"results":"117927"
}
}
}

View File

@@ -64,6 +64,8 @@ namespace NzbDrone.Core.Test.IndexerTests.BroadcastheNetTests
torrentInfo.Container.Should().Be("MP4");
torrentInfo.Codec.Should().Be("x264");
torrentInfo.Resolution.Should().Be("SD");
torrentInfo.IndexerFlags.Should().HaveFlag(IndexerFlags.Subtitles);
}
private void VerifyBackOff()

View File

@@ -60,7 +60,8 @@ namespace NzbDrone.Core.Test.Languages
new object[] { 48, Language.Uzbek },
new object[] { 49, Language.Malay },
new object[] { 50, Language.Urdu },
new object[] { 51, Language.Romansh }
new object[] { 51, Language.Romansh },
new object[] { 52, Language.Georgian }
};
public static object[] ToIntCases =
@@ -115,7 +116,8 @@ namespace NzbDrone.Core.Test.Languages
new object[] { Language.Uzbek, 48 },
new object[] { Language.Malay, 49 },
new object[] { Language.Urdu, 50 },
new object[] { Language.Romansh, 51 }
new object[] { Language.Romansh, 51 },
new object[] { Language.Georgian, 52 }
};
[Test]

View File

@@ -580,6 +580,8 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
[TestCase("ice", "IS")]
[TestCase("dut", "NL")]
[TestCase("nor", "NO")]
[TestCase("geo", "KA")]
[TestCase("kat", "KA")]
public void should_format_languagecodes_properly(string language, string code)
{
_namingConfig.StandardEpisodeFormat = "{Series.Title}.S{season:00}E{episode:00}.{Episode.Title}.{MEDIAINFO.FULL}";

View File

@@ -69,5 +69,15 @@ namespace NzbDrone.Core.Test.ParserTests
var result = IsoLanguages.Find(isoCode);
result.Language.Should().Be(Language.Romansh);
}
[TestCase("ka")]
[TestCase("geo")]
[TestCase("kat")]
[TestCase("ka-GE")]
public void should_return_georgian(string isoCode)
{
var result = IsoLanguages.Find(isoCode);
result.Language.Should().Be(Language.Georgian);
}
}
}

View File

@@ -175,6 +175,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[abc] My Series - 01 []")]
[TestCase("[ABC字幕组] My Series - 01 [HDTV]")]
[TestCase("[喵萌奶茶屋&LoliHouse] / Kengan Ashura - 17 [WebRip 1080p HEVC-10bit AAC][]")]
[TestCase("Series.Towards.You.S01.国语音轨.2023.1080p.NF.WEB-DL.H264.DDP2.0-SeeWEB")]
public void should_parse_language_chinese(string postTitle)
{
var result = LanguageParser.ParseLanguages(postTitle);
@@ -529,6 +530,22 @@ namespace NzbDrone.Core.Test.ParserTests
result.Should().Contain(Language.Romansh);
}
[TestCase("Title.the.Series.2025.S01.Georgian.1080p.WEB-DL.h264-RlsGrp")]
[TestCase("Title.the.Series.2025.S01.Geo.1080p.WEB-DL.h264-RlsGrp")]
[TestCase("Title.the.Series.2025.S01.KA.1080p.WEB-DL.h264-RlsGrp")]
public void should_parse_language_georgian(string postTitle)
{
var result = LanguageParser.ParseLanguages(postTitle);
result.Should().Contain(Language.Georgian);
}
[TestCase("Title.the.Series.2025.S01.RU-KA.1080p.WEB-DL.h264-RlsGrp")]
public void should_parse_language_russian_and_georgian(string postTitle)
{
var result = LanguageParser.ParseLanguages(postTitle);
result.Should().BeEquivalentTo(new[] { Language.Russian, Language.Georgian });
}
[TestCase("Name (2020) - S01E20 - [AAC 2.0].testtitle.default.eng.forced.ass", new[] { "default", "forced" }, "testtitle", "English")]
[TestCase("Name (2020) - S01E20 - [AAC 2.0].eng.default.testtitle.forced.ass", new[] { "default", "forced" }, "testtitle", "English")]
[TestCase("Name (2020) - S01E20 - [AAC 2.0].default.eng.testtitle.forced.ass", new[] { "default", "forced" }, "testtitle", "English")]

View File

@@ -40,6 +40,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[GROUP] Series: Title (2023) (Season 1) [BDRip] [1080p Dual Audio HEVC 10-bits DDP] (serie) (Batch)", "Series: Title (2023)", 1)]
[TestCase("[GROUP] Series: Title (2023) (Season 1) [BDRip] [1080p Dual Audio HEVC 10-bit DDP] (serie) (Batch)", "Series: Title (2023)", 1)]
[TestCase("Seriesless (2016/S01/WEB-DL/1080p/AC3 5.1/DUAL/SUB)", "Seriesless (2016)", 1)]
[TestCase("Series (1994) - Temporada 10", "Series (1994)", 10)]
public void should_parse_full_season_release(string postTitle, string title, int season)
{
var result = Parser.Parser.ParseTitle(postTitle);

View File

@@ -179,6 +179,8 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Mini Title (Miniserie) (2024/S01E07/DSNP/WEB-DL/1080p/ESP/EAC3 5.1/ING/EAC3 5.1 Atmos/SUBS) SPWEB", "Mini Title (2024)", 1, 7)]
[TestCase("Series.S006E18.Some.Title.Name-Part.1.1080p.WEB-DL.AAC2.0.H.264-Release", "Series", 6, 18)]
[TestCase("Series.2006.S006E18.Some.Title.Name-Part.1.1080p.WEB-DL.AAC2.0.H.264-Release", "Series 2006", 6, 18)]
[TestCase("我的人间烟火.Fireworks.Series.S01E01.2023.V2.1080p.WEB-DL.H264.AAC-SeeWEB", "Fireworks Series", 1, 1)]
[TestCase("Fireworks.Series.S01E01.2023.V2.1080p.WEB-DL.H264.AAC-SeeWEB", "Fireworks Series", 1, 1)]
// [TestCase("", "", 0, 0)]
public void should_parse_single_episode(string postTitle, string title, int seasonNumber, int episodeNumber)

View File

@@ -147,5 +147,33 @@ namespace NzbDrone.Core.Test.ParserTests
result.SeriesTitle.Should().Be(title);
result.FullSeason.Should().BeFalse();
}
[TestCase("【高清剧集网发布 www.DDHDTV.com】当我飞奔向你[全24集][+].Series.Towards.You.S01.2023.1080p.NF.WEB-DL.H264.DDP2.0-SeeWEB", "Series Towards You", "SeeWEB", 1)]
public void should_parse_full_season_releases(string postTitle, string title, string releaseGroup, int season)
{
postTitle = XmlCleaner.ReplaceUnicode(postTitle);
var result = Parser.Parser.ParseTitle(postTitle);
result.Should().NotBeNull();
result.ReleaseGroup.Should().Be(releaseGroup);
result.EpisodeNumbers.Should().BeEmpty();
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
result.SeriesTitle.Should().Be(title);
result.FullSeason.Should().BeTrue();
result.SeasonNumber.Should().Be(season);
}
[TestCase("【高清剧集网发布 www.DDHDTV.com】当我飞奔向你[第01-15集][+].Series.Towards.You.S01.2023.2160p.YK.WEB-DL.H265.AAC-BlackTV", "Series Towards You", "BlackTV", 1, new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 })]
public void should_parse_multi_episode_release(string postTitle, string title, string releaseGroup, int season, int[] episodes)
{
postTitle = XmlCleaner.ReplaceUnicode(postTitle);
var result = Parser.Parser.ParseTitle(postTitle);
result.SeasonNumber.Should().Be(season);
result.EpisodeNumbers.Should().BeEquivalentTo(episodes);
result.SeriesTitle.Should().Be(title);
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
result.FullSeason.Should().BeFalse();
}
}
}

View File

@@ -9,7 +9,7 @@ using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(229)]
[Migration(220)]
public class enable_season_pack_seeding_goal : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
@@ -21,37 +21,45 @@ namespace NzbDrone.Core.Datastore.Migration
{
var updatedIndexers = new List<object>();
using var selectCommand = conn.CreateCommand();
selectCommand.Transaction = tran;
selectCommand.CommandText = "SELECT * FROM \"Indexers\"";
using var reader = selectCommand.ExecuteReader();
while (reader.Read())
using (var selectCommand = conn.CreateCommand())
{
var idIndex = reader.GetOrdinal("Id");
var settingsIndex = reader.GetOrdinal("Settings");
selectCommand.Transaction = tran;
selectCommand.CommandText = "SELECT * FROM \"Indexers\"";
var id = reader.GetInt32(idIndex);
var settings = Json.Deserialize<Dictionary<string, object>>(reader.GetString(settingsIndex));
if (settings.TryGetValue("seedCriteria", out var seedCriteriaToken) && seedCriteriaToken is JObject seedCriteria)
using (var reader = selectCommand.ExecuteReader())
{
if (seedCriteria?["seasonPackSeedTime"] != null)
var indexerSettings = new List<(int Id, string Settings)>();
while (reader.Read())
{
seedCriteria["seasonPackSeedGoal"] = 1;
var idIndex = reader.GetOrdinal("Id");
var settingsIndex = reader.GetOrdinal("Settings");
if (seedCriteria["seedRatio"] != null)
indexerSettings.Add((reader.GetInt32(idIndex), reader.GetString(settingsIndex)));
}
foreach (var indexerSetting in indexerSettings)
{
var settings = Json.Deserialize<Dictionary<string, object>>(indexerSetting.Settings);
if (settings.TryGetValue("seedCriteria", out var seedCriteriaToken) && seedCriteriaToken is JObject seedCriteria)
{
seedCriteria["seasonPackSeedRatio"] = seedCriteria["seedRatio"];
if (seedCriteria?["seasonPackSeedTime"] != null)
{
seedCriteria["seasonPackSeedGoal"] = 1;
if (seedCriteria["seedRatio"] != null)
{
seedCriteria["seasonPackSeedRatio"] = seedCriteria["seedRatio"];
}
updatedIndexers.Add(new
{
Settings = settings.ToJson(),
Id = indexerSetting.Id,
});
}
}
updatedIndexers.Add(new
{
Settings = settings.ToJson(),
Id = id,
});
}
}
}

View File

@@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Download.Clients.RQBit
{
public class RQBit : TorrentClientBase<RQbitSettings>
{
private readonly IRQbitProxy _proxy;
private readonly IDownloadSeedConfigProvider _downloadSeedConfigProvider;
public override string Name => "RQBit";
public RQBit(IRQbitProxy proxy,
ITorrentFileInfoReader torrentFileInfoReader,
IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IDownloadSeedConfigProvider downloadSeedConfigProvider,
ILocalizationService localizationService,
IBlocklistService blocklistService,
Logger logger)
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, localizationService, blocklistService, logger)
{
_proxy = proxy;
_downloadSeedConfigProvider = downloadSeedConfigProvider;
}
public override IEnumerable<DownloadClientItem> GetItems()
{
var torrents = _proxy.GetTorrents(Settings);
_logger.Debug("Retrieved metadata of {0} torrents in client", torrents.Count);
var items = new List<DownloadClientItem>();
foreach (var torrent in torrents)
{
// Ignore torrents with an empty path
if (torrent.Path.IsNullOrWhiteSpace())
{
_logger.Warn("Torrent '{0}' has an empty download path and will not be processed", torrent.Name);
continue;
}
if (torrent.Path.StartsWith("."))
{
_logger.Warn("Torrent '{0}' has a download path starting with '.' and will not be processed", torrent.Name);
continue;
}
var item = new DownloadClientItem();
item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false);
item.Title = torrent.Name;
item.DownloadId = torrent.Hash;
item.Message = torrent.Message;
item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.Path));
item.TotalSize = torrent.TotalSize;
item.RemainingSize = torrent.RemainingSize;
item.Category = torrent.Category;
item.SeedRatio = torrent.Ratio;
if (torrent.DownRate > 0)
{
var secondsLeft = torrent.RemainingSize / torrent.DownRate;
item.RemainingTime = TimeSpan.FromSeconds(secondsLeft);
}
else
{
item.RemainingTime = TimeSpan.Zero;
}
if (torrent.IsFinished)
{
item.Status = DownloadItemStatus.Completed;
}
else if (torrent.IsActive)
{
item.Status = DownloadItemStatus.Downloading;
}
else if (!torrent.IsActive)
{
item.Status = DownloadItemStatus.Paused;
}
// Grab cached seedConfig
var seedConfig = _downloadSeedConfigProvider.GetSeedConfiguration(torrent.Hash);
if (item.DownloadClientInfo.RemoveCompletedDownloads && torrent.IsFinished && seedConfig != null)
{
var canRemove = false;
if (torrent.Ratio / 1000.0 >= seedConfig.Ratio)
{
_logger.Trace($"{item} has met seed ratio goal of {seedConfig.Ratio}");
canRemove = true;
}
else if (DateTimeOffset.Now - DateTimeOffset.FromUnixTimeSeconds(torrent.FinishedTime) >= seedConfig.SeedTime)
{
_logger.Trace($"{item} has met seed time goal of {seedConfig.SeedTime} minutes");
canRemove = true;
}
else
{
_logger.Trace($"{item} seeding goals have not yet been reached");
}
// Check if torrent is finished and if it exceeds cached seedConfig
item.CanMoveFiles = item.CanBeRemoved = canRemove;
}
items.Add(item);
}
return items;
}
public override void RemoveItem(DownloadClientItem item, bool deleteData)
{
_proxy.RemoveTorrent(item.DownloadId, deleteData, Settings);
}
public override DownloadClientInfo GetStatus()
{
return new DownloadClientInfo
{
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
};
}
protected override void Test(List<ValidationFailure> failures)
{
failures.AddIfNotNull(TestConnection());
if (failures.HasErrors())
{
return;
}
failures.AddIfNotNull(TestVersion());
}
private ValidationFailure TestConnection()
{
var version = _proxy.GetVersion(Settings);
return null;
}
private ValidationFailure TestVersion()
{
try
{
var versionString = _proxy.GetVersion(Settings);
if (string.IsNullOrWhiteSpace(versionString))
{
return new ValidationFailure("", "Unable to determine RQBit version. Please check that RQBit is running and accessible.");
}
// Parse version string to Version object
var versionMatch = System.Text.RegularExpressions.Regex.Match(versionString, @"(\d+)\.(\d+)\.(\d+)");
if (!versionMatch.Success)
{
return new ValidationFailure("", $"Unable to parse RQBit version '{versionString}'. Please check that RQBit is running and accessible.");
}
var major = int.Parse(versionMatch.Groups[1].Value);
var minor = int.Parse(versionMatch.Groups[2].Value);
var patch = int.Parse(versionMatch.Groups[3].Value);
var apiVersion = new Version(major, minor, patch);
var minimumVersion = new Version(8, 0, 0);
if (apiVersion < minimumVersion)
{
return new ValidationFailure("", $"RQBit version {apiVersion} is not supported. Please upgrade to version {minimumVersion} or higher.");
}
return null;
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to determine RQBit version");
return new ValidationFailure("", "Unable to determine RQBit version. Please check that RQBit is running and accessible.");
}
}
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
{
return _proxy.AddTorrentFromUrl(magnetLink, Settings);
}
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
{
return _proxy.AddTorrentFromFile(filename, fileContent, Settings);
}
}
}

View File

@@ -0,0 +1,8 @@
namespace NzbDrone.Core.Download.Clients.RQBit;
public class RQBitFile
{
public string FileName { get; set; }
public int FileSize { get; set; }
public int FileDownloaded { get; set; }
}

View File

@@ -0,0 +1,20 @@
namespace NzbDrone.Core.Download.Clients.RQBit
{
public class RQBitTorrent
{
public long Id { get; set; }
public string Name { get; set; }
public string Hash { get; set; }
public long TotalSize { get; set; }
public long RemainingSize { get; set; }
public string Category { get; set; }
public double? Ratio { get; set; }
public long DownRate { get; set; }
public bool IsFinished { get; set; }
public bool IsActive { get; set; }
public bool HasError { get; set; }
public long FinishedTime { get; set; }
public string Path { get; set; }
public string Message { get; set; }
}
}

View File

@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Net;
using Newtonsoft.Json;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Download.Clients.RQBit.ResponseModels;
namespace NzbDrone.Core.Download.Clients.RQBit
{
public interface IRQbitProxy
{
bool IsApiSupported(RQbitSettings settings);
string GetVersion(RQbitSettings settings);
List<RQBitTorrent> GetTorrents(RQbitSettings settings);
void RemoveTorrent(string hash, bool removeData, RQbitSettings settings);
string AddTorrentFromUrl(string torrentUrl, RQbitSettings settings);
string AddTorrentFromFile(string fileName, byte[] fileContent, RQbitSettings settings);
void SetTorrentLabel(string hash, string label, RQbitSettings settings);
bool HasHashTorrent(string hash, RQbitSettings settings);
}
public class RQbitProxy : IRQbitProxy
{
private readonly IHttpClient _httpClient;
private readonly Logger _logger;
public RQbitProxy(IHttpClient httpClient, Logger logger)
{
_httpClient = httpClient;
_logger = logger;
}
public bool IsApiSupported(RQbitSettings settings)
{
var request = BuildRequest(settings).Resource("");
request.SuppressHttpError = true;
try
{
var response = _httpClient.Get(request.Build());
// Check if we can connect and get a valid response
if (response.StatusCode == HttpStatusCode.OK)
{
var rootResponse = JsonConvert.DeserializeObject<RootResponse>(response.Content);
return rootResponse?.Version.IsNotNullOrWhiteSpace() ?? false;
}
return false;
}
catch (Exception ex)
{
_logger.Debug(ex, "RQBit API not supported or not reachable");
return false;
}
}
public string GetVersion(RQbitSettings settings)
{
var request = BuildRequest(settings).Resource("");
var response = _httpClient.Get(request.Build());
if (response.StatusCode == HttpStatusCode.OK)
{
var rootResponse = JsonConvert.DeserializeObject<RootResponse>(response.Content);
return rootResponse.Version;
}
else
{
_logger.Error("Failed to get torrent version");
}
return string.Empty;
}
public List<RQBitTorrent> GetTorrents(RQbitSettings settings)
{
var result = new List<RQBitTorrent>();
var request = BuildRequest(settings).Resource("/torrents?with_stats=true");
var response = _httpClient.Get(request.Build());
if (response.StatusCode != HttpStatusCode.OK)
{
_logger.Error("Failed to get torrent list with stats");
return result;
}
var torrentListWithStats = JsonConvert.DeserializeObject<ListTorrentsWithStatsResponse>(response.Content);
if (torrentListWithStats?.Torrents != null)
{
foreach (var torrentWithStats in torrentListWithStats.Torrents)
{
try
{
var torrent = new RQBitTorrent();
torrent.Id = torrentWithStats.Id;
torrent.Name = torrentWithStats.Name;
torrent.Hash = torrentWithStats.InfoHash;
torrent.TotalSize = torrentWithStats.Stats.TotalBytes;
torrent.Path = torrentWithStats.OutputFolder + torrentWithStats.Name;
var statsLive = torrentWithStats.Stats.Live;
if (statsLive?.DownloadSpeed != null)
{
// Convert mib/sec to bytes per second
torrent.DownRate = (long)(statsLive.DownloadSpeed.Mbps * 1048576);
}
torrent.RemainingSize = torrentWithStats.Stats.TotalBytes - torrentWithStats.Stats.ProgressBytes;
// Avoid division by zero
if (torrentWithStats.Stats.ProgressBytes > 0)
{
torrent.Ratio = (double)torrentWithStats.Stats.UploadedBytes / torrentWithStats.Stats.ProgressBytes;
}
else
{
torrent.Ratio = 0;
}
torrent.IsFinished = torrentWithStats.Stats.Finished;
torrent.IsActive = torrentWithStats.Stats.State == TorrentState.Live || torrentWithStats.Stats.State == TorrentState.Initializing;
torrent.HasError = torrentWithStats.Stats.State == TorrentState.Error || torrentWithStats.Stats.State == TorrentState.Invalid;
torrent.Message = torrentWithStats.Stats.Error;
result.Add(torrent);
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to process torrent {0}", torrentWithStats.InfoHash);
}
}
}
return result;
}
public void RemoveTorrent(string infoHash, bool removeData, RQbitSettings settings)
{
var endpoint = removeData ? "/delete" : "/forget";
var itemRequest = BuildRequest(settings).Resource("/torrents/" + infoHash + endpoint);
_httpClient.Post(itemRequest.Build());
}
public string AddTorrentFromUrl(string torrentUrl, RQbitSettings settings)
{
var itemRequest = BuildRequest(settings).Resource("/torrents?overwrite=true").Post().Build();
itemRequest.SetContent(torrentUrl);
var httpResponse = _httpClient.Post(itemRequest);
if (httpResponse.StatusCode != HttpStatusCode.OK)
{
return null;
}
var response = JsonConvert.DeserializeObject<PostTorrentResponse>(httpResponse.Content);
if (response.Details == null)
{
return null;
}
return response.Details.InfoHash;
}
public string AddTorrentFromFile(string fileName, byte[] fileContent, RQbitSettings settings)
{
var itemRequest = BuildRequest(settings)
.Post()
.Resource("/torrents?overwrite=true")
.Build();
itemRequest.SetContent(fileContent);
var httpResponse = _httpClient.Post(itemRequest);
if (httpResponse.StatusCode != HttpStatusCode.OK)
{
return null;
}
var response = JsonConvert.DeserializeObject<PostTorrentResponse>(httpResponse.Content);
if (response.Details == null)
{
return null;
}
return response.Details.InfoHash;
}
public void SetTorrentLabel(string hash, string label, RQbitSettings settings)
{
_logger.Warn("Torrent labels currently unsupported by RQBit");
}
public bool HasHashTorrent(string hash, RQbitSettings settings)
{
var result = true;
var rqBitTorrentResponse = GetTorrent(hash, settings);
if (rqBitTorrentResponse == null || rqBitTorrentResponse.InfoHash.IsNullOrWhiteSpace())
{
result = false;
}
return result;
}
private TorrentResponse GetTorrent(string infoHash, RQbitSettings settings)
{
var itemRequest = BuildRequest(settings).Resource("/torrents/" + infoHash);
var itemResponse = _httpClient.Get(itemRequest.Build());
if (itemResponse.StatusCode != HttpStatusCode.OK)
{
return null;
}
return JsonConvert.DeserializeObject<TorrentResponse>(itemResponse.Content);
}
private HttpRequestBuilder BuildRequest(RQbitSettings settings)
{
var requestBuilder = new HttpRequestBuilder(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase)
{
LogResponseContent = true,
};
return requestBuilder;
}
}
}

View File

@@ -0,0 +1,48 @@
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Download.Clients.RQBit
{
public class RQbitSettings : DownloadClientSettingsBase<RQbitSettings>
{
private static readonly RQbitSettingsValidator Validator = new();
public RQbitSettings()
{
Host = "localhost";
Port = 3030;
UrlBase = "/";
}
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
public string Host { get; set; }
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; }
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox)]
[FieldToken(TokenField.HelpText, "DownloadClientRQbitSettingsUseSslHelpText")]
public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true)]
[FieldToken(TokenField.HelpText, "DownloadClientRQbitSettingsUrlBaseHelpText")]
public string UrlBase { get; set; }
public override NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
public class RQbitSettingsValidator : AbstractValidator<RQbitSettings>
{
public RQbitSettingsValidator()
{
RuleFor(c => c.Host).ValidHost();
RuleFor(c => c.Port).InclusiveBetween(1, 65535);
RuleFor(c => c.UrlBase).ValidUrlBase();
}
}
}

View File

@@ -0,0 +1,109 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.RQBit.ResponseModels
{
public class ListTorrentsWithStatsResponse
{
public List<TorrentWithStatsResponse> Torrents { get; set; }
}
public class TorrentWithStatsResponse
{
public long Id { get; set; }
[JsonProperty("info_hash")]
public string InfoHash { get; set; }
public string Name { get; set; }
public string OutputFolder { get; set; }
public TorrentStatsResponse Stats { get; set; }
}
public class TorrentStatsResponse
{
public TorrentState State { get; set; }
[JsonProperty("file_progress")]
public List<long> FileProgress { get; set; }
public string Error { get; set; }
[JsonProperty("progress_bytes")]
public long ProgressBytes { get; set; }
[JsonProperty("uploaded_bytes")]
public long UploadedBytes { get; set; }
[JsonProperty("total_bytes")]
public long TotalBytes { get; set; }
public bool Finished { get; set; }
public TorrentLiveStatsResponse Live { get; set; }
}
public class TorrentLiveStatsResponse
{
public TorrentSnapshotResponse Snapshot { get; set; }
[JsonProperty("download_speed")]
public TorrentSpeedResponse DownloadSpeed { get; set; }
[JsonProperty("upload_speed")]
public TorrentSpeedResponse UploadSpeed { get; set; }
[JsonProperty("time_remaining")]
public TorrentTimeRemainingResponse TimeRemaining { get; set; }
}
public class TorrentSnapshotResponse
{
[JsonProperty("downloaded_and_checked_bytes")]
public long DownloadedAndCheckedBytes { get; set; }
[JsonProperty("fetched_bytes")]
public long FetchedBytes { get; set; }
[JsonProperty("uploaded_bytes")]
public long UploadedBytes { get; set; }
[JsonProperty("peer_stats")]
public TorrentPeerStatsResponse PeerStats { get; set; }
}
public class TorrentSpeedResponse
{
public double Mbps { get; set; }
[JsonProperty("human_readable")]
public string HumanReadable { get; set; }
}
public class TorrentTimeRemainingResponse
{
public TorrentDurationResponse Duration { get; set; }
[JsonProperty("human_readable")]
public string HumanReadable { get; set; }
}
public class TorrentDurationResponse
{
public long Secs { get; set; }
public long Nanos { get; set; }
}
public class TorrentPeerStatsResponse
{
public int Queued { get; set; }
public int Connecting { get; set; }
public int Live { get; set; }
public int Seen { get; set; }
public int Dead { get; set; }
[JsonProperty("not_needed")]
public int NotNeeded { get; set; }
public int Steals { get; set; }
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.RQBit.ResponseModels;
public class PostTorrentResponse
{
public long Id { get; set; }
public PostTorrentDetailsResponse Details { get; set; }
[JsonProperty("output_folder")]
public string OutputFolder { get; set; }
[JsonProperty("seen_peers")]
public List<string> SeenPeers { get; set; }
}
public class PostTorrentDetailsResponse
{
[JsonProperty("info_hash")]
public string InfoHash { get; set; }
public string Name { get; set; }
public List<TorrentFileResponse> Files { get; set; }
}

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace NzbDrone.Core.Download.Clients.RQBit.ResponseModels;
public class RootResponse
{
public Dictionary<string, string> Apis { get; set; }
public string Server { get; set; }
public string Version { get; set; }
}

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace NzbDrone.Core.Download.Clients.RQBit.ResponseModels;
public class TorrentFileResponse
{
public string Name { get; set; }
public List<string> Components { get; set; }
public long Length { get; set; }
public bool Included { get; set; }
}

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.RQBit.ResponseModels;
public class TorrentResponse
{
[JsonProperty("info_hash")]
public string InfoHash { get; set; }
public string Name { get; set; }
public List<TorrentFileResponse> Files { get; set; }
[JsonProperty("output_folder")]
public string OutputFolder { get; set; }
}

View File

@@ -0,0 +1,11 @@
namespace NzbDrone.Core.Download.Clients.RQBit.ResponseModels;
// https://github.com/ikatson/rqbit/blob/946ad3625892f4f40dde3d0e6bbc3030f68a973c/crates/librqbit/src/torrent_state/mod.rs#L65
public enum TorrentState
{
Initializing = 0,
Paused = 1,
Live = 2,
Error = 3,
Invalid = 4
}

View File

@@ -72,7 +72,7 @@ namespace NzbDrone.Core.ImportLists.Custom
}
var baseUrl = settings.BaseUrl.TrimEnd('/');
var request = new HttpRequestBuilder(baseUrl).Accept(HttpAccept.Json).Build();
var request = new HttpRequestBuilder(baseUrl).Accept(HttpAccept.Json).AllowRedirect().Build();
var response = _httpClient.Get(request);
var results = JsonConvert.DeserializeObject<List<TResource>>(response.Content);

View File

@@ -121,6 +121,11 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
break;
}
if (item.Tags?.Contains("Subtitles") == true)
{
flags |= IndexerFlags.Subtitles;
}
return flags;
}

View File

@@ -1,3 +1,5 @@
using System.Collections.Generic;
namespace NzbDrone.Core.Indexers.BroadcastheNet
{
public class BroadcastheNetTorrent
@@ -26,6 +28,7 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
public int? TvrageID { get; set; }
public string ImdbID { get; set; }
public string InfoHash { get; set; }
public List<string> Tags { get; set; }
public string DownloadURL { get; set; }
}
}

View File

@@ -122,6 +122,7 @@ namespace NzbDrone.Core.Languages
public static Language Malay => new Language(49, "Malay");
public static Language Urdu => new Language(50, "Urdu");
public static Language Romansh => new Language(51, "Romansh");
public static Language Georgian => new Language(52, "Georgian");
public static Language Original => new Language(-2, "Original");
public static List<Language> All
@@ -182,6 +183,7 @@ namespace NzbDrone.Core.Languages
Malay,
Urdu,
Romansh,
Georgian,
Original
};
}

View File

@@ -91,7 +91,7 @@
"SeasonDetails": "Детайли за сезона",
"SeasonCount": "Брой сезони",
"SslPort": "SSL порт",
"AppDataLocationHealthCheckMessage": "Актуализирането няма да бъде възможно, за да се предотврати изтриването на папката на приложението по време на актуализацията",
"AppDataLocationHealthCheckMessage": "Актуализацията няма да бъде възможна, за да се предотврати изтриването на AppData при обновяване",
"AppUpdated": "{appName} Актуализиран",
"ApplyTagsHelpTextReplace": "Замяна: Заменете таговете с въведените тагове (не въвеждайте тагове, за да изчистите всички тагове)",
"AudioLanguages": "Аудио езици",
@@ -139,5 +139,53 @@
"StandardEpisodeFormat": "Формат на епизода ( Стандартен )",
"SslCertPathHelpText": "Път до \"pfx\" файл",
"EpisodeNaming": "Именуване на епизоди",
"Close": "Затвори"
"Close": "Затвори",
"AutoTaggingSpecificationMaximumYear": "Максимална година",
"AutoTaggingSpecificationMinimumYear": "Минимална година",
"AutoTaggingSpecificationTag": "Етикет",
"AutoTaggingSpecificationNetwork": "Мрежа(и)",
"Airs": "Излъчва се",
"BranchUpdateMechanism": "Клон, използван от външен механизъм за актуализация",
"BranchUpdate": "Клон, който да се използва за актуализиране на {appName}",
"BindAddressHelpText": "Валиден IP адрес, localhost или '*' за всички интерфейси",
"BlackholeWatchFolder": "Наблюдавана папка",
"BlackholeWatchFolderHelpText": "Папка, от която {appName} трябва да импортира завършените изтегляния",
"BlackholeFolderHelpText": "Папка, в която {appName} ще съхранява файла {extension}",
"BlocklistAndSearchHint": "Започнете търсене на заместител след блокиране",
"BlocklistAndSearchMultipleHint": "Започнете търсене на заместители след блокиране",
"BlocklistAndSearch": "Списък за блокиране и търсене",
"Backups": "Архиви",
"AutoTaggingSpecificationRootFolder": "Основна директория",
"BlocklistFilterHasNoItems": "Избраният филтър за блокиране не съдържа елементи",
"Branch": "Клон",
"BuiltIn": "Вграден",
"BeforeUpdate": "Преди актуализация",
"BlocklistOnly": "Само списък за блокиране",
"BlocklistMultipleOnlyHint": "Списък за блокиране без търсене на заместители",
"BlocklistReleases": "Освобождаване на черния списък",
"BlocklistRelease": "Освобождаване на черния списък",
"Automatic": "Автоматично",
"BackupsLoadError": "Архивите не могат да се заредят",
"BindAddress": "Адрес за обвързване",
"BlocklistLoadError": "Неуспешно зареждане на списъка за блокиране",
"BackupIntervalHelpText": "Интервал между автоматичните резервни копия",
"AutoTaggingSpecificationQualityProfile": "Профил за качество",
"AutoTaggingSpecificationSeriesType": "Тип сериал",
"AutoTaggingSpecificationGenre": "Жанр(ове)",
"AutoTaggingSpecificationOriginalLanguage": "Език",
"AutoTaggingSpecificationStatus": "Статус",
"Backup": "Архивиране",
"BackupFolderHelpText": "Вариант 1: Относителните пътища ще се намират в директорията AppData на {appName}",
"BackupNow": "Архивиране сега",
"AutoTaggingRequiredHelpText": "Условието {implementationName} трябва да съвпада, за да се приложи правилото за автоматично тагване. В противен случай е достатъчно едно съвпадение на {implementationName}.",
"AutomaticAdd": "Автоматично добавяне",
"AutomaticUpdatesDisabledDocker": "Автоматичните актуализации не се поддържат директно при използване на механизма за актуализация на Docker. Ще трябва да актуализирате Image-a на контейнера извън {appName} или да използвате скрипт",
"AutomaticSearch": "Автоматично търсене",
"BackupRetentionHelpText": "Автоматичните резервни копия, по-стари от зададения период на съхранение, ще бъдат изтрити автоматично",
"BypassDelayIfAboveCustomFormatScore": "Пропусни, ако е над рейтинга на персонализирания формат",
"AbsoluteEpisodeNumbers": "Абсолютен номер на епизод(и)",
"BlocklistReleaseHelpText": "Блокира този релийз, така че {appName} да не го изтегля повторно чрез RSS или автоматично търсене",
"Blocklist": "Списък за блокиране",
"BrowserReloadRequired": "Необходимо е презареждане на браузъра",
"BlocklistOnlyHint": "Списък за блокиране без търсене на заместител"
}

View File

@@ -1505,7 +1505,7 @@
"IndexerSettingsCookieHelpText": "Si el vostre lloc requereix una galeta d'inici de sessió per accedir als rss, haureu de recuperar-la a través d'un navegador.",
"IndexerSettingsMultiLanguageRelease": "Multiidiomes",
"IndexerSettingsFailDownloadsHelpText": "Mentre es processen les descàrregues completades, {appName} tractarà aquests tipus d'arxiu seleccionats com a descàrregues fallides.",
"IndexerSettingsSeasonPackSeedTimeHelpText": "La quantitat de temps que un torrent de pack de temporada s'ha de sembrar abans d'aturar-se, deixeu en blanc per utilitzar el valor per defecte del client de baixada",
"IndexerSettingsSeasonPackSeedTimeHelpText": "La quantitat de temps que un torrent de paquet de temporada s'ha de sembrar abans d'aturar-se, deixeu en blanc per utilitzar el valor per defecte del client de baixada",
"IndexerValidationCloudFlareCaptchaRequired": "Lloc protegit per CloudFlare CAPTCHA. Cal un testimoni CAPTCHA vàlid.",
"IndexerValidationFeedNotSupported": "No s'admet el canal Indexer: {exceptionMessage}",
"IndexerValidationInvalidApiKey": "Clau API no vàlida",
@@ -1689,7 +1689,7 @@
"UpgradeUntilThisQualityIsMetOrExceeded": "Actualitza fins que aquesta qualitat es compleixi o superi",
"UpgradesAllowedHelpText": "Si es desactiven les qualitats no s'actualitzaran",
"IndexerSettingsFailDownloads": "Baixades fallides",
"IndexerSettingsSeasonPackSeedTime": "Hora de la llavor de les packs de temporada",
"IndexerSettingsSeasonPackSeedTime": "Hora de la llavor dels paquets de temporada",
"IndexerValidationNoRssFeedQueryAvailable": "No hi ha disponible cap consulta de fonts RSS. Aquest pot ser un problema amb l'indexador o la configuració de la categoria de l'indexador.",
"LibraryImportTipsSeriesUseRootFolder": "Apunteu {appName} a la carpeta que conté totes les sèries, no una específica. ex. \"`{goodFolderExample}`\" i no eg. \"`{badFolderExample}`\". A més, cada sèrie ha d'estar en la seva pròpia carpeta dins de la carpeta arrel/biblioteca.",
"LocalAirDate": "Data d'emissió local",
@@ -2170,5 +2170,19 @@
"UserRejectedExtensionsHelpText": "Llista d'extensions d'arxiu a fallar separades per coma (Descàrregues fallides també necessita ser activat per indexador)",
"UserRejectedExtensionsTextsExamples": "Exemples: '.ext, .xyz' o 'ext,xyz'",
"DownloadClientQbittorrentSettingsAddSeriesTags": "Afegeix etiquetes de sèries",
"DownloadClientQbittorrentSettingsAddSeriesTagsHelpText": "Afegeix etiquetes de sèries als nous torrents afegits al client de descàrrega (qBittorrent 4.1.0+)"
"DownloadClientQbittorrentSettingsAddSeriesTagsHelpText": "Afegeix etiquetes de sèries als nous torrents afegits al client de descàrrega (qBittorrent 4.1.0+)",
"IndexerSettingsSeasonPackSeedGoal": "Objectiu de compartició per als paquets de temporada",
"IndexerSettingsSeasonPackSeedGoalHelpText": "Trieu si utilitzar diferents objectius de llavor per als paquets de temporada",
"IndexerSettingsSeasonPackSeedGoalUseStandardGoals": "Utilitzeu els objectius estàndard",
"IndexerSettingsSeasonPackSeedGoalUseSeasonPackGoals": "Utilitzeu els objectius del paquet de temporada",
"IndexerSettingsSeasonPackSeedRatio": "Ràtio de la llavor del paquet de temporada",
"IndexerSettingsSeasonPackSeedRatioHelpText": "La relació a la qual ha d'arribar el torrent d'un paquet de temporada abans d'aturar-se, buida usa el valor predeterminat del client de baixada. La relació ha de ser com a mínim 1.0 i seguir les regles dels indexadors",
"RemoveRootFolderWithSeriesMessageText": "Esteu segur que voleu eliminar la carpeta arrel '{path}'? Els fitxers i carpetes no s'eliminaran del disc, i les sèries d'aquesta carpeta arrel no s'eliminaran de {appName}.",
"DownloadClientTriblerSettingsAnonymityLevel": "Nivell d'anonimat",
"DownloadClientTriblerSettingsAnonymityLevelHelpText": "Nombre de proxies a usar quan es descarrega contingut. Establir a 0 per a desactivar-ho. Els proxies redueixen la velocitat de descàrrega/pujada. Vegeu {url}",
"DownloadClientTriblerSettingsApiKeyHelpText": "[api].key de triblerd.conf",
"DownloadClientTriblerSettingsDirectoryHelpText": "Ubicació opcional per a desar les baixades, deixeu-ho en blanc per utilitzar la ubicació predeterminada del Tribler",
"DownloadClientTriblerSettingsSafeSeeding": "Compartició segura",
"DownloadClientTriblerSettingsSafeSeedingHelpText": "Quan està activat, només es comparteix a través de proxies.",
"DownloadClientTriblerProviderMessage": "La integració tribler és altament experimental. S'ha provat amb {clientName} versió {clientVersionRange}."
}

View File

@@ -178,7 +178,7 @@
"Any": "Jakákoliv",
"ApiKey": "Klíč API",
"AppUpdated": "{appName} aktualizován",
"AppUpdatedVersion": "{appName} byla aktualizována na verzi `{version}`, abyste získali nejnovější změny, musíte znovu načíst {appName}. ",
"AppUpdatedVersion": "{appName} byl aktualizován na verzi `{version}`. Abyste získali nejnovější změny, musíte znovu načíst {appName} ",
"ChooseImportMode": "Vyberte mód importu",
"ClickToChangeEpisode": "Kliknutím změníte epizodu",
"ClickToChangeLanguage": "Kliknutím změníte jazyk",
@@ -316,7 +316,7 @@
"EditSelectedImportLists": "Upravit vybrané seznamy k importu",
"FormatDateTime": "{formattedDate} {formattedTime}",
"AddRootFolderError": "Nepodařilo se přidat kořenový adresář",
"DownloadClientRemovesCompletedDownloadsHealthCheckMessage": "Klient stahování {downloadClientName} je nastaven na odstranění dokončených stahování. To může vést k tomu, že stahování budou z klienta odstraněna dříve, než je bude moci importovat {appName}.",
"DownloadClientRemovesCompletedDownloadsHealthCheckMessage": "Klient pro stahování {downloadClientName} je nastaven, aby odstraňoval dokončené stahování. To může vést k tomu, že stažená data budou z klienta odstraněna dříve, než je {appName} bude moci importovat.",
"ConnectionSettingsUrlBaseHelpText": "Přidá předponu do {connectionName} url, jako např. {url}",
"CustomFormatsSpecificationRegularExpressionHelpText": "Vlastní formát RegEx nerozlišuje velká a malá písmena",
"CustomFormatsSpecificationFlag": "Značka",
@@ -357,13 +357,13 @@
"DeleteSpecification": "Smaž specifikace",
"MappedNetworkDrivesWindowsService": "Mapované síťové jednotky nejsou k dispozici, když běží jako služba Windows. Další informace najdete v [FAQ]({url}).",
"DeletedSeriesDescription": "Seriál byl smazán z TheTVDB",
"RecycleBinUnableToWriteHealthCheckMessage": "Nelze zapisovat do nakonfigurované složky koše: {path}. Ujistěte se, že tato cesta existuje a že do ní může zapisovat uživatel se spuštěnou {appName}",
"RecycleBinUnableToWriteHealthCheckMessage": "Nelze zapisovat do nakonfigurované složky koše: {path}. Ujistěte se, že tato cesta existuje a že do ní může zapisovat uživatel, pod kterým běží {appName}",
"DeleteSelectedImportListExclusionsMessageText": "Opravdu smazat vybraný importovaný seznam vyjímek?",
"DoNotUpgradeAutomatically": "Neupgradovat automaticky",
"DownloadClientQbittorrentSettingsInitialStateHelpText": "Počáteční stav torrentů přidaných do qBittorrentu. Pamatujte, že vynucené torrenty nedodržují omezení týkající se seedů",
"DeleteSelectedCustomFormats": "Smazat vlastní formát(y)",
"ClickToChangeReleaseType": "Kliknutím změníte typ verze",
"CollapseAll": "Sbal Všechno",
"CollapseAll": "Sbalit vše",
"CutoffUnmetNoItems": "Žádné neodpovídající nesplněné položky",
"CutoffUnmetLoadError": "Chybné načítání nesplněných položek",
"AddDelayProfileError": "Nelze přidat nový profil zpoždění, zkuste to prosím znovu.",
@@ -401,7 +401,7 @@
"DoneEditingGroups": "Úpravy skupin dokončeny",
"DeleteSeriesModalHeader": "Smazat - {title}",
"DeleteSelectedCustomFormatsMessageText": "Opravdu odstranit {count} vybraný vlastní formát(y)?",
"DownloadClientCheckNoneAvailableHealthCheckMessage": "Nedostupný klient pro stahování",
"DownloadClientCheckNoneAvailableHealthCheckMessage": "Nedostupný žádný klient pro stahování",
"DownloadClientCheckUnableToCommunicateWithHealthCheckMessage": "Nelze komunikovat s {downloadClientName}. {errorMessage}",
"DownloadClientFreeboxSettingsAppTokenHelpText": "Token aplikace získaný při vytváření přístupu k Freebox API (tj. app_token)",
"DownloadClientFreeboxSettingsAppToken": "Token aplikace",
@@ -560,5 +560,19 @@
"Path": "Cesta",
"RegularExpressionsCanBeTested": "Regulární výrazy lze testovat [zde]({url}).",
"NotificationStatusSingleClientHealthCheckMessage": "Oznámení nedostupná z důvodu selhání: {notificationNames}",
"NotificationTriggersHelpText": "Vyber, které události mají vyvolat toto upozornění"
"NotificationTriggersHelpText": "Vyber, které události mají vyvolat toto upozornění",
"MonitorSelected": "Monitorovat vybrané",
"ShowBannersHelpText": "Zobrazit bannery místo jmen",
"ListRootFolderHelpText": "Kořenová složka, do které budou přidány položky seznamu",
"UnmonitorSelected": "Nemonitorovat vybrané",
"ErrorLoadingItem": "Nastala chyba při načítání této položky",
"IndexerJackettAllHealthCheckMessage": "Indexery, které používají nepodporovaný Jackett endpoint 'all': {indexerNames}",
"ReleaseProfileIndexerHelpText": "Výběr, jakých indexerů se profil týká",
"SkipRedownloadHelpText": "Zabraňuje {appName} zkoušet stahovat alternativní vydání pro odebrané položky",
"ErrorLoadingPage": "Nastala chyba při načítání této stránky",
"ExpandAll": "Rozbalit vše",
"ImportListsPlexSettingsAuthenticateWithPlex": "Autentizovat s Plex.tv",
"MissingNoItems": "Žádné chybějící položky",
"RegularExpression": "Regulární výraz",
"IndexerSearchNoInteractiveHealthCheckMessage": "Nejsou dostupné žádné indexery s povoleným interaktivním vyhledáváním, {appName} nemůže poskytnout žádné výsledky interaktivního hledání"
}

View File

@@ -474,7 +474,7 @@
"DelayProfiles": "Perfiles de retraso",
"DeleteCustomFormatMessageText": "¿Estás seguro que quieres eliminar el formato personalizado '{name}'?",
"DeleteBackup": "Eliminar copia de seguridad",
"CopyUsingHardlinksSeriesHelpText": "Los hardlinks permiten a {appName} a importar los torrents que se estén compartiendo a la carpeta de la serie sin usar espacio adicional en el disco o sin copiar el contenido completo del archivo. Los hardlinks solo funcionarán si el origen y el destino están en el mismo volumen",
"CopyUsingHardlinksSeriesHelpText": "Los enlaces físicos permiten a {appName} importar los torrents que se estén sembrando a la carpeta de la serie sin usar espacio adicional en el disco o copiar el contenido completo del archivo. Los enlaces físicos solo funcionarán si el origen y el destino están en el mismo volumen",
"DefaultDelayProfileSeries": "Este es el perfil por defecto. Aplica a todas las series que no tienen un perfil explícito.",
"DelayProfileSeriesTagsHelpText": "Aplica a series con al menos una etiqueta coincidente",
"DeleteCustomFormat": "Eliminar formato personalizado",
@@ -526,7 +526,7 @@
"DeleteEpisodesFilesHelpText": "Eliminar archivos de episodios y directorio de series",
"DoNotPrefer": "No preferir",
"DoNotUpgradeAutomatically": "No actualizar automáticamente",
"IndexerSettingsSeedRatioHelpText": "El ratio que un torrent debería alcanzar antes de parar, vacío usa el predeterminado del cliente de descarga. El ratio debería ser al menos 1.0 y seguir las reglas de los indexadores",
"IndexerSettingsSeedRatioHelpText": "El ratio que un torrent debería alcanzar antes de detenerse, dejar vacío utiliza el valor predeterminado del cliente de descarga. El ratio debería ser al menos 1.0 y seguir las reglas de los indexadores",
"Download": "Descargar",
"Donate": "Donar",
"DownloadClientDelugeValidationLabelPluginFailure": "La configuración de etiqueta falló",
@@ -730,7 +730,7 @@
"DownloadFailedEpisodeTooltip": "La descarga del episodio falló",
"DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Descarga primero las primeras y últimas piezas (qBittorrent 4.1.0+)",
"DownloadClientQbittorrentSettingsFirstAndLastFirst": "Primeras y últimas primero",
"DownloadClientQbittorrentSettingsInitialStateHelpText": "Estado inicial para los torrents añadidos a qBittorrent. Ten en cuenta que Forzar torrents no cumple las restricciones de semilla",
"DownloadClientQbittorrentSettingsInitialStateHelpText": "Estado inicial para los torrents añadidos a qBittorrent. Ten en cuenta que Forzar torrents no cumple las restricciones de sembrado",
"DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Descarga en orden secuencial (qBittorrent 4.1.0+)",
"DownloadClientDownloadStationValidationSharedFolderMissingDetail": "El Diskstation no tiene una carpeta compartida con el nombre '{sharedFolder}'. ¿Estás seguro que lo has especificado correctamente?",
"EnableCompletedDownloadHandlingHelpText": "Importa automáticamente las descargas completas del gestor de descargas",
@@ -771,7 +771,7 @@
"EpisodeIsNotMonitored": "El episodio no está monitorizado",
"EpisodesLoadError": "No se puede cargar los episodios",
"File": "Archivo",
"HardlinkCopyFiles": "Enlace permanente/Copiar archivos",
"HardlinkCopyFiles": "Enlace físico/Copiar archivos",
"EpisodeDownloaded": "Episodio descargado",
"FileBrowser": "Explorador de archivos",
"FilterDoesNotStartWith": "no empieza con",
@@ -1075,11 +1075,11 @@
"StartupDirectory": "Directorio de Arranque",
"IndexerSettingsAdditionalParametersNyaa": "Parámetros Adicionales",
"IndexerSettingsPasskey": "Clave de acceso",
"IndexerSettingsSeasonPackSeedTime": "Tiempo de Semillado de los Pack de Temporada",
"IndexerSettingsSeasonPackSeedTime": "Tiempo de sembrado de los pack de temporada",
"IndexerSettingsAnimeStandardFormatSearch": "Formato Estándar de Búsqueda de Anime",
"IndexerSettingsAnimeStandardFormatSearchHelpText": "Buscar también anime utilizando la numeración estándar",
"IndexerSettingsApiPathHelpText": "Ruta a la API, usualmente {url}",
"IndexerSettingsSeasonPackSeedTimeHelpText": "La cantidad de tiempo que un torrent de pack de temporada debe ser compartido antes de que se detenga, dejar vacío utiliza el valor por defecto del cliente de descarga",
"IndexerSettingsSeasonPackSeedTimeHelpText": "El tiempo que un torrent de pack de temporada debe ser sembrado antes de detenerse, si se deja vacío utiliza el valor predeterminado del cliente de descarga",
"IndexerSettingsSeedTime": "Tiempo de sembrado",
"IndexerStatusAllUnavailableHealthCheckMessage": "Todos los indexadores no están disponibles debido a errores",
"IndexerValidationCloudFlareCaptchaExpired": "El token CAPTCHA de CloudFlare ha caducado, actualícelo.",
@@ -1098,7 +1098,7 @@
"IndexerSettingsRssUrlHelpText": "Introduzca la URL de un canal RSS compatible con {indexer}",
"IndexerStatusUnavailableHealthCheckMessage": "Indexadores no disponibles debido a errores: {indexerNames}",
"IndexerHDBitsSettingsMediums": "Medios",
"IndexerSettingsSeedTimeHelpText": "El tiempo que un torrent debería ser sembrado antes de parar, vacío usa el predeterminado del cliente de descarga",
"IndexerSettingsSeedTimeHelpText": "El tiempo que un torrent debería ser sembrado antes de detenerse, dejar vacío utiliza el valor predeterminado del cliente de descarga",
"IndexerValidationCloudFlareCaptchaRequired": "Sitio protegido por CloudFlare CAPTCHA. Se requiere un token CAPTCHA válido.",
"NotificationsEmailSettingsUseEncryption": "Usar Cifrado",
"LastDuration": "Última Duración",
@@ -1706,7 +1706,7 @@
"UpgradeUntil": "Actualizar hasta",
"UpdaterLogFiles": "Actualizador de archivos de registro",
"UseSeasonFolder": "Usar carpeta de temporada",
"UseHardlinksInsteadOfCopy": "Utilizar enlaces directos en lugar de copiar",
"UseHardlinksInsteadOfCopy": "Utilizar enlaces físicos en lugar de copiar",
"View": "Vista",
"VisitTheWikiForMoreDetails": "Visita la wiki para más detalles: ",
"WaitingToProcess": "Esperar al proceso",
@@ -1875,7 +1875,7 @@
"SmartReplace": "Reemplazo inteligente",
"SupportedDownloadClientsMoreInfo": "Para más información en los clientes de descarga individuales, haz clic en los botones de más información.",
"SupportedImportListsMoreInfo": "Para más información de los listas de importación individuales, haz clic en los botones de más información.",
"TorrentBlackholeSaveMagnetFilesReadOnlyHelpText": "En lugar de mover archivos esto indicará a {appName} que copie o enlace (dependiendo de los ajustes/configuración del sistema)",
"TorrentBlackholeSaveMagnetFilesReadOnlyHelpText": "En lugar de mover archivos, esto indicará a {appName} que copie o haga un enlace físico (dependiendo de los ajustes/configuración del sistema)",
"TorrentDelay": "Retraso de torrent",
"ToggleMonitoredToUnmonitored": "Monitorizado, haz clic para dejar de monitorizar",
"TorrentBlackholeSaveMagnetFilesHelpText": "Guarda el enlace magnet si no hay ningún archivo .torrent disponible (útil solo si el cliente de descarga soporta magnets guardados en un archivo)",
@@ -2170,5 +2170,19 @@
"UserRejectedExtensions": "Extensiones adicionales de archivo rechazadas",
"DownloadClientQbittorrentSettingsAddSeriesTagsHelpText": "Añade etiquetas de series a los nuevos torrents añadidos al cliente de descarga (qBittorrent 4.1.0+)",
"UserRejectedExtensionsTextsExamples": "Ejemplos: '.ext, .xyz' o 'ext,xyz'",
"UserRejectedExtensionsHelpText": "Lista de extensiones de archivo a fallar separadas por coma (Descargas fallidas también necesita ser activado por indexador)"
"UserRejectedExtensionsHelpText": "Lista de extensiones de archivo a fallar separadas por coma (Descargas fallidas también necesita ser activado por indexador)",
"IndexerSettingsSeasonPackSeedGoalUseStandardGoals": "Usar objetivos estándar",
"IndexerSettingsSeasonPackSeedGoalUseSeasonPackGoals": "Usar objetivos de pack de temporada",
"IndexerSettingsSeasonPackSeedGoalHelpText": "Elige si usar diferentes objetivos de sembrado para packs de temporada",
"IndexerSettingsSeasonPackSeedRatio": "Ratio de siembra del pack de temporada",
"RemoveRootFolderWithSeriesMessageText": "¿Estás seguro que quieres eliminar la carpeta raíz '{path}'? Los archivos y carpetas no serán borrados del disco, y las series en esta carpeta raíz no serán eliminadas de {appName}.",
"IndexerSettingsSeasonPackSeedGoal": "Objetivo de sembrado para packs de temporada",
"DownloadClientTriblerSettingsApiKeyHelpText": "[api].key de triblerd.conf",
"DownloadClientTriblerSettingsDirectoryHelpText": "Localización opcional en la que poner las descargas, dejar en blanco para usar la localización predeterminada de Tribler",
"IndexerSettingsSeasonPackSeedRatioHelpText": "El ratio que un torrent de pack de temporada debería alcanzar antes de detenerse, si se deja vacío usa el valor predeterminado del cliente de descarga. El ratio debería ser al menos 1.0 y seguir las reglas de los indexadores",
"DownloadClientTriblerSettingsAnonymityLevel": "Nivel de anonimato",
"DownloadClientTriblerSettingsAnonymityLevelHelpText": "Número de proxies a usar cuando se descarga contenido. Establecer a 0 para deshabilitarlo. Los proxies reducen la velocidad de descarga/subida. Ver {url}",
"DownloadClientTriblerProviderMessage": "La integración con Tribler es altamente experimental. Probado con {clientName} versión {clientVersionRange}.",
"DownloadClientTriblerSettingsSafeSeedingHelpText": "Cuando se habilita, solo se siembra a través de los proxies.",
"DownloadClientTriblerSettingsSafeSeeding": "Sembrado seguro"
}

View File

@@ -1191,7 +1191,7 @@
"PackageVersion": "Version du paquet",
"PackageVersionInfo": "{packageVersion} par {packageAuthor}",
"QuickSearch": "Recherche rapide",
"ReleaseRejected": "Libération rejetée",
"ReleaseRejected": "Version rejetée",
"ReleaseSceneIndicatorAssumingScene": "En supposant la numérotation des scènes.",
"ReleaseSceneIndicatorAssumingTvdb": "En supposant la numérotation TVDB.",
"ReleaseSceneIndicatorMappedNotRequested": "L'épisode mappé n'a pas été demandé dans cette recherche.",
@@ -2164,5 +2164,25 @@
"UserRejectedExtensionsTextsExamples": "Examples : '.ext, .xyz' or 'ext,xyz'",
"Warning": "Avertissement",
"QualityDefinitionsSizeNotice": "Les restrictions de taille sont maintenant dans les profils de qualité",
"UserInvokedSearch": "Recherche invoquée par lutilisateur"
"UserInvokedSearch": "Recherche invoquée par lutilisateur",
"DownloadClientTriblerSettingsAnonymityLevelHelpText": "Nombre de proxy à utiliser pour télécharger. Pour désactiver, mettre à 0. Les proxy réduisent la vitesse d'envoi et de réception. Voir {url}",
"DownloadClientTriblerSettingsDirectoryHelpText": "Emplacement optionnel où mettre les téléchargements, laisser vide pour utiliser l'emplacement Tribler par défaut",
"CloneImportList": "Cloner la liste d'importation",
"IndexerSettingsSeasonPackSeedGoal": "Objective de partage pour les packs de saison",
"IndexerSettingsSeasonPackSeedGoalHelpText": "Sélectionné si des objectifs différent devraient être utilisés pour les packs de saison",
"IndexerSettingsSeasonPackSeedGoalUseStandardGoals": "Utiliser les objectifs standards",
"IndexerSettingsSeasonPackSeedGoalUseSeasonPackGoals": "Utiliser les objectifs pour les packs de saison",
"IndexerSettingsSeasonPackSeedRatio": "Ratio de partage pour les packs de saison",
"MediaInfoFootNote2": "MediaInfo AudioLanguages exclue langlais sil sagit de la seule langue. Utiliser MediaInfo AudioLanguagesAll pour inclure ceux seulement en anglais",
"NotificationsAppriseSettingsIncludePosterHelpText": "Inclure le poster dans le message",
"ReleasePush": "Délai de version",
"ReleaseSource": "Source de version",
"RemoveRootFolderWithSeriesMessageText": "Êtes-vous sûr de vouloir supprimer le dossier racine '{path}' ? Les fichiers et les dossiers ne seront pas supprimés du disque, et les séries dans ce dossier racine ne seront pas supprimées de {appName}.",
"IndexerSettingsSeasonPackSeedRatioHelpText": "Le ratio quun torrent de pack de saison devrait atteindre avant darrêter. Laisser à vide utilise le default du client de téléchargement. Le ratio devrait être au moins 1.0 et sur les règles de lindexeur",
"DownloadClientTriblerSettingsAnonymityLevel": "Niveau danonymat",
"DownloadClientTriblerSettingsApiKeyHelpText": "[api].key de triblerd.conf",
"DownloadClientTriblerSettingsSafeSeeding": "Partage protégé",
"DownloadClientTriblerSettingsSafeSeedingHelpText": "Lorsque activé, seulement partager via un proxy.",
"DownloadClientTriblerProviderMessage": "Lintégration avec tribler est hautement expérimental. Tester sur {clientName} version {clientVersionRange}.",
"NotificationsAppriseSettingsIncludePoster": "Inclure le poster"
}

View File

@@ -61,7 +61,7 @@
"AddDelayProfile": "Voeg vertragingsprofiel toe",
"AddCustomFormatError": "Kan geen nieuw aangepast formaat toevoegen. Probeer het opnieuw.",
"AddDownloadClientError": "Kan geen nieuwe downloadclient toevoegen. Probeer het opnieuw.",
"AddDownloadClient": "Download Client Toevoegen",
"AddDownloadClient": "Downloadprogramma Toevoegen",
"AddIndexerError": "Kon geen nieuwe indexeerder toevoegen, Probeer het opnieuw.",
"AddList": "Lijst Toevoegen",
"AddListError": "Kon geen nieuwe lijst toevoegen, probeer het opnieuw.",
@@ -266,5 +266,36 @@
"CertificateValidationHelpText": "Verander hoe strikt HTTPS-certificaatvalidatie is. Verander dit niet als je de risico's niet begrijpt.",
"ChmodFolderHelpText": "Octaal, toegepast tijdens importeren/hernoemen van mediamappen en -bestanden (zonder uitvoeringsbits)",
"CalendarFeed": "{appName} kalenderfeed",
"BypassDelayIfHighestQualityHelpText": "Omzeil uitstel wanneer de uitgave de hoogst mogelijke kwaliteit heeft uit het kwaliteitsprofiel van het geprefereerde protocol"
"BypassDelayIfHighestQualityHelpText": "Omzeil uitstel wanneer de uitgave de hoogst mogelijke kwaliteit heeft uit het kwaliteitsprofiel van het geprefereerde protocol",
"DownloadClientValidationSslConnectFailure": "Kan niet verbinden via SSL",
"DownloadClientValidationGroupMissing": "Groep bestaat niet",
"DownloadClientValidationUnknownException": "Onbekende fout: {exception}",
"DownloadClientValidationVerifySsl": "Verifieer SSL instellingen",
"DownloadClients": "Downloadprogramma's",
"DownloadClientTriblerSettingsAnonymityLevelHelpText": "Aantal proxies om te gebruiken bij het downloaden van content. Zet dit op 0 om uit te schakelen. Proxies vertragen download- en uploadsnelheid. Zie {url}",
"DownloadClientTriblerProviderMessage": "De Tribler-integratie is heel experimenteel. Getest met {clientName} versie {clientVersionRange}.",
"DownloadClientValidationAuthenticationFailureDetail": "Gelieve uw gebruikersnaam en wachtwoord te verifiëren. Verifieer ook of de host waar {appName} op draait niet geblokkeerd is voor toegang tot {clientName} door Whitelist limitaties in de {clientName} instellingen.",
"DownloadClientValidationSslConnectFailureDetail": "{appName} kan niet verbinden met {clientName} via SSL. Dit probleem kan computergerelateerd zijn. Probeer alstublieft om {appName} en {clientName} te configureren om geen SSL te gebruiken.",
"DownloadClientValidationAuthenticationFailure": "Authenticatiefout",
"DownloadClientTriblerSettingsApiKeyHelpText": "[api].key uit triblerd.conf",
"DownloadClientTriblerSettingsDirectoryHelpText": "Optionele locatie om downloads in te plaatsen, laat leeg om de standaard Tribler locatie te gebruiken",
"DownloadClientTriblerSettingsSafeSeeding": "Veilig Seeden",
"DownloadClientTriblerSettingsSafeSeedingHelpText": "Wanneer ingeschakeld alleen maar seeden via proxies.",
"DownloadClientUTorrentTorrentStateError": "uTorrent rapporteert een fout",
"DownloadClientUnavailable": "Download Client niet beschikbaar",
"DownloadClientValidationApiKeyIncorrect": "API-sleutel Incorrect",
"DownloadClientValidationApiKeyRequired": "API-sleutel Nodig",
"DownloadClientValidationCategoryMissing": "Categorie bestaat niet",
"DownloadClientValidationCategoryMissingDetail": "De ingevoerde categorie bestaat niet in {clientName}. Voeg deze eerst toe in {clientName}.",
"DownloadClientValidationErrorVersion": "{clientName} versie moet tenminste {requiredVersion} zijn. Gerapporteerde versie is {reportedVersion}",
"DownloadClientValidationGroupMissingDetail": "De ingevoerde groep bestaat niet in {clientName}. Voeg deze eerst toe in {clientName}.",
"DownloadClientValidationTestNzbs": "Kon de lijst van NZBs niet verkrijgen: {exceptionMessage}",
"DownloadClientValidationTestTorrents": "Kon de lijst van torrents niet verkrijgen: {exceptionMessage}",
"DownloadClientValidationUnableToConnect": "Kon niet verbinden met {clientName}",
"DownloadClientValidationUnableToConnectDetail": "Gelieve de hostnaam en poort te verifiëren.",
"DownloadClientValidationVerifySslDetail": "Gelieve uw SSL-configuratie te verifiëren in zowel {clientName} als {appName}",
"DownloadClientVuzeValidationErrorVersion": "Protocolversie niet ondersteund, gebruik Vuze 5.0.0.0 of hoger met de Vuze Web Remote plugin.",
"DownloadClientsLoadError": "Kan downloadprogramma's niet laden",
"GeneralSettingsLoadError": "Algemene instellingen konden niet worden geladen",
"GeneralSettingsSummary": "Poort, SSL, gebruikersnaam/wachtwoord, proxy, analytics en updates"
}

View File

@@ -55,7 +55,7 @@
"Added": "Adicionado",
"ApiKeyValidationHealthCheckMessage": "Atualize sua chave de API para ter pelo menos {length} caracteres. Você pode fazer isso através das configurações ou do arquivo de configuração",
"RemoveCompletedDownloads": "Remover downloads concluídos",
"AppDataLocationHealthCheckMessage": "Não será possível atualizar para evitar a exclusão de AppData na Atualização",
"AppDataLocationHealthCheckMessage": "A atualização não será possível para impedir a exclusão de AppData na Atualização",
"DownloadClientCheckUnableToCommunicateWithHealthCheckMessage": "Não é possível se comunicar com {downloadClientName}. {errorMessage}",
"DownloadClientRootFolderHealthCheckMessage": "O cliente de download {downloadClientName} coloca os downloads na pasta raiz {rootFolderPath}. Você não deve baixar para uma pasta raiz.",
"DownloadClientSortingHealthCheckMessage": "O cliente de download {downloadClientName} tem classificação {sortingMode} habilitada para a categoria do {appName}. Você deve desabilitar essa classificação em seu cliente de download para evitar problemas de importação.",
@@ -1632,7 +1632,7 @@
"IndexerSettingsMinimumSeedersHelpText": "Quantidade mínima de semeadores necessária.",
"IndexerSettingsPasskey": "Chave de acesso",
"IndexerSettingsRssUrl": "URL do RSS",
"IndexerSettingsSeasonPackSeedTime": "Tempo de semeadura para pacotes de temporada",
"IndexerSettingsSeasonPackSeedTime": "Tempo de Semeadura para Pacotes de Temporada",
"IndexerSettingsSeedRatio": "Proporção de semeadura",
"IndexerSettingsSeedTime": "Tempo de semeadura",
"IndexerSettingsSeedTimeHelpText": "Quanto tempo um torrent deve ser semeado antes de parar, deixe vazio para usar o padrão do cliente de download",
@@ -2170,5 +2170,19 @@
"UserRejectedExtensionsHelpText": "Lista separada por vírgulas de extensões de arquivos para falhar (Falha em downloads também precisa ser habilitado por indexador)",
"UserRejectedExtensionsTextsExamples": "Exemplos: '.ext, .xyz' or 'ext,xyz'",
"DownloadClientQbittorrentSettingsAddSeriesTagsHelpText": "Adicionar etiquetas das séries a novos torrents adicionados ao cliente de download (qBittorrent 4.1.0+)",
"DownloadClientQbittorrentSettingsAddSeriesTags": "Adicionar Etiquetas das Séries"
"DownloadClientQbittorrentSettingsAddSeriesTags": "Adicionar Etiquetas das Séries",
"IndexerSettingsSeasonPackSeedGoal": "Meta de Semeadura para Pacotes de Temporada",
"IndexerSettingsSeasonPackSeedGoalUseStandardGoals": "Usar Metas Padrões",
"IndexerSettingsSeasonPackSeedGoalUseSeasonPackGoals": "Usar as Metas de Pacote de Temporada",
"IndexerSettingsSeasonPackSeedRatio": "Proporção de Semeadura do Pacote de Temporada",
"IndexerSettingsSeasonPackSeedRatioHelpText": "A proporção que um torrent de pacote de temporada deve atingir antes de parar, vazio usa o padrão do cliente de download. A proporção deve ser de pelo menos 1,0 e seguir as regras dos indexadores",
"IndexerSettingsSeasonPackSeedGoalHelpText": "Escolha se deseja usar metas de semeadura diferentes para pacotes de temporada",
"RemoveRootFolderWithSeriesMessageText": "Tem certeza de que deseja remover a pasta raiz '{path}'? Arquivos e pastas não serão excluídos do disco e as séries nesta pasta raiz não serão removidas de {appName}.",
"DownloadClientTriblerSettingsAnonymityLevel": "Nível de anonimato",
"DownloadClientTriblerSettingsAnonymityLevelHelpText": "Número de proxies a serem usados ao baixar conteúdo. Para desativar, defina como 0. Os proxies reduzem a velocidade de download/upload. Veja {url}",
"DownloadClientTriblerSettingsApiKeyHelpText": "[API]. chave de triblerd.conf",
"DownloadClientTriblerSettingsDirectoryHelpText": "Local opcional para colocar downloads, deixe em branco para usar o local padrão do Tribler",
"DownloadClientTriblerSettingsSafeSeeding": "Semeadura Segura",
"DownloadClientTriblerSettingsSafeSeedingHelpText": "Quando ativado, apenas semeia por meio de proxies.",
"DownloadClientTriblerProviderMessage": "A integração tribler é altamente experimental. Testado em {clientName} versão {clientVersionRange}."
}

View File

@@ -1371,7 +1371,7 @@
"Hostname": "Имя хоста",
"Host": "Хост",
"IndexerSettingsRssUrlHelpText": "Введите URL-адрес RSS-канала, совместимого с {indexer}",
"IndexerSettingsSeasonPackSeedTimeHelpText": "Время, когда торрент сезонного пакета должен быть на раздаче перед остановкой, при пустом значении используется значение по умолчанию клиента загрузки",
"IndexerSettingsSeasonPackSeedTimeHelpText": "Время, когда торрент полного сезона должен быть на раздаче, перед остановкой, при пустом значении используется значение загрузчика по умолчанию",
"IndexerSettingsSeedRatioHelpText": "Рейтинг, которого должен достичь торрент перед остановкой, пусто — используется значение по умолчанию клиента загрузки. Рейтинг должен быть не менее 1,0 и соответствовать правилам индексаторов",
"IndexerHDBitsSettingsMediumsHelpText": "Если не указано, используются все параметры.",
"IndexerSettingsApiUrlHelpText": "Не меняйте это, если вы не знаете, что делаете. Поскольку ваш ключ API будет отправлен на этот хост.",
@@ -1681,7 +1681,7 @@
"ImportListsPlexSettingsWatchlistRSSName": "Список наблюдения Plex RSS",
"IndexerSettingsAnimeCategoriesHelpText": "Выпадающий список, оставьте пустым, чтобы отключить аниме",
"IndexerSettingsMultiLanguageRelease": "Несколько языков",
"IndexerSettingsSeasonPackSeedTime": "Время сидирования сезон-пака",
"IndexerSettingsSeasonPackSeedTime": "Время сидирования полного сезона",
"IndexerValidationUnableToConnectResolutionFailure": "Невозможно подключиться к индексатору. Проверьте подключение к серверу индексатора и DNS. {exceptionMessage}.",
"IndexerValidationUnableToConnectHttpError": "Невозможно подключиться к индексатору. Проверьте настройки DNS и убедитесь, что IPv6 работает или отключен. {exceptionMessage}.",
"IndexerTagSeriesHelpText": "Используйте этот индексатор только для сериалов, имеющих хотя бы один соответствующий тег. Оставьте поле пустым, чтобы использовать его для всех.",
@@ -2170,5 +2170,19 @@
"UserRejectedExtensionsTextsExamples": "Примеры: '.ext, .xyz' или 'ext,xyz'",
"DownloadClientQbittorrentSettingsAddSeriesTags": "Добавлять теги сериалов",
"DownloadClientQbittorrentSettingsAddSeriesTagsHelpText": "Добавлять теги сериалов к новым торрентам, добавляемым в загрузчик (qBittorrent 4.1.0+)",
"UserRejectedExtensionsHelpText": "Список запрещенных расширений файлов, разделенных запятой (так же нужно включить настройку Считать загрузки неуспешными в настройках индексаторов)"
"UserRejectedExtensionsHelpText": "Список запрещенных расширений файлов, разделенных запятой (так же нужно включить настройку Считать загрузки неуспешными в настройках индексаторов)",
"IndexerSettingsSeasonPackSeedGoal": "Целевая раздача для полных сезонов",
"IndexerSettingsSeasonPackSeedGoalHelpText": "Следует ли использовать другое значение целевой раздачи для полных сезонов",
"IndexerSettingsSeasonPackSeedGoalUseStandardGoals": "Использовать стандартные значения",
"IndexerSettingsSeasonPackSeedRatio": "Соотношение раздачи для полных сезонов",
"IndexerSettingsSeasonPackSeedGoalUseSeasonPackGoals": "Использовать целевые значения для полных сезонов",
"IndexerSettingsSeasonPackSeedRatioHelpText": "Соотношение, которое полные сезоны должны достигнуть перед остановкой. Пустое значение приведёт к использованию значения загрузчика по умолчанию. Соотношение должно быть не менее 1.0 и соответствовать правилам индексаторов",
"RemoveRootFolderWithSeriesMessageText": "Вы уверены, что хотите удалить корневой каталог '{path}'? Файлов и папки не будут удалены с диска, а сериалы в этом корневом каталоге останутся в {appName}.",
"DownloadClientTriblerSettingsAnonymityLevel": "Уровень анонимности",
"DownloadClientTriblerSettingsAnonymityLevelHelpText": "Количество используемых прокси-серверов для скачивания контента. Чтобы выключить, укажите 0. Прокси-серверы уменьшают скорость скачивания/загрузки. Доп. инфо: {url}",
"DownloadClientTriblerSettingsApiKeyHelpText": "[api].key из triblerd.conf",
"DownloadClientTriblerSettingsDirectoryHelpText": "Не обязательный путь для сохранения загрузок. Оставьте пустым, чтобы использовать путь Tribler по умолчанию",
"DownloadClientTriblerSettingsSafeSeeding": "Безопасная раздача",
"DownloadClientTriblerSettingsSafeSeedingHelpText": "Если включено, раздача будет вестись только через прокси-серверы.",
"DownloadClientTriblerProviderMessage": "Интеграция с Tribler находится в ранней экспериментальной стадии. Тестировалось на {clientName} версии {clientVersionRange}."
}

View File

@@ -1780,14 +1780,14 @@
"ImportListsTraktSettingsWatchListSortingHelpText": "Liste Türü İzlenen ise, listeyi sıralamak için sırayı seçin",
"IndexerSearchNoAutomaticHealthCheckMessage": "Otomatik Arama etkinleştirildiğinde hiçbir indeksleyici kullanılamaz, {appName} herhangi bir otomatik arama sonucu sağlamayacaktır",
"IndexerSettingsApiUrlHelpText": "Ne yaptığınızı bilmiyorsanız bunu değiştirmeyin. API anahtarınız ana sunucuya gönderilecektir.",
"IndexerSettingsSeasonPackSeedTimeHelpText": "Bir sezon paketi torrentinin durdurulmadan önce başlatılması gereken süre, boş bırakıldığında indirme istemcisinin varsayılanı kullanılır",
"IndexerSettingsSeasonPackSeedTimeHelpText": "Bir sezon paketi torrentinin durmadan önce paylaşılması gereken süre. Boş bırakılırsa indirme istemcisinin varsayılanı kullanılır",
"IndexerValidationCloudFlareCaptchaRequired": "Site CloudFlare CAPTCHA tarafından korunmaktadır. Geçerli CAPTCHA belirteci gereklidir.",
"IndexerValidationUnableToConnectServerUnavailable": "İndeksleyiciye bağlanılamıyor, indeksleyicinin sunucusu kullanılamıyor. Daha sonra tekrar deneyin. {exceptionMessage}.",
"ImportListsTraktSettingsUserListUsernameHelpText": "İçe aktarılacak Liste için Kullanıcı Adı (Yetkili Kullanıcı için boş bırakın)",
"ImportListsTraktSettingsYearsSeriesHelpText": "Diziyi yıla veya yıl aralığına göre filtreleyin",
"IndexerHDBitsSettingsMediums": "Ortamlar",
"IndexerSearchNoAvailableIndexersHealthCheckMessage": "Son zamanlardaki indeksleyici hataları nedeniyle tüm arama yeteneğine sahip indeksleyiciler geçici olarak kullanılamıyor",
"IndexerSettingsSeasonPackSeedTime": "Sezon Paketi Seed Süresi",
"IndexerSettingsSeasonPackSeedTime": "Sezon Paketi Gönderme (Seed) Süresi",
"IndexerValidationJackettAllNotSupportedHelpText": "Jackett'in tüm uç noktaları desteklenmiyor, lütfen indeksleyicileri tek tek ekleyin",
"IndexerValidationNoRssFeedQueryAvailable": "RSS besleme sorgusu mevcut değil. Bu, indeksleyici veya indeksleyici kategori ayarlarınızdan kaynaklı bir sorun olabilir.",
"IndexerValidationUnableToConnectResolutionFailure": "İndeksleyiciye bağlanılamıyor bağlantı hatası. İndeksleyicinin sunucusuna ve DNS'ine olan bağlantınızı kontrol edin. {exceptionMessage}.",
@@ -2170,5 +2170,19 @@
"MonitorEpisodesModalInfo": "Bu ayar, bir dizide hangi bölüm veya sezonların takip edileceğini kontrol eder. \"Hiçbiri\" seçilirse, dizi takip edilmeyecektir",
"UserRejectedExtensions": "Ek Olarak Reddedilen Dosya Uzantıları",
"UserRejectedExtensionsHelpText": "Başarısız sayılacak dosya uzantılarını virgülle ayırarak girin (Ayrıca, her dizinleyici için \"İndirmeleri Başarısız Say\" seçeneği etkin olmalıdır)",
"EpisodeMonitoring": "Bölüm Takibi"
"EpisodeMonitoring": "Bölüm Takibi",
"IndexerSettingsSeasonPackSeedGoalUseStandardGoals": "Standart Hedefleri Kullanın",
"IndexerSettingsSeasonPackSeedGoalUseSeasonPackGoals": "Sezon Paketi Hedeflerini Kullan",
"IndexerSettingsSeasonPackSeedRatio": "Sezon Paketi Paylaşım(Seed) Oranı",
"RemoveRootFolderWithSeriesMessageText": "Kök klasörü silmek istediğinizden emin misiniz? {path} klasörü silinecek. Diskten dosya ve klasörler silinmeyecek, ve bu kök klasördeki diziler {appName} içinden kaldırılmayacak.",
"IndexerSettingsSeasonPackSeedGoal": "Sezon Paketleri için Paylaşım(seed) Hedefi",
"IndexerSettingsSeasonPackSeedGoalHelpText": "Sezon paketleri için farklı paylaşım hedefleri kullanıp kullanmamayı seçin",
"IndexerSettingsSeasonPackSeedRatioHelpText": "Bir sezon paketi torrentinin durmadan önce ulaşması gereken oran. Boş bırakılırsa indirme istemcisinin varsayılanı kullanılır. Oran en az 1.0 olmalı ve indeksleyicinin kurallarına uymalıdır",
"DownloadClientTriblerSettingsAnonymityLevel": "Anonimlik seviyesi",
"DownloadClientTriblerSettingsAnonymityLevelHelpText": "İçerik indirirken kullanılacak proxy sayısı. Devre dışı bırakmak için 0 olarak ayarlayın. Proxyler indirme/yükleme hızını azaltır. Bkz. {url}",
"DownloadClientTriblerSettingsApiKeyHelpText": "triblerd.conf dosyasından [api].key",
"DownloadClientTriblerSettingsDirectoryHelpText": "İsteğe bağlı olarak indirmelerin kaydedileceği konum. Varsayılan Tribler konumunu kullanmak için boş bırakın",
"DownloadClientTriblerSettingsSafeSeeding": "Güvenli Paylaşım",
"DownloadClientTriblerSettingsSafeSeedingHelpText": "Etkinleştirildiğinde, yalnızca proxyler üzerinden paylaşım(seed) yapar.",
"DownloadClientTriblerProviderMessage": "Tribler entegrasyonu oldukça deneyseldir. {clientName} istemcisinin {clientVersionRange} sürüm aralığında test edilmiştir."
}

View File

@@ -61,7 +61,8 @@ namespace NzbDrone.Core.Parser
new IsoLanguage("uz", "", "uzb", Language.Uzbek),
new IsoLanguage("ms", "", "msa", Language.Malay),
new IsoLanguage("ur", "", "urd", Language.Urdu),
new IsoLanguage("rm", "", "roh", Language.Romansh)
new IsoLanguage("rm", "", "roh", Language.Romansh),
new IsoLanguage("ka", "", "kat", Language.Georgian)
};
private static readonly Dictionary<string, Language> AlternateIsoCodeMappings = new Dictionary<string, Language>

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Parser
new RegexReplace(@".*?[_. ](S\d{2}(?:E\d{2,4})*[_. ].*)", "$1", RegexOptions.Compiled | RegexOptions.IgnoreCase)
};
private static readonly Regex LanguageRegex = new Regex(@"(?<english>\b(?:ing|eng)\b)|(?<italian>\b(?:ita|italian)\b)|(?<german>(?:swiss)?german\b|videomann|ger[. ]dub|\bger\b)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_|\b)(?:FR|VF|VF2|VFF|VFI|VFQ|TRUEFRENCH|FRENCH|FRE|FRA)(?:\W|_|\b))|(?<russian>\b(?:rus|ru)\b)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<hebrew>\bHebDub\b)|(?<polish>\b(?:PL\W?DUB|DUB\W?PL|LEK\W?PL|PL\W?LEK)\b)|(?<chinese>\[(?:CH[ST]|BIG5|GB)\]|简|繁|字幕)|(?<bulgarian>\bbgaudio\b)|(?<spanish>\b(?:español|castellano|esp|spa(?!\(Latino\)))\b)|(?<ukrainian>\b(?:\dx?)?(?:ukr))|(?<thai>\b(?:THAI)\b)|(?<romanian>\b(?:RoDubbed|ROMANIAN)\b)|(?<catalan>[-,. ]cat[. ](?:DD|subs)|\b(?:catalan|catalán)\b)|(?<latvian>\b(?:lat|lav|lv)\b)|(?<turkish>\b(?:tur)\b)|(?<urdu>\burdu\b)|(?<romansh>\b(?:romansh|rumantsch|romansch)\b)|(?<japanese>\(JA\))|(?<original>\b(?:orig|original)\b)",
private static readonly Regex LanguageRegex = new Regex(@"(?<english>\b(?:ing|eng)\b)|(?<italian>\b(?:ita|italian)\b)|(?<german>(?:swiss)?german\b|videomann|ger[. ]dub|\bger\b)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_|\b)(?:FR|VF|VF2|VFF|VFI|VFQ|TRUEFRENCH|FRENCH|FRE|FRA)(?:\W|_|\b))|(?<russian>\b(?:rus|ru)\b)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<hebrew>\bHebDub\b)|(?<polish>\b(?:PL\W?DUB|DUB\W?PL|LEK\W?PL|PL\W?LEK)\b)|(?<chinese>\[(?:CH[ST]|BIG5|GB)\]|简|繁|字幕|国语音轨[.+])|(?<bulgarian>\bbgaudio\b)|(?<spanish>\b(?:español|castellano|esp|spa(?!\(Latino\)))\b)|(?<ukrainian>\b(?:\dx?)?(?:ukr))|(?<thai>\b(?:THAI)\b)|(?<romanian>\b(?:RoDubbed|ROMANIAN)\b)|(?<catalan>[-,. ]cat[. ](?:DD|subs)|\b(?:catalan|catalán)\b)|(?<latvian>\b(?:lat|lav|lv)\b)|(?<turkish>\b(?:tur)\b)|(?<urdu>\burdu\b)|(?<romansh>\b(?:romansh|rumantsch|romansch)\b)|(?<georgian>\b(?:geo|ka|kat|georgian)\b)|(?<japanese>\(JA\))|(?<original>\b(?:orig|original)\b)",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex CaseSensitiveLanguageRegex = new Regex(@"(?:(?i)(?<!SUB[\W|_|^]))(?:(?<lithuanian>\bLT\b)|(?<czech>\bCZ\b)|(?<polish>\bPL\b)|(?<bulgarian>\bBG\b)|(?<slovak>\bSK\b)|(?<german>\bDE\b))(?:(?i)(?![\W|_|^]SUB))",
@@ -496,6 +496,11 @@ namespace NzbDrone.Core.Parser
languages.Add(Language.Romansh);
}
if (match.Groups["georgian"].Success)
{
languages.Add(Language.Georgian);
}
if (match.Groups["japanese"].Success)
{
languages.Add(Language.Japanese);

View File

@@ -43,6 +43,11 @@ namespace NzbDrone.Core.Parser.Model
/// <summary>
/// The release is nuked
/// </summary>
Nuked = 128
Nuked = 128,
/// <summary>
/// The release contains subtitles
/// </summary>
Subtitles = 256
}
}

View File

@@ -181,6 +181,10 @@ namespace NzbDrone.Core.Parser
new Regex(@"^(?<title>.+?)(?:[-_\W](?<![()\[!]))+S(?<season>(?<!\d+)(?:\d{1,2})(?!\d+))E(?<episode>\d{1,2}(?!\d+))(?:-(?<episode>\d{1,2}(?!\d+)))+(?:[-_. ]|$)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Episodes with a Chinese title and English title, Single episodes (S01E05, 1x05, etc) & Multi-episode (S01E05E06, S01E05-06, S01E05 E06, etc)
new Regex(@"^(?<title>[\u4E00-\u9FCC]+)[_. ](?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)(?:\d{1,2})(?!\d+))(?:[ex]|\W[ex]){1,2}(?<episode>\d{2,3}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{2,3}(?!\d+)))*)(?:[-_. ]|$)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Episodes with a title, Single episodes (S01E05, 1x05, etc) & Multi-episode (S01E05E06, S01E05-06, S01E05 E06, etc)
new Regex(@"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)(?:\d{1,2})(?!\d+))(?:[ex]|\W[ex]){1,2}(?<episode>\d{2,3}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{2,3}(?!\d+)))*)(?:[-_. ]|$)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
@@ -414,6 +418,10 @@ namespace NzbDrone.Core.Parser
new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)?(?<title>.+?)(?:[-_. ]\[)(?:(?:-?)(?<absoluteepisode>(?<!\d+)\d{2,3}(\.\d{1,2})?(?!\d+|[ip])))+(?:\][-_. ]).*?(?<hash>[(\[]\w{8}[)\]])?$",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Simple season releases in the format 'Series (year) - Temporada season'
new Regex(@"^(?<title>.+?)[_. ]-[_. ](?:Temporada)[-_. ]?(?<season>\d{1,2}(?![_. ]?\d+))(?:[_. ]|$)+(?<extras>EXTRAS|SUBPACK)?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Anime - Title Absolute Episode Number
new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)?(?<title>.+?)(?:[-_. ]+(?<absoluteepisode>(?<!\d+)\d{2,4}(\.\d{1,2})?(?!\d+|[ip])))+.*?(?<hash>[(\[]\w{8}[)\]])?$",
RegexOptions.IgnoreCase | RegexOptions.Compiled),

View File

@@ -23,6 +23,12 @@ internal static class ParserCommon
// Some Chinese releases don't include a separation between Chinese and English titles within the same bracketed group
new RegexReplace(@"^\[(?<subgroup>[^\]]+)\]\[(?<chinesetitle>(?<![^a-zA-Z0-9])[^a-zA-Z0-9]+)(?<title>[^\]]+?)\](?:\[\d{4}\])?\[第?(?<episode>[0-9]+(?:-[0-9]+)?)(?:话|集)?(?: ?END|完| ?Fin)?\]", "[${subgroup}] ${title} - ${episode} ", RegexOptions.Compiled),
// Chinese season packs, that may not actually have the full season so treated as multi-episode
new RegexReplace(@"^\[(?<subgroup>[^\]]+)\](?:\s?★[^\[ -]+\s?)?\[?(?:(?<chinesetitle>(?=[^\]]*?[\u4E00-\u9FCC])[^\]]*?)(?:\]\[|\s*[_/·]\s*)){0,2}(?<title>[^\]]+?)\]?(?:\[\d{4}\])?\[(?:第|全)?(?<episode>[0-9]{1,4}-[0-9]{1,4})(?:话|集)?\](?:\[(?<language>.+?)\+.+?\])[_. ](?<title>.+?)S(?<season>[0-9]{1,2})(?<rest>.+?)$", "${title}S${season}E${episode}.${language}${rest}", RegexOptions.Compiled),
// Chinese season packs, that actually have the full season so treated as multi-episode
new RegexReplace(@"^\[(?<subgroup>[^\]]+)\](?:\s?★[^\[ -]+\s?)?\[?(?:(?<chinesetitle>(?=[^\]]*?[\u4E00-\u9FCC])[^\]]*?)(?:\]\[|\s*[_/·]\s*)){0,2}(?<title>[^\]]+?)\]?(?:\[\d{4}\])?\[(?:全)?(?<episode>[0-9]{1,4})(?:话|集)?\](?:\[(?<language>.+?)\+.+?\])[_. ](?<title>.+?)S(?<season>[0-9]{1,2})(?<rest>.+?)$", "${title}S${season}.${language}${rest}", RegexOptions.Compiled),
// Most Chinese anime releases contain additional brackets/separators for chinese and non-chinese titles, remove junk and replace with normal anime pattern
new RegexReplace(@"^\[(?<subgroup>[^\]]+)\](?:\s?★[^\[ -]+\s?)?\[?(?:(?<chinesetitle>(?=[^\]]*?[\u4E00-\u9FCC])[^\]]*?)(?:\]\[|\s*[_/·]\s*)){0,2}(?<title>[^\]]+?)\]?(?:\[\d{4}\])?\[第?(?<episode>[0-9]{1,4}(?:-[0-9]{1,4})?)(?:话|集)?(?: ?END|完| ?Fin)?\]", "[${subgroup}] ${title} - ${episode} ", RegexOptions.Compiled),