Merge branch 'Sonarr:develop' into develop

pull/6711/head
The Nostrus Dominion 4 weeks ago committed by GitHub
commit 00eabf9c78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,13 @@
// This file is used to open the backend and frontend in the same workspace, which is necessary as
// the frontend has vscode settings that are distinct from the backend
{
"folders": [
{
"path": ".."
},
{
"path": "../frontend"
}
],
"settings": {}
}

@ -1,17 +1,23 @@
'connection':
- src/NzbDrone.Core/Notifications/**/*
- changed-files:
- any-glob-to-any-file: src/NzbDrone.Core/Notifications/**/*
'db-migration':
- src/NzbDrone.Core/Datastore/Migration/*
- changed-files:
- any-glob-to-any-file: src/NzbDrone.Core/Datastore/Migration/*
'download-client':
- src/NzbDrone.Core/Download/Clients/**/*
- changed-files:
- any-glob-to-any-file: src/NzbDrone.Core/Download/Clients/**/*
'indexer':
- src/NzbDrone.Core/Indexers/**/*
- changed-files:
- any-glob-to-any-file: src/NzbDrone.Core/Indexers/**/*
'parsing':
- src/NzbDrone.Core/Parser/**/*
- changed-files:
- any-glob-to-any-file: src/NzbDrone.Core/Parser/**/*
'ui-only':
- all: ['frontend/**/*']
- changed-files:
- any-glob-to-all-files: frontend/**/*

@ -22,7 +22,7 @@ env:
FRAMEWORK: net6.0
RAW_BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
SONARR_MAJOR_VERSION: 4
VERSION: 4.0.3
VERSION: 4.0.4
jobs:
backend:
@ -217,7 +217,7 @@ jobs:
deploy:
if: ${{ github.ref_name == 'develop' || github.ref_name == 'main' }}
needs: [backend, unit_test, unit_test_postgres, integration_test]
needs: [backend, frontend, unit_test, unit_test_postgres, integration_test]
secrets: inherit
uses: ./.github/workflows/deploy.yml
with:
@ -228,7 +228,7 @@ jobs:
notify:
name: Discord Notification
needs: [backend, unit_test, unit_test_postgres, integration_test]
needs: [backend, frontend, unit_test, unit_test_postgres, integration_test, deploy]
if: ${{ !cancelled() && (github.ref_name == 'develop' || github.ref_name == 'main') }}
env:
STATUS: ${{ contains(needs.*.result, 'failure') && 'failure' || 'success' }}
@ -236,7 +236,7 @@ jobs:
steps:
- name: Notify
uses: tsickert/discord-webhook@v5.3.0
uses: tsickert/discord-webhook@v6.0.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
username: 'GitHub Actions'

@ -0,0 +1,26 @@
name: Merge Conflict Labeler
on:
push:
branches:
- develop
pull_request_target:
branches:
- develop
types: [synchronize]
jobs:
label:
name: Labeling
runs-on: ubuntu-latest
if: ${{ github.repository == 'Sonarr/Sonarr' }}
permissions:
contents: read
pull-requests: write
steps:
- name: Apply label
uses: eps1lon/actions-label-merge-conflict@v3
with:
dirtyLabel: 'merge-conflict'
repoToken: '${{ secrets.GITHUB_TOKEN }}'

@ -69,12 +69,38 @@ jobs:
pattern: release_*
merge-multiple: true
- name: Get Previous Release
id: previous-release
uses: cardinalby/git-get-release-action@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
latest: true
prerelease: ${{ inputs.branch != 'main' }}
- name: Generate Release Notes
id: generate-release-notes
uses: actions/github-script@v7
with:
github-token: ${{ github.token }}
result-encoding: string
script: |
const { data } = await github.rest.repos.generateReleaseNotes({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: 'v${{ inputs.version }}',
target_commitish: '${{ github.sha }}',
previous_tag_name: '${{ steps.previous-release.outputs.tag_name }}',
})
return data.body
- name: Create release
uses: ncipollo/release-action@v1
with:
artifacts: _artifacts/Sonarr.*
commit: ${{ github.sha }}
generateReleaseNotes: true
generateReleaseNotes: false
body: ${{ steps.generate-release-notes.outputs.result }}
name: ${{ inputs.version }}
prerelease: ${{ inputs.branch != 'main' }}
skipIfReleaseExists: true

@ -9,4 +9,4 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
- uses: actions/labeler@v5

@ -9,13 +9,13 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2
- uses: dessant/lock-threads@v5
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: '90'
issue-exclude-created-before: ''
issue-exclude-labels: 'one-day-maybe'
issue-lock-labels: ''
issue-lock-comment: ''
issue-inactive-days: '90'
exclude-issue-created-before: ''
exclude-any-issue-labels: 'one-day-maybe'
add-issue-labels: ''
issue-comment: ''
issue-lock-reason: 'resolved'
process-only: ''

