Compare commits

..

41 Commits

Author SHA1 Message Date
Bogdan
f20319fff1 Bump version to 1.6.3 2023-06-25 19:15:06 +03:00
Bogdan
20bcc00662 Fix apprise server url migration 2023-06-25 08:38:39 +03:00
Bogdan
c4af3e746f Add more trace logs related info to bug_report.yml [skip ci]
Co-authored-by: Bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
2023-06-24 08:38:50 +03:00
Bogdan
660a162b7e Fixed: (Cardigann) Throw exception only when all download selectors fail 2023-06-23 11:18:14 +03:00
Bogdan
20a3cad7fb Add indexer id in logs for invalid dates in Cardigann definitions 2023-06-23 10:41:25 +03:00
Bogdan
77fe3f78fe Fixed: (Cardigann) Skip to next download selector when max redirects reached
Fixes #578
2023-06-22 17:01:25 +03:00
Bogdan
d777cb8e29 Fixed: (API) Prevent NullRef when searching empty query with a non-default type 2023-06-22 10:36:53 +03:00
Bogdan
15e7cc7ea8 New: (UI) Show indexer categories in info modal 2023-06-20 13:23:06 +03:00
Shivam Dua
04cf061275 Fixed: (UI) Add New Indexer button on search page when no indexers are present
Add missing listeners and components to make add indexer button work on
search page when no indexers are present
2023-06-20 07:33:34 +03:00
Bogdan
d4cdeac69a Fixed: (Cardigann) Definitions with category mapping Other to use 8000 (Other) 2023-06-20 07:21:35 +03:00
Bogdan
e60fe05ee0 Revert "Fix typo botton to bottom"
This reverts commit e2e65627ee.
2023-06-20 05:20:26 +03:00
Bogdan
9a4c23797a Display error when search failed due to all indexers being disabled 2023-06-20 03:05:55 +03:00
Bogdan
acfdb5bae3 New: (UI) Show disabled indexers as disabled options in search page 2023-06-20 03:05:55 +03:00
Bogdan
e2e65627ee Fix typo botton to bottom 2023-06-19 14:31:37 +03:00
Bogdan
4b8906ea62 Cleanup redundant DownloadProtocol in indexers 2023-06-19 04:26:45 +03:00
Bogdan
f0c5d8ceea Minor refactoring in Cardigann definition 2023-06-19 04:08:01 +03:00
Bogdan
427802a50e Update status translations for Indexer index 2023-06-18 15:46:43 +03:00
Bogdan
0c9eae244a Add skip ci to API docs update commit 2023-06-18 15:45:04 +03:00
Bogdan
75ff2f41d3 Update description for freeleech only in BakaBT 2023-06-18 09:37:33 +03:00
Bakerboy448
d1ba208243 Fixed: (HttpIndexerBase) Better HTTP error handling 2023-06-18 08:15:23 +03:00
Bogdan
4e03ebadc4 New: (UI) Add filter by categories in add indexer modal
Fixes #872
Closes #1731
2023-06-18 08:14:39 +03:00
Bogdan
0155ff60fd Map Cardigann capabilities from meta definition 2023-06-18 08:14:35 +03:00
Bogdan
f0915638f3 New: (Apps) Sync Anime Standard Search with Sonarr
Fixes #998
Closes #1732
2023-06-18 07:05:08 +03:00
Bogdan
56eb58aed1 Bump version to 1.6.2 2023-06-18 07:01:38 +03:00
Bogdan
8a891d07cf Test eligibility of the first request in AvistazBase 2023-06-17 14:22:57 +03:00
Bogdan
40a932cd28 Improved page loading errors 2023-06-17 03:36:40 +03:00
Mark McDowall
4a81630073 Fixed: Clearing logs not updating UI once complete
(cherry picked from commit 56b3acddc9f50f59c78c03ca072fe802752b88a7)
2023-06-17 02:27:45 +03:00
Bogdan
0ff0fe2e68 Prevent NullRef when deleting missing backups 2023-06-17 02:08:40 +03:00
Bogdan
51e33740b0 Update import path in CategoryLabel 2023-06-17 01:08:06 +03:00
Bogdan
119164f729 Show indexer privacy in search results 2023-06-16 05:23:07 +03:00
Bogdan
ef0f8e25fd Sort limits in IndexerCapabilities 2023-06-16 05:23:07 +03:00
Bogdan
d21debe77f Convert to 'using' declaration in Housekeeping Tasks 2023-06-16 02:40:47 +03:00
Bogdan
a3ccc3d0cf Close database connections in housekeeping tasks 2023-06-16 02:40:39 +03:00
Bogdan
46d930e903 Apply template text to switch cases in Cardigann 2023-06-16 00:06:11 +03:00
Bogdan
4561859c2b Fixed: (UI) Case-insensitive sorting for add indexer modal 2023-06-14 10:03:02 +03:00
Bogdan
83166fb0b5 Allow array of string as value in EnhancedSelectInput 2023-06-14 07:11:50 +03:00
Bogdan
b98f9a945d Fix use of TmdbId in NewznabRequestGenerator 2023-06-14 04:11:26 +03:00
Bogdan
e658e3fe48 Fixed: (Cardigann) Skip duplicated GET requests 2023-06-12 03:58:02 +03:00
Bogdan
9042525f22 Bump version to 1.6.1 2023-06-11 09:33:46 +03:00
Bogdan
7b551a0af1 Update Anidub description 2023-06-11 07:42:41 +03:00
Bogdan
31c2917bad Fixed: (Indexers) Allow RSS searches in HttpIndexerBase 2023-06-11 03:25:00 +03:00
120 changed files with 599 additions and 317 deletions

