mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-03-05 13:40:08 -05:00
Compare commits
89 Commits
v1.3.2.300
...
v1.4.0.323
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65e6aa05c3 | ||
|
|
fb20b3e61b | ||
|
|
b8a77830aa | ||
|
|
d2ba52cdce | ||
|
|
43f881c442 | ||
|
|
4a5e923999 | ||
|
|
57e1b6b4a0 | ||
|
|
9cc60760c3 | ||
|
|
2811feb14e | ||
|
|
46af9223bc | ||
|
|
025156978b | ||
|
|
d3ca861aea | ||
|
|
c9249ed583 | ||
|
|
94cc56d0f6 | ||
|
|
c8addc0d62 | ||
|
|
2015156061 | ||
|
|
742c680014 | ||
|
|
b1add3f649 | ||
|
|
65d6d518d7 | ||
|
|
bc8ba5ca02 | ||
|
|
6aebc4ee01 | ||
|
|
9bbe51253b | ||
|
|
88fbc30be2 | ||
|
|
5fdc6ee25d | ||
|
|
4eb5a2d613 | ||
|
|
122883053a | ||
|
|
28d09cd384 | ||
|
|
17be8bb68a | ||
|
|
c5baded3d6 | ||
|
|
349cfacdca | ||
|
|
788fa6d96a | ||
|
|
fbea5bbc06 | ||
|
|
d667c7d853 | ||
|
|
a9e1204a9b | ||
|
|
88e3f86262 | ||
|
|
1c173fc984 | ||
|
|
6e8f3d814a | ||
|
|
14e105e37e | ||
|
|
9e0deb8f74 | ||
|
|
245e573089 | ||
|
|
5e8bfa2ffb | ||
|
|
555c924e50 | ||
|
|
8404b85624 | ||
|
|
dc5e6d29e1 | ||
|
|
8c42b7a69b | ||
|
|
3a6ebdef8a | ||
|
|
5f57957462 | ||
|
|
12526c1bb3 | ||
|
|
29f049f766 | ||
|
|
40f4e1b82a | ||
|
|
065fbb30bf | ||
|
|
ea24a81ef7 | ||
|
|
451f60319f | ||
|
|
c6ed5d65e0 | ||
|
|
4e5cd05bbd | ||
|
|
6b2b953686 | ||
|
|
31c05be9de | ||
|
|
bc852c0b55 | ||
|
|
18651d8be1 | ||
|
|
1608095345 | ||
|
|
7700014ceb | ||
|
|
3fbc2912f0 | ||
|
|
3192990874 | ||
|
|
fb908e8e19 | ||
|
|
8e60c707b2 | ||
|
|
a184bb0784 | ||
|
|
e5ccbaaf24 | ||
|
|
362e0acad1 | ||
|
|
54d06460d0 | ||
|
|
c11bcf4c41 | ||
|
|
2e58583263 | ||
|
|
bf7f769f13 | ||
|
|
7820a83a5d | ||
|
|
d937bdac69 | ||
|
|
ebca32af46 | ||
|
|
21bda07510 | ||
|
|
f638cf34d1 | ||
|
|
b7fcdb5356 | ||
|
|
2e4fa9d06d | ||
|
|
9b50fc40ca | ||
|
|
3c60159df0 | ||
|
|
e075003c8b | ||
|
|
b19202d9f5 | ||
|
|
2784ee8ce6 | ||
|
|
5aa4a5faaa | ||
|
|
1d00b40f90 | ||
|
|
93dd378ade | ||
|
|
534ca73bf8 | ||
|
|
bceebc34c1 |
7
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
7
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -71,3 +71,10 @@ body:
|
||||
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Trace Logs have been provided as applicable. Reports may be closed if the required logs are not provided.
|
||||
description: Trace logs are generally required for all bug reports
|
||||
options:
|
||||
- label: I have followed the steps in the wiki link above and provided the required trace logs that are relevant and show this issue.
|
||||
required: true
|
||||
|
||||
@@ -9,13 +9,13 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '1.3.2'
|
||||
majorVersion: '1.4.0'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||
sentryOrg: 'servarr'
|
||||
sentryUrl: 'https://sentry.servarr.com'
|
||||
dotnetVersion: '6.0.405'
|
||||
dotnetVersion: '6.0.408'
|
||||
innoVersion: '6.2.0'
|
||||
nodeVersion: '16.x'
|
||||
windowsImage: 'windows-2022'
|
||||
|
||||
@@ -112,6 +112,12 @@ class TextInput extends Component {
|
||||
this._isMouseTarget = false;
|
||||
};
|
||||
|
||||
onWheel = () => {
|
||||
if (this.props.type === 'number') {
|
||||
this._input.blur();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
@@ -161,6 +167,7 @@ class TextInput extends Component {
|
||||
onKeyUp={this.onKeyUp}
|
||||
onMouseDown={this.onMouseDown}
|
||||
onMouseUp={this.onMouseUp}
|
||||
onWheel={this.onWheel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ function TagDetailsModalContent(props) {
|
||||
indexers,
|
||||
notifications,
|
||||
indexerProxies,
|
||||
applications,
|
||||
onModalClose,
|
||||
onDeleteTagPress
|
||||
} = props;
|
||||
@@ -79,6 +80,21 @@ function TagDetailsModalContent(props) {
|
||||
}
|
||||
</FieldSet>
|
||||
}
|
||||
|
||||
{
|
||||
!!applications.length &&
|
||||
<FieldSet legend={translate('Applications')}>
|
||||
{
|
||||
applications.map((item) => {
|
||||
return (
|
||||
<div key={item.id}>
|
||||
{item.name}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</FieldSet>
|
||||
}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
@@ -110,6 +126,7 @@ TagDetailsModalContent.propTypes = {
|
||||
indexers: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
notifications: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
indexerProxies: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
applications: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onDeleteTagPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -18,16 +18,24 @@ function createMatchingIndexersSelector() {
|
||||
|
||||
function createMatchingIndexerProxiesSelector() {
|
||||
return createSelector(
|
||||
(state, { notificationIds }) => notificationIds,
|
||||
(state) => state.settings.notifications.items,
|
||||
(state, { indexerProxyIds }) => indexerProxyIds,
|
||||
(state) => state.settings.indexerProxies.items,
|
||||
findMatchingItems
|
||||
);
|
||||
}
|
||||
|
||||
function createMatchingNotificationsSelector() {
|
||||
return createSelector(
|
||||
(state, { indexerProxyIds }) => indexerProxyIds,
|
||||
(state) => state.settings.indexerProxies.items,
|
||||
(state, { notificationIds }) => notificationIds,
|
||||
(state) => state.settings.notifications.items,
|
||||
findMatchingItems
|
||||
);
|
||||
}
|
||||
|
||||
function createMatchingApplicationsSelector() {
|
||||
return createSelector(
|
||||
(state, { applicationIds }) => applicationIds,
|
||||
(state) => state.settings.applications.items,
|
||||
findMatchingItems
|
||||
);
|
||||
}
|
||||
@@ -37,11 +45,13 @@ function createMapStateToProps() {
|
||||
createMatchingIndexersSelector(),
|
||||
createMatchingIndexerProxiesSelector(),
|
||||
createMatchingNotificationsSelector(),
|
||||
(indexers, indexerProxies, notifications) => {
|
||||
createMatchingApplicationsSelector(),
|
||||
(indexers, indexerProxies, notifications, applications) => {
|
||||
return {
|
||||
indexers,
|
||||
indexerProxies,
|
||||
notifications
|
||||
notifications,
|
||||
applications
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -55,7 +55,8 @@ class Tag extends Component {
|
||||
label,
|
||||
notificationIds,
|
||||
indexerIds,
|
||||
indexerProxyIds
|
||||
indexerProxyIds,
|
||||
applicationIds
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
@@ -66,7 +67,8 @@ class Tag extends Component {
|
||||
const isTagUsed = !!(
|
||||
indexerIds.length ||
|
||||
notificationIds.length ||
|
||||
indexerProxyIds.length
|
||||
indexerProxyIds.length ||
|
||||
applicationIds.length
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -102,6 +104,13 @@ class Tag extends Component {
|
||||
{indexerProxyIds.length} {indexerProxyIds.length > 1 ? translate('IndexerProxies') : translate('IndexerProxy')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!!applicationIds.length &&
|
||||
<div>
|
||||
{applicationIds.length} {applicationIds.length > 1 ? translate('Applications') : translate('Application')}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -118,6 +127,7 @@ class Tag extends Component {
|
||||
indexerIds={indexerIds}
|
||||
notificationIds={notificationIds}
|
||||
indexerProxyIds={indexerProxyIds}
|
||||
applicationIds={applicationIds}
|
||||
isOpen={isDetailsModalOpen}
|
||||
onModalClose={this.onDetailsModalClose}
|
||||
onDeleteTagPress={this.onDeleteTagPress}
|
||||
@@ -143,13 +153,15 @@ Tag.propTypes = {
|
||||
notificationIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
indexerIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
indexerProxyIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
applicationIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
onConfirmDeleteTag: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
Tag.defaultProps = {
|
||||
indexerIds: [],
|
||||
notificationIds: [],
|
||||
indexerProxyIds: []
|
||||
indexerProxyIds: [],
|
||||
applicationIds: []
|
||||
};
|
||||
|
||||
export default Tag;
|
||||
|
||||
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchIndexerProxies, fetchNotifications } from 'Store/Actions/settingsActions';
|
||||
import { fetchApplications, fetchIndexerProxies, fetchNotifications } from 'Store/Actions/settingsActions';
|
||||
import { fetchTagDetails } from 'Store/Actions/tagActions';
|
||||
import Tags from './Tags';
|
||||
|
||||
@@ -27,7 +27,8 @@ function createMapStateToProps() {
|
||||
const mapDispatchToProps = {
|
||||
dispatchFetchTagDetails: fetchTagDetails,
|
||||
dispatchFetchNotifications: fetchNotifications,
|
||||
dispatchFetchIndexerProxies: fetchIndexerProxies
|
||||
dispatchFetchIndexerProxies: fetchIndexerProxies,
|
||||
dispatchFetchApplications: fetchApplications
|
||||
};
|
||||
|
||||
class MetadatasConnector extends Component {
|
||||
@@ -39,12 +40,14 @@ class MetadatasConnector extends Component {
|
||||
const {
|
||||
dispatchFetchTagDetails,
|
||||
dispatchFetchNotifications,
|
||||
dispatchFetchIndexerProxies
|
||||
dispatchFetchIndexerProxies,
|
||||
dispatchFetchApplications
|
||||
} = this.props;
|
||||
|
||||
dispatchFetchTagDetails();
|
||||
dispatchFetchNotifications();
|
||||
dispatchFetchIndexerProxies();
|
||||
dispatchFetchApplications();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -62,7 +65,8 @@ class MetadatasConnector extends Component {
|
||||
MetadatasConnector.propTypes = {
|
||||
dispatchFetchTagDetails: PropTypes.func.isRequired,
|
||||
dispatchFetchNotifications: PropTypes.func.isRequired,
|
||||
dispatchFetchIndexerProxies: PropTypes.func.isRequired
|
||||
dispatchFetchIndexerProxies: PropTypes.func.isRequired,
|
||||
dispatchFetchApplications: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(MetadatasConnector);
|
||||
|
||||
@@ -144,7 +144,7 @@ export const defaultState = {
|
||||
},
|
||||
|
||||
category: function(item) {
|
||||
if (item.categories.length > 0) {
|
||||
if (item.categories !== undefined && item.categories.length > 0) {
|
||||
const sortedCats = item.categories.filter((cat) => cat.name !== undefined).sort((c) => c.id);
|
||||
const firstCat = sortedCats[0];
|
||||
|
||||
|
||||
@@ -76,27 +76,27 @@ module.exports = {
|
||||
defaultButtonBackgroundColor: '#333',
|
||||
defaultBorderColor: '#eaeaea',
|
||||
defaultHoverBackgroundColor: '#444',
|
||||
defaultHoverBorderColor: '#d6d6d6;',
|
||||
defaultHoverBorderColor: '#d6d6d6',
|
||||
|
||||
primaryBackgroundColor: '#5d9cec',
|
||||
primaryBorderColor: '#5899eb',
|
||||
primaryHoverBackgroundColor: '#4b91ea',
|
||||
primaryHoverBorderColor: '#3483e7;',
|
||||
primaryHoverBorderColor: '#3483e7',
|
||||
|
||||
successBackgroundColor: '#27c24c',
|
||||
successBorderColor: '#26be4a',
|
||||
successHoverBackgroundColor: '#24b145',
|
||||
successHoverBorderColor: '#1f9c3d;',
|
||||
successHoverBorderColor: '#1f9c3d',
|
||||
|
||||
warningBackgroundColor: '#ff902b',
|
||||
warningBorderColor: '#ff8d26',
|
||||
warningHoverBackgroundColor: '#ff8517',
|
||||
warningHoverBorderColor: '#fc7800;',
|
||||
warningHoverBorderColor: '#fc7800',
|
||||
|
||||
dangerBackgroundColor: '#f05050',
|
||||
dangerBorderColor: '#f04b4b',
|
||||
dangerHoverBackgroundColor: '#ee3d3d',
|
||||
dangerHoverBorderColor: '#ec2626;',
|
||||
dangerHoverBorderColor: '#ec2626',
|
||||
|
||||
iconButtonDisabledColor: '#7a7a7a',
|
||||
iconButtonHoverColor: '#666',
|
||||
|
||||
@@ -76,27 +76,27 @@ module.exports = {
|
||||
defaultButtonBackgroundColor: '#fff',
|
||||
defaultBorderColor: '#eaeaea',
|
||||
defaultHoverBackgroundColor: '#f5f5f5',
|
||||
defaultHoverBorderColor: '#d6d6d6;',
|
||||
defaultHoverBorderColor: '#d6d6d6',
|
||||
|
||||
primaryBackgroundColor: '#5d9cec',
|
||||
primaryBorderColor: '#5899eb',
|
||||
primaryHoverBackgroundColor: '#4b91ea',
|
||||
primaryHoverBorderColor: '#3483e7;',
|
||||
primaryHoverBorderColor: '#3483e7',
|
||||
|
||||
successBackgroundColor: '#27c24c',
|
||||
successBorderColor: '#26be4a',
|
||||
successHoverBackgroundColor: '#24b145',
|
||||
successHoverBorderColor: '#1f9c3d;',
|
||||
successHoverBorderColor: '#1f9c3d',
|
||||
|
||||
warningBackgroundColor: '#ff902b',
|
||||
warningBorderColor: '#ff8d26',
|
||||
warningHoverBackgroundColor: '#ff8517',
|
||||
warningHoverBorderColor: '#fc7800;',
|
||||
warningHoverBorderColor: '#fc7800',
|
||||
|
||||
dangerBackgroundColor: '#f05050',
|
||||
dangerBorderColor: '#f04b4b',
|
||||
dangerHoverBackgroundColor: '#ee3d3d',
|
||||
dangerHoverBorderColor: '#ec2626;',
|
||||
dangerHoverBorderColor: '#ec2626',
|
||||
|
||||
iconButtonDisabledColor: '#7a7a7a',
|
||||
iconButtonHoverColor: '#666',
|
||||
|
||||
91
package.json
91
package.json
@@ -5,7 +5,7 @@
|
||||
"scripts": {
|
||||
"build": "webpack --config ./frontend/build/webpack.config.js",
|
||||
"prebuild": "yarn clean",
|
||||
"clean": "rimraf ./_output/UI && rimraf \"**/*.js.map\"",
|
||||
"clean": "rimraf ./_output/UI && rimraf -g \"**/*.js.map\"",
|
||||
"start": "webpack --watch --config ./frontend/build/webpack.config.js",
|
||||
"watch": "webpack --watch --config ./frontend/build/webpack.config.js",
|
||||
"lint": "eslint --config frontend/.eslintrc.js --ignore-path frontend/.eslintignore frontend/",
|
||||
@@ -26,36 +26,36 @@
|
||||
"not chrome < 60"
|
||||
],
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "6.2.1",
|
||||
"@fortawesome/fontawesome-svg-core": "6.2.1",
|
||||
"@fortawesome/free-regular-svg-icons": "6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "6.2.1",
|
||||
"@fortawesome/fontawesome-free": "6.4.0",
|
||||
"@fortawesome/fontawesome-svg-core": "6.4.0",
|
||||
"@fortawesome/free-regular-svg-icons": "6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "6.4.0",
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"@juggle/resize-observer": "3.4.0",
|
||||
"@microsoft/signalr": "6.0.13",
|
||||
"@sentry/browser": "7.28.0",
|
||||
"@sentry/integrations": "7.28.0",
|
||||
"@types/jest": "29.2.5",
|
||||
"@types/node": "18.11.18",
|
||||
"@types/react": "18.0.26",
|
||||
"@types/react-dom": "18.0.10",
|
||||
"chart.js": "4.1.1",
|
||||
"@microsoft/signalr": "6.0.16",
|
||||
"@sentry/browser": "7.46.0",
|
||||
"@sentry/integrations": "7.46.0",
|
||||
"@types/jest": "29.5.0",
|
||||
"@types/node": "18.15.11",
|
||||
"@types/react": "18.0.31",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"chart.js": "4.2.1",
|
||||
"classnames": "2.3.2",
|
||||
"clipboard": "2.0.11",
|
||||
"connected-react-router": "6.9.3",
|
||||
"element-class": "0.2.2",
|
||||
"filesize": "10.0.6",
|
||||
"filesize": "10.0.7",
|
||||
"history": "4.10.1",
|
||||
"https-browserify": "1.0.0",
|
||||
"jdu": "1.0.0",
|
||||
"jquery": "3.6.2",
|
||||
"jquery": "3.6.4",
|
||||
"lodash": "4.17.21",
|
||||
"mobile-detect": "1.4.5",
|
||||
"moment": "2.29.4",
|
||||
"mousetrap": "1.6.5",
|
||||
"normalize.css": "8.0.1",
|
||||
"prop-types": "15.8.1",
|
||||
"qs": "6.11.0",
|
||||
"qs": "6.11.1",
|
||||
"react": "17.0.2",
|
||||
"react-addons-shallow-compare": "15.6.3",
|
||||
"react-async-script": "1.2.0",
|
||||
@@ -67,7 +67,7 @@
|
||||
"react-dnd-touch-backend": "14.1.1",
|
||||
"react-document-title": "2.0.3",
|
||||
"react-dom": "17.0.2",
|
||||
"react-focus-lock": "2.9.2",
|
||||
"react-focus-lock": "2.9.4",
|
||||
"react-google-recaptcha": "2.1.0",
|
||||
"react-lazyload": "3.2.0",
|
||||
"react-measure": "1.4.7",
|
||||
@@ -77,78 +77,77 @@
|
||||
"react-router-dom": "5.2.0",
|
||||
"react-text-truncate": "0.19.0",
|
||||
"react-use-measure": "2.1.1",
|
||||
"react-virtualized": "9.21.1",
|
||||
"react-virtualized": "9.22.3",
|
||||
"react-window": "1.8.8",
|
||||
"redux": "4.2.0",
|
||||
"redux": "4.2.1",
|
||||
"redux-actions": "2.6.5",
|
||||
"redux-batched-actions": "0.5.0",
|
||||
"redux-localstorage": "0.4.1",
|
||||
"redux-thunk": "2.4.2",
|
||||
"reselect": "4.1.7",
|
||||
"stacktrace-js": "2.0.2",
|
||||
"typescript": "4.9.4"
|
||||
"typescript": "5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.20.5",
|
||||
"@babel/eslint-parser": "7.19.1",
|
||||
"@babel/core": "7.21.3",
|
||||
"@babel/eslint-parser": "7.21.3",
|
||||
"@babel/plugin-proposal-class-properties": "7.18.6",
|
||||
"@babel/plugin-proposal-decorators": "7.20.5",
|
||||
"@babel/plugin-proposal-decorators": "7.21.0",
|
||||
"@babel/plugin-proposal-export-default-from": "7.18.10",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.18.9",
|
||||
"@babel/plugin-proposal-function-sent": "7.18.6",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
|
||||
"@babel/plugin-proposal-numeric-separator": "7.18.6",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.18.9",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.21.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "7.18.6",
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@babel/preset-env": "7.20.2",
|
||||
"@babel/preset-react": "7.18.6",
|
||||
"@babel/preset-typescript": "7.18.6",
|
||||
"@babel/preset-typescript": "7.21.0",
|
||||
"@types/react-window": "1.8.5",
|
||||
"@typescript-eslint/eslint-plugin": "5.48.1",
|
||||
"@typescript-eslint/parser": "5.48.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.57.0",
|
||||
"@typescript-eslint/parser": "5.57.0",
|
||||
"are-you-es5": "2.1.2",
|
||||
"autoprefixer": "10.4.13",
|
||||
"babel-eslint": "10.1.0",
|
||||
"babel-loader": "9.1.0",
|
||||
"autoprefixer": "10.4.14",
|
||||
"babel-loader": "9.1.2",
|
||||
"babel-plugin-inline-classnames": "2.0.1",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
|
||||
"core-js": "3.26.1",
|
||||
"core-js": "3.29.1",
|
||||
"css-loader": "6.7.3",
|
||||
"css-modules-typescript-loader": "4.0.1",
|
||||
"eslint": "8.30.0",
|
||||
"eslint-config-prettier": "8.6.0",
|
||||
"eslint": "8.37.0",
|
||||
"eslint-config-prettier": "8.8.0",
|
||||
"eslint-plugin-filenames": "1.3.2",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
"eslint-plugin-react": "7.31.11",
|
||||
"eslint-plugin-react": "7.32.2",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-plugin-simple-import-sort": "8.0.0",
|
||||
"eslint-plugin-simple-import-sort": "10.0.0",
|
||||
"file-loader": "6.2.0",
|
||||
"filemanager-webpack-plugin": "8.0.0",
|
||||
"fork-ts-checker-webpack-plugin": "7.2.14",
|
||||
"fork-ts-checker-webpack-plugin": "8.0.0",
|
||||
"html-webpack-plugin": "5.5.0",
|
||||
"loader-utils": "^3.2.1",
|
||||
"mini-css-extract-plugin": "2.7.2",
|
||||
"postcss": "8.4.20",
|
||||
"mini-css-extract-plugin": "2.7.5",
|
||||
"postcss": "8.4.21",
|
||||
"postcss-color-function": "4.1.0",
|
||||
"postcss-loader": "7.0.2",
|
||||
"postcss-loader": "7.1.0",
|
||||
"postcss-mixins": "9.0.4",
|
||||
"postcss-nested": "6.0.0",
|
||||
"postcss-nested": "6.0.1",
|
||||
"postcss-simple-vars": "7.0.1",
|
||||
"postcss-url": "10.1.3",
|
||||
"prettier": "2.8.2",
|
||||
"prettier": "2.8.7",
|
||||
"require-nocache": "1.0.0",
|
||||
"rimraf": "3.0.2",
|
||||
"rimraf": "4.4.1",
|
||||
"run-sequence": "2.2.1",
|
||||
"streamqueue": "1.1.2",
|
||||
"style-loader": "3.3.1",
|
||||
"style-loader": "3.3.2",
|
||||
"stylelint": "14.16.0",
|
||||
"stylelint-order": "5.0.0",
|
||||
"ts-loader": "9.4.2",
|
||||
"typescript-plugin-css-modules": "4.1.1",
|
||||
"typescript-plugin-css-modules": "5.0.0",
|
||||
"url-loader": "4.1.1",
|
||||
"webpack": "5.75.0",
|
||||
"webpack": "5.77.0",
|
||||
"webpack-cli": "5.0.1",
|
||||
"webpack-livereload-plugin": "3.0.2"
|
||||
}
|
||||
|
||||
@@ -74,6 +74,8 @@
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
|
||||
<Deterministic Condition="$(AssemblyVersion.EndsWith('*'))">False</Deterministic>
|
||||
|
||||
<PathMap>$(MSBuildProjectDirectory)=./$(MSBuildProjectName)/</PathMap>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Set the AssemblyConfiguration attribute for projects -->
|
||||
|
||||
101
src/NzbDrone.Common.Test/Http/CookieUtilFixture.cs
Normal file
101
src/NzbDrone.Common.Test/Http/CookieUtilFixture.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
[TestFixture]
|
||||
public class CookieUtilFixture
|
||||
{
|
||||
[Test]
|
||||
public void CookieHeaderToDictionaryGood()
|
||||
{
|
||||
// valid cookies with non-alpha characters in the value
|
||||
var cookieHeader = "__cfduid=d6237f041586694295; __cf_bm=TlOng/xyqckk-TMen38z+0RFYA7YA=";
|
||||
var expectedCookieDictionary = new Dictionary<string, string>
|
||||
{
|
||||
{ "__cfduid", "d6237f041586694295" },
|
||||
{ "__cf_bm", "TlOng/xyqckk-TMen38z+0RFYA7YA=" }
|
||||
};
|
||||
CollectionAssert.AreEqual(expectedCookieDictionary, CookieUtil.CookieHeaderToDictionary(cookieHeader));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CookieHeaderToDictionaryDuplicateKeys()
|
||||
{
|
||||
// cookie with duplicate keys and whitespace separator instead of ;
|
||||
var cookieHeader = "__cfduid=d6237f041586694295; __cf_bm=TlOng/xyqckk-TMen38z+0RFYA7YA= __cf_bm=test";
|
||||
var expectedCookieDictionary = new Dictionary<string, string>
|
||||
{
|
||||
{ "__cfduid", "d6237f041586694295" },
|
||||
{ "__cf_bm", "test" } // we always assume the latest value is the most recent
|
||||
};
|
||||
CollectionAssert.AreEqual(expectedCookieDictionary, CookieUtil.CookieHeaderToDictionary(cookieHeader));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CookieHeaderToDictionaryMalformed()
|
||||
{
|
||||
// malformed cookies
|
||||
var cookieHeader = "__cfduidd6237f041586694295; __cf_;bm TlOng; good_cookie=value";
|
||||
var expectedCookieDictionary = new Dictionary<string, string> { { "good_cookie", "value" }, };
|
||||
CollectionAssert.AreEqual(expectedCookieDictionary, CookieUtil.CookieHeaderToDictionary(cookieHeader));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[SuppressMessage("ReSharper", "CollectionNeverUpdated.Local")]
|
||||
public void CookieHeaderToDictionaryNull()
|
||||
{
|
||||
// null cookie header
|
||||
var expectedCookieDictionary = new Dictionary<string, string>();
|
||||
CollectionAssert.AreEqual(expectedCookieDictionary, CookieUtil.CookieHeaderToDictionary(null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CookieDictionaryToHeaderGood()
|
||||
{
|
||||
// valid cookies with non-alpha characters in the value
|
||||
var cookieDictionary = new Dictionary<string, string>
|
||||
{
|
||||
{ "__cfduid", "d6237f041586694295" },
|
||||
{ "__cf_bm", "TlOng/xyqckk-TMen38z+0RFYA7YA=" }
|
||||
};
|
||||
var expectedCookieHeader = "__cfduid=d6237f041586694295; __cf_bm=TlOng/xyqckk-TMen38z+0RFYA7YA=";
|
||||
CollectionAssert.AreEqual(expectedCookieHeader, CookieUtil.CookieDictionaryToHeader(cookieDictionary));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CookieDictionaryToHeaderMalformed1()
|
||||
{
|
||||
// malformed key
|
||||
var cookieDictionary = new Dictionary<string, string>
|
||||
{
|
||||
{ "__cf_=bm", "34234234" }
|
||||
};
|
||||
var ex = Assert.Throws<FormatException>(() => CookieUtil.CookieDictionaryToHeader(cookieDictionary));
|
||||
Assert.AreEqual("The cookie '__cf_=bm=34234234' is malformed.", ex.Message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CookieDictionaryToHeaderMalformed2()
|
||||
{
|
||||
// malformed value
|
||||
var cookieDictionary = new Dictionary<string, string>
|
||||
{
|
||||
{ "__cf_bm", "34234 234" }
|
||||
};
|
||||
var ex = Assert.Throws<FormatException>(() => CookieUtil.CookieDictionaryToHeader(cookieDictionary));
|
||||
Assert.AreEqual("The cookie '__cf_bm=34234 234' is malformed.", ex.Message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CookieDictionaryToHeaderNull()
|
||||
{
|
||||
// null cookie dictionary
|
||||
var expectedCookieHeader = "";
|
||||
CollectionAssert.AreEqual(expectedCookieHeader, CookieUtil.CookieDictionaryToHeader(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,6 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Common.Composition
|
||||
@@ -19,16 +17,17 @@ namespace NzbDrone.Common.Composition
|
||||
RegisterSQLiteResolver();
|
||||
}
|
||||
|
||||
public static IEnumerable<Assembly> Load(IEnumerable<string> assemblies)
|
||||
public static IList<Assembly> Load(IList<string> assemblyNames)
|
||||
{
|
||||
var toLoad = assemblies.ToList();
|
||||
var toLoad = assemblyNames.ToList();
|
||||
toLoad.Add("Prowlarr.Common");
|
||||
toLoad.Add(OsInfo.IsWindows ? "Prowlarr.Windows" : "Prowlarr.Mono");
|
||||
|
||||
var startupPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
return toLoad.Select(x =>
|
||||
AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.Combine(startupPath, $"{x}.dll")));
|
||||
return toLoad
|
||||
.Select(x => AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.Combine(startupPath, $"{x}.dll")))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static Assembly ContainerResolveEventHandler(object sender, ResolveEventArgs args)
|
||||
|
||||
@@ -351,7 +351,7 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
}
|
||||
|
||||
public string GetPathRoot(string path)
|
||||
public virtual string GetPathRoot(string path)
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath();
|
||||
|
||||
@@ -478,8 +478,7 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
return mounts.Where(drive => drive.RootDirectory.PathEquals(path) ||
|
||||
drive.RootDirectory.IsParentPath(path))
|
||||
.OrderByDescending(drive => drive.RootDirectory.Length)
|
||||
.FirstOrDefault();
|
||||
.MaxBy(drive => drive.RootDirectory.Length);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -16,18 +16,7 @@ namespace NzbDrone.Common.Extensions
|
||||
return false;
|
||||
}
|
||||
|
||||
Uri uri;
|
||||
if (!Uri.TryCreate(path, UriKind.Absolute, out uri))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!uri.IsWellFormedOriginalString())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return Uri.TryCreate(path, UriKind.Absolute, out var uri) && uri.IsWellFormedOriginalString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
||||
// NOTE: we are not checking non-ascii characters and we should
|
||||
private static readonly Regex _CookieRegex = new Regex(@"([^\(\)<>@,;:\\""/\[\]\?=\{\}\s]+)=([^,;\\""\s]+)");
|
||||
private static readonly Regex CookieRegex = new (@"([^\(\)<>@,;:\\""/\[\]\?=\{\}\s]+)=([^,;\\""\s]+)");
|
||||
private static readonly string[] FilterProps = { "COMMENT", "COMMENTURL", "DISCORD", "DOMAIN", "EXPIRES", "MAX-AGE", "PATH", "PORT", "SECURE", "VERSION", "HTTPONLY", "SAMESITE" };
|
||||
private static readonly char[] InvalidKeyChars = { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t', '\n' };
|
||||
private static readonly char[] InvalidValueChars = { '"', ',', ';', '\\', ' ', '\t', '\n' };
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Common.Http
|
||||
return cookieDictionary;
|
||||
}
|
||||
|
||||
var matches = _CookieRegex.Match(cookieHeader);
|
||||
var matches = CookieRegex.Match(cookieHeader);
|
||||
while (matches.Success)
|
||||
{
|
||||
if (matches.Groups.Count > 2 && !FilterProps.Contains(matches.Groups[1].Value.ToUpperInvariant()))
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace NzbDrone.Common.Http
|
||||
public Dictionary<string, string> Segments { get; private set; }
|
||||
public HttpHeader Headers { get; private set; }
|
||||
public bool SuppressHttpError { get; set; }
|
||||
public IEnumerable<HttpStatusCode> SuppressHttpErrorStatusCodes { get; set; }
|
||||
public bool LogHttpError { get; set; }
|
||||
public bool UseSimplifiedUserAgent { get; set; }
|
||||
public bool AllowAutoRedirect { get; set; }
|
||||
@@ -108,6 +109,7 @@ namespace NzbDrone.Common.Http
|
||||
request.Method = Method;
|
||||
request.Encoding = Encoding;
|
||||
request.SuppressHttpError = SuppressHttpError;
|
||||
request.SuppressHttpErrorStatusCodes = SuppressHttpErrorStatusCodes;
|
||||
request.LogHttpError = LogHttpError;
|
||||
request.UseSimplifiedUserAgent = UseSimplifiedUserAgent;
|
||||
request.AllowAutoRedirect = AllowAutoRedirect;
|
||||
|
||||
@@ -14,14 +14,14 @@ namespace NzbDrone.Common.OAuth
|
||||
{
|
||||
get
|
||||
{
|
||||
var parameters = this.Where(p => p.Name.Equals(name));
|
||||
var parameters = this.Where(p => p.Name.Equals(name)).ToArray();
|
||||
|
||||
if (!parameters.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parameters.Count() == 1)
|
||||
if (parameters.Length == 1)
|
||||
{
|
||||
return parameters.Single();
|
||||
}
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
<DefineConstants Condition="'$(RuntimeIdentifier)' == 'linux-musl-x64' or '$(RuntimeIdentifier)' == 'linux-musl-arm64'">ISMUSL</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DryIoc.dll" Version="5.3.3" />
|
||||
<PackageReference Include="DryIoc.dll" Version="5.3.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NLog" Version="5.1.0" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.0" />
|
||||
<PackageReference Include="Npgsql" Version="5.0.11" />
|
||||
<PackageReference Include="Sentry" Version="3.24.1" />
|
||||
<PackageReference Include="Sentry" Version="3.29.1" />
|
||||
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" />
|
||||
|
||||
826
src/NzbDrone.Core.Test/Files/Indexers/AnimeBytes/recentfeed.json
Normal file
826
src/NzbDrone.Core.Test/Files/Indexers/AnimeBytes/recentfeed.json
Normal file
@@ -0,0 +1,826 @@
|
||||
{
|
||||
"Results": 9999,
|
||||
"Pagination": {
|
||||
"Current": 1,
|
||||
"Max": 99,
|
||||
"Limit": {
|
||||
"Min": 15,
|
||||
"Coerced": 15,
|
||||
"Max": 50
|
||||
}
|
||||
},
|
||||
"Matches": 2,
|
||||
"Groups": [
|
||||
{
|
||||
"ID": 575,
|
||||
"CategoryName": "Anime",
|
||||
"FullName": "Cowboy Bebop: Tengoku no Tobira - Movie [2001]",
|
||||
"GroupName": "Movie",
|
||||
"SeriesID": "141",
|
||||
"SeriesName": "Cowboy Bebop: Tengoku no Tobira",
|
||||
"Artists": null,
|
||||
"Year": "2001",
|
||||
"Image": "https://mei.animebytes.tv/2bac1a04148be77ce41251d3cb44bbd5.jpg",
|
||||
"Synonymns": [
|
||||
"カウボーイビバップ天国の扉",
|
||||
"Cowboy Bebop: Knockin' on Heaven's Door"
|
||||
],
|
||||
"SynonymnsV2": {
|
||||
"Japanese": "カウボーイビバップ天国の扉",
|
||||
"Romaji": "",
|
||||
"Alternative": "Cowboy Bebop: Knockin' on Heaven's Door"
|
||||
},
|
||||
"Snatched": 4900,
|
||||
"Comments": 11,
|
||||
"Links": {
|
||||
"AniDB": "https://anidb.net/anime/219",
|
||||
"ANN": "https://www.animenewsnetwork.com/encyclopedia/anime.php?id=353",
|
||||
"Wikipedia": "https://en.wikipedia.org/wiki/Cowboy_Bebop:_The_Movie",
|
||||
"MAL": "https://myanimelist.net/anime/5"
|
||||
},
|
||||
"Votes": 572,
|
||||
"AvgVote": 8.3,
|
||||
"Associations": null,
|
||||
"Description": "Mars is under siege! Just before Halloween 2071, a terrorist bomb destroys a tanker truck on Highway One, close to the densely-populated crater city. There are casualties up to half a mile from the blast — 500 killed or injured by what appears to be a biochemical weapon. The reward for the bomber's capture is a massive 300,000,000 woolongs... and there are four humans and a dog who really need the money. Down on their luck as usual, the crew of the Bebop get on the case.\r\n\r\n[i]Note: The movie takes place between (in the time period of) the Cowboy Bebop episodes 22 and 23.[/i]",
|
||||
"DescriptionHTML": "Mars is under siege! Just before Halloween 2071, a terrorist bomb destroys a tanker truck on Highway One, close to the densely-populated crater city. There are casualties up to half a mile from the blast — 500 killed or injured by what appears to be a biochemical weapon. The reward for the bomber's capture is a massive 300,000,000 woolongs... and there are four humans and a dog who really need the money. Down on their luck as usual, the crew of the Bebop get on the case.<br />\r\n<br />\r\n<em>Note: The movie takes place between (in the time period of) the Cowboy Bebop episodes 22 and 23.</em>",
|
||||
"EpCount": 0,
|
||||
"StudioList": "Sunrise///28|BONES///35",
|
||||
"PastWeek": 2,
|
||||
"Incomplete": false,
|
||||
"Ongoing": false,
|
||||
"Tags": [
|
||||
"adventure",
|
||||
"comedy",
|
||||
"drama",
|
||||
"scifi",
|
||||
"seinen",
|
||||
"action"
|
||||
],
|
||||
"Torrents": [
|
||||
{
|
||||
"ID": 959397,
|
||||
"EditionData": {
|
||||
"EditionTitle": ""
|
||||
},
|
||||
"RawDownMultiplier": 0,
|
||||
"RawUpMultiplier": 1,
|
||||
"Link": "https://animebytes.tv/torrent/959397/download/somepass",
|
||||
"Property": "Blu-ray | MKV | h265 10-bit | 1080p | Opus 5.1 | Softsubs (Polarwindz) | Freeleech",
|
||||
"Snatched": 16,
|
||||
"Seeders": 5,
|
||||
"Leechers": 1,
|
||||
"Status": 0,
|
||||
"Size": 13090646841,
|
||||
"FileCount": 1,
|
||||
"FileList": [
|
||||
{
|
||||
"filename": "[Polarwindz] Cowboy Bebop The Movie - Knockin' on Heaven's Door [BD 1080p x265 10bit Opus 5.1].mkv",
|
||||
"size": 13090646841
|
||||
}
|
||||
],
|
||||
"UploadTime": "2023-04-02 05:00:43"
|
||||
},
|
||||
{
|
||||
"ID": 909565,
|
||||
"EditionData": {
|
||||
"EditionTitle": ""
|
||||
},
|
||||
"RawDownMultiplier": 0,
|
||||
"RawUpMultiplier": 1,
|
||||
"Link": "https://animebytes.tv/torrent/909565/download/somepass",
|
||||
"Property": "Blu-ray | MKV | h265 10-bit | 1080p | Opus 5.1 | Dual Audio | Softsubs (Yūrei) | Freeleech",
|
||||
"Snatched": 29,
|
||||
"Seeders": 6,
|
||||
"Leechers": 0,
|
||||
"Status": 0,
|
||||
"Size": 15717521349,
|
||||
"FileCount": 1,
|
||||
"FileList": [
|
||||
{
|
||||
"filename": "Cowboy Bebop - Tengoku no Tobira.mkv",
|
||||
"size": 15717521349
|
||||
}
|
||||
],
|
||||
"UploadTime": "2020-09-03 18:04:38"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 2709,
|
||||
"CategoryName": "Anime",
|
||||
"FullName": "BLEACH - TV Series [2004]",
|
||||
"GroupName": "TV Series",
|
||||
"SeriesID": "191",
|
||||
"SeriesName": "BLEACH",
|
||||
"Artists": null,
|
||||
"Year": "2004",
|
||||
"Image": "https://mei.animebytes.tv/997c8ec3ca0e70254b182b0a176f0161.jpg",
|
||||
"Synonymns": [
|
||||
"ブリーチ"
|
||||
],
|
||||
"SynonymnsV2": {
|
||||
"Japanese": "ブリーチ",
|
||||
"Romaji": "",
|
||||
"Alternative": ""
|
||||
},
|
||||
"Snatched": 22653,
|
||||
"Comments": 51,
|
||||
"Links": {
|
||||
"AniDB": "https://anidb.net/anime/2369",
|
||||
"ANN": "https://www.animenewsnetwork.com/encyclopedia/anime.php?id=4240",
|
||||
"Wikipedia": "https://en.wikipedia.org/wiki/Bleach_(anime)",
|
||||
"MAL": "https://myanimelist.net/anime/269"
|
||||
},
|
||||
"Votes": 530,
|
||||
"AvgVote": 7.3,
|
||||
"Associations": null,
|
||||
"Description": "Kurosaki Ichigo is a teenager gifted with the ability to see spirits. His life is drastically changed by the sudden appearance of a Shinigami (literally, Death god) - one who governs the flow of souls between the human world and the afterlife - named Kuchiki Rukia, who arrives in search of a Hollow, a dangerous lost soul. When Rukia is severely wounded while trying to defeat the Hollow, she attempts to transfer half of her Reiatsu (literally, Spiritual pressure) energy to Ichigo so that he can defeat the Hollow. However, Ichigo takes almost all of her energy, transforming into a Shinigami and allowing him to defeat the Hollow with ease. With her powers diminished, Rukia is left stranded in the human world until she can recover her strength. In the meantime, Ichigo must take over Rukia's role as a Shinigami, battling Hollows and guiding souls to the afterlife realm known as the Soul Society.",
|
||||
"DescriptionHTML": "Kurosaki Ichigo is a teenager gifted with the ability to see spirits. His life is drastically changed by the sudden appearance of a Shinigami (literally, Death god) - one who governs the flow of souls between the human world and the afterlife - named Kuchiki Rukia, who arrives in search of a Hollow, a dangerous lost soul. When Rukia is severely wounded while trying to defeat the Hollow, she attempts to transfer half of her Reiatsu (literally, Spiritual pressure) energy to Ichigo so that he can defeat the Hollow. However, Ichigo takes almost all of her energy, transforming into a Shinigami and allowing him to defeat the Hollow with ease. With her powers diminished, Rukia is left stranded in the human world until she can recover her strength. In the meantime, Ichigo must take over Rukia's role as a Shinigami, battling Hollows and guiding souls to the afterlife realm known as the Soul Society.",
|
||||
"EpCount": 366,
|
||||
"StudioList": "Studio Pierrot///45",
|
||||
"PastWeek": 26,
|
||||
"Incomplete": false,
|
||||
"Ongoing": false,
|
||||
"Tags": [
|
||||
"adventure",
|
||||
"comedy",
|
||||
"fantasy",
|
||||
"martial.arts",
|
||||
"school.life",
|
||||
"shounen",
|
||||
"super.power",
|
||||
"contemporary.fantasy",
|
||||
"swordplay",
|
||||
"action",
|
||||
"supernatural"
|
||||
],
|
||||
"Torrents": [
|
||||
{
|
||||
"ID": 1031199,
|
||||
"EditionData": {
|
||||
"EditionTitle": "Season 02: The Entry (021-041)"
|
||||
},
|
||||
"RawDownMultiplier": 0,
|
||||
"RawUpMultiplier": 1,
|
||||
"Link": "https://animebytes.tv/torrent/1031199/download/somepass",
|
||||
"Property": "Blu-ray | MKV | h265 10-bit | 1080p | AAC 2.0 | Dual Audio | Softsubs (GHOST) | Freeleech",
|
||||
"Snatched": 20,
|
||||
"Seeders": 24,
|
||||
"Leechers": 0,
|
||||
"Status": 0,
|
||||
"Size": 19584943785,
|
||||
"FileCount": 21,
|
||||
"FileList": [
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 021 [BD HEVC 10bit Dual Audio AC3][035452C3].mkv",
|
||||
"size": 880693454
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 022 [BD HEVC 10bit Dual Audio AC3][0E923AAD].mkv",
|
||||
"size": 851531918
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 023 [BD HEVC 10bit Dual Audio AC3][604A0EC6].mkv",
|
||||
"size": 882518038
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 024 [BD HEVC 10bit Dual Audio AC3][ABABE9B3].mkv",
|
||||
"size": 837335522
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 025 [BD HEVC 10bit Dual Audio AC3][17AEB0C1].mkv",
|
||||
"size": 933034706
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 026 [BD HEVC 10bit Dual Audio AC3][E6F0017C].mkv",
|
||||
"size": 891916996
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 027 [BD HEVC 10bit Dual Audio AC3][317791C7].mkv",
|
||||
"size": 896856044
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 028 [BD HEVC 10bit Dual Audio AC3][5CA4DF06].mkv",
|
||||
"size": 1010510386
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 029 [BD HEVC 10bit Dual Audio AC3][549CF25A].mkv",
|
||||
"size": 995648398
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 030 [BD HEVC 10bit Dual Audio AC3][CED479F9].mkv",
|
||||
"size": 991633973
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 031 [BD HEVC 10bit Dual Audio AC3][1EEFAB0E].mkv",
|
||||
"size": 950195341
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 032 [BD HEVC 10bit Dual Audio AC3][C6F3C39A].mkv",
|
||||
"size": 859218784
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 033 [BD HEVC 10bit Dual Audio AC3][A8424897].mkv",
|
||||
"size": 933230823
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 034 [BD HEVC 10bit Dual Audio AC3][96C94DB8].mkv",
|
||||
"size": 818179634
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 035 [BD HEVC 10bit Dual Audio AC3][A07831AE].mkv",
|
||||
"size": 882457138
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 036 [BD HEVC 10bit Dual Audio AC3][D984C169].mkv",
|
||||
"size": 895683290
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 037 [BD HEVC 10bit Dual Audio AC3][32C05A7E].mkv",
|
||||
"size": 970033716
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 038 [BD HEVC 10bit Dual Audio AC3][824B3EEC].mkv",
|
||||
"size": 956573899
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 039 [BD HEVC 10bit Dual Audio AC3][A45233DF].mkv",
|
||||
"size": 1238240707
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 040 [BD HEVC 10bit Dual Audio AC3][5334A7F1].mkv",
|
||||
"size": 894491562
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 041 [BD HEVC 10bit Dual Audio AC3][A4F17363].mkv",
|
||||
"size": 1014959456
|
||||
}
|
||||
],
|
||||
"UploadTime": "2023-03-03 03:03:17"
|
||||
},
|
||||
{
|
||||
"ID": 1031203,
|
||||
"EditionData": {
|
||||
"EditionTitle": "Season 03: The Rescue (042-063)"
|
||||
},
|
||||
"RawDownMultiplier": 0,
|
||||
"RawUpMultiplier": 1,
|
||||
"Link": "https://animebytes.tv/torrent/1031203/download/somepass",
|
||||
"Property": "Blu-ray | MKV | h265 10-bit | 1080p | AC3 2.0 | Dual Audio | Softsubs (GHOST) | Freeleech",
|
||||
"Snatched": 6,
|
||||
"Seeders": 12,
|
||||
"Leechers": 2,
|
||||
"Status": 0,
|
||||
"Size": 24498538059,
|
||||
"FileCount": 22,
|
||||
"FileList": [
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 042 [BD HEVC 10bit Dual Audio AC3][55763BF6].mkv",
|
||||
"size": 899825828
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 043 [BD HEVC 10bit Dual Audio AC3][70B71ECC].mkv",
|
||||
"size": 902256094
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 044 [BD HEVC 10bit Dual Audio AC3][35F5526B].mkv",
|
||||
"size": 885323344
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 045 [BD HEVC 10bit Dual Audio AC3][9C1DAE4E].mkv",
|
||||
"size": 1082465042
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 046 [BD HEVC 10bit Dual Audio AC3][869EF5B6].mkv",
|
||||
"size": 957433930
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 047 [BD HEVC 10bit Dual Audio AC3][890DA7CE].mkv",
|
||||
"size": 997124540
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 048 [BD HEVC 10bit Dual Audio AC3][39064E08].mkv",
|
||||
"size": 1026751383
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 049 [BD HEVC 10bit Dual Audio AC3][C536D3DB].mkv",
|
||||
"size": 994918391
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 050 [BD HEVC 10bit Dual Audio AC3][A7A0CB24].mkv",
|
||||
"size": 1141146920
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 051 [BD HEVC 10bit Dual Audio AC3][25C06D9D].mkv",
|
||||
"size": 951751791
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 052 [BD HEVC 10bit Dual Audio AC3][FB506194].mkv",
|
||||
"size": 1131756065
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 053 [BD HEVC 10bit Dual Audio AC3][4A76C66D].mkv",
|
||||
"size": 1076952986
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 054 [BD HEVC 10bit Dual Audio AC3][51D8E5F8].mkv",
|
||||
"size": 1369454462
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 055 [BD HEVC 10bit Dual Audio AC3][DCF20007].mkv",
|
||||
"size": 1428073116
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 056 [BD HEVC 10bit Dual Audio AC3][34A28687].mkv",
|
||||
"size": 1304804717
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 057 [BD HEVC 10bit Dual Audio AC3][D1D5FE29].mkv",
|
||||
"size": 1056220730
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 058 [BD HEVC 10bit Dual Audio AC3][C6EAC278].mkv",
|
||||
"size": 1551455953
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 059 [BD HEVC 10bit Dual Audio AC3][E7B25869].mkv",
|
||||
"size": 1026910923
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 060 [BD HEVC 10bit Dual Audio AC3][C9D257D4].mkv",
|
||||
"size": 1059631177
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 061 [BD HEVC 10bit Dual Audio AC3][0521C8D3].mkv",
|
||||
"size": 1457893287
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 062 [BD HEVC 10bit Dual Audio AC3][65CBA616].mkv",
|
||||
"size": 1262863946
|
||||
},
|
||||
{
|
||||
"filename": "[GHOST][1080p] Bleach - 063 [BD HEVC 10bit Dual Audio AC3][CF63E244].mkv",
|
||||
"size": 933523434
|
||||
}
|
||||
],
|
||||
"UploadTime": "2023-04-03 03:14:35"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 81926,
|
||||
"CategoryName": "Anime",
|
||||
"FullName": "Dr. STONE: NEW WORLD - TV Series [2023]",
|
||||
"GroupName": "TV Series",
|
||||
"SeriesID": "79217",
|
||||
"SeriesName": "Dr. STONE: NEW WORLD",
|
||||
"Artists": null,
|
||||
"Year": "2023",
|
||||
"Image": "https://mei.animebytes.tv/Tu0p0k56514.jpg",
|
||||
"Synonymns": {
|
||||
"0": "ドクターストーン NEW WORLD",
|
||||
"2": "Dr. STONE S3, Dr. STONE Season 3, Dr.STONE 3rd Season"
|
||||
},
|
||||
"SynonymnsV2": {
|
||||
"Japanese": "ドクターストーン NEW WORLD",
|
||||
"Romaji": "",
|
||||
"Alternative": "Dr. STONE S3, Dr. STONE Season 3, Dr.STONE 3rd Season"
|
||||
},
|
||||
"Snatched": 1870,
|
||||
"Comments": 0,
|
||||
"Links": {
|
||||
"AniDB": "https://anidb.net/anime/17053",
|
||||
"ANN": "https://www.animenewsnetwork.com/encyclopedia/anime.php?id=25068",
|
||||
"Wikipedia": "https://en.wikipedia.org/wiki/Dr._Stone",
|
||||
"MAL": "https://myanimelist.net/anime/48549"
|
||||
},
|
||||
"Votes": 0,
|
||||
"AvgVote": 0,
|
||||
"Associations": null,
|
||||
"Description": "Third season of [i]Dr. STONE[/i].\r\n\r\nWith the Stone Wars over, the former members of Tsukasa's Empire of Might join forces with the Kingdom of Science to build a ship capable of sailing across the open ocean to seek answers to the mystery of global petrification. However, before they can begin their voyage Senku and his friends need to find some key resources and push some new scientific advancements to build the type of vessel they need.\r\n\r\n* Based on an adventure sci-fi shounen manga series written by Inagaki Riichirou and illustrated by Boichi.\r\n\r\n[i]Note: The first episode received an early screening at a special event on March 12, 2023 at Iino Hall in Tokyo. The regular TV broadcast started on April 6, 2023.[/i]",
|
||||
"DescriptionHTML": "Third season of <em>Dr. STONE</em>.<br />\r\n<br />\r\nWith the Stone Wars over, the former members of Tsukasa's Empire of Might join forces with the Kingdom of Science to build a ship capable of sailing across the open ocean to seek answers to the mystery of global petrification. However, before they can begin their voyage Senku and his friends need to find some key resources and push some new scientific advancements to build the type of vessel they need.<br />\r\n<br />\r\n* Based on an adventure sci-fi shounen manga series written by Inagaki Riichirou and illustrated by Boichi.<br />\r\n<br />\r\n<em>Note: The first episode received an early screening at a special event on March 12, 2023 at Iino Hall in Tokyo. The regular TV broadcast started on April 6, 2023.</em>",
|
||||
"EpCount": 0,
|
||||
"StudioList": "TMS Entertainment///11",
|
||||
"PastWeek": 6,
|
||||
"Incomplete": false,
|
||||
"Ongoing": false,
|
||||
"Tags": [
|
||||
"adventure",
|
||||
"scifi",
|
||||
"shounen",
|
||||
"post.apocalyptic"
|
||||
],
|
||||
"Torrents": [
|
||||
{
|
||||
"ID": 1041495,
|
||||
"EditionData": {
|
||||
"EditionTitle": "Episode 3"
|
||||
},
|
||||
"RawDownMultiplier": 0,
|
||||
"RawUpMultiplier": 1,
|
||||
"Link": "https://animebytes.tv/torrent/1041495/download/somepass",
|
||||
"Property": "Web | MKV | h264 | 720p | AAC 2.0 | Softsubs (SubsPlease) | Episode 3 | Freeleech",
|
||||
"Snatched": 165,
|
||||
"Seeders": 137,
|
||||
"Leechers": 3,
|
||||
"Status": 0,
|
||||
"Size": 748209543,
|
||||
"FileCount": 1,
|
||||
"FileList": [
|
||||
{
|
||||
"filename": "[SubsPlease] Dr. Stone S3 - 03 (720p) [DAC92E18].mkv",
|
||||
"size": 748209543
|
||||
}
|
||||
],
|
||||
"UploadTime": "2023-04-20 14:32:29"
|
||||
},
|
||||
{
|
||||
"ID": 1037731,
|
||||
"EditionData": {
|
||||
"EditionTitle": "Episode 2"
|
||||
},
|
||||
"RawDownMultiplier": 0,
|
||||
"RawUpMultiplier": 1,
|
||||
"Link": "https://animebytes.tv/torrent/1037731/download/somepass",
|
||||
"Property": "Web | MKV | h264 | 720p | AAC 2.0 | Softsubs (SubsPlease) | Episode 2 | Freeleech",
|
||||
"Snatched": 174,
|
||||
"Seeders": 122,
|
||||
"Leechers": 1,
|
||||
"Status": 0,
|
||||
"Size": 748808730,
|
||||
"FileCount": 1,
|
||||
"FileList": [
|
||||
{
|
||||
"filename": "[SubsPlease] Dr. Stone S3 - 02 (720p) [AE2DA9AB].mkv",
|
||||
"size": 748808730
|
||||
}
|
||||
],
|
||||
"UploadTime": "2023-04-13 14:34:16"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 69267,
|
||||
"CategoryName": "Anime",
|
||||
"FullName": "Dr. STONE: STONE WARS - TV Series [2021]",
|
||||
"GroupName": "TV Series",
|
||||
"SeriesID": "67161",
|
||||
"SeriesName": "Dr. STONE: STONE WARS",
|
||||
"Artists": null,
|
||||
"Year": "2021",
|
||||
"Image": "https://mei.animebytes.tv/6pqXEK82OfD.jpg",
|
||||
"Synonymns": {
|
||||
"0": "ドクターストーン STONE WARS",
|
||||
"2": "Dr. STONE S2, Dr. STONE Season 2, Dr.STONE 2nd Season"
|
||||
},
|
||||
"SynonymnsV2": {
|
||||
"Japanese": "ドクターストーン STONE WARS",
|
||||
"Romaji": "",
|
||||
"Alternative": "Dr. STONE S2, Dr. STONE Season 2, Dr.STONE 2nd Season"
|
||||
},
|
||||
"Snatched": 1181,
|
||||
"Comments": 4,
|
||||
"Links": {
|
||||
"AniDB": "https://anidb.net/anime/15305",
|
||||
"ANN": "https://www.animenewsnetwork.com/encyclopedia/anime.php?id=22942",
|
||||
"Wikipedia": "https://en.wikipedia.org/wiki/Dr._Stone",
|
||||
"MAL": "https://myanimelist.net/anime/40852/Dr_Stone__Stone_Wars"
|
||||
},
|
||||
"Votes": 23,
|
||||
"AvgVote": 7.8,
|
||||
"Associations": null,
|
||||
"Description": "* Based on an adventure sci-fi shounen manga series written by Inagaki Riichirou and illustrated by Boichi.\r\n\r\nSenkuu, Chrome and the other villagers are in a battle of wits and brawn against the Tsukasa Empire after the revelation that Senkuu's father left behind a lasting message.\r\n\r\nWith a plan to build a phone that could be used to take down the bad guys from within, Senkuu will need to finally enlist the help of his friends who are a part of the Tsukasa Empire.",
|
||||
"DescriptionHTML": "* Based on an adventure sci-fi shounen manga series written by Inagaki Riichirou and illustrated by Boichi.<br />\r\n<br />\r\nSenkuu, Chrome and the other villagers are in a battle of wits and brawn against the Tsukasa Empire after the revelation that Senkuu's father left behind a lasting message.<br />\r\n<br />\r\nWith a plan to build a phone that could be used to take down the bad guys from within, Senkuu will need to finally enlist the help of his friends who are a part of the Tsukasa Empire.",
|
||||
"EpCount": 11,
|
||||
"StudioList": "TMS Entertainment///11",
|
||||
"PastWeek": 0,
|
||||
"Incomplete": false,
|
||||
"Ongoing": false,
|
||||
"Tags": [
|
||||
"adventure",
|
||||
"scifi",
|
||||
"shounen",
|
||||
"post.apocalyptic"
|
||||
],
|
||||
"Torrents": [
|
||||
{
|
||||
"ID": 944509,
|
||||
"EditionData": {
|
||||
"EditionTitle": ""
|
||||
},
|
||||
"RawDownMultiplier": 0,
|
||||
"RawUpMultiplier": 1,
|
||||
"Link": "https://animebytes.tv/torrent/944509/download/somepass",
|
||||
"Property": "Web | MKV | h264 | 1080p | AAC 2.0 | Dual Audio | Softsubs (-ZR-) | Freeleech",
|
||||
"Snatched": 188,
|
||||
"Seeders": 31,
|
||||
"Leechers": 1,
|
||||
"Status": 0,
|
||||
"Size": 16611719364,
|
||||
"FileCount": 11,
|
||||
"FileList": [
|
||||
{
|
||||
"filename": "Dr. Stone S02E01v2 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1512195256
|
||||
},
|
||||
{
|
||||
"filename": "Dr. Stone S02E02 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1507917714
|
||||
},
|
||||
{
|
||||
"filename": "Dr. Stone S02E03 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1510054199
|
||||
},
|
||||
{
|
||||
"filename": "Dr. Stone S02E04 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1507100461
|
||||
},
|
||||
{
|
||||
"filename": "Dr. Stone S02E05 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1507258273
|
||||
},
|
||||
{
|
||||
"filename": "Dr. Stone S02E06 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1511039711
|
||||
},
|
||||
{
|
||||
"filename": "Dr. Stone S02E07 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1507219047
|
||||
},
|
||||
{
|
||||
"filename": "Dr. Stone S02E08 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1510996213
|
||||
},
|
||||
{
|
||||
"filename": "Dr. Stone S02E09 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1512785600
|
||||
},
|
||||
{
|
||||
"filename": "Dr. Stone S02E10 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1511889715
|
||||
},
|
||||
{
|
||||
"filename": "Dr. Stone S02E11 2021 1080p WEB-DL AVC AAC 2.0 Dual Audio -ZR-.mkv",
|
||||
"size": 1513263175
|
||||
}
|
||||
],
|
||||
"UploadTime": "2021-06-03 20:30:00"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 60598,
|
||||
"CategoryName": "Anime",
|
||||
"FullName": "Dr. STONE - TV Series [2019]",
|
||||
"GroupName": "TV Series",
|
||||
"SeriesID": "56617",
|
||||
"SeriesName": "Dr. STONE",
|
||||
"Artists": null,
|
||||
"Year": "2019",
|
||||
"Image": "https://mei.animebytes.tv/SaFez5XG8T3.jpg",
|
||||
"Synonymns": [
|
||||
"Dr.STONE [ドクターストーン]"
|
||||
],
|
||||
"SynonymnsV2": {
|
||||
"Japanese": "Dr.STONE [ドクターストーン]",
|
||||
"Romaji": "",
|
||||
"Alternative": ""
|
||||
},
|
||||
"Snatched": 2174,
|
||||
"Comments": 8,
|
||||
"Links": {
|
||||
"AniDB": "https://anidb.net/anime/14491",
|
||||
"ANN": "https://www.animenewsnetwork.com/encyclopedia/anime.php?id=21703",
|
||||
"Wikipedia": "https://en.wikipedia.org/wiki/Dr._Stone",
|
||||
"MAL": "https://myanimelist.net/anime/38691"
|
||||
},
|
||||
"Votes": 68,
|
||||
"AvgVote": 7.9,
|
||||
"Associations": null,
|
||||
"Description": "Several thousand years after a mysterious phenomenon that turns all of humanity to stone, the extraordinarily intelligent, science-driven boy, Senku Ishigami, awakens. Facing a world of stone and the total collapse of civilization, Senku makes up his mind to use science to rebuild the world. Starting with his super strong childhood friend Taiju Oki, who awakened at the same time, they will begin to rebuild civilization from nothing... Depicting two million years of scientific history from the Stone Age to present day, the unprecedented crafting adventure story is about to begin!\r\n\r\n* Based on an adventure sci-fi shounen manga series written by Inagaki Riichirou and illustrated by Boichi.",
|
||||
"DescriptionHTML": "Several thousand years after a mysterious phenomenon that turns all of humanity to stone, the extraordinarily intelligent, science-driven boy, Senku Ishigami, awakens. Facing a world of stone and the total collapse of civilization, Senku makes up his mind to use science to rebuild the world. Starting with his super strong childhood friend Taiju Oki, who awakened at the same time, they will begin to rebuild civilization from nothing... Depicting two million years of scientific history from the Stone Age to present day, the unprecedented crafting adventure story is about to begin!<br />\r\n<br />\r\n* Based on an adventure sci-fi shounen manga series written by Inagaki Riichirou and illustrated by Boichi.",
|
||||
"EpCount": 24,
|
||||
"StudioList": "TMS Entertainment///11|8PAN///6344",
|
||||
"PastWeek": 0,
|
||||
"Incomplete": false,
|
||||
"Ongoing": false,
|
||||
"Tags": [
|
||||
"adventure",
|
||||
"comedy",
|
||||
"scifi",
|
||||
"shounen"
|
||||
],
|
||||
"Torrents": [
|
||||
{
|
||||
"ID": 430074,
|
||||
"EditionData": {
|
||||
"EditionTitle": ""
|
||||
},
|
||||
"RawDownMultiplier": 1,
|
||||
"RawUpMultiplier": 1,
|
||||
"Link": "https://animebytes.tv/torrent/430074/download/somepass",
|
||||
"Property": "Web | MKV | h264 | 720p | AAC 2.0 | Softsubs (HorribleSubs)",
|
||||
"Snatched": 108,
|
||||
"Seeders": 33,
|
||||
"Leechers": 1,
|
||||
"Status": 0,
|
||||
"Size": 16366224176,
|
||||
"FileCount": 24,
|
||||
"FileList": [
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 01 [720p].mkv",
|
||||
"size": 477027555
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 02 [720p].mkv",
|
||||
"size": 489436551
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 03 [720p].mkv",
|
||||
"size": 503786828
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 04 [720p].mkv",
|
||||
"size": 442977598
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 05 [720p].mkv",
|
||||
"size": 523531555
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 06 [720p].mkv",
|
||||
"size": 506742468
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 07 [720p].mkv",
|
||||
"size": 746577276
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 08 [720p].mkv",
|
||||
"size": 745942485
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 09 [720p].mkv",
|
||||
"size": 746035250
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 10 [720p].mkv",
|
||||
"size": 746001386
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 11 [720p].mkv",
|
||||
"size": 746155088
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 12 [720p].mkv",
|
||||
"size": 746560710
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 13 [720p].mkv",
|
||||
"size": 745880614
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 14 [720p].mkv",
|
||||
"size": 744563919
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 15 [720p].mkv",
|
||||
"size": 745303312
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 16 [720p].mkv",
|
||||
"size": 746850910
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 17 [720p].mkv",
|
||||
"size": 744188496
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 18 [720p].mkv",
|
||||
"size": 746212236
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 19 [720p].mkv",
|
||||
"size": 744840131
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 20 [720p].mkv",
|
||||
"size": 746380081
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 21 [720p].mkv",
|
||||
"size": 744975636
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 22 [720p].mkv",
|
||||
"size": 746214757
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 23 [720p].mkv",
|
||||
"size": 744924693
|
||||
},
|
||||
{
|
||||
"filename": "[HorribleSubs] Dr. Stone - 24 [720p].mkv",
|
||||
"size": 745114641
|
||||
}
|
||||
],
|
||||
"UploadTime": "2019-12-13 17:02:48"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 41952,
|
||||
"CategoryName": "Anime",
|
||||
"FullName": "One Piece - TV Series [2019]",
|
||||
"GroupName": "TV Series",
|
||||
"SeriesID": "114",
|
||||
"SeriesName": "One Piece",
|
||||
"Artists": null,
|
||||
"Year": "2019",
|
||||
"Image": "https://mei.animebytes.tv/cQieN6oZ6Ft.jpg",
|
||||
"Synonymns": {
|
||||
"0": "ワンピース",
|
||||
"2": "One Piece: The Great Gold Pirate"
|
||||
},
|
||||
"SynonymnsV2": {
|
||||
"Japanese": "ワンピース",
|
||||
"Romaji": "",
|
||||
"Alternative": "One Piece: The Great Gold Pirate"
|
||||
},
|
||||
"Snatched": 100700,
|
||||
"Comments": 3,
|
||||
"Links": {
|
||||
"AniDB": "https://anidb.net/anime/69",
|
||||
"ANN": "https://www.animenewsnetwork.com/encyclopedia/anime.php?id=836",
|
||||
"Wikipedia": "https://en.wikipedia.org/wiki/One_Piece",
|
||||
"MAL": "https://myanimelist.net/anime/21/One_Piece"
|
||||
},
|
||||
"Votes": 89,
|
||||
"AvgVote": 8.8,
|
||||
"Associations": null,
|
||||
"Description": "The 20th season of One Piece. This represents episode 892 to current.",
|
||||
"DescriptionHTML": "The 20th season of One Piece. This represents episode 892 to current.",
|
||||
"EpCount": 0,
|
||||
"StudioList": null,
|
||||
"PastWeek": 10,
|
||||
"Incomplete": false,
|
||||
"Ongoing": false,
|
||||
"Tags": [
|
||||
"adventure",
|
||||
"fantasy",
|
||||
"martial.arts",
|
||||
"shounen",
|
||||
"super.power",
|
||||
"action"
|
||||
],
|
||||
"Torrents": [
|
||||
{
|
||||
"ID": 1043925,
|
||||
"EditionData": {
|
||||
"EditionTitle": "Episode 1059"
|
||||
},
|
||||
"RawDownMultiplier": 0,
|
||||
"RawUpMultiplier": 1,
|
||||
"Link": "https://animebytes.tv/torrent/1043925/download/somepass",
|
||||
"Property": "Web | MKV | h264 | 720p | AAC 2.0 | Softsubs (SubsPlease) | Episode 1059 | Freeleech",
|
||||
"Snatched": 125,
|
||||
"Seeders": 114,
|
||||
"Leechers": 1,
|
||||
"Status": 0,
|
||||
"Size": 743629489,
|
||||
"FileCount": 1,
|
||||
"FileList": [
|
||||
{
|
||||
"filename": "[SubsPlease] One Piece - 1059 (720p) [B347D9DE].mkv",
|
||||
"size": 743629489
|
||||
}
|
||||
],
|
||||
"UploadTime": "2023-04-23 02:06:08"
|
||||
},
|
||||
{
|
||||
"ID": 1039046,
|
||||
"EditionData": {
|
||||
"EditionTitle": "Episode 1058"
|
||||
},
|
||||
"RawDownMultiplier": 0,
|
||||
"RawUpMultiplier": 1,
|
||||
"Link": "https://animebytes.tv/torrent/1039046/download/somepass",
|
||||
"Property": "Web | MKV | h264 | 1080p | AAC 2.0 | Softsubs (SubsPlease) | Episode 1058 | Freeleech",
|
||||
"Snatched": 290,
|
||||
"Seeders": 232,
|
||||
"Leechers": 2,
|
||||
"Status": 0,
|
||||
"Size": 1453835224,
|
||||
"FileCount": 1,
|
||||
"FileList": [
|
||||
{
|
||||
"filename": "[SubsPlease] One Piece - 1058 (1080p) [E4094B4A].mkv",
|
||||
"size": 1453835224
|
||||
}
|
||||
],
|
||||
"UploadTime": "2023-04-16 02:07:42"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
1461
src/NzbDrone.Core.Test/Files/Indexers/Redacted/recentfeed.json
Normal file
1461
src/NzbDrone.Core.Test/Files/Indexers/Redacted/recentfeed.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Definitions;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class AnimeBytesFixture : CoreTest<AnimeBytes>
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "AnimeBytes",
|
||||
Settings = new AnimeBytesSettings
|
||||
{
|
||||
BaseUrl = "https://animebytes.tv/",
|
||||
Username = "someuser",
|
||||
Passkey = "somepass"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task should_parse_recent_feed_from_animebytes()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/AnimeBytes/recentfeed.json");
|
||||
|
||||
Mocker.GetMock<IIndexerHttpClient>()
|
||||
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
|
||||
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed)));
|
||||
|
||||
var releases = (await Subject.Fetch(new BasicSearchCriteria { Categories = new[] { 2000, 5000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(33);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
var firstTorrentInfo = releases.ElementAt(2) as TorrentInfo;
|
||||
|
||||
firstTorrentInfo.Title.Should().Be("[SubsPlease] One Piece: The Great Gold Pirate - 1059 [Web][MKV][h264][720p][AAC 2.0][Softsubs (SubsPlease)][Episode 1059]");
|
||||
firstTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
firstTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/1043925/download/somepass");
|
||||
firstTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/1043925/group");
|
||||
firstTorrentInfo.Guid.Should().Be("https://animebytes.tv/torrent/1043925/group?nh=0F6BB43603CC07F4C804B9A29139F852");
|
||||
firstTorrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
firstTorrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
firstTorrentInfo.PublishDate.Should().Be(DateTime.Parse("2023-04-23 02:06:08", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
|
||||
firstTorrentInfo.Size.Should().Be(743629489);
|
||||
firstTorrentInfo.InfoHash.Should().Be(null);
|
||||
firstTorrentInfo.MagnetUrl.Should().Be(null);
|
||||
firstTorrentInfo.Peers.Should().Be(1 + 114);
|
||||
firstTorrentInfo.Seeders.Should().Be(114);
|
||||
firstTorrentInfo.Files.Should().Be(1);
|
||||
firstTorrentInfo.MinimumSeedTime.Should().Be(259200);
|
||||
|
||||
var secondTorrentInfo = releases.ElementAt(16) as TorrentInfo;
|
||||
|
||||
secondTorrentInfo.Title.Should().Be("[GHOST] BLEACH S03 [Blu-ray][MKV][h265 10-bit][1080p][AC3 2.0][Dual Audio][Softsubs (GHOST)]");
|
||||
secondTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
secondTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/1031203/download/somepass");
|
||||
secondTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/1031203/group");
|
||||
secondTorrentInfo.Guid.Should().Be("https://animebytes.tv/torrent/1031203/group?nh=F7C73EF631FE269D3A7F10BD12EC99A1");
|
||||
secondTorrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
secondTorrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
secondTorrentInfo.PublishDate.Should().Be(DateTime.Parse("2023-04-03 03:14:35", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
|
||||
secondTorrentInfo.Size.Should().Be(24498538059);
|
||||
secondTorrentInfo.InfoHash.Should().Be(null);
|
||||
secondTorrentInfo.MagnetUrl.Should().Be(null);
|
||||
secondTorrentInfo.Peers.Should().Be(2 + 12);
|
||||
secondTorrentInfo.Seeders.Should().Be(12);
|
||||
secondTorrentInfo.Files.Should().Be(22);
|
||||
secondTorrentInfo.MinimumSeedTime.Should().Be(655200);
|
||||
|
||||
var thirdTorrentInfo = releases.ElementAt(18) as TorrentInfo;
|
||||
|
||||
thirdTorrentInfo.Title.Should().Be("[Polarwindz] Cowboy Bebop: Tengoku no Tobira 2001 [Blu-ray][MKV][h265 10-bit][1080p][Opus 5.1][Softsubs (Polarwindz)]");
|
||||
thirdTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
thirdTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/959397/download/somepass");
|
||||
thirdTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/959397/group");
|
||||
thirdTorrentInfo.Guid.Should().Be("https://animebytes.tv/torrent/959397/group?nh=D63895DA87A25239C11F9823F46000E1");
|
||||
thirdTorrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
thirdTorrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
thirdTorrentInfo.PublishDate.Should().Be(DateTime.Parse("2023-04-02 05:00:43", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
|
||||
thirdTorrentInfo.Size.Should().Be(13090646841);
|
||||
thirdTorrentInfo.InfoHash.Should().Be(null);
|
||||
thirdTorrentInfo.MagnetUrl.Should().Be(null);
|
||||
thirdTorrentInfo.Peers.Should().Be(1 + 5);
|
||||
thirdTorrentInfo.Seeders.Should().Be(5);
|
||||
thirdTorrentInfo.Files.Should().Be(1);
|
||||
thirdTorrentInfo.MinimumSeedTime.Should().Be(475200);
|
||||
|
||||
var fourthTorrentInfo = releases.ElementAt(3) as TorrentInfo;
|
||||
|
||||
fourthTorrentInfo.Title.Should().Be("[SubsPlease] Dr. STONE: NEW WORLD S03E03 - 03 [Web][MKV][h264][720p][AAC 2.0][Softsubs (SubsPlease)][Episode 3]");
|
||||
fourthTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
fourthTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/1041495/download/somepass");
|
||||
fourthTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/1041495/group");
|
||||
fourthTorrentInfo.Guid.Should().Be("https://animebytes.tv/torrent/1041495/group?nh=8B78B0DD3BCC6068BFCD927E4AC674F6");
|
||||
fourthTorrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
fourthTorrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
fourthTorrentInfo.PublishDate.Should().Be(DateTime.Parse("2023-04-20 14:32:29", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
|
||||
fourthTorrentInfo.Size.Should().Be(748209543);
|
||||
fourthTorrentInfo.InfoHash.Should().Be(null);
|
||||
fourthTorrentInfo.MagnetUrl.Should().Be(null);
|
||||
fourthTorrentInfo.Peers.Should().Be(3 + 137);
|
||||
fourthTorrentInfo.Seeders.Should().Be(137);
|
||||
fourthTorrentInfo.Files.Should().Be(1);
|
||||
fourthTorrentInfo.MinimumSeedTime.Should().Be(259200);
|
||||
|
||||
var fifthTorrentInfo = releases.ElementAt(23) as TorrentInfo;
|
||||
|
||||
fifthTorrentInfo.Title.Should().Be("[-ZR-] Dr. STONE: STONE WARS S02 [Web][MKV][h264][1080p][AAC 2.0][Dual Audio][Softsubs (-ZR-)]");
|
||||
fifthTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
fifthTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/944509/download/somepass");
|
||||
fifthTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/944509/group");
|
||||
fifthTorrentInfo.Guid.Should().Be("https://animebytes.tv/torrent/944509/group?nh=FDCAA1EAB36D7C802F1E4B13DAE5EED7");
|
||||
fifthTorrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
fifthTorrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
fifthTorrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-06-03 20:30:00", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
|
||||
fifthTorrentInfo.Size.Should().Be(16611719364);
|
||||
fifthTorrentInfo.InfoHash.Should().Be(null);
|
||||
fifthTorrentInfo.MagnetUrl.Should().Be(null);
|
||||
fifthTorrentInfo.Peers.Should().Be(1 + 31);
|
||||
fifthTorrentInfo.Seeders.Should().Be(31);
|
||||
fifthTorrentInfo.Files.Should().Be(11);
|
||||
fifthTorrentInfo.MinimumSeedTime.Should().Be(529200);
|
||||
|
||||
var sixthTorrentInfo = releases.ElementAt(31) as TorrentInfo;
|
||||
|
||||
sixthTorrentInfo.Title.Should().Be("[HorribleSubs] Dr. STONE S01 [Web][MKV][h264][720p][AAC 2.0][Softsubs (HorribleSubs)]");
|
||||
sixthTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
sixthTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/430074/download/somepass");
|
||||
sixthTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/430074/group");
|
||||
sixthTorrentInfo.Guid.Should().Be("https://animebytes.tv/torrent/430074/group?nh=32279E138015D8718B2B4B49AEF64574");
|
||||
sixthTorrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
sixthTorrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
sixthTorrentInfo.PublishDate.Should().Be(DateTime.Parse("2019-12-13 17:02:48", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
|
||||
sixthTorrentInfo.Size.Should().Be(16366224176);
|
||||
sixthTorrentInfo.InfoHash.Should().Be(null);
|
||||
sixthTorrentInfo.MagnetUrl.Should().Be(null);
|
||||
sixthTorrentInfo.Peers.Should().Be(1 + 33);
|
||||
sixthTorrentInfo.Seeders.Should().Be(33);
|
||||
sixthTorrentInfo.Files.Should().Be(24);
|
||||
sixthTorrentInfo.MinimumSeedTime.Should().Be(529200);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,10 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
Subject.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "AvistaZ",
|
||||
Settings = new AvistazSettings() { Username = "someuser", Password = "somepass", Pid = "somepid" }
|
||||
Settings = new AvistazSettings { Username = "someuser", Password = "somepass", Pid = "somepid" }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
|
||||
.Returns<HttpRequest, IndexerDefinition>((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;
|
||||
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new[] { 2000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(100);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://avistaz.to/torrent/187240-japan-sinks-people-of-hope-2021-s01e05-720p-nf-web-dl-ddp20-x264-seikel");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-11-14 22:26:21"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-11-14 21:26:21"));
|
||||
torrentInfo.Size.Should().Be(935127615);
|
||||
torrentInfo.InfoHash.Should().Be("a879261d4e6e792402f92401141a21de70d51bf2");
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -22,10 +22,10 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
Subject.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "ExoticaZ",
|
||||
Settings = new AvistazSettings() { Username = "someuser", Password = "somepass", Pid = "somepid" }
|
||||
Settings = new AvistazSettings { Username = "someuser", Password = "somepass", Pid = "somepid" }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
|
||||
.Returns<HttpRequest, IndexerDefinition>((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;
|
||||
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new[] { 2000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(100);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://exoticaz.to/torrent/64040-ssis-419-my-first-experience-is-yua-mikami-from-the-day-i-lost-my-virginity-i-was-devoted-to-sex");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-06-11 16:04:50"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-06-11 15:04:50"));
|
||||
torrentInfo.Size.Should().Be(7085405541);
|
||||
torrentInfo.InfoHash.Should().Be("asdjfiasdf54asd7f4a2sdf544asdf");
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -22,10 +22,10 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
Subject.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "PrivateHD",
|
||||
Settings = new AvistazSettings() { Username = "someuser", Password = "somepass", Pid = "somepid" }
|
||||
Settings = new AvistazSettings { Username = "someuser", Password = "somepass", Pid = "somepid" }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
|
||||
.Returns<HttpRequest, IndexerDefinition>((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;
|
||||
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new[] { 2000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(100);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://privatehd.to/torrent/78506-godzilla-2014-2160p-uhd-bluray-remux-hdr-hevc-atmos-triton");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-03-21 05:24:49"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-03-21 04:24:49"));
|
||||
torrentInfo.Size.Should().Be(69914591044);
|
||||
torrentInfo.InfoHash.Should().Be("a879261d4e6e792402f92401141a21de70d51bf2");
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
using NzbDrone.Core.Indexers.Definitions.Cardigann;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests.CardigannTests
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://filelist.io/details.php?id=665873");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2020-01-25 20:20:19"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2020-01-25 19:20:19"));
|
||||
torrentInfo.Size.Should().Be(8300512414);
|
||||
torrentInfo.InfoHash.Should().Be(null);
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.IndexerTests.OrpheusTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
Subject.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "Orpheus",
|
||||
Settings = new OrpheusSettings { Apikey = "somekey" }
|
||||
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.Test.IndexerTests.OrpheusTests
|
||||
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
|
||||
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed)));
|
||||
|
||||
var releases = (await Subject.Fetch(new BasicSearchCriteria { Categories = new[] { 2000 } })).Releases;
|
||||
var releases = (await Subject.Fetch(new BasicSearchCriteria { Categories = new[] { 3000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(65);
|
||||
releases.First().Should().BeOfType<GazelleInfo>();
|
||||
@@ -56,6 +56,7 @@ namespace NzbDrone.Core.Test.IndexerTests.OrpheusTests
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
torrentInfo.Peers.Should().Be(0);
|
||||
torrentInfo.Seeders.Should().Be(0);
|
||||
torrentInfo.Files.Should().Be(18);
|
||||
torrentInfo.ImdbId.Should().Be(0);
|
||||
torrentInfo.TmdbId.Should().Be(0);
|
||||
torrentInfo.TvdbId.Should().Be(0);
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Definitions;
|
||||
using NzbDrone.Core.Indexers.Definitions.Gazelle;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests.RedactedTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class RedactedFixture : CoreTest<Redacted>
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "Redacted",
|
||||
Settings = new RedactedSettings { Apikey = "somekey" }
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task should_parse_recent_feed_from_Redacted()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Redacted/recentfeed.json");
|
||||
|
||||
Mocker.GetMock<IIndexerHttpClient>()
|
||||
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
|
||||
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed)));
|
||||
|
||||
var releases = (await Subject.Fetch(new BasicSearchCriteria { Categories = new[] { 3000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(39);
|
||||
releases.First().Should().BeOfType<GazelleInfo>();
|
||||
|
||||
var torrentInfo = releases.First() as GazelleInfo;
|
||||
|
||||
torrentInfo.Title.Should().Be("Red Hot Chili Peppers - Californication [1999] [Album] [US / Reissue 2020] [FLAC 24bit Lossless] [Vinyl]");
|
||||
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
torrentInfo.DownloadUrl.Should().Be("https://redacted.ch/ajax.php?action=download&id=3892313&usetoken=0");
|
||||
torrentInfo.InfoUrl.Should().Be("https://redacted.ch/torrents.php?id=16720&torrentid=3892313");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-12-17 08:02:35"));
|
||||
torrentInfo.Size.Should().Be(1247137236);
|
||||
torrentInfo.InfoHash.Should().Be(null);
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
torrentInfo.Peers.Should().Be(4);
|
||||
torrentInfo.Seeders.Should().Be(4);
|
||||
torrentInfo.Files.Should().Be(23);
|
||||
torrentInfo.ImdbId.Should().Be(0);
|
||||
torrentInfo.TmdbId.Should().Be(0);
|
||||
torrentInfo.TvdbId.Should().Be(0);
|
||||
torrentInfo.Languages.Should().HaveCount(0);
|
||||
torrentInfo.Subs.Should().HaveCount(0);
|
||||
torrentInfo.DownloadVolumeFactor.Should().Be(1);
|
||||
torrentInfo.UploadVolumeFactor.Should().Be(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Test.IndexerTests.SecretCinemaTests
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
{
|
||||
Name = "SecretCinema",
|
||||
Settings = new GazelleSettings() { Username = "somekey", Password = "somekey" }
|
||||
Settings = new GazelleSettings { Username = "somekey", Password = "somekey" }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.Test.IndexerTests.SecretCinemaTests
|
||||
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
|
||||
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed)));
|
||||
|
||||
var releases = (await Subject.Fetch(new BasicSearchCriteria { Categories = new int[] { 2000 } })).Releases;
|
||||
var releases = (await Subject.Fetch(new BasicSearchCriteria { Categories = new[] { 2000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(3);
|
||||
releases.First().Should().BeOfType<GazelleInfo>();
|
||||
@@ -50,7 +50,7 @@ namespace NzbDrone.Core.Test.IndexerTests.SecretCinemaTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://secret-cinema.pw/torrents.php?id=2497&torrentid=45068");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-12-15 19:37:29"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-12-15 17:37:29"));
|
||||
torrentInfo.Size.Should().Be(57473058680);
|
||||
torrentInfo.InfoHash.Should().Be(null);
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -19,10 +19,12 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
|
||||
public class LazyLibrarianSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly LazyLibrarianSettingsValidator Validator = new LazyLibrarianSettingsValidator();
|
||||
private static readonly LazyLibrarianSettingsValidator Validator = new ();
|
||||
|
||||
public LazyLibrarianSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:5299";
|
||||
SyncCategories = new[]
|
||||
{
|
||||
NewznabStandardCategory.AudioAudiobook.Id,
|
||||
|
||||
@@ -18,10 +18,12 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
public class LidarrSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly LidarrSettingsValidator Validator = new LidarrSettingsValidator();
|
||||
private static readonly LidarrSettingsValidator Validator = new ();
|
||||
|
||||
public LidarrSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:8686";
|
||||
SyncCategories = new[] { 3000, 3010, 3030, 3040, 3050, 3060 };
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
public class LidarrV1Proxy : ILidarrV1Proxy
|
||||
{
|
||||
private const string AppApiRoute = "/api/v1";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@@ -34,13 +36,13 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
public LidarrStatus GetStatus(LidarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v1/system/status", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppApiRoute}/system/status", HttpMethod.Get);
|
||||
return Execute<LidarrStatus>(request);
|
||||
}
|
||||
|
||||
public List<LidarrIndexer> GetIndexers(LidarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v1/indexer", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}", HttpMethod.Get);
|
||||
return Execute<List<LidarrIndexer>>(request);
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v1/indexer/{indexerId}", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexerId}", HttpMethod.Get);
|
||||
return Execute<LidarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
@@ -64,37 +66,37 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
public void RemoveIndexer(int indexerId, LidarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v1/indexer/{indexerId}", HttpMethod.Delete);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexerId}", HttpMethod.Delete);
|
||||
_httpClient.Execute(request);
|
||||
}
|
||||
|
||||
public List<LidarrIndexer> GetIndexerSchema(LidarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v1/indexer/schema", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/schema", HttpMethod.Get);
|
||||
return Execute<List<LidarrIndexer>>(request);
|
||||
}
|
||||
|
||||
public LidarrIndexer AddIndexer(LidarrIndexer indexer, LidarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v1/indexer", HttpMethod.Post);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}", HttpMethod.Post);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return Execute<LidarrIndexer>(request);
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
|
||||
public LidarrIndexer UpdateIndexer(LidarrIndexer indexer, LidarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v1/indexer/{indexer.Id}", HttpMethod.Put);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexer.Id}", HttpMethod.Put);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return Execute<LidarrIndexer>(request);
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
|
||||
public ValidationFailure TestConnection(LidarrIndexer indexer, LidarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v1/indexer/test", HttpMethod.Post);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/test", HttpMethod.Post);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
@@ -134,6 +136,53 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
return null;
|
||||
}
|
||||
|
||||
private LidarrIndexer ExecuteIndexerRequest(HttpRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Execute<LidarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
switch (ex.Response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
break;
|
||||
case HttpStatusCode.BadRequest:
|
||||
if (ex.Response.Content.Contains("Query successful, but no results in the configured categories were returned from your indexer.", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Error(ex, "No Results in configured categories. See FAQ Entry: Prowlarr will not sync X Indexer to App");
|
||||
break;
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Invalid Request");
|
||||
break;
|
||||
case HttpStatusCode.SeeOther:
|
||||
_logger.Error(ex, "App returned redirect and is invalid. Check App URL");
|
||||
break;
|
||||
case HttpStatusCode.NotFound:
|
||||
_logger.Error(ex, "Remote indexer not found");
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unexpected response status code: {0}", ex.Response.StatusCode);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse JSON response from application");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to add or update indexer");
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest BuildRequest(LidarrSettings settings, string resource, HttpMethod method)
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
@@ -19,10 +19,12 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
|
||||
public class MylarSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly MylarSettingsValidator Validator = new MylarSettingsValidator();
|
||||
private static readonly MylarSettingsValidator Validator = new ();
|
||||
|
||||
public MylarSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:8090";
|
||||
SyncCategories = new[] { NewznabStandardCategory.BooksComics.Id };
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,12 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
public class RadarrSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly RadarrSettingsValidator Validator = new RadarrSettingsValidator();
|
||||
private static readonly RadarrSettingsValidator Validator = new ();
|
||||
|
||||
public RadarrSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:7878";
|
||||
SyncCategories = new[] { 2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060, 2070, 2080 };
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
public class RadarrV3Proxy : IRadarrV3Proxy
|
||||
{
|
||||
private const string AppApiRoute = "/api/v3";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@@ -34,13 +36,13 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
public RadarrStatus GetStatus(RadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/system/status", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppApiRoute}/system/status", HttpMethod.Get);
|
||||
return Execute<RadarrStatus>(request);
|
||||
}
|
||||
|
||||
public List<RadarrIndexer> GetIndexers(RadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/indexer", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}", HttpMethod.Get);
|
||||
return Execute<List<RadarrIndexer>>(request);
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/{indexerId}", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexerId}", HttpMethod.Get);
|
||||
return Execute<RadarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
@@ -64,37 +66,37 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
public void RemoveIndexer(int indexerId, RadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/{indexerId}", HttpMethod.Delete);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexerId}", HttpMethod.Delete);
|
||||
_httpClient.Execute(request);
|
||||
}
|
||||
|
||||
public List<RadarrIndexer> GetIndexerSchema(RadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/indexer/schema", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/schema", HttpMethod.Get);
|
||||
return Execute<List<RadarrIndexer>>(request);
|
||||
}
|
||||
|
||||
public RadarrIndexer AddIndexer(RadarrIndexer indexer, RadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/indexer", HttpMethod.Post);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}", HttpMethod.Post);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return Execute<RadarrIndexer>(request);
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
|
||||
public RadarrIndexer UpdateIndexer(RadarrIndexer indexer, RadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/{indexer.Id}", HttpMethod.Put);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexer.Id}", HttpMethod.Put);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return Execute<RadarrIndexer>(request);
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
|
||||
public ValidationFailure TestConnection(RadarrIndexer indexer, RadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/test", HttpMethod.Post);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/test", HttpMethod.Post);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
@@ -134,6 +136,53 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
return null;
|
||||
}
|
||||
|
||||
private RadarrIndexer ExecuteIndexerRequest(HttpRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Execute<RadarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
switch (ex.Response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
break;
|
||||
case HttpStatusCode.BadRequest:
|
||||
if (ex.Response.Content.Contains("Query successful, but no results in the configured categories were returned from your indexer.", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Error(ex, "No Results in configured categories. See FAQ Entry: Prowlarr will not sync X Indexer to App");
|
||||
break;
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Invalid Request");
|
||||
break;
|
||||
case HttpStatusCode.SeeOther:
|
||||
_logger.Error(ex, "App returned redirect and is invalid. Check App URL");
|
||||
break;
|
||||
case HttpStatusCode.NotFound:
|
||||
_logger.Error(ex, "Remote indexer not found");
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unexpected response status code: {0}", ex.Response.StatusCode);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse JSON response from application");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to add or update indexer");
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest BuildRequest(RadarrSettings settings, string resource, HttpMethod method)
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
@@ -19,10 +19,12 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
public class ReadarrSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly ReadarrSettingsValidator Validator = new ReadarrSettingsValidator();
|
||||
private static readonly ReadarrSettingsValidator Validator = new ();
|
||||
|
||||
public ReadarrSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:8787";
|
||||
SyncCategories = new[] { 3030, 7000, 7010, 7020, 7030, 7040, 7050, 7060 };
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
public class ReadarrV1Proxy : IReadarrV1Proxy
|
||||
{
|
||||
private const string AppApiRoute = "/api/v1";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@@ -34,13 +36,13 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
public ReadarrStatus GetStatus(ReadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v1/system/status", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppApiRoute}/system/status", HttpMethod.Get);
|
||||
return Execute<ReadarrStatus>(request);
|
||||
}
|
||||
|
||||
public List<ReadarrIndexer> GetIndexers(ReadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v1/indexer", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}", HttpMethod.Get);
|
||||
return Execute<List<ReadarrIndexer>>(request);
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v1/indexer/{indexerId}", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexerId}", HttpMethod.Get);
|
||||
return Execute<ReadarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
@@ -64,37 +66,37 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
public void RemoveIndexer(int indexerId, ReadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v1/indexer/{indexerId}", HttpMethod.Delete);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexerId}", HttpMethod.Delete);
|
||||
_httpClient.Execute(request);
|
||||
}
|
||||
|
||||
public List<ReadarrIndexer> GetIndexerSchema(ReadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v1/indexer/schema", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/schema", HttpMethod.Get);
|
||||
return Execute<List<ReadarrIndexer>>(request);
|
||||
}
|
||||
|
||||
public ReadarrIndexer AddIndexer(ReadarrIndexer indexer, ReadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v1/indexer", HttpMethod.Post);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}", HttpMethod.Post);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return Execute<ReadarrIndexer>(request);
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
|
||||
public ReadarrIndexer UpdateIndexer(ReadarrIndexer indexer, ReadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v1/indexer/{indexer.Id}", HttpMethod.Put);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexer.Id}", HttpMethod.Put);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return Execute<ReadarrIndexer>(request);
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
|
||||
public ValidationFailure TestConnection(ReadarrIndexer indexer, ReadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v1/indexer/test", HttpMethod.Post);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/test", HttpMethod.Post);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
@@ -134,6 +136,53 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
return null;
|
||||
}
|
||||
|
||||
private ReadarrIndexer ExecuteIndexerRequest(HttpRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Execute<ReadarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
switch (ex.Response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
break;
|
||||
case HttpStatusCode.BadRequest:
|
||||
if (ex.Response.Content.Contains("Query successful, but no results in the configured categories were returned from your indexer.", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Error(ex, "No Results in configured categories. See FAQ Entry: Prowlarr will not sync X Indexer to App");
|
||||
break;
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Invalid Request");
|
||||
break;
|
||||
case HttpStatusCode.SeeOther:
|
||||
_logger.Error(ex, "App returned redirect and is invalid. Check App URL");
|
||||
break;
|
||||
case HttpStatusCode.NotFound:
|
||||
_logger.Error(ex, "Remote indexer not found");
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unexpected response status code: {0}", ex.Response.StatusCode);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse JSON response from application");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to add or update indexer");
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest BuildRequest(ReadarrSettings settings, string resource, HttpMethod method)
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
@@ -18,10 +18,12 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
public class SonarrSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly SonarrSettingsValidator Validator = new SonarrSettingsValidator();
|
||||
private static readonly SonarrSettingsValidator Validator = new ();
|
||||
|
||||
public SonarrSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:8989";
|
||||
SyncCategories = new[] { 5000, 5010, 5020, 5030, 5040, 5045, 5050 };
|
||||
AnimeSyncCategories = new[] { 5070 };
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
public class SonarrV3Proxy : ISonarrV3Proxy
|
||||
{
|
||||
private const string AppApiRoute = "/api/v3";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@@ -34,13 +36,13 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
public SonarrStatus GetStatus(SonarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/system/status", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppApiRoute}/system/status", HttpMethod.Get);
|
||||
return Execute<SonarrStatus>(request);
|
||||
}
|
||||
|
||||
public List<SonarrIndexer> GetIndexers(SonarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/indexer", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}", HttpMethod.Get);
|
||||
return Execute<List<SonarrIndexer>>(request);
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/{indexerId}", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexerId}", HttpMethod.Get);
|
||||
return Execute<SonarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
@@ -64,37 +66,37 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
public void RemoveIndexer(int indexerId, SonarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/{indexerId}", HttpMethod.Delete);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexerId}", HttpMethod.Delete);
|
||||
_httpClient.Execute(request);
|
||||
}
|
||||
|
||||
public List<SonarrIndexer> GetIndexerSchema(SonarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/indexer/schema", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/schema", HttpMethod.Get);
|
||||
return Execute<List<SonarrIndexer>>(request);
|
||||
}
|
||||
|
||||
public SonarrIndexer AddIndexer(SonarrIndexer indexer, SonarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/indexer", HttpMethod.Post);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}", HttpMethod.Post);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return Execute<SonarrIndexer>(request);
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
|
||||
public SonarrIndexer UpdateIndexer(SonarrIndexer indexer, SonarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/{indexer.Id}", HttpMethod.Put);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexer.Id}", HttpMethod.Put);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return Execute<SonarrIndexer>(request);
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
|
||||
public ValidationFailure TestConnection(SonarrIndexer indexer, SonarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/test", HttpMethod.Post);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/test", HttpMethod.Post);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
@@ -140,6 +142,53 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
return null;
|
||||
}
|
||||
|
||||
private SonarrIndexer ExecuteIndexerRequest(HttpRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Execute<SonarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
switch (ex.Response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
break;
|
||||
case HttpStatusCode.BadRequest:
|
||||
if (ex.Response.Content.Contains("Query successful, but no results in the configured categories were returned from your indexer.", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Error(ex, "No Results in configured categories. See FAQ Entry: Prowlarr will not sync X Indexer to App");
|
||||
break;
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Invalid Request");
|
||||
break;
|
||||
case HttpStatusCode.SeeOther:
|
||||
_logger.Error(ex, "App returned redirect and is invalid. Check App URL");
|
||||
break;
|
||||
case HttpStatusCode.NotFound:
|
||||
_logger.Error(ex, "Remote indexer not found");
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unexpected response status code: {0}", ex.Response.StatusCode);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse JSON response from application");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to add or update indexer");
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest BuildRequest(SonarrSettings settings, string resource, HttpMethod method)
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
@@ -19,10 +19,12 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
public class WhisparrSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly WhisparrSettingsValidator Validator = new WhisparrSettingsValidator();
|
||||
private static readonly WhisparrSettingsValidator Validator = new ();
|
||||
|
||||
public WhisparrSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:6969";
|
||||
SyncCategories = new[] { 6000, 6010, 6020, 6030, 6040, 6045, 6050, 6070, 6080, 6090 };
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
public class WhisparrV3Proxy : IWhisparrV3Proxy
|
||||
{
|
||||
private const string AppApiRoute = "/api/v3";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@@ -34,13 +36,13 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
public WhisparrStatus GetStatus(WhisparrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/system/status", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppApiRoute}/system/status", HttpMethod.Get);
|
||||
return Execute<WhisparrStatus>(request);
|
||||
}
|
||||
|
||||
public List<WhisparrIndexer> GetIndexers(WhisparrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/indexer", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}", HttpMethod.Get);
|
||||
return Execute<List<WhisparrIndexer>>(request);
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/{indexerId}", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexerId}", HttpMethod.Get);
|
||||
return Execute<WhisparrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
@@ -64,19 +66,19 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
public void RemoveIndexer(int indexerId, WhisparrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/{indexerId}", HttpMethod.Delete);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexerId}", HttpMethod.Delete);
|
||||
_httpClient.Execute(request);
|
||||
}
|
||||
|
||||
public List<WhisparrIndexer> GetIndexerSchema(WhisparrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/indexer/schema", HttpMethod.Get);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/schema", HttpMethod.Get);
|
||||
return Execute<List<WhisparrIndexer>>(request);
|
||||
}
|
||||
|
||||
public WhisparrIndexer AddIndexer(WhisparrIndexer indexer, WhisparrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, "/api/v3/indexer", HttpMethod.Post);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}", HttpMethod.Post);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
@@ -85,16 +87,16 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
public WhisparrIndexer UpdateIndexer(WhisparrIndexer indexer, WhisparrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/{indexer.Id}", HttpMethod.Put);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexer.Id}", HttpMethod.Put);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return Execute<WhisparrIndexer>(request);
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
|
||||
public ValidationFailure TestConnection(WhisparrIndexer indexer, WhisparrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/test", HttpMethod.Post);
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/test", HttpMethod.Post);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
@@ -134,6 +136,53 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
return null;
|
||||
}
|
||||
|
||||
private WhisparrIndexer ExecuteIndexerRequest(HttpRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Execute<WhisparrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
switch (ex.Response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
break;
|
||||
case HttpStatusCode.BadRequest:
|
||||
if (ex.Response.Content.Contains("Query successful, but no results in the configured categories were returned from your indexer.", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Error(ex, "No Results in configured categories. See FAQ Entry: Prowlarr will not sync X Indexer to App");
|
||||
break;
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Invalid Request");
|
||||
break;
|
||||
case HttpStatusCode.SeeOther:
|
||||
_logger.Error(ex, "App returned redirect and is invalid. Check App URL");
|
||||
break;
|
||||
case HttpStatusCode.NotFound:
|
||||
_logger.Error(ex, "Remote indexer not found");
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unexpected response status code: {0}", ex.Response.StatusCode);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse JSON response from application");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to add or update indexer");
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest BuildRequest(WhisparrSettings settings, string resource, HttpMethod method)
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
@@ -69,7 +69,8 @@ namespace NzbDrone.Core.Backup
|
||||
_diskProvider.EnsureFolder(_backupTempFolder);
|
||||
_diskProvider.EnsureFolder(GetBackupFolder(backupType));
|
||||
|
||||
var backupFilename = string.Format("prowlarr_backup_v{0}_{1:yyyy.MM.dd_HH.mm.ss}.zip", BuildInfo.Version, DateTime.Now);
|
||||
var dateNow = DateTime.Now;
|
||||
var backupFilename = $"prowlarr_backup_v{BuildInfo.Version}_{dateNow:yyyy.MM.dd_HH.mm.ss}.zip";
|
||||
var backupPath = Path.Combine(GetBackupFolder(backupType), backupFilename);
|
||||
|
||||
Cleanup();
|
||||
@@ -81,7 +82,7 @@ namespace NzbDrone.Core.Backup
|
||||
|
||||
BackupConfigFile();
|
||||
BackupDatabase();
|
||||
CreateVersionInfo();
|
||||
CreateVersionInfo(dateNow);
|
||||
|
||||
_logger.ProgressDebug("Creating backup zip");
|
||||
|
||||
@@ -208,11 +209,15 @@ namespace NzbDrone.Core.Backup
|
||||
_diskTransferService.TransferFile(configFile, tempConfigFile, TransferMode.Copy);
|
||||
}
|
||||
|
||||
private void CreateVersionInfo()
|
||||
private void CreateVersionInfo(DateTime dateNow)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
var tempFile = Path.Combine(_backupTempFolder, "INFO");
|
||||
|
||||
builder.AppendLine(BuildInfo.Version.ToString());
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine($"v{BuildInfo.Version}");
|
||||
builder.AppendLine($"{dateNow:yyyy-MM-dd HH:mm:ss}");
|
||||
|
||||
_diskProvider.WriteAllText(tempFile, builder.ToString());
|
||||
}
|
||||
|
||||
private void CleanupOldBackups(BackupType backupType)
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace NzbDrone.Core.Configuration
|
||||
{
|
||||
Dictionary<string, object> GetConfigDictionary();
|
||||
void SaveConfigDictionary(Dictionary<string, object> configValues);
|
||||
void EnsureDefaultConfigFile();
|
||||
|
||||
string BindAddress { get; }
|
||||
int Port { get; }
|
||||
@@ -258,7 +259,7 @@ namespace NzbDrone.Core.Configuration
|
||||
|
||||
public T GetValueEnum<T>(string key, T defaultValue, bool persist = true)
|
||||
{
|
||||
return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), persist);
|
||||
return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue, persist), true);
|
||||
}
|
||||
|
||||
public string GetValue(string key, object defaultValue, bool persist = true)
|
||||
@@ -317,7 +318,7 @@ namespace NzbDrone.Core.Configuration
|
||||
SetValue(key, value.ToString().ToLower());
|
||||
}
|
||||
|
||||
private void EnsureDefaultConfigFile()
|
||||
public void EnsureDefaultConfigFile()
|
||||
{
|
||||
if (!File.Exists(_configFile))
|
||||
{
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
if (!ids.Any())
|
||||
{
|
||||
return new List<TModel>();
|
||||
return Array.Empty<TModel>();
|
||||
}
|
||||
|
||||
var result = Query(x => ids.Contains(x.Id));
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SQLite;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dapper;
|
||||
using NLog;
|
||||
@@ -38,17 +40,9 @@ namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
get
|
||||
{
|
||||
using (var db = _datamapperFactory())
|
||||
{
|
||||
if (db.ConnectionString.Contains(".db"))
|
||||
{
|
||||
return DatabaseType.SQLite;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DatabaseType.PostgreSQL;
|
||||
}
|
||||
}
|
||||
using var db = _datamapperFactory();
|
||||
|
||||
return db is SQLiteConnection ? DatabaseType.SQLite : DatabaseType.PostgreSQL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,24 +50,11 @@ namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
get
|
||||
{
|
||||
using (var db = _datamapperFactory())
|
||||
{
|
||||
string version;
|
||||
using var db = _datamapperFactory();
|
||||
var dbConnection = db as DbConnection;
|
||||
var version = Regex.Replace(dbConnection.ServerVersion, @"\(.*?\)", "");
|
||||
|
||||
try
|
||||
{
|
||||
version = db.QueryFirstOrDefault<string>("SHOW server_version");
|
||||
|
||||
//Postgres can return extra info about operating system on version call, ignore this
|
||||
version = Regex.Replace(version, @"\(.*?\)", "");
|
||||
}
|
||||
catch
|
||||
{
|
||||
version = db.QueryFirstOrDefault<string>("SELECT sqlite_version()");
|
||||
}
|
||||
|
||||
return new Version(version);
|
||||
}
|
||||
return new Version(version);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using NLog;
|
||||
@@ -64,7 +65,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation.Proxies
|
||||
catch (DownloadClientException e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
return new List<DownloadStationTask>();
|
||||
return Array.Empty<DownloadStationTask>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,11 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
return true;
|
||||
}
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
throw new DownloadClientException("Failed to connect to qBittorrent. Check your settings and qBittorrent configuration.", new HttpException(response));
|
||||
}
|
||||
|
||||
if (response.HasHttpError)
|
||||
{
|
||||
throw new DownloadClientException("Failed to connect to qBittorrent, check your settings.", new HttpException(response));
|
||||
|
||||
@@ -16,12 +16,12 @@ namespace NzbDrone.Core.Download.Clients.rTorrent
|
||||
public RTorrentDirectoryValidator(PathExistsValidator pathExistsValidator,
|
||||
MappedNetworkDriveValidator mappedNetworkDriveValidator)
|
||||
{
|
||||
RuleFor(c => c.Directory).Cascade(CascadeMode.StopOnFirstFailure)
|
||||
.IsValidPath()
|
||||
.SetValidator(mappedNetworkDriveValidator)
|
||||
.SetValidator(pathExistsValidator)
|
||||
.When(c => c.Directory.IsNotNullOrWhiteSpace())
|
||||
.When(c => c.Host == "localhost" || c.Host == "127.0.0.1");
|
||||
RuleFor(c => c.Directory).Cascade(CascadeMode.Stop)
|
||||
.IsValidPath()
|
||||
.SetValidator(mappedNetworkDriveValidator)
|
||||
.SetValidator(pathExistsValidator)
|
||||
.When(c => c.Directory.IsNotNullOrWhiteSpace())
|
||||
.When(c => c.Host == "localhost" || c.Host == "127.0.0.1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
#if !LIBRARY
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
#endif
|
||||
|
||||
namespace NzbDrone.Core
|
||||
{
|
||||
@@ -12,9 +10,8 @@ namespace NzbDrone.Core
|
||||
{
|
||||
public static string WithDefault(this string actual, object defaultValue)
|
||||
{
|
||||
#if !LIBRARY
|
||||
Ensure.That(defaultValue, () => defaultValue).IsNotNull();
|
||||
#endif
|
||||
|
||||
if (string.IsNullOrWhiteSpace(actual))
|
||||
{
|
||||
return defaultValue.ToString();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
using NzbDrone.Core.Indexers.Definitions.Cardigann;
|
||||
using NzbDrone.Core.IndexerVersions;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
using NzbDrone.Core.Indexers.Definitions.Cardigann;
|
||||
using NzbDrone.Core.IndexerVersions;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
@@ -31,9 +31,7 @@ namespace NzbDrone.Core.History
|
||||
|
||||
public History MostRecentForDownloadId(string downloadId)
|
||||
{
|
||||
return FindByDownloadId(downloadId)
|
||||
.OrderByDescending(h => h.Date)
|
||||
.FirstOrDefault();
|
||||
return FindByDownloadId(downloadId).MaxBy(h => h.Date);
|
||||
}
|
||||
|
||||
public List<History> FindByDownloadId(string downloadId)
|
||||
@@ -75,9 +73,7 @@ namespace NzbDrone.Core.History
|
||||
|
||||
public History MostRecentForIndexer(int indexerId)
|
||||
{
|
||||
return Query(x => x.IndexerId == indexerId)
|
||||
.OrderByDescending(h => h.Date)
|
||||
.FirstOrDefault();
|
||||
return Query(x => x.IndexerId == indexerId).MaxBy(h => h.Date);
|
||||
}
|
||||
|
||||
public List<History> Between(DateTime start, DateTime end)
|
||||
|
||||
@@ -118,12 +118,14 @@ namespace NzbDrone.Core.History
|
||||
|
||||
public void Handle(IndexerQueryEvent message)
|
||||
{
|
||||
var response = message.QueryResult.Response;
|
||||
|
||||
var history = new History
|
||||
{
|
||||
Date = DateTime.UtcNow,
|
||||
IndexerId = message.IndexerId,
|
||||
EventType = message.Query.IsRssSearch ? HistoryEventType.IndexerRss : HistoryEventType.IndexerQuery,
|
||||
Successful = message.QueryResult.Response?.StatusCode == HttpStatusCode.OK
|
||||
Successful = response?.StatusCode == HttpStatusCode.OK || (response is { Request: { SuppressHttpError: true, SuppressHttpErrorStatusCodes: not null } } && response.Request.SuppressHttpErrorStatusCodes.Contains(response.StatusCode))
|
||||
};
|
||||
|
||||
if (message.Query is MovieSearchCriteria)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
@"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
public List<ReleaseInfo> Releases { get; set; }
|
||||
public IList<ReleaseInfo> Releases { get; set; }
|
||||
|
||||
private static string RemoveInvalidXMLChars(string text)
|
||||
{
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
return spec;
|
||||
}
|
||||
|
||||
private async Task<List<ReleaseInfo>> Dispatch(Func<IIndexer, Task<IndexerPageableQueryResult>> searchAction, SearchCriteriaBase criteriaBase)
|
||||
private async Task<IList<ReleaseInfo>> Dispatch(Func<IIndexer, Task<IndexerPageableQueryResult>> searchAction, SearchCriteriaBase criteriaBase)
|
||||
{
|
||||
var indexers = _indexerFactory.Enabled();
|
||||
|
||||
@@ -168,7 +168,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
if (indexers.Count == 0)
|
||||
{
|
||||
_logger.Debug("All provided categories are unsupported by selected indexers: {0}", string.Join(", ", criteriaBase.Categories));
|
||||
return new List<ReleaseInfo>();
|
||||
return Array.Empty<ReleaseInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
if (_indexerLimitService.AtQueryLimit((IndexerDefinition)indexer.Definition))
|
||||
{
|
||||
return new List<ReleaseInfo>();
|
||||
return Array.Empty<ReleaseInfo>();
|
||||
}
|
||||
|
||||
try
|
||||
@@ -224,7 +224,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
_logger.Error(e, "Error while searching for {0}", criteriaBase);
|
||||
}
|
||||
|
||||
return new List<ReleaseInfo>();
|
||||
return Array.Empty<ReleaseInfo>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
using NzbDrone.Core.Indexers.Definitions.Cardigann;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -29,7 +29,7 @@ namespace NzbDrone.Core.IndexerVersions
|
||||
/* Update Service will fall back if version # does not exist for an indexer per Ta */
|
||||
|
||||
private const string DEFINITION_BRANCH = "master";
|
||||
private const int DEFINITION_VERSION = 8;
|
||||
private const int DEFINITION_VERSION = 9;
|
||||
|
||||
// Used when moving yml to C#
|
||||
private readonly List<string> _definitionBlocklist = new ()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -59,6 +59,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class AvistaZParser : AvistazParserBase
|
||||
{
|
||||
protected override string TimezoneOffset => "+01:00";
|
||||
protected override string TimezoneOffset => "+02:00";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,9 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
_logger.Debug("Avistaz authentication succeeded.");
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse response)
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
return response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.PreconditionFailed;
|
||||
return httpResponse.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.PreconditionFailed;
|
||||
}
|
||||
|
||||
protected override void ModifyRequest(IndexerRequest request)
|
||||
|
||||
@@ -13,18 +13,18 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
public class AvistazParserBase : IParseIndexerResponse
|
||||
{
|
||||
protected virtual string TimezoneOffset => "-05:00"; // Avistaz does not specify a timezone & returns server time
|
||||
protected virtual string TimezoneOffset => "-04:00"; // Avistaz does not specify a timezone & returns server time
|
||||
private readonly HashSet<string> _hdResolutions = new () { "1080p", "1080i", "720p" };
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<TorrentInfo>();
|
||||
var releaseInfos = new List<ReleaseInfo>();
|
||||
|
||||
if (indexerResponse.HttpResponse.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
return torrentInfos.ToArray();
|
||||
return releaseInfos.ToArray();
|
||||
}
|
||||
|
||||
if (indexerResponse.HttpResponse.StatusCode == HttpStatusCode.TooManyRequests)
|
||||
@@ -80,11 +80,13 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
release.TvdbId = row.MovieTvinfo.Tvdb.IsNullOrWhiteSpace() ? 0 : ParseUtil.TryCoerceInt(row.MovieTvinfo.Tvdb, out var tvdbResult) ? tvdbResult : 0;
|
||||
}
|
||||
|
||||
torrentInfos.Add(release);
|
||||
releaseInfos.Add(release);
|
||||
}
|
||||
|
||||
// order by date
|
||||
return torrentInfos.OrderByDescending(o => o.PublishDate).ToArray();
|
||||
return releaseInfos
|
||||
.OrderByDescending(o => o.PublishDate)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
// hook to adjust category parsing
|
||||
@@ -115,7 +117,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
cats.Add(NewznabStandardCategory.Audio);
|
||||
break;
|
||||
default:
|
||||
throw new Exception(string.Format("Error parsing Avistaz category type {0}", row.Type));
|
||||
throw new Exception($"Error parsing Avistaz category type {row.Type}");
|
||||
}
|
||||
|
||||
return cats;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
@@ -85,6 +86,8 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
request.HttpRequest.Headers.Add("Authorization", $"Bearer {Settings.Token}");
|
||||
|
||||
request.HttpRequest.SuppressHttpErrorStatusCodes = new[] { HttpStatusCode.NotFound };
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
@@ -27,7 +28,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override string[] IndexerUrls => new[] { "https://bakabt.me/" };
|
||||
public override string Description => "Anime Community";
|
||||
private string LoginUrl => Settings.BaseUrl + "login.php";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
@@ -59,7 +59,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var dom = parser.ParseDocument(response.Content);
|
||||
var downloadLink = dom.QuerySelector(".download_link")?.GetAttribute("href");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(downloadLink))
|
||||
if (downloadLink.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new Exception("Unable to find download link.");
|
||||
}
|
||||
@@ -71,17 +71,19 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
UpdateCookies(null, null);
|
||||
|
||||
var requestBuilder = new HttpRequestBuilder(LoginUrl)
|
||||
var loginUrl = Settings.BaseUrl + "login.php";
|
||||
|
||||
var requestBuilder = new HttpRequestBuilder(loginUrl)
|
||||
{
|
||||
LogResponseContent = true,
|
||||
AllowAutoRedirect = true,
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
|
||||
var loginPage = await ExecuteAuth(new HttpRequest(LoginUrl));
|
||||
var loginPage = await ExecuteAuth(new HttpRequest(loginUrl));
|
||||
|
||||
var parser = new HtmlParser();
|
||||
var dom = parser.ParseDocument(loginPage.Content);
|
||||
var dom = await parser.ParseDocumentAsync(loginPage.Content);
|
||||
var loginKey = dom.QuerySelector("input[name=\"loginKey\"]");
|
||||
if (loginKey != null)
|
||||
{
|
||||
@@ -98,21 +100,23 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var response = await ExecuteAuth(authLoginRequest);
|
||||
|
||||
if (response.Content != null && response.Content.Contains("<a href=\"logout.php\">Logout</a>"))
|
||||
if (CheckIfLoginNeeded(response))
|
||||
{
|
||||
UpdateCookies(response.GetCookies(), DateTime.Now.AddDays(30));
|
||||
var htmlParser = new HtmlParser();
|
||||
var document = await htmlParser.ParseDocumentAsync(response.Content);
|
||||
var errorMessage = document.QuerySelector("#loginError, .error")?.TextContent.Trim();
|
||||
|
||||
_logger.Debug("BakaBT authentication succeeded");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IndexerAuthException("BakaBT authentication failed");
|
||||
throw new IndexerAuthException(errorMessage ?? "Unknown error message, please report.");
|
||||
}
|
||||
|
||||
UpdateCookies(response.GetCookies(), DateTime.Now.AddDays(30));
|
||||
|
||||
_logger.Debug("BakaBT authentication succeeded");
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
return !httpResponse.Content.Contains("<a href=\"logout.php\">Logout</a>");
|
||||
return httpResponse.Content.Contains("loginForm");
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
@@ -241,7 +245,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<TorrentInfo>();
|
||||
var releaseInfos = new List<ReleaseInfo>();
|
||||
|
||||
var parser = new HtmlParser();
|
||||
var dom = parser.ParseDocument(indexerResponse.Content);
|
||||
@@ -356,11 +360,11 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
release.DownloadVolumeFactor = row.QuerySelector("span.freeleech") != null ? 0 : 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
torrentInfos.Add(release);
|
||||
releaseInfos.Add(release);
|
||||
}
|
||||
}
|
||||
|
||||
return torrentInfos.ToArray();
|
||||
return releaseInfos.ToArray();
|
||||
}
|
||||
|
||||
private ICollection<IndexerCategory> GetNextCategory(IElement row, ICollection<IndexerCategory> currentCategories)
|
||||
@@ -388,12 +392,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var categoryName = categoryElement.GetAttribute("title");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(categoryName))
|
||||
{
|
||||
return categoryName;
|
||||
}
|
||||
|
||||
return null;
|
||||
return categoryName.IsNotNullOrWhiteSpace() ? categoryName : null;
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
@@ -50,50 +50,61 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
|
||||
|
||||
var parameters = new BroadcastheNetTorrentQuery();
|
||||
|
||||
var searchString = searchCriteria.SearchTerm != null ? searchCriteria.SearchTerm : "";
|
||||
var searchString = searchCriteria.SearchTerm ?? "";
|
||||
|
||||
var btnResults = searchCriteria.Limit.GetValueOrDefault();
|
||||
if (btnResults == 0)
|
||||
{
|
||||
btnResults = (int)Capabilities.LimitsDefault;
|
||||
btnResults = Capabilities.LimitsDefault.GetValueOrDefault(PageSize);
|
||||
}
|
||||
|
||||
var btnOffset = searchCriteria.Offset.GetValueOrDefault();
|
||||
var btnOffset = searchCriteria.Offset.GetValueOrDefault(0);
|
||||
|
||||
if (searchCriteria.TvdbId > 0)
|
||||
{
|
||||
parameters.Tvdb = string.Format("{0}", searchCriteria.TvdbId);
|
||||
parameters.Tvdb = $"{searchCriteria.TvdbId}";
|
||||
}
|
||||
|
||||
if (searchCriteria.RId > 0)
|
||||
{
|
||||
parameters.Tvrage = string.Format("{0}", searchCriteria.RId);
|
||||
parameters.Tvrage = $"{searchCriteria.RId}";
|
||||
}
|
||||
|
||||
// If only the season/episode is searched for then change format to match expected format
|
||||
if (searchCriteria.Season > 0 && searchCriteria.Episode.IsNullOrWhiteSpace())
|
||||
{
|
||||
// Season Only
|
||||
parameters.Name = string.Format("Season {0}%", searchCriteria.Season.Value);
|
||||
// Search Season
|
||||
parameters.Category = "Season";
|
||||
parameters.Name = $"Season {searchCriteria.Season}%";
|
||||
pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset));
|
||||
|
||||
parameters = parameters.Clone();
|
||||
|
||||
// Search Episode
|
||||
parameters.Category = "Episode";
|
||||
parameters.Name = $"S{searchCriteria.Season:00}E%";
|
||||
pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset));
|
||||
}
|
||||
else if (searchCriteria.Season > 0 && Regex.IsMatch(searchCriteria.EpisodeSearchString, "(\\d{4}\\.\\d{2}\\.\\d{2})"))
|
||||
else if (DateTime.TryParseExact($"{searchCriteria.Season} {searchCriteria.Episode}", "yyyy MM/dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var showDate))
|
||||
{
|
||||
// Daily Episode
|
||||
parameters.Name = searchCriteria.EpisodeSearchString;
|
||||
parameters.Name = showDate.ToString("yyyy.MM.dd");
|
||||
parameters.Category = "Episode";
|
||||
pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset));
|
||||
}
|
||||
else if (searchCriteria.Season > 0 && int.Parse(searchCriteria.Episode) > 0)
|
||||
else if (searchCriteria.Season > 0 && int.TryParse(searchCriteria.Episode, out var episode) && episode > 0)
|
||||
{
|
||||
// Standard (S/E) Episode
|
||||
parameters.Name = string.Format("S{0:00}E{1:00}", searchCriteria.Season.Value, int.Parse(searchCriteria.Episode));
|
||||
parameters.Name = $"S{searchCriteria.Season:00}E{episode:00}";
|
||||
parameters.Category = "Episode";
|
||||
pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Neither a season only search nor daily nor standard, fall back to query
|
||||
parameters.Search = searchString.Replace(" ", "%");
|
||||
pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset));
|
||||
}
|
||||
|
||||
// Neither a season only search nor daily nor standard, fall back to query
|
||||
parameters.Search = searchString.Replace(" ", "%");
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -109,17 +120,17 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
|
||||
|
||||
var parameters = new BroadcastheNetTorrentQuery();
|
||||
|
||||
var searchString = searchCriteria.SearchTerm != null ? searchCriteria.SearchTerm : "";
|
||||
var searchString = searchCriteria.SearchTerm ?? "";
|
||||
|
||||
var btnResults = searchCriteria.Limit.GetValueOrDefault();
|
||||
if (btnResults == 0)
|
||||
{
|
||||
btnResults = (int)Capabilities.LimitsDefault;
|
||||
btnResults = Capabilities.LimitsDefault.GetValueOrDefault(PageSize);
|
||||
}
|
||||
|
||||
parameters.Search = searchString.Replace(" ", "%");
|
||||
var btnOffset = searchCriteria.Offset.GetValueOrDefault(0);
|
||||
|
||||
var btnOffset = searchCriteria.Offset.GetValueOrDefault();
|
||||
parameters.Search = searchString.Replace(" ", "%");
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset));
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
{
|
||||
public class Cardigann : TorrentIndexerBase<CardigannSettings>
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
{
|
||||
public class CardigannBase
|
||||
{
|
||||
@@ -825,9 +825,19 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
protected JArray JsonParseRowsSelector(JToken parsedJson, string rowSelector)
|
||||
{
|
||||
var selector = rowSelector.Split(':')[0];
|
||||
var rowsObj = parsedJson.SelectToken(selector).Value<JArray>();
|
||||
return new JArray(rowsObj.Where(t =>
|
||||
JsonParseFieldSelector(t.Value<JObject>(), rowSelector.Remove(0, selector.Length)) != null));
|
||||
|
||||
try
|
||||
{
|
||||
var rowsObj = parsedJson.SelectToken(selector).Value<JArray>();
|
||||
|
||||
return new JArray(rowsObj.Where(t => JsonParseFieldSelector(t.Value<JObject>(), rowSelector.Remove(0, selector.Length)) != null));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex, "Failed to parse JSON rows for selector \"{0}\"", rowSelector);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string JsonParseFieldSelector(JToken parsedJson, string rowSelector)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
{
|
||||
// A Dictionary allowing the same key multiple times
|
||||
public class KeyValuePairList : List<KeyValuePair<string, SelectorBlock>>, IDictionary<string, SelectorBlock>
|
||||
@@ -116,6 +116,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
public string Selector { get; set; }
|
||||
public bool Optional { get; set; }
|
||||
public string Default { get; set; }
|
||||
public string Text { get; set; }
|
||||
public string Attribute { get; set; }
|
||||
public string Remove { get; set; }
|
||||
@@ -146,6 +147,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
public List<SearchPathBlock> Paths { get; set; }
|
||||
public Dictionary<string, List<string>> Headers { get; set; }
|
||||
public List<FilterBlock> Keywordsfilters { get; set; }
|
||||
public bool AllowEmptyInputs { get; set; }
|
||||
public Dictionary<string, string> Inputs { get; set; }
|
||||
public List<ErrorBlock> Error { get; set; }
|
||||
public List<FilterBlock> Preprocessingfilters { get; set; }
|
||||
@@ -159,6 +161,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
public SelectorBlock Dateheaders { get; set; }
|
||||
public SelectorBlock Count { get; set; }
|
||||
public bool Multiple { get; set; }
|
||||
public bool MissingAttributeEqualsNoResults { get; set; }
|
||||
}
|
||||
|
||||
public class SearchPathBlock : RequestBlock
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
{
|
||||
public class CardigannMetaDefinition
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using AngleSharp.Xml.Parser;
|
||||
@@ -16,7 +15,7 @@ using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
{
|
||||
public class CardigannParser : CardigannBase, IParseIndexerResponse
|
||||
{
|
||||
@@ -81,20 +80,38 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
|
||||
if (search.Rows.Count != null)
|
||||
{
|
||||
var countVal = HandleJsonSelector(search.Rows.Count, parsedJson, variables);
|
||||
|
||||
if (int.TryParse(countVal, out var count) && count < 1)
|
||||
try
|
||||
{
|
||||
return releases;
|
||||
var countVal = HandleJsonSelector(search.Rows.Count, parsedJson, variables);
|
||||
|
||||
if (int.TryParse(countVal, out var count) && count < 1)
|
||||
{
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex, "Failed to parse JSON rows count.");
|
||||
}
|
||||
}
|
||||
|
||||
var rowsArray = JsonParseRowsSelector(parsedJson, search.Rows.Selector);
|
||||
|
||||
if (rowsArray == null)
|
||||
{
|
||||
if (search.Rows.MissingAttributeEqualsNoResults)
|
||||
{
|
||||
return releases;
|
||||
}
|
||||
|
||||
throw new IndexerException(indexerResponse, "Error Parsing Rows Selector");
|
||||
}
|
||||
|
||||
if (rowsArray.Count == 0)
|
||||
{
|
||||
return releases;
|
||||
}
|
||||
|
||||
foreach (var row in rowsArray)
|
||||
{
|
||||
var selObj = search.Rows.Attribute != null ? row.SelectToken(search.Rows.Attribute).Value<JToken>() : row;
|
||||
@@ -117,6 +134,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
string value = null;
|
||||
var variablesKey = ".Result." + fieldName;
|
||||
var isOptional = OptionalFields.Contains(field.Key) || fieldModifiers.Contains("optional") || field.Value.Optional;
|
||||
|
||||
try
|
||||
{
|
||||
var parentObj = mulRow;
|
||||
@@ -126,28 +144,35 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
}
|
||||
|
||||
value = HandleJsonSelector(field.Value, parentObj, variables, !isOptional);
|
||||
if (isOptional && string.IsNullOrWhiteSpace(value))
|
||||
|
||||
if (isOptional && value.IsNullOrWhiteSpace())
|
||||
{
|
||||
variables[variablesKey] = null;
|
||||
continue;
|
||||
var defaultValue = ApplyGoTemplateText(field.Value.Default, variables);
|
||||
|
||||
if (defaultValue.IsNullOrWhiteSpace())
|
||||
{
|
||||
variables[variablesKey] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
variables[variablesKey] = ParseFields(value, fieldName, release, fieldModifiers, searchUrlUri);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!variables.ContainsKey(variablesKey))
|
||||
if (!variables.ContainsKey(variablesKey) || isOptional)
|
||||
{
|
||||
variables[variablesKey] = null;
|
||||
}
|
||||
|
||||
if (isOptional)
|
||||
{
|
||||
variables[variablesKey] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new CardigannException(string.Format("Error while parsing field={0}, selector={1}, value={2}: {3}", field.Key, field.Value.Selector, value ?? "<null>", ex.Message));
|
||||
throw new CardigannException($"Error while parsing field={field.Key}, selector={field.Value.Selector}, value={value ?? "<null>"}: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,34 +273,41 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
string value = null;
|
||||
var variablesKey = ".Result." + fieldName;
|
||||
var isOptional = OptionalFields.Contains(field.Key) || fieldModifiers.Contains("optional") || field.Value.Optional;
|
||||
|
||||
try
|
||||
{
|
||||
value = HandleSelector(field.Value, row, variables, !isOptional);
|
||||
|
||||
if (isOptional && string.IsNullOrWhiteSpace(value))
|
||||
if (isOptional && value.IsNullOrWhiteSpace())
|
||||
{
|
||||
variables[variablesKey] = null;
|
||||
continue;
|
||||
var defaultValue = ApplyGoTemplateText(field.Value.Default, variables);
|
||||
|
||||
if (defaultValue.IsNullOrWhiteSpace())
|
||||
{
|
||||
variables[variablesKey] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
variables[variablesKey] = ParseFields(value, fieldName, release, fieldModifiers, searchUrlUri);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!variables.ContainsKey(variablesKey))
|
||||
if (!variables.ContainsKey(variablesKey) || isOptional)
|
||||
{
|
||||
variables[variablesKey] = null;
|
||||
}
|
||||
|
||||
if (OptionalFields.Contains(field.Key) || fieldModifiers.Contains("optional") || field.Value.Optional)
|
||||
if (isOptional)
|
||||
{
|
||||
variables[variablesKey] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (indexerLogging)
|
||||
{
|
||||
_logger.Trace("Error while parsing field={0}, selector={1}, value={2}: {3}", field.Key, field.Value.Selector, value == null ? "<null>" : value, ex.Message);
|
||||
_logger.Trace(ex, "Error while parsing field={0}, selector={1}, value={2}: {3}", field.Key, field.Value.Selector, value ?? "<null>", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
{
|
||||
public class CardigannRequest : IndexerRequest
|
||||
{
|
||||
|
||||
@@ -9,15 +9,15 @@ using AngleSharp.Html.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Definitions.Cardigann;
|
||||
using NzbDrone.Core.Indexers.Definitions.Cardigann.Exceptions;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
{
|
||||
public class CardigannRequestGenerator : CardigannBase, IIndexerRequestGenerator
|
||||
{
|
||||
@@ -589,7 +589,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<Captcha> GetConfigurationForSetup(bool automaticlogin)
|
||||
public async Task<Captcha> GetConfigurationForSetup(bool automaticLogin)
|
||||
{
|
||||
var login = _definition.Login;
|
||||
|
||||
@@ -610,6 +610,11 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
Encoding = _encoding
|
||||
};
|
||||
|
||||
if (_definition.Followredirect)
|
||||
{
|
||||
requestBuilder.AllowAutoRedirect = true;
|
||||
}
|
||||
|
||||
Cookies = null;
|
||||
if (login.Cookies != null)
|
||||
{
|
||||
@@ -627,11 +632,6 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
|
||||
Cookies = landingResult.GetCookies();
|
||||
|
||||
// Some sites have a temporary redirect before the login page, we need to process it.
|
||||
//if (_definition.Followredirect)
|
||||
//{
|
||||
// await FollowIfRedirect(landingResult, loginUrl.AbsoluteUri, overrideCookies: landingResult.Cookies, accumulateCookies: true);
|
||||
//}
|
||||
var htmlParser = new HtmlParser();
|
||||
landingResultDocument = htmlParser.ParseDocument(landingResult.Content);
|
||||
|
||||
@@ -642,7 +642,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
captcha = await GetCaptcha(login);
|
||||
}
|
||||
|
||||
if (captcha != null && automaticlogin)
|
||||
if (captcha != null && automaticLogin)
|
||||
{
|
||||
_logger.Error("CardigannIndexer ({0}): Found captcha during automatic login, aborting", _definition.Id);
|
||||
}
|
||||
@@ -727,16 +727,19 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
pairs = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
foreach (var input in request.Inputs)
|
||||
if (request.Inputs != null)
|
||||
{
|
||||
var value = ApplyGoTemplateText(input.Value, variables);
|
||||
if (method == HttpMethod.Get)
|
||||
foreach (var input in request.Inputs)
|
||||
{
|
||||
queryCollection.Add(input.Key, value);
|
||||
}
|
||||
else if (method == HttpMethod.Post)
|
||||
{
|
||||
pairs.Add(input.Key, value);
|
||||
var value = ApplyGoTemplateText(input.Value, variables);
|
||||
if (method == HttpMethod.Get)
|
||||
{
|
||||
queryCollection.Add(input.Key, value);
|
||||
}
|
||||
else if (method == HttpMethod.Post)
|
||||
{
|
||||
pairs.Add(input.Key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1101,7 +1104,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
var rawStr = ApplyGoTemplateText(input.Value, variables, WebUtility.UrlEncode);
|
||||
foreach (var part in rawStr.Split('&'))
|
||||
{
|
||||
var parts = part.Split(new char[] { '=' }, 2);
|
||||
var parts = part.Split(new[] { '=' }, 2);
|
||||
var key = parts[0];
|
||||
if (key.Length == 0)
|
||||
{
|
||||
@@ -1119,7 +1122,12 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
}
|
||||
else
|
||||
{
|
||||
queryCollection.Add(input.Key, ApplyGoTemplateText(input.Value, variables));
|
||||
var inputValue = ApplyGoTemplateText(input.Value, variables);
|
||||
|
||||
if (inputValue.IsNotNullOrWhiteSpace() || search.AllowEmptyInputs)
|
||||
{
|
||||
queryCollection.Add(input.Key, inputValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1133,7 +1141,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Info($"Adding request: {searchUrl}");
|
||||
_logger.Debug($"Adding request: {searchUrl}");
|
||||
|
||||
var requestBuilder = new HttpRequestBuilder(searchUrl)
|
||||
{
|
||||
@@ -1157,19 +1165,16 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
requestBuilder.SetHeaders(headers ?? new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
if (searchPath.Followredirect)
|
||||
{
|
||||
requestBuilder.AllowAutoRedirect = true;
|
||||
}
|
||||
|
||||
var request = requestBuilder
|
||||
.WithRateLimit(_rateLimit.TotalSeconds)
|
||||
.Build();
|
||||
|
||||
var cardigannRequest = new CardigannRequest(request, variables, searchPath)
|
||||
{
|
||||
HttpRequest =
|
||||
{
|
||||
AllowAutoRedirect = searchPath.Followredirect
|
||||
}
|
||||
};
|
||||
|
||||
yield return cardigannRequest;
|
||||
yield return new CardigannRequest(request, variables, searchPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
{
|
||||
public class CardigannSettings : NoAuthTorrentBaseSettings
|
||||
{
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
using NzbDrone.Common.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Cardigann.Exceptions
|
||||
{
|
||||
public class CardigannConfigException : CardigannException
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NzbDrone.Common.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Cardigann.Exceptions
|
||||
|
||||
@@ -72,7 +72,7 @@ public class FileListParser : IParseIndexerResponse
|
||||
InfoUrl = GetInfoUrl(id),
|
||||
Seeders = row.Seeders,
|
||||
Peers = row.Leechers + row.Seeders,
|
||||
PublishDate = DateTime.Parse(row.UploadDate + " +0200", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal),
|
||||
PublishDate = DateTime.Parse(row.UploadDate + " +0300", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal),
|
||||
Description = row.SmallDescription,
|
||||
Genres = row.SmallDescription.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList(),
|
||||
ImdbId = imdbId,
|
||||
|
||||
@@ -378,7 +378,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var jsonResponse = JsonConvert.DeserializeObject<MyAnonamouseResponse>(indexerResponse.Content);
|
||||
|
||||
var error = jsonResponse.Error;
|
||||
if (error != null && error == "Nothing returned, out of 0")
|
||||
if (error is "Nothing returned, out of 0" or "Nothing returned, out of 1")
|
||||
{
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
@@ -47,6 +48,11 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return new NebulanceParser(Settings);
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override Task<HttpRequest> GetDownloadRequest(Uri link)
|
||||
{
|
||||
// Avoid using cookies to prevent redirects to login page
|
||||
@@ -189,6 +195,11 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
|
||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
|
||||
}
|
||||
|
||||
var jsonResponse = new HttpResponse<JsonRpcResponse<NebulanceTorrents>>(indexerResponse.HttpResponse).Resource;
|
||||
|
||||
if (jsonResponse.Error != null || jsonResponse.Result == null)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using FluentValidation;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
@@ -35,7 +36,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new NzbIndexRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
return new NzbIndexRequestGenerator(Settings, Capabilities);
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
@@ -48,21 +49,21 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
},
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
// TODO build this out more
|
||||
@@ -349,15 +350,15 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
caps.Categories.AddCategoryMapping(915, NewznabStandardCategory.Other, "a.b.graveyard");
|
||||
caps.Categories.AddCategoryMapping(255, NewznabStandardCategory.Other, "a.b.guitar.tab");
|
||||
caps.Categories.AddCategoryMapping(256, NewznabStandardCategory.Other, "a.b.hd.ger.moviez");
|
||||
caps.Categories.AddCategoryMapping(257, NewznabStandardCategory.Other, "a.b.hdtv");
|
||||
caps.Categories.AddCategoryMapping(257, NewznabStandardCategory.TV, "a.b.hdtv");
|
||||
caps.Categories.AddCategoryMapping(258, NewznabStandardCategory.Other, "a.b.hdtv.french");
|
||||
caps.Categories.AddCategoryMapping(259, NewznabStandardCategory.Other, "a.b.hdtv.french.repost");
|
||||
caps.Categories.AddCategoryMapping(260, NewznabStandardCategory.Other, "a.b.hdtv.german");
|
||||
caps.Categories.AddCategoryMapping(261, NewznabStandardCategory.Other, "a.b.hdtv.german-audio");
|
||||
caps.Categories.AddCategoryMapping(260, NewznabStandardCategory.TVForeign, "a.b.hdtv.german");
|
||||
caps.Categories.AddCategoryMapping(261, NewznabStandardCategory.TVForeign, "a.b.hdtv.german-audio");
|
||||
caps.Categories.AddCategoryMapping(262, NewznabStandardCategory.Other, "a.b.hdtv.repost");
|
||||
caps.Categories.AddCategoryMapping(263, NewznabStandardCategory.Other, "a.b.hdtv.tv-episodes");
|
||||
caps.Categories.AddCategoryMapping(264, NewznabStandardCategory.Other, "a.b.hdtv.x264");
|
||||
caps.Categories.AddCategoryMapping(265, NewznabStandardCategory.Other, "a.b.hdtv.x264.french");
|
||||
caps.Categories.AddCategoryMapping(263, NewznabStandardCategory.TV, "a.b.hdtv.tv-episodes");
|
||||
caps.Categories.AddCategoryMapping(264, NewznabStandardCategory.TV, "a.b.hdtv.x264");
|
||||
caps.Categories.AddCategoryMapping(265, NewznabStandardCategory.TVForeign, "a.b.hdtv.x264.french");
|
||||
caps.Categories.AddCategoryMapping(266, NewznabStandardCategory.Other, "a.b.highspeed");
|
||||
caps.Categories.AddCategoryMapping(267, NewznabStandardCategory.Other, "a.b.hitoshirezu");
|
||||
caps.Categories.AddCategoryMapping(268, NewznabStandardCategory.Other, "a.b.holiday");
|
||||
@@ -1021,45 +1022,20 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class NzbIndexRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public NzbIndexSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
private readonly NzbIndexSettings _settings;
|
||||
private readonly IndexerCapabilities _capabilities;
|
||||
|
||||
public NzbIndexRequestGenerator()
|
||||
public NzbIndexRequestGenerator(NzbIndexSettings settings, IndexerCapabilities capabilities)
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, int limit, int offset)
|
||||
{
|
||||
var searchString = term;
|
||||
|
||||
var queryCollection = new NameValueCollection
|
||||
{
|
||||
{ "key", Settings.ApiKey },
|
||||
{ "max", limit.ToString() },
|
||||
{ "q", searchString },
|
||||
{ "p", ((offset / limit) + 1).ToString() }
|
||||
};
|
||||
|
||||
var searchUrl = string.Format("{0}/api/v3/search/?{1}", Settings.BaseUrl.TrimEnd('/'), queryCollection.GetQueryString());
|
||||
|
||||
if (categories != null)
|
||||
{
|
||||
foreach (var cat in Capabilities.Categories.MapTorznabCapsToTrackers(categories))
|
||||
{
|
||||
searchUrl += string.Format("&g[]={0}", cat);
|
||||
}
|
||||
}
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Json);
|
||||
|
||||
yield return request;
|
||||
_settings = settings;
|
||||
_capabilities = capabilities;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.Limit ?? 100, searchCriteria.Offset ?? 0));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories, searchCriteria.Limit ?? 100, searchCriteria.Offset ?? 0));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -1068,7 +1044,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.Limit ?? 100, searchCriteria.Offset ?? 0));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories, searchCriteria.Limit ?? 100, searchCriteria.Offset ?? 0));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -1077,7 +1053,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.Limit ?? 100, searchCriteria.Offset ?? 0));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedTvSearchString}", searchCriteria.Categories, searchCriteria.Limit ?? 100, searchCriteria.Offset ?? 0));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -1086,7 +1062,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.Limit ?? 100, searchCriteria.Offset ?? 0));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories, searchCriteria.Limit ?? 100, searchCriteria.Offset ?? 0));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -1095,11 +1071,36 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.Limit ?? 100, searchCriteria.Offset ?? 0));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories, searchCriteria.Limit ?? 100, searchCriteria.Offset ?? 0));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, int limit, int offset)
|
||||
{
|
||||
var queryCollection = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
{ "key", _settings.ApiKey },
|
||||
{ "max", limit.ToString() },
|
||||
{ "q", term },
|
||||
{ "p", (offset / limit).ToString() }
|
||||
};
|
||||
|
||||
if (categories != null)
|
||||
{
|
||||
foreach (var cat in _capabilities.Categories.MapTorznabCapsToTrackers(categories))
|
||||
{
|
||||
queryCollection.Add("g[]", $"{cat}");
|
||||
}
|
||||
}
|
||||
|
||||
var searchUrl = $"{_settings.BaseUrl.TrimEnd('/')}/api/v3/search/?{queryCollection.GetQueryString()}";
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Json);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
@@ -1109,6 +1110,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
private readonly NzbIndexSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
private static readonly Regex ParseTitleRegex = new (@"\""(?<title>[^:\/]*?)(?:\.(rar|nfo|mkv|par2|001|nzb|url|zip|r[0-9]{2}))?\""", RegexOptions.Compiled);
|
||||
|
||||
public NzbIndexParser(NzbIndexSettings settings, IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
@@ -1117,6 +1120,16 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new IndexerException(indexerResponse, "Unexpected response status {0} code from API request", indexerResponse.HttpResponse.StatusCode);
|
||||
}
|
||||
|
||||
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
|
||||
{
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
|
||||
}
|
||||
|
||||
var releaseInfos = new List<ReleaseInfo>();
|
||||
|
||||
// TODO Deserialize to TorrentSyndikatResponse Type
|
||||
@@ -1154,8 +1167,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return releaseInfos.ToArray();
|
||||
}
|
||||
|
||||
private static readonly Regex ParseTitleRegex = new Regex(@"\""(?<title>[^:\/]*?)(?:\.(rar|nfo|mkv|par2|001|nzb|url|zip|r[0-9]{2}))?\""");
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -281,7 +281,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.Time.ToUniversalTime(),
|
||||
Scene = torrent.Scene,
|
||||
Freeleech = torrent.IsFreeLeech || torrent.IsPersonalFreeLeech,
|
||||
Files = torrent.FileCount,
|
||||
Grabs = torrent.Snatches,
|
||||
DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1,
|
||||
@@ -318,7 +317,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Seeders = int.Parse(result.Seeders),
|
||||
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
|
||||
PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime(result.GroupTime),
|
||||
Freeleech = result.IsFreeLeech || result.IsPersonalFreeLeech,
|
||||
Files = result.FileCount,
|
||||
Grabs = result.Snatches,
|
||||
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
@@ -10,20 +8,19 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
public class PassThePopcorn : TorrentIndexerBase<PassThePopcornSettings>
|
||||
{
|
||||
public override string Name => "PassThePopcorn";
|
||||
public override string[] IndexerUrls => new string[] { "https://passthepopcorn.me" };
|
||||
public override string[] IndexerUrls => new[] { "https://passthepopcorn.me" };
|
||||
public override string Description => "PassThePopcorn (PTP) is a Private site for MOVIES / TV";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override bool SupportsRss => true;
|
||||
public override bool SupportsSearch => true;
|
||||
public override bool SupportsPagination => true;
|
||||
public override int PageSize => 50;
|
||||
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public override int PageSize => 50;
|
||||
|
||||
public PassThePopcorn(IIndexerHttpClient httpClient,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheManager cacheManager,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
@@ -33,7 +30,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new PassThePopcornRequestGenerator()
|
||||
return new PassThePopcornRequestGenerator
|
||||
{
|
||||
Settings = Settings,
|
||||
HttpClient = _httpClient,
|
||||
|
||||
@@ -39,11 +39,6 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
public List<string> Tags { get; set; }
|
||||
public List<Director> Directors { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
public int TotalLeechers { get; set; }
|
||||
public int TotalSeeders { get; set; }
|
||||
public int TotalSnatched { get; set; }
|
||||
public long MaxSize { get; set; }
|
||||
public string LastUploadTime { get; set; }
|
||||
public List<Torrent> Torrents { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
|
||||
}
|
||||
|
||||
var jsonResponse = JsonConvert.DeserializeObject<PassThePopcornResponse>(indexerResponse.Content);
|
||||
var jsonResponse = STJson.Deserialize<PassThePopcornResponse>(indexerResponse.Content);
|
||||
if (jsonResponse.TotalResults == "0" ||
|
||||
jsonResponse.TotalResults.IsNullOrWhiteSpace() ||
|
||||
jsonResponse.Movies == null)
|
||||
@@ -94,13 +94,14 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
// Only add approved torrents
|
||||
try
|
||||
{
|
||||
torrentInfos.Add(new TorrentInfo()
|
||||
torrentInfos.Add(new TorrentInfo
|
||||
{
|
||||
Guid = string.Format("PassThePopcorn-{0}", id),
|
||||
Title = title,
|
||||
Size = long.Parse(torrent.Size),
|
||||
DownloadUrl = GetDownloadUrl(id, jsonResponse.AuthKey, jsonResponse.PassKey),
|
||||
InfoUrl = GetInfoUrl(result.GroupId, id),
|
||||
Grabs = int.Parse(torrent.Snatched),
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.UploadTime.ToUniversalTime(),
|
||||
@@ -128,8 +129,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
torrentInfos;
|
||||
return torrentInfos;
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
@@ -24,11 +24,11 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
pageableRequests.Add(GetRequest(searchCriteria.FullImdbId));
|
||||
pageableRequests.Add(GetRequest(searchCriteria.FullImdbId, searchCriteria));
|
||||
}
|
||||
else
|
||||
{
|
||||
pageableRequests.Add(GetRequest(string.Format("{0}", searchCriteria.SearchTerm)));
|
||||
pageableRequests.Add(GetRequest($"{searchCriteria.SearchTerm}", searchCriteria));
|
||||
}
|
||||
|
||||
return pageableRequests;
|
||||
@@ -37,18 +37,25 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
|
||||
private IEnumerable<IndexerRequest> GetRequest(string searchParameters, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var queryParams = new NameValueCollection
|
||||
{
|
||||
{ "action", "advanced" },
|
||||
{ "json", "noredirect" },
|
||||
{ "grouping", "0" },
|
||||
{ "searchstr", searchParameters }
|
||||
};
|
||||
|
||||
if (searchCriteria.Limit is > 0 && searchCriteria.Offset is > 0)
|
||||
{
|
||||
var page = (int)(searchCriteria.Offset / searchCriteria.Limit) + 1;
|
||||
queryParams.Set("page", page.ToString());
|
||||
}
|
||||
|
||||
if (Settings.FreeleechOnly)
|
||||
{
|
||||
queryParams.Add("freetorrent", "1");
|
||||
queryParams.Set("freetorrent", "1");
|
||||
}
|
||||
|
||||
var request =
|
||||
@@ -91,7 +98,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetRequest(string.Format("{0}", searchCriteria.SearchTerm)));
|
||||
pageableRequests.Add(GetRequest($"{searchCriteria.SearchTerm}", searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Rarbg
|
||||
case (int)HttpStatusCode.OK:
|
||||
break;
|
||||
default:
|
||||
throw new IndexerException(response, "Indexer API call returned an unexpected StatusCode [{0}]", responseCode);
|
||||
throw new IndexerException(response, "Indexer API call returned an unexpected status code [{0}]", responseCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,9 +112,9 @@ namespace NzbDrone.Core.Indexers.Definitions.Rarbg
|
||||
return caps;
|
||||
}
|
||||
|
||||
protected override async Task<IndexerQueryResult> FetchPage(IndexerRequest request, IParseIndexerResponse parser)
|
||||
protected override async Task<IndexerResponse> FetchIndexerResponse(IndexerRequest request)
|
||||
{
|
||||
var response = await FetchIndexerResponse(request);
|
||||
var response = await base.FetchIndexerResponse(request);
|
||||
|
||||
CheckResponseByStatusCode(response);
|
||||
|
||||
@@ -133,36 +133,19 @@ namespace NzbDrone.Core.Indexers.Definitions.Rarbg
|
||||
qs.Set("token", newToken);
|
||||
|
||||
request.HttpRequest.Url = request.Url.SetQuery(qs.GetQueryString());
|
||||
response = await FetchIndexerResponse(request);
|
||||
|
||||
return await FetchIndexerResponse(request);
|
||||
}
|
||||
else if (jsonResponse.Resource.error_code is 5)
|
||||
|
||||
if (jsonResponse.Resource.error_code is 5)
|
||||
{
|
||||
_logger.Debug("Rarbg temp rate limit hit, retrying request");
|
||||
response = await FetchIndexerResponse(request);
|
||||
|
||||
return await FetchIndexerResponse(request);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var releases = parser.ParseResponse(response).ToList();
|
||||
|
||||
if (releases.Count == 0)
|
||||
{
|
||||
_logger.Trace(response.Content);
|
||||
}
|
||||
|
||||
return new IndexerQueryResult
|
||||
{
|
||||
Releases = releases,
|
||||
Response = response.HttpResponse
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.WithData(response.HttpResponse, 128 * 1024);
|
||||
_logger.Trace("Unexpected Response content ({0} bytes): {1}", response.HttpResponse.ResponseData.Length, response.HttpResponse.Content);
|
||||
throw;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||
|
||||
@@ -46,11 +46,11 @@ namespace NzbDrone.Core.Indexers.Definitions.Rarbg
|
||||
{
|
||||
requestBuilder.AddQueryParam("search_imdb", imdbId);
|
||||
}
|
||||
else if (tmdbId.HasValue && tmdbId > 0)
|
||||
else if (tmdbId is > 0)
|
||||
{
|
||||
requestBuilder.AddQueryParam("search_themoviedb", tmdbId);
|
||||
}
|
||||
else if (tvdbId.HasValue && tvdbId > 0)
|
||||
else if (tvdbId is > 0)
|
||||
{
|
||||
requestBuilder.AddQueryParam("search_tvdb", tvdbId);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Rarbg
|
||||
|
||||
return response.Resource["token"].ToString();
|
||||
},
|
||||
TimeSpan.FromMinutes(14.0));
|
||||
TimeSpan.FromMinutes(14));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +247,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.Time.ToUniversalTime(),
|
||||
Scene = torrent.Scene,
|
||||
Freeleech = torrent.IsFreeLeech || torrent.IsPersonalFreeLeech,
|
||||
Files = torrent.FileCount,
|
||||
Grabs = torrent.Snatches,
|
||||
DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1,
|
||||
@@ -284,7 +283,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Seeders = int.Parse(result.Seeders),
|
||||
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
|
||||
PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime,
|
||||
Freeleech = result.IsFreeLeech || result.IsPersonalFreeLeech,
|
||||
Files = result.FileCount,
|
||||
Grabs = result.Snatches,
|
||||
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1,
|
||||
|
||||
@@ -27,7 +27,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override string[] IndexerUrls => new[]
|
||||
{
|
||||
"https://rutracker.org/",
|
||||
"https://rutracker.net/"
|
||||
"https://rutracker.net/",
|
||||
"https://rutracker.nl/"
|
||||
};
|
||||
public override string Description => "RuTracker is a Semi-Private Russian torrent site with a thriving file-sharing community";
|
||||
public override string Language => "ru-RU";
|
||||
|
||||
@@ -72,7 +72,7 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
var releaseInfos = new List<ReleaseInfo>();
|
||||
|
||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
@@ -95,7 +95,7 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
jsonResponse.Resource.Status.IsNullOrWhiteSpace() ||
|
||||
jsonResponse.Resource.Response == null)
|
||||
{
|
||||
return torrentInfos;
|
||||
return releaseInfos.ToArray();
|
||||
}
|
||||
|
||||
foreach (var result in jsonResponse.Resource.Response.Results)
|
||||
@@ -109,10 +109,11 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
// in SC movies, artist=director and GroupName=title
|
||||
var artist = WebUtility.HtmlDecode(result.Artist);
|
||||
var title = WebUtility.HtmlDecode(result.GroupName);
|
||||
var time = DateTime.SpecifyKind(torrent.Time, DateTimeKind.Unspecified);
|
||||
|
||||
var release = new GazelleInfo
|
||||
{
|
||||
Guid = string.Format("SecretCinema-{0}", id),
|
||||
Guid = $"SecretCinema-{id}",
|
||||
Title = title,
|
||||
Container = torrent.Encoding,
|
||||
Files = torrent.FileCount,
|
||||
@@ -123,7 +124,7 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
InfoUrl = GetInfoUrl(result.GroupId, id),
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.Time.ToUniversalTime(),
|
||||
PublishDate = new DateTimeOffset(time, TimeSpan.FromHours(2)).UtcDateTime,
|
||||
Scene = torrent.Scene,
|
||||
DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1,
|
||||
UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1
|
||||
@@ -162,7 +163,7 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
release.Title += " [Cue]";
|
||||
}
|
||||
|
||||
torrentInfos.Add(release);
|
||||
releaseInfos.Add(release);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -172,7 +173,7 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
|
||||
var release = new GazelleInfo
|
||||
{
|
||||
Guid = string.Format("SecretCinema-{0}", id),
|
||||
Guid = $"SecretCinema-{id}",
|
||||
Title = groupName,
|
||||
Size = long.Parse(result.Size),
|
||||
DownloadUrl = GetDownloadUrl(id),
|
||||
@@ -196,13 +197,13 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
|
||||
}
|
||||
|
||||
torrentInfos.Add(release);
|
||||
releaseInfos.Add(release);
|
||||
}
|
||||
}
|
||||
|
||||
// order by date
|
||||
return
|
||||
torrentInfos
|
||||
releaseInfos
|
||||
.OrderByDescending(o => o.PublishDate)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
@@ -323,6 +323,11 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{ "nm", term.IsNotNullOrWhiteSpace() ? term.Replace("-", " ") : "" }
|
||||
};
|
||||
|
||||
if (_settings.FreeleechOnly)
|
||||
{
|
||||
parameters.Add("sds", "1");
|
||||
}
|
||||
|
||||
var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(categories);
|
||||
if (queryCats.Any())
|
||||
{
|
||||
@@ -408,6 +413,19 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
MinimumSeedTime = 0
|
||||
};
|
||||
|
||||
if (row.QuerySelector("img[src=\"images/gold.gif\"], img[src=\"images/authors.gif\"]") != null)
|
||||
{
|
||||
release.DownloadVolumeFactor = 0;
|
||||
}
|
||||
else if (row.QuerySelector("img[src=\"images/silver.gif\"]") != null)
|
||||
{
|
||||
release.DownloadVolumeFactor = 0.5;
|
||||
}
|
||||
else if (row.QuerySelector("img[src=\"images/bronze.gif\"]") != null)
|
||||
{
|
||||
release.DownloadVolumeFactor = 0.75;
|
||||
}
|
||||
|
||||
releaseInfos.Add(release);
|
||||
}
|
||||
|
||||
@@ -540,7 +558,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
StripCyrillicLetters = true;
|
||||
}
|
||||
|
||||
[FieldDefinition(4, Label = "Strip Cyrillic Letters", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(4, Label = "Freeleech Only", HelpText = "Search Freeleech torrents only", Type = FieldType.Checkbox)]
|
||||
public bool FreeleechOnly { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Strip Cyrillic Letters", Type = FieldType.Checkbox)]
|
||||
public bool StripCyrillicLetters { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato
|
||||
{
|
||||
public class TorrentPotato : TorrentIndexerBase<TorrentPotatoSettings>
|
||||
{
|
||||
public override string Name => "TorrentPotato";
|
||||
public override string[] IndexerUrls => new string[] { "http://127.0.0.1" };
|
||||
public override string[] IndexerUrls => new[] { "http://127.0.0.1" };
|
||||
public override string Description => "A JSON based torrent provider previously developed for CouchPotato";
|
||||
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
|
||||
@@ -6,7 +6,7 @@ using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato
|
||||
{
|
||||
public class TorrentPotatoParser : IParseIndexerResponse
|
||||
{
|
||||
@@ -31,17 +31,17 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
|
||||
foreach (var torrent in jsonResponse.Resource.results)
|
||||
{
|
||||
var torrentInfo = new TorrentInfo();
|
||||
|
||||
torrentInfo.Guid = GetGuid(torrent);
|
||||
torrentInfo.Title = torrent.release_name;
|
||||
torrentInfo.Size = (long)torrent.size * 1000 * 1000;
|
||||
torrentInfo.DownloadUrl = torrent.download_url;
|
||||
torrentInfo.InfoUrl = torrent.details_url;
|
||||
torrentInfo.PublishDate = torrent.publish_date.ToUniversalTime();
|
||||
torrentInfo.Seeders = torrent.seeders;
|
||||
torrentInfo.Peers = torrent.leechers + torrent.seeders;
|
||||
torrentInfo.Freeleech = torrent.freeleech;
|
||||
var torrentInfo = new TorrentInfo
|
||||
{
|
||||
Guid = GetGuid(torrent),
|
||||
Title = torrent.release_name,
|
||||
Size = (long)torrent.size * 1000 * 1000,
|
||||
DownloadUrl = torrent.download_url,
|
||||
InfoUrl = torrent.details_url,
|
||||
PublishDate = torrent.publish_date.ToUniversalTime(),
|
||||
Seeders = torrent.seeders,
|
||||
Peers = torrent.leechers + torrent.seeders
|
||||
};
|
||||
|
||||
results.Add(torrentInfo);
|
||||
}
|
||||
|
||||
@@ -4,16 +4,12 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato
|
||||
{
|
||||
public class TorrentPotatoRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public TorrentPotatoSettings Settings { get; set; }
|
||||
|
||||
public TorrentPotatoRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual IndexerPageableRequestChain GetRecentRequests()
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato
|
||||
{
|
||||
public class TorrentPotatoResponse
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato
|
||||
{
|
||||
public class TorrentPotatoSettingsValidator : NoAuthSettingsValidator<TorrentPotatoSettings>
|
||||
{
|
||||
|
||||
@@ -508,9 +508,12 @@ namespace NzbDrone.Core.Indexers
|
||||
}
|
||||
|
||||
// Throw common http errors here before we try to parse
|
||||
if (response.HasHttpError)
|
||||
if (response.HasHttpError && (request.HttpRequest.SuppressHttpErrorStatusCodes == null || !request.HttpRequest.SuppressHttpErrorStatusCodes.Contains(response.StatusCode)))
|
||||
{
|
||||
_logger.Warn("HTTP Error - {0}", response);
|
||||
if (response.Request.LogHttpError)
|
||||
{
|
||||
_logger.Warn("HTTP Error - {0}", response);
|
||||
}
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.TooManyRequests)
|
||||
{
|
||||
@@ -644,7 +647,7 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
_logger.Warn(ex, "Unable to connect to indexer");
|
||||
|
||||
return new ValidationFailure(string.Empty, "Unable to connect to indexer, check the log above the ValidationFailure for more details");
|
||||
return new ValidationFailure(string.Empty, "Unable to connect to indexer, check the log above the ValidationFailure for more details. " + ex.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return new List<IndexerCategory>();
|
||||
return Array.Empty<IndexerCategory>();
|
||||
}
|
||||
|
||||
var cats = _categoryMapping
|
||||
@@ -109,7 +109,7 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(trackerCategoryDesc))
|
||||
{
|
||||
return new List<IndexerCategory>();
|
||||
return Array.Empty<IndexerCategory>();
|
||||
}
|
||||
|
||||
var cats = _categoryMapping
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
using NzbDrone.Core.Indexers.Definitions.Cardigann;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Text;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
using NzbDrone.Core.Indexers.Definitions.Cardigann;
|
||||
using NzbDrone.Core.Indexers.Newznab;
|
||||
using NzbDrone.Core.IndexerVersions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
foreach (var definition in definitions)
|
||||
{
|
||||
if (definition.Implementation == typeof(Cardigann.Cardigann).Name)
|
||||
if (definition.Implementation == nameof(Cardigann))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -72,7 +72,7 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
var definition = base.Get(id);
|
||||
|
||||
if (definition.Implementation == typeof(Cardigann.Cardigann).Name)
|
||||
if (definition.Implementation == nameof(Cardigann))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -177,7 +177,7 @@ namespace NzbDrone.Core.Indexers
|
||||
}
|
||||
|
||||
var definitions = provider.DefaultDefinitions
|
||||
.Where(v => v.Name != null && v.Name != nameof(Cardigann.Cardigann) && v.Name != nameof(Newznab.Newznab) && v.Name != nameof(Torznab.Torznab));
|
||||
.Where(v => v.Name != null && v.Name != nameof(Cardigann) && v.Name != nameof(Newznab.Newznab) && v.Name != nameof(Torznab.Torznab));
|
||||
|
||||
foreach (IndexerDefinition definition in definitions)
|
||||
{
|
||||
@@ -189,7 +189,7 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
public override IEnumerable<IndexerDefinition> GetPresetDefinitions(IndexerDefinition providerDefinition)
|
||||
{
|
||||
return new List<IndexerDefinition>();
|
||||
return Array.Empty<IndexerDefinition>();
|
||||
}
|
||||
|
||||
public override void SetProviderCharacteristics(IIndexer provider, IndexerDefinition definition)
|
||||
@@ -203,7 +203,7 @@ namespace NzbDrone.Core.Indexers
|
||||
definition.SupportsPagination = provider.SupportsPagination;
|
||||
|
||||
//We want to use the definition Caps and Privacy for Cardigann instead of the provider.
|
||||
if (definition.Implementation != nameof(Cardigann.Cardigann))
|
||||
if (definition.Implementation != nameof(Cardigann))
|
||||
{
|
||||
definition.IndexerUrls = provider.IndexerUrls;
|
||||
definition.LegacyUrls = provider.LegacyUrls;
|
||||
@@ -276,13 +276,13 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
SetProviderCharacteristics(provider, definition);
|
||||
|
||||
if (definition.Implementation == typeof(Newznab.Newznab).Name || definition.Implementation == typeof(Torznab.Torznab).Name)
|
||||
if (definition.Implementation is nameof(Newznab.Newznab) or nameof(Torznab.Torznab))
|
||||
{
|
||||
var settings = (NewznabSettings)definition.Settings;
|
||||
settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings, definition)?.Categories.GetTorznabCategoryList() ?? null;
|
||||
}
|
||||
|
||||
if (definition.Implementation == typeof(Cardigann.Cardigann).Name)
|
||||
if (definition.Implementation == nameof(Cardigann))
|
||||
{
|
||||
MapCardigannDefinition(definition);
|
||||
}
|
||||
@@ -296,13 +296,13 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
SetProviderCharacteristics(provider, definition);
|
||||
|
||||
if (definition.Enable && (definition.Implementation == typeof(Newznab.Newznab).Name || definition.Implementation == typeof(Torznab.Torznab).Name))
|
||||
if (definition.Enable && definition.Implementation is nameof(Newznab.Newznab) or nameof(Torznab.Torznab))
|
||||
{
|
||||
var settings = (NewznabSettings)definition.Settings;
|
||||
settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings, definition)?.Categories.GetTorznabCategoryList() ?? null;
|
||||
}
|
||||
|
||||
if (definition.Implementation == typeof(Cardigann.Cardigann).Name)
|
||||
if (definition.Implementation == nameof(Cardigann))
|
||||
{
|
||||
MapCardigannDefinition(definition);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user