diff --git a/frontend/src/Components/Page/PageConnector.js b/frontend/src/Components/Page/PageConnector.js index afd2743c5..7e6968155 100644 --- a/frontend/src/Components/Page/PageConnector.js +++ b/frontend/src/Components/Page/PageConnector.js @@ -7,7 +7,7 @@ import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions'; import { fetchCustomFilters } from 'Store/Actions/customFilterActions'; import { fetchIndexers } from 'Store/Actions/indexerActions'; import { fetchIndexerStatus } from 'Store/Actions/indexerStatusActions'; -import { fetchGeneralSettings, fetchIndexerCategories, fetchIndexerFlags, fetchLanguages, fetchUISettings } from 'Store/Actions/settingsActions'; +import { fetchGeneralSettings, fetchIndexerCategories, fetchLanguages, fetchUISettings } from 'Store/Actions/settingsActions'; import { fetchStatus } from 'Store/Actions/systemActions'; import { fetchTags } from 'Store/Actions/tagActions'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; @@ -52,7 +52,6 @@ const selectIsPopulated = createSelector( (state) => state.indexers.isPopulated, (state) => state.indexerStatus.isPopulated, (state) => state.settings.indexerCategories.isPopulated, - (state) => state.settings.indexerFlags.isPopulated, (state) => state.system.status.isPopulated, ( customFiltersIsPopulated, @@ -63,7 +62,6 @@ const selectIsPopulated = createSelector( indexersIsPopulated, indexerStatusIsPopulated, indexerCategoriesIsPopulated, - indexerFlagsIsPopulated, systemStatusIsPopulated ) => { return ( @@ -75,7 +73,6 @@ const selectIsPopulated = createSelector( indexersIsPopulated && indexerStatusIsPopulated && indexerCategoriesIsPopulated && - indexerFlagsIsPopulated && systemStatusIsPopulated ); } @@ -90,7 +87,6 @@ const selectErrors = createSelector( (state) => state.indexers.error, (state) => state.indexerStatus.error, (state) => state.settings.indexerCategories.error, - (state) => state.settings.indexerFlags.error, (state) => state.system.status.error, ( customFiltersError, @@ -101,7 +97,6 @@ const selectErrors = createSelector( indexersError, indexerStatusError, indexerCategoriesError, - indexerFlagsError, systemStatusError ) => { const hasError = !!( @@ -113,7 +108,6 @@ const selectErrors = createSelector( indexersError || indexerStatusError || indexerCategoriesError || - indexerFlagsError || systemStatusError ); @@ -127,7 +121,6 @@ const selectErrors = createSelector( indexersError, indexerStatusError, indexerCategoriesError, - indexerFlagsError, systemStatusError }; } @@ -178,9 +171,6 @@ function createMapDispatchToProps(dispatch, props) { dispatchFetchIndexerCategories() { dispatch(fetchIndexerCategories()); }, - dispatchFetchIndexerFlags() { - dispatch(fetchIndexerFlags()); - }, dispatchFetchUISettings() { dispatch(fetchUISettings()); }, @@ -220,7 +210,6 @@ class PageConnector extends Component { this.props.dispatchFetchIndexers(); this.props.dispatchFetchIndexerStatus(); this.props.dispatchFetchIndexerCategories(); - this.props.dispatchFetchIndexerFlags(); this.props.dispatchFetchUISettings(); this.props.dispatchFetchGeneralSettings(); this.props.dispatchFetchStatus(); @@ -246,7 +235,6 @@ class PageConnector extends Component { dispatchFetchIndexers, dispatchFetchIndexerStatus, dispatchFetchIndexerCategories, - dispatchFetchIndexerFlags, dispatchFetchUISettings, dispatchFetchGeneralSettings, dispatchFetchStatus, @@ -287,7 +275,6 @@ PageConnector.propTypes = { dispatchFetchIndexers: PropTypes.func.isRequired, dispatchFetchIndexerStatus: PropTypes.func.isRequired, dispatchFetchIndexerCategories: PropTypes.func.isRequired, - dispatchFetchIndexerFlags: PropTypes.func.isRequired, dispatchFetchUISettings: PropTypes.func.isRequired, dispatchFetchGeneralSettings: PropTypes.func.isRequired, dispatchFetchStatus: PropTypes.func.isRequired, diff --git a/frontend/src/Search/Table/SearchIndexRow.js b/frontend/src/Search/Table/SearchIndexRow.js index b1971c2d2..9925e45cd 100644 --- a/frontend/src/Search/Table/SearchIndexRow.js +++ b/frontend/src/Search/Table/SearchIndexRow.js @@ -10,6 +10,7 @@ import { icons, kinds, tooltipPositions } from 'Helpers/Props'; import formatDateTime from 'Utilities/Date/formatDateTime'; import formatAge from 'Utilities/Number/formatAge'; import formatBytes from 'Utilities/Number/formatBytes'; +import titleCase from 'Utilities/String/titleCase'; import translate from 'Utilities/String/translate'; import CategoryLabel from './CategoryLabel'; import Peers from './Peers'; @@ -249,7 +250,7 @@ class SearchIndexRow extends Component { indexerFlags.map((flag, index) => { return (
  • - {flag} + {titleCase(flag)}
  • ); }) diff --git a/frontend/src/Store/Actions/Settings/indexerFlags.js b/frontend/src/Store/Actions/Settings/indexerFlags.js deleted file mode 100644 index a53fe1c61..000000000 --- a/frontend/src/Store/Actions/Settings/indexerFlags.js +++ /dev/null @@ -1,48 +0,0 @@ -import createFetchHandler from 'Store/Actions/Creators/createFetchHandler'; -import { createThunk } from 'Store/thunks'; - -// -// Variables - -const section = 'settings.indexerFlags'; - -// -// Actions Types - -export const FETCH_INDEXER_FLAGS = 'settings/indexerFlags/fetchIndexerFlags'; - -// -// Action Creators - -export const fetchIndexerFlags = createThunk(FETCH_INDEXER_FLAGS); - -// -// Details - -export default { - - // - // State - - defaultState: { - isFetching: false, - isPopulated: false, - error: null, - items: [] - }, - - // - // Action Handlers - - actionHandlers: { - [FETCH_INDEXER_FLAGS]: createFetchHandler(section, '/indexerFlag') - }, - - // - // Reducers - - reducers: { - - } - -}; diff --git a/frontend/src/Store/Actions/settingsActions.js b/frontend/src/Store/Actions/settingsActions.js index 662456e2d..1f033ff60 100644 --- a/frontend/src/Store/Actions/settingsActions.js +++ b/frontend/src/Store/Actions/settingsActions.js @@ -6,7 +6,6 @@ import development from './Settings/development'; import downloadClients from './Settings/downloadClients'; import general from './Settings/general'; import indexerCategories from './Settings/indexerCategories'; -import indexerFlags from './Settings/indexerFlags'; import languages from './Settings/languages'; import notifications from './Settings/notifications'; import ui from './Settings/ui'; @@ -14,7 +13,6 @@ import ui from './Settings/ui'; export * from './Settings/downloadClients'; export * from './Settings/general'; export * from './Settings/indexerCategories'; -export * from './Settings/indexerFlags'; export * from './Settings/languages'; export * from './Settings/notifications'; export * from './Settings/applications'; @@ -35,7 +33,6 @@ export const defaultState = { downloadClients: downloadClients.defaultState, general: general.defaultState, indexerCategories: indexerCategories.defaultState, - indexerFlags: indexerFlags.defaultState, languages: languages.defaultState, notifications: notifications.defaultState, applications: applications.defaultState, @@ -64,7 +61,6 @@ export const actionHandlers = handleThunks({ ...downloadClients.actionHandlers, ...general.actionHandlers, ...indexerCategories.actionHandlers, - ...indexerFlags.actionHandlers, ...languages.actionHandlers, ...notifications.actionHandlers, ...applications.actionHandlers, @@ -84,7 +80,6 @@ export const reducers = createHandleActions({ ...downloadClients.reducers, ...general.reducers, ...indexerCategories.reducers, - ...indexerFlags.reducers, ...languages.reducers, ...notifications.reducers, ...applications.reducers, diff --git a/src/NzbDrone.Core.Test/IndexerTests/PTPTests/PTPFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/PTPTests/PTPFixture.cs index ef44ed04d..3a04ebee6 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/PTPTests/PTPFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/PTPTests/PTPFixture.cs @@ -64,7 +64,7 @@ namespace NzbDrone.Core.Test.IndexerTests.PTPTests first.Peers.Should().Be(28); first.Seeders.Should().Be(26); - torrents.Any(t => t.IndexerFlags.HasFlag(IndexerFlags.G_Freeleech)).Should().Be(true); + torrents.Any(t => t.IndexerFlags.Contains(IndexerFlag.Scene)).Should().Be(true); } } } diff --git a/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs b/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs index 545ecfde4..f747f9fd3 100644 --- a/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs +++ b/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs @@ -87,6 +87,7 @@ namespace NzbDrone.Core.IndexerSearch r.Size == null ? null : new XAttribute("length", r.Size), new XAttribute("type", protocol == DownloadProtocol.Torrent ? "application/x-bittorrent" : "application/x-nzb")), r.Category == null ? null : from c in r.Category select GetNabElement("category", c.Id, protocol), + r.IndexerFlags == null ? null : from f in r.IndexerFlags select GetNabElement("tag", f.Name, protocol), GetNabElement("rageid", r.TvRageId, protocol), GetNabElement("thetvdb", r.TvdbId, protocol), GetNabElement("imdb", r.ImdbId.ToString("D7"), protocol), diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs index d10d68927..244b49cf8 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs @@ -51,7 +51,11 @@ namespace NzbDrone.Core.Indexers.FileList BookSearchParams = new List { BookSearchParam.Q - } + }, + Flags = new List + { + IndexerFlag.FreeLeech + } }; caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.MoviesSD, "Filme SD"); diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs index ab58fe849..18822b9e0 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs @@ -36,11 +36,11 @@ namespace NzbDrone.Core.Indexers.FileList { var id = result.Id; - IndexerFlags flags = 0; + var flags = new List(); if (result.FreeLeech) { - flags |= IndexerFlags.G_Freeleech; + flags.Add(IndexerFlag.FreeLeech); } var imdbId = 0; diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs index 009b0a2d0..9dd24cf6e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs @@ -55,16 +55,16 @@ namespace NzbDrone.Core.Indexers.HDBits var id = result.Id; var internalRelease = result.TypeOrigin == 1 ? true : false; - IndexerFlags flags = 0; + var flags = new List(); if (result.FreeLeech == "yes") { - flags |= IndexerFlags.G_Freeleech; + flags.Add(IndexerFlag.FreeLeech); } if (internalRelease) { - flags |= IndexerFlags.HDB_Internal; + flags.Add(IndexerFlag.Internal); } torrentInfos.Add(new HDBitsInfo() diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs index ecec80aa2..c6929ab2f 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs @@ -52,6 +52,12 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn MovieSearchParams = new List { MovieSearchParam.Q, MovieSearchParam.ImdbId + }, + Flags = new List + { + IndexerFlag.FreeLeech, + PassThePopcornFlag.Golden, + PassThePopcornFlag.Approved } }; @@ -74,7 +80,13 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn public override IParseIndexerResponse GetParser() { - return new PassThePopcornParser(BaseUrl, _logger); + return new PassThePopcornParser(BaseUrl, Capabilities, _logger); } } + + public class PassThePopcornFlag : IndexerFlag + { + public static IndexerFlag Golden => new IndexerFlag("golden", "Release follows Golden Popcorn quality rules"); + public static IndexerFlag Approved => new IndexerFlag("approved", "Release approved by PTP"); + } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs index 8931c0ded..1f575a8ed 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs @@ -13,10 +13,12 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn public class PassThePopcornParser : IParseIndexerResponse { private readonly string _baseUrl; + private readonly IndexerCapabilities _capabilities; private readonly Logger _logger; - public PassThePopcornParser(string baseUrl, Logger logger) + public PassThePopcornParser(string baseUrl, IndexerCapabilities capabilities, Logger logger) { _baseUrl = baseUrl; + _capabilities = capabilities; _logger = logger; } @@ -63,26 +65,27 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn { var id = torrent.Id; var title = torrent.ReleaseName; - IndexerFlags flags = 0; + + var flags = new List(); if (torrent.GoldenPopcorn) { - flags |= IndexerFlags.PTP_Golden; //title = $"{title} 🍿"; + flags.Add(PassThePopcornFlag.Golden); } if (torrent.Checked) { - flags |= IndexerFlags.PTP_Approved; //title = $"{title} ✔"; + flags.Add(PassThePopcornFlag.Approved); //title = $"{title} ✔"; } if (torrent.FreeleechType == "Freeleech") { - flags |= IndexerFlags.G_Freeleech; + flags.Add(IndexerFlag.FreeLeech); } if (torrent.Scene) { - flags |= IndexerFlags.G_Scene; + flags.Add(IndexerFlag.Scene); } var free = !(torrent.FreeleechType is null); diff --git a/src/NzbDrone.Core/Indexers/Definitions/Torznab/TorznabRssParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Torznab/TorznabRssParser.cs index 917c80104..62369102c 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Torznab/TorznabRssParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Torznab/TorznabRssParser.cs @@ -173,9 +173,9 @@ namespace NzbDrone.Core.Indexers.Torznab return base.GetPeers(item); } - protected IndexerFlags GetFlags(XElement item) + protected List GetFlags(XElement item) { - IndexerFlags flags = 0; + var flags = new List(); var downloadFactor = TryGetFloatTorznabAttribute(item, "downloadvolumefactor", 1); @@ -183,17 +183,17 @@ namespace NzbDrone.Core.Indexers.Torznab if (uploadFactor == 2) { - flags |= IndexerFlags.G_DoubleUpload; + flags.Add(IndexerFlag.DoubleUpload); } if (downloadFactor == 0.5) { - flags |= IndexerFlags.G_Halfleech; + flags.Add(IndexerFlag.HalfLeech); } if (downloadFactor == 0.0) { - flags |= IndexerFlags.G_Freeleech; + flags.Add(IndexerFlag.FreeLeech); } return flags; diff --git a/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs b/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs index 1aff497a0..b6a2c04dc 100644 --- a/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs +++ b/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs @@ -83,6 +83,7 @@ namespace NzbDrone.Core.Indexers public bool BookSearchAuthorAvailable => BookSearchParams.Contains(BookSearchParam.Author); public readonly IndexerCapabilitiesCategories Categories; + public List Flags; public IndexerCapabilities() { @@ -92,6 +93,7 @@ namespace NzbDrone.Core.Indexers MusicSearchParams = new List(); BookSearchParams = new List(); Categories = new IndexerCapabilitiesCategories(); + Flags = new List(); LimitsDefault = 100; LimitsMax = 100; } @@ -386,7 +388,12 @@ namespace NzbDrone.Core.Indexers from sc in c.SubCategories select new XElement("subcat", new XAttribute("id", sc.Id), - new XAttribute("name", sc.Name)))))); + new XAttribute("name", sc.Name)))), + new XElement("tags", + from c in Flags + select new XElement("tag", + new XAttribute("name", c.Name), + new XAttribute("description", c.Description))))); return xdoc; } diff --git a/src/NzbDrone.Core/Indexers/IndexerFlag.cs b/src/NzbDrone.Core/Indexers/IndexerFlag.cs new file mode 100644 index 000000000..abcf5862f --- /dev/null +++ b/src/NzbDrone.Core/Indexers/IndexerFlag.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NzbDrone.Core.Indexers +{ + public class IndexerFlag : IEquatable + { + public string Name { get; set; } + public string Description { get; set; } + + public IndexerFlag() + { + } + + public IndexerFlag(string name, string description) + { + Name = name; + Description = description; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public bool Equals(IndexerFlag other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return Name.Equals(other.Name); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + return Equals(obj as IndexerFlag); + } + + public static bool operator ==(IndexerFlag left, IndexerFlag right) + { + return Equals(left, right); + } + + public static bool operator !=(IndexerFlag left, IndexerFlag right) + { + return !Equals(left, right); + } + + public static IndexerFlag Internal => new IndexerFlag("internal", "Uploader is an internal release group"); + public static IndexerFlag FreeLeech => new IndexerFlag("freeleech", "Release doesn't count torward ratio"); + public static IndexerFlag HalfLeech => new IndexerFlag("halfleech", "Release counts 50% to ratio"); + public static IndexerFlag Scene => new IndexerFlag("scene", "Uploader follows scene rules"); + public static IndexerFlag DoubleUpload => new IndexerFlag("doubleupload", "Seeding counts double for release"); + } +} diff --git a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs index d7d8106e9..8878cfab8 100644 --- a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.Parser.Model public string Resolution { get; set; } public ICollection Category { get; set; } - public IndexerFlags IndexerFlags { get; set; } + public ICollection IndexerFlags { get; set; } public int Age { @@ -134,20 +134,4 @@ namespace NzbDrone.Core.Parser.Model public static long BytesFromKB(float kb) => (long)(kb * 1024f); } - - [Flags] - public enum IndexerFlags - { - G_Freeleech = 1, //General - G_Halfleech = 2, //General, only 1/2 of download counted - G_DoubleUpload = 4, //General - PTP_Golden = 8, //PTP - PTP_Approved = 16, //PTP - HDB_Internal = 32, //HDBits, internal - AHD_Internal = 64, // AHD, internal - G_Scene = 128, //General, the torrent comes from the "scene" - G_Freeleech75 = 256, //Currently only used for AHD, signifies a torrent counts towards 75 percent of your download quota. - G_Freeleech25 = 512, //Currently only used for AHD, signifies a torrent counts towards 25 percent of your download quota. - AHD_UserRelease = 1024 // AHD, internal - } } diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerFlagController.cs b/src/Prowlarr.Api.V1/Indexers/IndexerFlagController.cs deleted file mode 100644 index 9121aa63d..000000000 --- a/src/Prowlarr.Api.V1/Indexers/IndexerFlagController.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Mvc; -using NzbDrone.Core.Parser.Model; -using Prowlarr.Http; - -namespace Prowlarr.Api.V1.Indexers -{ - [V1ApiController] - public class IndexerFlagController : Controller - { - [HttpGet] - public List GetAll() - { - return Enum.GetValues(typeof(IndexerFlags)).Cast().Select(f => new IndexerFlagResource - { - Id = (int)f, - Name = f.ToString() - }).ToList(); - } - } -} diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerFlagResource.cs b/src/Prowlarr.Api.V1/Indexers/IndexerFlagResource.cs deleted file mode 100644 index 89fe4037a..000000000 --- a/src/Prowlarr.Api.V1/Indexers/IndexerFlagResource.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Newtonsoft.Json; -using Prowlarr.Http.REST; - -namespace Prowlarr.Api.V1.Indexers -{ - public class IndexerFlagResource : RestResource - { - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)] - public new int Id { get; set; } - public string Name { get; set; } - public string NameLower => Name.ToLowerInvariant(); - } -} diff --git a/src/Prowlarr.Api.V1/Search/SearchResource.cs b/src/Prowlarr.Api.V1/Search/SearchResource.cs index 0d50a78c6..e75c806aa 100644 --- a/src/Prowlarr.Api.V1/Search/SearchResource.cs +++ b/src/Prowlarr.Api.V1/Search/SearchResource.cs @@ -43,7 +43,7 @@ namespace Prowlarr.Api.V1.Search { var releaseInfo = model; var torrentInfo = (model as TorrentInfo) ?? new TorrentInfo(); - var indexerFlags = torrentInfo.IndexerFlags.ToString().Split(new string[] { ", " }, StringSplitOptions.None).Where(x => x != "0"); + var indexerFlags = torrentInfo.IndexerFlags.Select(f => f.Name); // TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead? (Got a huge Deja Vu, didn't we talk about this already once?) return new SearchResource