mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-03-05 13:40:08 -05:00
Compare commits
24 Commits
v1.6.1.356
...
v1.6.3.360
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f20319fff1 | ||
|
|
20bcc00662 | ||
|
|
c4af3e746f | ||
|
|
660a162b7e | ||
|
|
20a3cad7fb | ||
|
|
77fe3f78fe | ||
|
|
d777cb8e29 | ||
|
|
15e7cc7ea8 | ||
|
|
04cf061275 | ||
|
|
d4cdeac69a | ||
|
|
e60fe05ee0 | ||
|
|
9a4c23797a | ||
|
|
acfdb5bae3 | ||
|
|
e2e65627ee | ||
|
|
4b8906ea62 | ||
|
|
f0c5d8ceea | ||
|
|
427802a50e | ||
|
|
0c9eae244a | ||
|
|
75ff2f41d3 | ||
|
|
d1ba208243 | ||
|
|
4e03ebadc4 | ||
|
|
0155ff60fd | ||
|
|
f0915638f3 | ||
|
|
56eb58aed1 |
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -74,7 +74,7 @@ body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Trace Logs have been provided as applicable. Reports may be closed if the required logs are not provided.
|
||||
description: Trace logs are generally required for all bug reports
|
||||
description: Trace logs are generally required for all bug reports and contain `trace`. Info logs are invalid for bug reports and do not contain `debug` nor `trace`
|
||||
options:
|
||||
- label: I have followed the steps in the wiki link above and provided the required trace logs that are relevant and show this issue.
|
||||
- label: I have read and followed the steps in the wiki link above and provided the required trace logs - the logs contain `trace` - that are relevant and show this issue.
|
||||
required: true
|
||||
|
||||
@@ -9,7 +9,7 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '1.6.1'
|
||||
majorVersion: '1.6.3'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||
@@ -1003,7 +1003,7 @@ stages:
|
||||
git add .
|
||||
if git status | grep -q modified
|
||||
then
|
||||
git commit -am 'Automated API Docs update'
|
||||
git commit -am 'Automated API Docs update [skip ci]'
|
||||
git push -f --set-upstream origin api-docs
|
||||
curl -X POST -H "Authorization: token ${GITHUBTOKEN}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/prowlarr/prowlarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
|
||||
else
|
||||
|
||||
@@ -12,7 +12,7 @@ function createMapStateToProps() {
|
||||
(state) => state.indexers,
|
||||
(value, indexers) => {
|
||||
const values = [];
|
||||
const groupedIndexers = _(indexers.items).groupBy((x) => x.protocol).map((val, key) => ({ protocol: key, indexers: val })).value();
|
||||
const groupedIndexers = _.map(_.groupBy(indexers.items, 'protocol'), (val, key) => ({ protocol: key, indexers: val }));
|
||||
|
||||
groupedIndexers.forEach((element) => {
|
||||
values.push({
|
||||
@@ -21,10 +21,11 @@ function createMapStateToProps() {
|
||||
});
|
||||
|
||||
if (element.indexers && element.indexers.length > 0) {
|
||||
element.indexers.forEach((subCat) => {
|
||||
element.indexers.forEach((indexer) => {
|
||||
values.push({
|
||||
key: subCat.id,
|
||||
value: subCat.name,
|
||||
key: indexer.id,
|
||||
value: indexer.name,
|
||||
isDisabled: !indexer.enable,
|
||||
parentKey: element.protocol === 'usenet' ? -1 : -2
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
margin-right: 12px;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.filterContainer:last-child {
|
||||
|
||||
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import EnhancedSelectInput from 'Components/Form/EnhancedSelectInput';
|
||||
import NewznabCategorySelectInputConnector from 'Components/Form/NewznabCategorySelectInputConnector';
|
||||
import TextInput from 'Components/Form/TextInput';
|
||||
import Button from 'Components/Link/Button';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
@@ -89,7 +90,8 @@ class AddIndexerModalContent extends Component {
|
||||
filter: '',
|
||||
filterProtocols: [],
|
||||
filterLanguages: [],
|
||||
filterPrivacyLevels: []
|
||||
filterPrivacyLevels: [],
|
||||
filterCategories: []
|
||||
};
|
||||
}
|
||||
|
||||
@@ -121,7 +123,13 @@ class AddIndexerModalContent extends Component {
|
||||
.map((language) => ({ key: language, value: language }));
|
||||
|
||||
const filteredIndexers = indexers.filter((indexer) => {
|
||||
const { filter, filterProtocols, filterLanguages, filterPrivacyLevels } = this.state;
|
||||
const {
|
||||
filter,
|
||||
filterProtocols,
|
||||
filterLanguages,
|
||||
filterPrivacyLevels,
|
||||
filterCategories
|
||||
} = this.state;
|
||||
|
||||
if (!indexer.name.toLowerCase().includes(filter.toLocaleLowerCase()) && !indexer.description.toLowerCase().includes(filter.toLocaleLowerCase())) {
|
||||
return false;
|
||||
@@ -139,6 +147,18 @@ class AddIndexerModalContent extends Component {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterCategories.length) {
|
||||
const { categories = [] } = indexer.capabilities || {};
|
||||
const flat = ({ id, subCategories = [] }) => [id, ...subCategories.flatMap(flat)];
|
||||
const flatCategories = categories
|
||||
.filter((item) => item.id < 100000)
|
||||
.flatMap(flat);
|
||||
|
||||
if (!filterCategories.every((item) => flatCategories.includes(item))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -165,7 +185,7 @@ class AddIndexerModalContent extends Component {
|
||||
|
||||
<div className={styles.filterRow}>
|
||||
<div className={styles.filterContainer}>
|
||||
<label className={styles.filterLabel}>Protocol</label>
|
||||
<label className={styles.filterLabel}>{translate('Protocol')}</label>
|
||||
<EnhancedSelectInput
|
||||
name="indexerProtocols"
|
||||
value={this.state.filterProtocols}
|
||||
@@ -175,7 +195,7 @@ class AddIndexerModalContent extends Component {
|
||||
</div>
|
||||
|
||||
<div className={styles.filterContainer}>
|
||||
<label className={styles.filterLabel}>Language</label>
|
||||
<label className={styles.filterLabel}>{translate('Language')}</label>
|
||||
<EnhancedSelectInput
|
||||
name="indexerLanguages"
|
||||
value={this.state.filterLanguages}
|
||||
@@ -185,7 +205,7 @@ class AddIndexerModalContent extends Component {
|
||||
</div>
|
||||
|
||||
<div className={styles.filterContainer}>
|
||||
<label className={styles.filterLabel}>Privacy</label>
|
||||
<label className={styles.filterLabel}>{translate('Privacy')}</label>
|
||||
<EnhancedSelectInput
|
||||
name="indexerPrivacyLevels"
|
||||
value={this.state.filterPrivacyLevels}
|
||||
@@ -193,6 +213,15 @@ class AddIndexerModalContent extends Component {
|
||||
onChange={({ value }) => this.setState({ filterPrivacyLevels: value })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.filterContainer}>
|
||||
<label className={styles.filterLabel}>{translate('Categories')}</label>
|
||||
<NewznabCategorySelectInputConnector
|
||||
name="indexerCategories"
|
||||
value={this.state.filterCategories}
|
||||
onChange={({ value }) => this.setState({ filterCategories: value })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Alert
|
||||
@@ -212,7 +241,7 @@ class AddIndexerModalContent extends Component {
|
||||
isFetching ? <LoadingIndicator /> : null
|
||||
}
|
||||
{
|
||||
error ? <div>{errorMessage}</div> : null
|
||||
error ? <Alert kind={kinds.DANGER}>{errorMessage}</Alert> : null
|
||||
}
|
||||
{
|
||||
isPopulated && !!indexers.length ?
|
||||
@@ -237,6 +266,15 @@ class AddIndexerModalContent extends Component {
|
||||
</Table> :
|
||||
null
|
||||
}
|
||||
{
|
||||
isPopulated && !!indexers.length && !filteredIndexers.length ?
|
||||
<Alert
|
||||
kind={kinds.WARNING}
|
||||
>
|
||||
{translate('NoIndexersFound')}
|
||||
</Alert> :
|
||||
null
|
||||
}
|
||||
</Scroller>
|
||||
</ModalBody>
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ function IndexerStatusCell(props: IndexerStatusCellProps) {
|
||||
className={styles.statusIcon}
|
||||
kind={enabled ? enableKind : kinds.DEFAULT}
|
||||
name={enabled ? enableIcon : icons.BLOCKLIST}
|
||||
title={enabled ? enableTitle : translate('EnabledIndexerIsDisabled')}
|
||||
title={enabled ? enableTitle : translate('Disabled')}
|
||||
/>
|
||||
}
|
||||
{status ? (
|
||||
|
||||
@@ -13,6 +13,10 @@ import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import TagListConnector from 'Components/TagListConnector';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import DeleteIndexerModal from 'Indexer/Delete/DeleteIndexerModal';
|
||||
@@ -149,6 +153,7 @@ function IndexerInfoModalContent(props: IndexerInfoModalContentProps) {
|
||||
</DescriptionList>
|
||||
</div>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet legend={translate('SearchCapabilities')}>
|
||||
<div>
|
||||
<DescriptionList>
|
||||
@@ -237,6 +242,54 @@ function IndexerInfoModalContent(props: IndexerInfoModalContentProps) {
|
||||
</DescriptionList>
|
||||
</div>
|
||||
</FieldSet>
|
||||
|
||||
{capabilities.categories !== null &&
|
||||
capabilities.categories.length > 0 ? (
|
||||
<FieldSet legend={translate('IndexerCategories')}>
|
||||
<Table
|
||||
columns={[
|
||||
{
|
||||
name: 'id',
|
||||
label: translate('Id'),
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: translate('Name'),
|
||||
isVisible: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{capabilities.categories
|
||||
.sort((a, b) => a.id - b.id)
|
||||
.map((category) => {
|
||||
return (
|
||||
<TableBody key={category.id}>
|
||||
<TableRow key={category.id}>
|
||||
<TableRowCell>{category.id}</TableRowCell>
|
||||
<TableRowCell>{category.name}</TableRowCell>
|
||||
</TableRow>
|
||||
{category.subCategories !== null &&
|
||||
category.subCategories.length > 0
|
||||
? category.subCategories
|
||||
.sort((a, b) => a.id - b.id)
|
||||
.map((subCategory) => {
|
||||
return (
|
||||
<TableRow key={subCategory.id}>
|
||||
<TableRowCell>{subCategory.id}</TableRowCell>
|
||||
<TableRowCell>
|
||||
{subCategory.name}
|
||||
</TableRowCell>
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</TableBody>
|
||||
);
|
||||
})}
|
||||
</Table>
|
||||
</FieldSet>
|
||||
) : null}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
|
||||
@@ -12,6 +12,8 @@ import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import { align, icons, kinds, sortDirections } from 'Helpers/Props';
|
||||
import AddIndexerModal from 'Indexer/Add/AddIndexerModal';
|
||||
import EditIndexerModalConnector from 'Indexer/Edit/EditIndexerModalConnector';
|
||||
import NoIndexer from 'Indexer/NoIndexer';
|
||||
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||
@@ -54,7 +56,9 @@ class SearchIndex extends Component {
|
||||
lastToggled: null,
|
||||
allSelected: false,
|
||||
allUnselected: false,
|
||||
selectedState: {}
|
||||
selectedState: {},
|
||||
isAddIndexerModalOpen: false,
|
||||
isEditIndexerModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -181,6 +185,22 @@ class SearchIndex extends Component {
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onAddIndexerPress = () => {
|
||||
this.setState({ isAddIndexerModalOpen: true });
|
||||
};
|
||||
|
||||
onAddIndexerModalClose = () => {
|
||||
this.setState({ isAddIndexerModalOpen: false });
|
||||
};
|
||||
|
||||
onAddIndexerSelectIndexer = () => {
|
||||
this.setState({ isEditIndexerModalOpen: true });
|
||||
};
|
||||
|
||||
onEditIndexerModalClose = () => {
|
||||
this.setState({ isEditIndexerModalOpen: false });
|
||||
};
|
||||
|
||||
onJumpBarItemPress = (jumpToCharacter) => {
|
||||
this.setState({ jumpToCharacter });
|
||||
};
|
||||
@@ -252,7 +272,9 @@ class SearchIndex extends Component {
|
||||
jumpToCharacter,
|
||||
selectedState,
|
||||
allSelected,
|
||||
allUnselected
|
||||
allUnselected,
|
||||
isAddIndexerModalOpen,
|
||||
isEditIndexerModalOpen
|
||||
} = this.state;
|
||||
|
||||
const selectedIndexerIds = this.getSelectedIds();
|
||||
@@ -348,6 +370,17 @@ class SearchIndex extends Component {
|
||||
!error && !isFetching && hasIndexers && !items.length &&
|
||||
<NoSearchResults totalItems={totalItems} />
|
||||
}
|
||||
|
||||
<AddIndexerModal
|
||||
isOpen={isAddIndexerModalOpen}
|
||||
onModalClose={this.onAddIndexerModalClose}
|
||||
onSelectIndexer={this.onAddIndexerSelectIndexer}
|
||||
/>
|
||||
|
||||
<EditIndexerModalConnector
|
||||
isOpen={isEditIndexerModalOpen}
|
||||
onModalClose={this.onEditIndexerModalClose}
|
||||
/>
|
||||
</PageContentBody>
|
||||
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ export const defaultState = {
|
||||
columns: [
|
||||
{
|
||||
name: 'status',
|
||||
columnLabel: translate('ReleaseStatus'),
|
||||
columnLabel: translate('IndexerStatus'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
isModifiable: false
|
||||
|
||||
@@ -162,7 +162,7 @@ module.exports = {
|
||||
inputHoverBackgroundColor: 'rgba(255, 255, 255, 0.20)',
|
||||
inputSelectedBackgroundColor: 'rgba(255, 255, 255, 0.05)',
|
||||
advancedFormLabelColor: '#ff902b',
|
||||
disabledCheckInputColor: '#ddd',
|
||||
disabledCheckInputColor: '#999',
|
||||
disabledInputColor: '#808080',
|
||||
|
||||
//
|
||||
|
||||
@@ -63,6 +63,8 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
public bool HasHttpError => (int)StatusCode >= 400;
|
||||
|
||||
public bool HasHttpServerError => (int)StatusCode >= 500;
|
||||
|
||||
public bool HasHttpRedirect => StatusCode == HttpStatusCode.Moved ||
|
||||
StatusCode == HttpStatusCode.Found ||
|
||||
StatusCode == HttpStatusCode.SeeOther ||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class apprise_server_urlFixture : MigrationTest<apprise_server_url>
|
||||
{
|
||||
[Test]
|
||||
public void should_rename_server_url_setting_for_apprise()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Notifications").Row(new
|
||||
{
|
||||
Name = "Apprise",
|
||||
Implementation = "Apprise",
|
||||
Settings = new
|
||||
{
|
||||
BaseUrl = "http://localhost:8000",
|
||||
NotificationType = 0
|
||||
}.ToJson(),
|
||||
ConfigContract = "AppriseSettings",
|
||||
OnHealthIssue = true,
|
||||
IncludeHealthWarnings = true,
|
||||
OnApplicationUpdate = true,
|
||||
OnGrab = true,
|
||||
IncludeManualGrabs = true
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<NotificationDefinition31>("SELECT * FROM \"Notifications\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().Settings.Should().NotContainKey("baseUrl");
|
||||
items.First().Settings.Should().ContainKey("serverUrl");
|
||||
items.First().Settings.GetValueOrDefault("serverUrl").Should().Be("http://localhost:8000");
|
||||
}
|
||||
}
|
||||
|
||||
public class NotificationDefinition31
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int Priority { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Implementation { get; set; }
|
||||
public Dictionary<string, string> Settings { get; set; }
|
||||
public string ConfigContract { get; set; }
|
||||
public bool OnHealthIssue { get; set; }
|
||||
public bool IncludeHealthWarnings { get; set; }
|
||||
public bool OnApplicationUpdate { get; set; }
|
||||
public bool OnGrab { get; set; }
|
||||
public bool IncludeManualGrabs { get; set; }
|
||||
public List<int> Tags { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -183,12 +183,12 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _sonarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "animeCategories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.seasonPackSeedTime" };
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "animeCategories", "animeStandardFormatSearch", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.seasonPackSeedTime" };
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
// Ensuring backward compatibility with older versions on first sync
|
||||
syncFields.AddRange(new List<string> { "animeStandardFormatSearch", "additionalParameters" });
|
||||
syncFields.AddRange(new List<string> { "additionalParameters" });
|
||||
}
|
||||
|
||||
var newznab = schemas.First(i => i.Implementation == "Newznab");
|
||||
@@ -218,6 +218,11 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "animeCategories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()));
|
||||
|
||||
if (sonarrIndexer.Fields.Any(x => x.Name == "animeStandardFormatSearch"))
|
||||
{
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "animeStandardFormatSearch").Value = Settings.SyncAnimeStandardFormatSearch;
|
||||
}
|
||||
|
||||
if (indexer.Protocol == DownloadProtocol.Torrent)
|
||||
{
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.AppMinimumSeeders ?? indexer.AppProfile.Value.MinimumSeeders;
|
||||
|
||||
@@ -38,6 +38,10 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiPathCompare = apiPath.Equals(otherApiPath);
|
||||
|
||||
var animeStandardFormatSearch = Fields.FirstOrDefault(x => x.Name == "animeStandardFormatSearch")?.Value == null ? null : (bool?)Convert.ToBoolean(Fields.FirstOrDefault(x => x.Name == "animeStandardFormatSearch").Value);
|
||||
var otherAnimeStandardFormatSearch = other.Fields.FirstOrDefault(x => x.Name == "animeStandardFormatSearch")?.Value == null ? null : (bool?)Convert.ToBoolean(other.Fields.FirstOrDefault(x => x.Name == "animeStandardFormatSearch").Value);
|
||||
var animeStandardFormatSearchCompare = animeStandardFormatSearch == otherAnimeStandardFormatSearch;
|
||||
|
||||
var minimumSeeders = Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var otherMinimumSeeders = other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var minimumSeedersCompare = minimumSeeders == otherMinimumSeeders;
|
||||
@@ -61,7 +65,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKey && apiPathCompare && baseUrl && cats && animeCats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && seasonSeedTimeCompare;
|
||||
apiKey && apiPathCompare && baseUrl && cats && animeCats && animeStandardFormatSearchCompare && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && seasonSeedTimeCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
[FieldDefinition(4, Label = "Anime Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
public IEnumerable<int> AnimeSyncCategories { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Sync Anime Standard Format Search", Type = FieldType.Checkbox, Advanced = true, HelpText = "Sync also searching for anime using the standard numbering")]
|
||||
public bool SyncAnimeStandardFormatSearch { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
using FluentMigrator;
|
||||
@@ -17,33 +18,43 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||
|
||||
private void MigrateToServerUrl(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
using var selectCommand = conn.CreateCommand();
|
||||
selectCommand.Transaction = tran;
|
||||
selectCommand.CommandText = "SELECT \"Id\", \"Settings\" FROM \"Notifications\" WHERE \"Implementation\" = 'Apprise'";
|
||||
var updatedNotifications = new List<object>();
|
||||
|
||||
using var reader = selectCommand.ExecuteReader();
|
||||
|
||||
while (reader.Read())
|
||||
using (var selectCommand = conn.CreateCommand())
|
||||
{
|
||||
var id = reader.GetInt32(0);
|
||||
var settings = reader.GetString(1);
|
||||
selectCommand.Transaction = tran;
|
||||
selectCommand.CommandText = "SELECT \"Id\", \"Settings\" FROM \"Notifications\" WHERE \"Implementation\" = 'Apprise'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(settings))
|
||||
using var reader = selectCommand.ExecuteReader();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
var jsonObject = Json.Deserialize<JObject>(settings);
|
||||
var id = reader.GetInt32(0);
|
||||
var settings = reader.GetString(1);
|
||||
|
||||
if (jsonObject.ContainsKey("baseUrl"))
|
||||
if (!string.IsNullOrWhiteSpace(settings))
|
||||
{
|
||||
jsonObject.Add("serverUrl", jsonObject.Value<string>("baseUrl"));
|
||||
jsonObject.Remove("baseUrl");
|
||||
var jsonObject = Json.Deserialize<JObject>(settings);
|
||||
|
||||
if (jsonObject.ContainsKey("baseUrl"))
|
||||
{
|
||||
jsonObject.Add("serverUrl", jsonObject.Value<string>("baseUrl"));
|
||||
jsonObject.Remove("baseUrl");
|
||||
}
|
||||
|
||||
settings = jsonObject.ToJson();
|
||||
}
|
||||
|
||||
settings = jsonObject.ToJson();
|
||||
updatedNotifications.Add(new
|
||||
{
|
||||
Id = id,
|
||||
Settings = settings
|
||||
});
|
||||
}
|
||||
|
||||
var parameters = new { Settings = settings, Id = id };
|
||||
conn.Execute("UPDATE Notifications SET Settings = @Settings WHERE Id = @Id", parameters, transaction: tran);
|
||||
}
|
||||
|
||||
var updateNotificationsSql = "UPDATE \"Notifications\" SET \"Settings\" = @Settings WHERE \"Id\" = @Id";
|
||||
conn.Execute(updateNotificationsSql, updatedNotifications, transaction: tran);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
public class NewznabRequest
|
||||
{
|
||||
private static readonly Regex TvRegex = new Regex(@"\{((?:imdbid\:)(?<imdbid>[^{]+)|(?:rid\:)(?<rid>[^{]+)|(?:tvdbid\:)(?<tvdbid>[^{]+)|(?:tmdbid\:)(?<tmdbid>[^{]+)|(?:doubanid\:)(?<doubanid>[^{]+)|(?:season\:)(?<season>[^{]+)|(?:episode\:)(?<episode>[^{]+)|(?:year\:)(?<year>[^{]+)|(?:genre\:)(?<genre>[^{]+))\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex MovieRegex = new Regex(@"\{((?:imdbid\:)(?<imdbid>[^{]+)|(?:doubanid\:)(?<doubanid>[^{]+)|(?:tmdbid\:)(?<tmdbid>[^{]+)|(?:traktid\:)(?<traktid>[^{]+)|(?:year\:)(?<year>[^{]+)|(?:genre\:)(?<genre>[^{]+))\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex MusicRegex = new Regex(@"\{((?:artist\:)(?<artist>[^{]+)|(?:album\:)(?<album>[^{]+)|(?:track\:)(?<track>[^{]+)|(?:label\:)(?<label>[^{]+)|(?:year\:)(?<year>[^{]+)|(?:genre\:)(?<genre>[^{]+))\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex BookRegex = new Regex(@"\{((?:author\:)(?<author>[^{]+)|(?:publisher\:)(?<publisher>[^{]+)|(?:title\:)(?<title>[^{]+)|(?:year\:)(?<year>[^{]+)|(?:genre\:)(?<genre>[^{]+))\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex TvRegex = new (@"\{((?:imdbid\:)(?<imdbid>[^{]+)|(?:rid\:)(?<rid>[^{]+)|(?:tvdbid\:)(?<tvdbid>[^{]+)|(?:tmdbid\:)(?<tmdbid>[^{]+)|(?:doubanid\:)(?<doubanid>[^{]+)|(?:season\:)(?<season>[^{]+)|(?:episode\:)(?<episode>[^{]+)|(?:year\:)(?<year>[^{]+)|(?:genre\:)(?<genre>[^{]+))\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex MovieRegex = new (@"\{((?:imdbid\:)(?<imdbid>[^{]+)|(?:doubanid\:)(?<doubanid>[^{]+)|(?:tmdbid\:)(?<tmdbid>[^{]+)|(?:traktid\:)(?<traktid>[^{]+)|(?:year\:)(?<year>[^{]+)|(?:genre\:)(?<genre>[^{]+))\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex MusicRegex = new (@"\{((?:artist\:)(?<artist>[^{]+)|(?:album\:)(?<album>[^{]+)|(?:track\:)(?<track>[^{]+)|(?:label\:)(?<label>[^{]+)|(?:year\:)(?<year>[^{]+)|(?:genre\:)(?<genre>[^{]+))\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex BookRegex = new (@"\{((?:author\:)(?<author>[^{]+)|(?:publisher\:)(?<publisher>[^{]+)|(?:title\:)(?<title>[^{]+)|(?:year\:)(?<year>[^{]+)|(?:genre\:)(?<genre>[^{]+))\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public string t { get; set; }
|
||||
public string q { get; set; }
|
||||
@@ -40,6 +41,11 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
|
||||
public void QueryToParams()
|
||||
{
|
||||
if (q.IsNullOrWhiteSpace())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (t == "tvsearch")
|
||||
{
|
||||
var matches = TvRegex.Matches(q);
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Events;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
@@ -157,15 +158,22 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
var indexers = _indexerFactory.Enabled();
|
||||
|
||||
if (criteriaBase.IndexerIds != null && criteriaBase.IndexerIds.Count > 0)
|
||||
if (criteriaBase.IndexerIds is { Count: > 0 })
|
||||
{
|
||||
indexers = indexers.Where(i => criteriaBase.IndexerIds.Contains(i.Definition.Id) ||
|
||||
(criteriaBase.IndexerIds.Contains(-1) && i.Protocol == DownloadProtocol.Usenet) ||
|
||||
(criteriaBase.IndexerIds.Contains(-2) && i.Protocol == DownloadProtocol.Torrent))
|
||||
.ToList();
|
||||
|
||||
if (indexers.Count == 0)
|
||||
{
|
||||
_logger.Debug("Search failed due to all selected indexers being unavailable: {0}", string.Join(", ", criteriaBase.IndexerIds));
|
||||
|
||||
throw new SearchFailedException("Search failed due to all selected indexers being unavailable");
|
||||
}
|
||||
}
|
||||
|
||||
if (criteriaBase.Categories != null && criteriaBase.Categories.Length > 0)
|
||||
if (criteriaBase.Categories is { Length: > 0 })
|
||||
{
|
||||
//Only query supported indexers
|
||||
indexers = indexers.Where(i => ((IndexerDefinition)i.Definition).Capabilities.Categories.SupportedCategories(criteriaBase.Categories).Any()).ToList();
|
||||
@@ -173,6 +181,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
if (indexers.Count == 0)
|
||||
{
|
||||
_logger.Debug("All provided categories are unsupported by selected indexers: {0}", string.Join(", ", criteriaBase.Categories));
|
||||
|
||||
return Array.Empty<ReleaseInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "Anidex is a Public torrent tracker and indexer, primarily for English fansub groups of anime";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "Anidub is RUSSIAN anime voiceover group and eponymous anime tracker.";
|
||||
public override string Language => "ru-RU";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.SemiPrivate;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "AnimeBytes (AB) is the largest private torrent tracker that specialises in anime and anime-related content.";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(4);
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Name => "AnimeTorrents";
|
||||
public override string[] IndexerUrls => new[] { "https://animetorrents.me/" };
|
||||
public override string Description => "Definitive source for anime and manga";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override bool SupportsPagination => true;
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(4);
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "Animedia is RUSSIAN anime voiceover group and eponymous anime tracker.";
|
||||
public override string Language => "ru-RU";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "A movies tracker";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ public class AudioBookBay : TorrentIndexerBase<NoAuthTorrentBaseSettings>
|
||||
};
|
||||
public override string Description => "AudioBook Bay (ABB) is a public Torrent Tracker for AUDIOBOOKS";
|
||||
public override string Language => "en-US";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override int PageSize => 15;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
public abstract class AvistazBase : TorrentIndexerBase<AvistazSettings>
|
||||
{
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override bool SupportsRss => true;
|
||||
public override bool SupportsSearch => true;
|
||||
public override bool SupportsPagination => true;
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "BB is a Private Torrent Tracker for 0DAY / GENERAL";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override string[] IndexerUrls => new[] { "https://bakabt.me/" };
|
||||
public override string Description => "Anime Community";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
@@ -413,7 +412,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
FreeleechOnly = false;
|
||||
}
|
||||
|
||||
[FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Search freeleech torrents only")]
|
||||
[FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Show freeleech torrents only")]
|
||||
public bool FreeleechOnly { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Add Romaji Title", Type = FieldType.Checkbox, HelpText = "Add releases for Romaji Title")]
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override string[] IndexerUrls => new string[] { "https://beyond-hd.me/" };
|
||||
public override string Description => "BeyondHD (BHD) is a Private Torrent Tracker for HD MOVIES / TV";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "The binary Usenet search engine";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override bool SupportsRss => false;
|
||||
public override bool SupportsPagination => true;
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "BIT-HDTV - Home of High Definition";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.GetEncoding("iso-8859-1");
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
|
||||
public override string Name => "BroadcasTheNet";
|
||||
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override bool SupportsRss => true;
|
||||
public override bool SupportsSearch => true;
|
||||
public override bool SupportsPagination => true;
|
||||
|
||||
@@ -24,8 +24,6 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
public override string Name => "Cardigann";
|
||||
public override string[] IndexerUrls => new string[] { "" };
|
||||
public override string Description => "";
|
||||
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
// Page size is different per indexer, setting to 1 ensures we don't break out of paging logic
|
||||
@@ -166,7 +164,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
SupportsSearch = SupportsSearch,
|
||||
SupportsRedirect = SupportsRedirect,
|
||||
SupportsPagination = SupportsPagination,
|
||||
Capabilities = new IndexerCapabilities(),
|
||||
Capabilities = ParseCardigannCapabilities(definition),
|
||||
ExtraFields = settings
|
||||
};
|
||||
}
|
||||
@@ -238,5 +236,55 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IndexerCapabilities ParseCardigannCapabilities(CardigannMetaDefinition definition)
|
||||
{
|
||||
var capabilities = new IndexerCapabilities();
|
||||
|
||||
if (definition.Caps == null)
|
||||
{
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
capabilities.ParseCardigannSearchModes(definition.Caps.Modes);
|
||||
capabilities.SupportsRawSearch = definition.Caps.Allowrawsearch;
|
||||
|
||||
if (definition.Caps.Categories != null && definition.Caps.Categories.Any())
|
||||
{
|
||||
foreach (var category in definition.Caps.Categories)
|
||||
{
|
||||
var cat = NewznabStandardCategory.GetCatByName(category.Value);
|
||||
|
||||
if (cat == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
capabilities.Categories.AddCategoryMapping(category.Key, cat);
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.Caps.Categorymappings != null && definition.Caps.Categorymappings.Any())
|
||||
{
|
||||
foreach (var categoryMapping in definition.Caps.Categorymappings)
|
||||
{
|
||||
IndexerCategory torznabCat = null;
|
||||
|
||||
if (categoryMapping.Cat != null)
|
||||
{
|
||||
torznabCat = NewznabStandardCategory.GetCatByName(categoryMapping.Cat);
|
||||
|
||||
if (torznabCat == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
capabilities.Categories.AddCategoryMapping(categoryMapping.Id, torznabCat, categoryMapping.Desc);
|
||||
}
|
||||
}
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
|
||||
protected virtual string SiteLink { get; private set; }
|
||||
|
||||
protected readonly IndexerCapabilitiesCategories _categories = new IndexerCapabilitiesCategories();
|
||||
protected readonly List<string> _defaultCategories = new List<string>();
|
||||
protected readonly IndexerCapabilitiesCategories _categories = new ();
|
||||
protected readonly List<string> _defaultCategories = new ();
|
||||
|
||||
protected readonly string[] OptionalFields = new string[] { "imdb", "imdbid", "tmdbid", "rageid", "tvdbid", "tvmazeid", "traktid", "doubanid", "poster", "banner", "description", "genre" };
|
||||
|
||||
@@ -65,14 +65,16 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
|
||||
SiteLink = definition.Links.First();
|
||||
|
||||
if (_definition.Caps.Categories != null)
|
||||
if (_definition.Caps.Categories != null && _definition.Caps.Categories.Any())
|
||||
{
|
||||
foreach (var category in _definition.Caps.Categories)
|
||||
{
|
||||
var cat = NewznabStandardCategory.GetCatByName(category.Value);
|
||||
|
||||
if (cat == null)
|
||||
{
|
||||
_logger.Error(string.Format("CardigannIndexer ({0}): invalid Torznab category for id {1}: {2}", _definition.Id, category.Key, category.Value));
|
||||
_logger.Error("CardigannIndexer ({0}): invalid Torznab category for id {1}: {2}", _definition.Id, category.Key, category.Value);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -80,27 +82,29 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
}
|
||||
}
|
||||
|
||||
if (_definition.Caps.Categorymappings != null)
|
||||
if (_definition.Caps.Categorymappings != null && _definition.Caps.Categorymappings.Any())
|
||||
{
|
||||
foreach (var categorymapping in _definition.Caps.Categorymappings)
|
||||
foreach (var categoryMapping in _definition.Caps.Categorymappings)
|
||||
{
|
||||
IndexerCategory torznabCat = null;
|
||||
|
||||
if (categorymapping.cat != null)
|
||||
if (categoryMapping.Cat != null)
|
||||
{
|
||||
torznabCat = NewznabStandardCategory.GetCatByName(categorymapping.cat);
|
||||
torznabCat = NewznabStandardCategory.GetCatByName(categoryMapping.Cat);
|
||||
|
||||
if (torznabCat == null)
|
||||
{
|
||||
_logger.Error(string.Format("CardigannIndexer ({0}): invalid Torznab category for id {1}: {2}", _definition.Id, categorymapping.id, categorymapping.cat));
|
||||
_logger.Error("CardigannIndexer ({0}): invalid Torznab category for id {1}: {2}", _definition.Id, categoryMapping.Id, categoryMapping.Cat);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
_categories.AddCategoryMapping(categorymapping.id, torznabCat, categorymapping.desc);
|
||||
_categories.AddCategoryMapping(categoryMapping.Id, torznabCat, categoryMapping.Desc);
|
||||
|
||||
if (categorymapping.Default)
|
||||
if (categoryMapping.Default)
|
||||
{
|
||||
_defaultCategories.Add(categorymapping.id);
|
||||
_defaultCategories.Add(categoryMapping.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -622,7 +626,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
}
|
||||
catch (InvalidDateException ex)
|
||||
{
|
||||
_logger.Debug(ex.Message);
|
||||
_logger.Debug("{0}: {1}", _definition.Id, ex.Message);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -66,9 +66,9 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
|
||||
public class CategorymappingBlock
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string cat { get; set; }
|
||||
public string desc { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Cat { get; set; }
|
||||
public string Desc { get; set; }
|
||||
public bool Default { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
public List<string> Links { get; set; }
|
||||
public List<string> Legacylinks { get; set; }
|
||||
public List<SettingsField> Settings { get; set; }
|
||||
public string Sha { get; set; }
|
||||
public LoginBlock Login { get; set; }
|
||||
public CapabilitiesBlock Caps { get; set; }
|
||||
public string Sha { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -890,11 +890,11 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
download.Infohash.Title.Selector);
|
||||
}
|
||||
}
|
||||
else if (download.Selectors != null)
|
||||
else if (download.Selectors != null && download.Selectors.Any())
|
||||
{
|
||||
foreach (var selector in download.Selectors)
|
||||
{
|
||||
var queryselector = ApplyGoTemplateText(selector.Selector, variables);
|
||||
var querySelector = ApplyGoTemplateText(selector.Selector, variables);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -904,16 +904,21 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
}
|
||||
|
||||
var href = MatchSelector(response, selector, variables, debugMatch: true);
|
||||
|
||||
if (href == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var torrentLink = ResolvePath(href, link);
|
||||
|
||||
if (torrentLink.Scheme != "magnet" && _definition.TestLinkTorrent)
|
||||
{
|
||||
// Test link
|
||||
var testLinkRequest = new HttpRequestBuilder(torrentLink.ToString())
|
||||
{
|
||||
AllowAutoRedirect = true
|
||||
}
|
||||
.SetCookies(Cookies ?? new Dictionary<string, string>())
|
||||
.SetHeaders(headers ?? new Dictionary<string, string>())
|
||||
.SetEncoding(_encoding)
|
||||
@@ -923,9 +928,10 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
response = await HttpClient.ExecuteProxiedAsync(testLinkRequest, Definition);
|
||||
|
||||
var content = response.Content;
|
||||
|
||||
if (content.Length >= 1 && content[0] != 'd')
|
||||
{
|
||||
_logger.Debug("CardigannIndexer ({0}): Download selector {1}'s torrent file is invalid, retrying with next available selector", _definition.Id, queryselector);
|
||||
_logger.Debug("{0}: Download selector {1}'s torrent file is invalid, retrying with next available selector", _definition.Id, querySelector);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -944,13 +950,13 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
|
||||
return selectorDownloadRequest;
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error("{0} CardigannIndexer ({1}): An exception occurred while trying selector {2}, retrying with next available selector", e, _definition.Id, queryselector);
|
||||
|
||||
throw new CardigannException(string.Format("An exception occurred while trying selector {0}", queryselector));
|
||||
_logger.Error(ex, "{0}: An exception occurred while trying selector {1}, retrying with next available selector.", _definition.Id, querySelector);
|
||||
}
|
||||
}
|
||||
|
||||
throw new CardigannException($"Download selectors didn't match for {link}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ public class FileList : TorrentIndexerBase<FileListSettings>
|
||||
};
|
||||
public override string[] LegacyUrls => new[] { "https://filelist.io" };
|
||||
public override string Description => "FileList (FL) is a ROMANIAN Private Torrent Tracker for 0DAY / GENERAL";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override bool SupportsRss => true;
|
||||
public override bool SupportsSearch => true;
|
||||
|
||||
@@ -26,7 +26,6 @@ public class FunFile : TorrentIndexerBase<UserPassTorrentBaseSettings>
|
||||
public override string Description => "FunFile is a general tracker";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.GetEncoding("iso-8859-1");
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace NzbDrone.Core.Indexers.Definitions.Gazelle;
|
||||
public abstract class GazelleBase<TSettings> : TorrentIndexerBase<TSettings>
|
||||
where TSettings : GazelleSettings, new()
|
||||
{
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override string[] IndexerUrls => new[] { "" };
|
||||
protected virtual string LoginUrl => Settings.BaseUrl + "login.php";
|
||||
public override bool SupportsRss => true;
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "GazelleGames (GGn) is a Private Torrent Tracker for GAMES";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
public override string[] IndexerUrls => new[] { "https://hdbits.org/" };
|
||||
public override string[] LegacyUrls => new[] { "https://hdbits.org" };
|
||||
public override string Description => "Best HD Tracker";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override bool SupportsRedirect => true;
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "HD-Space (HDS) is a Private Torrent Tracker for HD MOVIES / TV";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
"https://hd-torrents.me/",
|
||||
};
|
||||
public override string Description => "HD-Torrents is a private torrent website with HD torrents and strict rules on their content.";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ namespace NzbDrone.Core.Indexers.Headphones
|
||||
public class Headphones : UsenetIndexerBase<HeadphonesSettings>
|
||||
{
|
||||
public override string Name => "Headphones VIP";
|
||||
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override string[] IndexerUrls => new string[] { "https://indexer.codeshy.com" };
|
||||
public override string Description => "A Private Usenet indexer for music";
|
||||
|
||||
@@ -38,7 +38,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
"https://ipt.world/"
|
||||
};
|
||||
public override string Description => "IPTorrents (IPT) is a Private Torrent Tracker for 0DAY / GENERAL.";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override bool SupportsPagination => true;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Name => "ImmortalSeed";
|
||||
public override string[] IndexerUrls => new[] { "https://immortalseed.me/" };
|
||||
public override string Description => "ImmortalSeed (iS) is a Private Torrent Tracker for MOVIES / TV / GENERAL";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(5);
|
||||
|
||||
@@ -31,7 +31,6 @@ public class Libble : TorrentIndexerBase<LibbleSettings>
|
||||
private string LoginUrl => Settings.BaseUrl + "login.php";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override bool SupportsPagination => true;
|
||||
public override int PageSize => 50;
|
||||
|
||||
@@ -26,7 +26,6 @@ public class MoreThanTV : TorrentIndexerBase<CookieTorrentBaseSettings>
|
||||
public override string Name => "MoreThanTV";
|
||||
public override string[] IndexerUrls => new[] { "https://www.morethantv.me/" };
|
||||
public override string Description => "Private torrent tracker for TV / MOVIES";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override bool FollowRedirect => true;
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Name => "MyAnonamouse";
|
||||
public override string[] IndexerUrls => new[] { "https://www.myanonamouse.net/" };
|
||||
public override string Description => "MyAnonaMouse (MAM) is a large ebook and audiobook tracker.";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override bool SupportsPagination => true;
|
||||
public override int PageSize => 100;
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "Nebulance (NBL) is a ratioless Private Torrent Tracker for TV";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override bool SupportsRedirect => true;
|
||||
public override bool SupportsPagination => true;
|
||||
|
||||
@@ -23,12 +23,8 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
public override bool FollowRedirect => true;
|
||||
public override bool SupportsRedirect => true;
|
||||
public override bool SupportsPagination => true;
|
||||
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
public override IndexerCapabilities Capabilities { get => GetCapabilitiesFromSettings(); protected set => base.Capabilities = value; }
|
||||
|
||||
public override int PageSize => _capabilitiesProvider.GetCapabilities(Settings, Definition).LimitsDefault.Value;
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
|
||||
@@ -29,7 +29,6 @@ public class NorBits : TorrentIndexerBase<NorBitsSettings>
|
||||
public override string Description => "NorBits is a Norwegian Private site for MOVIES / TV / GENERAL";
|
||||
public override string Language => "nb-NO";
|
||||
public override Encoding Encoding => Encoding.GetEncoding("iso-8859-1");
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Name => "NZBIndex";
|
||||
public override string[] IndexerUrls => new[] { "https://nzbindex.com/" };
|
||||
public override string Description => "A Usenet Indexer";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.SemiPrivate;
|
||||
public override bool SupportsPagination => true;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Name => "Orpheus";
|
||||
public override string[] IndexerUrls => new[] { "https://orpheus.network/" };
|
||||
public override string Description => "Orpheus (APOLLO) is a Private Torrent Tracker for MUSIC";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override bool SupportsRedirect => true;
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
public override string Name => "PassThePopcorn";
|
||||
public override string[] IndexerUrls => new[] { "https://passthepopcorn.me" };
|
||||
public override string Description => "PassThePopcorn (PTP) is a Private site for MOVIES / TV";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override bool SupportsRss => true;
|
||||
public override bool SupportsSearch => true;
|
||||
|
||||
@@ -26,7 +26,6 @@ public class PirateTheNet : TorrentIndexerBase<UserPassTorrentBaseSettings>
|
||||
public override string[] IndexerUrls => new[] { "https://piratethenet.org/" };
|
||||
public override string[] LegacyUrls => new[] { "http://piratethenet.org/" };
|
||||
public override string Description => "PirateTheNet (PTN) is a ratioless movie tracker.";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
private string LoginUrl => Settings.BaseUrl + "takelogin.php";
|
||||
|
||||
@@ -28,7 +28,6 @@ public class PixelHD : TorrentIndexerBase<PixelHDSettings>
|
||||
public override string Description => "PixelHD (PxHD) is a ratioless Private Torrent Tracker for HD .MP4 MOVIES / TV";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "PornoLab is a Semi-Private Russian site for Adult content";
|
||||
public override string Language => "ru-RU";
|
||||
public override Encoding Encoding => Encoding.GetEncoding("windows-1251");
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.SemiPrivate;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ public class PreToMe : TorrentIndexerBase<PreToMeSettings>
|
||||
public override string Description => "PreToMe is a ratioless 0Day/General tracker.";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.GetEncoding("iso-8859-1");
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace NzbDrone.Core.Indexers.Definitions.Rarbg
|
||||
public override string[] IndexerUrls => new[] { "https://torrentapi.org/" };
|
||||
public override string[] LegacyUrls => new[] { "https://torrentapi.org" };
|
||||
public override string Description => "RARBG is a Public torrent site for MOVIES / TV / GENERAL";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(7);
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Name => "Redacted";
|
||||
public override string[] IndexerUrls => new[] { "https://redacted.ch/" };
|
||||
public override string Description => "REDActed (Aka.PassTheHeadPhones) is one of the most well-known music trackers.";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override bool SupportsRedirect => true;
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string[] IndexerUrls => new[] { "https://revolutiontt.me/" };
|
||||
public override string Description => "The Revolution has begun";
|
||||
private string LoginUrl => Settings.BaseUrl + "takelogin.php";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "RuTracker is a Semi-Private Russian torrent site with a thriving file-sharing community";
|
||||
public override string Language => "ru-RU";
|
||||
public override Encoding Encoding => Encoding.GetEncoding("windows-1251");
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.SemiPrivate;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "SceneHD is Private site for HD TV / MOVIES";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "Always on time";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ public class SecretCinema : GazelleBase<GazelleSettings>
|
||||
public override string Name => "Secret Cinema";
|
||||
public override string[] IndexerUrls => new[] { "https://secret-cinema.pw/" };
|
||||
public override string Description => "A tracker for rare movies.";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ public class Shazbat : TorrentIndexerBase<ShazbatSettings>
|
||||
public override string Description => "Shazbat is a PRIVATE Torrent Tracker with highly curated TV content";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(5.1);
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "Shizaproject is russian anime voiceover group and eponymous anime tracker.";
|
||||
public override string Language => "ru-RU";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
private string LoginUrl => Settings.BaseUrl + "api/login";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override bool SupportsPagination => true;
|
||||
public override int PageSize => 100;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
@@ -33,7 +33,6 @@ public class SpeedCD : TorrentIndexerBase<SpeedCDSettings>
|
||||
public override string Description => "Your home now!";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override bool SupportsPagination => true;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Language => "en-US";
|
||||
public override string Description => "SubsPlease - A better HorribleSubs/Erai replacement";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "TV-Vault is a very unique tracker dedicated for old TV shows, TV movies and documentaries.";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(5);
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "Toloka.to is a Semi-Private Ukrainian torrent site with a thriving file-sharing community";
|
||||
public override string Language => "uk-UA";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.SemiPrivate;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "A decade of TorrentBytes";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.GetEncoding("iso-8859-1");
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
"https://td.workisboring.net/"
|
||||
};
|
||||
public override string Description => "TorrentDay (TD) is a Private site for TV / MOVIES / GENERAL";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato
|
||||
public override string[] IndexerUrls => new[] { "http://127.0.0.1" };
|
||||
public override string Description => "A JSON based torrent provider previously developed for CouchPotato";
|
||||
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(2);
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentRss
|
||||
public override string Name => "Torrent RSS Feed";
|
||||
public override string[] IndexerUrls => new[] { "" };
|
||||
public override string Description => "Generic RSS Feed containing torrents";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override bool SupportsRss => true;
|
||||
public override bool SupportsSearch => false;
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "A German general tracker";
|
||||
public override string Language => "de-DE";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Language => "en-US";
|
||||
public override string Description => "Torrents.csv is a self-hostable open source torrent search engine and database";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override bool SupportsRss => false;
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
public override bool SupportsRedirect => true;
|
||||
public override bool SupportsPagination => true;
|
||||
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
public override int PageSize => _capabilitiesProvider.GetCapabilities(Settings, Definition).LimitsDefault.Value;
|
||||
|
||||
@@ -6,7 +6,6 @@ namespace NzbDrone.Core.Indexers.Definitions.UNIT3D
|
||||
{
|
||||
public abstract class Unit3dBase : TorrentIndexerBase<Unit3dSettings>
|
||||
{
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override string[] IndexerUrls => new string[] { "" };
|
||||
public override bool SupportsRss => true;
|
||||
public override bool SupportsSearch => true;
|
||||
|
||||
@@ -25,7 +25,6 @@ public class Uniotaku : TorrentIndexerBase<UniotakuSettings>
|
||||
public override string[] IndexerUrls => new[] { "https://tracker.uniotaku.com/" };
|
||||
public override string Description => "UniOtaku is a BRAZILIAN Semi-Private Torrent Tracker for ANIME";
|
||||
public override string Language => "pt-BR";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.SemiPrivate;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ public class XSpeeds : TorrentIndexerBase<UserPassTorrentBaseSettings>
|
||||
public override string Name => "XSpeeds";
|
||||
public override string[] IndexerUrls => new[] { "https://www.xspeeds.eu/" };
|
||||
public override string Description => "XSpeeds (XS) is a Private Torrent Tracker for MOVIES / TV / GENERAL";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
private string LandingUrl => Settings.BaseUrl + "login.php";
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace NzbDrone.Core.Indexers.Definitions.Xthor
|
||||
public override string Language => "fr-FR";
|
||||
public override string Description => "Xthor is a general Private torrent site";
|
||||
public override Encoding Encoding => Encoding.GetEncoding("windows-1252");
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(2.5);
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string Description => "ZonaQ is a SPANISH Private Torrent Tracker for MOVIES / TV";
|
||||
public override string Language => "es-ES";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
|
||||
@@ -264,6 +264,7 @@ namespace NzbDrone.Core.Indexers
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var result = new IndexerPageableQueryResult();
|
||||
var url = string.Empty;
|
||||
var minimumBackoff = TimeSpan.FromHours(1);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -317,8 +318,7 @@ namespace NzbDrone.Core.Indexers
|
||||
}
|
||||
catch (WebException webException)
|
||||
{
|
||||
if (webException.Status == WebExceptionStatus.NameResolutionFailure ||
|
||||
webException.Status == WebExceptionStatus.ConnectFailure)
|
||||
if (webException.Status is WebExceptionStatus.NameResolutionFailure or WebExceptionStatus.ConnectFailure)
|
||||
{
|
||||
_indexerStatusService.RecordConnectionFailure(Definition.Id);
|
||||
}
|
||||
@@ -341,7 +341,7 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
result.Queries.Add(new IndexerQueryResult { Response = ex.Response });
|
||||
|
||||
var retryTime = ex.RetryAfter != TimeSpan.Zero ? ex.RetryAfter : TimeSpan.FromHours(1);
|
||||
var retryTime = ex.RetryAfter != TimeSpan.Zero ? ex.RetryAfter : minimumBackoff;
|
||||
|
||||
_indexerStatusService.RecordFailure(Definition.Id, retryTime);
|
||||
_logger.Warn("Request Limit reached for {0}. Disabled for {1}", this, retryTime);
|
||||
@@ -350,13 +350,21 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
result.Queries.Add(new IndexerQueryResult { Response = ex.Response });
|
||||
_indexerStatusService.RecordFailure(Definition.Id);
|
||||
_logger.Warn("{0} {1}", this, ex.Message);
|
||||
|
||||
if (ex.Response.HasHttpServerError)
|
||||
{
|
||||
_logger.Warn("Unable to connect to {0} at [{1}]. Indexer's server is unavailable. Try again later. {2}", this, url, ex.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warn("{0} {1}", this, ex.Message);
|
||||
}
|
||||
}
|
||||
catch (RequestLimitReachedException ex)
|
||||
{
|
||||
result.Queries.Add(new IndexerQueryResult { Response = ex.Response.HttpResponse });
|
||||
_indexerStatusService.RecordFailure(Definition.Id, TimeSpan.FromHours(1));
|
||||
_logger.Warn("API Request Limit reached for {0}", this);
|
||||
_indexerStatusService.RecordFailure(Definition.Id, minimumBackoff);
|
||||
_logger.Warn("Request Limit reached for {0}. Disabled for {1}", this, minimumBackoff);
|
||||
}
|
||||
catch (IndexerAuthException ex)
|
||||
{
|
||||
@@ -494,7 +502,7 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
var response = await _httpClient.ExecuteProxiedAsync(request.HttpRequest, Definition);
|
||||
|
||||
// Check reponse to see if auth is needed, if needed try again
|
||||
// Check response to see if auth is needed, if needed try again
|
||||
if (CheckIfLoginNeeded(response))
|
||||
{
|
||||
_logger.Trace("Attempting to re-auth based on indexer search response");
|
||||
@@ -507,6 +515,11 @@ namespace NzbDrone.Core.Indexers
|
||||
response = await _httpClient.ExecuteProxiedAsync(request.HttpRequest, Definition);
|
||||
}
|
||||
|
||||
if (CloudFlareDetectionService.IsCloudflareProtected(response))
|
||||
{
|
||||
throw new CloudFlareProtectionException(response);
|
||||
}
|
||||
|
||||
// Throw common http errors here before we try to parse
|
||||
if (response.HasHttpError && (request.HttpRequest.SuppressHttpErrorStatusCodes == null || !request.HttpRequest.SuppressHttpErrorStatusCodes.Contains(response.StatusCode)))
|
||||
{
|
||||
@@ -519,11 +532,11 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
throw new TooManyRequestsException(request.HttpRequest, response);
|
||||
}
|
||||
}
|
||||
|
||||
if (CloudFlareDetectionService.IsCloudflareProtected(response))
|
||||
{
|
||||
throw new CloudFlareProtectionException(response);
|
||||
if (response.HasHttpServerError)
|
||||
{
|
||||
throw new HttpException(request.HttpRequest, response);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateCookies(request.HttpRequest.Cookies, DateTime.Now.AddDays(30));
|
||||
@@ -594,9 +607,9 @@ namespace NzbDrone.Core.Indexers
|
||||
}
|
||||
catch (IndexerAuthException ex)
|
||||
{
|
||||
_logger.Warn("Indexer returned result for RSS URL, Credentials appears to be invalid: " + ex.Message);
|
||||
_logger.Warn("Indexer returned result for RSS URL, Credentials appears to be invalid. Response: " + ex.Message);
|
||||
|
||||
return new ValidationFailure("", ex.Message);
|
||||
return new ValidationFailure("", "Indexer returned result for RSS URL, Credentials appears to be invalid. Response: " + ex.Message);
|
||||
}
|
||||
catch (RequestLimitReachedException ex)
|
||||
{
|
||||
@@ -629,6 +642,11 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
_logger.Warn(ex, "Unable to connect to indexer");
|
||||
|
||||
if (ex.Response.HasHttpServerError)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "Unable to connect to indexer, indexer's server is unavailable. Try again later. " + ex.Message);
|
||||
}
|
||||
|
||||
return new ValidationFailure(string.Empty, "Unable to connect to indexer, check the log above the ValidationFailure for more details. " + ex.Message);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
@@ -643,6 +661,21 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
return new ValidationFailure(string.Empty, "Unable to connect to indexer, possibly due to a timeout. Try again or check your network settings. " + ex.Message);
|
||||
}
|
||||
catch (WebException webException)
|
||||
{
|
||||
_logger.Warn("Unable to connect to indexer.");
|
||||
|
||||
if (webException.Status is WebExceptionStatus.NameResolutionFailure or WebExceptionStatus.ConnectFailure)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "Unable to connect to indexer connection failure. Check your connection to the indexer's server and DNS." + webException.Message);
|
||||
}
|
||||
|
||||
if (webException.Message.Contains("502") || webException.Message.Contains("503") ||
|
||||
webException.Message.Contains("504") || webException.Message.Contains("timed out"))
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "Unable to connect to indexer, indexer's server is unavailable. Try again later. " + webException.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Unable to connect to indexer");
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace NzbDrone.Core.Indexers
|
||||
catch
|
||||
{
|
||||
// Skip indexer if we fail in Cardigann mapping
|
||||
_logger.Debug("Indexer {0} has no definition", definition.Name);
|
||||
_logger.Debug("Indexer '{0}' has no definition", definition.Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
private void MapCardigannCategories(IndexerDefinition def, CardigannDefinition defFile)
|
||||
{
|
||||
if (defFile.Caps.Categories != null)
|
||||
if (defFile.Caps.Categories != null && defFile.Caps.Categories.Any())
|
||||
{
|
||||
foreach (var category in defFile.Caps.Categories)
|
||||
{
|
||||
@@ -142,27 +142,23 @@ namespace NzbDrone.Core.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
if (defFile.Caps.Categorymappings != null)
|
||||
if (defFile.Caps.Categorymappings != null && defFile.Caps.Categorymappings.Any())
|
||||
{
|
||||
foreach (var categorymapping in defFile.Caps.Categorymappings)
|
||||
foreach (var categoryMapping in defFile.Caps.Categorymappings)
|
||||
{
|
||||
IndexerCategory torznabCat = null;
|
||||
|
||||
if (categorymapping.cat != null)
|
||||
if (categoryMapping.Cat != null)
|
||||
{
|
||||
torznabCat = NewznabStandardCategory.GetCatByName(categorymapping.cat);
|
||||
torznabCat = NewznabStandardCategory.GetCatByName(categoryMapping.Cat);
|
||||
|
||||
if (torznabCat == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
def.Capabilities.Categories.AddCategoryMapping(categorymapping.id, torznabCat, categorymapping.desc);
|
||||
|
||||
//if (categorymapping.Default)
|
||||
//{
|
||||
// DefaultCategories.Add(categorymapping.id);
|
||||
//}
|
||||
def.Capabilities.Categories.AddCategoryMapping(categoryMapping.Id, torznabCat, categoryMapping.Desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,9 +103,6 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
public static readonly IndexerCategory[] AllCats =
|
||||
{
|
||||
ZedOther,
|
||||
ZedOtherHashed,
|
||||
ZedOtherMisc,
|
||||
Console,
|
||||
ConsoleNDS,
|
||||
ConsolePSP,
|
||||
@@ -176,7 +173,10 @@ namespace NzbDrone.Core.Indexers
|
||||
BooksForeign,
|
||||
Other,
|
||||
OtherMisc,
|
||||
OtherHashed
|
||||
OtherHashed,
|
||||
ZedOther,
|
||||
ZedOtherHashed,
|
||||
ZedOtherMisc
|
||||
};
|
||||
|
||||
static NewznabStandardCategory()
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace NzbDrone.Core.Indexers
|
||||
public abstract class TorrentIndexerBase<TSettings> : HttpIndexerBase<TSettings>
|
||||
where TSettings : IIndexerSettings, new()
|
||||
{
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
|
||||
protected TorrentIndexerBase(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
private readonly IValidateNzbs _nzbValidationService;
|
||||
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
|
||||
|
||||
protected UsenetIndexerBase(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
|
||||
@@ -201,6 +201,7 @@
|
||||
"Indexer": "Indexer",
|
||||
"IndexerAlreadySetup": "At least one instance of indexer is already setup",
|
||||
"IndexerAuth": "Indexer Auth",
|
||||
"IndexerCategories": "Indexer Categories",
|
||||
"IndexerDetails": "Indexer Details",
|
||||
"IndexerDisabled": "Indexer Disabled",
|
||||
"IndexerFailureRate": "Indexer Failure Rate",
|
||||
@@ -222,6 +223,7 @@
|
||||
"IndexerRss": "Indexer Rss",
|
||||
"IndexerSettingsSummary": "Configure various global Indexer settings including Proxies.",
|
||||
"IndexerSite": "Indexer Site",
|
||||
"IndexerStatus": "Indexer Status",
|
||||
"IndexerStatusCheckAllClientMessage": "All indexers are unavailable due to failures",
|
||||
"IndexerStatusCheckSingleClientMessage": "Indexers unavailable due to failures: {0}",
|
||||
"IndexerTagsHelpText": "Use tags to specify Indexer Proxies or which apps the indexer is synced to. Applications without 1 or more matching Indexer Tags will not be synced to.",
|
||||
@@ -277,6 +279,7 @@
|
||||
"NoChange": "No Change",
|
||||
"NoChanges": "No Changes",
|
||||
"NoHistoryFound": "No history found",
|
||||
"NoIndexersFound": "No indexers found",
|
||||
"NoLeaveIt": "No, Leave It",
|
||||
"NoLinks": "No Links",
|
||||
"NoLogFiles": "No log files",
|
||||
|
||||
Reference in New Issue
Block a user