diff --git a/frontend/src/App/AppRoutes.js b/frontend/src/App/AppRoutes.js index d9ff7b470..bf08d82b1 100644 --- a/frontend/src/App/AppRoutes.js +++ b/frontend/src/App/AppRoutes.js @@ -11,6 +11,7 @@ import ApplicationSettingsConnector from 'Settings/Applications/ApplicationSetti import DevelopmentSettingsConnector from 'Settings/Development/DevelopmentSettingsConnector'; import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector'; import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector'; +import IndexerSettings from 'Settings/Indexers/IndexerSettings'; import NotificationSettings from 'Settings/Notifications/NotificationSettings'; import Settings from 'Settings/Settings'; import TagSettings from 'Settings/Tags/TagSettings'; @@ -90,6 +91,11 @@ function AppRoutes(props) { component={Settings} /> + + ); diff --git a/frontend/src/Components/Page/Sidebar/PageSidebar.js b/frontend/src/Components/Page/Sidebar/PageSidebar.js index 42db980a9..9de449f95 100644 --- a/frontend/src/Components/Page/Sidebar/PageSidebar.js +++ b/frontend/src/Components/Page/Sidebar/PageSidebar.js @@ -48,6 +48,10 @@ const links = [ title: translate('Settings'), to: '/settings', children: [ + { + title: translate('Indexers'), + to: '/settings/indexers' + }, { title: translate('Apps'), to: '/settings/applications' diff --git a/frontend/src/Indexer/Edit/EditIndexerModalContent.js b/frontend/src/Indexer/Edit/EditIndexerModalContent.js index 2a7c7a6ee..2cc55b8a2 100644 --- a/frontend/src/Indexer/Edit/EditIndexerModalContent.js +++ b/frontend/src/Indexer/Edit/EditIndexerModalContent.js @@ -45,6 +45,7 @@ function EditIndexerModalContent(props) { supportsRss, supportsRedirect, appProfileId, + tags, fields, priority } = item; @@ -151,6 +152,18 @@ function EditIndexerModalContent(props) { onChange={onInputChange} /> + + + {translate('Tags')} + + + } diff --git a/frontend/src/Indexer/Edit/EditIndexerModalContentConnector.js b/frontend/src/Indexer/Edit/EditIndexerModalContentConnector.js index de0120f90..676457a12 100644 --- a/frontend/src/Indexer/Edit/EditIndexerModalContentConnector.js +++ b/frontend/src/Indexer/Edit/EditIndexerModalContentConnector.js @@ -59,7 +59,6 @@ class EditIndexerModalContentConnector extends Component { } onAdvancedSettingsPress = () => { - console.log('settings'); this.props.toggleAdvancedSettings(); } diff --git a/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyItem.css b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyItem.css new file mode 100644 index 000000000..81e135559 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyItem.css @@ -0,0 +1,44 @@ +.indexerProxy { + composes: card from '~Components/Card.css'; + + position: relative; + width: 300px; + height: 100px; +} + +.underlay { + @add-mixin cover; +} + +.overlay { + @add-mixin linkOverlay; + + padding: 10px; +} + +.name { + text-align: center; + font-weight: lighter; + font-size: 24px; +} + +.actions { + margin-top: 20px; + text-align: right; +} + +.presetsMenu { + composes: menu from '~Components/Menu/Menu.css'; + + display: inline-block; + margin: 0 5px; +} + +.presetsMenuButton { + composes: button from '~Components/Link/Button.css'; + + &::after { + margin-left: 5px; + content: '\25BE'; + } +} diff --git a/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyItem.js b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyItem.js new file mode 100644 index 000000000..73e6454a9 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyItem.js @@ -0,0 +1,111 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import Button from 'Components/Link/Button'; +import Link from 'Components/Link/Link'; +import Menu from 'Components/Menu/Menu'; +import MenuContent from 'Components/Menu/MenuContent'; +import { sizes } from 'Helpers/Props'; +import translate from 'Utilities/String/translate'; +import AddIndexerProxyPresetMenuItem from './AddIndexerProxyPresetMenuItem'; +import styles from './AddIndexerProxyItem.css'; + +class AddIndexerProxyItem extends Component { + + // + // Listeners + + onIndexerProxySelect = () => { + const { + implementation + } = this.props; + + this.props.onIndexerProxySelect({ implementation }); + } + + // + // Render + + render() { + const { + implementation, + implementationName, + infoLink, + presets, + onIndexerProxySelect + } = this.props; + + const hasPresets = !!presets && !!presets.length; + + return ( +
+ + +
+
+ {implementationName} +
+ +
+ { + hasPresets && + + + + + + + + { + presets.map((preset) => { + return ( + + ); + }) + } + + + + } + + +
+
+
+ ); + } +} + +AddIndexerProxyItem.propTypes = { + implementation: PropTypes.string.isRequired, + implementationName: PropTypes.string.isRequired, + infoLink: PropTypes.string.isRequired, + presets: PropTypes.arrayOf(PropTypes.object), + onIndexerProxySelect: PropTypes.func.isRequired +}; + +export default AddIndexerProxyItem; diff --git a/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModal.js b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModal.js new file mode 100644 index 000000000..55550d3c5 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModal.js @@ -0,0 +1,25 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import Modal from 'Components/Modal/Modal'; +import AddIndexerProxyModalContentConnector from './AddIndexerProxyModalContentConnector'; + +function AddIndexerProxyModal({ isOpen, onModalClose, ...otherProps }) { + return ( + + + + ); +} + +AddIndexerProxyModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onModalClose: PropTypes.func.isRequired +}; + +export default AddIndexerProxyModal; diff --git a/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModalContent.css b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModalContent.css new file mode 100644 index 000000000..89168dc33 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModalContent.css @@ -0,0 +1,5 @@ +.indexerProxies { + display: flex; + justify-content: center; + flex-wrap: wrap; +} diff --git a/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModalContent.js b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModalContent.js new file mode 100644 index 000000000..f00a05bd5 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModalContent.js @@ -0,0 +1,88 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import Button from 'Components/Link/Button'; +import LoadingIndicator from 'Components/Loading/LoadingIndicator'; +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 translate from 'Utilities/String/translate'; +import AddIndexerProxyItem from './AddIndexerProxyItem'; +import styles from './AddIndexerProxyModalContent.css'; + +class AddIndexerProxyModalContent extends Component { + + // + // Render + + render() { + const { + isSchemaFetching, + isSchemaPopulated, + schemaError, + schema, + onIndexerProxySelect, + onModalClose + } = this.props; + + return ( + + + Add IndexerProxy + + + + { + isSchemaFetching && + + } + + { + !isSchemaFetching && !!schemaError && +
+ {translate('UnableToAddANewIndexerProxyPleaseTryAgain')} +
+ } + + { + isSchemaPopulated && !schemaError && +
+
+ { + schema.map((indexerProxy) => { + return ( + + ); + }) + } +
+
+ } +
+ + + +
+ ); + } +} + +AddIndexerProxyModalContent.propTypes = { + isSchemaFetching: PropTypes.bool.isRequired, + isSchemaPopulated: PropTypes.bool.isRequired, + schemaError: PropTypes.object, + schema: PropTypes.arrayOf(PropTypes.object).isRequired, + onIndexerProxySelect: PropTypes.func.isRequired, + onModalClose: PropTypes.func.isRequired +}; + +export default AddIndexerProxyModalContent; diff --git a/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModalContentConnector.js b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModalContentConnector.js new file mode 100644 index 000000000..cccafe922 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyModalContentConnector.js @@ -0,0 +1,70 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; +import { fetchIndexerProxySchema, selectIndexerProxySchema } from 'Store/Actions/settingsActions'; +import AddIndexerProxyModalContent from './AddIndexerProxyModalContent'; + +function createMapStateToProps() { + return createSelector( + (state) => state.settings.indexerProxies, + (indexerProxies) => { + const { + isSchemaFetching, + isSchemaPopulated, + schemaError, + schema + } = indexerProxies; + + return { + isSchemaFetching, + isSchemaPopulated, + schemaError, + schema + }; + } + ); +} + +const mapDispatchToProps = { + fetchIndexerProxySchema, + selectIndexerProxySchema +}; + +class AddIndexerProxyModalContentConnector extends Component { + + // + // Lifecycle + + componentDidMount() { + this.props.fetchIndexerProxySchema(); + } + + // + // Listeners + + onIndexerProxySelect = ({ implementation, name }) => { + this.props.selectIndexerProxySchema({ implementation, presetName: name }); + this.props.onModalClose({ indexerProxySelected: true }); + } + + // + // Render + + render() { + return ( + + ); + } +} + +AddIndexerProxyModalContentConnector.propTypes = { + fetchIndexerProxySchema: PropTypes.func.isRequired, + selectIndexerProxySchema: PropTypes.func.isRequired, + onModalClose: PropTypes.func.isRequired +}; + +export default connect(createMapStateToProps, mapDispatchToProps)(AddIndexerProxyModalContentConnector); diff --git a/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyPresetMenuItem.js b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyPresetMenuItem.js new file mode 100644 index 000000000..52ee40c10 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/AddIndexerProxyPresetMenuItem.js @@ -0,0 +1,49 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import MenuItem from 'Components/Menu/MenuItem'; + +class AddIndexerProxyPresetMenuItem extends Component { + + // + // Listeners + + onPress = () => { + const { + name, + implementation + } = this.props; + + this.props.onPress({ + name, + implementation + }); + } + + // + // Render + + render() { + const { + name, + implementation, + ...otherProps + } = this.props; + + return ( + + {name} + + ); + } +} + +AddIndexerProxyPresetMenuItem.propTypes = { + name: PropTypes.string.isRequired, + implementation: PropTypes.string.isRequired, + onPress: PropTypes.func.isRequired +}; + +export default AddIndexerProxyPresetMenuItem; diff --git a/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModal.js b/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModal.js new file mode 100644 index 000000000..adacde9c3 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModal.js @@ -0,0 +1,27 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import Modal from 'Components/Modal/Modal'; +import { sizes } from 'Helpers/Props'; +import EditIndexerProxyModalContentConnector from './EditIndexerProxyModalContentConnector'; + +function EditIndexerProxyModal({ isOpen, onModalClose, ...otherProps }) { + return ( + + + + ); +} + +EditIndexerProxyModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onModalClose: PropTypes.func.isRequired +}; + +export default EditIndexerProxyModal; diff --git a/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalConnector.js b/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalConnector.js new file mode 100644 index 000000000..a1a6563e7 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalConnector.js @@ -0,0 +1,65 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { clearPendingChanges } from 'Store/Actions/baseActions'; +import { cancelSaveIndexerProxy, cancelTestIndexerProxy } from 'Store/Actions/settingsActions'; +import EditIndexerProxyModal from './EditIndexerProxyModal'; + +function createMapDispatchToProps(dispatch, props) { + const section = 'settings.indexerProxies'; + + return { + dispatchClearPendingChanges() { + dispatch(clearPendingChanges({ section })); + }, + + dispatchCancelTestIndexerProxy() { + dispatch(cancelTestIndexerProxy({ section })); + }, + + dispatchCancelSaveIndexerProxy() { + dispatch(cancelSaveIndexerProxy({ section })); + } + }; +} + +class EditIndexerProxyModalConnector extends Component { + + // + // Listeners + + onModalClose = () => { + this.props.dispatchClearPendingChanges(); + this.props.dispatchCancelTestIndexerProxy(); + this.props.dispatchCancelSaveIndexerProxy(); + this.props.onModalClose(); + } + + // + // Render + + render() { + const { + dispatchClearPendingChanges, + dispatchCancelTestIndexerProxy, + dispatchCancelSaveIndexerProxy, + ...otherProps + } = this.props; + + return ( + + ); + } +} + +EditIndexerProxyModalConnector.propTypes = { + onModalClose: PropTypes.func.isRequired, + dispatchClearPendingChanges: PropTypes.func.isRequired, + dispatchCancelTestIndexerProxy: PropTypes.func.isRequired, + dispatchCancelSaveIndexerProxy: PropTypes.func.isRequired +}; + +export default connect(null, createMapDispatchToProps)(EditIndexerProxyModalConnector); diff --git a/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalContent.css b/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalContent.css new file mode 100644 index 000000000..8e1c16507 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalContent.css @@ -0,0 +1,11 @@ +.deleteButton { + composes: button from '~Components/Link/Button.css'; + + margin-right: auto; +} + +.message { + composes: alert from '~Components/Alert.css'; + + margin-bottom: 30px; +} diff --git a/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalContent.js b/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalContent.js new file mode 100644 index 000000000..59ce4e820 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalContent.js @@ -0,0 +1,175 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import Alert from 'Components/Alert'; +import Form from 'Components/Form/Form'; +import FormGroup from 'Components/Form/FormGroup'; +import FormInputGroup from 'Components/Form/FormInputGroup'; +import FormLabel from 'Components/Form/FormLabel'; +import ProviderFieldFormGroup from 'Components/Form/ProviderFieldFormGroup'; +import Button from 'Components/Link/Button'; +import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton'; +import LoadingIndicator from 'Components/Loading/LoadingIndicator'; +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 { inputTypes, kinds } from 'Helpers/Props'; +import translate from 'Utilities/String/translate'; +import styles from './EditIndexerProxyModalContent.css'; + +function EditIndexerProxyModalContent(props) { + const { + advancedSettings, + isFetching, + error, + isSaving, + isTesting, + saveError, + item, + onInputChange, + onFieldChange, + onModalClose, + onSavePress, + onTestPress, + onDeleteIndexerProxyPress, + ...otherProps + } = props; + + const { + id, + implementationName, + name, + tags, + fields, + message + } = item; + + return ( + + + {`${id ? 'Edit' : 'Add'} Proxy - ${implementationName}`} + + + + { + isFetching && + + } + + { + !isFetching && !!error && +
+ {translate('UnableToAddANewIndexerProxyPleaseTryAgain')} +
+ } + + { + !isFetching && !error && +
+ { + !!message && + + {message.value.message} + + } + + + {translate('Name')} + + + + + + {translate('Tags')} + + + + + { + fields.map((field) => { + return ( + + ); + }) + } + + + } +
+ + { + id && + + } + + + {translate('Test')} + + + + + + {translate('Save')} + + +
+ ); +} + +EditIndexerProxyModalContent.propTypes = { + advancedSettings: PropTypes.bool.isRequired, + isFetching: PropTypes.bool.isRequired, + error: PropTypes.object, + isSaving: PropTypes.bool.isRequired, + isTesting: PropTypes.bool.isRequired, + saveError: PropTypes.object, + item: PropTypes.object.isRequired, + onInputChange: PropTypes.func.isRequired, + onFieldChange: PropTypes.func.isRequired, + onModalClose: PropTypes.func.isRequired, + onSavePress: PropTypes.func.isRequired, + onTestPress: PropTypes.func.isRequired, + onDeleteIndexerProxyPress: PropTypes.func +}; + +export default EditIndexerProxyModalContent; diff --git a/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalContentConnector.js b/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalContentConnector.js new file mode 100644 index 000000000..01db7709b --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/EditIndexerProxyModalContentConnector.js @@ -0,0 +1,88 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; +import { saveIndexerProxy, setIndexerProxyFieldValue, setIndexerProxyValue, testIndexerProxy } from 'Store/Actions/settingsActions'; +import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector'; +import EditIndexerProxyModalContent from './EditIndexerProxyModalContent'; + +function createMapStateToProps() { + return createSelector( + (state) => state.settings.advancedSettings, + createProviderSettingsSelector('indexerProxies'), + (advancedSettings, indexerProxy) => { + return { + advancedSettings, + ...indexerProxy + }; + } + ); +} + +const mapDispatchToProps = { + setIndexerProxyValue, + setIndexerProxyFieldValue, + saveIndexerProxy, + testIndexerProxy +}; + +class EditIndexerProxyModalContentConnector extends Component { + + // + // Lifecycle + + componentDidUpdate(prevProps, prevState) { + if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) { + this.props.onModalClose(); + } + } + + // + // Listeners + + onInputChange = ({ name, value }) => { + this.props.setIndexerProxyValue({ name, value }); + } + + onFieldChange = ({ name, value }) => { + this.props.setIndexerProxyFieldValue({ name, value }); + } + + onSavePress = () => { + this.props.saveIndexerProxy({ id: this.props.id }); + } + + onTestPress = () => { + this.props.testIndexerProxy({ id: this.props.id }); + } + + // + // Render + + render() { + return ( + + ); + } +} + +EditIndexerProxyModalContentConnector.propTypes = { + id: PropTypes.number, + isFetching: PropTypes.bool.isRequired, + isSaving: PropTypes.bool.isRequired, + saveError: PropTypes.object, + item: PropTypes.object.isRequired, + setIndexerProxyValue: PropTypes.func.isRequired, + setIndexerProxyFieldValue: PropTypes.func.isRequired, + saveIndexerProxy: PropTypes.func.isRequired, + testIndexerProxy: PropTypes.func.isRequired, + onModalClose: PropTypes.func.isRequired +}; + +export default connect(createMapStateToProps, mapDispatchToProps)(EditIndexerProxyModalContentConnector); diff --git a/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxies.css b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxies.css new file mode 100644 index 000000000..4a80c6128 --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxies.css @@ -0,0 +1,20 @@ +.indexerProxies { + display: flex; + flex-wrap: wrap; +} + +.addIndexerProxy { + composes: indexerProxy from '~./IndexerProxy.css'; + + background-color: $cardAlternateBackgroundColor; + color: $gray; + text-align: center; +} + +.center { + display: inline-block; + padding: 5px 20px 0; + border: 1px solid $borderColor; + border-radius: 4px; + background-color: $white; +} diff --git a/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxies.js b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxies.js new file mode 100644 index 000000000..b51b8116f --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxies.js @@ -0,0 +1,121 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import Card from 'Components/Card'; +import FieldSet from 'Components/FieldSet'; +import Icon from 'Components/Icon'; +import PageSectionContent from 'Components/Page/PageSectionContent'; +import { icons } from 'Helpers/Props'; +import translate from 'Utilities/String/translate'; +import AddIndexerProxyModal from './AddIndexerProxyModal'; +import EditIndexerProxyModalConnector from './EditIndexerProxyModalConnector'; +import IndexerProxy from './IndexerProxy'; +import styles from './IndexerProxies.css'; + +class IndexerProxies extends Component { + + // + // Lifecycle + + constructor(props, context) { + super(props, context); + + this.state = { + isAddIndexerProxyModalOpen: false, + isEditIndexerProxyModalOpen: false + }; + } + + // + // Listeners + + onAddIndexerProxyPress = () => { + this.setState({ isAddIndexerProxyModalOpen: true }); + } + + onAddIndexerProxyModalClose = ({ indexerProxySelected = false } = {}) => { + this.setState({ + isAddIndexerProxyModalOpen: false, + isEditIndexerProxyModalOpen: indexerProxySelected + }); + } + + onEditIndexerProxyModalClose = () => { + this.setState({ isEditIndexerProxyModalOpen: false }); + } + + // + // Render + + render() { + const { + items, + tagList, + indexerList, + onConfirmDeleteIndexerProxy, + ...otherProps + } = this.props; + + const { + isAddIndexerProxyModalOpen, + isEditIndexerProxyModalOpen + } = this.state; + + return ( +
+ +
+ { + items.map((item) => { + return ( + + ); + }) + } + + +
+ +
+
+
+ + + + +
+
+ ); + } +} + +IndexerProxies.propTypes = { + isFetching: PropTypes.bool.isRequired, + error: PropTypes.object, + items: PropTypes.arrayOf(PropTypes.object).isRequired, + tagList: PropTypes.arrayOf(PropTypes.object).isRequired, + indexerList: PropTypes.arrayOf(PropTypes.object).isRequired, + onConfirmDeleteIndexerProxy: PropTypes.func.isRequired +}; + +export default IndexerProxies; diff --git a/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxiesConnector.js b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxiesConnector.js new file mode 100644 index 000000000..94258079a --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxiesConnector.js @@ -0,0 +1,65 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; +import { deleteIndexerProxy, fetchIndexerProxies } from 'Store/Actions/settingsActions'; +import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector'; +import createTagsSelector from 'Store/Selectors/createTagsSelector'; +import sortByName from 'Utilities/Array/sortByName'; +import IndexerProxies from './IndexerProxies'; + +function createMapStateToProps() { + return createSelector( + createSortedSectionSelector('settings.indexerProxies', sortByName), + createSortedSectionSelector('indexers', sortByName), + createTagsSelector(), + (indexerProxies, indexers, tagList) => { + return { + ...indexerProxies, + indexerList: indexers.items, + tagList + }; + } + ); +} + +const mapDispatchToProps = { + fetchIndexerProxies, + deleteIndexerProxy +}; + +class IndexerProxiesConnector extends Component { + + // + // Lifecycle + + componentDidMount() { + this.props.fetchIndexerProxies(); + } + + // + // Listeners + + onConfirmDeleteIndexerProxy = (id) => { + this.props.deleteIndexerProxy({ id }); + } + + // + // Render + + render() { + return ( + + ); + } +} + +IndexerProxiesConnector.propTypes = { + fetchIndexerProxies: PropTypes.func.isRequired, + deleteIndexerProxy: PropTypes.func.isRequired +}; + +export default connect(createMapStateToProps, mapDispatchToProps)(IndexerProxiesConnector); diff --git a/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxy.css b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxy.css new file mode 100644 index 000000000..061e6d4bd --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxy.css @@ -0,0 +1,23 @@ +.indexerProxy { + composes: card from '~Components/Card.css'; + + width: 290px; +} + +.name { + @add-mixin truncate; + + margin-bottom: 20px; + font-weight: 300; + font-size: 24px; +} + +.indexers { + flex: 1 0 auto; +} + +.enabled { + display: flex; + flex-wrap: wrap; + margin-top: 5px; +} diff --git a/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxy.js b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxy.js new file mode 100644 index 000000000..18c96144a --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxy.js @@ -0,0 +1,144 @@ +import _ from 'lodash'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import Card from 'Components/Card'; +import Label from 'Components/Label'; +import ConfirmModal from 'Components/Modal/ConfirmModal'; +import TagList from 'Components/TagList'; +import { kinds } from 'Helpers/Props'; +import translate from 'Utilities/String/translate'; +import EditIndexerProxyModalConnector from './EditIndexerProxyModalConnector'; +import styles from './IndexerProxy.css'; + +class IndexerProxy extends Component { + + // + // Lifecycle + + constructor(props, context) { + super(props, context); + + this.state = { + isEditIndexerProxyModalOpen: false, + isDeleteIndexerProxyModalOpen: false + }; + } + + // + // Listeners + + onEditIndexerProxyPress = () => { + this.setState({ isEditIndexerProxyModalOpen: true }); + } + + onEditIndexerProxyModalClose = () => { + this.setState({ isEditIndexerProxyModalOpen: false }); + } + + onDeleteIndexerProxyPress = () => { + this.setState({ + isEditIndexerProxyModalOpen: false, + isDeleteIndexerProxyModalOpen: true + }); + } + + onDeleteIndexerProxyModalClose= () => { + this.setState({ isDeleteIndexerProxyModalOpen: false }); + } + + onConfirmDeleteIndexerProxy = () => { + this.props.onConfirmDeleteIndexerProxy(this.props.id); + } + + // + // Render + + render() { + const { + id, + name, + tags, + tagList, + indexerList + } = this.props; + + return ( + +
+ {name} +
+ + + +
+ { + tags.map((t) => { + const indexers = _.filter(indexerList, { tags: [t] }); + + if (!indexers || indexers.length === 0) { + return null; + } + + return indexers.map((i) => { + return ( + + ); + }); + }) + } +
+ + { + !tags || tags.length === 0 ? + : + null + } + + + + +
+ ); + } +} + +IndexerProxy.propTypes = { + id: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, + tags: PropTypes.arrayOf(PropTypes.number).isRequired, + tagList: PropTypes.arrayOf(PropTypes.object).isRequired, + indexerList: PropTypes.arrayOf(PropTypes.object).isRequired, + onConfirmDeleteIndexerProxy: PropTypes.func.isRequired +}; + +export default IndexerProxy; diff --git a/frontend/src/Settings/Indexers/IndexerSettings.js b/frontend/src/Settings/Indexers/IndexerSettings.js new file mode 100644 index 000000000..ec73bc0aa --- /dev/null +++ b/frontend/src/Settings/Indexers/IndexerSettings.js @@ -0,0 +1,22 @@ +import React from 'react'; +import PageContent from 'Components/Page/PageContent'; +import PageContentBody from 'Components/Page/PageContentBody'; +import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector'; +import translate from 'Utilities/String/translate'; +import IndexerProxiesConnector from './IndexerProxies/IndexerProxiesConnector'; + +function IndexerSettings() { + return ( + + + + + + + + ); +} + +export default IndexerSettings; diff --git a/frontend/src/Settings/Tags/Details/TagDetailsDelayProfile.js b/frontend/src/Settings/Tags/Details/TagDetailsDelayProfile.js deleted file mode 100644 index ab670359b..000000000 --- a/frontend/src/Settings/Tags/Details/TagDetailsDelayProfile.js +++ /dev/null @@ -1,47 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import titleCase from 'Utilities/String/titleCase'; - -function TagDetailsDelayProfile(props) { - const { - preferredProtocol, - enableUsenet, - enableTorrent, - usenetDelay, - torrentDelay - } = props; - - return ( -
-
- Protocol: {titleCase(preferredProtocol)} -
- -
- { - enableUsenet ? - `Usenet Delay: ${usenetDelay}` : - 'Usenet disabled' - } -
- -
- { - enableTorrent ? - `Torrent Delay: ${torrentDelay}` : - 'Torrents disabled' - } -
-
- ); -} - -TagDetailsDelayProfile.propTypes = { - preferredProtocol: PropTypes.string.isRequired, - enableUsenet: PropTypes.bool.isRequired, - enableTorrent: PropTypes.bool.isRequired, - usenetDelay: PropTypes.number.isRequired, - torrentDelay: PropTypes.number.isRequired -}; - -export default TagDetailsDelayProfile; diff --git a/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js b/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js index aa248f158..c65f46974 100644 --- a/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js +++ b/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js @@ -1,26 +1,22 @@ import PropTypes from 'prop-types'; import React from 'react'; import FieldSet from 'Components/FieldSet'; -import Label from 'Components/Label'; import Button from 'Components/Link/Button'; 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 { kinds } from 'Helpers/Props'; -import split from 'Utilities/String/split'; import translate from 'Utilities/String/translate'; -import TagDetailsDelayProfile from './TagDetailsDelayProfile'; import styles from './TagDetailsModalContent.css'; function TagDetailsModalContent(props) { const { label, isTagUsed, - movies, - delayProfiles, + indexers, notifications, - restrictions, + indexerProxies, onModalClose, onDeleteTagPress } = props; @@ -40,13 +36,13 @@ function TagDetailsModalContent(props) { } { - !!movies.length && -
+ !!indexers.length && +
{ - movies.map((item) => { + indexers.map((item) => { return (
- {item.title} + {item.name}
); }) @@ -54,35 +50,6 @@ function TagDetailsModalContent(props) {
} - { - !!delayProfiles.length && -
- { - delayProfiles.map((item) => { - const { - id, - preferredProtocol, - enableUsenet, - enableTorrent, - usenetDelay, - torrentDelay - } = item; - - return ( - - ); - }) - } -
- } - { !!notifications.length &&
@@ -99,44 +66,13 @@ function TagDetailsModalContent(props) { } { - !!restrictions.length && -
+ !!indexerProxies.length && +
{ - restrictions.map((item) => { + indexerProxies.map((item) => { return ( -
-
- { - split(item.required).map((r) => { - return ( - - ); - }) - } -
- -
- { - split(item.ignored).map((i) => { - return ( - - ); - }) - } -
+
+ {item.name}
); }) @@ -171,10 +107,9 @@ function TagDetailsModalContent(props) { TagDetailsModalContent.propTypes = { label: PropTypes.string.isRequired, isTagUsed: PropTypes.bool.isRequired, - movies: PropTypes.arrayOf(PropTypes.object).isRequired, - delayProfiles: PropTypes.arrayOf(PropTypes.object).isRequired, + indexers: PropTypes.arrayOf(PropTypes.object).isRequired, notifications: PropTypes.arrayOf(PropTypes.object).isRequired, - restrictions: PropTypes.arrayOf(PropTypes.object).isRequired, + indexerProxies: PropTypes.arrayOf(PropTypes.object).isRequired, onModalClose: PropTypes.func.isRequired, onDeleteTagPress: PropTypes.func.isRequired }; diff --git a/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js b/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js index 43a1f9b3c..4c67407ba 100644 --- a/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js +++ b/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js @@ -1,6 +1,5 @@ import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import createAllIndexersSelector from 'Store/Selectors/createAllIndexersSelector'; import TagDetailsModalContent from './TagDetailsModalContent'; function findMatchingItems(ids, items) { @@ -9,49 +8,39 @@ function findMatchingItems(ids, items) { }); } -function createUnorderedMatchingMoviesSelector() { +function createMatchingIndexersSelector() { return createSelector( (state, { indexerIds }) => indexerIds, - createAllIndexersSelector(), + (state) => state.indexers.items, findMatchingItems ); } -function createMatchingMoviesSelector() { +function createMatchingIndexerProxiesSelector() { return createSelector( - createUnorderedMatchingMoviesSelector(), - (movies) => { - return movies.sort((movieA, movieB) => { - const sortTitleA = movieA.sortTitle; - const sortTitleB = movieB.sortTitle; - - if (sortTitleA > sortTitleB) { - return 1; - } else if (sortTitleA < sortTitleB) { - return -1; - } - - return 0; - }); - } + (state, { notificationIds }) => notificationIds, + (state) => state.settings.notifications.items, + findMatchingItems ); } function createMatchingNotificationsSelector() { return createSelector( - (state, { notificationIds }) => notificationIds, - (state) => state.settings.notifications.items, + (state, { indexerProxyIds }) => indexerProxyIds, + (state) => state.settings.indexerProxies.items, findMatchingItems ); } function createMapStateToProps() { return createSelector( - createMatchingMoviesSelector(), + createMatchingIndexersSelector(), + createMatchingIndexerProxiesSelector(), createMatchingNotificationsSelector(), - (movies, notifications) => { + (indexers, indexerProxies, notifications) => { return { - movies, + indexers, + indexerProxies, notifications }; } diff --git a/frontend/src/Settings/Tags/Tag.js b/frontend/src/Settings/Tags/Tag.js index 86219ccf6..eed93bd4e 100644 --- a/frontend/src/Settings/Tags/Tag.js +++ b/frontend/src/Settings/Tags/Tag.js @@ -53,11 +53,9 @@ class Tag extends Component { render() { const { label, - delayProfileIds, notificationIds, - restrictionIds, - importListIds, - movieIds + indexerIds, + indexerProxyIds } = this.props; const { @@ -66,11 +64,9 @@ class Tag extends Component { } = this.state; const isTagUsed = !!( - delayProfileIds.length || + indexerIds.length || notificationIds.length || - restrictionIds.length || - importListIds.length || - movieIds.length + indexerProxyIds.length ); return ( @@ -87,16 +83,9 @@ class Tag extends Component { isTagUsed &&
{ - !!movieIds.length && + !!indexerIds.length &&
- {movieIds.length} movies -
- } - - { - !!delayProfileIds.length && -
- {delayProfileIds.length} delay profile{delayProfileIds.length > 1 && 's'} + {indexerIds.length} indexer{indexerIds.length > 1 && 's'}
} @@ -108,16 +97,9 @@ class Tag extends Component { } { - !!restrictionIds.length && -
- {restrictionIds.length} restriction{restrictionIds.length > 1 && 's'} -
- } - - { - !!importListIds.length && + !!indexerProxyIds.length &&
- {importListIds.length} list{importListIds.length > 1 && 's'} + {indexerProxyIds.length} indexerProxy{indexerProxyIds.length > 1 && 's'}
}
@@ -133,11 +115,9 @@ class Tag extends Component { { + return { + section, + ...payload + }; +}); + +export const setIndexerProxyFieldValue = createAction(SET_INDEXER_PROXY_FIELD_VALUE, (payload) => { + return { + section, + ...payload + }; +}); + +// +// Details + +export default { + + // + // State + + defaultState: { + isFetching: false, + isPopulated: false, + error: null, + isSchemaFetching: false, + isSchemaPopulated: false, + schemaError: null, + schema: [], + selectedSchema: {}, + isSaving: false, + saveError: null, + isTesting: false, + items: [], + pendingChanges: {} + }, + + // + // Action Handlers + + actionHandlers: { + [FETCH_INDEXER_PROXYS]: createFetchHandler(section, '/indexerProxy'), + [FETCH_INDEXER_PROXY_SCHEMA]: createFetchSchemaHandler(section, '/indexerProxy/schema'), + + [SAVE_INDEXER_PROXY]: createSaveProviderHandler(section, '/indexerProxy'), + [CANCEL_SAVE_INDEXER_PROXY]: createCancelSaveProviderHandler(section), + [DELETE_INDEXER_PROXY]: createRemoveItemHandler(section, '/indexerProxy'), + [TEST_INDEXER_PROXY]: createTestProviderHandler(section, '/indexerProxy'), + [CANCEL_TEST_INDEXER_PROXY]: createCancelTestProviderHandler(section) + }, + + // + // Reducers + + reducers: { + [SET_INDEXER_PROXY_VALUE]: createSetSettingValueReducer(section), + [SET_INDEXER_PROXY_FIELD_VALUE]: createSetProviderFieldValueReducer(section), + + [SELECT_INDEXER_PROXY_SCHEMA]: (state, { payload }) => { + return selectProviderSchema(state, section, payload, (selectedSchema) => { + return selectedSchema; + }); + } + } + +}; diff --git a/frontend/src/Store/Actions/settingsActions.js b/frontend/src/Store/Actions/settingsActions.js index 09540a2b6..e7fe7247a 100644 --- a/frontend/src/Store/Actions/settingsActions.js +++ b/frontend/src/Store/Actions/settingsActions.js @@ -7,6 +7,7 @@ import development from './Settings/development'; import downloadClients from './Settings/downloadClients'; import general from './Settings/general'; import indexerCategories from './Settings/indexerCategories'; +import indexerProxies from './Settings/indexerProxies'; import languages from './Settings/languages'; import notifications from './Settings/notifications'; import ui from './Settings/ui'; @@ -14,6 +15,7 @@ import ui from './Settings/ui'; export * from './Settings/downloadClients'; export * from './Settings/general'; export * from './Settings/indexerCategories'; +export * from './Settings/indexerProxies'; export * from './Settings/languages'; export * from './Settings/notifications'; export * from './Settings/applications'; @@ -35,6 +37,7 @@ export const defaultState = { downloadClients: downloadClients.defaultState, general: general.defaultState, indexerCategories: indexerCategories.defaultState, + indexerProxies: indexerProxies.defaultState, languages: languages.defaultState, notifications: notifications.defaultState, applications: applications.defaultState, @@ -64,6 +67,7 @@ export const actionHandlers = handleThunks({ ...downloadClients.actionHandlers, ...general.actionHandlers, ...indexerCategories.actionHandlers, + ...indexerProxies.actionHandlers, ...languages.actionHandlers, ...notifications.actionHandlers, ...applications.actionHandlers, @@ -84,6 +88,7 @@ export const reducers = createHandleActions({ ...downloadClients.reducers, ...general.reducers, ...indexerCategories.reducers, + ...indexerProxies.reducers, ...languages.reducers, ...notifications.reducers, ...applications.reducers, diff --git a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs index d51a14929..e20341305 100644 --- a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs +++ b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs @@ -59,7 +59,7 @@ namespace NzbDrone.Common.Http.Dispatchers webRequest.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalMilliseconds); } - webRequest.Proxy = GetProxy(request.Url); + webRequest.Proxy = request.Proxy ?? GetProxy(request.Url); if (request.Headers != null) { diff --git a/src/NzbDrone.Common/Http/HttpClient.cs b/src/NzbDrone.Common/Http/HttpClient.cs index 2e8f7530f..47286b3b0 100644 --- a/src/NzbDrone.Common/Http/HttpClient.cs +++ b/src/NzbDrone.Common/Http/HttpClient.cs @@ -61,7 +61,7 @@ namespace NzbDrone.Common.Http _cookieContainerCache = cacheManager.GetCache(typeof(HttpClient)); } - public async Task ExecuteAsync(HttpRequest request) + public virtual async Task ExecuteAsync(HttpRequest request) { var cookieContainer = InitializeRequestCookies(request); diff --git a/src/NzbDrone.Common/Http/HttpRequest.cs b/src/NzbDrone.Common/Http/HttpRequest.cs index ed09dff85..9f2aab320 100644 --- a/src/NzbDrone.Common/Http/HttpRequest.cs +++ b/src/NzbDrone.Common/Http/HttpRequest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Net; using System.Text; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; @@ -31,6 +32,7 @@ namespace NzbDrone.Common.Http public HttpMethod Method { get; set; } public HttpHeader Headers { get; set; } public Encoding Encoding { get; set; } + public IWebProxy Proxy { get; set; } public byte[] ContentData { get; set; } public string ContentSummary { get; set; } public bool SuppressHttpError { get; set; } @@ -87,6 +89,19 @@ namespace NzbDrone.Common.Http } } + public string GetContent() + { + if (Encoding != null) + { + return Encoding.GetString(ContentData); + } + else + { + var encoding = HttpHeader.GetEncodingFromContentType(Headers.ContentType); + return encoding.GetString(ContentData); + } + } + public void AddBasicAuthentication(string username, string password) { var authInfo = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes($"{username}:{password}")); diff --git a/src/NzbDrone.Core.Test/IndexerTests/AvistazTests/PrivateHDFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/AvistazTests/PrivateHDFixture.cs index c5143bb00..86469cdc8 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/AvistazTests/PrivateHDFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/AvistazTests/PrivateHDFixture.cs @@ -33,9 +33,9 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests { var recentFeed = ReadAllText(@"Files/Indexers/PrivateHD/recentfeed.json"); - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed))); var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases; diff --git a/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListFixture.cs index ce24c5a7b..63570a01e 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListFixture.cs @@ -32,9 +32,9 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests { var recentFeed = ReadAllText(@"Files/Indexers/FileList/recentfeed.json"); - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases; diff --git a/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsFixture.cs index dcb82d705..49feabe43 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsFixture.cs @@ -44,9 +44,9 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests { var responseJson = ReadAllText(fileName); - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.POST))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), responseJson))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.POST), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), responseJson))); var torrents = (await Subject.Fetch(_movieSearchCriteria)).Releases; @@ -73,9 +73,9 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests { var responseJson = new { status = 5, message = "Invalid authentication credentials" }.ToJson(); - Mocker.GetMock() - .Setup(v => v.ExecuteAsync(It.IsAny())) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), Encoding.UTF8.GetBytes(responseJson)))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.IsAny(), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), Encoding.UTF8.GetBytes(responseJson)))); var torrents = (await Subject.Fetch(_movieSearchCriteria)).Releases; diff --git a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabCapabilitiesProviderFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabCapabilitiesProviderFixture.cs index fce49a5a4..03aa7da01 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabCapabilitiesProviderFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabCapabilitiesProviderFixture.cs @@ -1,10 +1,12 @@ using System; using System.Net; +using System.Threading.Tasks; using System.Xml; using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Common.Http; +using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Test.Framework; @@ -14,6 +16,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests public class NewznabCapabilitiesProviderFixture : CoreTest { private NewznabSettings _settings; + private IndexerDefinition _definition; private string _caps; [SetUp] @@ -24,14 +27,24 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests BaseUrl = "http://indxer.local" }; + _definition = new IndexerDefinition() + { + Id = 5, + Name = "Newznab", + Settings = new NewznabSettings() + { + BaseUrl = "http://indexer.local/" + } + }; + _caps = ReadAllText("Files/Indexers/Newznab/newznab_caps.xml"); } private void GivenCapsResponse(string caps) { - Mocker.GetMock() - .Setup(o => o.Get(It.IsAny())) - .Returns(r => new HttpResponse(r, new HttpHeader(), new CookieCollection(), caps)); + Mocker.GetMock() + .Setup(o => o.Execute(It.IsAny(), It.IsAny())) + .Returns((r, d) => new HttpResponse(r, new HttpHeader(), new CookieCollection(), caps)); } [Test] @@ -39,11 +52,11 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests { GivenCapsResponse(_caps); - Subject.GetCapabilities(_settings); - Subject.GetCapabilities(_settings); + Subject.GetCapabilities(_settings, _definition); + Subject.GetCapabilities(_settings, _definition); - Mocker.GetMock() - .Verify(o => o.Get(It.IsAny()), Times.Once()); + Mocker.GetMock() + .Verify(o => o.Execute(It.IsAny(), It.IsAny()), Times.Once()); } [Test] @@ -51,7 +64,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests { GivenCapsResponse(_caps); - var caps = Subject.GetCapabilities(_settings); + var caps = Subject.GetCapabilities(_settings, _definition); caps.LimitsDefault.Value.Should().Be(25); caps.LimitsMax.Value.Should().Be(60); @@ -62,7 +75,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests { GivenCapsResponse(_caps.Replace("() - .Setup(o => o.Get(It.IsAny())) + Mocker.GetMock() + .Setup(o => o.Execute(It.IsAny(), It.IsAny())) .Throws(); - Assert.Throws(() => Subject.GetCapabilities(_settings)); + Assert.Throws(() => Subject.GetCapabilities(_settings, _definition)); } [Test] @@ -83,7 +96,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests { GivenCapsResponse(_caps.Replace("")); - Assert.Throws(() => Subject.GetCapabilities(_settings)); + Assert.Throws(() => Subject.GetCapabilities(_settings, _definition)); } [Test] @@ -91,7 +104,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests { GivenCapsResponse(_caps.Replace("5030", "asdf")); - var result = Subject.GetCapabilities(_settings); + var result = Subject.GetCapabilities(_settings, _definition); result.Should().NotBeNull(); } diff --git a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabFixture.cs index a3d5a8d14..77120b8d4 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabFixture.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests _caps = new IndexerCapabilities(); Mocker.GetMock() - .Setup(v => v.GetCapabilities(It.IsAny())) + .Setup(v => v.GetCapabilities(It.IsAny(), It.IsAny())) .Returns(_caps); } @@ -42,9 +42,9 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests { var recentFeed = ReadAllText(@"Files/Indexers/Newznab/newznab_nzb_su.xml"); - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 }, Limit = 100, Offset = 0 })).Releases; diff --git a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs index 1f097a7ed..ead7db168 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs @@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests _capabilities = new IndexerCapabilities(); Mocker.GetMock() - .Setup(v => v.GetCapabilities(It.IsAny())) + .Setup(v => v.GetCapabilities(It.IsAny(), It.IsAny())) .Returns(_capabilities); } diff --git a/src/NzbDrone.Core.Test/IndexerTests/PTPTests/PTPFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/PTPTests/PTPFixture.cs index 3a04ebee6..736386139 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/PTPTests/PTPFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/PTPTests/PTPFixture.cs @@ -36,13 +36,13 @@ namespace NzbDrone.Core.Test.IndexerTests.PTPTests Json.Serialize(authResponse, authStream); var responseJson = ReadAllText(fileName); - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.POST))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), authStream.ToString()))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.POST), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), authStream.ToString()))); - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader { ContentType = HttpAccept.Json.Value }, new CookieCollection(), responseJson))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { ContentType = HttpAccept.Json.Value }, new CookieCollection(), responseJson))); var torrents = (await Subject.Fetch(new MovieSearchCriteria())).Releases; diff --git a/src/NzbDrone.Core.Test/IndexerTests/RarbgTests/RarbgFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/RarbgTests/RarbgFixture.cs index 7fcfe1cdf..7d6fd31d5 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/RarbgTests/RarbgFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/RarbgTests/RarbgFixture.cs @@ -37,9 +37,9 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests { var recentFeed = ReadAllText(@"Files/Indexers/Rarbg/RecentFeed_v2.json"); - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases; @@ -64,9 +64,9 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests [Test] public async Task should_parse_error_20_as_empty_results() { - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), "{ error_code: 20, error: \"some message\" }"))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), "{ error_code: 20, error: \"some message\" }"))); var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases; @@ -76,9 +76,9 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests [Test] public async Task should_warn_on_unknown_error() { - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), "{ error_code: 25, error: \"some message\" }"))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), "{ error_code: 25, error: \"some message\" }"))); var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases; diff --git a/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs b/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs index c88fa7f22..969f0c014 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs @@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.IndexerTests public int _supportedPageSize; public override int PageSize => _supportedPageSize; - public TestIndexer(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) + public TestIndexer(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger) { } diff --git a/src/NzbDrone.Core.Test/IndexerTests/TorznabTests/TorznabFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/TorznabTests/TorznabFixture.cs index 8922e36be..444ad4433 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/TorznabTests/TorznabFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/TorznabTests/TorznabFixture.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests _caps = new IndexerCapabilities(); Mocker.GetMock() - .Setup(v => v.GetCapabilities(It.IsAny())) + .Setup(v => v.GetCapabilities(It.IsAny(), It.IsAny())) .Returns(_caps); } @@ -43,9 +43,9 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests { var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_hdaccess_net.xml"); - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); var releases = (await Subject.Fetch(new MovieSearchCriteria())).Releases; @@ -72,9 +72,9 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests { var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml"); - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); var releases = (await Subject.Fetch(new MovieSearchCriteria())).Releases; @@ -102,9 +102,9 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests { var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_animetosho.xml"); - Mocker.GetMock() - .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); + Mocker.GetMock() + .Setup(o => o.ExecuteAsync(It.Is(v => v.Method == HttpMethod.GET), Subject.Definition)) + .Returns((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed))); var releases = (await Subject.Fetch(new MovieSearchCriteria())).Releases; diff --git a/src/NzbDrone.Core/Datastore/Migration/010_indexer_proxies.cs b/src/NzbDrone.Core/Datastore/Migration/010_indexer_proxies.cs new file mode 100644 index 000000000..e6fb9a703 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/010_indexer_proxies.cs @@ -0,0 +1,21 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(10)] + public class IndexerProxies : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Create.TableForModel("IndexerProxies") + .WithColumn("Name").AsString() + .WithColumn("Settings").AsString() + .WithColumn("Implementation").AsString() + .WithColumn("ConfigContract").AsString().Nullable() + .WithColumn("Tags").AsString().Nullable(); + + Alter.Table("Indexers").AddColumn("Tags").AsString().Nullable(); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 1ab5e9994..74e6c6b28 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -9,6 +9,7 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.CustomFilters; using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Download; +using NzbDrone.Core.IndexerProxies; using NzbDrone.Core.Indexers; using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Jobs; @@ -53,7 +54,6 @@ namespace NzbDrone.Core.Datastore .Ignore(i => i.SupportsSearch) .Ignore(i => i.SupportsRedirect) .Ignore(i => i.Capabilities) - .Ignore(d => d.Tags) .HasOne(a => a.AppProfile, a => a.AppProfileId); Mapper.Entity("DownloadClients").RegisterModel() @@ -65,6 +65,9 @@ namespace NzbDrone.Core.Datastore .Ignore(x => x.ImplementationName) .Ignore(i => i.SupportsOnHealthIssue); + Mapper.Entity("IndexerProxies").RegisterModel() + .Ignore(x => x.ImplementationName); + Mapper.Entity("Applications").RegisterModel() .Ignore(x => x.ImplementationName); diff --git a/src/NzbDrone.Core/HealthCheck/Checks/IndexerProxyCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/IndexerProxyCheck.cs new file mode 100644 index 000000000..1a48fc6ca --- /dev/null +++ b/src/NzbDrone.Core/HealthCheck/Checks/IndexerProxyCheck.cs @@ -0,0 +1,92 @@ +using System; +using System.Linq; +using System.Net; +using NLog; +using NzbDrone.Common.Cloud; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Core.IndexerProxies; +using NzbDrone.Core.Localization; + +namespace NzbDrone.Core.HealthCheck.Checks +{ + public class IndexerProxyCheck : HealthCheckBase + { + private readonly Logger _logger; + private readonly IIndexerProxyFactory _proxyFactory; + private readonly IHttpClient _client; + + private readonly IHttpRequestBuilderFactory _cloudRequestBuilder; + + public IndexerProxyCheck(IProwlarrCloudRequestBuilder cloudRequestBuilder, + IHttpClient client, + IIndexerProxyFactory proxyFactory, + ILocalizationService localizationService, + Logger logger) + : base(localizationService) + { + _proxyFactory = proxyFactory; + _cloudRequestBuilder = cloudRequestBuilder.Services; + _logger = logger; + _client = client; + } + + public override HealthCheck Check() + { + var enabledProviders = _proxyFactory.GetAvailableProviders(); + + var badProxies = enabledProviders.Where(p => !IsProxyWorking(p)).ToList(); + + if (enabledProviders.Empty()) + { + return new HealthCheck(GetType()); + } + + if (badProxies.Count == enabledProviders.Count) + { + return new HealthCheck(GetType(), + HealthCheckResult.Error, + _localizationService.GetLocalizedString("IndexerProxyStatusCheckAllClientMessage"), + "#proxies-are-unavailable-due-to-failures"); + } + + return new HealthCheck(GetType(), + HealthCheckResult.Warning, + string.Format(_localizationService.GetLocalizedString("IndexerProxyStatusCheckSingleClientMessage"), + string.Join(", ", badProxies.Select(v => v.Definition.Name))), + "#proxies-are-unavailable-due-to-failures"); + } + + private bool IsProxyWorking(IIndexerProxy indexerProxy) + { + var request = _cloudRequestBuilder.Create() + .Resource("/ping") + .Build(); + + try + { + var addresses = Dns.GetHostAddresses(((IIndexerProxySettings)indexerProxy.Definition.Settings).Host); + if (!addresses.Any()) + { + return false; + } + + var response = _client.Execute(request); + + // We only care about 400 responses, other error codes can be ignored + if (response.StatusCode == HttpStatusCode.BadRequest) + { + _logger.Error("Proxy Health Check failed: {0}", response.StatusCode); + return false; + } + } + catch (Exception ex) + { + _logger.Error(ex, "Proxy Health Check failed"); + return false; + } + + return true; + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/Http/Http.cs b/src/NzbDrone.Core/IndexerProxies/Http/Http.cs new file mode 100644 index 000000000..03ab6a802 --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/Http/Http.cs @@ -0,0 +1,34 @@ +using System.Net; +using NLog; +using NzbDrone.Common.Cloud; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.IndexerProxies.Http +{ + public class Http : HttpIndexerProxyBase + { + public Http(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger) + : base(cloudRequestBuilder, httpClient, logger) + { + } + + public override string Name => "Http"; + + public override HttpRequest PreRequest(HttpRequest request) + { + if (Settings.Username.IsNotNullOrWhiteSpace() && Settings.Password.IsNotNullOrWhiteSpace()) + { + request.Proxy = new WebProxy(Settings.Host + ":" + Settings.Port, false, null, new NetworkCredential(Settings.Username, Settings.Password)); + } + else + { + request.Proxy = new WebProxy(Settings.Host + ":" + Settings.Port, false, null); + } + + _logger.Debug("Applying HTTP(S) Proxy {0} to request {1}", Name, request.Url); + + return request; + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/Http/HttpSettings.cs b/src/NzbDrone.Core/IndexerProxies/Http/HttpSettings.cs new file mode 100644 index 000000000..4ba1d65bb --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/Http/HttpSettings.cs @@ -0,0 +1,43 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.IndexerProxies.Http +{ + public class HttpSettingsValidator : AbstractValidator + { + public HttpSettingsValidator() + { + RuleFor(c => c.Host).NotEmpty(); + RuleFor(c => c.Port).NotEmpty(); + } + } + + public class HttpSettings : IIndexerProxySettings + { + private static readonly HttpSettingsValidator Validator = new HttpSettingsValidator(); + + public HttpSettings() + { + Username = ""; + Password = ""; + } + + [FieldDefinition(0, Label = "Host")] + public string Host { get; set; } + + [FieldDefinition(1, Label = "Port")] + public int Port { get; set; } + + [FieldDefinition(2, Label = "Username", Privacy = PrivacyLevel.UserName)] + public string Username { get; set; } + + [FieldDefinition(3, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] + public string Password { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/HttpIndexerProxyBase.cs b/src/NzbDrone.Core/IndexerProxies/HttpIndexerProxyBase.cs new file mode 100644 index 000000000..5868ef412 --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/HttpIndexerProxyBase.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using FluentValidation.Results; +using NLog; +using NzbDrone.Common.Cloud; +using NzbDrone.Common.Http; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.IndexerProxies +{ + public abstract class HttpIndexerProxyBase : IndexerProxyBase + where TSettings : IIndexerProxySettings, new() + { + protected readonly IHttpClient _httpClient; + protected readonly IHttpRequestBuilderFactory _cloudRequestBuilder; + protected readonly Logger _logger; + + public HttpIndexerProxyBase(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger) + { + _httpClient = httpClient; + _logger = logger; + _cloudRequestBuilder = cloudRequestBuilder.Services; + } + + public override ValidationResult Test() + { + var failures = new List(); + + var addresses = Dns.GetHostAddresses(Settings.Host); + if (!addresses.Any()) + { + failures.Add(new NzbDroneValidationFailure("Host", "ProxyCheckResolveIpMessage")); + } + + var request = PreRequest(_cloudRequestBuilder.Create() + .Resource("/ping") + .Build()); + + try + { + var response = _httpClient.Execute(request); + + // We only care about 400 responses, other error codes can be ignored + if (response.StatusCode == HttpStatusCode.BadRequest) + { + _logger.Error("Proxy Health Check failed: {0}", response.StatusCode); + failures.Add(new NzbDroneValidationFailure("Host", "ProxyCheckBadRequestMessage")); + } + } + catch (Exception ex) + { + _logger.Error(ex, "Proxy Health Check failed"); + failures.Add(new NzbDroneValidationFailure("Host", "ProxyCheckFailedToTestMessage")); + } + + return new ValidationResult(failures); + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/IIndexerProxy.cs b/src/NzbDrone.Core/IndexerProxies/IIndexerProxy.cs new file mode 100644 index 000000000..a814d564f --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/IIndexerProxy.cs @@ -0,0 +1,11 @@ +using NzbDrone.Common.Http; +using NzbDrone.Core.ThingiProvider; + +namespace NzbDrone.Core.IndexerProxies +{ + public interface IIndexerProxy : IProvider + { + HttpRequest PreRequest(HttpRequest request); + HttpResponse PostResponse(HttpResponse response); + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/IIndexerProxySettings.cs b/src/NzbDrone.Core/IndexerProxies/IIndexerProxySettings.cs new file mode 100644 index 000000000..932e42f89 --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/IIndexerProxySettings.cs @@ -0,0 +1,9 @@ +using NzbDrone.Core.ThingiProvider; + +namespace NzbDrone.Core.IndexerProxies +{ + public interface IIndexerProxySettings : IProviderConfig + { + string Host { get; set; } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/IndexerProxyBase.cs b/src/NzbDrone.Core/IndexerProxies/IndexerProxyBase.cs new file mode 100644 index 000000000..b4c82808d --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/IndexerProxyBase.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using FluentValidation.Results; +using NzbDrone.Common.Http; +using NzbDrone.Core.ThingiProvider; + +namespace NzbDrone.Core.IndexerProxies +{ + public abstract class IndexerProxyBase : IIndexerProxy + where TSettings : IIndexerProxySettings, new() + { + public abstract string Name { get; } + public virtual ProviderMessage Message => null; + + public Type ConfigContract => typeof(TSettings); + + public IEnumerable DefaultDefinitions => new List(); + + public ProviderDefinition Definition { get; set; } + public abstract ValidationResult Test(); + + public abstract HttpRequest PreRequest(HttpRequest request); + public virtual HttpResponse PostResponse(HttpResponse response) + { + return response; + } + + protected TSettings Settings => (TSettings)Definition.Settings; + + public override string ToString() + { + return GetType().Name; + } + + public virtual object RequestAction(string action, IDictionary query) + { + return null; + } + + private bool HasConcreteImplementation(string methodName) + { + var method = GetType().GetMethod(methodName); + + if (method == null) + { + throw new MissingMethodException(GetType().Name, Name); + } + + return !method.DeclaringType.IsAbstract; + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/IndexerProxyDefinition.cs b/src/NzbDrone.Core/IndexerProxies/IndexerProxyDefinition.cs new file mode 100644 index 000000000..6fb2c7ff2 --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/IndexerProxyDefinition.cs @@ -0,0 +1,10 @@ +using System.Linq; +using NzbDrone.Core.ThingiProvider; + +namespace NzbDrone.Core.IndexerProxies +{ + public class IndexerProxyDefinition : ProviderDefinition + { + public override bool Enable => Tags.Any(); + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/IndexerProxyFactory.cs b/src/NzbDrone.Core/IndexerProxies/IndexerProxyFactory.cs new file mode 100644 index 000000000..c21d9e2f4 --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/IndexerProxyFactory.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NLog; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.ThingiProvider; + +namespace NzbDrone.Core.IndexerProxies +{ + public interface IIndexerProxyFactory : IProviderFactory + { + } + + public class IndexerProxyFactory : ProviderFactory, IIndexerProxyFactory + { + public IndexerProxyFactory(IIndexerProxyRepository providerRepository, IEnumerable providers, IServiceProvider container, IEventAggregator eventAggregator, Logger logger) + : base(providerRepository, providers, container, eventAggregator, logger) + { + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/IndexerProxyRepository.cs b/src/NzbDrone.Core/IndexerProxies/IndexerProxyRepository.cs new file mode 100644 index 000000000..7b471a031 --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/IndexerProxyRepository.cs @@ -0,0 +1,24 @@ +using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.ThingiProvider; + +namespace NzbDrone.Core.IndexerProxies +{ + public interface IIndexerProxyRepository : IProviderRepository + { + void UpdateSettings(IndexerProxyDefinition model); + } + + public class IndexerProxyRepository : ProviderRepository, IIndexerProxyRepository + { + public IndexerProxyRepository(IMainDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) + { + } + + public void UpdateSettings(IndexerProxyDefinition model) + { + SetFields(model, m => m.Settings); + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/IndexerProxyService.cs b/src/NzbDrone.Core/IndexerProxies/IndexerProxyService.cs new file mode 100644 index 000000000..5a86d1fb8 --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/IndexerProxyService.cs @@ -0,0 +1,19 @@ +using System; +using NLog; +using NzbDrone.Core.HealthCheck; +using NzbDrone.Core.Messaging.Events; + +namespace NzbDrone.Core.IndexerProxies +{ + public class IndexerProxyService + { + private readonly IIndexerProxyFactory _indexerProxyFactory; + private readonly Logger _logger; + + public IndexerProxyService(IIndexerProxyFactory indexerProxyFactory, Logger logger) + { + _indexerProxyFactory = indexerProxyFactory; + _logger = logger; + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/Socks4/Socks4.cs b/src/NzbDrone.Core/IndexerProxies/Socks4/Socks4.cs new file mode 100644 index 000000000..fa32e1673 --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/Socks4/Socks4.cs @@ -0,0 +1,66 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using com.LandonKey.SocksWebProxy; +using com.LandonKey.SocksWebProxy.Proxy; +using NLog; +using NzbDrone.Common.Cloud; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.IndexerProxies.Socks4 +{ + public class Socks4 : HttpIndexerProxyBase + { + public Socks4(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger) + : base(cloudRequestBuilder, httpClient, logger) + { + } + + public override string Name => "Socks4"; + public override HttpRequest PreRequest(HttpRequest request) + { + if (Settings.Username.IsNotNullOrWhiteSpace() && Settings.Password.IsNotNullOrWhiteSpace()) + { + request.Proxy = new SocksWebProxy(new ProxyConfig(IPAddress.Loopback, GetNextFreePort(), GetProxyIpAddress(Settings.Host), Settings.Port, ProxyConfig.SocksVersion.Four), false); + } + else + { + request.Proxy = new SocksWebProxy(new ProxyConfig(IPAddress.Loopback, GetNextFreePort(), GetProxyIpAddress(Settings.Host), Settings.Port, ProxyConfig.SocksVersion.Four, Settings.Username, Settings.Password), false); + } + + _logger.Debug("Applying Socks4 Proxy {0} to request {1}", Name, request.Url); + + return request; + } + + private static int GetNextFreePort() + { + var listener = new TcpListener(IPAddress.Loopback, 0); + listener.Start(); + var port = ((IPEndPoint)listener.LocalEndpoint).Port; + listener.Stop(); + + return port; + } + + private static IPAddress GetProxyIpAddress(string host) + { + IPAddress ipAddress; + if (!IPAddress.TryParse(host, out ipAddress)) + { + try + { + ipAddress = Dns.GetHostEntry(host).AddressList.OrderByDescending(a => a.AddressFamily == AddressFamily.InterNetwork).First(); + } + catch (Exception e) + { + throw new InvalidOperationException(string.Format("Unable to resolve proxy hostname '{0}' to a valid IP address.", host), e); + } + } + + return ipAddress; + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/Socks4/Socks4Settings.cs b/src/NzbDrone.Core/IndexerProxies/Socks4/Socks4Settings.cs new file mode 100644 index 000000000..694b8fdea --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/Socks4/Socks4Settings.cs @@ -0,0 +1,43 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.IndexerProxies.Socks4 +{ + public class Socks4SettingsValidator : AbstractValidator + { + public Socks4SettingsValidator() + { + RuleFor(c => c.Host).NotEmpty(); + RuleFor(c => c.Port).NotEmpty(); + } + } + + public class Socks4Settings : IIndexerProxySettings + { + private static readonly Socks4SettingsValidator Validator = new Socks4SettingsValidator(); + + public Socks4Settings() + { + Username = ""; + Password = ""; + } + + [FieldDefinition(0, Label = "Host")] + public string Host { get; set; } + + [FieldDefinition(1, Label = "Port")] + public int Port { get; set; } + + [FieldDefinition(2, Label = "Username", Privacy = PrivacyLevel.UserName)] + public string Username { get; set; } + + [FieldDefinition(3, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] + public string Password { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/Socks5/Socks5.cs b/src/NzbDrone.Core/IndexerProxies/Socks5/Socks5.cs new file mode 100644 index 000000000..5c3727972 --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/Socks5/Socks5.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using com.LandonKey.SocksWebProxy; +using com.LandonKey.SocksWebProxy.Proxy; +using NLog; +using NzbDrone.Common.Cloud; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.IndexerProxies.Socks5 +{ + public class Socks5 : HttpIndexerProxyBase + { + public Socks5(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger) + : base(cloudRequestBuilder, httpClient, logger) + { + } + + public override string Name => "Socks5"; + + public override HttpRequest PreRequest(HttpRequest request) + { + if (Settings.Username.IsNotNullOrWhiteSpace() && Settings.Password.IsNotNullOrWhiteSpace()) + { + request.Proxy = new SocksWebProxy(new ProxyConfig(IPAddress.Loopback, GetNextFreePort(), GetProxyIpAddress(Settings.Host), Settings.Port, ProxyConfig.SocksVersion.Five), false); + } + else + { + request.Proxy = new SocksWebProxy(new ProxyConfig(IPAddress.Loopback, GetNextFreePort(), GetProxyIpAddress(Settings.Host), Settings.Port, ProxyConfig.SocksVersion.Five, Settings.Username, Settings.Password), false); + } + + _logger.Debug("Applying Socks5 Proxy {0} to request {1}", Name, request.Url); + + return request; + } + + private static int GetNextFreePort() + { + var listener = new TcpListener(IPAddress.Loopback, 0); + listener.Start(); + var port = ((IPEndPoint)listener.LocalEndpoint).Port; + listener.Stop(); + + return port; + } + + private static IPAddress GetProxyIpAddress(string host) + { + IPAddress ipAddress; + if (!IPAddress.TryParse(host, out ipAddress)) + { + try + { + ipAddress = Dns.GetHostEntry(host).AddressList.OrderByDescending(a => a.AddressFamily == AddressFamily.InterNetwork).First(); + } + catch (Exception e) + { + throw new InvalidOperationException(string.Format("Unable to resolve proxy hostname '{0}' to a valid IP address.", host), e); + } + } + + return ipAddress; + } + } +} diff --git a/src/NzbDrone.Core/IndexerProxies/Socks5/Socks5Settings.cs b/src/NzbDrone.Core/IndexerProxies/Socks5/Socks5Settings.cs new file mode 100644 index 000000000..071881441 --- /dev/null +++ b/src/NzbDrone.Core/IndexerProxies/Socks5/Socks5Settings.cs @@ -0,0 +1,43 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.IndexerProxies.Socks5 +{ + public class Socks5SettingsValidator : AbstractValidator + { + public Socks5SettingsValidator() + { + RuleFor(c => c.Host).NotEmpty(); + RuleFor(c => c.Port).NotEmpty(); + } + } + + public class Socks5Settings : IIndexerProxySettings + { + private static readonly Socks5SettingsValidator Validator = new Socks5SettingsValidator(); + + public Socks5Settings() + { + Username = ""; + Password = ""; + } + + [FieldDefinition(0, Label = "Host")] + public string Host { get; set; } + + [FieldDefinition(1, Label = "Port")] + public int Port { get; set; } + + [FieldDefinition(2, Label = "Username", Privacy = PrivacyLevel.UserName)] + public string Username { get; set; } + + [FieldDefinition(3, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] + public string Password { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Aither.cs b/src/NzbDrone.Core/Indexers/Definitions/Aither.cs index ea6d3a5b8..1aff169c4 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Aither.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Aither.cs @@ -15,7 +15,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Language => "en-us"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public Aither(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Aither(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs b/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs index 90390fbdf..1b1329374 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Description => "AlphaRatio(AR) is a Private Torrent Tracker for 0DAY / GENERAL"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public AlphaRatio(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public AlphaRatio(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Anidub.cs b/src/NzbDrone.Core/Indexers/Definitions/Anidub.cs index 278e2040f..c31bae08e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Anidub.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Anidub.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.SemiPublic; public override IndexerCapabilities Capabilities => SetCapabilities(); - public Anidub(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Anidub(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } @@ -58,7 +58,7 @@ namespace NzbDrone.Core.Indexers.Definitions AllowAutoRedirect = true }; - var mainPage = await _httpClient.ExecuteAsync(new HttpRequest(Settings.BaseUrl)); + var mainPage = await ExecuteAuth(new HttpRequest(Settings.BaseUrl)); requestBuilder.Method = Common.Http.HttpMethod.POST; requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); @@ -254,7 +254,7 @@ namespace NzbDrone.Core.Indexers.Definitions { private readonly AnidubSettings _settings; private readonly IndexerCapabilitiesCategories _categories; - public IHttpClient HttpClient { get; set; } + public IIndexerHttpClient HttpClient { get; set; } public Logger Logger { get; set; } private static Dictionary CategoriesMap => new Dictionary diff --git a/src/NzbDrone.Core/Indexers/Definitions/Anilibria.cs b/src/NzbDrone.Core/Indexers/Definitions/Anilibria.cs index 464e392a8..fd8469149 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Anilibria.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Anilibria.cs @@ -28,7 +28,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Public; public override IndexerCapabilities Capabilities => SetCapabilities(); - public Anilibria(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Anilibria(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/AnimeBytes.cs b/src/NzbDrone.Core/Indexers/Definitions/AnimeBytes.cs index 54318e20f..d0f9135b6 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AnimeBytes.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AnimeBytes.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public AnimeBytes(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public AnimeBytes(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/AnimeTorrents.cs b/src/NzbDrone.Core/Indexers/Definitions/AnimeTorrents.cs index e68495616..600205049 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AnimeTorrents.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AnimeTorrents.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public AnimeTorrents(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public AnimeTorrents(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } @@ -56,7 +56,7 @@ namespace NzbDrone.Core.Indexers.Definitions AllowAutoRedirect = true }; - var loginPage = await _httpClient.ExecuteAsync(new HttpRequest(LoginUrl)); + var loginPage = await ExecuteAuth(new HttpRequest(LoginUrl)); requestBuilder.Method = HttpMethod.POST; requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); requestBuilder.SetCookies(loginPage.GetCookies()); diff --git a/src/NzbDrone.Core/Indexers/Definitions/AnimeWorld.cs b/src/NzbDrone.Core/Indexers/Definitions/AnimeWorld.cs index c6eb1a217..55f871a0e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AnimeWorld.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AnimeWorld.cs @@ -15,7 +15,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Language => "de-de"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public AnimeWorld(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public AnimeWorld(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Animedia.cs b/src/NzbDrone.Core/Indexers/Definitions/Animedia.cs index 1a6275347..7d8297d69 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Animedia.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Animedia.cs @@ -29,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Public; public override IndexerCapabilities Capabilities => SetCapabilities(); - public Animedia(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Animedia(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } @@ -153,7 +153,7 @@ namespace NzbDrone.Core.Indexers.Definitions private static readonly Regex CategorieMovieRegex = new Regex(@"Фильм", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex CategorieOVARegex = new Regex(@"ОВА|OVA|ОНА|ONA|Special", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex CategorieDoramaRegex = new Regex(@"Дорама", RegexOptions.Compiled | RegexOptions.IgnoreCase); - public IHttpClient HttpClient { get; set; } + public IIndexerHttpClient HttpClient { get; set; } public Logger Logger { get; set; } public AnimediaParser(AnimediaSettings settings, IndexerCapabilitiesCategories categories) diff --git a/src/NzbDrone.Core/Indexers/Definitions/Anthelion.cs b/src/NzbDrone.Core/Indexers/Definitions/Anthelion.cs index 6b4178183..b99807cdc 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Anthelion.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Anthelion.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public Anthelion(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Anthelion(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/AvistaZ.cs b/src/NzbDrone.Core/Indexers/Definitions/AvistaZ.cs index ec55fd785..97dda7e42 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AvistaZ.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AvistaZ.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Description => "Aka AsiaTorrents"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public AvistaZ(IIndexerRepository indexerRepository, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public AvistaZ(IIndexerRepository indexerRepository, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(indexerRepository, httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazBase.cs b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazBase.cs index 3e39259c8..f231fae7c 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazBase.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazBase.cs @@ -21,7 +21,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz private IIndexerRepository _indexerRepository; public AvistazBase(IIndexerRepository indexerRepository, - IHttpClient httpClient, + IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, diff --git a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazRequestGenerator.cs index 81f9b3c42..ab8d086b7 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazRequestGenerator.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz public AvistazSettings Settings { get; set; } public IDictionary AuthCookieCache { get; set; } - public IHttpClient HttpClient { get; set; } + public IIndexerHttpClient HttpClient { get; set; } public IndexerCapabilities Capabilities { get; set; } public Logger Logger { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/BB.cs b/src/NzbDrone.Core/Indexers/Definitions/BB.cs index cb8114d93..b6ecf0c38 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/BB.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/BB.cs @@ -32,7 +32,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public BB(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public BB(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/BakaBT.cs b/src/NzbDrone.Core/Indexers/Definitions/BakaBT.cs index bc8e123c4..4266f7889 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/BakaBT.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/BakaBT.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public BakaBT(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public BakaBT(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } @@ -56,7 +56,7 @@ namespace NzbDrone.Core.Indexers.Definitions AllowAutoRedirect = true }; - var loginPage = await _httpClient.ExecuteAsync(new HttpRequest(LoginUrl)); + var loginPage = await ExecuteAuth(new HttpRequest(LoginUrl)); requestBuilder.Method = HttpMethod.POST; requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); @@ -78,7 +78,7 @@ namespace NzbDrone.Core.Indexers.Definitions .SetHeader("Content-Type", "multipart/form-data") .Build(); - var response = await _httpClient.ExecuteAsync(authLoginRequest); + var response = await ExecuteAuth(authLoginRequest); if (response.Content != null && response.Content.Contains("Logout")) { diff --git a/src/NzbDrone.Core/Indexers/Definitions/BeyondHD.cs b/src/NzbDrone.Core/Indexers/Definitions/BeyondHD.cs index 3337c89b6..acda788be 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/BeyondHD.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/BeyondHD.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public BeyondHD(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public BeyondHD(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/BinSearch.cs b/src/NzbDrone.Core/Indexers/Definitions/BinSearch.cs index dd1ed9d45..a2cfdc38f 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/BinSearch.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/BinSearch.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override bool SupportsRss => false; public override IndexerCapabilities Capabilities => SetCapabilities(); - public BinSearch(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) + public BinSearch(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Blutopia.cs b/src/NzbDrone.Core/Indexers/Definitions/Blutopia.cs index 72152ed02..45302f0d9 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Blutopia.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Blutopia.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Description => "Blutopia (BLU) is a Private Torrent Tracker for HD MOVIES / TV"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public Blutopia(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Blutopia(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNet.cs b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNet.cs index 7664792af..bf5762aaa 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNet.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNet.cs @@ -20,7 +20,7 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet public override string[] IndexerUrls => new string[] { "http://api.broadcasthe.net/" }; public override string Description => "BroadcasTheNet (BTN) is an invite-only torrent tracker focused on TV shows"; - public BroadcastheNet(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public BroadcastheNet(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/BrokenStones.cs b/src/NzbDrone.Core/Indexers/Definitions/BrokenStones.cs index 1be45bf2c..1ab58c181 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/BrokenStones.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/BrokenStones.cs @@ -12,7 +12,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Description => "Broken Stones is a Private site for MacOS and iOS APPS / GAMES"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public BrokenStones(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public BrokenStones(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/CGPeers.cs b/src/NzbDrone.Core/Indexers/Definitions/CGPeers.cs index 61f7baaed..c82b62fa2 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/CGPeers.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/CGPeers.cs @@ -12,7 +12,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Description => "CGPeers is a Private Torrent Tracker for GRAPHICS SOFTWARE / TUTORIALS / ETC"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public CGPeers(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public CGPeers(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs index 07d2e5b0d..3ba142241 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs @@ -41,6 +41,7 @@ namespace NzbDrone.Core.Indexers.Cardigann _logger) { HttpClient = _httpClient, + Definition = Definition, Settings = Settings }); @@ -85,7 +86,7 @@ namespace NzbDrone.Core.Indexers.Cardigann } public Cardigann(IIndexerDefinitionUpdateService definitionService, - IHttpClient httpClient, + IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, @@ -176,7 +177,7 @@ namespace NzbDrone.Core.Indexers.Cardigann try { - var response = await _httpClient.ExecuteAsync(request); + var response = await _httpClient.ExecuteAsync(request, Definition); downloadBytes = response.ResponseData; } catch (HttpException ex) diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs index 52aa156bd..644229ad2 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs @@ -13,12 +13,14 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Definitions.Cardigann; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser; +using NzbDrone.Core.ThingiProvider; namespace NzbDrone.Core.Indexers.Cardigann { public class CardigannRequestGenerator : CardigannBase, IIndexerRequestGenerator { - public IHttpClient HttpClient { get; set; } + public IIndexerHttpClient HttpClient { get; set; } + public ProviderDefinition Definition { get; set; } public IDictionary Cookies { get; set; } protected HttpResponse landingResult; protected IHtmlDocument landingResultDocument; @@ -192,7 +194,7 @@ namespace NzbDrone.Core.Indexers.Cardigann requestBuilder.Headers.Add("Referer", SiteLink); - var response = await HttpClient.ExecuteAsync(requestBuilder.Build()); + var response = await HttpClient.ExecuteAsync(requestBuilder.Build(), Definition); Cookies = response.GetCookies(); @@ -329,7 +331,7 @@ namespace NzbDrone.Core.Indexers.Cardigann requestBuilder.Headers.Add("Referer", loginUrl); - var simpleCaptchaResult = await HttpClient.ExecuteAsync(requestBuilder.Build()); + var simpleCaptchaResult = await HttpClient.ExecuteAsync(requestBuilder.Build(), Definition); var simpleCaptchaJSON = JObject.Parse(simpleCaptchaResult.Content); var captchaSelection = simpleCaptchaJSON["images"][0]["hash"].ToString(); @@ -409,7 +411,7 @@ namespace NzbDrone.Core.Indexers.Cardigann var request = requestBuilder.Build(); request.SetContent(body); - loginResult = await HttpClient.ExecuteAsync(request); + loginResult = await HttpClient.ExecuteAsync(request, Definition); } else { @@ -429,7 +431,7 @@ namespace NzbDrone.Core.Indexers.Cardigann requestBuilder.AddFormParameter(pair.Key, pair.Value); } - loginResult = await HttpClient.ExecuteAsync(requestBuilder.Build()); + loginResult = await HttpClient.ExecuteAsync(requestBuilder.Build(), Definition); } Cookies = loginResult.GetCookies(); @@ -464,7 +466,7 @@ namespace NzbDrone.Core.Indexers.Cardigann requestBuilder.Headers.Add("Referer", SiteLink); - var response = await HttpClient.ExecuteAsync(requestBuilder.Build()); + var response = await HttpClient.ExecuteAsync(requestBuilder.Build(), Definition); Cookies = response.GetCookies(); @@ -488,7 +490,7 @@ namespace NzbDrone.Core.Indexers.Cardigann requestBuilder.Headers.Add("Referer", SiteLink); - var response = await HttpClient.ExecuteAsync(requestBuilder.Build()); + var response = await HttpClient.ExecuteAsync(requestBuilder.Build(), Definition); Cookies = response.GetCookies(); @@ -567,7 +569,7 @@ namespace NzbDrone.Core.Indexers.Cardigann var request = requestBuilder.Build(); - landingResult = await HttpClient.ExecuteAsync(request); + landingResult = await HttpClient.ExecuteAsync(request, Definition); Cookies = landingResult.GetCookies(); @@ -611,7 +613,7 @@ namespace NzbDrone.Core.Indexers.Cardigann .SetHeader("Referer", loginUrl.AbsoluteUri) .Build(); - var response = await HttpClient.ExecuteAsync(request); + var response = await HttpClient.ExecuteAsync(request, Definition); return new Captcha { @@ -701,7 +703,7 @@ namespace NzbDrone.Core.Indexers.Cardigann } } - var response = await HttpClient.ExecuteAsync(httpRequest.Build()); + var response = await HttpClient.ExecuteAsync(httpRequest.Build(), Definition); _logger.Debug($"CardigannIndexer ({_definition.Id}): handleRequest() remote server returned {response.StatusCode.ToString()}"); return response; @@ -741,7 +743,7 @@ namespace NzbDrone.Core.Indexers.Cardigann request.AllowAutoRedirect = true; - var response = await HttpClient.ExecuteAsync(request); + var response = await HttpClient.ExecuteAsync(request, Definition); var results = response.Content; var searchResultParser = new HtmlParser(); diff --git a/src/NzbDrone.Core/Indexers/Definitions/CinemaZ.cs b/src/NzbDrone.Core/Indexers/Definitions/CinemaZ.cs index c1e39a218..0f872ee08 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/CinemaZ.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/CinemaZ.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Description => "CinemaZ (EuTorrents) is a Private Torrent Tracker for FOREIGN NON-ASIAN MOVIES."; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public CinemaZ(IIndexerRepository indexerRepository, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public CinemaZ(IIndexerRepository indexerRepository, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(indexerRepository, httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/DanishBytes.cs b/src/NzbDrone.Core/Indexers/Definitions/DanishBytes.cs index 50ba133a8..b4a66c93e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/DanishBytes.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/DanishBytes.cs @@ -28,7 +28,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public DanishBytes(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public DanishBytes(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/DigitalCore.cs b/src/NzbDrone.Core/Indexers/Definitions/DigitalCore.cs index 7631cc6db..432946efb 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/DigitalCore.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/DigitalCore.cs @@ -29,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public DigitalCore(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public DigitalCore(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/ExoticaZ.cs b/src/NzbDrone.Core/Indexers/Definitions/ExoticaZ.cs index 91618971a..61fc89751 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/ExoticaZ.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/ExoticaZ.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Description => "ExoticaZ (YourExotic) is a Private Torrent Tracker for 3X"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public ExoticaZ(IIndexerRepository indexerRepository, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public ExoticaZ(IIndexerRepository indexerRepository, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(indexerRepository, httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs index 3be337450..d07ac31c2 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs @@ -18,7 +18,7 @@ namespace NzbDrone.Core.Indexers.FileList public override bool SupportsRedirect => true; public override IndexerCapabilities Capabilities => SetCapabilities(); - public FileList(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public FileList(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs index 03e7281af..bf316f438 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Core.Indexers.Gazelle public override int PageSize => 50; public override IndexerCapabilities Capabilities => SetCapabilities(); - public Gazelle(IHttpClient httpClient, + public Gazelle(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs index c2646c441..3a1e5361d 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs @@ -11,7 +11,7 @@ namespace NzbDrone.Core.Indexers.Gazelle public GazelleSettings Settings { get; set; } public IDictionary AuthCookieCache { get; set; } - public IHttpClient HttpClient { get; set; } + public IIndexerHttpClient HttpClient { get; set; } public IndexerCapabilities Capabilities { get; set; } public Logger Logger { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/GazelleGames.cs b/src/NzbDrone.Core/Indexers/Definitions/GazelleGames.cs index 30ffb5da0..01eb277e9 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/GazelleGames.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/GazelleGames.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public GazelleGames(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public GazelleGames(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs index 43425cb96..b5426b8ac 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs @@ -18,7 +18,7 @@ namespace NzbDrone.Core.Indexers.HDBits public override int PageSize => 30; - public HDBits(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public HDBits(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs b/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs index 8c1832067..b3a953b0b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs @@ -32,7 +32,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public HDSpace(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public HDSpace(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } @@ -49,7 +49,7 @@ namespace NzbDrone.Core.Indexers.Definitions protected override async Task DoLogin() { - var loginPage = await _httpClient.ExecuteAsync(new HttpRequest(LoginUrl)); + var loginPage = await ExecuteAuth(new HttpRequest(LoginUrl)); var requestBuilder = new HttpRequestBuilder(LoginUrl) { diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDTorrents.cs b/src/NzbDrone.Core/Indexers/Definitions/HDTorrents.cs index c15401f91..4c860d812 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDTorrents.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDTorrents.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public HDTorrents(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public HDTorrents(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Headphones/Headphones.cs b/src/NzbDrone.Core/Indexers/Definitions/Headphones/Headphones.cs index d19ddabeb..c6afa18f1 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Headphones/Headphones.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Headphones/Headphones.cs @@ -36,7 +36,7 @@ namespace NzbDrone.Core.Indexers.Headphones return new HeadphonesRssParser(Capabilities.Categories); } - public Headphones(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) + public Headphones(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger) { } @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Indexers.Headphones try { - var response = await _httpClient.ExecuteAsync(request); + var response = await _httpClient.ExecuteAsync(request, Definition); downloadBytes = response.ResponseData; } catch (Exception) diff --git a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents.cs b/src/NzbDrone.Core/Indexers/Definitions/IPTorrents.cs index 6a29006ac..e42c20979 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/IPTorrents.cs @@ -40,7 +40,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public IPTorrents(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public IPTorrents(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs b/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs index 28fa0aac8..77b81d1fc 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs @@ -32,7 +32,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public ImmortalSeed(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public ImmortalSeed(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/InternetArchive.cs b/src/NzbDrone.Core/Indexers/Definitions/InternetArchive.cs index 1f3474262..91e01dded 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/InternetArchive.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/InternetArchive.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override bool FollowRedirect => true; - public InternetArchive(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public InternetArchive(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Milkie.cs b/src/NzbDrone.Core/Indexers/Definitions/Milkie.cs index 5e681adad..6b465ca5a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Milkie.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Milkie.cs @@ -25,7 +25,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public Milkie(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Milkie(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/MyAnonamouse.cs b/src/NzbDrone.Core/Indexers/Definitions/MyAnonamouse.cs index 8da08bd97..0e2db997e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/MyAnonamouse.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/MyAnonamouse.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public MyAnonamouse(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public MyAnonamouse(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Nebulance.cs b/src/NzbDrone.Core/Indexers/Definitions/Nebulance.cs index 901175f5d..a73234f40 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Nebulance.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Nebulance.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public Nebulance(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Nebulance(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Newznab/Newznab.cs b/src/NzbDrone.Core/Indexers/Definitions/Newznab/Newznab.cs index 37ba5f4df..d6e73e22e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Newznab/Newznab.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Newznab/Newznab.cs @@ -29,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Newznab public override IndexerCapabilities Capabilities { get => GetCapabilitiesFromSettings(); protected set => base.Capabilities = value; } - public override int PageSize => _capabilitiesProvider.GetCapabilities(Settings).LimitsDefault.Value; + public override int PageSize => _capabilitiesProvider.GetCapabilities(Settings, Definition).LimitsDefault.Value; public override IIndexerRequestGenerator GetRequestGenerator() { @@ -77,7 +77,7 @@ namespace NzbDrone.Core.Indexers.Newznab public override IndexerCapabilities GetCapabilities() { // Newznab uses different Caps per site, so we need to cache them to db on first indexer add to prevent issues with loading UI and pulling caps every time. - return _capabilitiesProvider.GetCapabilities(Settings); + return _capabilitiesProvider.GetCapabilities(Settings, Definition); } public override IEnumerable DefaultDefinitions @@ -112,7 +112,7 @@ namespace NzbDrone.Core.Indexers.Newznab } } - public Newznab(INewznabCapabilitiesProvider capabilitiesProvider, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) + public Newznab(INewznabCapabilitiesProvider capabilitiesProvider, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger) { _capabilitiesProvider = capabilitiesProvider; @@ -169,7 +169,7 @@ namespace NzbDrone.Core.Indexers.Newznab { try { - var capabilities = _capabilitiesProvider.GetCapabilities(Settings); + var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition); if (capabilities.SearchParams != null && capabilities.SearchParams.Contains(SearchParam.Q)) { diff --git a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabCapabilitiesProvider.cs b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabCapabilitiesProvider.cs index 9b9a4fe2a..fe069c0bc 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabCapabilitiesProvider.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabCapabilitiesProvider.cs @@ -7,36 +7,37 @@ using NzbDrone.Common.Cache; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Common.Serializer; +using NzbDrone.Core.ThingiProvider; namespace NzbDrone.Core.Indexers.Newznab { public interface INewznabCapabilitiesProvider { - IndexerCapabilities GetCapabilities(NewznabSettings settings); + IndexerCapabilities GetCapabilities(NewznabSettings settings, ProviderDefinition definition); } public class NewznabCapabilitiesProvider : INewznabCapabilitiesProvider { private readonly ICached _capabilitiesCache; - private readonly IHttpClient _httpClient; + private readonly IIndexerHttpClient _httpClient; private readonly Logger _logger; - public NewznabCapabilitiesProvider(ICacheManager cacheManager, IHttpClient httpClient, Logger logger) + public NewznabCapabilitiesProvider(ICacheManager cacheManager, IIndexerHttpClient httpClient, Logger logger) { _capabilitiesCache = cacheManager.GetCache(GetType()); _httpClient = httpClient; _logger = logger; } - public IndexerCapabilities GetCapabilities(NewznabSettings indexerSettings) + public IndexerCapabilities GetCapabilities(NewznabSettings indexerSettings, ProviderDefinition definition) { var key = indexerSettings.ToJson(); - var capabilities = _capabilitiesCache.Get(key, () => FetchCapabilities(indexerSettings), TimeSpan.FromDays(7)); + var capabilities = _capabilitiesCache.Get(key, () => FetchCapabilities(indexerSettings, definition), TimeSpan.FromDays(7)); return capabilities; } - private IndexerCapabilities FetchCapabilities(NewznabSettings indexerSettings) + private IndexerCapabilities FetchCapabilities(NewznabSettings indexerSettings, ProviderDefinition definition) { var capabilities = new IndexerCapabilities(); @@ -49,12 +50,13 @@ namespace NzbDrone.Core.Indexers.Newznab var request = new HttpRequest(url, HttpAccept.Rss); request.AllowAutoRedirect = true; + request.Method = HttpMethod.GET; HttpResponse response; try { - response = _httpClient.Get(request); + response = _httpClient.Execute(request, definition); } catch (Exception ex) { diff --git a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs index 45a8b8729..f8835e82a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs @@ -6,6 +6,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser; +using NzbDrone.Core.ThingiProvider; namespace NzbDrone.Core.Indexers.Newznab { @@ -15,6 +16,7 @@ namespace NzbDrone.Core.Indexers.Newznab public int MaxPages { get; set; } public int PageSize { get; set; } public NewznabSettings Settings { get; set; } + public ProviderDefinition Definition { get; set; } public NewznabRequestGenerator(INewznabCapabilitiesProvider capabilitiesProvider) { @@ -26,7 +28,7 @@ namespace NzbDrone.Core.Indexers.Newznab public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) { - var capabilities = _capabilitiesProvider.GetCapabilities(Settings); + var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition); var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); @@ -72,7 +74,7 @@ namespace NzbDrone.Core.Indexers.Newznab public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) { - var capabilities = _capabilitiesProvider.GetCapabilities(Settings); + var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition); var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); @@ -113,7 +115,7 @@ namespace NzbDrone.Core.Indexers.Newznab public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) { - var capabilities = _capabilitiesProvider.GetCapabilities(Settings); + var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition); var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); @@ -174,7 +176,7 @@ namespace NzbDrone.Core.Indexers.Newznab public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) { - var capabilities = _capabilitiesProvider.GetCapabilities(Settings); + var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition); var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); @@ -215,7 +217,7 @@ namespace NzbDrone.Core.Indexers.Newznab public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) { - var capabilities = _capabilitiesProvider.GetCapabilities(Settings); + var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition); var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); diff --git a/src/NzbDrone.Core/Indexers/Definitions/NotWhatCD.cs b/src/NzbDrone.Core/Indexers/Definitions/NotWhatCD.cs index b305358f8..36b0ff1f0 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/NotWhatCD.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/NotWhatCD.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Description => "NotWhat.CD (NWCD) is a private Music tracker that arised after the former (WCD) shut down."; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public NotWhatCD(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public NotWhatCD(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs b/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs index 1cf42b846..a0316a18b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Description => "Orpheus (APOLLO) is a Private Torrent Tracker for MUSIC"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public Orpheus(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Orpheus(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs index a7dc63a15..9f76e5109 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs @@ -21,7 +21,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn public override int PageSize => 50; - public PassThePopcorn(IHttpClient httpClient, + public PassThePopcorn(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, ICacheManager cacheManager, IIndexerStatusService indexerStatusService, diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs index 0bc70fd41..40c1fd867 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn public IDictionary Cookies { get; set; } - public IHttpClient HttpClient { get; set; } + public IIndexerHttpClient HttpClient { get; set; } public Logger Logger { get; set; } public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) diff --git a/src/NzbDrone.Core/Indexers/Definitions/PreToMe.cs b/src/NzbDrone.Core/Indexers/Definitions/PreToMe.cs index 69ab1cfea..c7e3e7390 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PreToMe.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PreToMe.cs @@ -32,7 +32,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public PreToMe(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public PreToMe(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } @@ -57,7 +57,7 @@ namespace NzbDrone.Core.Indexers.Definitions AllowAutoRedirect = true }; - var loginPage = await _httpClient.ExecuteAsync(new HttpRequest(Settings.BaseUrl + "login.php")); + var loginPage = await ExecuteAuth(new HttpRequest(Settings.BaseUrl + "login.php")); requestBuilder.Method = HttpMethod.POST; requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); diff --git a/src/NzbDrone.Core/Indexers/Definitions/PrivateHD.cs b/src/NzbDrone.Core/Indexers/Definitions/PrivateHD.cs index ee4d6595b..bb1fac39e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PrivateHD.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PrivateHD.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Description => "PrivateHD is a Private Torrent Tracker for HD MOVIES / TV and the sister-site of AvistaZ, CinemaZ, ExoticaZ, and AnimeTorrents"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public PrivateHD(IIndexerRepository indexerRepository, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public PrivateHD(IIndexerRepository indexerRepository, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(indexerRepository, httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/Rarbg.cs b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/Rarbg.cs index 6d17559fb..5ba132468 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/Rarbg.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/Rarbg.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Indexers.Rarbg public override TimeSpan RateLimit => TimeSpan.FromSeconds(2); - public Rarbg(IRarbgTokenProvider tokenProvider, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Rarbg(IRarbgTokenProvider tokenProvider, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { _tokenProvider = tokenProvider; diff --git a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgTokenProvider.cs b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgTokenProvider.cs index a4686ee82..2319105a4 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgTokenProvider.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgTokenProvider.cs @@ -14,11 +14,11 @@ namespace NzbDrone.Core.Indexers.Rarbg public class RarbgTokenProvider : IRarbgTokenProvider { - private readonly IHttpClient _httpClient; + private readonly IIndexerHttpClient _httpClient; private readonly ICached _tokenCache; private readonly Logger _logger; - public RarbgTokenProvider(IHttpClient httpClient, ICacheManager cacheManager, Logger logger) + public RarbgTokenProvider(IIndexerHttpClient httpClient, ICacheManager cacheManager, Logger logger) { _httpClient = httpClient; _tokenCache = cacheManager.GetCache(GetType()); diff --git a/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs b/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs index 0a13f6000..5f664cd05 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs @@ -32,7 +32,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerCapabilities Capabilities => SetCapabilities(); public override bool SupportsRedirect => true; - public Redacted(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Redacted(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } @@ -93,7 +93,7 @@ namespace NzbDrone.Core.Indexers.Definitions public IndexerCapabilities Capabilities { get; set; } public Func> GetCookies { get; set; } public Action, DateTime?> CookiesUpdater { get; set; } - public IHttpClient HttpClient { get; set; } + public IIndexerHttpClient HttpClient { get; set; } public RedactedRequestGenerator() { diff --git a/src/NzbDrone.Core/Indexers/Definitions/RevolutionTT.cs b/src/NzbDrone.Core/Indexers/Definitions/RevolutionTT.cs index 31f4e0c2a..41e66ba09 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/RevolutionTT.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/RevolutionTT.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public RevolutionTT(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public RevolutionTT(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } @@ -56,7 +56,7 @@ namespace NzbDrone.Core.Indexers.Definitions AllowAutoRedirect = true }; - var loginPage = await _httpClient.ExecuteAsync(new HttpRequest(Settings.BaseUrl + "login.php")); + var loginPage = await ExecuteAuth(new HttpRequest(Settings.BaseUrl + "login.php")); requestBuilder.Method = HttpMethod.POST; requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); diff --git a/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs b/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs index 4a48547fc..408c7ad83 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public RuTracker(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public RuTracker(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/SceneTime.cs b/src/NzbDrone.Core/Indexers/Definitions/SceneTime.cs index fc0ae8fa3..f982bcdc3 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/SceneTime.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/SceneTime.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public SceneTime(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public SceneTime(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs b/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs index b9f5c3996..31553311e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public SecretCinema(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public SecretCinema(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/ShareIsland.cs b/src/NzbDrone.Core/Indexers/Definitions/ShareIsland.cs index 6b497eee2..0698e0859 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/ShareIsland.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/ShareIsland.cs @@ -15,7 +15,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override string Language => "it-it"; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public ShareIsland(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public ShareIsland(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Shizaproject.cs b/src/NzbDrone.Core/Indexers/Definitions/Shizaproject.cs index 3c329afbf..5ad5cbf9f 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Shizaproject.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Shizaproject.cs @@ -29,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Public; public override IndexerCapabilities Capabilities => SetCapabilities(); - public Shizaproject(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Shizaproject(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/ShowRSS.cs b/src/NzbDrone.Core/Indexers/Definitions/ShowRSS.cs index ad7d0992b..11ec011d1 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/ShowRSS.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/ShowRSS.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Public; public override IndexerCapabilities Capabilities => SetCapabilities(); - public ShowRSS(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public ShowRSS(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/SpeedApp.cs b/src/NzbDrone.Core/Indexers/Definitions/SpeedApp.cs index 5ef0518ca..316b3f7ba 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/SpeedApp.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/SpeedApp.cs @@ -48,7 +48,7 @@ namespace NzbDrone.Core.Indexers.Definitions private IIndexerRepository _indexerRepository; - public SpeedApp(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger, IIndexerRepository indexerRepository) + public SpeedApp(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger, IIndexerRepository indexerRepository) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { _indexerRepository = indexerRepository; @@ -141,7 +141,7 @@ namespace NzbDrone.Core.Indexers.Definitions try { - var response = await _httpClient.ExecuteAsync(request); + var response = await _httpClient.ExecuteAsync(request, Definition); torrentData = response.ResponseData; } catch (HttpException ex) diff --git a/src/NzbDrone.Core/Indexers/Definitions/SubsPlease.cs b/src/NzbDrone.Core/Indexers/Definitions/SubsPlease.cs index 9085a1afd..69e47b9d5 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/SubsPlease.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/SubsPlease.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Public; public override IndexerCapabilities Capabilities => SetCapabilities(); - public SubsPlease(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public SubsPlease(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/SuperBits.cs b/src/NzbDrone.Core/Indexers/Definitions/SuperBits.cs index dc92cf62a..4b433eea3 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/SuperBits.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/SuperBits.cs @@ -28,7 +28,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public SuperBits(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public SuperBits(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TVVault.cs b/src/NzbDrone.Core/Indexers/Definitions/TVVault.cs index ed822a85d..6f2891cb1 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TVVault.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TVVault.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public TVVault(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public TVVault(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/ThePirateBay.cs b/src/NzbDrone.Core/Indexers/Definitions/ThePirateBay.cs index 17a2ced98..047cb5ece 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/ThePirateBay.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/ThePirateBay.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Public; public override IndexerCapabilities Capabilities => SetCapabilities(); - public ThePirateBay(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public ThePirateBay(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentDay.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentDay.cs index b2c9c8bc2..af9c444d0 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentDay.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentDay.cs @@ -38,7 +38,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public TorrentDay(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public TorrentDay(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentLeech.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentLeech.cs index 1783ebe8c..6b21342ee 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentLeech.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentLeech.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public TorrentLeech(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public TorrentLeech(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentParadiseMl.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentParadiseMl.cs index 0e95632df..cf8973080 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentParadiseMl.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentParadiseMl.cs @@ -29,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Public; public override IndexerCapabilities Capabilities => SetCapabilities(); - public TorrentParadiseMl(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public TorrentParadiseMl(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs index e9638c821..a75e3e67b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs @@ -16,7 +16,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override TimeSpan RateLimit => TimeSpan.FromSeconds(2); - public TorrentPotato(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public TorrentPotato(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentSeeds.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentSeeds.cs index 6557a917b..f6e400e0e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentSeeds.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentSeeds.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public TorrentSeeds(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public TorrentSeeds(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } @@ -53,7 +53,7 @@ namespace NzbDrone.Core.Indexers.Definitions LogResponseContent = true }; - var loginPage = await _httpClient.ExecuteAsync(new HttpRequest(TokenUrl)); + var loginPage = await ExecuteAuth(new HttpRequest(TokenUrl)); var parser = new HtmlParser(); var dom = parser.ParseDocument(loginPage.Content); var token = dom.QuerySelector("form.form-horizontal > span"); @@ -74,7 +74,7 @@ namespace NzbDrone.Core.Indexers.Definitions .SetHeader("Content-Type", "multipart/form-data") .Build(); - var response = await _httpClient.ExecuteAsync(authLoginRequest); + var response = await ExecuteAuth(authLoginRequest); cookies = response.GetCookies(); UpdateCookies(cookies, DateTime.Now + TimeSpan.FromDays(30)); diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentSyndikat.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentSyndikat.cs index 97cd08897..ce819f2eb 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentSyndikat.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentSyndikat.cs @@ -29,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public TorrentSyndikat(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public TorrentSyndikat(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentsCSV.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentsCSV.cs index b68da5b9f..027febd03 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentsCSV.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentsCSV.cs @@ -29,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerCapabilities Capabilities => SetCapabilities(); public override bool SupportsRss => false; - public TorrentsCSV(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public TorrentsCSV(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Torznab/Torznab.cs b/src/NzbDrone.Core/Indexers/Definitions/Torznab/Torznab.cs index 8bf5c06ff..997a9477f 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Torznab/Torznab.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Torznab/Torznab.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Indexers.Torznab public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public override int PageSize => _capabilitiesProvider.GetCapabilities(Settings).LimitsDefault.Value; + public override int PageSize => _capabilitiesProvider.GetCapabilities(Settings, Definition).LimitsDefault.Value; public override IndexerCapabilities Capabilities { get => GetCapabilitiesFromSettings(); protected set => base.Capabilities = value; } public override IIndexerRequestGenerator GetRequestGenerator() @@ -76,7 +76,7 @@ namespace NzbDrone.Core.Indexers.Torznab public override IndexerCapabilities GetCapabilities() { // Newznab uses different Caps per site, so we need to cache them to db on first indexer add to prevent issues with loading UI and pulling caps every time. - return _capabilitiesProvider.GetCapabilities(Settings); + return _capabilitiesProvider.GetCapabilities(Settings, Definition); } public override IEnumerable DefaultDefinitions @@ -89,7 +89,7 @@ namespace NzbDrone.Core.Indexers.Torznab } } - public Torznab(INewznabCapabilitiesProvider capabilitiesProvider, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public Torznab(INewznabCapabilitiesProvider capabilitiesProvider, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { _capabilitiesProvider = capabilitiesProvider; @@ -145,7 +145,7 @@ namespace NzbDrone.Core.Indexers.Torznab { try { - var capabilities = _capabilitiesProvider.GetCapabilities(Settings); + var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition); if (capabilities.SearchParams != null && capabilities.SearchParams.Contains(SearchParam.Q)) { diff --git a/src/NzbDrone.Core/Indexers/Definitions/UNIT3D/Unit3dBase.cs b/src/NzbDrone.Core/Indexers/Definitions/UNIT3D/Unit3dBase.cs index a96a6da84..c5e17194c 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/UNIT3D/Unit3dBase.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/UNIT3D/Unit3dBase.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Definitions.UNIT3D public override int PageSize => 50; public override IndexerCapabilities Capabilities => SetCapabilities(); - public Unit3dBase(IHttpClient httpClient, + public Unit3dBase(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, diff --git a/src/NzbDrone.Core/Indexers/Definitions/UNIT3D/Unit3dRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/UNIT3D/Unit3dRequestGenerator.cs index 4797a26f5..5957534f2 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/UNIT3D/Unit3dRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/UNIT3D/Unit3dRequestGenerator.cs @@ -12,7 +12,7 @@ namespace NzbDrone.Core.Indexers.Definitions.UNIT3D { public Unit3dSettings Settings { get; set; } - public IHttpClient HttpClient { get; set; } + public IIndexerHttpClient HttpClient { get; set; } public IndexerCapabilities Capabilities { get; set; } public Logger Logger { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Xthor/Xthor.cs b/src/NzbDrone.Core/Indexers/Definitions/Xthor/Xthor.cs index c5f1419ea..108df2755 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Xthor/Xthor.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Xthor/Xthor.cs @@ -21,7 +21,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Xthor public override TimeSpan RateLimit => TimeSpan.FromSeconds(2.1); public override IndexerCapabilities Capabilities => SetCapabilities(); - public Xthor(IHttpClient httpClient, + public Xthor(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, diff --git a/src/NzbDrone.Core/Indexers/Definitions/YTS.cs b/src/NzbDrone.Core/Indexers/Definitions/YTS.cs index d98ee986e..169835c23 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/YTS.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/YTS.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override TimeSpan RateLimit => TimeSpan.FromSeconds(2.5); public override IndexerCapabilities Capabilities => SetCapabilities(); - public YTS(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public YTS(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/Definitions/ZonaQ.cs b/src/NzbDrone.Core/Indexers/Definitions/ZonaQ.cs index 1b07f4a4b..80bb816c2 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/ZonaQ.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/ZonaQ.cs @@ -39,7 +39,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); - public ZonaQ(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public ZonaQ(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } @@ -59,7 +59,7 @@ namespace NzbDrone.Core.Indexers.Definitions _logger.Debug("ZonaQ authentication succeeded."); // The first page set the cookies and the session_id - var loginPage = await _httpClient.ExecuteAsync(new HttpRequest(Login1Url)); + var loginPage = await ExecuteAuth(new HttpRequest(Login1Url)); var parser = new HtmlParser(); var dom = parser.ParseDocument(loginPage.Content); var sessionId = dom.QuerySelector("input#session_id")?.GetAttribute("value"); @@ -132,7 +132,7 @@ namespace NzbDrone.Core.Indexers.Definitions requestBuilder4.SetCookies(response.GetCookies()); var authLoginRequest3 = requestBuilder4.Build(); - response = await _httpClient.ExecuteAsync(authLoginRequest3); + response = await ExecuteAuth(authLoginRequest3); UpdateCookies(response.GetCookies(), DateTime.Now + TimeSpan.FromDays(30)); } diff --git a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs index ef1e4f51d..5a7c87bad 100644 --- a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs @@ -23,7 +23,7 @@ namespace NzbDrone.Core.Indexers { protected const int MaxNumResultsPerQuery = 1000; - protected readonly IHttpClient _httpClient; + protected readonly IIndexerHttpClient _httpClient; protected readonly IEventAggregator _eventAggregator; public IDictionary Cookies { get; set; } @@ -42,7 +42,7 @@ namespace NzbDrone.Core.Indexers public abstract IIndexerRequestGenerator GetRequestGenerator(); public abstract IParseIndexerResponse GetParser(); - public HttpIndexerBase(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + public HttpIndexerBase(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(indexerStatusService, configService, logger) { _httpClient = httpClient; @@ -379,7 +379,7 @@ namespace NzbDrone.Core.Indexers request.HttpRequest.SuppressHttpError = true; request.HttpRequest.Encoding = Encoding; - var response = await _httpClient.ExecuteAsync(request.HttpRequest); + var response = await _httpClient.ExecuteAsync(request.HttpRequest, Definition); // Check reponse to see if auth is needed, if needed try again if (CheckIfLoginNeeded(response)) @@ -391,7 +391,7 @@ namespace NzbDrone.Core.Indexers request.HttpRequest.Url = originalUrl; ModifyRequest(request); - response = await _httpClient.ExecuteAsync(request.HttpRequest); + response = await _httpClient.ExecuteAsync(request.HttpRequest, Definition); } // Throw common http errors here before we try to parse @@ -414,7 +414,7 @@ namespace NzbDrone.Core.Indexers { request.Encoding = Encoding; - var response = await _httpClient.ExecuteAsync(request); + var response = await _httpClient.ExecuteAsync(request, Definition); _eventAggregator.PublishEvent(new IndexerAuthEvent(Definition.Id, !response.HasHttpError, response.ElapsedTime)); diff --git a/src/NzbDrone.Core/Indexers/IndexerFactory.cs b/src/NzbDrone.Core/Indexers/IndexerFactory.cs index b93d79147..9096d22b9 100644 --- a/src/NzbDrone.Core/Indexers/IndexerFactory.cs +++ b/src/NzbDrone.Core/Indexers/IndexerFactory.cs @@ -259,7 +259,7 @@ namespace NzbDrone.Core.Indexers if (definition.Implementation == typeof(Newznab.Newznab).Name || definition.Implementation == typeof(Torznab.Torznab).Name) { var settings = (NewznabSettings)definition.Settings; - settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings)?.Categories.GetTorznabCategoryList() ?? null; + settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings, definition)?.Categories.GetTorznabCategoryList() ?? null; } if (definition.Implementation == typeof(Cardigann.Cardigann).Name) @@ -279,7 +279,7 @@ namespace NzbDrone.Core.Indexers if (definition.Enable && (definition.Implementation == typeof(Newznab.Newznab).Name || definition.Implementation == typeof(Torznab.Torznab).Name)) { var settings = (NewznabSettings)definition.Settings; - settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings)?.Categories.GetTorznabCategoryList() ?? null; + settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings, definition)?.Categories.GetTorznabCategoryList() ?? null; } if (definition.Implementation == typeof(Cardigann.Cardigann).Name) diff --git a/src/NzbDrone.Core/Indexers/IndexerHttpClient.cs b/src/NzbDrone.Core/Indexers/IndexerHttpClient.cs new file mode 100644 index 000000000..1558b139c --- /dev/null +++ b/src/NzbDrone.Core/Indexers/IndexerHttpClient.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NLog; +using NzbDrone.Common.Cache; +using NzbDrone.Common.Http; +using NzbDrone.Common.Http.Dispatchers; +using NzbDrone.Common.TPL; +using NzbDrone.Core.IndexerProxies; +using NzbDrone.Core.ThingiProvider; + +namespace NzbDrone.Core.Indexers +{ + public interface IIndexerHttpClient : IHttpClient + { + Task ExecuteAsync(HttpRequest request, ProviderDefinition definition); + HttpResponse Execute(HttpRequest request, ProviderDefinition definition); + } + + public class IndexerHttpClient : HttpClient, IIndexerHttpClient + { + private readonly IIndexerProxyFactory _indexerProxyFactory; + public IndexerHttpClient(IIndexerProxyFactory indexerProxyFactory, + IEnumerable requestInterceptors, + ICacheManager cacheManager, + IRateLimitService rateLimitService, + IHttpDispatcher httpDispatcher, + Logger logger) + : base(requestInterceptors, cacheManager, rateLimitService, httpDispatcher, logger) + { + _indexerProxyFactory = indexerProxyFactory; + } + + public async Task ExecuteAsync(HttpRequest request, ProviderDefinition definition) + { + var selectedProxy = GetProxy(definition); + + request = PreRequest(request, selectedProxy); + + return PostResponse(await ExecuteAsync(request), selectedProxy); + } + + public HttpResponse Execute(HttpRequest request, ProviderDefinition definition) + { + var selectedProxy = GetProxy(definition); + + request = PreRequest(request, selectedProxy); + + return PostResponse(Execute(request), selectedProxy); + } + + private IIndexerProxy GetProxy(ProviderDefinition definition) + { + //Skip DB call if no tags on the indexers + if (definition.Tags.Count == 0) + { + return null; + } + + var proxies = _indexerProxyFactory.GetAvailableProviders(); + IIndexerProxy selectedProxy = null; + + foreach (var proxy in proxies) + { + if (definition.Tags.Intersect(proxy.Definition.Tags).Any()) + { + selectedProxy = proxy; + break; + } + } + + return selectedProxy; + } + + private HttpRequest PreRequest(HttpRequest request, IIndexerProxy selectedProxy) + { + if (selectedProxy != null) + { + request = selectedProxy.PreRequest(request); + } + + return request; + } + + private HttpResponse PostResponse(HttpResponse response, IIndexerProxy selectedProxy) + { + if (selectedProxy != null) + { + response = selectedProxy.PostResponse(response); + } + + return response; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/IndexerRequest.cs b/src/NzbDrone.Core/Indexers/IndexerRequest.cs index 572273d9f..6a1b9c167 100644 --- a/src/NzbDrone.Core/Indexers/IndexerRequest.cs +++ b/src/NzbDrone.Core/Indexers/IndexerRequest.cs @@ -1,4 +1,4 @@ -using NzbDrone.Common.Http; +using NzbDrone.Common.Http; namespace NzbDrone.Core.Indexers { diff --git a/src/NzbDrone.Core/Indexers/TorrentIndexerBase.cs b/src/NzbDrone.Core/Indexers/TorrentIndexerBase.cs index 510eab655..226be41b1 100644 --- a/src/NzbDrone.Core/Indexers/TorrentIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/TorrentIndexerBase.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers public abstract class TorrentIndexerBase : HttpIndexerBase where TSettings : IIndexerSettings, new() { - protected TorrentIndexerBase(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + protected TorrentIndexerBase(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } diff --git a/src/NzbDrone.Core/Indexers/UsenetIndexerBase.cs b/src/NzbDrone.Core/Indexers/UsenetIndexerBase.cs index 42d8e20b4..d1ab70ddc 100644 --- a/src/NzbDrone.Core/Indexers/UsenetIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/UsenetIndexerBase.cs @@ -15,7 +15,7 @@ namespace NzbDrone.Core.Indexers { private readonly IValidateNzbs _nzbValidationService; - protected UsenetIndexerBase(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) + protected UsenetIndexerBase(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { _nzbValidationService = nzbValidationService; diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index f0a9a911c..24a8b5ed4 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -95,6 +95,8 @@ "DeleteDownloadClientMessageText": "Are you sure you want to delete the download client '{0}'?", "DeleteIndexer": "Delete Indexer", "DeleteIndexerMessageText": "Are you sure you want to delete the indexer '{0}'?", + "DeleteIndexerProxy": "Delete Indexer Proxy", + "DeleteIndexerProxyMessageText": "Are you sure you want to delete the proxy '{0}'?", "DeleteNotification": "Delete Notification", "DeleteNotificationMessageText": "Are you sure you want to delete the notification '{0}'?", "DeleteTag": "Delete Tag", @@ -186,12 +188,16 @@ "IndexerObsoleteCheckMessage": "Indexers are obsolete or have been updated: {0}. Please remove and (or) re-add to Prowlarr", "IndexerPriority": "Indexer Priority", "IndexerPriorityHelpText": "Indexer Priority from 1 (Highest) to 50 (Lowest). Default: 25.", + "IndexerProxies": "Indexer Proxies", + "IndexerProxyStatusCheckAllClientMessage": "All proxies are unavailable due to failures", + "IndexerProxyStatusCheckSingleClientMessage": "Proxies unavailable due to failures: {0}", "IndexerQuery": "Indexer Query", "IndexerRss": "Indexer Rss", "Indexers": "Indexers", "IndexersSelectedInterp": "{0} Indexer(s) Selected", "IndexerStatusCheckAllClientMessage": "All indexers are unavailable due to failures", "IndexerStatusCheckSingleClientMessage": "Indexers unavailable due to failures: {0}", + "IndexerTagsHelpText": "Use tags to specify default clients, specify Indexer Proxies, or just to organize your indexers.", "Info": "Info", "InteractiveSearch": "Interactive Search", "Interval": "Interval", @@ -392,6 +398,7 @@ "UnableToAddANewAppProfilePleaseTryAgain": "Unable to add a new application profile, please try again.", "UnableToAddANewDownloadClientPleaseTryAgain": "Unable to add a new download client, please try again.", "UnableToAddANewIndexerPleaseTryAgain": "Unable to add a new indexer, please try again.", + "UnableToAddANewIndexerProxyPleaseTryAgain": "Unable to add a new indexer proxy, please try again.", "UnableToAddANewNotificationPleaseTryAgain": "Unable to add a new notification, please try again.", "UnableToLoadAppProfiles": "Unable to load app profiles", "UnableToLoadBackups": "Unable to load backups", @@ -399,6 +406,7 @@ "UnableToLoadDownloadClients": "Unable to load download clients", "UnableToLoadGeneralSettings": "Unable to load General settings", "UnableToLoadHistory": "Unable to load history", + "UnableToLoadIndexerProxies": "Unable To Load Indexer Proxies", "UnableToLoadIndexers": "Unable to load Indexers", "UnableToLoadNotifications": "Unable to load Notifications", "UnableToLoadQualityDefinitions": "Unable to load Quality Definitions", diff --git a/src/NzbDrone.Core/Tags/TagDetails.cs b/src/NzbDrone.Core/Tags/TagDetails.cs index d538b7771..e0b2c018a 100644 --- a/src/NzbDrone.Core/Tags/TagDetails.cs +++ b/src/NzbDrone.Core/Tags/TagDetails.cs @@ -8,12 +8,14 @@ namespace NzbDrone.Core.Tags { public string Label { get; set; } public List NotificationIds { get; set; } + public List IndexerIds { get; set; } + public List IndexerProxyIds { get; set; } public bool InUse { get { - return NotificationIds.Any(); + return NotificationIds.Any() || IndexerIds.Any() || IndexerProxyIds.Any(); } } } diff --git a/src/NzbDrone.Core/Tags/TagService.cs b/src/NzbDrone.Core/Tags/TagService.cs index b8994d127..727ad88ca 100644 --- a/src/NzbDrone.Core/Tags/TagService.cs +++ b/src/NzbDrone.Core/Tags/TagService.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Linq; using NzbDrone.Core.Datastore; +using NzbDrone.Core.IndexerProxies; +using NzbDrone.Core.Indexers; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Notifications; @@ -24,14 +26,20 @@ namespace NzbDrone.Core.Tags private readonly ITagRepository _repo; private readonly IEventAggregator _eventAggregator; private readonly INotificationFactory _notificationFactory; + private readonly IIndexerFactory _indexerFactory; + private readonly IIndexerProxyFactory _indexerProxyFactory; public TagService(ITagRepository repo, IEventAggregator eventAggregator, - INotificationFactory notificationFactory) + INotificationFactory notificationFactory, + IIndexerFactory indexerFactory, + IIndexerProxyFactory indexerProxyFactory) { _repo = repo; _eventAggregator = eventAggregator; _notificationFactory = notificationFactory; + _indexerFactory = indexerFactory; + _indexerProxyFactory = indexerProxyFactory; } public Tag GetTag(int tagId) @@ -60,12 +68,16 @@ namespace NzbDrone.Core.Tags { var tag = GetTag(tagId); var notifications = _notificationFactory.AllForTag(tagId); + var indexers = _indexerFactory.AllForTag(tagId); + var indexerProxies = _indexerProxyFactory.AllForTag(tagId); return new TagDetails { Id = tagId, Label = tag.Label, - NotificationIds = notifications.Select(c => c.Id).ToList() + NotificationIds = notifications.Select(c => c.Id).ToList(), + IndexerIds = indexers.Select(c => c.Id).ToList(), + IndexerProxyIds = indexerProxies.Select(c => c.Id).ToList() }; } @@ -73,6 +85,8 @@ namespace NzbDrone.Core.Tags { var tags = All(); var notifications = _notificationFactory.All(); + var indexers = _indexerFactory.All(); + var indexerProxies = _indexerProxyFactory.All(); var details = new List(); @@ -82,7 +96,9 @@ namespace NzbDrone.Core.Tags { Id = tag.Id, Label = tag.Label, - NotificationIds = notifications.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList() + NotificationIds = notifications.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(), + IndexerIds = indexers.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(), + IndexerProxyIds = indexerProxies.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList() }); } diff --git a/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs b/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs new file mode 100644 index 000000000..f63ee4a3e --- /dev/null +++ b/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs @@ -0,0 +1,16 @@ +using NzbDrone.Core.IndexerProxies; +using Prowlarr.Http; + +namespace Prowlarr.Api.V1.IndexerProxies +{ + [V1ApiController] + public class IndexerProxyController : ProviderControllerBase + { + public static readonly IndexerProxyResourceMapper ResourceMapper = new IndexerProxyResourceMapper(); + + public IndexerProxyController(IndexerProxyFactory notificationFactory) + : base(notificationFactory, "indexerProxy", ResourceMapper) + { + } + } +} diff --git a/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyResource.cs b/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyResource.cs new file mode 100644 index 000000000..6b8719289 --- /dev/null +++ b/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyResource.cs @@ -0,0 +1,40 @@ +using NzbDrone.Core.IndexerProxies; + +namespace Prowlarr.Api.V1.IndexerProxies +{ + public class IndexerProxyResource : ProviderResource + { + public string Link { get; set; } + public bool OnHealthIssue { get; set; } + public bool SupportsOnHealthIssue { get; set; } + public bool IncludeHealthWarnings { get; set; } + public string TestCommand { get; set; } + } + + public class IndexerProxyResourceMapper : ProviderResourceMapper + { + public override IndexerProxyResource ToResource(IndexerProxyDefinition definition) + { + if (definition == null) + { + return default(IndexerProxyResource); + } + + var resource = base.ToResource(definition); + + return resource; + } + + public override IndexerProxyDefinition ToModel(IndexerProxyResource resource) + { + if (resource == null) + { + return default(IndexerProxyDefinition); + } + + var definition = base.ToModel(resource); + + return definition; + } + } +} diff --git a/src/Prowlarr.Api.V1/Tags/TagDetailsResource.cs b/src/Prowlarr.Api.V1/Tags/TagDetailsResource.cs index 41ef94c94..2404e6857 100644 --- a/src/Prowlarr.Api.V1/Tags/TagDetailsResource.cs +++ b/src/Prowlarr.Api.V1/Tags/TagDetailsResource.cs @@ -9,6 +9,8 @@ namespace Prowlarr.Api.V1.Tags { public string Label { get; set; } public List NotificationIds { get; set; } + public List IndexerIds { get; set; } + public List IndexerProxyIds { get; set; } } public static class TagDetailsResourceMapper @@ -24,7 +26,9 @@ namespace Prowlarr.Api.V1.Tags { Id = model.Id, Label = model.Label, - NotificationIds = model.NotificationIds + NotificationIds = model.NotificationIds, + IndexerIds = model.IndexerIds, + IndexerProxyIds = model.IndexerProxyIds }; }