diff --git a/src/NzbDrone.Core.Test/IndexerTests/TestIndexerSettings.cs b/src/NzbDrone.Core.Test/IndexerTests/TestIndexerSettings.cs index 0ee1716fa..948867108 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/TestIndexerSettings.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/TestIndexerSettings.cs @@ -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 MultiLanguages { get; set; } } } diff --git a/src/NzbDrone.Core/Indexers/BroadcastheNet/BroadcastheNetSettings.cs b/src/NzbDrone.Core/Indexers/BroadcastheNet/BroadcastheNetSettings.cs index 7f4491a2b..e424a46f8 100644 --- a/src/NzbDrone.Core/Indexers/BroadcastheNet/BroadcastheNetSettings.cs +++ b/src/NzbDrone.Core/Indexers/BroadcastheNet/BroadcastheNetSettings.cs @@ -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(); } [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 MultiLanguages { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/Fanzub/FanzubSettings.cs b/src/NzbDrone.Core/Indexers/Fanzub/FanzubSettings.cs index 51ff19fa1..57abc672e 100644 --- a/src/NzbDrone.Core/Indexers/Fanzub/FanzubSettings.cs +++ b/src/NzbDrone.Core/Indexers/Fanzub/FanzubSettings.cs @@ -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(); } [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 MultiLanguages { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/FileList/FileListSettings.cs b/src/NzbDrone.Core/Indexers/FileList/FileListSettings.cs index 2ef02c7de..13846a25f 100644 --- a/src/NzbDrone.Core/Indexers/FileList/FileListSettings.cs +++ b/src/NzbDrone.Core/Indexers/FileList/FileListSettings.cs @@ -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(); + MultiLanguages = Array.Empty(); } [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 MultiLanguages { get; set; } + [FieldDefinition(3, Label = "IndexerSettingsApiUrl", Advanced = true, HelpText = "IndexerSettingsApiUrlHelpText")] public string BaseUrl { get; set; } diff --git a/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs b/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs index b5833789f..7d90cfa40 100644 --- a/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs +++ b/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs @@ -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(); Mediums = Array.Empty(); + MultiLanguages = Array.Empty(); } [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 MultiLanguages { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/IIndexerSettings.cs b/src/NzbDrone.Core/Indexers/IIndexerSettings.cs index 87e7f03d2..5491b7c52 100644 --- a/src/NzbDrone.Core/Indexers/IIndexerSettings.cs +++ b/src/NzbDrone.Core/Indexers/IIndexerSettings.cs @@ -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 MultiLanguages { get; set; } } } diff --git a/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs b/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs index 5c1271459..841c98ebf 100644 --- a/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs +++ b/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs @@ -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(); } [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 MultiLanguages { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/IndexerBase.cs b/src/NzbDrone.Core/Indexers/IndexerBase.cs index dbb9916c0..4696bea3c 100644 --- a/src/NzbDrone.Core/Indexers/IndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/IndexerBase.cs @@ -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 : IIndexer where TSettings : IIndexerSettings, new() { + private static readonly Regex MultiRegex = new (@"[_. ](?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 CleanupReleases(IEnumerable 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; diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs index a38229560..b329140ea 100644 --- a/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs +++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs @@ -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(); + MultiLanguages = Array.Empty(); } [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 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() diff --git a/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs b/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs index 6983b2d67..516c34604 100644 --- a/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs +++ b/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs @@ -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(); } [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 MultiLanguages { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs b/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs index 898442bf7..5b3d4f3ef 100644 --- a/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs +++ b/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs @@ -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(); } [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 MultiLanguages { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/Torrentleech/TorrentleechSettings.cs b/src/NzbDrone.Core/Indexers/Torrentleech/TorrentleechSettings.cs index d999d84ba..47713230d 100644 --- a/src/NzbDrone.Core/Indexers/Torrentleech/TorrentleechSettings.cs +++ b/src/NzbDrone.Core/Indexers/Torrentleech/TorrentleechSettings.cs @@ -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(); } [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 MultiLanguages { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/Torznab/TorznabSettings.cs b/src/NzbDrone.Core/Indexers/Torznab/TorznabSettings.cs index 6ed534bb2..6a84b59cd 100644 --- a/src/NzbDrone.Core/Indexers/Torznab/TorznabSettings.cs +++ b/src/NzbDrone.Core/Indexers/Torznab/TorznabSettings.cs @@ -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() diff --git a/src/NzbDrone.Core/Languages/RealLanguageFieldConverter.cs b/src/NzbDrone.Core/Languages/RealLanguageFieldConverter.cs new file mode 100644 index 000000000..daca95472 --- /dev/null +++ b/src/NzbDrone.Core/Languages/RealLanguageFieldConverter.cs @@ -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 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 }); + } + } +} diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index f6b312928..78b8557e7 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -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",