@ -1,8 +1,11 @@
import $ from 'jquery';
import _ from 'lodash';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import getProviderState from 'Utilities/State/getProviderState';
import { set } from '../baseActions';
const abortCurrentRequests = {};
let lastTestData = null;
export function createCancelTestProviderHandler(section) {
return function(getState, payload, dispatch) {
@ -17,10 +20,25 @@ function createTestProviderHandler(section, url) {
return function(getState, payload, dispatch) {
dispatch(set({ section, isTesting: true }));
const testData = getProviderState(payload, getState, section);
const {
queryParams = {},
...otherPayload
} = payload;
const testData = getProviderState({ ...otherPayload }, getState, section);
const params = { ...queryParams };
// If the user is re-testing the same provider without changes
// force it to be tested.
if (_.isEqual(testData, lastTestData)) {
params.forceTest = true;
}
lastTestData = testData;
const ajaxOptions = {
url: `${url}/test`,
url: `${url}/test?${$.param(params, true)}`,
method: 'POST',
contentType: 'application/json',
dataType: 'json',
@ -32,6 +50,8 @@ function createTestProviderHandler(section, url) {
abortCurrentRequests[section] = abortRequest;
request.done((data) => {
lastTestData = null;
dispatch(set({
section,
isTesting: false,

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Validation;
@ -12,5 +13,7 @@ namespace NzbDrone.Core.Test.IndexerTests
}
public string BaseUrl { get; set; }
public IEnumerable<int> MultiLanguages { get; set; }
}
}

@ -134,6 +134,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[Naruto-Kun.Hu] Anime Triangle - 08 [1080p].mkv", "Anime Triangle", 8, 0, 0)]
[TestCase("[Mystic Z-Team] Series Title Super - Episode 013 VF - Non-censuré [720p].mp4", "Series Title Super", 13, 0, 0)]
[TestCase("Series Title Kai Episodio 13 Audio Latino", "Series Title Kai", 13, 0, 0)]
[TestCase("Series_Title_2_[01]_[AniLibria_TV]_[WEBRip_1080p]", "Series Title 2", 1, 0, 0)]
// [TestCase("", "", 0, 0, 0)]
public void should_parse_absolute_numbers(string postTitle, string title, int absoluteEpisodeNumber, int seasonNumber, int episodeNumber)
@ -179,6 +180,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[Erai-raws] Series-Title! 2 - 01~10 [1080p][Multiple Subtitle]", "Series-Title! 2", 1, 10)]
[TestCase("[Erai-raws] Series Title! - 01 ~ 10 [1080p][Multiple Subtitle]", "Series Title!", 1, 10)]
[TestCase("[Erai-raws] Series-Title! 2 - 01 ~ 10 [1080p][Multiple Subtitle]", "Series-Title! 2", 1, 10)]
[TestCase("Series_Title_2_[01-05]_[AniLibria_TV]_[WEBRip_1080p]", "Series Title 2", 1, 5)]
// [TestCase("", "", 1, 2)]
public void should_parse_multi_episode_absolute_numbers(string postTitle, string title, int firstAbsoluteEpisodeNumber, int lastAbsoluteEpisodeNumber)

@ -429,6 +429,26 @@ namespace NzbDrone.Core.Test.ParserTests
result.Languages.Should().Contain(Language.English);
}
[TestCase("Остання серія (Сезон 1) / The Last Series (Season 1) (2024) WEB-DLRip-AVC 2xUkr/Eng | Sub Ukr/Eng")]
[TestCase("Справжня серія (Сезон 1-3) / True Series (Season 1-3) (2014-2019) BDRip-AVC 3xUkr/Eng | Ukr/Eng")]
[TestCase("Серія (Сезон 1-3) / The Series (Seasons 1-3) (2019-2022) BDRip-AVC 4xUkr/Eng | Sub 2xUkr/Eng")]
public void should_parse_english_and_ukranian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Languages.Count.Should().Be(2);
result.Languages.Should().Contain(Language.Ukrainian);
result.Languages.Should().Contain(Language.English);
}
[TestCase("Серія (Сезон 1, серії 01-26 із 51) / Seri (Season 1, episodes 01-26) (2018) WEBRip-AVC 2Ukr/Tur")]
public void should_parse_turkish_and_ukranian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Languages.Count.Should().Be(2);
result.Languages.Should().Contain(Language.Ukrainian);
result.Languages.Should().Contain(Language.Turkish);
}
[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")]

@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.BroadcastheNet
@ -23,6 +26,7 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
{
BaseUrl = "https://api.broadcasthe.net/";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
MultiLanguages = Array.Empty<int>();
}
[FieldDefinition(0, Label = "IndexerSettingsApiUrl", Advanced = true, HelpText = "IndexerSettingsApiUrlHelpText")]
@ -40,6 +44,9 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
[FieldDefinition(5, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Fanzub
@ -19,6 +22,7 @@ namespace NzbDrone.Core.Indexers.Fanzub
public FanzubSettings()
{
BaseUrl = "http://fanzub.com/rss/";
MultiLanguages = Array.Empty<int>();
}
[FieldDefinition(0, Label = "IndexerSettingsRssUrl", HelpText = "IndexerSettingsRssUrlHelpText")]
@ -28,6 +32,9 @@ namespace NzbDrone.Core.Indexers.Fanzub
[FieldDefinition(1, Label = "IndexerSettingsAnimeStandardFormatSearch", Type = FieldType.Checkbox, HelpText = "IndexerSettingsAnimeStandardFormatSearchHelpText")]
public bool AnimeStandardFormatSearch { get; set; }
[FieldDefinition(2, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.FileList
@ -35,6 +36,7 @@ namespace NzbDrone.Core.Indexers.FileList
};
AnimeCategories = Array.Empty<int>();
MultiLanguages = Array.Empty<int>();
}
[FieldDefinition(0, Label = "Username", Privacy = PrivacyLevel.UserName)]
@ -43,6 +45,9 @@ namespace NzbDrone.Core.Indexers.FileList
[FieldDefinition(1, Label = "IndexerSettingsPasskey", Privacy = PrivacyLevel.ApiKey)]
public string Passkey { get; set; }
[FieldDefinition(2, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(3, Label = "IndexerSettingsApiUrl", Advanced = true, HelpText = "IndexerSettingsApiUrlHelpText")]
public string BaseUrl { get; set; }

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.HDBits
@ -29,6 +30,7 @@ namespace NzbDrone.Core.Indexers.HDBits
Categories = new[] { (int)HdBitsCategory.Tv, (int)HdBitsCategory.Documentary };
Codecs = Array.Empty<int>();
Mediums = Array.Empty<int>();
MultiLanguages = Array.Empty<int>();
}
[FieldDefinition(0, Label = "IndexerSettingsApiUrl", Advanced = true, HelpText = "IndexerSettingsApiUrlHelpText")]
@ -58,6 +60,9 @@ namespace NzbDrone.Core.Indexers.HDBits
[FieldDefinition(8, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
[FieldDefinition(9, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -1,9 +1,12 @@
using NzbDrone.Core.ThingiProvider;
using System.Collections.Generic;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers
{
public interface IIndexerSettings : IProviderConfig
{
string BaseUrl { get; set; }
IEnumerable<int> MultiLanguages { get; set; }
}
}

@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.IPTorrents
@ -29,6 +32,7 @@ namespace NzbDrone.Core.Indexers.IPTorrents
public IPTorrentsSettings()
{
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
MultiLanguages = Array.Empty<int>();
}
[FieldDefinition(0, Label = "IndexerIPTorrentsSettingsFeedUrl", HelpText = "IndexerIPTorrentsSettingsFeedUrlHelpText")]
@ -43,6 +47,9 @@ namespace NzbDrone.Core.Indexers.IPTorrents
[FieldDefinition(3, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
[FieldDefinition(4, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -1,12 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
@ -17,6 +20,8 @@ namespace NzbDrone.Core.Indexers
public abstract class IndexerBase<TSettings> : IIndexer
where TSettings : IIndexerSettings, new()
{
private static readonly Regex MultiRegex = new (@"[_. ](?<multi>multi)[_. ]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
protected readonly IIndexerStatusService _indexerStatusService;
protected readonly IConfigService _configService;
protected readonly IParsingService _parsingService;
@ -84,9 +89,16 @@ namespace NzbDrone.Core.Indexers
protected virtual IList<ReleaseInfo> CleanupReleases(IEnumerable<ReleaseInfo> releases)
{
var result = releases.DistinctBy(v => v.Guid).ToList();
var settings = Definition.Settings as IIndexerSettings;
result.ForEach(c =>
{
// Use multi languages from setting if ReleaseInfo languages is empty
if (c.Languages.Empty() && MultiRegex.IsMatch(c.Title) && settings.MultiLanguages.Any())
{
c.Languages = settings.MultiLanguages.Select(i => (Language)i).ToList();
}
c.IndexerId = Definition.Id;
c.Indexer = Definition.Name;
c.DownloadProtocol = Protocol;

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Newznab
@ -55,6 +57,7 @@ namespace NzbDrone.Core.Indexers.Newznab
ApiPath = "/api";
Categories = new[] { 5030, 5040 };
AnimeCategories = Enumerable.Empty<int>();
MultiLanguages = Array.Empty<int>();
}
[FieldDefinition(0, Label = "URL")]
@ -79,7 +82,10 @@ namespace NzbDrone.Core.Indexers.Newznab
[FieldDefinition(6, Label = "IndexerSettingsAdditionalParameters", HelpText = "IndexerSettingsAdditionalNewznabParametersHelpText", Advanced = true)]
public string AdditionalParameters { get; set; }
// Field 7 is used by TorznabSettings MinimumSeeders
[FieldDefinition(7, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
// Field 8 is used by TorznabSettings MinimumSeeders
// If you need to add another field here, update TorznabSettings as well and this comment
public virtual NzbDroneValidationResult Validate()

@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Nyaa
@ -25,6 +28,7 @@ namespace NzbDrone.Core.Indexers.Nyaa
BaseUrl = "";
AdditionalParameters = "&cats=1_0&filter=1";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
MultiLanguages = Array.Empty<int>();
}
[FieldDefinition(0, Label = "IndexerSettingsWebsiteUrl")]
@ -45,6 +49,9 @@ namespace NzbDrone.Core.Indexers.Nyaa
[FieldDefinition(5, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.TorrentRss
@ -23,6 +26,7 @@ namespace NzbDrone.Core.Indexers.TorrentRss
BaseUrl = string.Empty;
AllowZeroSize = false;
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
MultiLanguages = Array.Empty<int>();
}
[FieldDefinition(0, Label = "IndexerSettingsRssUrl")]
@ -43,6 +47,9 @@ namespace NzbDrone.Core.Indexers.TorrentRss
[FieldDefinition(5, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
[FieldDefinition(6, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Torrentleech
@ -23,6 +26,7 @@ namespace NzbDrone.Core.Indexers.Torrentleech
{
BaseUrl = "http://rss.torrentleech.org";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
MultiLanguages = Array.Empty<int>();
}
[FieldDefinition(0, Label = "IndexerSettingsWebsiteUrl")]
@ -40,6 +44,9 @@ namespace NzbDrone.Core.Indexers.Torrentleech
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
[FieldDefinition(5, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "IndexerSettingsMultiLanguageRelease", HelpText = "IndexerSettingsMultiLanguageReleaseHelpText", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -49,13 +49,13 @@ namespace NzbDrone.Core.Indexers.Torznab
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
}
[FieldDefinition(7, Type = FieldType.Number, Label = "IndexerSettingsMinimumSeeders", HelpText = "IndexerSettingsMinimumSeedersHelpText", Advanced = true)]
[FieldDefinition(8, Type = FieldType.Number, Label = "IndexerSettingsMinimumSeeders", HelpText = "IndexerSettingsMinimumSeedersHelpText", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(8)]
[FieldDefinition(9)]
public SeedCriteriaSettings SeedCriteria { get; set; } = new SeedCriteriaSettings();
[FieldDefinition(9, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
[FieldDefinition(10, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
public override NzbDroneValidationResult Validate()

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Languages
{
public class RealLanguageFieldConverter : ISelectOptionsConverter
{
public List<SelectOption> GetSelectOptions()
{
return Language.All
.Where(l => l != Language.Unknown)
.OrderBy(l => l.Id > 0).ThenBy(l => l.Name)
.ToList()
.ConvertAll(v => new SelectOption { Value = v.Id, Name = v.Name });
}
}
}

@ -978,6 +978,8 @@
"IndexerSettingsPasskey": "Passkey",
"IndexerSettingsRejectBlocklistedTorrentHashes": "Reject Blocklisted Torrent Hashes While Grabbing",
"IndexerSettingsRejectBlocklistedTorrentHashesHelpText": "If a torrent is blocked by hash it may not properly be rejected during RSS/Search for some indexers, enabling this will allow it to be rejected after the torrent is grabbed, but before it is sent to the client.",
"IndexerSettingsMultiLanguageRelease": "Multi Languages",
"IndexerSettingsMultiLanguageReleaseHelpText": "What languages are normally in a multi release on this indexer?",
"IndexerSettingsRssUrl": "RSS URL",
"IndexerSettingsRssUrlHelpText": "Enter to URL to an {indexer} compatible RSS feed",
"IndexerSettingsSeasonPackSeedTime": "Season-Pack Seed Time",

@ -2066,5 +2066,8 @@
"ReleaseGroupFootNote": "Opcionalmente controla el truncamiento hasta un número máximo de bytes, incluyendo elipsis (`...`). Está soportado truncar tanto desde el final (p. ej. `{Grupo de lanzamiento:30}`) como desde el principio (p. ej. `{Grupo de lanzamiento:-30}`).",
"SelectReleaseType": "Seleccionar tipo de lanzamiento",
"SeriesFootNote": "Opcionalmente controla el truncamiento hasta un número máximo de bytes, incluyendo elipsis (`...`). Está soportado truncar tanto desde el final (p. ej. `{Título de serie:30}`) como desde el principio (p. ej. `{Título de serie:-30}`).",
"EpisodeTitleFootNote": "Opcionalmente controla el truncamiento hasta un número máximo de bytes, incluyendo elipsis (`...`). Está soportado truncar tanto desde el final (p. ej. `{Título de episodio:30}`) como desde el principio (p. ej. `{Título de episodio:-30}`). Los títulos de episodio serán truncados automáticamente acorde a las limitaciones del sistema de archivos si es necesario."
"EpisodeTitleFootNote": "Opcionalmente controla el truncamiento hasta un número máximo de bytes, incluyendo elipsis (`...`). Está soportado truncar tanto desde el final (p. ej. `{Título de episodio:30}`) como desde el principio (p. ej. `{Título de episodio:-30}`). Los títulos de episodio serán truncados automáticamente acorde a las limitaciones del sistema de archivos si es necesario.",
"AutoTaggingSpecificationTag": "Etiqueta",
"NotificationsTelegramSettingsIncludeAppName": "Incluir {appName} en el título",
"NotificationsTelegramSettingsIncludeAppNameHelpText": "Prefija opcionalmente el título de mensaje con {appName} para diferenciar notificaciones de aplicaciones diferentes"
}

@ -68,11 +68,11 @@
"CancelPendingTask": "Êtes-vous sur de vouloir annuler cette tâche en attente ?",
"Clear": "Effacer",
"AddAutoTagError": "Impossible d'ajouter un nouveau tag automatique, veuillez réessayer.",
"AddConditionError": "Impossible d'ajouter une nouvelle condition, Réessayer.",
"AddConditionError": "Impossible d'ajouter une nouvelle condition, veuillez réessayer.",
"AddCondition": "Ajouter une condition",
"AddAutoTag": "Ajouter un tag automatique",
"AddCustomFormatError": "Impossible d'ajouter un nouveau format personnalisé, veuillez réessayer.",
"AddIndexerError": "Impossible d'ajouter un nouvelle indexeur, veuillez réessayer.",
"AddIndexerError": "Impossible d'ajouter un nouvel indexeur, veuillez réessayer.",
"AddNewRestriction": "Ajouter une nouvelle restriction",
"AddListError": "Impossible d'ajouter une nouvelle liste, veuillez réessayer.",
"AddDownloadClientError": "Impossible d'ajouter un nouveau client de téléchargement, veuillez réessayer.",
@ -534,7 +534,7 @@
"IndexerSearchNoInteractiveHealthCheckMessage": "Aucun indexeur n'est disponible avec la recherche interactive activée. {appName} ne fournira aucun résultat de recherche interactif",
"IndexerStatusUnavailableHealthCheckMessage": "Indexeurs indisponibles en raison d'échecs : {indexerNames}",
"Info": "Information",
"InstallLatest": "Installer le dernier",
"InstallLatest": "Installer la dernière",
"InteractiveImportNoLanguage": "La ou les langues doivent être choisies pour chaque fichier sélectionné",
"InteractiveImportNoQuality": "La qualité doit être choisie pour chaque fichier sélectionné",
"InteractiveSearchModalHeader": "Recherche interactive",
@ -2066,5 +2066,8 @@
"EpisodeTitleFootNote": "Contrôlez éventuellement la troncature à un nombre maximum d'octets, y compris les points de suspension (`...`). La troncature de la fin (par exemple `{Episode Title:30}`) ou du début (par exemple `{Episode Title:-30}`) sont toutes deux prises en charge. Les titres des épisodes seront automatiquement tronqués en fonction des limitations du système de fichiers si nécessaire.",
"SelectReleaseType": "Sélectionnez le type de version",
"SeriesFootNote": "Contrôlez éventuellement la troncature à un nombre maximum d'octets, y compris les points de suspension (`...`). La troncature de la fin (par exemple `{Series Title:30}`) ou du début (par exemple `{Series Title:-30}`) sont toutes deux prises en charge.",
"ReleaseGroupFootNote": "Contrôlez éventuellement la troncature à un nombre maximum d'octets, y compris les points de suspension (`...`). La troncature de la fin (par exemple `{Release Group:30}`) ou du début (par exemple `{Release Group:-30}`) sont toutes deux prises en charge.`)."
"ReleaseGroupFootNote": "Contrôlez éventuellement la troncature à un nombre maximum d'octets, y compris les points de suspension (`...`). La troncature de la fin (par exemple `{Release Group:30}`) ou du début (par exemple `{Release Group:-30}`) sont toutes deux prises en charge.`).",
"AutoTaggingSpecificationTag": "Étiquette",
"NotificationsTelegramSettingsIncludeAppName": "Inclure {appName} dans le Titre",
"NotificationsTelegramSettingsIncludeAppNameHelpText": "Préfixer éventuellement le titre du message par {appName} pour différencier les notifications des différentes applications"
}

@ -249,5 +249,6 @@
"AnimeEpisodeTypeFormat": "Numero assoluto dell'episodio ({format})",
"AutoRedownloadFailed": "Download fallito",
"AddDelayProfileError": "Impossibile aggiungere un nuovo profilo di ritardo, riprova.",
"Cutoff": "Taglio"
"Cutoff": "Taglio",
"AddListExclusion": "Aggiungi elenco esclusioni"
}

@ -989,7 +989,7 @@
"AgeWhenGrabbed": "Tempo de vida (quando obtido)",
"DelayingDownloadUntil": "Atrasando o download até {date} às {time}",
"DeletedReasonEpisodeMissingFromDisk": "O {appName} não conseguiu encontrar o arquivo no disco, então o arquivo foi desvinculado do episódio no banco de dados",
"DeletedReasonManual": "O arquivo foi excluído por meio da IU",
"DeletedReasonManual": "O arquivo foi excluído usando {appName} manualmente ou por outra ferramenta por meio da API",
"DownloadFailed": "Download Falhou",
"DestinationRelativePath": "Caminho Relativo de Destino",
"DownloadIgnoredEpisodeTooltip": "Download do Episódio Ignorado",
@ -2061,5 +2061,12 @@
"ImportListsMyAnimeListSettingsAuthenticateWithMyAnimeList": "Autenticar com MyAnimeList",
"ImportListsMyAnimeListSettingsListStatus": "Status da Lista",
"ImportListsMyAnimeListSettingsListStatusHelpText": "Tipo de lista da qual você deseja importar, defina como 'Todas' para todas as listas",
"CustomFormatsSettingsTriggerInfo": "Um formato personalizado será aplicado a um lançamento ou arquivo quando corresponder a pelo menos um de cada um dos diferentes tipos de condição escolhidos."
"CustomFormatsSettingsTriggerInfo": "Um formato personalizado será aplicado a um lançamento ou arquivo quando corresponder a pelo menos um de cada um dos diferentes tipos de condição escolhidos.",
"EpisodeTitleFootNote": "Opcionalmente, controle o truncamento para um número máximo de bytes, incluindo reticências (`...`). Truncar do final (por exemplo, `{Episode Title:30}`) ou do início (por exemplo, `{Episode Title:-30}`) é suportado. Os títulos dos episódios serão automaticamente truncados de acordo com as limitações do sistema de arquivos, se necessário.",
"NotificationsTelegramSettingsIncludeAppNameHelpText": "Opcionalmente, prefixe o título da mensagem com {appName} para diferenciar notificações de diferentes aplicativos",
"ReleaseGroupFootNote": "Opcionalmente, controle o truncamento para um número máximo de bytes, incluindo reticências (`...`). Truncar do final (por exemplo, `{Release Group:30}`) ou do início (por exemplo, `{Release Group:-30}`) é suportado.`).",
"ClickToChangeReleaseType": "Clique para alterar o tipo de lançamento",
"NotificationsTelegramSettingsIncludeAppName": "Incluir {appName} no Título",
"SelectReleaseType": "Selecionar o Tipo de Lançamento",
"SeriesFootNote": "Opcionalmente, controle o truncamento para um número máximo de bytes, incluindo reticências (`...`). Truncar do final (por exemplo, `{Series Title:30}`) ou do início (por exemplo, `{Series Title:-30}`) é suportado."
}

@ -136,12 +136,90 @@
"AuthenticationMethod": "Kimlik Doğrulama Yöntemi",
"AuthenticationRequired": "Kimlik Doğrulama Gerekli",
"AuthenticationRequiredWarning": "Kimlik doğrulaması olmadan uzaktan erişimi engellemek için, {appName}'da artık kimlik doğrulamanın etkinleştirilmesini gerektiriyor. İsteğe bağlı olarak yerel adresler için kimlik doğrulamayı devre dışı bırakabilirsiniz.",
"ApiKeyValidationHealthCheckMessage": "Lütfen API anahtarınızı en az {length} karakter uzunluğunda olacak şekilde güncelleyin. Bunu ayarlar veya yapılandırma dosyası aracılığıyla yapabilirsiniz",
"ApiKeyValidationHealthCheckMessage": "Lütfen API anahtarınızı en az {length} karakter sayısı kadar güncelleyiniz. Bunu ayarlar veya yapılandırma dosyası üzerinden yapabilirsiniz",
"ClearBlocklistMessageText": "Engellenenler listesindeki tüm öğeleri temizlemek istediğinizden emin misiniz?",
"AutomaticUpdatesDisabledDocker": "Docker güncelleme mekanizması kullanıldığında otomatik güncellemeler doğrudan desteklenmez. Kapsayıcı görüntüsünü {appName} dışında güncellemeniz veya bir komut dosyası kullanmanız gerekecek",
"ConnectionLostReconnect": "{appName} otomatik bağlanmayı deneyecek veya aşağıda yeniden yükle seçeneğini işaretleyebilirsiniz.",
"BlackholeWatchFolderHelpText": "{appName} uygulamasının tamamlanmış indirmeleri içe aktaracağı klasör",
"AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Yeni şifreyi onayla",
"BindAddressHelpText": "Tüm arayüzler için geçerli IP adresi, localhost veya '*'",
"CloneAutoTag": "Otomatik Etiketi Klonla"
"CloneAutoTag": "Otomatik Etiketi Klonla",
"Dash": "Çizgi",
"DeleteReleaseProfileMessageText": "'{name}' bu sürüm profilini silmek istediğinizden emin misiniz?",
"DownloadClientFreeboxApiError": "Freebox API'si şu hatayı döndürdü: {errorDescription}",
"DeleteSelectedDownloadClients": "İndirme İstemcilerini Sil",
"DeleteSelectedDownloadClientsMessageText": "Seçilen {count} indirme istemcisini silmek istediğinizden emin misiniz?",
"DeleteRootFolderMessageText": "'{path}' kök klasörünü silmek istediğinizden emin misiniz?",
"DeleteSpecificationHelpText": "'{name}' spesifikasyonunu silmek istediğinizden emin misiniz?",
"DeletedReasonUpgrade": "Bir yükseltmeyi içe aktarmak için dosya silindi",
"DelayMinutes": "{delay} Dakika",
"DeleteImportListMessageText": "'{name}' listesini silmek istediğinizden emin misiniz?",
"DeleteReleaseProfile": "Sürüm Profilini Sil",
"DeleteSelectedIndexers": "Dizin Oluşturucuları Sil",
"Directory": "Rehber",
"Donate": "Bağış yap",
"DownloadClientDownloadStationValidationFolderMissing": "Klasör mevcut değil",
"DownloadClientFloodSettingsAdditionalTags": "Ek Etiketler",
"DownloadClientFloodSettingsPostImportTags": "İçe Aktarma Sonrası Etiketler",
"DownloadClientFloodSettingsStartOnAdd": "Eklemeye Başla",
"DownloadClientFloodSettingsTagsHelpText": "Bir indirme işleminin başlangıç etiketleri. Bir indirmenin tanınabilmesi için tüm başlangıç etiketlerine sahip olması gerekir. Bu, ilgisiz indirmelerle çakışmaları önler.",
"DownloadClientAriaSettingsDirectoryHelpText": "İndirilenlerin yerleştirileceği isteğe bağlı konum, varsayılan Aria2 konumunu kullanmak için boş bırakın",
"DefaultNameCopiedProfile": "{name} - Kopyala",
"DeleteAutoTag": "Etiketi Otomatik Sil",
"DeleteCondition": "Koşulu Sil",
"DeleteDelayProfileMessageText": "Bu gecikme profilini silmek istediğinizden emin misiniz?",
"DeleteRootFolder": "Kök Klasörü Sil",
"DeleteSpecification": "Spesifikasyonu Sil",
"DeletedReasonManual": "Dosya, {appName} kullanılarak manuel olarak veya API aracılığıyla başka bir araçla silindi",
"DeleteCustomFormatMessageText": "'{name}' özel biçimini silmek istediğinizden emin misiniz?",
"DefaultNameCopiedSpecification": "{name} - Kopyala",
"DeleteConditionMessageText": "'{name}' koşulunu silmek istediğinizden emin misiniz?",
"DeleteImportListExclusionMessageText": "Bu içe aktarma listesi hariç tutma işlemini silmek istediğinizden emin misiniz?",
"DeleteQualityProfileMessageText": "'{name}' kalite profilini silmek istediğinizden emin misiniz?",
"DeleteSelectedIndexersMessageText": "Seçilen {count} dizin oluşturucuyu silmek istediğinizden emin misiniz?",
"DownloadClientDelugeValidationLabelPluginFailureDetail": "{appName}, etiketi {clientName} uygulamasına ekleyemedi.",
"DownloadClientDownloadStationProviderMessage": "DSM hesabınızda 2 Faktörlü Kimlik Doğrulama etkinleştirilmişse {appName}, Download Station'a bağlanamaz",
"DownloadClientDownloadStationValidationApiVersion": "Download Station API sürümü desteklenmiyor; en az {requiredVersion} olmalıdır. {minVersion}'dan {maxVersion}'a kadar destekler",
"DownloadClientDownloadStationValidationNoDefaultDestination": "Varsayılan hedef yok",
"DownloadClientFloodSettingsAdditionalTagsHelpText": "Medyanın özelliklerini etiket olarak ekler. İpuçları örnektir.",
"DownloadClientFloodSettingsPostImportTagsHelpText": "İndirmelere içe aktarıldıktan sonra etiket ekler.",
"DownloadClientFloodSettingsUrlBaseHelpText": "Flood API'sine {url} gibi bir önek ekler",
"ReplaceIllegalCharactersHelpText": "Geçersiz karakterleri değiştirin. İşaretlenmezse bunun yerine {appName} bunları kaldıracak",
"ConnectionSettingsUrlBaseHelpText": "{connectionName} URL'sine {url} gibi bir önek ekler",
"DeleteSelectedImportLists": "İçe Aktarma Listelerini Sil",
"DelayingDownloadUntil": "İndirme işlemi {date} tarihine, {time} tarihine kadar erteleniyor",
"Destination": "Hedef",
"DoNotBlocklist": "Engelleme Listesine Eklemeyin",
"DoNotBlocklistHint": "Engellenenler listesine eklemeden kaldır",
"DownloadClientDelugeTorrentStateError": "Deluge bir hata bildiriyor",
"DownloadClientDelugeValidationLabelPluginFailure": "Etiket yapılandırılması başarısız oldu",
"DownloadClientDownloadStationValidationSharedFolderMissing": "Paylaşılan klasör mevcut değil",
"DeleteImportList": "İçe Aktarma Listesini Sil",
"IndexerPriorityHelpText": "Dizin Oluşturucu Önceliği (En Yüksek) 1'den (En Düşük) 50'ye kadar. Varsayılan: 25'dir. Eşit olmayan sürümler için eşitlik bozucu olarak sürümler alınırken kullanılan {appName}, RSS Senkronizasyonu ve Arama için etkinleştirilmiş tüm dizin oluşturucuları kullanmaya devam edecek",
"DisabledForLocalAddresses": "Yerel Adresler için Devre Dışı Bırakıldı",
"DownloadClientDelugeValidationLabelPluginInactive": "Etiket eklentisi etkinleştirilmedi",
"DownloadClientDelugeValidationLabelPluginInactiveDetail": "Kategorileri kullanmak için {clientName} uygulamasında Etiket eklentisini etkinleştirmiş olmanız gerekir.",
"DownloadClientDownloadStationValidationNoDefaultDestinationDetail": "Diskstation'ınızda {username} olarak oturum açmalı ve BT/HTTP/FTP/NZB -> Konum altında DownloadStation ayarlarında manuel olarak ayarlamalısınız.",
"DownloadClientDownloadStationValidationSharedFolderMissingDetail": "Diskstation'da '{sharedFolder}' adında bir Paylaşımlı Klasör yok, bunu doğru belirttiğinizden emin misiniz?",
"DownloadClientFloodSettingsRemovalInfo": "{appName}, Ayarlar -> Dizin Oluşturucular'daki mevcut tohum kriterlerine göre torrentlerin otomatik olarak kaldırılmasını gerçekleştirecek",
"Database": "Veri tabanı",
"DelayProfileProtocol": "Protokol: {preferredProtocol}",
"DownloadClientDownloadStationValidationFolderMissingDetail": "'{downloadDir}' klasörü mevcut değil, '{sharedFolder}' Paylaşımlı Klasöründe manuel olarak oluşturulması gerekiyor.",
"DeleteAutoTagHelpText": "'{name}' etiketini otomatik silmek istediğinizden emin misiniz?",
"DownloadClientDelugeSettingsUrlBaseHelpText": "Deluge json URL'sine bir önek ekler, bkz. {url}",
"DownloadClientFreeboxSettingsPortHelpText": "Freebox arayüzüne erişim için kullanılan bağlantı noktası, varsayılan olarak '{port}' şeklindedir",
"DownloadClientFreeboxUnableToReachFreebox": "Freebox API'sine ulaşılamıyor. 'Ana Bilgisayar', 'Bağlantı Noktası' veya 'SSL Kullan' ayarlarını doğrulayın. (Hata: {istisnaMessage})",
"CustomFormatsSettingsTriggerInfo": "Bir yayına veya dosyaya, seçilen farklı koşul türlerinden en az biriyle eşleştiğinde Özel Format uygulanacaktır.",
"Default": "Varsayılan",
"DeleteSelectedImportListsMessageText": "Seçilen {count} içe aktarma listesini silmek istediğinizden emin misiniz?",
"DownloadClientDelugeSettingsDirectory": "İndirme Dizini",
"DownloadClientDelugeSettingsDirectoryCompleted": "Tamamlandığında Dizini Taşı",
"DownloadClientDelugeSettingsDirectoryCompletedHelpText": "Tamamlanan indirmelerin taşınacağı isteğe bağlı konum; varsayılan Deluge konumunu kullanmak için boş bırakın",
"DownloadClientDelugeSettingsDirectoryHelpText": "İndirilenlerin yerleştirileceği isteğe bağlı konum; varsayılan Deluge konumunu kullanmak için boş bırakın",
"DownloadClientDownloadStationSettingsDirectoryHelpText": "İndirilenlerin yerleştirileceği isteğe bağlı paylaşımlı klasör, varsayılan Download Station konumunu kullanmak için boş bırakın",
"ApiKey": "API Anahtarı",
"Analytics": "Analiz",
"All": "Herşey",
"AppDataLocationHealthCheckMessage": "Güncellemede AppData'nın silinmesini önlemek için güncelleme mümkün olmayacak",
"AnalyticsEnabledHelpText": "Anonim kullanım ve hata bilgilerini {appName} sunucularına gönderin. Bu, tarayıcınızla ilgili bilgileri, kullandığınız {appName} Web arayüz sayfalarını, hata raporlamasının yanı sıra işletim sistemi ve çalışma zamanı sürümünü içerir. Bu bilgileri, özellikleri ve hata düzeltmelerini önceliklendirmek için kullanacağız."
}

@ -588,7 +588,6 @@
"DownloadIgnored": "忽略下载",
"DownloadIgnoredEpisodeTooltip": "集下载被忽略",
"EditAutoTag": "编辑自动标签",
"AddAutoTagError": "无法添加新的自动标签,请重试。",
"AddImportListExclusionError": "无法添加新排除列表,请再试一次。",
"AddIndexer": "添加索引器",
"AddImportList": "添加导入列表",

@ -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(@"(?:\W|_)(?<english>\b(?:ing|eng)\b)|(?<italian>\b(?:ita|italian)\b)|(?<german>german\b|videomann|ger[. ]dub)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_)(?:FR|VF|VF2|VFF|VFQ|TRUEFRENCH)(?:\W|_))|(?<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(?:ukr)\b)|(?<thai>\b(?:THAI)\b)|(?<romanian>\b(?:RoDubbed|ROMANIAN)\b)|(?<catalan>[-,. ]cat[. ](?:DD|subs)|\b(?:catalan|catalán)\b)|(?<latvian>\b(?:lat|lav|lv)\b)",
private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?<english>\b(?:ing|eng)\b)|(?<italian>\b(?:ita|italian)\b)|(?<german>german\b|videomann|ger[. ]dub)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_)(?:FR|VF|VF2|VFF|VFQ|TRUEFRENCH)(?:\W|_))|(?<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)",
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))(?:(?i)(?![\W|_|^]SUB))",
@ -470,6 +470,11 @@ namespace NzbDrone.Core.Parser
{
languages.Add(Language.Latvian);
}
if (match.Groups["turkish"].Success)
{
languages.Add(Language.Turkish);
}
}
return languages;

@ -294,10 +294,10 @@ namespace NzbDrone.Core.Parser
new Regex(@"^(?<title>.+?)(?:_|-|\s|\.)+S(?<season>\d{2}(?!\d+))(\W-\W)E(?<episode>(?<!\d+)\d{2}(?!\d+))(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Season and episode numbers in square brackets (single and mult-episode)
// Season and episode numbers in square brackets (single and multi-episode)
// Series Title - [02x01] - Episode 1
// Series Title - [02x01x02] - Episode 1
new Regex(@"^(?<title>.+?)?(?:[-_\W](?<![()\[!]))+\[(?<season>(?<!\d+)\d{1,2})(?:(?:-|x){1,2}(?<episode>\d{2}))+\].+?(?:\.|$)",
new Regex(@"^(?<title>.+?)?(?:[-_\W](?<![()\[!]))+\[(?:s)?(?<season>(?<!\d+)\d{1,2})(?:(?:[ex])(?<episode>\d{2}))(?:(?:[-ex]){1,2}(?<episode>\d{2}))*\].+?(?:\.|$)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Anime - Title with season number - Absolute Episode Number (Title S01 - EP14)
@ -328,10 +328,6 @@ namespace NzbDrone.Core.Parser
new Regex(@"^(?<title>.+?)[-_. ]+?(?:S|Season|Saison|Series|Stagione)[-_. ]?(?<season>\d{4}(?![-_. ]?\d+))(\W+|_|$)(?<extras>EXTRAS|SUBPACK)?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Episodes with a title and season/episode in square brackets
new Regex(@"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+\[S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>(?<!\d+)\d{2}(?!\d+|i|p)))+\])\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Supports 103/113 naming
new Regex(@"^(?<title>.+?)?(?:(?:[_.-](?<![()\[!]))+(?<season>(?<!\d+)[1-9])(?<episode>[1-9][0-9]|[0][1-9])(?![a-z]|\d+))+(?:[_.]|$)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
@ -409,6 +405,10 @@ namespace NzbDrone.Core.Parser
new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)?(?<title>.+?)[-_. ]+(?:Episode|Episodio)(?:[-_. ]+(?<absoluteepisode>(?<!\d+)\d{2,4}(\.\d{1,2})?(?!\d+|[ip])))+.*?(?<hash>[(\[]\w{8}[)\]])?$",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Anime - Title [Absolute Episode Number] from AniLibriaTV
new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)?(?<title>.+?)(?:[-_. ]\[)(?:(?:-?)(?<absoluteepisode>(?<!\d+)\d{2,3}(\.\d{1,2})?(?!\d+|[ip])))+(?:\][-_. ]).*?(?<hash>[(\[]\w{8}[)\]])?$",
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),

@ -205,10 +205,10 @@ namespace Sonarr.Api.V3
[SkipValidation(true, false)]
[HttpPost("test")]
[Consumes("application/json")]
public object Test([FromBody] TProviderResource providerResource)
public object Test([FromBody] TProviderResource providerResource, [FromQuery] bool forceTest = false)
{
var existingDefinition = providerResource.Id > 0 ? _providerFactory.Find(providerResource.Id) : null;
var providerDefinition = GetDefinition(providerResource, existingDefinition, true, true, true);
var providerDefinition = GetDefinition(providerResource, existingDefinition, true, !forceTest, true);
Test(providerDefinition, true);

@ -1654,6 +1654,16 @@
"tags": [
"DownloadClient"
],
"parameters": [
{
"name": "forceTest",
"in": "query",
"schema": {
"type": "boolean",
"default": false
}
}
],
"requestBody": {
"content": {
"application/json": {
@ -2897,6 +2907,16 @@
"tags": [
"ImportList"
],
"parameters": [
{
"name": "forceTest",
"in": "query",
"schema": {
"type": "boolean",
"default": false
}
}
],
"requestBody": {
"content": {
"application/json": {
@ -3487,6 +3507,16 @@
"tags": [
"Indexer"
],
"parameters": [
{
"name": "forceTest",
"in": "query",
"schema": {
"type": "boolean",
"default": false
}
}
],
"requestBody": {
"content": {
"application/json": {
@ -4470,6 +4500,16 @@
"tags": [
"Metadata"
],
"parameters": [
{
"name": "forceTest",
"in": "query",
"schema": {
"type": "boolean",
"default": false
}
}
],
"requestBody": {
"content": {
"application/json": {
@ -5032,6 +5072,16 @@
"tags": [
"Notification"
],
"parameters": [
{
"name": "forceTest",
"in": "query",
"schema": {
"type": "boolean",
"default": false
}
}
],
"requestBody": {
"content": {
"application/json": {

Loading…
Cancel
Save