View File

@@ -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

View File

@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '1.6.0'
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

View File

@@ -578,7 +578,7 @@ EnhancedSelectInput.propTypes = {
className: PropTypes.string,
disabledClassName: PropTypes.string,
name: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.arrayOf(PropTypes.number)]).isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.arrayOf(PropTypes.string), PropTypes.arrayOf(PropTypes.number)]).isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
isDisabled: PropTypes.bool.isRequired,
isFetching: PropTypes.bool.isRequired,

View File

@@ -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
});
});

View File

@@ -1,6 +1,8 @@
import PropTypes from 'prop-types';
import React from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import { kinds } from 'Helpers/Props';
function PageSectionContent(props) {
const {
@@ -17,7 +19,7 @@ function PageSectionContent(props) {
);
} else if (!isFetching && !!error) {
return (
<div>{errorMessage}</div>
<Alert kind={kinds.DANGER}>{errorMessage}</Alert>
);
} else if (isPopulated && !error) {
return (

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import FilterMenu from 'Components/Menu/FilterMenu';
import ConfirmModal from 'Components/Modal/ConfirmModal';
@@ -121,9 +122,9 @@ class History extends Component {
{
!isFetchingAny && hasError &&
<div>
<Alert kind={kinds.DANGER}>
{translate('UnableToLoadHistory')}
</div>
</Alert>
}
{
@@ -131,9 +132,9 @@ class History extends Component {
// wait for the episodes to populate because they are never coming.
isPopulated && !hasError && !items.length &&
<div>
No history found
</div>
<Alert kind={kinds.INFO}>
{translate('NoHistoryFound')}
</Alert>
}
{

View File

@@ -40,6 +40,7 @@
flex: 1;
flex-direction: column;
margin-right: 12px;
max-width: 50%;
}
.filterContainer:last-child {

View File

@@ -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';
@@ -26,7 +27,7 @@ const columns = [
isVisible: true
},
{
name: 'name',
name: 'sortName',
label: translate('Name'),
isSortable: true,
isVisible: true
@@ -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>

View File

@@ -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 ? (

View File

@@ -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

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import Alert from 'Components/Alert';
import BarChart from 'Components/Chart/BarChart';
import DoughnutChart from 'Components/Chart/DoughnutChart';
import StackedBarChart from 'Components/Chart/StackedBarChart';
@@ -178,9 +179,9 @@ function Stats(props) {
{
!isFetching && !!error &&
<div className={styles.errorMessage}>
<Alert kind={kinds.DANGER}>
{getErrorMessage(error, 'Failed to load indexer stats from API')}
</div>
</Alert>
}
{

View File

@@ -1,6 +1,7 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
@@ -10,7 +11,9 @@ import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import { align, icons, sortDirections } from 'Helpers/Props';
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';
@@ -53,7 +56,9 @@ class SearchIndex extends Component {
lastToggled: null,
allSelected: false,
allUnselected: false,
selectedState: {}
selectedState: {},
isAddIndexerModalOpen: false,
isEditIndexerModalOpen: false
};
}
@@ -180,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 });
};
@@ -251,7 +272,9 @@ class SearchIndex extends Component {
jumpToCharacter,
selectedState,
allSelected,
allUnselected
allUnselected,
isAddIndexerModalOpen,
isEditIndexerModalOpen
} = this.state;
const selectedIndexerIds = this.getSelectedIds();
@@ -309,9 +332,9 @@ class SearchIndex extends Component {
{
!isFetching && !!error &&
<div className={styles.errorMessage}>
<Alert kind={kinds.DANGER}>
{getErrorMessage(error, 'Failed to load search results from API')}
</div>
</Alert>
}
{
@@ -347,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>
{

View File

@@ -1,8 +1,8 @@
import PropTypes from 'prop-types';
import React from 'react';
import Label from 'Components/Label';
import Tooltip from 'Components/Tooltip/Tooltip';
import { kinds, tooltipPositions } from 'Helpers/Props';
import Tooltip from '../../Components/Tooltip/Tooltip';
function CategoryLabel({ categories }) {
const sortedCategories = categories.filter((cat) => cat.name !== undefined).sort((c) => c.id);

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import FieldSet from 'Components/FieldSet';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
@@ -8,7 +9,7 @@ import FormLabel from 'Components/Form/FormLabel';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import { inputTypes } from 'Helpers/Props';
import { inputTypes, kinds } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate';
@@ -49,9 +50,9 @@ class DevelopmentSettings extends Component {
{
!isFetching && error &&
<div>
<Alert kind={kinds.DANGER}>
{translate('UnableToLoadDevelopmentSettings')}
</div>
</Alert>
}
{

View File

@@ -1,6 +1,7 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import Form from 'Components/Form/Form';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ConfirmModal from 'Components/Modal/ConfirmModal';
@@ -123,9 +124,9 @@ class GeneralSettings extends Component {
{
!isFetching && error &&
<div>
<Alert kind={kinds.DANGER}>
{translate('UnableToLoadGeneralSettings')}
</div>
</Alert>
}
{

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import FieldSet from 'Components/FieldSet';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
@@ -8,7 +9,7 @@ import FormLabel from 'Components/Form/FormLabel';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import { inputTypes } from 'Helpers/Props';
import { inputTypes, kinds } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import themes from 'Styles/Themes';
import titleCase from 'Utilities/String/titleCase';
@@ -80,9 +81,9 @@ class UISettings extends Component {
{
!isFetching && error &&
<div>
<Alert kind={kinds.DANGER}>
{translate('UnableToLoadUISettings')}
</div>
</Alert>
}
{

View File

@@ -41,7 +41,7 @@ export const defaultState = {
isFetching: false,
isPopulated: false,
error: null,
sortKey: 'name',
sortKey: 'sortName',
sortDirection: sortDirections.ASCENDING,
items: []
}

View File

@@ -36,7 +36,7 @@ export const defaultState = {
columns: [
{
name: 'status',
columnLabel: translate('ReleaseStatus'),
columnLabel: translate('IndexerStatus'),
isSortable: true,
isVisible: true,
isModifiable: false

View File

@@ -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',
//

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
@@ -8,7 +9,7 @@ import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { icons } from 'Helpers/Props';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import BackupRow from './BackupRow';
import RestoreBackupModalConnector from './RestoreBackupModalConnector';
@@ -107,16 +108,16 @@ class Backups extends Component {
{
!isFetching && !!error &&
<div>
<Alert kind={kinds.DANGER}>
{translate('UnableToLoadBackups')}
</div>
</Alert>
}
{
noBackups &&
<div>
<Alert kind={kinds.INFO}>
{translate('NoBackupsAreAvailable')}
</div>
</Alert>
}
{

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import FilterMenu from 'Components/Menu/FilterMenu';
import PageContent from 'Components/Page/PageContent';
@@ -11,7 +12,7 @@ import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import TablePager from 'Components/Table/TablePager';
import { align, icons } from 'Helpers/Props';
import { align, icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import LogsTableRow from './LogsTableRow';
@@ -82,9 +83,9 @@ function LogsTable(props) {
{
isPopulated && !error && !items.length &&
<div>
<Alert kind={kinds.INFO}>
No events found
</div>
</Alert>
}
{

View File

@@ -96,7 +96,14 @@ class LogsTableConnector extends Component {
};
onClearLogsPress = () => {
this.props.executeCommand({ name: commandNames.CLEAR_LOGS });
this.props.executeCommand({
name: commandNames.CLEAR_LOGS,
commandFinished: this.onCommandFinished
});
};
onCommandFinished = () => {
this.props.gotoLogsFirstPage();
};
//

View File

@@ -11,7 +11,7 @@ import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { icons } from 'Helpers/Props';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import LogsNavMenu from '../LogsNavMenu';
import LogFilesTableRow from './LogFilesTableRow';
@@ -118,9 +118,9 @@ class LogFiles extends Component {
{
!isFetching && !items.length &&
<div>
<Alert kind={kinds.INFO}>
{translate('NoLogFiles')}
</div>
</Alert>
}
</PageContentBody>
</PageContent>

View File

@@ -50,12 +50,6 @@ class LogFilesConnector extends Component {
this.props.fetchLogFiles();
}
componentDidUpdate(prevProps) {
if (prevProps.deleteFilesExecuting && !this.props.deleteFilesExecuting) {
this.props.fetchLogFiles();
}
}
//
// Listeners
@@ -64,7 +58,14 @@ class LogFilesConnector extends Component {
};
onDeleteFilesPress = () => {
this.props.executeCommand({ name: commandNames.DELETE_LOG_FILES });
this.props.executeCommand({
name: commandNames.DELETE_LOG_FILES,
commandFinished: this.onCommandFinished
});
};
onCommandFinished = () => {
this.props.fetchLogFiles();
};
//

View File

@@ -1,6 +1,7 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component, Fragment } from 'react';
import Alert from 'Components/Alert';
import Icon from 'Components/Icon';
import Label from 'Components/Label';
import SpinnerButton from 'Components/Link/SpinnerButton';
@@ -61,9 +62,9 @@ class Updates extends Component {
{
noUpdates &&
<div>
<Alert kind={kinds.INFO}>
{translate('NoUpdatesAreAvailable')}
</div>
</Alert>
}
{

View File

@@ -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 ||

View File

@@ -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; }
}
}

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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));

View File

@@ -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);
}
}
}

View File

@@ -14,13 +14,11 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean()
{
using (var mapper = _database.OpenConnection())
{
mapper.Execute(@"DELETE FROM ""Users""
WHERE ""Id"" NOT IN (
SELECT ""Id"" FROM ""Users""
LIMIT 1)");
}
using var mapper = _database.OpenConnection();
mapper.Execute(@"DELETE FROM ""Users""
WHERE ""Id"" NOT IN (
SELECT ""Id"" FROM ""Users""
LIMIT 1)");
}
}
}

View File

@@ -14,14 +14,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean()
{
var mapper = _database.OpenConnection();
using var mapper = _database.OpenConnection();
mapper.Execute(@"DELETE FROM ""ApplicationStatus""
WHERE ""Id"" IN (
SELECT ""ApplicationStatus"".""Id"" FROM ""ApplicationStatus""
LEFT OUTER JOIN ""Applications""
ON ""ApplicationStatus"".""ProviderId"" = ""Applications"".""Id""
WHERE ""Applications"".""Id"" IS NULL)");
WHERE ""Id"" IN (
SELECT ""ApplicationStatus"".""Id"" FROM ""ApplicationStatus""
LEFT OUTER JOIN ""Applications""
ON ""ApplicationStatus"".""ProviderId"" = ""Applications"".""Id""
WHERE ""Applications"".""Id"" IS NULL)");
}
}
}

View File

@@ -14,14 +14,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean()
{
var mapper = _database.OpenConnection();
using var mapper = _database.OpenConnection();
mapper.Execute(@"DELETE FROM ""DownloadClientStatus""
WHERE ""Id"" IN (
SELECT ""DownloadClientStatus"".""Id"" FROM ""DownloadClientStatus""
LEFT OUTER JOIN ""DownloadClients""
ON ""DownloadClientStatus"".""ProviderId"" = ""DownloadClients"".""Id""
WHERE ""DownloadClients"".""Id"" IS NULL)");
WHERE ""Id"" IN (
SELECT ""DownloadClientStatus"".""Id"" FROM ""DownloadClientStatus""
LEFT OUTER JOIN ""DownloadClients""
ON ""DownloadClientStatus"".""ProviderId"" = ""DownloadClients"".""Id""
WHERE ""DownloadClients"".""Id"" IS NULL)");
}
}
}

View File

@@ -19,15 +19,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
private void CleanupOrphanedByIndexer()
{
using (var mapper = _database.OpenConnection())
{
mapper.Execute(@"DELETE FROM ""History""
WHERE ""Id"" IN (
SELECT ""History"".""Id"" FROM ""History""
LEFT OUTER JOIN ""Indexers""
ON ""History"".""IndexerId"" = ""Indexers"".""Id""
WHERE ""Indexers"".""Id"" IS NULL)");
}
using var mapper = _database.OpenConnection();
mapper.Execute(@"DELETE FROM ""History""
WHERE ""Id"" IN (
SELECT ""History"".""Id"" FROM ""History""
LEFT OUTER JOIN ""Indexers""
ON ""History"".""IndexerId"" = ""Indexers"".""Id""
WHERE ""Indexers"".""Id"" IS NULL)");
}
}
}

View File

@@ -14,15 +14,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean()
{
using (var mapper = _database.OpenConnection())
{
mapper.Execute(@"DELETE FROM ""IndexerStatus""
WHERE ""Id"" IN (
SELECT ""IndexerStatus"".""Id"" FROM ""IndexerStatus""
LEFT OUTER JOIN ""Indexers""
ON ""IndexerStatus"".""ProviderId"" = ""Indexers"".""Id""
WHERE ""Indexers"".""Id"" IS NULL)");
}
using var mapper = _database.OpenConnection();
mapper.Execute(@"DELETE FROM ""IndexerStatus""
WHERE ""Id"" IN (
SELECT ""IndexerStatus"".""Id"" FROM ""IndexerStatus""
LEFT OUTER JOIN ""Indexers""
ON ""IndexerStatus"".""ProviderId"" = ""Indexers"".""Id""
WHERE ""Indexers"".""Id"" IS NULL)");
}
}
}

View File

@@ -17,23 +17,21 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean()
{
using (var mapper = _database.OpenConnection())
using var mapper = _database.OpenConnection();
var usedTags = new[] { "Notifications", "IndexerProxies", "Indexers", "Applications" }
.SelectMany(v => GetUsedTags(v, mapper))
.Distinct()
.ToArray();
if (usedTags.Length > 0)
{
var usedTags = new[] { "Notifications", "IndexerProxies", "Indexers", "Applications" }
.SelectMany(v => GetUsedTags(v, mapper))
.Distinct()
.ToArray();
var usedTagsList = string.Join(",", usedTags.Select(d => d.ToString()).ToArray());
if (usedTags.Length > 0)
{
var usedTagsList = string.Join(",", usedTags.Select(d => d.ToString()).ToArray());
mapper.Execute($"DELETE FROM \"Tags\" WHERE NOT \"Id\" IN ({usedTagsList})");
}
else
{
mapper.Execute($"DELETE FROM \"Tags\"");
}
mapper.Execute($"DELETE FROM \"Tags\" WHERE NOT \"Id\" IN ({usedTagsList})");
}
else
{
mapper.Execute("DELETE FROM \"Tags\"");
}
}

View File

@@ -24,13 +24,11 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
_logger.Debug("Not running scheduled task last execution cleanup during debug");
}
using (var mapper = _database.OpenConnection())
{
mapper.Execute(@"UPDATE ""ScheduledTasks""
SET ""LastExecution"" = @time
WHERE ""LastExecution"" > @time",
new { time = DateTime.UtcNow });
}
using var mapper = _database.OpenConnection();
mapper.Execute(@"UPDATE ""ScheduledTasks""
SET ""LastExecution"" = @time
WHERE ""LastExecution"" > @time",
new { time = DateTime.UtcNow });
}
}
}

View File

@@ -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);

View File

@@ -76,8 +76,12 @@ namespace NzbDrone.Core.IndexerSearch
select new XElement("item",
new XElement("title", RemoveInvalidXMLChars(r.Title)),
new XElement("description", RemoveInvalidXMLChars(r.Description)),
new XElement("guid", r.Guid), // GUID and (Link or Magnet) are mandatory
new XElement("prowlarrindexer", new XAttribute("id", r.IndexerId), r.Indexer),
new XElement("guid", r.Guid), // GUID and (Link or Magnet) are mandatory
new XElement(
"prowlarrindexer",
new XAttribute("id", r.IndexerId),
new XAttribute("type", r.IndexerPrivacy switch { IndexerPrivacy.Private => "private", IndexerPrivacy.Public => "public", _ => "semi-private" }),
r.Indexer),
r.InfoUrl == null ? null : new XElement("comments", r.InfoUrl),
r.PublishDate == DateTime.MinValue ? new XElement("pubDate", XmlDateFormat(DateTime.Now)) : new XElement("pubDate", XmlDateFormat(r.PublishDate)),
new XElement("size", r.Size),

View File

@@ -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>();
}
}

View File

@@ -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();

View File

@@ -25,10 +25,9 @@ namespace NzbDrone.Core.Indexers.Definitions
{
public override string Name => "Anidub";
public override string[] IndexerUrls => new[] { "https://tr.anidub.com/" };
public override string Description => "Anidub is russian anime voiceover group and eponymous anime tracker.";
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();

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;
@@ -93,12 +92,10 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
var jsonResponse = new HttpResponse<AvistazErrorResponse>(ex.Response);
return new ValidationFailure(string.Empty, jsonResponse.Resource?.Message ?? "Unauthorized request to indexer");
}
else
{
_logger.Warn(ex, "Unable to connect to indexer");
return new ValidationFailure(string.Empty, "Unable to connect to indexer, check the log above the ValidationFailure for more details. " + ex.Message);
}
_logger.Warn(ex, "Unable to connect to indexer");
return new ValidationFailure(string.Empty, "Unable to connect to indexer, check the log above the ValidationFailure for more details. " + ex.Message);
}
catch (Exception ex)
{
@@ -107,7 +104,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
return new ValidationFailure(string.Empty, "Unable to connect to indexer, check the log above the ValidationFailure for more details");
}
return null;
return await base.TestConnection();
}
private async Task<string> GetToken()

View File

@@ -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();

View File

@@ -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")]

View File

@@ -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();

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}
@@ -169,7 +173,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
{
if (selection.Matches(@case.Key) || QuerySelector(selection, @case.Key) != null)
{
value = @case.Value;
value = ApplyGoTemplateText(@case.Value, variables);
break;
}
}
@@ -178,7 +182,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
{
if (required)
{
throw new Exception(string.Format("None of the case selectors \"{0}\" matched {1}", string.Join(",", selector.Case), selection.ToHtmlPretty()));
throw new Exception($"None of the case selectors \"{string.Join(",", selector.Case)}\" matched {selection.ToHtmlPretty()}");
}
return null;
@@ -249,11 +253,11 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
if (selector.Case != null)
{
foreach (var jcase in selector.Case)
foreach (var @case in selector.Case)
{
if (value.Equals(jcase.Key) || jcase.Key.Equals("*"))
if ((value != null && value.Equals(@case.Key)) || @case.Key.Equals("*"))
{
value = jcase.Value;
value = ApplyGoTemplateText(@case.Value, variables);
break;
}
}
@@ -262,7 +266,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
{
if (required)
{
throw new Exception(string.Format("None of the case selectors \"{0}\" matched {1}", string.Join(",", selector.Case), parentObj.ToString()));
throw new Exception($"None of the case selectors \"{string.Join(",", selector.Case)}\" matched {parentObj}");
}
return null;
@@ -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;

View File

@@ -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; }
}

View File

@@ -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; }
}
}

View File

@@ -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}");
}
}
@@ -1073,9 +1079,9 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
variables[".Query.Keywords"] = string.Join(" ", keywordTokens);
variables[".Keywords"] = ApplyFilters((string)variables[".Query.Keywords"], search.Keywordsfilters, variables);
// TODO: prepare queries first and then send them parallel
var searchPaths = search.Paths;
foreach (var searchPath in searchPaths)
var searchUrls = new List<string>();
foreach (var searchPath in search.Paths)
{
variables[".Categories"] = mappedCategories;
@@ -1158,14 +1164,20 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
}
}
if (method == HttpMethod.Get)
if (method == HttpMethod.Get && queryCollection.Count > 0)
{
if (queryCollection.Count > 0)
{
searchUrl += "?" + queryCollection.GetQueryString(_encoding);
}
searchUrl += "?" + queryCollection.GetQueryString(_encoding);
}
if (method == HttpMethod.Get && searchUrls.Contains(searchUrl))
{
_logger.Trace("Skip duplicated request {0}", searchUrl);
continue;
}
searchUrls.Add(searchUrl);
_logger.Debug($"Adding request: {searchUrl}");
var requestBuilder = new HttpRequestBuilder(searchUrl)

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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();

View File

@@ -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();

View File

@@ -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";

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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()

View File

@@ -129,7 +129,7 @@ namespace NzbDrone.Core.Indexers.Newznab
if (searchCriteria.TmdbId.HasValue && capabilities.TvSearchTvdbAvailable)
{
parameters.Set("tmdbid", searchCriteria.TvdbId.Value.ToString());
parameters.Set("tmdbid", searchCriteria.TmdbId.Value.ToString());
}
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace() && capabilities.TvSearchImdbAvailable)

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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";

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

Some files were not shown because too many files have changed in this diff Show More