mirror of
https://github.com/Readarr/Readarr.git
synced 2026-03-15 15:54:08 -04:00
Compare commits
71 Commits
v0.3.3.217
...
v0.3.9.227
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e188c9aac0 | ||
|
|
a3ae2359f5 | ||
|
|
5b92905dd4 | ||
|
|
fc402743aa | ||
|
|
b9d53ed732 | ||
|
|
d248747635 | ||
|
|
d70224c811 | ||
|
|
acdf8c8aa8 | ||
|
|
3ed41554ce | ||
|
|
ce808c6d7b | ||
|
|
63b1b56a4f | ||
|
|
a5647bedc8 | ||
|
|
fe659bb79d | ||
|
|
9918535509 | ||
|
|
f9a6db40b8 | ||
|
|
6273d69ed6 | ||
|
|
7012380e95 | ||
|
|
b001ecd698 | ||
|
|
e28becdda4 | ||
|
|
eae06695e8 | ||
|
|
54a9af2ced | ||
|
|
c9b55266fc | ||
|
|
05b64406a4 | ||
|
|
1f37c5387b | ||
|
|
4a6c7042fe | ||
|
|
d7305b9753 | ||
|
|
bd56643eaa | ||
|
|
44e6de2e23 | ||
|
|
b209d047fa | ||
|
|
fd5ab27df6 | ||
|
|
4a89befd79 | ||
|
|
1a30293c33 | ||
|
|
f5c2a6bf51 | ||
|
|
f3d90fdaf1 | ||
|
|
04c5671a0a | ||
|
|
22cc88c5e7 | ||
|
|
ca0c95a2d2 | ||
|
|
419f790d66 | ||
|
|
9fe08429bc | ||
|
|
71f4a88ab3 | ||
|
|
30b283eda3 | ||
|
|
e23d0bbfa1 | ||
|
|
765a2aa01b | ||
|
|
64895c3210 | ||
|
|
03ab84a814 | ||
|
|
b77e5b14e1 | ||
|
|
75efbd45e1 | ||
|
|
00cac507ad | ||
|
|
c4850505b0 | ||
|
|
75213c86a1 | ||
|
|
b8c3a42643 | ||
|
|
8acb034aa6 | ||
|
|
889d32552b | ||
|
|
adc5f4db97 | ||
|
|
9d08050f96 | ||
|
|
f8cffbb4cf | ||
|
|
14aeb66142 | ||
|
|
37e8e11e31 | ||
|
|
bdb2f14936 | ||
|
|
a97af657be | ||
|
|
301127e6dc | ||
|
|
1f95bcae4e | ||
|
|
29118cda45 | ||
|
|
09beaa939d | ||
|
|
2107624f1c | ||
|
|
c1c2076e5c | ||
|
|
c31a797bd8 | ||
|
|
ebb2b4eca3 | ||
|
|
3ec5d9b9fe | ||
|
|
1ad84a7c44 | ||
|
|
9d67c18254 |
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Bug Report
|
||||
description: 'Report a new bug, if you are not 100% certain this is a bug please go to our Reddit or Discord first'
|
||||
description: 'Report a new bug, if you are not 100% certain this is a bug please go to our Discord first'
|
||||
labels: ['Type: Bug', 'Status: Needs Triage']
|
||||
body:
|
||||
- type: checkboxes
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -3,6 +3,3 @@ contact_links:
|
||||
- name: Support via Discord
|
||||
url: https://readarr.com/discord
|
||||
about: Chat with users and devs on support and setup related topics.
|
||||
- name: Support via Reddit
|
||||
url: https://reddit.com/r/Readarr
|
||||
about: Discuss and search thru support topics.
|
||||
|
||||
3
.github/workflows/support.yml
vendored
3
.github/workflows/support.yml
vendored
@@ -15,8 +15,7 @@ jobs:
|
||||
issue-comment: >
|
||||
:wave: @{issue-author}, we use the issue tracker exclusively
|
||||
for bug reports and feature requests. However, this issue appears
|
||||
to be a support request. Please hop over onto our [Discord](https://readarr.com/discord)
|
||||
or [Subreddit](https://reddit.com/r/readarr)
|
||||
to be a support request. Please hop over onto our [Discord](https://readarr.com/discord).
|
||||
close-issue: true
|
||||
lock-issue: false
|
||||
- uses: dessant/support-requests@v3
|
||||
|
||||
@@ -9,13 +9,13 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '0.3.3'
|
||||
majorVersion: '0.3.9'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
readarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(readarrVersion)'
|
||||
sentryOrg: 'servarr'
|
||||
sentryUrl: 'https://sentry.servarr.com'
|
||||
dotnetVersion: '6.0.408'
|
||||
dotnetVersion: '6.0.413'
|
||||
nodeVersion: '16.X'
|
||||
innoVersion: '6.2.0'
|
||||
windowsImage: 'windows-2022'
|
||||
|
||||
@@ -4,14 +4,14 @@ module.exports = {
|
||||
plugins: [
|
||||
// Stage 1
|
||||
'@babel/plugin-proposal-export-default-from',
|
||||
['@babel/plugin-proposal-optional-chaining', { loose }],
|
||||
['@babel/plugin-proposal-nullish-coalescing-operator', { loose }],
|
||||
['@babel/plugin-transform-optional-chaining', { loose }],
|
||||
['@babel/plugin-transform-nullish-coalescing-operator', { loose }],
|
||||
|
||||
// Stage 2
|
||||
'@babel/plugin-proposal-export-namespace-from',
|
||||
'@babel/plugin-transform-export-namespace-from',
|
||||
|
||||
// Stage 3
|
||||
['@babel/plugin-proposal-class-properties', { loose }],
|
||||
['@babel/plugin-transform-class-properties', { loose }],
|
||||
'@babel/plugin-syntax-dynamic-import'
|
||||
],
|
||||
env: {
|
||||
|
||||
@@ -7,13 +7,10 @@ function findImage(images, coverType) {
|
||||
}
|
||||
|
||||
function getUrl(image, coverType, size) {
|
||||
if (image) {
|
||||
// Remove protocol
|
||||
let url = image.url;
|
||||
const imageUrl = image?.url;
|
||||
|
||||
url = url.replace(`${coverType}.jpg`, `${coverType}-${size}.jpg`);
|
||||
|
||||
return url;
|
||||
if (imageUrl) {
|
||||
return imageUrl.replace(`${coverType}.jpg`, `${coverType}-${size}.jpg`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,7 @@ const defaultFontSize = parseInt(fonts.defaultFontSize);
|
||||
const lineHeight = parseFloat(fonts.lineHeight);
|
||||
|
||||
function getFanartUrl(images) {
|
||||
const fanartImage = images.find((x) => x.coverType === 'fanart');
|
||||
|
||||
if (fanartImage) {
|
||||
// Remove protocol
|
||||
return fanartImage.url.replace(/^https?:/, '');
|
||||
}
|
||||
return images.find((x) => x.coverType === 'fanart')?.url;
|
||||
}
|
||||
|
||||
class AuthorDetailsHeader extends Component {
|
||||
|
||||
@@ -16,7 +16,7 @@ import AuthorIndex from './AuthorIndex';
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createAuthorClientSideCollectionItemsSelector('authorIndex'),
|
||||
createCommandExecutingSelector(commandNames.REFRESH_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.BULK_REFRESH_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.RSS_SYNC),
|
||||
createCommandExecutingSelector(commandNames.RENAME_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.RETAG_AUTHOR),
|
||||
@@ -24,17 +24,17 @@ function createMapStateToProps() {
|
||||
(
|
||||
author,
|
||||
isRefreshingAuthor,
|
||||
isRssSyncExecuting,
|
||||
isOrganizingAuthor,
|
||||
isRetaggingAuthor,
|
||||
isRssSyncExecuting,
|
||||
dimensionsState
|
||||
) => {
|
||||
return {
|
||||
...author,
|
||||
isRefreshingAuthor,
|
||||
isRssSyncExecuting,
|
||||
isOrganizingAuthor,
|
||||
isRetaggingAuthor,
|
||||
isRssSyncExecuting,
|
||||
isSmallScreen: dimensionsState.isSmallScreen
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,12 +21,7 @@ const defaultFontSize = parseInt(fonts.defaultFontSize);
|
||||
const lineHeight = parseFloat(fonts.lineHeight);
|
||||
|
||||
function getFanartUrl(images) {
|
||||
const fanartImage = images.find((x) => x.coverType === 'fanart');
|
||||
|
||||
if (fanartImage) {
|
||||
// Remove protocol
|
||||
return fanartImage.url.replace(/^https?:/, '');
|
||||
}
|
||||
return images.find((x) => x.coverType === 'fanart')?.url;
|
||||
}
|
||||
|
||||
class BookDetailsHeader extends Component {
|
||||
|
||||
@@ -16,8 +16,8 @@ import BookIndex from './BookIndex';
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createBookClientSideCollectionItemsSelector('bookIndex'),
|
||||
createCommandExecutingSelector(commandNames.REFRESH_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.REFRESH_BOOK),
|
||||
createCommandExecutingSelector(commandNames.BULK_REFRESH_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.BULK_REFRESH_BOOK),
|
||||
createCommandExecutingSelector(commandNames.RSS_SYNC),
|
||||
createCommandExecutingSelector(commandNames.CUTOFF_UNMET_BOOK_SEARCH),
|
||||
createCommandExecutingSelector(commandNames.MISSING_BOOK_SEARCH),
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.version {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointMedium) {
|
||||
.image {
|
||||
height: 250px;
|
||||
|
||||
@@ -6,6 +6,7 @@ interface CssExports {
|
||||
'image': string;
|
||||
'imageContainer': string;
|
||||
'message': string;
|
||||
'version': string;
|
||||
}
|
||||
export const cssExports: CssExports;
|
||||
export default cssExports;
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import styles from './ErrorBoundaryError.css';
|
||||
|
||||
function ErrorBoundaryError(props) {
|
||||
const {
|
||||
className,
|
||||
messageClassName,
|
||||
detailsClassName,
|
||||
message,
|
||||
error,
|
||||
info
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className={messageClassName}>
|
||||
{message}
|
||||
</div>
|
||||
|
||||
<div className={styles.imageContainer}>
|
||||
<img
|
||||
className={styles.image}
|
||||
src={`${window.Readarr.urlBase}/Content/Images/error.png`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<details className={detailsClassName}>
|
||||
{
|
||||
error &&
|
||||
<div>
|
||||
{error.toString()}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className={styles.info}>
|
||||
{info.componentStack}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ErrorBoundaryError.propTypes = {
|
||||
className: PropTypes.string.isRequired,
|
||||
messageClassName: PropTypes.string.isRequired,
|
||||
detailsClassName: PropTypes.string.isRequired,
|
||||
message: PropTypes.string.isRequired,
|
||||
error: PropTypes.object.isRequired,
|
||||
info: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
ErrorBoundaryError.defaultProps = {
|
||||
className: styles.container,
|
||||
messageClassName: styles.message,
|
||||
detailsClassName: styles.details,
|
||||
message: 'There was an error loading this content'
|
||||
};
|
||||
|
||||
export default ErrorBoundaryError;
|
||||
77
frontend/src/Components/Error/ErrorBoundaryError.tsx
Normal file
77
frontend/src/Components/Error/ErrorBoundaryError.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import StackTrace from 'stacktrace-js';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ErrorBoundaryError.css';
|
||||
|
||||
interface ErrorBoundaryErrorProps {
|
||||
className: string;
|
||||
messageClassName: string;
|
||||
detailsClassName: string;
|
||||
message: string;
|
||||
error: Error;
|
||||
info: {
|
||||
componentStack: string;
|
||||
};
|
||||
}
|
||||
|
||||
function ErrorBoundaryError(props: ErrorBoundaryErrorProps) {
|
||||
const {
|
||||
className = styles.container,
|
||||
messageClassName = styles.message,
|
||||
detailsClassName = styles.details,
|
||||
message = translate('ErrorLoadingContent'),
|
||||
error,
|
||||
info,
|
||||
} = props;
|
||||
|
||||
const [detailedError, setDetailedError] = useState<
|
||||
StackTrace.StackFrame[] | null
|
||||
>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
StackTrace.fromError(error).then((de) => {
|
||||
setDetailedError(de);
|
||||
});
|
||||
} else {
|
||||
setDetailedError(null);
|
||||
}
|
||||
}, [error, setDetailedError]);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className={messageClassName}>{message}</div>
|
||||
|
||||
<div className={styles.imageContainer}>
|
||||
<img
|
||||
className={styles.image}
|
||||
src={`${window.Readarr.urlBase}/Content/Images/error.png`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<details className={detailsClassName}>
|
||||
{error ? <div>{error.message}</div> : null}
|
||||
|
||||
{detailedError ? (
|
||||
detailedError.map((d, index) => {
|
||||
return (
|
||||
<div key={index}>
|
||||
{` at ${d.functionName} (${d.fileName}:${d.lineNumber}:${d.columnNumber})`}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div>{info.componentStack}</div>
|
||||
)}
|
||||
|
||||
{
|
||||
<div className={styles.version}>
|
||||
Version: {window.Readarr.version}
|
||||
</div>
|
||||
}
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ErrorBoundaryError;
|
||||
@@ -9,6 +9,10 @@
|
||||
&:hover {
|
||||
background-color: var(--inputHoverBackgroundColor);
|
||||
}
|
||||
|
||||
&.isDisabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.optionCheck {
|
||||
|
||||
@@ -60,6 +60,7 @@ class Notification extends Component {
|
||||
onReleaseImport,
|
||||
onUpgrade,
|
||||
onRename,
|
||||
onAuthorAdded,
|
||||
onAuthorDelete,
|
||||
onBookDelete,
|
||||
onBookFileDelete,
|
||||
@@ -73,6 +74,7 @@ class Notification extends Component {
|
||||
supportsOnReleaseImport,
|
||||
supportsOnUpgrade,
|
||||
supportsOnRename,
|
||||
supportsOnAuthorAdded,
|
||||
supportsOnAuthorDelete,
|
||||
supportsOnBookDelete,
|
||||
supportsOnBookFileDelete,
|
||||
@@ -136,6 +138,14 @@ class Notification extends Component {
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnAuthorAdded && onAuthorAdded ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnAuthorAdded')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnAuthorDelete && onAuthorDelete ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
@@ -244,6 +254,7 @@ Notification.propTypes = {
|
||||
onReleaseImport: PropTypes.bool.isRequired,
|
||||
onUpgrade: PropTypes.bool.isRequired,
|
||||
onRename: PropTypes.bool.isRequired,
|
||||
onAuthorAdded: PropTypes.bool.isRequired,
|
||||
onAuthorDelete: PropTypes.bool.isRequired,
|
||||
onBookDelete: PropTypes.bool.isRequired,
|
||||
onBookFileDelete: PropTypes.bool.isRequired,
|
||||
@@ -257,6 +268,7 @@ Notification.propTypes = {
|
||||
supportsOnReleaseImport: PropTypes.bool.isRequired,
|
||||
supportsOnUpgrade: PropTypes.bool.isRequired,
|
||||
supportsOnRename: PropTypes.bool.isRequired,
|
||||
supportsOnAuthorAdded: PropTypes.bool.isRequired,
|
||||
supportsOnAuthorDelete: PropTypes.bool.isRequired,
|
||||
supportsOnBookDelete: PropTypes.bool.isRequired,
|
||||
supportsOnBookFileDelete: PropTypes.bool.isRequired,
|
||||
|
||||
@@ -19,6 +19,7 @@ function NotificationEventItems(props) {
|
||||
onReleaseImport,
|
||||
onUpgrade,
|
||||
onRename,
|
||||
onAuthorAdded,
|
||||
onAuthorDelete,
|
||||
onBookDelete,
|
||||
onBookFileDelete,
|
||||
@@ -32,6 +33,7 @@ function NotificationEventItems(props) {
|
||||
supportsOnReleaseImport,
|
||||
supportsOnUpgrade,
|
||||
supportsOnRename,
|
||||
supportsOnAuthorAdded,
|
||||
supportsOnAuthorDelete,
|
||||
supportsOnBookDelete,
|
||||
supportsOnBookFileDelete,
|
||||
@@ -123,6 +125,17 @@ function NotificationEventItems(props) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onAuthorAdded"
|
||||
helpText={translate('OnAuthorAddedHelpText')}
|
||||
isDisabled={!supportsOnAuthorAdded.value}
|
||||
{...onAuthorAdded}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
|
||||
@@ -106,6 +106,7 @@ export default {
|
||||
selectedSchema.onReleaseImport = selectedSchema.supportsOnReleaseImport;
|
||||
selectedSchema.onUpgrade = selectedSchema.supportsOnUpgrade;
|
||||
selectedSchema.onRename = selectedSchema.supportsOnRename;
|
||||
selectedSchema.onAuthorAdded = selectedSchema.supportsOnAuthorAdded;
|
||||
selectedSchema.onAuthorDelete = selectedSchema.supportsOnAuthorDelete;
|
||||
selectedSchema.onBookDelete = selectedSchema.supportsOnBookDelete;
|
||||
selectedSchema.onBookFileDelete = selectedSchema.supportsOnBookFileDelete;
|
||||
|
||||
@@ -71,6 +71,7 @@ function getInternalLink(source) {
|
||||
function getTestLink(source, props) {
|
||||
switch (source) {
|
||||
case 'IndexerStatusCheck':
|
||||
case 'IndexerLongTermStatusCheck':
|
||||
return (
|
||||
<SpinnerIconButton
|
||||
name={icons.TEST}
|
||||
|
||||
38
package.json
38
package.json
@@ -30,7 +30,7 @@
|
||||
"@fortawesome/free-regular-svg-icons": "6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "6.4.0",
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"@microsoft/signalr": "6.0.16",
|
||||
"@microsoft/signalr": "6.0.21",
|
||||
"@sentry/browser": "7.51.2",
|
||||
"@sentry/integrations": "7.51.2",
|
||||
"@types/node": "18.16.16",
|
||||
@@ -83,30 +83,28 @@
|
||||
"redux-localstorage": "0.4.1",
|
||||
"redux-thunk": "2.3.0",
|
||||
"reselect": "4.1.8",
|
||||
"stacktrace-js": "2.0.2",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.22.9",
|
||||
"@babel/eslint-parser": "7.22.9",
|
||||
"@babel/plugin-proposal-class-properties": "7.18.6",
|
||||
"@babel/core": "7.22.11",
|
||||
"@babel/eslint-parser": "7.22.11",
|
||||
"@babel/plugin-proposal-export-default-from": "7.22.5",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.18.9",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.21.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@babel/preset-env": "7.22.9",
|
||||
"@babel/preset-env": "7.22.15",
|
||||
"@babel/preset-react": "7.22.5",
|
||||
"@babel/preset-typescript": "7.22.5",
|
||||
"@babel/preset-typescript": "7.22.11",
|
||||
"@types/lodash": "4.14.197",
|
||||
"@types/react-lazyload": "3.2.1",
|
||||
"@types/redux-actions": "2.6.2",
|
||||
"@typescript-eslint/eslint-plugin": "6.0.0",
|
||||
"@typescript-eslint/parser": "6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "6.5.0",
|
||||
"@typescript-eslint/parser": "6.5.0",
|
||||
"autoprefixer": "10.4.14",
|
||||
"babel-loader": "9.1.3",
|
||||
"babel-plugin-inline-classnames": "2.0.1",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
|
||||
"core-js": "3.31.1",
|
||||
"css-loader": "6.7.3",
|
||||
"core-js": "3.32.1",
|
||||
"css-loader": "6.8.1",
|
||||
"css-modules-typescript-loader": "4.0.1",
|
||||
"eslint": "8.44.0",
|
||||
"eslint-config-prettier": "8.8.0",
|
||||
@@ -120,10 +118,10 @@
|
||||
"file-loader": "6.2.0",
|
||||
"filemanager-webpack-plugin": "8.0.0",
|
||||
"fork-ts-checker-webpack-plugin": "8.0.0",
|
||||
"html-webpack-plugin": "5.5.1",
|
||||
"html-webpack-plugin": "5.5.3",
|
||||
"loader-utils": "^3.2.1",
|
||||
"mini-css-extract-plugin": "2.7.5",
|
||||
"postcss": "8.4.23",
|
||||
"mini-css-extract-plugin": "2.7.6",
|
||||
"postcss": "8.4.31",
|
||||
"postcss-color-function": "4.1.0",
|
||||
"postcss-loader": "7.3.0",
|
||||
"postcss-mixins": "9.0.4",
|
||||
@@ -135,14 +133,14 @@
|
||||
"rimraf": "4.4.1",
|
||||
"run-sequence": "2.2.1",
|
||||
"streamqueue": "1.1.2",
|
||||
"style-loader": "3.3.2",
|
||||
"stylelint": "15.10.1",
|
||||
"style-loader": "3.3.3",
|
||||
"stylelint": "15.10.3",
|
||||
"stylelint-order": "6.0.3",
|
||||
"terser-webpack-plugin": "5.3.9",
|
||||
"ts-loader": "9.4.3",
|
||||
"ts-loader": "9.4.4",
|
||||
"typescript-plugin-css-modules": "5.0.1",
|
||||
"url-loader": "4.1.1",
|
||||
"webpack": "5.88.1",
|
||||
"webpack": "5.88.2",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-livereload-plugin": "3.0.2",
|
||||
"worker-loader": "3.0.8"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<PackageVersion Include="AutoFixture" Version="4.17.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="3.0.4-preview.27.ge7cb7c3b40" PrivateAssets="all" />
|
||||
<PackageVersion Include="Dapper" Version="2.0.123" />
|
||||
<PackageVersion Include="DryIoc.dll" Version="5.4.0" />
|
||||
<PackageVersion Include="DryIoc.dll" Version="5.4.1" />
|
||||
<PackageVersion Include="DryIoc.Microsoft.DependencyInjection" Version="6.2.0" />
|
||||
<PackageVersion Include="Equ" Version="2.3.0" />
|
||||
<PackageVersion Include="FluentAssertions" Version="5.10.3" />
|
||||
@@ -16,7 +16,7 @@
|
||||
<PackageVersion Include="ImpromptuInterface" Version="7.0.1" />
|
||||
<PackageVersion Include="LazyCache" Version="2.4.0" />
|
||||
<PackageVersion Include="Mailkit" Version="3.6.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.16" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.21" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||
@@ -43,7 +43,7 @@
|
||||
<PackageVersion Include="Selenium.WebDriver.ChromeDriver" Version="91.0.4472.10100" />
|
||||
<PackageVersion Include="Sentry" Version="3.31.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.0.1" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.0.2" />
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
|
||||
<PackageVersion Include="System.Buffers" Version="4.5.1" />
|
||||
@@ -57,10 +57,10 @@
|
||||
<PackageVersion Include="System.Resources.Extensions" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="6.0.0" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Text.Json" Version="6.0.7" />
|
||||
<PackageVersion Include="System.Text.Json" Version="6.0.8" />
|
||||
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
|
||||
<PackageVersion Include="TagLibSharp-Lidarr" Version="2.2.0.19" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NLog;
|
||||
@@ -114,21 +115,21 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_simple_get()
|
||||
public async Task should_execute_simple_get()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Execute(request);
|
||||
var response = await Subject.ExecuteAsync(request);
|
||||
|
||||
response.Content.Should().NotBeNullOrWhiteSpace();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_https_get()
|
||||
public async Task should_execute_https_get()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Execute(request);
|
||||
var response = await Subject.ExecuteAsync(request);
|
||||
|
||||
response.Content.Should().NotBeNullOrWhiteSpace();
|
||||
}
|
||||
@@ -140,47 +141,47 @@ namespace NzbDrone.Common.Test.Http
|
||||
Mocker.GetMock<IConfigService>().SetupGet(x => x.CertificateValidation).Returns(validationType);
|
||||
var request = new HttpRequest($"https://expired.badssl.com");
|
||||
|
||||
Assert.Throws<HttpRequestException>(() => Subject.Execute(request));
|
||||
Assert.ThrowsAsync<HttpRequestException>(async () => await Subject.ExecuteAsync(request));
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void bad_ssl_should_pass_if_remote_validation_disabled()
|
||||
public async Task bad_ssl_should_pass_if_remote_validation_disabled()
|
||||
{
|
||||
Mocker.GetMock<IConfigService>().SetupGet(x => x.CertificateValidation).Returns(CertificateValidationType.Disabled);
|
||||
|
||||
var request = new HttpRequest($"https://expired.badssl.com");
|
||||
|
||||
Subject.Execute(request);
|
||||
await Subject.ExecuteAsync(request);
|
||||
ExceptionVerification.ExpectedErrors(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_typed_get()
|
||||
public async Task should_execute_typed_get()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get?test=1");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Url.EndsWith("/get?test=1");
|
||||
response.Resource.Args.Should().Contain("test", "1");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_simple_post()
|
||||
public async Task should_execute_simple_post()
|
||||
{
|
||||
var message = "{ my: 1 }";
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/post");
|
||||
request.SetContent(message);
|
||||
|
||||
var response = Subject.Post<HttpBinResource>(request);
|
||||
var response = await Subject.PostAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Data.Should().Be(message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_post_with_content_type()
|
||||
public async Task should_execute_post_with_content_type()
|
||||
{
|
||||
var message = "{ my: 1 }";
|
||||
|
||||
@@ -188,17 +189,16 @@ namespace NzbDrone.Common.Test.Http
|
||||
request.SetContent(message);
|
||||
request.Headers.ContentType = "application/json";
|
||||
|
||||
var response = Subject.Post<HttpBinResource>(request);
|
||||
var response = await Subject.PostAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Data.Should().Be(message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_get_using_gzip()
|
||||
public async Task should_execute_get_using_gzip()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/gzip");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers["Accept-Encoding"].ToString().Should().Contain("gzip");
|
||||
|
||||
@@ -208,11 +208,10 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
[Test]
|
||||
[Platform(Exclude = "MacOsX", Reason = "Azure agent update prevents brotli on OSX")]
|
||||
public void should_execute_get_using_brotli()
|
||||
public async Task should_execute_get_using_brotli()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/brotli");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers["Accept-Encoding"].ToString().Should().Contain("br");
|
||||
|
||||
@@ -230,7 +229,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/status/{statusCode}");
|
||||
|
||||
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
|
||||
var exception = Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
|
||||
|
||||
((int)exception.Response.StatusCode).Should().Be(statusCode);
|
||||
|
||||
@@ -243,7 +242,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}");
|
||||
request.SuppressHttpErrorStatusCodes = new[] { HttpStatusCode.NotFound };
|
||||
|
||||
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
|
||||
Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
|
||||
|
||||
ExceptionVerification.IgnoreWarns();
|
||||
}
|
||||
@@ -253,7 +252,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}");
|
||||
|
||||
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
|
||||
var exception = Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
@@ -264,28 +263,28 @@ namespace NzbDrone.Common.Test.Http
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}");
|
||||
request.LogHttpError = false;
|
||||
|
||||
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
|
||||
Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
|
||||
|
||||
ExceptionVerification.ExpectedWarns(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_follow_redirects_when_not_in_production()
|
||||
public async Task should_not_follow_redirects_when_not_in_production()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/redirect/1");
|
||||
|
||||
Subject.Get(request);
|
||||
await Subject.GetAsync(request);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_follow_redirects()
|
||||
public async Task should_follow_redirects()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/redirect/1");
|
||||
request.AllowAutoRedirect = true;
|
||||
|
||||
var response = Subject.Get(request);
|
||||
var response = await Subject.GetAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
@@ -293,12 +292,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_follow_redirects()
|
||||
public async Task should_not_follow_redirects()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/redirect/1");
|
||||
request.AllowAutoRedirect = false;
|
||||
|
||||
var response = Subject.Get(request);
|
||||
var response = await Subject.GetAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Found);
|
||||
|
||||
@@ -306,14 +305,14 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_follow_redirects_to_https()
|
||||
public async Task should_follow_redirects_to_https()
|
||||
{
|
||||
var request = new HttpRequestBuilder($"https://{_httpBinHost}/redirect-to")
|
||||
.AddQueryParam("url", $"https://readarr.com/")
|
||||
.Build();
|
||||
request.AllowAutoRedirect = true;
|
||||
|
||||
var response = Subject.Get(request);
|
||||
var response = await Subject.GetAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
response.Content.Should().Contain("Readarr");
|
||||
@@ -327,17 +326,17 @@ namespace NzbDrone.Common.Test.Http
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/redirect/6");
|
||||
request.AllowAutoRedirect = true;
|
||||
|
||||
Assert.Throws<WebException>(() => Subject.Get(request));
|
||||
Assert.ThrowsAsync<WebException>(async () => await Subject.GetAsync(request));
|
||||
|
||||
ExceptionVerification.ExpectedErrors(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_send_user_agent()
|
||||
public async Task should_send_user_agent()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("User-Agent");
|
||||
|
||||
@@ -347,24 +346,24 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[TestCase("Accept", "text/xml, text/rss+xml, application/rss+xml")]
|
||||
public void should_send_headers(string header, string value)
|
||||
public async Task should_send_headers(string header, string value)
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
request.Headers.Add(header, value);
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers[header].ToString().Should().Be(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_download_file()
|
||||
public async Task should_download_file()
|
||||
{
|
||||
var file = GetTempFilePath();
|
||||
|
||||
var url = "https://readarr.com/img/slider/artistdetails.png";
|
||||
|
||||
Subject.DownloadFile(url, file);
|
||||
await Subject.DownloadFileAsync(url, file);
|
||||
|
||||
var fileInfo = new FileInfo(file);
|
||||
fileInfo.Exists.Should().BeTrue();
|
||||
@@ -372,7 +371,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_download_file_with_redirect()
|
||||
public async Task should_download_file_with_redirect()
|
||||
{
|
||||
var file = GetTempFilePath();
|
||||
|
||||
@@ -380,7 +379,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
.AddQueryParam("url", $"https://readarr.com/img/slider/artistdetails.png")
|
||||
.Build();
|
||||
|
||||
Subject.DownloadFile(request.Url.FullUri, file);
|
||||
await Subject.DownloadFileAsync(request.Url.FullUri, file);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(0);
|
||||
|
||||
@@ -394,7 +393,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
var file = GetTempFilePath();
|
||||
|
||||
Assert.Throws<HttpException>(() => Subject.DownloadFile("https://download.sonarr.tv/wrongpath", file));
|
||||
Assert.ThrowsAsync<HttpException>(async () => await Subject.DownloadFileAsync("https://download.sonarr.tv/wrongpath", file));
|
||||
|
||||
File.Exists(file).Should().BeFalse();
|
||||
|
||||
@@ -402,7 +401,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_write_redirect_content_to_stream()
|
||||
public async Task should_not_write_redirect_content_to_stream()
|
||||
{
|
||||
var file = GetTempFilePath();
|
||||
|
||||
@@ -412,7 +411,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
request.AllowAutoRedirect = false;
|
||||
request.ResponseStream = fileStream;
|
||||
|
||||
var response = Subject.Get(request);
|
||||
var response = await Subject.GetAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Moved);
|
||||
}
|
||||
@@ -427,12 +426,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_send_cookie()
|
||||
public async Task should_send_cookie()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
request.Cookies["my"] = "cookie";
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("Cookie");
|
||||
|
||||
@@ -461,13 +460,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_preserve_cookie_during_session()
|
||||
public async Task should_preserve_cookie_during_session()
|
||||
{
|
||||
GivenOldCookie();
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost2}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("Cookie");
|
||||
|
||||
@@ -477,30 +476,30 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_send_cookie_to_other_host()
|
||||
public async Task should_not_send_cookie_to_other_host()
|
||||
{
|
||||
GivenOldCookie();
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().NotContainKey("Cookie");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_store_request_cookie()
|
||||
public async Task should_not_store_request_cookie()
|
||||
{
|
||||
var requestGet = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
requestGet.Cookies.Add("my", "cookie");
|
||||
requestGet.AllowAutoRedirect = false;
|
||||
requestGet.StoreRequestCookie = false;
|
||||
requestGet.StoreResponseCookie = false;
|
||||
var responseGet = Subject.Get<HttpBinResource>(requestGet);
|
||||
var responseGet = await Subject.GetAsync<HttpBinResource>(requestGet);
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
@@ -508,18 +507,18 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_store_request_cookie()
|
||||
public async Task should_store_request_cookie()
|
||||
{
|
||||
var requestGet = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
requestGet.Cookies.Add("my", "cookie");
|
||||
requestGet.AllowAutoRedirect = false;
|
||||
requestGet.StoreRequestCookie.Should().BeTrue();
|
||||
requestGet.StoreResponseCookie = false;
|
||||
var responseGet = Subject.Get<HttpBinResource>(requestGet);
|
||||
var responseGet = await Subject.GetAsync<HttpBinResource>(requestGet);
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -527,7 +526,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_request_cookie()
|
||||
public async Task should_delete_request_cookie()
|
||||
{
|
||||
var requestDelete = new HttpRequest($"https://{_httpBinHost}/cookies/delete?my");
|
||||
requestDelete.Cookies.Add("my", "cookie");
|
||||
@@ -536,13 +535,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestDelete.StoreResponseCookie = false;
|
||||
|
||||
// Delete and redirect since that's the only way to check the internal temporary cookie container
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestDelete);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestDelete);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_clear_request_cookie()
|
||||
public async Task should_clear_request_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestSet.Cookies.Add("my", "cookie");
|
||||
@@ -550,7 +549,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestSet.StoreRequestCookie = true;
|
||||
requestSet.StoreResponseCookie = false;
|
||||
|
||||
var responseSet = Subject.Get<HttpCookieResource>(requestSet);
|
||||
var responseSet = await Subject.GetAsync<HttpCookieResource>(requestSet);
|
||||
|
||||
var requestClear = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestClear.Cookies.Add("my", null);
|
||||
@@ -558,24 +557,24 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestClear.StoreRequestCookie = true;
|
||||
requestClear.StoreResponseCookie = false;
|
||||
|
||||
var responseClear = Subject.Get<HttpCookieResource>(requestClear);
|
||||
var responseClear = await Subject.GetAsync<HttpCookieResource>(requestClear);
|
||||
|
||||
responseClear.Resource.Cookies.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_store_response_cookie()
|
||||
public async Task should_not_store_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie.Should().BeFalse();
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = await Subject.GetAsync(requestSet);
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
@@ -583,18 +582,18 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_store_response_cookie()
|
||||
public async Task should_store_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = await Subject.GetAsync(requestSet);
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -602,13 +601,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_temp_store_response_cookie()
|
||||
public async Task should_temp_store_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.AllowAutoRedirect = true;
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie.Should().BeFalse();
|
||||
var responseSet = Subject.Get<HttpCookieResource>(requestSet);
|
||||
var responseSet = await Subject.GetAsync<HttpCookieResource>(requestSet);
|
||||
|
||||
// Set and redirect since that's the only way to check the internal temporary cookie container
|
||||
responseSet.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
@@ -617,7 +616,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_overwrite_response_cookie()
|
||||
public async Task should_overwrite_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.Cookies.Add("my", "oldcookie");
|
||||
@@ -625,11 +624,11 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = await Subject.GetAsync(requestSet);
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -637,7 +636,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_overwrite_temp_response_cookie()
|
||||
public async Task should_overwrite_temp_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.Cookies.Add("my", "oldcookie");
|
||||
@@ -645,13 +644,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestSet.StoreRequestCookie = true;
|
||||
requestSet.StoreResponseCookie = false;
|
||||
|
||||
var responseSet = Subject.Get<HttpCookieResource>(requestSet);
|
||||
var responseSet = await Subject.GetAsync<HttpCookieResource>(requestSet);
|
||||
|
||||
responseSet.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "oldcookie");
|
||||
|
||||
@@ -659,14 +658,14 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_response_cookie()
|
||||
public async Task should_not_delete_response_cookie()
|
||||
{
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.Cookies.Add("my", "cookie");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
requestCookies.StoreRequestCookie = true;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -675,13 +674,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestDelete.StoreRequestCookie = false;
|
||||
requestDelete.StoreResponseCookie = false;
|
||||
|
||||
var responseDelete = Subject.Get(requestDelete);
|
||||
var responseDelete = await Subject.GetAsync(requestDelete);
|
||||
|
||||
requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.StoreRequestCookie = false;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
|
||||
responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -689,14 +688,14 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_response_cookie()
|
||||
public async Task should_delete_response_cookie()
|
||||
{
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.Cookies.Add("my", "cookie");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
requestCookies.StoreRequestCookie = true;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -705,13 +704,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestDelete.StoreRequestCookie = false;
|
||||
requestDelete.StoreResponseCookie = true;
|
||||
|
||||
var responseDelete = Subject.Get(requestDelete);
|
||||
var responseDelete = await Subject.GetAsync(requestDelete);
|
||||
|
||||
requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.StoreRequestCookie = false;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
|
||||
responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
@@ -719,14 +718,14 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_temp_response_cookie()
|
||||
public async Task should_delete_temp_response_cookie()
|
||||
{
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.Cookies.Add("my", "cookie");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
requestCookies.StoreRequestCookie = true;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -734,7 +733,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestDelete.AllowAutoRedirect = true;
|
||||
requestDelete.StoreRequestCookie = false;
|
||||
requestDelete.StoreResponseCookie = false;
|
||||
var responseDelete = Subject.Get<HttpCookieResource>(requestDelete);
|
||||
var responseDelete = await Subject.GetAsync<HttpCookieResource>(requestDelete);
|
||||
|
||||
responseDelete.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
@@ -752,13 +751,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/status/429");
|
||||
|
||||
Assert.Throws<TooManyRequestsException>(() => Subject.Get(request));
|
||||
Assert.ThrowsAsync<TooManyRequestsException>(async () => await Subject.GetAsync(request));
|
||||
|
||||
ExceptionVerification.IgnoreWarns();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_call_interceptor()
|
||||
public async Task should_call_interceptor()
|
||||
{
|
||||
Mocker.SetConstant<IEnumerable<IHttpRequestInterceptor>>(new[] { Mocker.GetMock<IHttpRequestInterceptor>().Object });
|
||||
|
||||
@@ -772,7 +771,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
Subject.Get(request);
|
||||
await Subject.GetAsync(request);
|
||||
|
||||
Mocker.GetMock<IHttpRequestInterceptor>()
|
||||
.Verify(v => v.PreRequest(It.IsAny<HttpRequest>()), Times.Once());
|
||||
@@ -783,7 +782,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
[TestCase("en-US")]
|
||||
[TestCase("es-ES")]
|
||||
public void should_parse_malformed_cloudflare_cookie(string culture)
|
||||
public async Task should_parse_malformed_cloudflare_cookie(string culture)
|
||||
{
|
||||
var origCulture = Thread.CurrentThread.CurrentCulture;
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
|
||||
@@ -799,11 +798,11 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = await Subject.GetAsync(requestSet);
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("Cookie");
|
||||
|
||||
@@ -821,7 +820,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[TestCase("lang_code=en; expires=Wed, 23-Dec-2026 18:09:14 GMT; Max-Age=31536000; path=/; domain=.abc.com")]
|
||||
public void should_reject_malformed_domain_cookie(string malformedCookie)
|
||||
public async Task should_reject_malformed_domain_cookie(string malformedCookie)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -831,11 +830,11 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = await Subject.GetAsync(requestSet);
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().NotContainKey("Cookie");
|
||||
|
||||
@@ -847,12 +846,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_correctly_use_basic_auth_with_basic_network_credential()
|
||||
public async Task should_correctly_use_basic_auth()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/basic-auth/username/password");
|
||||
request.Credentials = new BasicNetworkCredential("username", "password");
|
||||
|
||||
var response = Subject.Execute(request);
|
||||
var response = await Subject.ExecuteAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
@@ -70,15 +70,15 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
[TestCase(@"[Info] MigrationController: *** Migrating Database=readarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
|
||||
|
||||
// Announce URLs (passkeys) Magnet & Tracker
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2ftracker.php%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce%2f9pr04sg601233210imaveql2tyu8xyui""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce.php%3fpasskey%3d9pr04sg601233210imaveql2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/9pr04sg601233210imaveql2tyu8xyui/announce""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/tracker.php/9pr04sg601233210imaveql2tyu8xyui/announce""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/announce/9pr04sg601233210imaveql2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""http://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui"",""info"":""http://xxx.yyy/info?a=b""")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210IMAveQL2tyu8xyui%2fannounce""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2ftracker.php%2f9pr04sg601233210IMAveQL2tyu8xyui%2fannounce""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce%2f9pr04sg601233210IMAveQL2tyu8xyui""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce.php%3fpasskey%3d9pr04sg601233210IMAveQL2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/9pr04sg601233210IMAveQL2tyu8xyui/announce""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/tracker.php/9pr04sg601233210IMAveQL2tyu8xyui/announce""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/announce/9pr04sg601233210IMAveQL2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/announce.php?passkey=9pr04sg601233210IMAveQL2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""http://xxx.yyy/announce.php?passkey=9pr04sg601233210IMAveQL2tyu8xyui"",""info"":""http://xxx.yyy/info?a=b""")]
|
||||
|
||||
// Notifiarr
|
||||
[TestCase(@"https://xxx.yyy/api/v1/notification/readarr/9pr04sg6-0123-3210-imav-eql2tyu8xyui")]
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
if (text.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new ArgumentNullException("text");
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
}
|
||||
|
||||
return text.IndexOfAny(Path.GetInvalidPathChars()) >= 0;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
public interface IHttpDispatcher
|
||||
{
|
||||
HttpResponse GetResponse(HttpRequest request, CookieContainer cookies);
|
||||
Task<HttpResponse> GetResponseAsync(HttpRequest request, CookieContainer cookies);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,13 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
_credentialCache = cacheManager.GetCache<CredentialCache>(typeof(ManagedHttpDispatcher), "credentialcache");
|
||||
}
|
||||
|
||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||
public async Task<HttpResponse> GetResponseAsync(HttpRequest request, CookieContainer cookies)
|
||||
{
|
||||
var requestMessage = new HttpRequestMessage(request.Method, (Uri)request.Url);
|
||||
var requestMessage = new HttpRequestMessage(request.Method, (Uri)request.Url)
|
||||
{
|
||||
Version = HttpVersion.Version20,
|
||||
VersionPolicy = HttpVersionPolicy.RequestVersionOrLower
|
||||
};
|
||||
requestMessage.Headers.UserAgent.ParseAdd(_userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent));
|
||||
requestMessage.Headers.ConnectionClose = !request.ConnectionKeepAlive;
|
||||
|
||||
@@ -99,7 +103,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
var httpClient = GetClient(request.Url);
|
||||
|
||||
using var responseMessage = httpClient.Send(requestMessage, HttpCompletionOption.ResponseHeadersRead, cts.Token);
|
||||
using var responseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cts.Token);
|
||||
{
|
||||
byte[] data = null;
|
||||
|
||||
@@ -107,11 +111,11 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
if (request.ResponseStream != null && responseMessage.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
responseMessage.Content.CopyTo(request.ResponseStream, null, cts.Token);
|
||||
await responseMessage.Content.CopyToAsync(request.ResponseStream, null, cts.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = responseMessage.Content.ReadAsByteArrayAsync(cts.Token).GetAwaiter().GetResult();
|
||||
data = await responseMessage.Content.ReadAsByteArrayAsync(cts.Token);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -123,7 +127,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
headers.Add(responseMessage.Content.Headers.ToNameValueCollection());
|
||||
|
||||
return new HttpResponse(request, new HttpHeader(headers), data, responseMessage.StatusCode);
|
||||
return new HttpResponse(request, new HttpHeader(headers), data, responseMessage.StatusCode, responseMessage.Version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +164,8 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
var client = new System.Net.Http.HttpClient(handler)
|
||||
{
|
||||
DefaultRequestVersion = HttpVersion.Version20,
|
||||
DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower,
|
||||
Timeout = Timeout.InfiniteTimeSpan
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
@@ -25,6 +26,16 @@ namespace NzbDrone.Common.Http
|
||||
HttpResponse Post(HttpRequest request);
|
||||
HttpResponse<T> Post<T>(HttpRequest request)
|
||||
where T : new();
|
||||
|
||||
Task<HttpResponse> ExecuteAsync(HttpRequest request);
|
||||
Task DownloadFileAsync(string url, string fileName, string userAgent = null);
|
||||
Task<HttpResponse> GetAsync(HttpRequest request);
|
||||
Task<HttpResponse<T>> GetAsync<T>(HttpRequest request)
|
||||
where T : new();
|
||||
Task<HttpResponse> HeadAsync(HttpRequest request);
|
||||
Task<HttpResponse> PostAsync(HttpRequest request);
|
||||
Task<HttpResponse<T>> PostAsync<T>(HttpRequest request)
|
||||
where T : new();
|
||||
}
|
||||
|
||||
public class HttpClient : IHttpClient
|
||||
@@ -52,11 +63,11 @@ namespace NzbDrone.Common.Http
|
||||
_cookieContainerCache = cacheManager.GetCache<CookieContainer>(typeof(HttpClient));
|
||||
}
|
||||
|
||||
public HttpResponse Execute(HttpRequest request)
|
||||
public virtual async Task<HttpResponse> ExecuteAsync(HttpRequest request)
|
||||
{
|
||||
var cookieContainer = InitializeRequestCookies(request);
|
||||
|
||||
var response = ExecuteRequest(request, cookieContainer);
|
||||
var response = await ExecuteRequestAsync(request, cookieContainer);
|
||||
|
||||
if (request.AllowAutoRedirect && response.HasHttpRedirect)
|
||||
{
|
||||
@@ -82,7 +93,7 @@ namespace NzbDrone.Common.Http
|
||||
request.ContentSummary = null;
|
||||
}
|
||||
|
||||
response = ExecuteRequest(request, cookieContainer);
|
||||
response = await ExecuteRequestAsync(request, cookieContainer);
|
||||
}
|
||||
while (response.HasHttpRedirect);
|
||||
}
|
||||
@@ -112,6 +123,11 @@ namespace NzbDrone.Common.Http
|
||||
return response;
|
||||
}
|
||||
|
||||
public HttpResponse Execute(HttpRequest request)
|
||||
{
|
||||
return ExecuteAsync(request).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private static bool RequestRequiresForceGet(HttpStatusCode statusCode, HttpMethod requestMethod)
|
||||
{
|
||||
return statusCode switch
|
||||
@@ -122,7 +138,7 @@ namespace NzbDrone.Common.Http
|
||||
};
|
||||
}
|
||||
|
||||
private HttpResponse ExecuteRequest(HttpRequest request, CookieContainer cookieContainer)
|
||||
private async Task<HttpResponse> ExecuteRequestAsync(HttpRequest request, CookieContainer cookieContainer)
|
||||
{
|
||||
foreach (var interceptor in _requestInterceptors)
|
||||
{
|
||||
@@ -131,14 +147,14 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
if (request.RateLimit != TimeSpan.Zero)
|
||||
{
|
||||
_rateLimitService.WaitAndPulse(request.Url.Host, request.RateLimitKey, request.RateLimit);
|
||||
await _rateLimitService.WaitAndPulseAsync(request.Url.Host, request.RateLimitKey, request.RateLimit);
|
||||
}
|
||||
|
||||
_logger.Trace(request);
|
||||
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
|
||||
var response = _httpDispatcher.GetResponse(request, cookieContainer);
|
||||
var response = await _httpDispatcher.GetResponseAsync(request, cookieContainer);
|
||||
|
||||
HandleResponseCookies(response, cookieContainer);
|
||||
|
||||
@@ -246,7 +262,7 @@ namespace NzbDrone.Common.Http
|
||||
}
|
||||
}
|
||||
|
||||
public void DownloadFile(string url, string fileName, string userAgent = null)
|
||||
public async Task DownloadFileAsync(string url, string fileName, string userAgent = null)
|
||||
{
|
||||
var fileNamePart = fileName + ".part";
|
||||
|
||||
@@ -261,12 +277,13 @@ namespace NzbDrone.Common.Http
|
||||
_logger.Debug("Downloading [{0}] to [{1}]", url, fileName);
|
||||
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
using (var fileStream = new FileStream(fileNamePart, FileMode.Create, FileAccess.ReadWrite))
|
||||
await using (var fileStream = new FileStream(fileNamePart, FileMode.Create, FileAccess.ReadWrite))
|
||||
{
|
||||
var request = new HttpRequest(url);
|
||||
request.AllowAutoRedirect = true;
|
||||
request.ResponseStream = fileStream;
|
||||
var response = Get(request);
|
||||
request.RequestTimeout = TimeSpan.FromSeconds(300);
|
||||
var response = await GetAsync(request);
|
||||
|
||||
if (response.Headers.ContentType != null && response.Headers.ContentType.Contains("text/html"))
|
||||
{
|
||||
@@ -293,38 +310,71 @@ namespace NzbDrone.Common.Http
|
||||
}
|
||||
}
|
||||
|
||||
public HttpResponse Get(HttpRequest request)
|
||||
public void DownloadFile(string url, string fileName, string userAgent = null)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development#the-thread-pool-hack
|
||||
Task.Run(() => DownloadFileAsync(url, fileName, userAgent)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public Task<HttpResponse> GetAsync(HttpRequest request)
|
||||
{
|
||||
request.Method = HttpMethod.Get;
|
||||
return Execute(request);
|
||||
return ExecuteAsync(request);
|
||||
}
|
||||
|
||||
public HttpResponse Get(HttpRequest request)
|
||||
{
|
||||
return Task.Run(() => GetAsync(request)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<HttpResponse<T>> GetAsync<T>(HttpRequest request)
|
||||
where T : new()
|
||||
{
|
||||
var response = await GetAsync(request);
|
||||
CheckResponseContentType(response);
|
||||
return new HttpResponse<T>(response);
|
||||
}
|
||||
|
||||
public HttpResponse<T> Get<T>(HttpRequest request)
|
||||
where T : new()
|
||||
{
|
||||
var response = Get(request);
|
||||
CheckResponseContentType(response);
|
||||
return new HttpResponse<T>(response);
|
||||
return Task.Run(() => GetAsync<T>(request)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public Task<HttpResponse> HeadAsync(HttpRequest request)
|
||||
{
|
||||
request.Method = HttpMethod.Head;
|
||||
return ExecuteAsync(request);
|
||||
}
|
||||
|
||||
public HttpResponse Head(HttpRequest request)
|
||||
{
|
||||
request.Method = HttpMethod.Head;
|
||||
return Execute(request);
|
||||
return Task.Run(() => HeadAsync(request)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public Task<HttpResponse> PostAsync(HttpRequest request)
|
||||
{
|
||||
request.Method = HttpMethod.Post;
|
||||
return ExecuteAsync(request);
|
||||
}
|
||||
|
||||
public HttpResponse Post(HttpRequest request)
|
||||
{
|
||||
request.Method = HttpMethod.Post;
|
||||
return Execute(request);
|
||||
return Task.Run(() => PostAsync(request)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<HttpResponse<T>> PostAsync<T>(HttpRequest request)
|
||||
where T : new()
|
||||
{
|
||||
var response = await PostAsync(request);
|
||||
CheckResponseContentType(response);
|
||||
return new HttpResponse<T>(response);
|
||||
}
|
||||
|
||||
public HttpResponse<T> Post<T>(HttpRequest request)
|
||||
where T : new()
|
||||
{
|
||||
var response = Post(request);
|
||||
CheckResponseContentType(response);
|
||||
return new HttpResponse<T>(response);
|
||||
return Task.Run(() => PostAsync<T>(request)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void CheckResponseContentType(HttpResponse response)
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace NzbDrone.Common.Http
|
||||
Method = HttpMethod.Get;
|
||||
Url = new HttpUri(url);
|
||||
Headers = new HttpHeader();
|
||||
ConnectionKeepAlive = true;
|
||||
AllowAutoRedirect = true;
|
||||
StoreRequestCookie = true;
|
||||
LogHttpError = true;
|
||||
|
||||
@@ -9,28 +9,31 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class HttpResponse
|
||||
{
|
||||
private static readonly Regex RegexSetCookie = new Regex("^(.*?)=(.*?)(?:;|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex RegexSetCookie = new ("^(.*?)=(.*?)(?:;|$)", RegexOptions.Compiled);
|
||||
|
||||
public HttpResponse(HttpRequest request, HttpHeader headers, byte[] binaryData, HttpStatusCode statusCode = HttpStatusCode.OK)
|
||||
public HttpResponse(HttpRequest request, HttpHeader headers, byte[] binaryData, HttpStatusCode statusCode = HttpStatusCode.OK, Version version = null)
|
||||
{
|
||||
Request = request;
|
||||
Headers = headers;
|
||||
ResponseData = binaryData;
|
||||
StatusCode = statusCode;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public HttpResponse(HttpRequest request, HttpHeader headers, string content, HttpStatusCode statusCode = HttpStatusCode.OK)
|
||||
public HttpResponse(HttpRequest request, HttpHeader headers, string content, HttpStatusCode statusCode = HttpStatusCode.OK, Version version = null)
|
||||
{
|
||||
Request = request;
|
||||
Headers = headers;
|
||||
ResponseData = Headers.GetEncodingFromContentType().GetBytes(content);
|
||||
_content = content;
|
||||
StatusCode = statusCode;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public HttpRequest Request { get; private set; }
|
||||
public HttpHeader Headers { get; private set; }
|
||||
public HttpStatusCode StatusCode { get; private set; }
|
||||
public Version Version { get; private set; }
|
||||
public byte[] ResponseData { get; private set; }
|
||||
|
||||
private string _content;
|
||||
@@ -84,7 +87,7 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var result = string.Format("Res: [{0}] {1}: {2}.{3} ({4} bytes)", Request.Method, Request.Url, (int)StatusCode, StatusCode, ResponseData?.Length ?? 0);
|
||||
var result = $"Res: HTTP/{Version} [{Request.Method}] {Request.Url}: {(int)StatusCode}.{StatusCode} ({ResponseData?.Length ?? 0} bytes)";
|
||||
|
||||
if (HasHttpError && Headers.ContentType.IsNotNullOrWhiteSpace() && !Headers.ContentType.Equals("text/html", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
@@ -99,7 +102,7 @@ namespace NzbDrone.Common.Http
|
||||
where T : new()
|
||||
{
|
||||
public HttpResponse(HttpResponse response)
|
||||
: base(response.Request, response.Headers, response.ResponseData, response.StatusCode)
|
||||
: base(response.Request, response.Headers, response.ResponseData, response.StatusCode, response.Version)
|
||||
{
|
||||
Resource = Json.Deserialize<T>(response.Content);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
new (@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
|
||||
new (@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"),
|
||||
new (@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Path
|
||||
new (@"C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -10,6 +11,8 @@ namespace NzbDrone.Common.TPL
|
||||
{
|
||||
void WaitAndPulse(string key, TimeSpan interval);
|
||||
void WaitAndPulse(string key, string subKey, TimeSpan interval);
|
||||
Task WaitAndPulseAsync(string key, TimeSpan interval);
|
||||
Task WaitAndPulseAsync(string key, string subKey, TimeSpan interval);
|
||||
}
|
||||
|
||||
public class RateLimitService : IRateLimitService
|
||||
@@ -28,7 +31,34 @@ namespace NzbDrone.Common.TPL
|
||||
WaitAndPulse(key, null, interval);
|
||||
}
|
||||
|
||||
public async Task WaitAndPulseAsync(string key, TimeSpan interval)
|
||||
{
|
||||
await WaitAndPulseAsync(key, null, interval);
|
||||
}
|
||||
|
||||
public void WaitAndPulse(string key, string subKey, TimeSpan interval)
|
||||
{
|
||||
var delay = GetDelay(key, subKey, interval);
|
||||
|
||||
if (delay.TotalSeconds > 0.0)
|
||||
{
|
||||
_logger.Trace("Rate Limit triggered, delaying '{0}' for {1:0.000} sec", key, delay.TotalSeconds);
|
||||
System.Threading.Thread.Sleep(delay);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WaitAndPulseAsync(string key, string subKey, TimeSpan interval)
|
||||
{
|
||||
var delay = GetDelay(key, subKey, interval);
|
||||
|
||||
if (delay.TotalSeconds > 0.0)
|
||||
{
|
||||
_logger.Trace("Rate Limit triggered, delaying '{0}' for {1:0.000} sec", key, delay.TotalSeconds);
|
||||
await Task.Delay(delay);
|
||||
}
|
||||
}
|
||||
|
||||
private TimeSpan GetDelay(string key, string subKey, TimeSpan interval)
|
||||
{
|
||||
var waitUntil = DateTime.UtcNow.Add(interval);
|
||||
|
||||
@@ -59,13 +89,7 @@ namespace NzbDrone.Common.TPL
|
||||
|
||||
waitUntil -= interval;
|
||||
|
||||
var delay = waitUntil - DateTime.UtcNow;
|
||||
|
||||
if (delay.TotalSeconds > 0.0)
|
||||
{
|
||||
_logger.Trace("Rate Limit triggered, delaying '{0}' for {1:0.000} sec", key, delay.TotalSeconds);
|
||||
System.Threading.Thread.Sleep(delay);
|
||||
}
|
||||
return waitUntil - DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
@@ -33,8 +34,13 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
||||
};
|
||||
|
||||
Mocker
|
||||
.GetMock<IIndexerRepository>()
|
||||
.GetMock<IIndexerFactory>()
|
||||
.Setup(m => m.Get(It.IsAny<int>()))
|
||||
.Throws(new ModelNotFoundException(typeof(IndexerDefinition), -1));
|
||||
|
||||
Mocker
|
||||
.GetMock<IIndexerFactory>()
|
||||
.Setup(m => m.Get(1))
|
||||
.Returns(_fakeIndexerDefinition);
|
||||
|
||||
_specification = Mocker.Resolve<IndexerTagSpecification>();
|
||||
@@ -106,5 +112,25 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
||||
|
||||
_specification.IsSatisfiedBy(_parseResultMulti, new BookSearchCriteria { MonitoredBooksOnly = true }).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void release_without_indexerid_should_return_true()
|
||||
{
|
||||
_fakeIndexerDefinition.Tags = new HashSet<int> { 456 };
|
||||
_fakeAuthor.Tags = new HashSet<int> { 123, 789 };
|
||||
_fakeRelease.IndexerId = 0;
|
||||
|
||||
_specification.IsSatisfiedBy(_parseResultMulti, new BookSearchCriteria { MonitoredBooksOnly = true }).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void release_with_invalid_indexerid_should_return_true()
|
||||
{
|
||||
_fakeIndexerDefinition.Tags = new HashSet<int> { 456 };
|
||||
_fakeAuthor.Tags = new HashSet<int> { 123, 789 };
|
||||
_fakeRelease.IndexerId = 2;
|
||||
|
||||
_specification.IsSatisfiedBy(_parseResultMulti, new BookSearchCriteria { MonitoredBooksOnly = true }).Accepted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -58,7 +59,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_download_report_if_book_was_not_already_downloaded()
|
||||
public async Task should_download_report_if_book_was_not_already_downloaded()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -66,12 +67,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteBook>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_only_download_book_once()
|
||||
public async Task should_only_download_book_once()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -80,12 +81,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteBook>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_download_if_any_book_was_already_downloaded()
|
||||
public async Task should_not_download_if_any_book_was_already_downloaded()
|
||||
{
|
||||
var remoteBook1 = GetRemoteBook(
|
||||
new List<Book> { GetBook(1) },
|
||||
@@ -99,12 +100,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook1));
|
||||
decisions.Add(new DownloadDecision(remoteBook2));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteBook>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_downloaded_reports()
|
||||
public async Task should_return_downloaded_reports()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -112,11 +113,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(1);
|
||||
var result = await Subject.ProcessDecisions(decisions);
|
||||
|
||||
result.Grabbed.Should().HaveCount(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_all_downloaded_reports()
|
||||
public async Task should_return_all_downloaded_reports()
|
||||
{
|
||||
var remoteBook1 = GetRemoteBook(
|
||||
new List<Book> { GetBook(1) },
|
||||
@@ -130,11 +133,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook1));
|
||||
decisions.Add(new DownloadDecision(remoteBook2));
|
||||
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(2);
|
||||
var result = await Subject.ProcessDecisions(decisions);
|
||||
|
||||
result.Grabbed.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_only_return_downloaded_reports()
|
||||
public async Task should_only_return_downloaded_reports()
|
||||
{
|
||||
var remoteBook1 = GetRemoteBook(
|
||||
new List<Book> { GetBook(1) },
|
||||
@@ -153,11 +158,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook2));
|
||||
decisions.Add(new DownloadDecision(remoteBook3));
|
||||
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(2);
|
||||
var result = await Subject.ProcessDecisions(decisions);
|
||||
|
||||
result.Grabbed.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_add_to_downloaded_list_when_download_fails()
|
||||
public async Task should_not_add_to_downloaded_list_when_download_fails()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -166,7 +173,11 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
|
||||
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteBook>())).Throws(new Exception());
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().BeEmpty();
|
||||
|
||||
var result = await Subject.ProcessDecisions(decisions);
|
||||
|
||||
result.Grabbed.Should().BeEmpty();
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
@@ -181,7 +192,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_grab_if_pending()
|
||||
public async Task should_not_grab_if_pending()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -189,12 +200,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteBook, new Rejection("Failure!", RejectionType.Temporary)));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteBook>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_add_to_pending_if_book_was_grabbed()
|
||||
public async Task should_not_add_to_pending_if_book_was_grabbed()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -203,12 +214,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
decisions.Add(new DownloadDecision(remoteBook, new Rejection("Failure!", RejectionType.Temporary)));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_to_pending_even_if_already_added_to_pending()
|
||||
public async Task should_add_to_pending_even_if_already_added_to_pending()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -217,12 +228,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook, new Rejection("Failure!", RejectionType.Temporary)));
|
||||
decisions.Add(new DownloadDecision(remoteBook, new Rejection("Failure!", RejectionType.Temporary)));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_to_failed_if_already_failed_for_that_protocol()
|
||||
public async Task should_add_to_failed_if_already_failed_for_that_protocol()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -234,12 +245,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteBook>()))
|
||||
.Throws(new DownloadClientUnavailableException("Download client failed"));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteBook>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_add_to_failed_if_failed_for_a_different_protocol()
|
||||
public async Task should_not_add_to_failed_if_failed_for_a_different_protocol()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3), DownloadProtocol.Usenet);
|
||||
@@ -252,13 +263,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.Is<RemoteBook>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)))
|
||||
.Throws(new DownloadClientUnavailableException("Download client failed"));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteBook>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)), Times.Once());
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteBook>(r => r.Release.DownloadProtocol == DownloadProtocol.Torrent)), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_to_rejected_if_release_unavailable_on_indexer()
|
||||
public async Task should_add_to_rejected_if_release_unavailable_on_indexer()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -270,7 +281,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
.Setup(s => s.DownloadReport(It.IsAny<RemoteBook>()))
|
||||
.Throws(new ReleaseUnavailableException(remoteBook.Release, "That 404 Error is not just a Quirk"));
|
||||
|
||||
var result = Subject.ProcessDecisions(decisions);
|
||||
var result = await Subject.ProcessDecisions(decisions);
|
||||
|
||||
result.Grabbed.Should().BeEmpty();
|
||||
result.Rejected.Should().NotBeEmpty();
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -69,7 +70,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
protected void GivenFailedDownload()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
|
||||
.Setup(s => s.GetAsync(It.IsAny<HttpRequest>()))
|
||||
.Throws(new WebException());
|
||||
}
|
||||
|
||||
@@ -147,19 +148,19 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_download_file_if_it_doesnt_exist()
|
||||
public async Task Download_should_download_file_if_it_doesnt_exist()
|
||||
{
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_save_magnet_if_enabled()
|
||||
public async Task Download_should_save_magnet_if_enabled()
|
||||
{
|
||||
GivenMagnetFilePath();
|
||||
Subject.Definition.Settings.As<TorrentBlackholeSettings>().SaveMagnetFiles = true;
|
||||
@@ -167,16 +168,16 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = null;
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_save_magnet_using_specified_extension()
|
||||
public async Task Download_should_save_magnet_using_specified_extension()
|
||||
{
|
||||
var magnetFileExtension = ".url";
|
||||
GivenMagnetFilePath(magnetFileExtension);
|
||||
@@ -187,12 +188,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = null;
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -202,31 +203,31 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = null;
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_prefer_torrent_over_magnet()
|
||||
public async Task Download_should_prefer_torrent_over_magnet()
|
||||
{
|
||||
Subject.Definition.Settings.As<TorrentBlackholeSettings>().SaveMagnetFiles = true;
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_replace_illegal_characters_in_title()
|
||||
public async Task Download_should_replace_illegal_characters_in_title()
|
||||
{
|
||||
var illegalTitle = "Radiohead - Scotch Mist [2008/FLAC/Lossless]";
|
||||
var expectedFilename = Path.Combine(_blackholeFolder, "Radiohead - Scotch Mist [2008+FLAC+Lossless]" + Path.GetExtension(_filePath));
|
||||
@@ -234,11 +235,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.Title = illegalTitle;
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -247,7 +248,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = null;
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -317,11 +318,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_hash()
|
||||
public async Task should_return_null_hash()
|
||||
{
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer()).Should().BeNull();
|
||||
var result = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -119,19 +120,19 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_download_file_if_it_doesnt_exist()
|
||||
public async Task Download_should_download_file_if_it_doesnt_exist()
|
||||
{
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_replace_illegal_characters_in_title()
|
||||
public async Task Download_should_replace_illegal_characters_in_title()
|
||||
{
|
||||
var illegalTitle = "Radiohead - Scotch Mist [2008/FLAC/Lossless]";
|
||||
var expectedFilename = Path.Combine(_blackholeFolder, "Radiohead - Scotch Mist [2008+FLAC+Lossless]" + Path.GetExtension(_filePath));
|
||||
@@ -139,11 +140,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.Title = illegalTitle;
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -200,26 +201,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DelugeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = magnetUrl;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NLog;
|
||||
@@ -36,8 +37,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
.Returns(() => CreateRemoteBook());
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
|
||||
.Setup(s => s.GetAsync(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), Array.Empty<byte>())));
|
||||
|
||||
Mocker.GetMock<IRemotePathMappingService>()
|
||||
.Setup(v => v.RemapRemoteToLocal(It.IsAny<string>(), It.IsAny<OsPath>()))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -385,7 +386,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_TvDirectory_should_force_directory()
|
||||
public async Task Download_with_TvDirectory_should_force_directory()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenTvDirectory();
|
||||
@@ -393,7 +394,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -402,7 +403,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_force_directory()
|
||||
public async Task Download_with_category_should_force_directory()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenMusicCategory();
|
||||
@@ -410,7 +411,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -419,14 +420,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_without_TvDirectory_and_Category_should_use_default()
|
||||
public async Task Download_without_TvDirectory_and_Category_should_use_default()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -505,7 +506,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
.Setup(s => s.GetSerialNumber(_settings))
|
||||
.Throws(new ApplicationException("Some unknown exception, HttpException or DownloadClientException"));
|
||||
|
||||
Assert.Throws(Is.InstanceOf<Exception>(), () => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync(Is.InstanceOf<Exception>(), async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
|
||||
Mocker.GetMock<IDownloadStationTaskProxy>()
|
||||
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), null, _settings), Times.Never());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -262,7 +263,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_TvDirectory_should_force_directory()
|
||||
public async Task Download_with_TvDirectory_should_force_directory()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenTvDirectory();
|
||||
@@ -270,7 +271,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -279,7 +280,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_force_directory()
|
||||
public async Task Download_with_category_should_force_directory()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenMusicCategory();
|
||||
@@ -287,7 +288,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -296,14 +297,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_without_TvDirectory_and_Category_should_use_default()
|
||||
public async Task Download_without_TvDirectory_and_Category_should_use_default()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -382,7 +383,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
.Setup(s => s.GetSerialNumber(_settings))
|
||||
.Throws(new ApplicationException("Some unknown exception, HttpException or DownloadClientException"));
|
||||
|
||||
Assert.Throws(Is.InstanceOf<Exception>(), () => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync(Is.InstanceOf<Exception>(), async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
|
||||
Mocker.GetMock<IDownloadStationTaskProxy>()
|
||||
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), null, _settings), Times.Never());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -103,8 +104,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
|
||||
protected void GivenSuccessfulDownload()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[1000]));
|
||||
.Setup(s => s.GetAsync(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new byte[1000])));
|
||||
|
||||
Mocker.GetMock<IHadoukenProxy>()
|
||||
.Setup(s => s.AddTorrentUri(It.IsAny<HadoukenSettings>(), It.IsAny<string>()))
|
||||
@@ -196,13 +197,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -277,7 +278,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_from_magnet_link_should_return_hash_uppercase()
|
||||
public async Task Download_from_magnet_link_should_return_hash_uppercase()
|
||||
{
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
@@ -286,13 +287,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
|
||||
Mocker.GetMock<IHadoukenProxy>()
|
||||
.Setup(v => v.AddTorrentUri(It.IsAny<HadoukenSettings>(), It.IsAny<string>()));
|
||||
|
||||
var result = Subject.Download(remoteBook, CreateIndexer());
|
||||
var result = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Assert.IsFalse(result.Any(c => char.IsLower(c)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_from_torrent_file_should_return_hash_uppercase()
|
||||
public async Task Download_from_torrent_file_should_return_hash_uppercase()
|
||||
{
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
@@ -300,7 +301,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
|
||||
.Setup(v => v.AddTorrentFile(It.IsAny<HadoukenSettings>(), It.IsAny<byte[]>()))
|
||||
.Returns("hash");
|
||||
|
||||
var result = Subject.Download(remoteBook, CreateIndexer());
|
||||
var result = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Assert.IsFalse(result.Any(c => char.IsLower(c)));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -200,13 +201,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -218,7 +219,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Assert.Throws<DownloadClientException>(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync<DownloadClientException>(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -339,13 +340,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -357,7 +358,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Assert.Throws<DownloadClientRejectedReleaseException>(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync<DownloadClientRejectedReleaseException>(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NLog;
|
||||
@@ -65,15 +66,15 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
|
||||
private void WithFailedDownload()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>().Setup(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Throws(new WebException());
|
||||
Mocker.GetMock<IHttpClient>().Setup(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Throws(new WebException());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_download_file_if_it_doesnt_exist()
|
||||
public async Task should_download_file_if_it_doesnt_exist()
|
||||
{
|
||||
Subject.Download(_remoteBook, _indexer);
|
||||
await Subject.Download(_remoteBook, _indexer);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(_nzbUrl, _nzbPath, null), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(_nzbUrl, _nzbPath, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -81,7 +82,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
{
|
||||
WithFailedDownload();
|
||||
|
||||
Assert.Throws<WebException>(() => Subject.Download(_remoteBook, _indexer));
|
||||
Assert.ThrowsAsync<WebException>(async () => await Subject.Download(_remoteBook, _indexer));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -90,7 +91,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
_remoteBook.Release.Title = "Alien Ant Farm - Discography";
|
||||
_remoteBook.ParsedBookInfo.Discography = true;
|
||||
|
||||
Assert.Throws<NotSupportedException>(() => Subject.Download(_remoteBook, _indexer));
|
||||
Assert.ThrowsAsync<NotSupportedException>(async () => await Subject.Download(_remoteBook, _indexer));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -100,15 +101,15 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_replace_illegal_characters_in_title()
|
||||
public async Task should_replace_illegal_characters_in_title()
|
||||
{
|
||||
var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]";
|
||||
var expectedFilename = Path.Combine(_pneumaticFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb");
|
||||
_remoteBook.Release.Title = illegalTitle;
|
||||
|
||||
Subject.Download(_remoteBook, _indexer);
|
||||
await Subject.Download(_remoteBook, _indexer);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), expectedFilename, null), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), expectedFilename, null), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -404,7 +405,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
Size = 1000,
|
||||
Progress = 0.7,
|
||||
Eta = 8640000,
|
||||
State = "stalledDL",
|
||||
State = "pausedUP",
|
||||
Label = "",
|
||||
SavePath = @"C:\Torrents".AsOsAgnostic(),
|
||||
ContentPath = @"C:\Torrents\Droned.S01.12".AsOsAgnostic()
|
||||
@@ -449,26 +450,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = magnetUrl;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
@@ -483,7 +484,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR";
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -496,28 +497,28 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp://abc";
|
||||
|
||||
Assert.DoesNotThrow(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.DoesNotThrowAsync(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Verify(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<TorrentSeedConfiguration>(), It.IsAny<QBittorrentSettings>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_set_top_priority()
|
||||
public async Task Download_should_set_top_priority()
|
||||
{
|
||||
GivenHighPriority();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Verify(v => v.MoveTorrentToTopInQueue(It.IsAny<string>(), It.IsAny<QBittorrentSettings>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_not_fail_if_top_priority_not_available()
|
||||
public async Task Download_should_not_fail_if_top_priority_not_available()
|
||||
{
|
||||
GivenHighPriority();
|
||||
GivenSuccessfulDownload();
|
||||
@@ -528,7 +529,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -555,27 +556,27 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_handle_http_redirect_to_magnet()
|
||||
public async Task Download_should_handle_http_redirect_to_magnet()
|
||||
{
|
||||
GivenRedirectToMagnet();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_handle_http_redirect_to_torrent()
|
||||
public async Task Download_should_handle_http_redirect_to_torrent()
|
||||
{
|
||||
GivenRedirectToTorrent();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -633,7 +634,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
.Returns(new QBittorrentTorrentProperties
|
||||
{
|
||||
Hash = "HASH",
|
||||
SeedingTime = seedingTime
|
||||
SeedingTime = seedingTime * 60
|
||||
});
|
||||
|
||||
return torrent;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -121,13 +122,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.RTorrentTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -300,27 +301,27 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
||||
}
|
||||
|
||||
[TestCase("[ TOWN ]-[ http://www.town.ag ]-[ ANIME ]-[Usenet Provider >> http://www.ssl- <<] - [Commie] Aldnoah Zero 18 [234C8FC7]", "[ TOWN ]-[ http-++www.town.ag ]-[ ANIME ]-[Usenet Provider http-++www.ssl- ] - [Commie] Aldnoah Zero 18 [234C8FC7].nzb")]
|
||||
public void Download_should_use_clean_title(string title, string filename)
|
||||
public async Task Download_should_use_clean_title(string title, string filename)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.Title = title;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<ISabnzbdProxy>()
|
||||
.Verify(v => v.DownloadNzb(It.IsAny<byte[]>(), filename, It.IsAny<string>(), It.IsAny<int>(), It.IsAny<SabnzbdSettings>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -353,7 +354,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_use_sabRecentTvPriority_when_recentEpisode_is_true()
|
||||
public async Task Download_should_use_sabRecentTvPriority_when_recentEpisode_is_true()
|
||||
{
|
||||
Mocker.GetMock<ISabnzbdProxy>()
|
||||
.Setup(s => s.DownloadNzb(It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<string>(), (int)SabnzbdPriority.High, It.IsAny<SabnzbdSettings>()))
|
||||
@@ -366,7 +367,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<ISabnzbdProxy>()
|
||||
.Verify(v => v.DownloadNzb(It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<string>(), (int)SabnzbdPriority.High, It.IsAny<SabnzbdSettings>()), Times.Once());
|
||||
@@ -451,6 +452,30 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
||||
result.OutputRootFolders.First().Should().Be(fullCategoryDir);
|
||||
}
|
||||
|
||||
[TestCase("0")]
|
||||
[TestCase("15d")]
|
||||
public void should_set_history_removes_completed_downloads_false(string historyRetention)
|
||||
{
|
||||
_config.Misc.history_retention = historyRetention;
|
||||
|
||||
var downloadClientInfo = Subject.GetStatus();
|
||||
|
||||
downloadClientInfo.RemovesCompletedDownloads.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("-1")]
|
||||
[TestCase("15")]
|
||||
[TestCase("3")]
|
||||
[TestCase("3d")]
|
||||
public void should_set_history_removes_completed_downloads_true(string historyRetention)
|
||||
{
|
||||
_config.Misc.history_retention = historyRetention;
|
||||
|
||||
var downloadClientInfo = Subject.GetStatus();
|
||||
|
||||
downloadClientInfo.RemovesCompletedDownloads.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase(@"Y:\nzbget\root", @"completed\downloads", @"vv", @"Y:\nzbget\root\completed\downloads", @"Y:\nzbget\root\completed\downloads\vv")]
|
||||
[TestCase(@"Y:\nzbget\root", @"completed", @"vv", @"Y:\nzbget\root\completed", @"Y:\nzbget\root\completed\vv")]
|
||||
[TestCase(@"/nzbget/root", @"completed/downloads", @"vv", @"/nzbget/root/completed/downloads", @"/nzbget/root/completed/downloads/vv")]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -55,26 +56,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_TvDirectory_should_force_directory()
|
||||
public async Task Download_with_TvDirectory_should_force_directory()
|
||||
{
|
||||
GivenTvDirectory();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -83,14 +84,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_force_directory()
|
||||
public async Task Download_with_category_should_force_directory()
|
||||
{
|
||||
GivenMusicCategory();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -99,7 +100,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_not_have_double_slashes()
|
||||
public async Task Download_with_category_should_not_have_double_slashes()
|
||||
{
|
||||
GivenMusicCategory();
|
||||
GivenSuccessfulDownload();
|
||||
@@ -108,7 +109,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -117,13 +118,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_without_TvDirectory_and_Category_should_use_default()
|
||||
public async Task Download_without_TvDirectory_and_Category_should_use_default()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -132,14 +133,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
}
|
||||
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = magnetUrl;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -228,13 +229,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -252,14 +253,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
|
||||
|
||||
// Proxy.GetTorrents does not return original url. So item has to be found via magnet url.
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = magnetUrl;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
@@ -350,27 +351,27 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_handle_http_redirect_to_magnet()
|
||||
public async Task Download_should_handle_http_redirect_to_magnet()
|
||||
{
|
||||
GivenRedirectToMagnet();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_handle_http_redirect_to_torrent()
|
||||
public async Task Download_should_handle_http_redirect_to_torrent()
|
||||
{
|
||||
GivenRedirectToTorrent();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -63,26 +64,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_TvDirectory_should_force_directory()
|
||||
public async Task Download_with_TvDirectory_should_force_directory()
|
||||
{
|
||||
GivenTvDirectory();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -91,14 +92,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_force_directory()
|
||||
public async Task Download_with_category_should_force_directory()
|
||||
{
|
||||
GivenMusicCategory();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -107,7 +108,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_not_have_double_slashes()
|
||||
public async Task Download_with_category_should_not_have_double_slashes()
|
||||
{
|
||||
GivenMusicCategory();
|
||||
GivenSuccessfulDownload();
|
||||
@@ -116,7 +117,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -125,13 +126,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_without_TvDirectory_and_Category_should_use_default()
|
||||
public async Task Download_without_TvDirectory_and_Category_should_use_default()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -140,14 +141,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = magnetUrl;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -77,23 +78,23 @@ namespace NzbDrone.Core.Test.Download
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_report_should_publish_on_grab_event()
|
||||
public async Task Download_report_should_publish_on_grab_event()
|
||||
{
|
||||
var mock = WithUsenetClient();
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()));
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
await Subject.DownloadReport(_parseResult);
|
||||
|
||||
VerifyEventPublished<BookGrabbedEvent>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_report_should_grab_using_client()
|
||||
public async Task Download_report_should_grab_using_client()
|
||||
{
|
||||
var mock = WithUsenetClient();
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()));
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
await Subject.DownloadReport(_parseResult);
|
||||
|
||||
mock.Verify(s => s.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Once());
|
||||
}
|
||||
@@ -105,7 +106,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()))
|
||||
.Throws(new WebException());
|
||||
|
||||
Assert.Throws<WebException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<WebException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
VerifyEventNotPublished<BookGrabbedEvent>();
|
||||
}
|
||||
@@ -120,7 +121,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
throw new ReleaseDownloadException(v.Release, "Error", new WebException());
|
||||
});
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Once());
|
||||
@@ -140,7 +141,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response));
|
||||
});
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.FromMinutes(5.0)), Times.Once());
|
||||
@@ -160,7 +161,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response));
|
||||
});
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(),
|
||||
@@ -174,7 +175,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()))
|
||||
.Throws(new DownloadClientException("Some Error"));
|
||||
|
||||
Assert.Throws<DownloadClientException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<DownloadClientException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Never());
|
||||
@@ -190,7 +191,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
throw new ReleaseUnavailableException(v.Release, "Error", new WebException());
|
||||
});
|
||||
|
||||
Assert.Throws<ReleaseUnavailableException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<ReleaseUnavailableException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Never());
|
||||
@@ -199,14 +200,14 @@ namespace NzbDrone.Core.Test.Download
|
||||
[Test]
|
||||
public void should_not_attempt_download_if_client_isnt_configured()
|
||||
{
|
||||
Assert.Throws<DownloadClientUnavailableException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<DownloadClientUnavailableException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IDownloadClient>().Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Never());
|
||||
VerifyEventNotPublished<BookGrabbedEvent>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_attempt_download_even_if_client_is_disabled()
|
||||
public async Task should_attempt_download_even_if_client_is_disabled()
|
||||
{
|
||||
var mockUsenet = WithUsenetClient();
|
||||
|
||||
@@ -221,7 +222,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
}
|
||||
});
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
await Subject.DownloadReport(_parseResult);
|
||||
|
||||
Mocker.GetMock<IDownloadClientStatusService>().Verify(c => c.GetBlockedProviders(), Times.Never());
|
||||
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Once());
|
||||
@@ -229,26 +230,26 @@ namespace NzbDrone.Core.Test.Download
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_send_download_to_correct_usenet_client()
|
||||
public async Task should_send_download_to_correct_usenet_client()
|
||||
{
|
||||
var mockTorrent = WithTorrentClient();
|
||||
var mockUsenet = WithUsenetClient();
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
await Subject.DownloadReport(_parseResult);
|
||||
|
||||
mockTorrent.Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Never());
|
||||
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_send_download_to_correct_torrent_client()
|
||||
public async Task should_send_download_to_correct_torrent_client()
|
||||
{
|
||||
var mockTorrent = WithTorrentClient();
|
||||
var mockUsenet = WithUsenetClient();
|
||||
|
||||
_parseResult.Release.DownloadProtocol = DownloadProtocol.Torrent;
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
await Subject.DownloadReport(_parseResult);
|
||||
|
||||
mockTorrent.Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Once());
|
||||
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Never());
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Clients;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class DownloadClientRemovesCompletedDownloadsCheckFixture : CoreTest<DownloadClientRemovesCompletedDownloadsCheck>
|
||||
{
|
||||
private DownloadClientInfo _clientStatus;
|
||||
private Mock<IDownloadClient> _downloadClient;
|
||||
|
||||
private static Exception[] DownloadClientExceptions =
|
||||
{
|
||||
new DownloadClientUnavailableException("error"),
|
||||
new DownloadClientAuthenticationException("error"),
|
||||
new DownloadClientException("error")
|
||||
};
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_clientStatus = new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = true,
|
||||
|
||||
// SortingMode = null,
|
||||
RemovesCompletedDownloads = true
|
||||
};
|
||||
|
||||
_downloadClient = Mocker.GetMock<IDownloadClient>();
|
||||
_downloadClient.Setup(s => s.Definition)
|
||||
.Returns(new DownloadClientDefinition { Name = "Test" });
|
||||
|
||||
_downloadClient.Setup(s => s.GetStatus())
|
||||
.Returns(_clientStatus);
|
||||
|
||||
Mocker.GetMock<IProvideDownloadClient>()
|
||||
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
|
||||
.Returns(new IDownloadClient[] { _downloadClient.Object });
|
||||
|
||||
Mocker.GetMock<ILocalizationService>()
|
||||
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
|
||||
.Returns("Some Warning Message");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_if_removing_completed_downloads_is_enabled()
|
||||
{
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_ok_if_remove_completed_downloads_is_not_enabled()
|
||||
{
|
||||
_clientStatus.RemovesCompletedDownloads = false;
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(DownloadClientExceptions))]
|
||||
public void should_return_ok_if_client_throws_downloadclientexception(Exception ex)
|
||||
{
|
||||
_downloadClient.Setup(s => s.GetStatus())
|
||||
.Throws(ex);
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
|
||||
ExceptionVerification.ExpectedErrors(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Books;
|
||||
@@ -27,11 +28,11 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
Mocker.GetMock<ISearchForReleases>()
|
||||
.Setup(s => s.AuthorSearch(_author.Id, false, true, false))
|
||||
.Returns(new List<DownloadDecision>());
|
||||
.Returns(Task.FromResult(new List<DownloadDecision>()));
|
||||
|
||||
Mocker.GetMock<IProcessDownloadDecisions>()
|
||||
.Setup(s => s.ProcessDecisions(It.IsAny<List<DownloadDecision>>()))
|
||||
.Returns(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>(), new List<DownloadDecision>()));
|
||||
.Returns(Task.FromResult(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>(), new List<DownloadDecision>())));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -60,13 +61,13 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
_mockIndexer.Setup(v => v.Fetch(It.IsAny<BookSearchCriteria>()))
|
||||
.Callback<BookSearchCriteria>(s => result.Add(s))
|
||||
.Returns(new List<Parser.Model.ReleaseInfo>());
|
||||
.Returns(Task.FromResult<IList<Parser.Model.ReleaseInfo>>(new List<Parser.Model.ReleaseInfo>()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tags_IndexerTags_AuthorNoTags_IndexerNotIncluded()
|
||||
public async Task Tags_IndexerTags_AuthorNoTags_IndexerNotIncluded()
|
||||
{
|
||||
_mockIndexer.SetupGet(s => s.Definition).Returns(new IndexerDefinition
|
||||
{
|
||||
@@ -76,7 +77,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.BookSearch(_firstBook, false, true, false);
|
||||
await Subject.BookSearch(_firstBook, false, true, false);
|
||||
|
||||
var criteria = allCriteria.OfType<BookSearchCriteria>().ToList();
|
||||
|
||||
@@ -84,7 +85,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tags_IndexerNoTags_AuthorTags_IndexerIncluded()
|
||||
public async Task Tags_IndexerNoTags_AuthorTags_IndexerIncluded()
|
||||
{
|
||||
_mockIndexer.SetupGet(s => s.Definition).Returns(new IndexerDefinition
|
||||
{
|
||||
@@ -102,7 +103,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.BookSearch(_firstBook, false, true, false);
|
||||
await Subject.BookSearch(_firstBook, false, true, false);
|
||||
|
||||
var criteria = allCriteria.OfType<BookSearchCriteria>().ToList();
|
||||
|
||||
@@ -110,7 +111,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tags_IndexerAndAuthorTagsMatch_IndexerIncluded()
|
||||
public async Task Tags_IndexerAndAuthorTagsMatch_IndexerIncluded()
|
||||
{
|
||||
_mockIndexer.SetupGet(s => s.Definition).Returns(new IndexerDefinition
|
||||
{
|
||||
@@ -129,7 +130,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.BookSearch(_firstBook, false, true, false);
|
||||
await Subject.BookSearch(_firstBook, false, true, false);
|
||||
|
||||
var criteria = allCriteria.OfType<BookSearchCriteria>().ToList();
|
||||
|
||||
@@ -137,7 +138,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tags_IndexerAndAuthorTagsMismatch_IndexerNotIncluded()
|
||||
public async Task Tags_IndexerAndAuthorTagsMismatch_IndexerNotIncluded()
|
||||
{
|
||||
_mockIndexer.SetupGet(s => s.Definition).Returns(new IndexerDefinition
|
||||
{
|
||||
@@ -156,7 +157,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.BookSearch(_firstBook, false, true, false);
|
||||
await Subject.BookSearch(_firstBook, false, true, false);
|
||||
|
||||
var criteria = allCriteria.OfType<BookSearchCriteria>().ToList();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -26,15 +27,15 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_FileList()
|
||||
public async Task should_parse_recent_feed_from_FileList()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/FileList/RecentFeed.json");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(2);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -30,24 +31,24 @@ namespace NzbDrone.Core.Test.IndexerTests.GazelleTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_gazelle()
|
||||
public async Task should_parse_recent_feed_from_gazelle()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Gazelle/Gazelle.json");
|
||||
var indexFeed = ReadAllText(@"Files/Indexers/Gazelle/GazelleIndex.json");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get && v.Url.FullUri.Contains("ajax.php?action=browse"))))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader { ContentType = "application/json" }, recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get && v.Url.FullUri.Contains("ajax.php?action=browse"))))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader { ContentType = "application/json" }, recentFeed)));
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("ajax.php?action=index"))))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), indexFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("ajax.php?action=index"))))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), indexFeed)));
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("login.php"))))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), indexFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("login.php"))))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), indexFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(4);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -84,15 +85,15 @@ namespace NzbDrone.Core.Test.IndexerTests.IPTorrentsTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_IPTorrents()
|
||||
public async Task should_parse_recent_feed_from_IPTorrents()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/IPTorrents/IPTorrents.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -39,15 +40,15 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_newznab_nzb_su()
|
||||
public async Task should_parse_recent_feed_from_newznab_nzb_su()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Newznab/newznab_nzb_su.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(100);
|
||||
|
||||
@@ -83,7 +84,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_record_indexer_failure_if_caps_throw()
|
||||
public async Task should_record_indexer_failure_if_caps_throw()
|
||||
{
|
||||
var request = new HttpRequest("http://my.indexer.com");
|
||||
var response = new HttpResponse(request, new HttpHeader(), new byte[0], (HttpStatusCode)429);
|
||||
@@ -96,7 +97,9 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
_caps.MaxPageSize = 30;
|
||||
_caps.DefaultPageSize = 25;
|
||||
|
||||
Subject.FetchRecent().Should().BeEmpty();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().BeEmpty();
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.FromMinutes(5.0)), Times.Once());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -26,15 +27,15 @@ namespace NzbDrone.Core.Test.IndexerTests.NyaaTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_Nyaa()
|
||||
public async Task should_parse_recent_feed_from_Nyaa()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Nyaa/Nyaa.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(4);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
{
|
||||
var result = new List<ValidationFailure>();
|
||||
SetupNLog(); // Enable this to enable trace logging with nlog for debugging purposes
|
||||
Test(result);
|
||||
Test(result).GetAwaiter().GetResult();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -34,17 +35,21 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/" + rssXmlFile);
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.ExecuteAsync(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_ImmortalSeed()
|
||||
public async Task should_parse_recent_feed_from_ImmortalSeed()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/ImmortalSeed.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(50);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -66,11 +71,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_Ezrss()
|
||||
public async Task should_parse_recent_feed_from_Ezrss()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/Ezrss.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(3);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -92,13 +97,13 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_ShowRSS_info()
|
||||
public async Task should_parse_recent_feed_from_ShowRSS_info()
|
||||
{
|
||||
Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true;
|
||||
|
||||
GivenRecentFeedResponse("TorrentRss/ShowRSS.info.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -120,13 +125,13 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_Doki()
|
||||
public async Task should_parse_recent_feed_from_Doki()
|
||||
{
|
||||
Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true;
|
||||
|
||||
GivenRecentFeedResponse("TorrentRss/Doki.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -148,11 +153,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_ExtraTorrents()
|
||||
public async Task should_parse_recent_feed_from_ExtraTorrents()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/ExtraTorrents.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -174,11 +179,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_LimeTorrents()
|
||||
public async Task should_parse_recent_feed_from_LimeTorrents()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/LimeTorrents.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -200,11 +205,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_AnimeTosho_without_size()
|
||||
public async Task should_parse_recent_feed_from_AnimeTosho_without_size()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(2);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -226,11 +231,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_multi_enclosure_from_AnimeTosho()
|
||||
public async Task should_parse_multi_enclosure_from_AnimeTosho()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(2);
|
||||
releases.Last().Should().BeOfType<TorrentInfo>();
|
||||
@@ -243,11 +248,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_AlphaRatio()
|
||||
public async Task should_parse_recent_feed_from_AlphaRatio()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/AlphaRatio.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(2);
|
||||
releases.Last().Should().BeOfType<TorrentInfo>();
|
||||
@@ -260,12 +265,12 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_EveolutionWorld_without_size()
|
||||
public async Task should_parse_recent_feed_from_EveolutionWorld_without_size()
|
||||
{
|
||||
Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true;
|
||||
GivenRecentFeedResponse("TorrentRss/EvolutionWorld.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(2);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -287,11 +292,13 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_record_indexer_failure_if_unsupported_feed()
|
||||
public async Task should_record_indexer_failure_if_unsupported_feed()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/invalid/TorrentDay_NoPubDate.xml");
|
||||
|
||||
Subject.FetchRecent().Should().BeEmpty();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().BeEmpty();
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.Zero), Times.Once());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -26,15 +27,15 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentleechTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_Torrentleech()
|
||||
public async Task should_parse_recent_feed_from_Torrentleech()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Torrentleech/Torrentleech.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -44,15 +45,15 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_torznab_hdaccess_net()
|
||||
public async Task should_parse_recent_feed_from_torznab_hdaccess_net()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_hdaccess_net.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
|
||||
@@ -73,15 +74,15 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_torznab_tpb()
|
||||
public async Task should_parse_recent_feed_from_torznab_tpb()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
|
||||
@@ -140,8 +141,8 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
(Subject.Definition.Settings as TorznabSettings).BaseUrl = baseUrl;
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var result = new NzbDroneValidationResult(Subject.Test());
|
||||
result.IsValid.Should().BeTrue();
|
||||
@@ -155,8 +156,8 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
(Subject.Definition.Settings as TorznabSettings).ApiPath = apiPath;
|
||||
|
||||
|
||||
@@ -64,6 +64,11 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||
TestLogger.Info("OnRename was called");
|
||||
}
|
||||
|
||||
public override void OnAuthorAdded(Author author)
|
||||
{
|
||||
TestLogger.Info("OnAuthorAdded was called");
|
||||
}
|
||||
|
||||
public override void OnAuthorDelete(AuthorDeleteMessage message)
|
||||
{
|
||||
TestLogger.Info("OnAuthorDelete was called");
|
||||
@@ -138,6 +143,7 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||
notification.SupportsOnUpgrade.Should().BeTrue();
|
||||
notification.SupportsOnRename.Should().BeTrue();
|
||||
notification.SupportsOnHealthIssue.Should().BeTrue();
|
||||
notification.SupportsOnAuthorAdded.Should().BeTrue();
|
||||
notification.SupportsOnAuthorDelete.Should().BeTrue();
|
||||
notification.SupportsOnBookDelete.Should().BeTrue();
|
||||
notification.SupportsOnBookFileDelete.Should().BeTrue();
|
||||
@@ -157,6 +163,7 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||
notification.SupportsOnReleaseImport.Should().BeFalse();
|
||||
notification.SupportsOnUpgrade.Should().BeFalse();
|
||||
notification.SupportsOnRename.Should().BeFalse();
|
||||
notification.SupportsOnAuthorAdded.Should().BeFalse();
|
||||
notification.SupportsOnAuthorDelete.Should().BeFalse();
|
||||
notification.SupportsOnBookDelete.Should().BeFalse();
|
||||
notification.SupportsOnBookFileDelete.Should().BeFalse();
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace NzbDrone.Core.Blocklisting
|
||||
{
|
||||
if (release.InfoHash.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return release.InfoHash.Equals(item.TorrentInfoHash);
|
||||
return release.InfoHash.Equals(item.TorrentInfoHash, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
return HasSameIndexer(item, release.Indexer);
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace NzbDrone.Core.Books.Calibre
|
||||
|
||||
var updatedPath = GetOriginalFormat(updated.Formats);
|
||||
|
||||
if (updatedPath != file.Path)
|
||||
if (updatedPath != null && updatedPath != file.Path)
|
||||
{
|
||||
_rootFolderWatchingService.ReportFileSystemChangeBeginning(updatedPath);
|
||||
file.Path = updatedPath;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(038)]
|
||||
public class add_on_author_added_to_notifications : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Notifications").AddColumn("OnAuthorAdded").AsBoolean().WithDefaultValue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,7 @@ namespace NzbDrone.Core.Datastore
|
||||
.Ignore(i => i.SupportsOnReleaseImport)
|
||||
.Ignore(i => i.SupportsOnUpgrade)
|
||||
.Ignore(i => i.SupportsOnRename)
|
||||
.Ignore(i => i.SupportsOnAuthorAdded)
|
||||
.Ignore(i => i.SupportsOnAuthorDelete)
|
||||
.Ignore(i => i.SupportsOnBookDelete)
|
||||
.Ignore(i => i.SupportsOnBookFileDelete)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@@ -10,12 +11,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||
public class IndexerTagSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly IIndexerRepository _indexerRepository;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
|
||||
public IndexerTagSpecification(Logger logger, IIndexerRepository indexerRepository)
|
||||
public IndexerTagSpecification(Logger logger, IIndexerFactory indexerFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_indexerRepository = indexerRepository;
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public SpecificationPriority Priority => SpecificationPriority.Default;
|
||||
@@ -23,8 +24,24 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteBook subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
// If indexer has tags, check that at least one of them is present on the author
|
||||
var indexerTags = _indexerRepository.Get(subject.Release.IndexerId).Tags;
|
||||
if (subject.Release == null || subject.Author?.Tags == null || subject.Release.IndexerId == 0)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
IndexerDefinition indexer;
|
||||
try
|
||||
{
|
||||
indexer = _indexerFactory.Get(subject.Release.IndexerId);
|
||||
}
|
||||
catch (ModelNotFoundException)
|
||||
{
|
||||
_logger.Debug("Indexer with id {0} does not exist, skipping indexer tags check", subject.Release.IndexerId);
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
// If indexer has tags, check that at least one of them is present on the series
|
||||
var indexerTags = indexer.Tags;
|
||||
|
||||
if (indexerTags.Any() && indexerTags.Intersect(subject.Author.Tags).Empty())
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
@@ -32,7 +33,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
||||
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
|
||||
|
||||
public override string Download(RemoteBook remoteBook, IIndexer indexer)
|
||||
public override async Task<string> Download(RemoteBook remoteBook, IIndexer indexer)
|
||||
{
|
||||
var url = remoteBook.Release.DownloadUrl;
|
||||
var title = remoteBook.Release.Title;
|
||||
@@ -48,7 +49,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
||||
var nzbFile = Path.Combine(Settings.NzbFolder, title + ".nzb");
|
||||
|
||||
_logger.Debug("Downloading NZB from: {0} to: {1}", url, nzbFile);
|
||||
_httpClient.DownloadFile(url, nzbFile);
|
||||
await _httpClient.DownloadFileAsync(url, nzbFile);
|
||||
|
||||
_logger.Debug("NZB Download succeeded, saved to: {0}", nzbFile);
|
||||
|
||||
|
||||
@@ -304,13 +304,13 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
break;
|
||||
}
|
||||
|
||||
if (version >= new Version("2.6.1"))
|
||||
if (version >= new Version("2.6.1") && item.Status == DownloadItemStatus.Completed)
|
||||
{
|
||||
if (torrent.ContentPath != torrent.SavePath)
|
||||
{
|
||||
item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.ContentPath));
|
||||
}
|
||||
else if (item.Status == DownloadItemStatus.Completed)
|
||||
else
|
||||
{
|
||||
item.Status = DownloadItemStatus.Warning;
|
||||
item.Message = "Unable to Import. Path matches client base download directory, it's possible 'Keep top-level folder' is disabled for this torrent or 'Torrent Content Layout' is NOT set to 'Original' or 'Create Subfolder'?";
|
||||
@@ -386,10 +386,13 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
}
|
||||
|
||||
var minimumRetention = 60 * 24 * 14;
|
||||
|
||||
return new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) }
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) },
|
||||
RemovesCompletedDownloads = (config.MaxRatioEnabled || (config.MaxSeedingTimeEnabled && config.MaxSeedingTime < minimumRetention)) && (config.MaxRatioAction == QBittorrentMaxRatioAction.Remove || config.MaxRatioAction == QBittorrentMaxRatioAction.DeleteFiles)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -628,11 +631,11 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
|
||||
if (torrent.SeedingTimeLimit >= 0)
|
||||
{
|
||||
seedingTimeLimit = torrent.SeedingTimeLimit;
|
||||
seedingTimeLimit = torrent.SeedingTimeLimit * 60;
|
||||
}
|
||||
else if (torrent.SeedingTimeLimit == -2 && config.MaxSeedingTimeEnabled)
|
||||
{
|
||||
seedingTimeLimit = config.MaxSeedingTime;
|
||||
seedingTimeLimit = config.MaxSeedingTime * 60;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
public float RatioLimit { get; set; } = -2;
|
||||
|
||||
[JsonProperty(PropertyName = "seeding_time")]
|
||||
public long? SeedingTime { get; set; } // Torrent seeding time (not provided by the list api)
|
||||
public long? SeedingTime { get; set; } // Torrent seeding time (in seconds, not provided by the list api)
|
||||
|
||||
[JsonProperty(PropertyName = "seeding_time_limit")] // Per torrent seeding time limit (-2 = use global, -1 = unlimited)
|
||||
public long SeedingTimeLimit { get; set; } = -2;
|
||||
@@ -47,7 +47,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
public string SavePath { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "seeding_time")]
|
||||
public long SeedingTime { get; set; } // Torrent seeding time
|
||||
public long SeedingTime { get; set; } // Torrent seeding time (in seconds)
|
||||
}
|
||||
|
||||
public class QBittorrentTorrentFile
|
||||
|
||||
@@ -263,6 +263,17 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, category.FullPath) };
|
||||
}
|
||||
|
||||
if (config.Misc.history_retention.IsNotNullOrWhiteSpace() && config.Misc.history_retention.EndsWith("d"))
|
||||
{
|
||||
int.TryParse(config.Misc.history_retention.AsSpan(0, config.Misc.history_retention.Length - 1),
|
||||
out var daysRetention);
|
||||
status.RemovesCompletedDownloads = daysRetention < 14;
|
||||
}
|
||||
else
|
||||
{
|
||||
status.RemovesCompletedDownloads = config.Misc.history_retention != "0";
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
public string[] date_categories { get; set; }
|
||||
public bool enable_date_sorting { get; set; }
|
||||
public bool pre_check { get; set; }
|
||||
public string history_retention { get; set; }
|
||||
}
|
||||
|
||||
public class SabnzbdCategory
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
@@ -58,7 +59,7 @@ namespace NzbDrone.Core.Download
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract string Download(RemoteBook remoteBook, IIndexer indexer);
|
||||
public abstract Task<string> Download(RemoteBook remoteBook, IIndexer indexer);
|
||||
public abstract IEnumerable<DownloadClientItem> GetItems();
|
||||
|
||||
public virtual DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt)
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.Download
|
||||
}
|
||||
|
||||
public bool IsLocalhost { get; set; }
|
||||
public bool RemovesCompletedDownloads { get; set; }
|
||||
public List<OsPath> OutputRootFolders { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -16,7 +17,7 @@ namespace NzbDrone.Core.Download
|
||||
{
|
||||
public interface IDownloadService
|
||||
{
|
||||
void DownloadReport(RemoteBook remoteBook);
|
||||
Task DownloadReport(RemoteBook remoteBook);
|
||||
}
|
||||
|
||||
public class DownloadService : IDownloadService
|
||||
@@ -49,15 +50,23 @@ namespace NzbDrone.Core.Download
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void DownloadReport(RemoteBook remoteBook)
|
||||
public async Task DownloadReport(RemoteBook remoteBook)
|
||||
{
|
||||
var filterBlockedClients = remoteBook.Release.PendingReleaseReason == PendingReleaseReason.DownloadClientUnavailable;
|
||||
|
||||
var tags = remoteBook.Author?.Tags;
|
||||
|
||||
var downloadClient = _downloadClientProvider.GetDownloadClient(remoteBook.Release.DownloadProtocol, remoteBook.Release.IndexerId, filterBlockedClients, tags);
|
||||
|
||||
await DownloadReport(remoteBook, downloadClient);
|
||||
}
|
||||
|
||||
private async Task DownloadReport(RemoteBook remoteBook, IDownloadClient downloadClient)
|
||||
{
|
||||
Ensure.That(remoteBook.Author, () => remoteBook.Author).IsNotNull();
|
||||
Ensure.That(remoteBook.Books, () => remoteBook.Books).HasItems();
|
||||
|
||||
var downloadTitle = remoteBook.Release.Title;
|
||||
var filterBlockedClients = remoteBook.Release.PendingReleaseReason == PendingReleaseReason.DownloadClientUnavailable;
|
||||
var tags = remoteBook.Author?.Tags;
|
||||
var downloadClient = _downloadClientProvider.GetDownloadClient(remoteBook.Release.DownloadProtocol, remoteBook.Release.IndexerId, filterBlockedClients, tags);
|
||||
|
||||
if (downloadClient == null)
|
||||
{
|
||||
@@ -71,7 +80,7 @@ namespace NzbDrone.Core.Download
|
||||
if (remoteBook.Release.DownloadUrl.IsNotNullOrWhiteSpace() && !remoteBook.Release.DownloadUrl.StartsWith("magnet:"))
|
||||
{
|
||||
var url = new HttpUri(remoteBook.Release.DownloadUrl);
|
||||
_rateLimitService.WaitAndPulse(url.Host, TimeSpan.FromSeconds(2));
|
||||
await _rateLimitService.WaitAndPulseAsync(url.Host, TimeSpan.FromSeconds(2));
|
||||
}
|
||||
|
||||
IIndexer indexer = null;
|
||||
@@ -84,7 +93,7 @@ namespace NzbDrone.Core.Download
|
||||
string downloadClientId;
|
||||
try
|
||||
{
|
||||
downloadClientId = downloadClient.Download(remoteBook, indexer);
|
||||
downloadClientId = await downloadClient.Download(remoteBook, indexer);
|
||||
_downloadClientStatusService.RecordSuccess(downloadClient.Definition.Id);
|
||||
_indexerStatusService.RecordSuccess(remoteBook.Release.IndexerId);
|
||||
}
|
||||
@@ -100,8 +109,7 @@ namespace NzbDrone.Core.Download
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
{
|
||||
var http429 = ex.InnerException as TooManyRequestsException;
|
||||
if (http429 != null)
|
||||
if (ex.InnerException is TooManyRequestsException http429)
|
||||
{
|
||||
_indexerStatusService.RecordFailure(remoteBook.Release.IndexerId, http429.RetryAfter);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
@@ -8,7 +9,7 @@ namespace NzbDrone.Core.Download
|
||||
public interface IDownloadClient : IProvider
|
||||
{
|
||||
DownloadProtocol Protocol { get; }
|
||||
string Download(RemoteBook remoteBook, IIndexer indexer);
|
||||
Task<string> Download(RemoteBook remoteBook, IIndexer indexer);
|
||||
IEnumerable<DownloadClientItem> GetItems();
|
||||
DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt);
|
||||
void RemoveItem(DownloadClientItem item, bool deleteData);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download.Clients;
|
||||
@@ -12,7 +13,7 @@ namespace NzbDrone.Core.Download
|
||||
{
|
||||
public interface IProcessDownloadDecisions
|
||||
{
|
||||
ProcessedDecisions ProcessDecisions(List<DownloadDecision> decisions);
|
||||
Task<ProcessedDecisions> ProcessDecisions(List<DownloadDecision> decisions);
|
||||
}
|
||||
|
||||
public class ProcessDownloadDecisions : IProcessDownloadDecisions
|
||||
@@ -33,7 +34,7 @@ namespace NzbDrone.Core.Download
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public ProcessedDecisions ProcessDecisions(List<DownloadDecision> decisions)
|
||||
public async Task<ProcessedDecisions> ProcessDecisions(List<DownloadDecision> decisions)
|
||||
{
|
||||
var qualifiedReports = GetQualifiedReports(decisions);
|
||||
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(qualifiedReports);
|
||||
@@ -75,7 +76,7 @@ namespace NzbDrone.Core.Download
|
||||
try
|
||||
{
|
||||
_logger.Trace("Grabbing from Indexer {0} at priority {1}.", remoteBook.Release.Indexer, remoteBook.Release.IndexerPriority);
|
||||
_downloadService.DownloadReport(remoteBook);
|
||||
await _downloadService.DownloadReport(remoteBook);
|
||||
grabbed.Add(report);
|
||||
}
|
||||
catch (ReleaseUnavailableException)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using MonoTorrent;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
@@ -41,7 +42,7 @@ namespace NzbDrone.Core.Download
|
||||
protected abstract string AddFromMagnetLink(RemoteBook remoteBook, string hash, string magnetLink);
|
||||
protected abstract string AddFromTorrentFile(RemoteBook remoteBook, string hash, string filename, byte[] fileContent);
|
||||
|
||||
public override string Download(RemoteBook remoteBook, IIndexer indexer)
|
||||
public override async Task<string> Download(RemoteBook remoteBook, IIndexer indexer)
|
||||
{
|
||||
var torrentInfo = remoteBook.Release as TorrentInfo;
|
||||
|
||||
@@ -68,7 +69,7 @@ namespace NzbDrone.Core.Download
|
||||
{
|
||||
try
|
||||
{
|
||||
return DownloadFromWebUrl(remoteBook, indexer, torrentUrl);
|
||||
return await DownloadFromWebUrl(remoteBook, indexer, torrentUrl);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -114,14 +115,14 @@ namespace NzbDrone.Core.Download
|
||||
|
||||
if (torrentUrl.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return DownloadFromWebUrl(remoteBook, indexer, torrentUrl);
|
||||
return await DownloadFromWebUrl(remoteBook, indexer, torrentUrl);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string DownloadFromWebUrl(RemoteBook remoteBook, IIndexer indexer, string torrentUrl)
|
||||
private async Task<string> DownloadFromWebUrl(RemoteBook remoteBook, IIndexer indexer, string torrentUrl)
|
||||
{
|
||||
byte[] torrentFile = null;
|
||||
|
||||
@@ -132,7 +133,7 @@ namespace NzbDrone.Core.Download
|
||||
request.Headers.Accept = "application/x-bittorrent";
|
||||
request.AllowAutoRedirect = false;
|
||||
|
||||
var response = _httpClient.Get(request);
|
||||
var response = await _httpClient.GetAsync(request);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.MovedPermanently ||
|
||||
response.StatusCode == HttpStatusCode.Found ||
|
||||
@@ -151,7 +152,7 @@ namespace NzbDrone.Core.Download
|
||||
|
||||
request.Url += new HttpUri(locationHeader);
|
||||
|
||||
return DownloadFromWebUrl(remoteBook, indexer, request.Url.ToString());
|
||||
return await DownloadFromWebUrl(remoteBook, indexer, request.Url.ToString());
|
||||
}
|
||||
|
||||
throw new WebException("Remote website tried to redirect without providing a location.");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Http;
|
||||
@@ -34,7 +35,7 @@ namespace NzbDrone.Core.Download
|
||||
|
||||
protected abstract string AddFromNzbFile(RemoteBook remoteBook, string filename, byte[] fileContent);
|
||||
|
||||
public override string Download(RemoteBook remoteBook, IIndexer indexer)
|
||||
public override async Task<string> Download(RemoteBook remoteBook, IIndexer indexer)
|
||||
{
|
||||
var url = remoteBook.Release.DownloadUrl;
|
||||
var filename = FileNameBuilder.CleanFileName(remoteBook.Release.Title) + ".nzb";
|
||||
@@ -46,7 +47,9 @@ namespace NzbDrone.Core.Download
|
||||
var request = indexer?.GetDownloadRequest(url) ?? new HttpRequest(url);
|
||||
request.RateLimitKey = remoteBook?.Release?.IndexerId.ToString();
|
||||
|
||||
nzbData = _httpClient.Get(request).ResponseData;
|
||||
var response = await _httpClient.GetAsync(request);
|
||||
|
||||
nzbData = response.ResponseData;
|
||||
|
||||
_logger.Debug("Downloaded nzb for release '{0}' finished ({1} bytes from {2})", remoteBook.Release.Title, nzbData.Length, url);
|
||||
}
|
||||
|
||||
@@ -346,6 +346,7 @@ namespace NzbDrone.Core.Extras.Metadata
|
||||
private void DownloadImage(Author author, ImageFileResult image)
|
||||
{
|
||||
var fullPath = Path.Combine(author.Path, image.RelativePath);
|
||||
var downloaded = true;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -353,12 +354,19 @@ namespace NzbDrone.Core.Extras.Metadata
|
||||
{
|
||||
_httpClient.DownloadFile(image.Url, fullPath);
|
||||
}
|
||||
else
|
||||
else if (_diskProvider.FileExists(image.Url))
|
||||
{
|
||||
_diskProvider.CopyFile(image.Url, fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
downloaded = false;
|
||||
}
|
||||
|
||||
_mediaFileAttributeService.SetFilePermissions(fullPath);
|
||||
if (downloaded)
|
||||
{
|
||||
_mediaFileAttributeService.SetFilePermissions(fullPath);
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Clients;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.RemotePathMappings;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
[CheckOn(typeof(ProviderUpdatedEvent<IDownloadClient>))]
|
||||
[CheckOn(typeof(ProviderDeletedEvent<IDownloadClient>))]
|
||||
[CheckOn(typeof(ModelEvent<RootFolder>))]
|
||||
[CheckOn(typeof(ModelEvent<RemotePathMapping>))]
|
||||
|
||||
public class DownloadClientRemovesCompletedDownloadsCheck : HealthCheckBase, IProvideHealthCheck
|
||||
{
|
||||
private readonly IProvideDownloadClient _downloadClientProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DownloadClientRemovesCompletedDownloadsCheck(IProvideDownloadClient downloadClientProvider,
|
||||
Logger logger,
|
||||
ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
{
|
||||
_downloadClientProvider = downloadClientProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var clients = _downloadClientProvider.GetDownloadClients(true);
|
||||
|
||||
foreach (var client in clients)
|
||||
{
|
||||
try
|
||||
{
|
||||
var clientName = client.Definition.Name;
|
||||
var status = client.GetStatus();
|
||||
|
||||
if (status.RemovesCompletedDownloads)
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
string.Format(_localizationService.GetLocalizedString("DownloadClientRemovesCompletedDownloadsHealthCheckMessage"), clientName, "Readarr"),
|
||||
"#download-client-removes-completed-downloads");
|
||||
}
|
||||
}
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unknown error occurred in DownloadClientHistoryRetentionCheck HealthCheck");
|
||||
}
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
{
|
||||
public class FixMultipleMonitoredEditions : IHousekeepingTask
|
||||
{
|
||||
private readonly IMainDatabase _database;
|
||||
|
||||
public FixMultipleMonitoredEditions(IMainDatabase database)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public void Clean()
|
||||
{
|
||||
using var mapper = _database.OpenConnection();
|
||||
|
||||
if (_database.DatabaseType == DatabaseType.PostgreSQL)
|
||||
{
|
||||
mapper.Execute(@"UPDATE ""Editions""
|
||||
SET ""Monitored"" = true
|
||||
WHERE ""Id"" IN (
|
||||
SELECT MIN(""Id"")
|
||||
FROM ""Editions""
|
||||
WHERE ""Monitored"" = true
|
||||
GROUP BY ""BookId""
|
||||
HAVING COUNT(""BookId"") > 1
|
||||
)");
|
||||
}
|
||||
else
|
||||
{
|
||||
mapper.Execute(@"UPDATE ""Editions""
|
||||
SET ""Monitored"" = 0
|
||||
WHERE ""Id"" IN (
|
||||
SELECT MIN(""Id"")
|
||||
FROM ""Editions""
|
||||
WHERE ""Monitored"" = 1
|
||||
GROUP BY ""BookId""
|
||||
HAVING COUNT(""BookId"") > 1
|
||||
)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,8 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
|
||||
public void Execute(AuthorSearchCommand message)
|
||||
{
|
||||
var decisions = _releaseSearchService.AuthorSearch(message.AuthorId, false, message.Trigger == CommandTrigger.Manual, false);
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
var decisions = _releaseSearchService.AuthorSearch(message.AuthorId, false, message.Trigger == CommandTrigger.Manual, false).GetAwaiter().GetResult();
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions).GetAwaiter().GetResult();
|
||||
|
||||
_logger.ProgressInfo("Author search completed. {0} reports downloaded.", processed.Grabbed.Count);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Queue;
|
||||
@@ -39,16 +39,15 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private void SearchForMissingBooks(List<Book> books, bool userInvokedSearch)
|
||||
private async Task SearchForMissingBooks(List<Book> books, bool userInvokedSearch)
|
||||
{
|
||||
_logger.ProgressInfo("Performing missing search for {0} books", books.Count);
|
||||
var downloadedCount = 0;
|
||||
|
||||
foreach (var book in books)
|
||||
{
|
||||
List<DownloadDecision> decisions;
|
||||
decisions = _releaseSearchService.BookSearch(book.Id, false, userInvokedSearch, false);
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
var decisions = await _releaseSearchService.BookSearch(book.Id, false, userInvokedSearch, false);
|
||||
var processed = await _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
|
||||
downloadedCount += processed.Grabbed.Count;
|
||||
}
|
||||
@@ -60,9 +59,8 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
foreach (var bookId in message.BookIds)
|
||||
{
|
||||
var decisions =
|
||||
_releaseSearchService.BookSearch(bookId, false, message.Trigger == CommandTrigger.Manual, false);
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
var decisions = _releaseSearchService.BookSearch(bookId, false, message.Trigger == CommandTrigger.Manual, false).GetAwaiter().GetResult();
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions).GetAwaiter().GetResult();
|
||||
|
||||
_logger.ProgressInfo("Book search completed. {0} reports downloaded.", processed.Grabbed.Count);
|
||||
}
|
||||
@@ -106,7 +104,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
var queue = _queueService.GetQueue().Where(q => q.Book != null).Select(q => q.Book.Id);
|
||||
var missing = books.Where(e => !queue.Contains(e.Id)).ToList();
|
||||
|
||||
SearchForMissingBooks(missing, message.Trigger == CommandTrigger.Manual);
|
||||
SearchForMissingBooks(missing, message.Trigger == CommandTrigger.Manual).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public void Execute(CutoffUnmetBookSearchCommand message)
|
||||
@@ -132,7 +130,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
var queue = _queueService.GetQueue().Where(q => q.Book != null).Select(q => q.Book.Id);
|
||||
var missing = books.Where(e => !queue.Contains(e.Id)).ToList();
|
||||
|
||||
SearchForMissingBooks(missing, message.Trigger == CommandTrigger.Manual);
|
||||
SearchForMissingBooks(missing, message.Trigger == CommandTrigger.Manual).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Common.TPL;
|
||||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Indexers;
|
||||
@@ -16,8 +15,8 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
public interface ISearchForReleases
|
||||
{
|
||||
List<DownloadDecision> BookSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
|
||||
List<DownloadDecision> AuthorSearch(int authorId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
|
||||
Task<List<DownloadDecision>> BookSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
|
||||
Task<List<DownloadDecision>> AuthorSearch(int authorId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
|
||||
}
|
||||
|
||||
public class ReleaseSearchService : ISearchForReleases
|
||||
@@ -41,31 +40,31 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<DownloadDecision> BookSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
|
||||
public async Task<List<DownloadDecision>> BookSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
|
||||
{
|
||||
var downloadDecisions = new List<DownloadDecision>();
|
||||
|
||||
var book = _bookService.GetBook(bookId);
|
||||
|
||||
var decisions = BookSearch(book, missingOnly, userInvokedSearch, interactiveSearch);
|
||||
var decisions = await BookSearch(book, missingOnly, userInvokedSearch, interactiveSearch);
|
||||
downloadDecisions.AddRange(decisions);
|
||||
|
||||
return DeDupeDecisions(downloadDecisions);
|
||||
}
|
||||
|
||||
public List<DownloadDecision> AuthorSearch(int authorId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
|
||||
public async Task<List<DownloadDecision>> AuthorSearch(int authorId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
|
||||
{
|
||||
var downloadDecisions = new List<DownloadDecision>();
|
||||
|
||||
var author = _authorService.GetAuthor(authorId);
|
||||
|
||||
var decisions = AuthorSearch(author, missingOnly, userInvokedSearch, interactiveSearch);
|
||||
var decisions = await AuthorSearch(author, missingOnly, userInvokedSearch, interactiveSearch);
|
||||
downloadDecisions.AddRange(decisions);
|
||||
|
||||
return DeDupeDecisions(downloadDecisions);
|
||||
}
|
||||
|
||||
public List<DownloadDecision> AuthorSearch(Author author, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
|
||||
public async Task<List<DownloadDecision>> AuthorSearch(Author author, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
|
||||
{
|
||||
var searchSpec = Get<AuthorSearchCriteria>(author, userInvokedSearch, interactiveSearch);
|
||||
var books = _bookService.GetBooksByAuthor(author.Id);
|
||||
@@ -74,10 +73,10 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
|
||||
searchSpec.Books = books;
|
||||
|
||||
return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
|
||||
return await Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
|
||||
}
|
||||
|
||||
public List<DownloadDecision> BookSearch(Book book, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
|
||||
public async Task<List<DownloadDecision>> BookSearch(Book book, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
|
||||
{
|
||||
var author = _authorService.GetAuthor(book.AuthorId);
|
||||
|
||||
@@ -91,7 +90,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
searchSpec.BookYear = book.ReleaseDate.Value.Year;
|
||||
}
|
||||
|
||||
return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
|
||||
return await Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
|
||||
}
|
||||
|
||||
private TSpec Get<TSpec>(Author author, List<Book> books, bool userInvokedSearch, bool interactiveSearch)
|
||||
@@ -118,7 +117,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
return spec;
|
||||
}
|
||||
|
||||
private List<DownloadDecision> Dispatch(Func<IIndexer, IEnumerable<ReleaseInfo>> searchAction, SearchCriteriaBase criteriaBase)
|
||||
private async Task<List<DownloadDecision>> Dispatch(Func<IIndexer, Task<IList<ReleaseInfo>>> searchAction, SearchCriteriaBase criteriaBase)
|
||||
{
|
||||
var indexers = criteriaBase.InteractiveSearch ?
|
||||
_indexerFactory.InteractiveSearchEnabled() :
|
||||
@@ -127,42 +126,33 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
// Filter indexers to untagged indexers and indexers with intersecting tags
|
||||
indexers = indexers.Where(i => i.Definition.Tags.Empty() || i.Definition.Tags.Intersect(criteriaBase.Author.Tags).Any()).ToList();
|
||||
|
||||
var reports = new List<ReleaseInfo>();
|
||||
|
||||
_logger.ProgressInfo("Searching indexers for {0}. {1} active indexers", criteriaBase, indexers.Count);
|
||||
|
||||
var taskList = new List<Task>();
|
||||
var taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None);
|
||||
var tasks = indexers.Select(indexer => DispatchIndexer(searchAction, indexer, criteriaBase));
|
||||
|
||||
foreach (var indexer in indexers)
|
||||
{
|
||||
var indexerLocal = indexer;
|
||||
var batch = await Task.WhenAll(tasks);
|
||||
|
||||
taskList.Add(taskFactory.StartNew(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var indexerReports = searchAction(indexerLocal);
|
||||
|
||||
lock (reports)
|
||||
{
|
||||
reports.AddRange(indexerReports);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Error while searching for {0}", criteriaBase);
|
||||
}
|
||||
}).LogExceptions());
|
||||
}
|
||||
|
||||
Task.WaitAll(taskList.ToArray());
|
||||
var reports = batch.SelectMany(x => x).ToList();
|
||||
|
||||
_logger.Debug("Total of {0} reports were found for {1} from {2} indexers", reports.Count, criteriaBase, indexers.Count);
|
||||
|
||||
return _makeDownloadDecision.GetSearchDecision(reports, criteriaBase).ToList();
|
||||
}
|
||||
|
||||
private async Task<IList<ReleaseInfo>> DispatchIndexer(Func<IIndexer, Task<IList<ReleaseInfo>>> searchAction, IIndexer indexer, SearchCriteriaBase criteriaBase)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await searchAction(indexer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Error while searching for {0}", criteriaBase);
|
||||
}
|
||||
|
||||
return Array.Empty<ReleaseInfo>();
|
||||
}
|
||||
|
||||
private List<DownloadDecision> DeDupeDecisions(List<DownloadDecision> decisions)
|
||||
{
|
||||
// De-dupe reports by guid so duplicate results aren't returned. Pick the one with the least rejections and higher indexer priority.
|
||||
|
||||
@@ -3,13 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.TPL;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public interface IFetchAndParseRss
|
||||
{
|
||||
List<ReleaseInfo> Fetch();
|
||||
Task<List<ReleaseInfo>> Fetch();
|
||||
}
|
||||
|
||||
public class FetchAndParseRssService : IFetchAndParseRss
|
||||
@@ -23,54 +22,42 @@ namespace NzbDrone.Core.Indexers
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ReleaseInfo> Fetch()
|
||||
public async Task<List<ReleaseInfo>> Fetch()
|
||||
{
|
||||
var result = new List<ReleaseInfo>();
|
||||
|
||||
var indexers = _indexerFactory.RssEnabled();
|
||||
|
||||
if (!indexers.Any())
|
||||
{
|
||||
_logger.Warn("No available indexers. check your configuration.");
|
||||
return result;
|
||||
|
||||
return new List<ReleaseInfo>();
|
||||
}
|
||||
|
||||
_logger.Debug("Available indexers {0}", indexers.Count);
|
||||
|
||||
var taskList = new List<Task>();
|
||||
var taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None);
|
||||
var tasks = indexers.Select(FetchIndexer);
|
||||
|
||||
foreach (var indexer in indexers)
|
||||
{
|
||||
var indexerLocal = indexer;
|
||||
var batch = await Task.WhenAll(tasks);
|
||||
|
||||
var task = taskFactory.StartNew(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var indexerReports = indexerLocal.FetchRecent();
|
||||
|
||||
lock (result)
|
||||
{
|
||||
_logger.Debug("Found {0} from {1}", indexerReports.Count, indexer.Name);
|
||||
|
||||
result.AddRange(indexerReports);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Error during RSS Sync");
|
||||
}
|
||||
}).LogExceptions();
|
||||
|
||||
taskList.Add(task);
|
||||
}
|
||||
|
||||
Task.WaitAll(taskList.ToArray());
|
||||
var result = batch.SelectMany(x => x).ToList();
|
||||
|
||||
_logger.Debug("Found {0} reports", result.Count);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<IList<ReleaseInfo>> FetchIndexer(IIndexer indexer)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await indexer.FetchRecent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Error during RSS Sync");
|
||||
}
|
||||
|
||||
return Array.Empty<ReleaseInfo>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetRequest("search-torrents", Settings.Categories, string.Format("&type=name&query={0}+{1}", Uri.EscapeDataString(searchCriteria.AuthorQuery.Trim()), Uri.EscapeDataString(searchCriteria.BookQuery.Trim()))));
|
||||
var authorQuery = searchCriteria.AuthorQuery.Replace("+", " ").Trim();
|
||||
var bookQuery = searchCriteria.BookQuery.Replace("+", " ").Trim();
|
||||
|
||||
pageableRequests.Add(GetRequest("search-torrents", Settings.Categories, string.Format("&type=name&query={0}+{1}", Uri.EscapeDataString(authorQuery), Uri.EscapeDataString(bookQuery))));
|
||||
pageableRequests.Add(GetRequest("search-torrents", Settings.Categories, string.Format("&type=name&query={0}+{1}", Uri.EscapeDataString(bookQuery), Uri.EscapeDataString(authorQuery))));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -32,7 +36,9 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetRequest("search-torrents", Settings.Categories, string.Format("&type=name&query={0}", Uri.EscapeDataString(searchCriteria.AuthorQuery.Trim()))));
|
||||
var authorQuery = searchCriteria.AuthorQuery.Replace("+", " ").Trim();
|
||||
|
||||
pageableRequests.Add(GetRequest("search-torrents", Settings.Categories, string.Format("&type=name&query={0}", Uri.EscapeDataString(authorQuery))));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
|
||||
Categories = new int[]
|
||||
{
|
||||
(int)FileListCategories.DOCS
|
||||
(int)FileListCategories.Docs
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
|
||||
public enum FileListCategories
|
||||
{
|
||||
[FieldOption]
|
||||
DOCS = 16,
|
||||
[FieldOption(Label = "Docs")]
|
||||
Docs = 16,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
@@ -68,11 +69,12 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
return settings;
|
||||
}
|
||||
|
||||
protected override void Test(List<ValidationFailure> failures)
|
||||
protected override async Task Test(List<ValidationFailure> failures)
|
||||
{
|
||||
// Remove previous cookies when testing incase user or pwd change
|
||||
_authCookieCache.Remove(Settings.BaseUrl.Trim().TrimEnd('/'));
|
||||
base.Test(failures);
|
||||
|
||||
await base.Test(failures);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -43,17 +44,14 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
|
||||
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
|
||||
{
|
||||
Authenticate();
|
||||
Authenticate().GetAwaiter().GetResult();
|
||||
|
||||
var filter = "";
|
||||
if (searchParameters == null)
|
||||
{
|
||||
}
|
||||
|
||||
var request =
|
||||
new IndexerRequest(
|
||||
$"{Settings.BaseUrl.Trim().TrimEnd('/')}/ajax.php?action=browse&searchstr={searchParameters}{filter}",
|
||||
HttpAccept.Json);
|
||||
var request = new IndexerRequest($"{Settings.BaseUrl.Trim().TrimEnd('/')}/ajax.php?action=browse&searchstr={searchParameters}{filter}", HttpAccept.Json);
|
||||
|
||||
var cookies = AuthCookieCache.Find(Settings.BaseUrl.Trim().TrimEnd('/'));
|
||||
foreach (var cookie in cookies)
|
||||
@@ -64,36 +62,36 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
yield return request;
|
||||
}
|
||||
|
||||
private GazelleAuthResponse GetIndex(Dictionary<string, string> cookies)
|
||||
private async Task<GazelleAuthResponse> GetIndex(Dictionary<string, string> cookies)
|
||||
{
|
||||
var indexRequestBuilder = new HttpRequestBuilder($"{Settings.BaseUrl.Trim().TrimEnd('/')}")
|
||||
{
|
||||
LogResponseContent = true
|
||||
LogResponseContent = true,
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
|
||||
indexRequestBuilder.SetCookies(cookies);
|
||||
indexRequestBuilder.Method = HttpMethod.Post;
|
||||
indexRequestBuilder.Resource("ajax.php?action=index");
|
||||
|
||||
var authIndexRequest = indexRequestBuilder
|
||||
.Accept(HttpAccept.Json)
|
||||
.Build();
|
||||
|
||||
var indexResponse = HttpClient.Execute(authIndexRequest);
|
||||
var indexResponse = await HttpClient.ExecuteAsync(authIndexRequest);
|
||||
|
||||
var result = Json.Deserialize<GazelleAuthResponse>(indexResponse.Content);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void Authenticate()
|
||||
private async Task Authenticate()
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder($"{Settings.BaseUrl.Trim().TrimEnd('/')}")
|
||||
{
|
||||
LogResponseContent = true
|
||||
LogResponseContent = true,
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
|
||||
requestBuilder.Method = HttpMethod.Post;
|
||||
requestBuilder.Resource("login.php");
|
||||
requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15);
|
||||
|
||||
@@ -111,19 +109,20 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
.Accept(HttpAccept.Json)
|
||||
.Build();
|
||||
|
||||
var response = HttpClient.Execute(authLoginRequest);
|
||||
var response = await HttpClient.ExecuteAsync(authLoginRequest);
|
||||
|
||||
cookies = response.GetCookies();
|
||||
|
||||
AuthCookieCache.Set(authKey, cookies);
|
||||
}
|
||||
|
||||
var index = GetIndex(cookies);
|
||||
var index = await GetIndex(cookies);
|
||||
|
||||
if (index == null || index.Status.IsNullOrWhiteSpace() || index.Status != "success")
|
||||
{
|
||||
Logger.Debug("Gazelle authentication failed.");
|
||||
AuthCookieCache.Remove(authKey);
|
||||
|
||||
throw new Exception("Failed to authenticate with Gazelle.");
|
||||
}
|
||||
|
||||
|
||||
@@ -40,31 +40,31 @@ namespace NzbDrone.Core.Indexers
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public override IList<ReleaseInfo> FetchRecent()
|
||||
public override Task<IList<ReleaseInfo>> FetchRecent()
|
||||
{
|
||||
if (!SupportsRss)
|
||||
{
|
||||
return new List<ReleaseInfo>();
|
||||
return Task.FromResult<IList<ReleaseInfo>>(Array.Empty<ReleaseInfo>());
|
||||
}
|
||||
|
||||
return FetchReleases(g => g.GetRecentRequests(), true);
|
||||
}
|
||||
|
||||
public override IList<ReleaseInfo> Fetch(BookSearchCriteria searchCriteria)
|
||||
public override Task<IList<ReleaseInfo>> Fetch(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
if (!SupportsSearch)
|
||||
{
|
||||
return new List<ReleaseInfo>();
|
||||
return Task.FromResult<IList<ReleaseInfo>>(Array.Empty<ReleaseInfo>());
|
||||
}
|
||||
|
||||
return FetchReleases(g => g.GetSearchRequests(searchCriteria));
|
||||
}
|
||||
|
||||
public override IList<ReleaseInfo> Fetch(AuthorSearchCriteria searchCriteria)
|
||||
public override Task<IList<ReleaseInfo>> Fetch(AuthorSearchCriteria searchCriteria)
|
||||
{
|
||||
if (!SupportsSearch)
|
||||
{
|
||||
return new List<ReleaseInfo>();
|
||||
return Task.FromResult<IList<ReleaseInfo>>(Array.Empty<ReleaseInfo>());
|
||||
}
|
||||
|
||||
return FetchReleases(g => g.GetSearchRequests(searchCriteria));
|
||||
@@ -75,7 +75,7 @@ namespace NzbDrone.Core.Indexers
|
||||
return new HttpRequest(link);
|
||||
}
|
||||
|
||||
protected virtual IList<ReleaseInfo> FetchReleases(Func<IIndexerRequestGenerator, IndexerPageableRequestChain> pageableRequestChainSelector, bool isRecent = false)
|
||||
protected virtual async Task<IList<ReleaseInfo>> FetchReleases(Func<IIndexerRequestGenerator, IndexerPageableRequestChain> pageableRequestChainSelector, bool isRecent = false)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var url = string.Empty;
|
||||
@@ -107,7 +107,7 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
url = request.Url.FullUri;
|
||||
|
||||
var page = FetchPage(request, parser);
|
||||
var page = await FetchPage(request, parser);
|
||||
|
||||
pagedReleases.AddRange(page);
|
||||
|
||||
@@ -257,9 +257,17 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
protected virtual bool IsValidRelease(ReleaseInfo release)
|
||||
{
|
||||
if (release.Title.IsNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Trace("Invalid Release: '{0}' from indexer: {1}. No title provided.", release.InfoUrl, Definition.Name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (release.DownloadUrl.IsNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Trace("Invalid Release: '{0}' from indexer: {1}. No Download URL provided.", release.Title, release.Indexer);
|
||||
_logger.Trace("Invalid Release: '{0}' from indexer: {1}. No Download URL provided.", release.Title, Definition.Name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -271,9 +279,9 @@ namespace NzbDrone.Core.Indexers
|
||||
return PageSize != 0 && page.Count >= PageSize;
|
||||
}
|
||||
|
||||
protected virtual IList<ReleaseInfo> FetchPage(IndexerRequest request, IParseIndexerResponse parser)
|
||||
protected virtual async Task<IList<ReleaseInfo>> FetchPage(IndexerRequest request, IParseIndexerResponse parser)
|
||||
{
|
||||
var response = FetchIndexerResponse(request);
|
||||
var response = await FetchIndexerResponse(request);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -287,7 +295,7 @@ namespace NzbDrone.Core.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IndexerResponse FetchIndexerResponse(IndexerRequest request)
|
||||
protected virtual async Task<IndexerResponse> FetchIndexerResponse(IndexerRequest request)
|
||||
{
|
||||
_logger.Debug("Downloading Feed " + request.HttpRequest.ToString(false));
|
||||
|
||||
@@ -298,15 +306,17 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
request.HttpRequest.RateLimitKey = Definition.Id.ToString();
|
||||
|
||||
return new IndexerResponse(request, _httpClient.Execute(request.HttpRequest));
|
||||
var response = await _httpClient.ExecuteAsync(request.HttpRequest);
|
||||
|
||||
return new IndexerResponse(request, response);
|
||||
}
|
||||
|
||||
protected override void Test(List<ValidationFailure> failures)
|
||||
protected override async Task Test(List<ValidationFailure> failures)
|
||||
{
|
||||
failures.AddIfNotNull(TestConnection());
|
||||
failures.AddIfNotNull(await TestConnection());
|
||||
}
|
||||
|
||||
protected virtual ValidationFailure TestConnection()
|
||||
protected virtual async Task<ValidationFailure> TestConnection()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -319,7 +329,7 @@ namespace NzbDrone.Core.Indexers
|
||||
return new ValidationFailure(string.Empty, "No rss feed query available. This may be an issue with the indexer or your indexer category settings.");
|
||||
}
|
||||
|
||||
var releases = FetchPage(firstRequest, parser);
|
||||
var releases = await FetchPage(firstRequest, parser);
|
||||
|
||||
if (releases.Empty())
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@@ -12,9 +13,9 @@ namespace NzbDrone.Core.Indexers
|
||||
bool SupportsSearch { get; }
|
||||
DownloadProtocol Protocol { get; }
|
||||
|
||||
IList<ReleaseInfo> FetchRecent();
|
||||
IList<ReleaseInfo> Fetch(BookSearchCriteria searchCriteria);
|
||||
IList<ReleaseInfo> Fetch(AuthorSearchCriteria searchCriteria);
|
||||
Task<IList<ReleaseInfo>> FetchRecent();
|
||||
Task<IList<ReleaseInfo>> Fetch(BookSearchCriteria searchCriteria);
|
||||
Task<IList<ReleaseInfo>> Fetch(AuthorSearchCriteria searchCriteria);
|
||||
HttpRequest GetDownloadRequest(string link);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
@@ -66,16 +67,14 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
protected TSettings Settings => (TSettings)Definition.Settings;
|
||||
|
||||
public abstract IList<ReleaseInfo> FetchRecent();
|
||||
|
||||
public abstract IList<ReleaseInfo> Fetch(BookSearchCriteria searchCriteria);
|
||||
public abstract IList<ReleaseInfo> Fetch(AuthorSearchCriteria searchCriteria);
|
||||
public abstract Task<IList<ReleaseInfo>> FetchRecent();
|
||||
public abstract Task<IList<ReleaseInfo>> Fetch(BookSearchCriteria searchCriteria);
|
||||
public abstract Task<IList<ReleaseInfo>> Fetch(AuthorSearchCriteria searchCriteria);
|
||||
public abstract HttpRequest GetDownloadRequest(string link);
|
||||
|
||||
protected virtual IList<ReleaseInfo> CleanupReleases(IEnumerable<ReleaseInfo> releases)
|
||||
{
|
||||
var result = releases.DistinctBy(v => v.Guid).ToList();
|
||||
var settings = Definition.Settings as IIndexerSettings;
|
||||
|
||||
result.ForEach(c =>
|
||||
{
|
||||
@@ -94,7 +93,7 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
try
|
||||
{
|
||||
Test(failures);
|
||||
Test(failures).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -105,7 +104,7 @@ namespace NzbDrone.Core.Indexers
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
|
||||
protected abstract void Test(List<ValidationFailure> failures);
|
||||
protected abstract Task Test(List<ValidationFailure> failures);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -92,9 +93,10 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
return settings;
|
||||
}
|
||||
|
||||
protected override void Test(List<ValidationFailure> failures)
|
||||
protected override async Task Test(List<ValidationFailure> failures)
|
||||
{
|
||||
base.Test(failures);
|
||||
await base.Test(failures);
|
||||
|
||||
if (failures.HasErrors())
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -7,6 +7,7 @@ using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
@@ -73,6 +74,13 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
_logger.Debug(ex, "Failed to parse newznab api capabilities for {0}", indexerSettings.BaseUrl);
|
||||
throw;
|
||||
}
|
||||
catch (ApiKeyException ex)
|
||||
{
|
||||
ex.WithData(response, 128 * 1024);
|
||||
_logger.Trace("Unexpected Response content ({0} bytes): {1}", response.ResponseData.Length, response.Content);
|
||||
_logger.Debug(ex, "Failed to parse newznab api capabilities for {0}, invalid API key", indexerSettings.BaseUrl);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.WithData(response, 128 * 1024);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
@@ -39,16 +40,16 @@ namespace NzbDrone.Core.Indexers
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private ProcessedDecisions Sync()
|
||||
private async Task<ProcessedDecisions> Sync()
|
||||
{
|
||||
_logger.ProgressInfo("Starting RSS Sync");
|
||||
|
||||
var rssReleases = _rssFetcherAndParser.Fetch();
|
||||
var rssReleases = await _rssFetcherAndParser.Fetch();
|
||||
var pendingReleases = _pendingReleaseService.GetPending();
|
||||
|
||||
var reports = rssReleases.Concat(pendingReleases).ToList();
|
||||
var decisions = _downloadDecisionMaker.GetRssDecision(reports);
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
var processed = await _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
|
||||
var message = string.Format("RSS Sync Completed. Reports found: {0}, Reports grabbed: {1}", reports.Count, processed.Grabbed.Count);
|
||||
|
||||
@@ -64,7 +65,7 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
public void Execute(RssSyncCommand message)
|
||||
{
|
||||
var processed = Sync();
|
||||
var processed = Sync().GetAwaiter().GetResult();
|
||||
var grabbedOrPending = processed.Grabbed.Concat(processed.Pending).ToList();
|
||||
|
||||
_eventAggregator.PublishEvent(new RssSyncCompleteEvent(processed));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -69,9 +70,10 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
return settings;
|
||||
}
|
||||
|
||||
protected override void Test(List<ValidationFailure> failures)
|
||||
protected override async Task Test(List<ValidationFailure> failures)
|
||||
{
|
||||
base.Test(failures);
|
||||
await base.Test(failures);
|
||||
|
||||
if (failures.HasErrors())
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -626,5 +626,7 @@
|
||||
"AddNew": "Добави нов",
|
||||
"NextExecution": "Следващо изпълнение",
|
||||
"AllResultsAreHiddenByTheAppliedFilter": "Всички резултати са скрити от приложения филтър",
|
||||
"Backup": "Архивиране"
|
||||
"Backup": "Архивиране",
|
||||
"MetadataProfiles": "Добави профил на метадата",
|
||||
"MetadataProfile": "Добави профил на метадата"
|
||||
}
|
||||
|
||||
@@ -13,36 +13,36 @@
|
||||
"45MinutesFourtyFive": "90 minut: {0}",
|
||||
"60MinutesSixty": "60 minut: {0}",
|
||||
"APIKey": "Klíč API",
|
||||
"About": "O",
|
||||
"About": "O aplikaci",
|
||||
"AddListExclusion": "Přidat vyloučení seznamu",
|
||||
"AddingTag": "Přidávání značky",
|
||||
"AgeWhenGrabbed": "Stáří (při zachycení)",
|
||||
"AlreadyInYourLibrary": "Již ve vaší knihovně",
|
||||
"AddingTag": "Přidání značky",
|
||||
"AgeWhenGrabbed": "Stáří (kdy bylo získáno)",
|
||||
"AlreadyInYourLibrary": "Již máte ve své knihovně",
|
||||
"AlternateTitles": "Alternativní název",
|
||||
"Analytics": "Analytics",
|
||||
"Analytics": "Analýzy",
|
||||
"AnalyticsEnabledHelpText": "Odesílejte anonymní informace o použití a chybách na servery Radarru. To zahrnuje informace o vašem prohlížeči, které stránky Radarr WebUI používáte, hlášení chyb a také verzi operačního systému a běhového prostředí. Tyto informace použijeme k upřednostnění funkcí a oprav chyb.",
|
||||
"AppDataDirectory": "Adresář AppData",
|
||||
"ApplyTags": "Použít značky",
|
||||
"Authentication": "Ověření",
|
||||
"Authentication": "Ověřování",
|
||||
"AuthenticationMethodHelpText": "Vyžadovat uživatelské jméno a heslo pro přístup k Radarr",
|
||||
"AuthorClickToChangeBook": "Kliknutím změníte film",
|
||||
"AutoRedownloadFailedHelpText": "Automaticky vyhledejte a pokuste se stáhnout jiné vydání",
|
||||
"AutoRedownloadFailedHelpText": "Automatické vyhledání a pokus o stažení jiného vydání",
|
||||
"AutoUnmonitorPreviouslyDownloadedBooksHelpText": "Filmy odstraněné z disku jsou automaticky sledovány v Radarru",
|
||||
"Automatic": "Automatický",
|
||||
"BackupFolderHelpText": "Relativní cesty budou v adresáři AppData společnosti Radarr",
|
||||
"BackupNow": "Zálohovat hned",
|
||||
"BackupRetentionHelpText": "Automatické zálohy starší než doba uchování budou automaticky vyčištěny",
|
||||
"BackupNow": "Ihned zálohovat",
|
||||
"BackupRetentionHelpText": "Automatické zálohy starší než doba uchovávání budou automaticky vyčištěny",
|
||||
"Backups": "Zálohy",
|
||||
"BindAddress": "Vazba adresy",
|
||||
"BindAddressHelpText": "Platná adresa IP4 nebo '*' pro všechna rozhraní",
|
||||
"BindAddress": "Vázat adresu",
|
||||
"BindAddressHelpText": "Platná IP adresa, localhost nebo '*' pro všechna rozhraní",
|
||||
"BindAddressHelpTextWarning": "Vyžaduje restart, aby se projevilo",
|
||||
"BookIsDownloading": "Film se stahuje",
|
||||
"BookIsDownloadingInterp": "Film se stahuje - {0}% {1}",
|
||||
"Branch": "Větev",
|
||||
"BypassProxyForLocalAddresses": "Obejít proxy pro místní adresy",
|
||||
"BypassProxyForLocalAddresses": "Obcházení proxy serveru pro místní adresy",
|
||||
"Calendar": "Kalendář",
|
||||
"CalendarWeekColumnHeaderHelpText": "Zobrazuje se nad každým sloupcem, když je aktivní zobrazení týden",
|
||||
"Cancel": "zrušení",
|
||||
"Cancel": "Zrušit",
|
||||
"CancelMessageText": "Opravdu chcete zrušit tento nevyřízený úkol?",
|
||||
"CertificateValidation": "Ověření certifikátu",
|
||||
"CertificateValidationHelpText": "Změňte přísnost ověřování certifikátů HTTPS. Neměňte, pokud nerozumíte rizikům.",
|
||||
@@ -53,33 +53,33 @@
|
||||
"ChmodFolderHelpTextWarning": "Funguje to pouze v případě, že je uživatel souboru Radarr vlastníkem souboru. Je lepší zajistit, aby stahovací klient správně nastavil oprávnění.",
|
||||
"ChownGroupHelpText": "Název skupiny nebo gid. Použijte gid pro vzdálené systémy souborů.",
|
||||
"ChownGroupHelpTextWarning": "Funguje to pouze v případě, že je uživatel souboru Radarr vlastníkem souboru. Je lepší zajistit, aby stahovací klient používal stejnou skupinu jako Radarr.",
|
||||
"Clear": "Průhledná",
|
||||
"ClickToChangeQuality": "Klepnutím změníte kvalitu",
|
||||
"Clear": "Vyčistit",
|
||||
"ClickToChangeQuality": "Kliknutím změníte kvalitu",
|
||||
"ClientPriority": "Priorita klienta",
|
||||
"CloneIndexer": "Klonovat indexátor",
|
||||
"CloneProfile": "Klonovat profil",
|
||||
"Close": "Zavřít",
|
||||
"Columns": "Sloupce",
|
||||
"CompletedDownloadHandling": "Zpracování stahování bylo dokončeno",
|
||||
"ConnectSettings": "Připojit nastavení",
|
||||
"ConnectSettings": "Nastavení připojení",
|
||||
"Connections": "Připojení",
|
||||
"CopyUsingHardlinksHelpText": "Hardlinks použijte, když se pokoušíte kopírovat soubory z torrentů, které se stále používají",
|
||||
"CopyUsingHardlinksHelpTextWarning": "Zámky souborů mohou občas zabránit přejmenování souborů, které se právě vysazují. Výsev můžete dočasně deaktivovat a použít funkci Radarr pro přejmenování.",
|
||||
"CreateEmptyAuthorFoldersHelpText": "Během skenování disku vytvářejte chybějící složky filmů",
|
||||
"CreateGroup": "Vytvořit skupinu",
|
||||
"CutoffHelpText": "Jakmile je této kvality dosaženo, Radarr již nebude stahovat filmy",
|
||||
"CutoffUnmet": "Cut-off Unmet",
|
||||
"CutoffUnmet": "Vynechat nevyhovující",
|
||||
"DBMigration": "Migrace databáze",
|
||||
"Dates": "Termíny",
|
||||
"DelayProfile": "Zpožděný profil",
|
||||
"DelayProfile": "Profil zpoždění",
|
||||
"DelayProfiles": "Profily zpoždění",
|
||||
"DelayingDownloadUntilInterp": "Zpoždění stahování do {0} o {1}",
|
||||
"Delete": "Vymazat",
|
||||
"DeleteBackup": "Odstranit zálohu",
|
||||
"DeleteBackupMessageText": "Opravdu chcete smazat zálohu „{0}“?",
|
||||
"DeleteDelayProfile": "Smazat profil zpoždění",
|
||||
"Delete": "Smazat",
|
||||
"DeleteBackup": "Odstranění zálohy",
|
||||
"DeleteBackupMessageText": "Opravdu chcete odstranit zálohu '{name}'?",
|
||||
"DeleteDelayProfile": "Odstranění profilu zpoždění",
|
||||
"DeleteDelayProfileMessageText": "Opravdu chcete smazat tento profil zpoždění?",
|
||||
"DeleteDownloadClient": "Odstranit staženého klienta",
|
||||
"DeleteDownloadClient": "Odstranění klienta pro stahování",
|
||||
"DeleteDownloadClientMessageText": "Opravdu chcete odstranit klienta pro stahování „{0}“?",
|
||||
"DeleteEmptyFolders": "Odstraňte prázdné složky",
|
||||
"DeleteEmptyFoldersHelpText": "Během skenování disku a při mazání filmových souborů odstraňte prázdné složky s filmy",
|
||||
@@ -455,12 +455,12 @@
|
||||
"ShowTitle": "Ukázat nadpis",
|
||||
"RemoveFromBlocklist": "Odebrat z černé listiny",
|
||||
"UnableToLoadBlocklist": "Nelze načíst černou listinu",
|
||||
"Component": "Součástka",
|
||||
"Component": "Komponenta",
|
||||
"Level": "Úroveň",
|
||||
"ReleaseBranchCheckOfficialBranchMessage": "Pobočka {0} není platná větev vydání Radarr, nebudete dostávat aktualizace",
|
||||
"Time": "Čas",
|
||||
"Blocklist": "Černá listina",
|
||||
"BlocklistRelease": "Vydání černé listiny",
|
||||
"Blocklist": "Blocklist",
|
||||
"BlocklistRelease": "Blocklist pro vydání",
|
||||
"ShowUnknownAuthorItems": "Zobrazit neznámé položky filmu",
|
||||
"ThisCannotBeCancelled": "Toto nelze zrušit po spuštění bez restartování Radarru.",
|
||||
"UnselectAll": "Odznačit vše",
|
||||
@@ -485,7 +485,7 @@
|
||||
"SettingsRemotePathMappingRemotePath": "Vzdálená cesta",
|
||||
"UISettingsSummary": "Možnosti kalendáře, data a barev",
|
||||
"DownloadClientsSettingsSummary": "Stahování klientů, zpracování stahování a mapování vzdálených cest",
|
||||
"AppDataLocationHealthCheckMessage": "Aktualizace nebude možné zabránit smazání AppData při aktualizaci",
|
||||
"AppDataLocationHealthCheckMessage": "Aktualizace nebude možná, aby se zabránilo odstranění AppData při aktualizaci",
|
||||
"FileWasDeletedByViaUI": "Soubor byl odstraněn prostřednictvím uživatelského rozhraní",
|
||||
"IndexersSettingsSummary": "Indexery a omezení vydání",
|
||||
"IndexerStatusCheckSingleClientMessage": "Indexery nedostupné z důvodu selhání: {0}",
|
||||
@@ -556,7 +556,7 @@
|
||||
"Conditions": "Podmínky",
|
||||
"CopyToClipboard": "Zkopírovat do schránky",
|
||||
"CustomFormat": "Vlastní formát",
|
||||
"DeleteCustomFormat": "Odstranit vlastní formát",
|
||||
"DeleteCustomFormat": "Odstranění vlastního formátu",
|
||||
"DeleteCustomFormatMessageText": "Opravdu chcete odstranit indexer „{0}“?",
|
||||
"ExportCustomFormat": "Exportovat vlastní formát",
|
||||
"Formats": "Formáty",
|
||||
@@ -573,14 +573,14 @@
|
||||
"HideAdvanced": "Skrýt pokročilé",
|
||||
"ShowAdvanced": "Zobrazit pokročilé",
|
||||
"ShownClickToHide": "Zobrazeno, kliknutím se skryjete",
|
||||
"ColonReplacement": "Výměna tlustého střeva",
|
||||
"ColonReplacement": "Nahrazení dvojtečky",
|
||||
"ReplaceWithDash": "Nahraďte Dash",
|
||||
"ReplaceWithSpaceDashSpace": "Nahraďte Space Dash Space",
|
||||
"ReplaceWithSpaceDash": "Nahraďte Space Dash",
|
||||
"DeleteRemotePathMapping": "Upravit vzdálené mapování cesty",
|
||||
"DeleteRemotePathMappingMessageText": "Opravdu chcete toto vzdálené mapování cesty odstranit?",
|
||||
"Negated": "Negovaný",
|
||||
"BlocklistReleases": "Vydání černé listiny",
|
||||
"BlocklistReleases": "Blocklist pro vydání",
|
||||
"DeleteConditionMessageText": "Opravdu chcete smazat značku „{0}“?",
|
||||
"RemoveSelectedItemBlocklistMessageText": "Opravdu chcete odebrat vybrané položky z černé listiny?",
|
||||
"RemoveSelectedItemQueueMessageText": "Opravdu chcete odebrat {0} položku {1} z fronty?",
|
||||
@@ -597,12 +597,12 @@
|
||||
"NoChange": "Žádná změna",
|
||||
"RemovingTag": "Odebírání značky",
|
||||
"SetTags": "Nastavit značky",
|
||||
"ApplyTagsHelpTextAdd": "Přidat: Přidejte značky do existujícího seznamu značek",
|
||||
"ApplyTagsHelpTextHowToApplyDownloadClients": "Jak použít značky na vybrané filmy",
|
||||
"ApplyTagsHelpTextHowToApplyImportLists": "Jak použít značky na vybrané filmy",
|
||||
"ApplyTagsHelpTextHowToApplyIndexers": "Jak použít značky na vybrané filmy",
|
||||
"ApplyTagsHelpTextRemove": "Odebrat: Odebere zadané značky",
|
||||
"ApplyTagsHelpTextReplace": "Nahradit: Nahradit tagy zadanými tagy (pro vymazání všech tagů zadejte žádné tagy)",
|
||||
"ApplyTagsHelpTextAdd": "Přidat: Přidá značky k již existujícímu seznamu",
|
||||
"ApplyTagsHelpTextHowToApplyDownloadClients": "Jak použít značky na vybrané klienty pro stahování",
|
||||
"ApplyTagsHelpTextHowToApplyImportLists": "Jak použít značky na vybrané importní seznamy",
|
||||
"ApplyTagsHelpTextHowToApplyIndexers": "Jak použít značky na vybrané indexátory",
|
||||
"ApplyTagsHelpTextRemove": "Odebrat: Odebrat zadané značky",
|
||||
"ApplyTagsHelpTextReplace": "Nahradit: Nahradit značky zadanými značkami (zadáním žádné značky vymažete všechny značky)",
|
||||
"DeleteSelectedDownloadClients": "Odstranit staženého klienta",
|
||||
"DeleteSelectedIndexersMessageText": "Opravdu chcete odstranit indexer „{0}“?",
|
||||
"Yes": "Ano",
|
||||
@@ -617,19 +617,51 @@
|
||||
"Medium": "Střední",
|
||||
"NoResultsFound": "Nebyly nalezeny žádné výsledky",
|
||||
"SomeResultsAreHiddenByTheAppliedFilter": "Některé výsledky jsou použitým filtrem skryty",
|
||||
"AllResultsAreHiddenByTheAppliedFilter": "Všechny výsledky jsou skryty použitým filtrem",
|
||||
"AllResultsAreHiddenByTheAppliedFilter": "Všechny výsledky jsou schovány použitým filtrem",
|
||||
"Events": "Události",
|
||||
"FreeSpace": "Volný prostor",
|
||||
"System": "Systém",
|
||||
"TotalSpace": "Celkový prostor",
|
||||
"ConnectionLost": "Spojení ztraceno",
|
||||
"ConnectionLostReconnect": "Radarr se pokusí připojit automaticky, nebo můžete kliknout na znovu načíst níže.",
|
||||
"ConnectionLostToBackend": "Radarr ztratil spojení s back-endem a pro obnovení funkčnosti bude nutné jej znovu načíst.",
|
||||
"ConnectionLostReconnect": "{appName} se pokusí připojit automaticky, nebo můžete kliknout na tlačítko znovunačtení níže.",
|
||||
"ConnectionLostToBackend": "{appName} ztratila spojení s backendem a pro obnovení funkčnosti bude třeba ji znovu načíst.",
|
||||
"Large": "Velký",
|
||||
"LastDuration": "lastDuration",
|
||||
"Ui": "UI",
|
||||
"WhatsNew": "Co je nového?",
|
||||
"Activity": "Aktivita",
|
||||
"AddNew": "Přidat nový",
|
||||
"NextExecution": "Další spuštění"
|
||||
"AddNew": "Přidat nové",
|
||||
"NextExecution": "Další spuštění",
|
||||
"ClickToChangeReleaseGroup": "Kliknutím změníte skupinu vydání",
|
||||
"ApplicationURL": "URL aplikace",
|
||||
"ApplicationUrlHelpText": "Externí adresa URL této aplikace včetně http(s)://, portu a základní adresy URL",
|
||||
"Continuing": "Pokračující",
|
||||
"AutomaticUpdatesDisabledDocker": "Automatické aktualizace nejsou při použití aktualizačního mechanismu Docker přímo podporovány. Obraz kontejneru je nutné aktualizovat mimo {appName} nebo použít skript",
|
||||
"AppUpdated": "{appName} aktualizován",
|
||||
"ApplyChanges": "Použít změny",
|
||||
"AutoAdd": "Přidat automaticky",
|
||||
"AutomaticAdd": "Přidat automaticky",
|
||||
"ChownGroup": "Skupina chown",
|
||||
"CloneCondition": "Klonovat podmínku",
|
||||
"Clone": "Klonovat",
|
||||
"ApiKeyValidationHealthCheckMessage": "Aktualizujte svůj klíč API tak, aby měl alespoň {0} znaků. Můžete to provést prostřednictvím nastavení nebo konfiguračního souboru",
|
||||
"ChooseImportMethod": "Vyberte mód importu",
|
||||
"CatalogNumber": "katalogové číslo",
|
||||
"Publisher": "Vydavatel",
|
||||
"StatusEndedContinuing": "Pokračující",
|
||||
"MetadataProfiles": "profil metadat",
|
||||
"ReleaseProfiles": "profil vydání",
|
||||
"MetadataProfile": "profil metadat",
|
||||
"Label": "Etiketa",
|
||||
"Library": "Knihovna",
|
||||
"BypassIfAboveCustomFormatScore": "Obejít, pokud je vyšší než skóre vlastního formátu",
|
||||
"AppUpdatedVersion": "{appName} byla aktualizována na verzi `{version}`, abyste získali nejnovější změny, musíte znovu načíst {appName}.",
|
||||
"BypassIfAboveCustomFormatScoreHelpText": "Povolit obcházení, pokud má vydání vyšší skóre, než je nakonfigurované minimální skóre vlastního formátu",
|
||||
"BypassIfHighestQuality": "Obejít v případě nejvyšší kvality",
|
||||
"Theme": "Motiv",
|
||||
"MinimumCustomFormatScoreHelpText": "Minimální skóre vlastního formátu požadované pro obejití zpoždění preferovaného protokolu",
|
||||
"Series": "Seriál",
|
||||
"DeleteCondition": "Odstranit podmínku",
|
||||
"Database": "Databáze",
|
||||
"CountDownloadClientsSelected": "{count} vybraných klientů ke stahování"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user