Compare commits

...

61 Commits

Author SHA1 Message Date
Qstick
a8fdd46cd3 Update NewznabController.cs 2021-06-12 19:41:40 -04:00
Qstick
b6a08bdd9e Fixed: Accept ImdbIds in string format on Newznab endpoint 2021-06-12 18:02:10 -04:00
Qstick
59df0351ac New: (Indexer) YTS 2021-06-12 17:11:36 -04:00
Qstick
0fd242cd62 Additional help text for Download Clients 2021-06-12 16:45:19 -04:00
Qstick
ed7c5a937f Fixed: Correctly handle 302 and 303 redirects in HttpClient
Fixes #204
2021-06-12 16:24:48 -04:00
ntldr0
f6906d0f18 Fixed: (Cardigann) fix checkbox configuration (#169)
In ApplyGoTemplateText(), boolean values are resolved by using IsNullOrWhiteSpace().  Since ".False" is neither null or whitespace, ".False" always resolves to true.
2021-06-12 16:23:53 -04:00
Qstick
561563b48c Fixed: (Avistaz) Empty query failing with error
Fixes #192
2021-06-12 15:17:43 -04:00
Servarr
e383036c84 Translations update from Weblate (#148)
* Translated using Weblate (French)

Currently translated at 100.0% (373 of 373 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (373 of 373 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/

* Translated using Weblate (German)

Currently translated at 98.9% (369 of 373 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/de/

* Translated using Weblate (French)

Currently translated at 100.0% (373 of 373 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/

* Translated using Weblate (Chinese (Simplified) (zh_CN))

Currently translated at 5.0% (19 of 373 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/zh_CN/

* Translated using Weblate (German)

Currently translated at 100.0% (384 of 384 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/de/

* Translated using Weblate (German)

Currently translated at 100.0% (384 of 384 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/de/

* Translated using Weblate (German)

Currently translated at 100.0% (384 of 384 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/de/

* Translated using Weblate (French)

Currently translated at 100.0% (384 of 384 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (384 of 384 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/hu/

* Translated using Weblate (Portuguese)

Currently translated at 75.5% (290 of 384 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (384 of 384 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt_BR/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (417 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (417 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/hu/

* Translated using Weblate (Italian)

Currently translated at 81.7% (341 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/it/

* Translated using Weblate (Dutch)

Currently translated at 84.1% (351 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/nl/

* Translated using Weblate (French)

Currently translated at 94.0% (392 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (417 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (417 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt_BR/

* Translated using Weblate (Portuguese)

Currently translated at 76.2% (318 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt/

* Translated using Weblate (Portuguese)

Currently translated at 76.2% (318 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt/

* Translated using Weblate (Spanish)

Currently translated at 81.7% (341 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/es/

* Translated using Weblate (German)

Currently translated at 94.0% (392 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/de/

* Translated using Weblate (German)

Currently translated at 94.0% (392 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/de/

* Translated using Weblate (French)

Currently translated at 100.0% (417 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/

* Translated using Weblate (Portuguese)

Currently translated at 76.2% (318 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (417 of 417 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt_BR/

Co-authored-by: foXaCe <foxace66@gmail.com>
Co-authored-by: Hummingbirdy The Second <hummingbirdy@pm.me>
Co-authored-by: doob187 <amderkum@gmail.com>
Co-authored-by: muihiuwev <muihiuwev@outlook.com>
Co-authored-by: reloxx <reloxx@interia.pl>
Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Co-authored-by: Anonymous <noreply@weblate.org>
2021-06-12 14:52:55 -04:00
Zippy79
202f439a60 Fixed: Newznab additional parameters (#189) 2021-06-12 14:50:34 -04:00
nitsua
449e60afc0 Fix broken health check level so it is not always "Type" for Notifiarr connect (#202) 2021-06-12 14:49:44 -04:00
Qstick
9675171aff Fixed: (Cardigann) Don't die if no categories can be mapped for a release
Fixes #105
2021-06-12 02:17:17 -04:00
Qstick
bcee5f1754 Fixed: (Torznab) Categories not parsed correctly
Fixes #181
2021-06-12 01:53:22 -04:00
Qstick
d1a3e61979 Fixed: Many torrent indexers use full ImdbId instead of int version
Fixes #192
2021-06-12 01:37:01 -04:00
Qstick
b4a0c272c9 New: (indexer) Newz69
Fixes #198
2021-06-11 23:50:56 -04:00
nitsua
75cbabf716 Add detailed help text for sync levels (#155)
Adjust donation buttons to fit on mobile
Other translation updates
2021-06-11 15:42:32 -04:00
bakerboy448
6612202384 Fixed: App not syncing correct if only Interactive Search enabled (#197)
Fixes #196
2021-06-11 15:42:00 -04:00
HDVinnie
6b52dd6e7a (Indexers) Add Support for Aither via UNIT3D API (#176)
* (Indexers) Add Support for Aither via UNIT3D API

- Added support for Aither via Unit3d API

- Updated IndexerDefinitionUpdateService.cs

* fix: aither definition

* fix: aither definition

* fix: line ending

* Tmp vinnie aithia (#1)

* (Indexers) Add Support for Aither via UNIT3D API

- Added support for Aither via Unit3d API

* fix: aither definition

Signed-off-by: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>

* !fixup fix: aither definition

Co-authored-by: HDVinnie <hdinnovations@protonmail.com>

* update: Aither.cs

- add language and description

Co-authored-by: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
2021-06-10 17:07:29 -05:00
Robin Dadswell
974ab6387f Fixed: Website Links going to api not site 2021-06-09 15:48:51 -05:00
ta264
9ac435bc41 Fixed: Cardigann redirections after login 2021-06-08 18:21:17 -04:00
ta264
b0f04bb9d7 Fixed: Bad login redirect using a reverse proxy 2021-06-08 14:03:57 +01:00
Lagicrus
52bff3d7bd Fixed: BarChart to have a stepSize multiplier of 1 instead of auto (#163)
* Fixes the BarChart to have a stepSize multiplier of 1 instead of auto

* Moves the size to props as per Discord

* Adds the same behavior to StackedBarChart's
2021-06-08 05:56:25 -04:00
Lagicrus
d965cb3c98 New: Sort 'Add Indexer' by Language (#130)
* New: Sort 'Add Indexer' by Language

* Adds language to propTypes

Updates propTypes to appease lint

* Call translate for labels
2021-06-08 05:20:26 -04:00
bakerboy448
b592a137cf Fixed: Cleanse Token from logs 2021-06-08 03:44:24 -04:00
Lagicrus
b0819c97ed Fixed: Adds a fix to support 3 digit issues (#157)
* Adds a fix to support 3 digit issues

* Fixes mistype with bug fix
2021-06-08 03:43:07 -04:00
Pfuenzle
5802d20b93 (Indexers) Add Newz-Complex 2021-06-07 19:11:16 -04:00
bakerboy448
58e30cc9a6 Update indexer_request.md 2021-06-07 17:36:35 -05:00
bakerboy448
513b9d2324 Fixed: Add missing Translate Keys (#152) 2021-06-07 15:15:11 -05:00
bakerboy448
3ebdc1c1ac Fixed: Develop branch out of date warning
Fixed: Develop branch invalid warning
2021-06-07 14:54:00 -04:00
bakerboy448
e9a5c5f7d2 Fixed: Missing Translate Keys for Redirect & Help Text (#143) 2021-06-07 10:42:01 -05:00
bakerboy448
23c01f7dbe fixup wiki link [skip ci] 2021-06-07 09:41:50 -05:00
bakerboy448
c35864cc7f update contributing [skip ci] 2021-06-07 09:41:28 -05:00
Pfuenzle
e545e0a129 (Indexers) Add Support for Unit3d API / Convert AnimeWorld (#132)
* (Indexers) Add Support for Unit3d API / Convert AnimeWorld

Added support for the Unit3d API for the tracker AnimeWorld.
Tested all the categories, they all returned the correct results

* Update IndexerDefinitionUpdateService.cs

* Added description and language
2021-06-07 09:33:07 -04:00
bakerboy448
1e72944998 Fixed: MaM Helptext clarified
#125
2021-06-06 23:23:31 -04:00
bakerboy448
d234cbda8a Fixed: Cleanse Username prefixed with ': ' from logs
- remove unused var in log cleansing code
2021-06-06 23:19:16 -04:00
Lizandra Candido da Silva
09a9731dae Translated using Weblate (Portuguese)
Currently translated at 88.2% (329 of 373 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt/
2021-06-06 22:58:14 -04:00
doob187
3b4df1706e Translated using Weblate (German)
Currently translated at 98.6% (368 of 373 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/de/
2021-06-06 22:58:14 -04:00
Lizandra Candido da Silva
20038e4757 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (373 of 373 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt_BR/
2021-06-06 22:58:14 -04:00
Csaba
c57c77d8e7 Translated using Weblate (Hungarian)
Currently translated at 100.0% (373 of 373 strings)

Translation: Servarr/Prowlarr
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/hu/
2021-06-06 22:58:14 -04:00
Qstick
c61c3a9c45 Fixed: (IPTorrent) Cookie not used on grabs 2021-06-06 22:46:38 -04:00
Qstick
d6d418f7b3 New: (IPTorrent) Books/Non-English Category 2021-06-06 22:43:16 -04:00
Qstick
ab5cf45d88 Fixed: Allow Blank Search
#118
2021-06-06 22:37:27 -04:00
Qstick
40d55b915e Fixed: History PropTypes warning 2021-06-06 22:35:15 -04:00
Qstick
4a851c37d5 New: AutoFocus search and Search on 'Enter'
Fixes #113
2021-06-06 22:16:39 -04:00
Qstick
63b6adf0e1 Fixed: Empty should redirect to Base 2021-06-06 21:20:04 -04:00
Qstick
d9e211472b Fixed: Cardigann redirect handling fails for relative location headers 2021-06-06 20:54:46 -04:00
Qstick
fa05dbc642 Fixed: (Rarbg) Tv search doesn't use Season and Ep 2021-06-06 17:54:57 -04:00
bakerboy448
709dc0ee5f fix template for length [skip ci] 2021-06-06 16:12:46 -05:00
bakerboy448
67f26fe185 Fixed: Updates to System > More Info (#67) 2021-06-06 16:11:06 -05:00
bakerboy448
5f4218ae91 add indexer request template [skip ci] (#116) 2021-06-06 16:10:51 -05:00
Qstick
30b54d8340 Fix Filelist unit tests 2021-06-06 13:33:51 -04:00
Qstick
ee6ae386ca Fixed: (Filelist) Unable to add
Fixes #109
2021-06-06 11:48:52 -04:00
Robin Dadswell
b3fb640969 New: App Profile help text on addition and edit of Indexers and other misc translations 2021-06-05 21:58:53 +01:00
Qstick
8b0a8e82b5 Wikijs Links 2021-06-05 16:37:49 -04:00
Robin Dadswell
f25998959e New: Added all Arr donation links 2021-06-05 21:15:06 +01:00
Qstick
f56ce129e6 Fixed: Push to client for Cardigann magnet links 2021-06-05 14:55:43 -04:00
Qstick
ae9930a03f Update ZonaQ.cs 2021-06-05 10:20:54 -04:00
Qstick
85be0be455 Fixed: Push Downloads to client fails for download overrides 2021-06-05 09:32:40 -04:00
Qstick
cf1c44ed75 Fixed: Normalize definitions when serving local and remote 2021-06-04 19:27:50 -04:00
Robin Dadswell
f062fafe82 Fixed: Error when trying to parse the value 'Unknown' as an IP Address 2021-06-05 00:18:12 +01:00
Qstick
1032d8b3ab Fixed: Slash on ProwlarrURL causes App failures 2021-06-04 19:13:01 -04:00
Qstick
99c4ed7dbc Fixed: Host not set for UI Search 2021-06-03 19:40:53 -04:00
165 changed files with 2236 additions and 675 deletions

View File

@@ -0,0 +1,22 @@
---
name: Indexer Request
about: Request an indexer for Prowlarr. Check the pinned Jackett parity issue prior to submitting a request. Duplicated requests will be closed without warning. Please search GitHub prior to requesting.
title: '(Indexer) '
labels: 'Type: Indexer Request'
assignees: ''
---
<!-- Check the pinned Jackett parity issue prior to submitting a request. Duplicated requests or requests covered in existing GitHub Issues will be closed without warning. Please search GitHub prior to requesting.-->
**Type:** <Usenet|Torrents>
**Tracker:** <Indexer/Tracker Name>
**URL:** <Indexer/Tracker URL>
**In Jackett?:** <Yes|No>
<!-- Check the pinned Jackett parity issue prior to submitting a request. Duplicated requests or requests covered in existing GitHub Issues will be closed without warning. Please search GitHub prior to requesting.-->
**Additional Context:**
<!-- Add any other context or screenshots about the request here. -->

View File

@@ -3,15 +3,31 @@
We're always looking for people to help make Prowlarr even better, there are a number of ways to contribute.
## Documentation ##
Setup guides, FAQ, the more information we have on the wiki the better.
Setup guides, FAQ, the more information we have on the [wiki](https://wikijs.servarr.com/prowlarr) the better.
## Development ##
See the readme for information on setting up your development environment.
### Tools required ###
- Visual Studio 2019 or higher (https://www.visualstudio.com/vs/). The community version is free and works (https://www.visualstudio.com/downloads/).
- HTML/Javascript editor of choice (VS Code/Sublime Text/Webstorm/Atom/etc)
- [Git](https://git-scm.com/downloads)
- [NodeJS](https://nodejs.org/en/download/) (Node 12.X.X or higher)
- [Yarn](https://yarnpkg.com/)
- .NET Core 5.0.
### Getting started ###
1. Fork Prowlarr
2. Clone the repository into your development machine. [*info*](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github)
3. Install the required Node Packages `yarn install`
4. Start gulp to monitor your dev environment for any changes that need post processing using `yarn start` command.
5. Build the project in Visual Studio, Setting startup project to `Prowlarr.Console` and framework to `net5.0`
6. Debug the project in Visual Studio
7. Open http://localhost:7878
### Contributing Code ###
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Prowlarr/Prowlarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
- Rebase from Prowlarr's develop branch, don't merge
- Rebase from Radarr's develop branch, don't merge
- Make meaningful commits, or squash them
- Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements
- Reach out to us on the discord if you have any questions
@@ -20,6 +36,10 @@ See the readme for information on setting up your development environment.
- One feature/bug fix per pull request to keep things clean and easy to understand
- Use 4 spaces instead of tabs, this is the default for VS 2019 and WebStorm (to my knowledge)
### Contributing Indexers ###
- If you're contributing an indexer please phrase your commit as something like: `New: (Indexer) {Indexer Name}`, `New: (Indexer) {Usenet|Torrent} {Indexer Name}`, `New: (Indexer) {Torznab|Newznab} {Indexer Name}`
- If you're updating an indexer please phrase your commit as something like: `Fixed: (Indexer) {Indexer Name} {changes}` e.g. `Fixed: (Indexer) Changed BHD to use API`
### Pull Requesting ###
- Only make pull requests to develop, never master, if you make a PR to master we'll comment on it and close it
- You're probably going to get some comments or questions from us, they will be to ensure consistency and maintainability

View File

@@ -2,7 +2,7 @@
[![Build Status](https://dev.azure.com/Prowlarr/Prowlarr/_apis/build/status/Prowlarr.Prowlarr?branchName=develop)](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop)
[![Translated](https://translate.servarr.com/widgets/servarr/-/prowlarr/svg-badge.svg)](https://translate.servarr.com/engage/prowlarr/?utm_source=widget)
[![Docker Pulls](https://img.shields.io/docker/pulls/hotio/prowlarr.svg)](https://wiki.servarr.com/Prowlarr_Installation#Docker)
[![Docker Pulls](https://img.shields.io/docker/pulls/hotio/prowlarr.svg)](https://wikijs.servarr.com/prowlarr/installation#docker)
![Github Downloads](https://img.shields.io/github/downloads/Prowlarr/Prowlarr/total.svg)
[![Backers on Open Collective](https://opencollective.com/Prowlarr/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/Prowlarr/sponsors/badge.svg)](#sponsors)
@@ -23,7 +23,7 @@ Note: Prowlarr is currently early in life, thus bugs should be expected
[![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60)](https://prowlarr.com/discord)
[![Reddit](https://img.shields.io/badge/reddit-discussion-FF4500.svg?maxAge=60)](https://www.reddit.com/r/Prowlarr)
[![GitHub - Bugs and Feature Requests Only](https://img.shields.io/badge/github-issues-red.svg?maxAge=60)](https://github.com/Prowlarr/Prowlarr/issues)
[![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wiki.servarr.com/Prowlarr)
[![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wikijs.servarr.com/prowlarr)
## Feature Requests

View File

@@ -23,6 +23,16 @@ class BarChart extends Component {
this.myChart = new Chart(this.canvasRef.current, {
type: 'bar',
options: {
x: {
ticks: {
stepSize: this.props.stepSize
}
},
y: {
ticks: {
stepSize: this.props.stepSize
}
},
indexAxis: this.props.horizontal ? 'y' : 'x',
maintainAspectRatio: false,
plugins: {
@@ -64,7 +74,8 @@ BarChart.propTypes = {
horizontal: PropTypes.bool,
legend: PropTypes.bool,
title: PropTypes.string.isRequired,
kind: PropTypes.oneOf(kinds.all).isRequired
kind: PropTypes.oneOf(kinds.all).isRequired,
stepSize: PropTypes.number
};
BarChart.defaultProps = {
@@ -72,7 +83,8 @@ BarChart.defaultProps = {
horizontal: false,
legend: false,
title: '',
kind: kinds.INFO
kind: kinds.INFO,
stepSize: 1
};
export default BarChart;

View File

@@ -16,10 +16,16 @@ class StackedBarChart extends Component {
maintainAspectRatio: false,
scales: {
x: {
stacked: true
stacked: true,
ticks: {
stepSize: this.props.stepSize
}
},
y: {
stacked: true
stacked: true,
ticks: {
stepSize: this.props.stepSize
}
}
},
plugins: {
@@ -63,11 +69,13 @@ class StackedBarChart extends Component {
StackedBarChart.propTypes = {
data: PropTypes.object.isRequired,
title: PropTypes.string.isRequired
title: PropTypes.string.isRequired,
stepSize: PropTypes.number
};
StackedBarChart.defaultProps = {
title: ''
title: '',
stepSize: 1
};
export default StackedBarChart;

View File

@@ -129,7 +129,7 @@ class FileBrowserModalContent extends Component {
className={styles.mappedDrivesWarning}
kind={kinds.WARNING}
>
<Link to="https://wiki.servarr.com/Prowlarr_FAQ#Why_cant_Prowlarr_see_my_files_on_a_remote_server">
<Link to="https://wikijs.servarr.com/prowlarr/faq#why-cant-prowlarr-see-my-files-on-a-remote-server">
{translate('MappedDrivesRunningAsService')}
</Link>
</Alert>

View File

@@ -25,7 +25,7 @@ function FormInputHelpText(props) {
isCheckInput && styles.isCheckInput
)}
>
{text}
<div dangerouslySetInnerHTML={{ __html: text }} />
{
link ?

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -73,7 +73,7 @@ class HistoryOptions extends Component {
}
HistoryOptions.propTypes = {
historyCleanupDays: PropTypes.bool.isRequired,
historyCleanupDays: PropTypes.number.isRequired,
dispatchSaveGeneralSettings: PropTypes.func.isRequired
};

View File

@@ -20,19 +20,25 @@ import styles from './AddIndexerModalContent.css';
const columns = [
{
name: 'protocol',
label: 'Protocol',
label: translate('Protocol'),
isSortable: true,
isVisible: true
},
{
name: 'name',
label: 'Name',
label: translate('Name'),
isSortable: true,
isVisible: true
},
{
name: 'language',
label: translate('Language'),
isSortable: true,
isVisible: true
},
{
name: 'privacy',
label: 'Privacy',
label: translate('Privacy'),
isSortable: true,
isVisible: true
}

View File

@@ -26,7 +26,8 @@ class SelectIndexerRow extends Component {
const {
protocol,
privacy,
name
name,
language
} = this.props;
return (
@@ -41,6 +42,10 @@ class SelectIndexerRow extends Component {
{name}
</TableRowCell>
<TableRowCell>
{language}
</TableRowCell>
<TableRowCell>
{privacy}
</TableRowCell>
@@ -53,6 +58,7 @@ SelectIndexerRow.propTypes = {
name: PropTypes.string.isRequired,
protocol: PropTypes.string.isRequired,
privacy: PropTypes.string.isRequired,
language: PropTypes.string.isRequired,
implementation: PropTypes.string.isRequired,
onIndexerSelect: PropTypes.func.isRequired
};

View File

@@ -99,7 +99,7 @@ function EditIndexerModalContent(props) {
<FormInputGroup
type={inputTypes.CHECK}
name="redirect"
helpText={'Redirect incoming download requests for indexer instead of Proxying using Prowlarr'}
helpText={translate('RedirectHelpText')}
isDisabled={!supportsRedirect.value}
{...redirect}
onChange={onInputChange}
@@ -113,6 +113,7 @@ function EditIndexerModalContent(props) {
type={inputTypes.APP_PROFILE_SELECT}
name="appProfileId"
{...appProfileId}
helpText={translate('AppProfileSelectHelpText')}
onChange={onInputChange}
/>
</FormGroup>

View File

@@ -50,7 +50,7 @@ class TagsModalContent extends Component {
render() {
const {
movieTags,
indexerTags,
tagList,
onModalClose
} = this.props;
@@ -108,7 +108,7 @@ class TagsModalContent extends Component {
<div className={styles.result}>
{
movieTags.map((t) => {
indexerTags.map((t) => {
const tag = _.find(tagList, { id: t });
if (!tag) {
@@ -140,7 +140,7 @@ class TagsModalContent extends Component {
return null;
}
if (movieTags.indexOf(t) > -1) {
if (indexerTags.indexOf(t) > -1) {
return null;
}
@@ -179,7 +179,7 @@ class TagsModalContent extends Component {
}
TagsModalContent.propTypes = {
movieTags: PropTypes.arrayOf(PropTypes.number).isRequired,
indexerTags: PropTypes.arrayOf(PropTypes.number).isRequired,
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
onModalClose: PropTypes.func.isRequired,
onApplyTagsPress: PropTypes.func.isRequired

View File

@@ -10,15 +10,15 @@ function createMapStateToProps() {
(state, { indexerIds }) => indexerIds,
createAllIndexersSelector(),
createTagsSelector(),
(indexerIds, allMovies, tagList) => {
const movies = _.intersectionWith(allMovies, indexerIds, (s, id) => {
(indexerIds, allIndexers, tagList) => {
const indexers = _.intersectionWith(allIndexers, indexerIds, (s, id) => {
return s.id === id;
});
const movieTags = _.uniq(_.concat(..._.map(movies, 'tags')));
const indexerTags = _.uniq(_.concat(..._.map(indexers, 'tags')));
return {
movieTags,
indexerTags,
tagList
};
}

View File

@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import withScrollPosition from 'Components/withScrollPosition';
import { testAllIndexers } from 'Store/Actions/indexerActions';
import { saveMovieEditor, setMovieFilter, setMovieSort, setMovieTableOption } from 'Store/Actions/indexerIndexActions';
import { saveIndexerEditor, setMovieFilter, setMovieSort, setMovieTableOption } from 'Store/Actions/indexerIndexActions';
import scrollPositions from 'Store/scrollPositions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createIndexerClientSideCollectionItemsSelector from 'Store/Selectors/createIndexerClientSideCollectionItemsSelector';
@@ -40,8 +40,8 @@ function createMapDispatchToProps(dispatch, props) {
dispatch(setMovieFilter({ selectedFilterKey }));
},
dispatchSaveMovieEditor(payload) {
dispatch(saveMovieEditor(payload));
dispatchSaveIndexerEditor(payload) {
dispatch(saveIndexerEditor(payload));
},
onTestAllPress() {
@@ -56,7 +56,7 @@ class IndexerIndexConnector extends Component {
// Listeners
onSaveSelected = (payload) => {
this.props.dispatchSaveMovieEditor(payload);
this.props.dispatchSaveIndexerEditor(payload);
}
onScroll = ({ scrollTop }) => {
@@ -79,7 +79,7 @@ class IndexerIndexConnector extends Component {
IndexerIndexConnector.propTypes = {
isSmallScreen: PropTypes.bool.isRequired,
dispatchSaveMovieEditor: PropTypes.func.isRequired,
dispatchSaveIndexerEditor: PropTypes.func.isRequired,
items: PropTypes.arrayOf(PropTypes.object)
};

View File

@@ -248,7 +248,7 @@ class IndexerIndexRow extends Component {
className={styles.externalLink}
name={icons.EXTERNAL_LINK}
title={'Website'}
to={baseUrl}
to={baseUrl.replace('api.', '')}
/>
<IconButton

View File

@@ -4,6 +4,7 @@ import React, { Component } from 'react';
import IndexersSelectInputConnector from 'Components/Form/IndexersSelectInputConnector';
import NewznabCategorySelectInputConnector from 'Components/Form/NewznabCategorySelectInputConnector';
import TextInput from 'Components/Form/TextInput';
import keyboardShortcuts from 'Components/keyboardShortcuts';
import SpinnerButton from 'Components/Link/SpinnerButton';
import PageContentFooter from 'Components/Page/PageContentFooter';
import SearchFooterLabel from './SearchFooterLabel';
@@ -38,9 +39,11 @@ class SearchFooter extends Component {
searchQuery
} = this.state;
if (searchQuery !== '' || searchCategories !== [] || searchIndexerIds !== []) {
if (searchQuery !== '' || searchCategories.length > 0 || searchIndexerIds.length > 0) {
this.onSearchPress();
}
this.props.bindShortcut('enter', this.onSearchPress, { isGlobal: true });
}
componentDidUpdate(prevProps) {
@@ -114,6 +117,7 @@ class SearchFooter extends Component {
<TextInput
name='searchQuery'
autoFocus={true}
value={searchQuery}
isDisabled={isFetching}
onChange={onInputChange}
@@ -181,7 +185,8 @@ SearchFooter.propTypes = {
onSearchPress: PropTypes.func.isRequired,
hasIndexers: PropTypes.bool.isRequired,
onInputChange: PropTypes.func.isRequired,
searchError: PropTypes.object
searchError: PropTypes.object,
bindShortcut: PropTypes.func.isRequired
};
export default SearchFooter;
export default keyboardShortcuts(SearchFooter);

View File

@@ -18,9 +18,9 @@ import translate from 'Utilities/String/translate';
import styles from './EditApplicationModalContent.css';
const syncLevelOptions = [
{ key: 'disabled', value: 'Disabled' },
{ key: 'addOnly', value: 'Add and Remove Only' },
{ key: 'fullSync', value: 'Full Sync' }
{ key: 'disabled', value: translate('Disabled') },
{ key: 'addOnly', value: translate('AddRemoveOnly') },
{ key: 'fullSync', value: translate('FullSync') }
];
function EditApplicationModalContent(props) {
@@ -53,7 +53,7 @@ function EditApplicationModalContent(props) {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{`${id ? 'Edit' : 'Add'} Application`}
{`${id ? translate('Edit') : translate('Add')} ${translate('Application')}`}
</ModalHeader>
<ModalBody>
@@ -94,13 +94,13 @@ function EditApplicationModalContent(props) {
</FormGroup>
<FormGroup>
<FormLabel>{'Sync Level'}</FormLabel>
<FormLabel>{translate('SyncLevel')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
values={syncLevelOptions}
name="syncLevel"
helpText={'Sync Level'}
helpText={`${translate('SyncLevelAddRemove')}<br>${translate('SyncLevelFull')}`}
{...syncLevel}
onChange={onInputChange}
/>

View File

@@ -53,6 +53,9 @@ class AddDownloadClientModalContent extends Component {
<div>
<Alert kind={kinds.INFO}>
<div>
{translate('AddDownloadClientToProwlarr')}
</div>
<div>
{translate('ProwlarrSupportsAnyDownloadClient')}
</div>

View File

@@ -55,7 +55,7 @@ function UpdateSettings(props) {
type={inputTypes.TEXT}
name="branch"
helpText={usingExternalUpdateMechanism ? translate('BranchUpdateMechanism') : translate('BranchUpdate')}
helpLink="https://wiki.servarr.com/Prowlarr_Settings#Updates"
helpLink="https://wikijs.servarr.com/prowlarr/settings#updates"
{...branch}
onChange={onInputChange}
readOnly={usingExternalUpdateMechanism}
@@ -92,7 +92,7 @@ function UpdateSettings(props) {
name="updateMechanism"
values={updateOptions}
helpText={translate('UpdateMechanismHelpText')}
helpLink="https://wiki.servarr.com/Prowlarr_Settings#Updates"
helpLink="https://wikijs.servarr.com/prowlarr/settings#updates"
onChange={onInputChange}
{...updateMechanism}
/>

View File

@@ -25,8 +25,8 @@ function NotificationEventItems(props) {
<FormLabel>{translate('NotificationTriggers')}</FormLabel>
<div>
<FormInputHelpText
text={translate('NotifcationTriggersHelpText')}
link="https://wiki.servarr.com/Prowlarr_Settings#Connections"
text={translate('NotificationTriggersHelpText')}
link="https://wikijs.servarr.com/prowlarr/settings#connections"
/>
<div className={styles.events}>
<div>

View File

@@ -176,7 +176,7 @@ export const SET_MOVIE_SORT = 'indexerIndex/setMovieSort';
export const SET_MOVIE_FILTER = 'indexerIndex/setMovieFilter';
export const SET_MOVIE_VIEW = 'indexerIndex/setMovieView';
export const SET_MOVIE_TABLE_OPTION = 'indexerIndex/setMovieTableOption';
export const SAVE_MOVIE_EDITOR = 'indexerIndex/saveMovieEditor';
export const SAVE_INDEXER_EDITOR = 'indexerIndex/saveIndexerEditor';
export const BULK_DELETE_INDEXERS = 'indexerIndex/bulkDeleteIndexers';
//
@@ -186,14 +186,14 @@ export const setMovieSort = createAction(SET_MOVIE_SORT);
export const setMovieFilter = createAction(SET_MOVIE_FILTER);
export const setMovieView = createAction(SET_MOVIE_VIEW);
export const setMovieTableOption = createAction(SET_MOVIE_TABLE_OPTION);
export const saveMovieEditor = createThunk(SAVE_MOVIE_EDITOR);
export const saveIndexerEditor = createThunk(SAVE_INDEXER_EDITOR);
export const bulkDeleteIndexers = createThunk(BULK_DELETE_INDEXERS);
//
// Action Handlers
export const actionHandlers = handleThunks({
[SAVE_MOVIE_EDITOR]: function(getState, payload, dispatch) {
[SAVE_INDEXER_EDITOR]: function(getState, payload, dispatch) {
dispatch(set({
section,
isSaving: true

View File

@@ -13,7 +13,7 @@ function createHealthCheckSelector() {
source: 'UI',
type: 'warning',
message: translate('CouldNotConnectSignalR'),
wikiUrl: 'https://wiki.servarr.com/Prowlarr_System#Could_not_connect_to_signalR'
wikiUrl: 'https://wikijs.servarr.com/prowlarr/system#could-not-connect-to-signalr'
});
}

View File

@@ -0,0 +1,64 @@
import React, { Component } from 'react';
import FieldSet from 'Components/FieldSet';
import Link from 'Components/Link/Link';
import translate from 'Utilities/String/translate';
import styles from '../styles.css';
class Donations extends Component {
//
// Render
render() {
return (
<FieldSet legend={translate('Donations')}>
<div className={styles.logoContainer} title="Radarr">
<Link to="https://opencollective.com/radarr">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-radarr.png`}
/>
</Link>
</div>
<div className={styles.logoContainer} title="Lidarr">
<Link to="https://opencollective.com/lidarr">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-lidarr.png`}
/>
</Link>
</div>
<div className={styles.logoContainer} title="Readarr">
<Link to="https://opencollective.com/readarr">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-readarr.png`}
/>
</Link>
</div>
<div className={styles.logoContainer} title="Prowlarr">
<Link to="https://opencollective.com/prowlarr">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-prowlarr.png`}
/>
</Link>
</div>
<div className={styles.logoContainer} title="Sonarr">
<Link to="https://opencollective.com/sonarr">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-sonarr.png`}
/>
</Link>
</div>
</FieldSet>
);
}
}
Donations.propTypes = {
};
export default Donations;

View File

@@ -15,32 +15,32 @@ class MoreInfo extends Component {
return (
<FieldSet legend={translate('MoreInfo')}>
<DescriptionList>
<DescriptionListItemTitle>Home page</DescriptionListItemTitle>
<DescriptionListItemTitle>{translate('HomePage')}</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://prowlarr.com/">prowlarr.com</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Discord</DescriptionListItemTitle>
<DescriptionListItemTitle>{translate('Wiki')}</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://wikijs.servarr.com/prowlarr">wikijs.servarr.com/prowlarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>{translate('Reddit')}</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://reddit.com/r/prowlarr">r/prowlarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>{translate('Discord')}</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://prowlarr.com/discord">prowlarr.com/discord</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Wiki</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://wiki.servarr.com/Prowlarr">wiki.servarr.com/Prowlarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Donations</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://opencollective.com/prowlarr">opencollective.com/prowlarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Source</DescriptionListItemTitle>
<DescriptionListItemTitle>{translate('Source')}</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://github.com/Prowlarr/Prowlarr/">github.com/Prowlarr/Prowlarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Feature Requests</DescriptionListItemTitle>
<DescriptionListItemTitle>{translate('FeatureRequests')}</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://github.com/Prowlarr/Prowlarr/issues">github.com/Prowlarr/Prowlarr/issues</Link>
</DescriptionListItemDescription>

View File

@@ -3,6 +3,7 @@ import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import translate from 'Utilities/String/translate';
import AboutConnector from './About/AboutConnector';
import Donations from './Donations/Donations';
import HealthConnector from './Health/HealthConnector';
import MoreInfo from './MoreInfo/MoreInfo';
@@ -18,6 +19,7 @@ class Status extends Component {
<HealthConnector />
<AboutConnector />
<MoreInfo />
<Donations />
</PageContentBody>
</PageContent>
);

View File

@@ -0,0 +1,17 @@
.logo {
margin: auto;
padding: 9px;
}
.logoContainer {
display: inline-block;
margin: 0.5em;
width: 50px;
height: 50px;
outline: none;
border: solid 1px #e6e6e6;
border-radius: 0.5em;
background: #f8f8ff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
cursor: pointer;
}

View File

@@ -24,7 +24,7 @@ class UpdateChanges extends Component {
<ul>
{
changes.map((change, index) => {
const checkChange = change.replace(/#\d{4,5}\b/g, (match, contents) => {
const checkChange = change.replace(/#\d{3,5}\b/g, (match, contents) => {
return `[${match}](https://github.com/Prowlarr/Prowlarr/issues/${match.substring(1)})`;
});

View File

@@ -252,7 +252,7 @@
</span>
<a
href="https://wiki.servarr.com/Prowlarr_FAQ#Help_I_have_locked_my_self_out_of_Prowlarr_and_do_not_know_the_password"
href="https://wikijs.servarr.com/prowlarr/faq#help-i-have-locked-myself-out"
class="forgot-password"
>Forgot your password?</a
>

View File

@@ -17,6 +17,8 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[TestCase(@"https://baconbits.org/feeds.php?feed=torrents_tv&user=12345&auth=2b51db35e1910123321025a12b9933d2&passkey=mySecret&authkey=2b51db35e1910123321025a12b9933d2")]
[TestCase(@"http://127.0.0.1:9117/dl/indexername?jackett_apikey=flwjiefewklfjacketmySecretsdfldskjfsdlk&path=we0re9f0sdfbase64sfdkfjsdlfjk&file=The+Torrent+File+Name.torrent")]
[TestCase(@"http://nzb.su/getnzb/2b51db35e1912ffc138825a12b9933d2.nzb&i=37292&r=2b51db35e1910123321025a12b9933d2")]
[TestCase(@"https://horrorcharnel.org/takeloginhorror.php: username=mySecret&password=mySecret&use_sslvalue==&perm_ssl=1&submitme=X&use_ssl=1&returnto=%2F&captchaSelection=1230456")]
[TestCase(@"https://torrentdb.net/login: _token=2b51db35e1912ffc138825a12b9933d2&username=mySecret&password=mySecret&remember=on")]
// NzbGet
[TestCase(@"{ ""Name"" : ""ControlUsername"", ""Value"" : ""mySecret"" }, { ""Name"" : ""ControlPassword"", ""Value"" : ""mySecret"" }, ")]

View File

@@ -84,6 +84,13 @@ namespace NzbDrone.Common.Http
throw new WebException($"Too many automatic redirections were attempted for {autoRedirectChain.Join(" -> ")}", WebExceptionStatus.ProtocolError);
}
// 302 or 303 should default to GET on redirect even if POST on original
if (response.StatusCode == HttpStatusCode.Redirect || response.StatusCode == HttpStatusCode.RedirectMethod)
{
request.Method = HttpMethod.GET;
request.ContentData = null;
}
response = await ExecuteRequestAsync(request, cookieContainer);
}
while (response.HasHttpRedirect);

View File

@@ -62,6 +62,20 @@ namespace NzbDrone.Common.Http
StatusCode == HttpStatusCode.TemporaryRedirect ||
StatusCode == HttpStatusCode.Found;
public string RedirectUrl
{
get
{
var newUrl = Headers["Location"];
if (newUrl == null)
{
return string.Empty;
}
return (Request.Url += new HttpUri(newUrl)).FullUri;
}
}
public string[] GetCookieHeaders()
{
return Headers.GetValues("Set-Cookie") ?? Array.Empty<string>();

View File

@@ -12,7 +12,7 @@ namespace NzbDrone.Common.Instrumentation
{
// Url
new Regex(@"(?<=\?|&|: |;)(apikey|token|passkey|auth|authkey|user|uid|api|[a-z_]*apikey|account|passwd)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=\?|&)[^=]*?(username|password)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=\?|&| )[^=]*?(_?token|username|password)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
@@ -77,7 +77,6 @@ namespace NzbDrone.Common.Instrumentation
private static string CleanseRemoteIP(Match match)
{
var group = match.Groups[1];
var valueAll = match.Value;
var valueIP = group.Value;
if (IPAddress.TryParse(valueIP, out var address) && !address.IsLocalAddress())

View File

@@ -0,0 +1,227 @@
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
[TestFixture]
public class DownloadClientProviderFixture : CoreTest<DownloadClientProvider>
{
private List<IDownloadClient> _downloadClients;
private List<DownloadClientStatus> _blockedProviders;
private int _nextId;
[SetUp]
public void SetUp()
{
_downloadClients = new List<IDownloadClient>();
_blockedProviders = new List<DownloadClientStatus>();
_nextId = 1;
Mocker.GetMock<IDownloadClientFactory>()
.Setup(v => v.GetAvailableProviders())
.Returns(_downloadClients);
Mocker.GetMock<IDownloadClientStatusService>()
.Setup(v => v.GetBlockedProviders())
.Returns(_blockedProviders);
}
private Mock<IDownloadClient> WithUsenetClient(int priority = 0)
{
var mock = new Mock<IDownloadClient>(MockBehavior.Default);
mock.SetupGet(s => s.Definition)
.Returns(Builder<DownloadClientDefinition>
.CreateNew()
.With(v => v.Id = _nextId++)
.With(v => v.Priority = priority)
.Build());
_downloadClients.Add(mock.Object);
mock.SetupGet(v => v.Protocol).Returns(DownloadProtocol.Usenet);
return mock;
}
private Mock<IDownloadClient> WithTorrentClient(int priority = 0)
{
var mock = new Mock<IDownloadClient>(MockBehavior.Default);
mock.SetupGet(s => s.Definition)
.Returns(Builder<DownloadClientDefinition>
.CreateNew()
.With(v => v.Id = _nextId++)
.With(v => v.Priority = priority)
.Build());
_downloadClients.Add(mock.Object);
mock.SetupGet(v => v.Protocol).Returns(DownloadProtocol.Torrent);
return mock;
}
private void GivenBlockedClient(int id)
{
_blockedProviders.Add(new DownloadClientStatus
{
ProviderId = id,
DisabledTill = DateTime.UtcNow.AddHours(3)
});
}
[Test]
public void should_roundrobin_over_usenet_client()
{
WithUsenetClient();
WithUsenetClient();
WithUsenetClient();
WithTorrentClient();
var client1 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
client1.Definition.Id.Should().Be(1);
client2.Definition.Id.Should().Be(2);
client3.Definition.Id.Should().Be(3);
client4.Definition.Id.Should().Be(1);
client5.Definition.Id.Should().Be(2);
}
[Test]
public void should_roundrobin_over_torrent_client()
{
WithUsenetClient();
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(2);
client2.Definition.Id.Should().Be(3);
client3.Definition.Id.Should().Be(4);
client4.Definition.Id.Should().Be(2);
client5.Definition.Id.Should().Be(3);
}
[Test]
public void should_roundrobin_over_protocol_separately()
{
WithUsenetClient();
WithTorrentClient();
WithTorrentClient();
var client1 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(1);
client2.Definition.Id.Should().Be(2);
client3.Definition.Id.Should().Be(3);
client4.Definition.Id.Should().Be(2);
}
[Test]
public void should_skip_blocked_torrent_client()
{
WithUsenetClient();
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
GivenBlockedClient(3);
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(2);
client2.Definition.Id.Should().Be(4);
client3.Definition.Id.Should().Be(2);
client4.Definition.Id.Should().Be(4);
}
[Test]
public void should_not_skip_blocked_torrent_client_if_all_blocked()
{
WithUsenetClient();
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
GivenBlockedClient(2);
GivenBlockedClient(3);
GivenBlockedClient(4);
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(2);
client2.Definition.Id.Should().Be(3);
client3.Definition.Id.Should().Be(4);
client4.Definition.Id.Should().Be(2);
}
[Test]
public void should_skip_secondary_prio_torrent_client()
{
WithUsenetClient();
WithTorrentClient(2);
WithTorrentClient();
WithTorrentClient();
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(3);
client2.Definition.Id.Should().Be(4);
client3.Definition.Id.Should().Be(3);
client4.Definition.Id.Should().Be(4);
}
[Test]
public void should_not_skip_secondary_prio_torrent_client_if_primary_blocked()
{
WithUsenetClient();
WithTorrentClient(2);
WithTorrentClient(2);
WithTorrentClient();
GivenBlockedClient(4);
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(2);
client2.Definition.Id.Should().Be(3);
client3.Definition.Id.Should().Be(2);
client4.Definition.Id.Should().Be(3);
}
}
}

View File

@@ -0,0 +1,161 @@
using System;
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Download;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
public class DownloadClientStatusServiceFixture : CoreTest<DownloadClientStatusService>
{
private DateTime _epoch;
[SetUp]
public void SetUp()
{
_epoch = DateTime.UtcNow;
Mocker.GetMock<IRuntimeInfo>()
.SetupGet(v => v.StartTime)
.Returns(_epoch - TimeSpan.FromHours(1));
}
private DownloadClientStatus WithStatus(DownloadClientStatus status)
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(v => v.FindByProviderId(1))
.Returns(status);
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(v => v.All())
.Returns(new[] { status });
return status;
}
private void VerifyUpdate()
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<DownloadClientStatus>()), Times.Once());
}
private void VerifyNoUpdate()
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<DownloadClientStatus>()), Times.Never());
}
[Test]
public void should_not_consider_blocked_within_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(4),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(4),
EscalationLevel = 3
});
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
}
[Test]
public void should_consider_blocked_after_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
}
[Test]
public void should_not_escalate_further_till_after_5_minutes_since_initial_failure()
{
var origStatus = WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(4),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(4),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
origStatus.EscalationLevel.Should().Be(3);
}
[Test]
public void should_escalate_further_after_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.EscalationLevel.Should().BeGreaterThan(3);
}
[Test]
public void should_not_escalate_beyond_3_hours()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Should().NotBeAfter(_epoch + TimeSpan.FromHours(3.1));
}
}
}

View File

@@ -33,14 +33,8 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Subject.Check().ShouldBeWarning();
}
[TestCase("Develop")]
[TestCase("develop")]
public void should_return_error_when_branch_is_v1(string branch)
{
GivenValidBranch(branch);
Subject.Check().ShouldBeError();
}
[TestCase("nightly")]
[TestCase("Nightly")]
public void should_return_no_warning_when_branch_valid(string branch)

View File

@@ -1,4 +1,4 @@
using FluentAssertions;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.HealthCheck;
using NzbDrone.Core.Test.Framework;
@@ -8,11 +8,11 @@ namespace NzbDrone.Core.Test.HealthCheck
[TestFixture]
public class HealthCheckFixture : CoreTest
{
private const string WikiRoot = "https://wiki.servarr.com/";
private const string WikiRoot = "https://wikijs.servarr.com/";
[TestCase("I blew up because of some weird user mistake", null, WikiRoot + "Prowlarr_System#i_blew_up_because_of_some_weird_user_mistake")]
[TestCase("I blew up because of some weird user mistake", "#my_health_check", WikiRoot + "Prowlarr_System#my_health_check")]
[TestCase("I blew up because of some weird user mistake", "Custom-Page#my_health_check", WikiRoot + "Custom-Page#my_health_check")]
[TestCase("I blew up because of some weird user mistake", null, WikiRoot + "prowlarr/system#i-blew-up-because-of-some-weird-user-mistake")]
[TestCase("I blew up because of some weird user mistake", "#my-health-check", WikiRoot + "prowlarr/system#my-health-check")]
[TestCase("I blew up because of some weird user mistake", "custom-page#my-health-check", WikiRoot + "prowlarr/custom-page#my-health-check")]
public void should_format_wiki_url(string message, string wikiFragment, string expectedUrl)
{
var subject = new NzbDrone.Core.HealthCheck.HealthCheck(typeof(HealthCheckBase), HealthCheckResult.Warning, message, wikiFragment);

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.FileList;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Test.Framework;
@@ -21,6 +22,33 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
Username = "somename"
};
Subject.Capabilities = new IndexerCapabilities
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q, MovieSearchParam.ImdbId
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q
},
BookSearchParams = new List<BookSearchParam>
{
BookSearchParam.Q
},
Flags = new List<IndexerFlag>
{
IndexerFlag.FreeLeech
}
};
Subject.Capabilities.Categories.AddCategoryMapping(1, NewznabStandardCategory.MoviesSD, "Filme SD");
Subject.Capabilities.Categories.AddCategoryMapping(2, NewznabStandardCategory.MoviesDVD, "Filme DVD");
_movieSearchCriteria = new MovieSearchCriteria
{
SearchTerm = "Star Wars",
@@ -38,7 +66,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
[Test]
public void should_use_categories_for_feed()
{
var results = Subject.GetSearchRequests(new MovieSearchCriteria { Categories = new int[] { 1, 2 } });
var results = Subject.GetSearchRequests(new MovieSearchCriteria { Categories = new int[] { NewznabStandardCategory.MoviesSD.Id, NewznabStandardCategory.MoviesDVD.Id } });
results.GetAllTiers().Should().HaveCount(1);
@@ -50,7 +78,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
[Test]
public void should_not_search_by_imdbid_if_not_supported()
{
_movieSearchCriteria.ImdbId = "tt0076759";
_movieSearchCriteria.ImdbId = "0076759";
var results = Subject.GetSearchRequests(_movieSearchCriteria);
results.GetAllTiers().Should().HaveCount(1);

View File

@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
_movieSearchCriteria = new MovieSearchCriteria
{
Categories = new int[] { 2000, 2010 },
ImdbId = "tt0076759"
ImdbId = "0076759"
};
}

View File

@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
_movieSearchCriteria = new MovieSearchCriteria
{
Categories = new int[] { 2000, 2010 },
ImdbId = "tt0076759"
ImdbId = "0076759"
};
}
@@ -56,7 +56,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
public void should_search_by_imdbid_if_supported()
{
var results = Subject.GetSearchRequests(_movieSearchCriteria);
var imdbQuery = int.Parse(_movieSearchCriteria.ImdbId.Substring(2));
var imdbQuery = int.Parse(_movieSearchCriteria.ImdbId);
results.GetAllTiers().Should().HaveCount(1);

View File

@@ -1,12 +1,13 @@
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Test.IndexerTests
{
public class TestIndexer : HttpIndexerBase<TestIndexerSettings>
public class TestIndexer : UsenetIndexerBase<TestIndexerSettings>
{
public override string Name => "Test Indexer";
public override string BaseUrl => "http://testindexer.com";
@@ -18,8 +19,8 @@ namespace NzbDrone.Core.Test.IndexerTests
public int _supportedPageSize;
public override int PageSize => _supportedPageSize;
public TestIndexer(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
public TestIndexer(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger)
{
}

View File

@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Lidarr
Fields = schema.Fields,
};
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

View File

@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Applications.Lidarr
return other.EnableRss == EnableRss &&
other.EnableAutomaticSearch == EnableAutomaticSearch &&
other.EnableInteractiveSearch == EnableAutomaticSearch &&
other.EnableInteractiveSearch == EnableInteractiveSearch &&
other.Name == Name &&
other.Implementation == Implementation &&
other.Priority == Priority &&

View File

@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Radarr
Fields = schema.Fields,
};
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

View File

@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Applications.Radarr
return other.EnableRss == EnableRss &&
other.EnableAutomaticSearch == EnableAutomaticSearch &&
other.EnableInteractiveSearch == EnableAutomaticSearch &&
other.EnableInteractiveSearch == EnableInteractiveSearch &&
other.Name == Name &&
other.Implementation == Implementation &&
other.Priority == Priority &&

View File

@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Readarr
Fields = schema.Fields,
};
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

View File

@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Applications.Readarr
return other.EnableRss == EnableRss &&
other.EnableAutomaticSearch == EnableAutomaticSearch &&
other.EnableInteractiveSearch == EnableAutomaticSearch &&
other.EnableInteractiveSearch == EnableInteractiveSearch &&
other.Name == Name &&
other.Implementation == Implementation &&
other.Priority == Priority &&

View File

@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Sonarr
Fields = schema.Fields,
};
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

View File

@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Applications.Sonarr
return other.EnableRss == EnableRss &&
other.EnableAutomaticSearch == EnableAutomaticSearch &&
other.EnableInteractiveSearch == EnableAutomaticSearch &&
other.EnableInteractiveSearch == EnableInteractiveSearch &&
other.Name == Name &&
other.Implementation == Implementation &&
other.Priority == Priority &&

View File

@@ -108,10 +108,10 @@ namespace NzbDrone.Core.Datastore
if (OsInfo.IsOsx)
{
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wiki.servarr.com/Prowlarr_FAQ#I_use_Prowlarr_on_a_Mac_and_it_suddenly_stopped_working_What_happened", e, fileName);
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wikijs.servarr.com/prowlarr/faq#i-use-prowlarr-on-a-mac-and-it-suddenly-stopped-working-what-happened", e, fileName);
}
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wiki.servarr.com/Prowlarr_FAQ#I_am_getting_an_error_Database_disk_image_is_malformed", e, fileName);
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wikijs.servarr.com/prowlarr/faq#i-am-getting-an-error-database-disk-image-is-malformed", e, fileName);
}
catch (Exception e)
{

View File

@@ -31,9 +31,8 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
: base(httpClient, configService, diskProvider, logger)
{
_dsInfoProxy = dsInfoProxy;
_dsTaskProxy = dsTaskProxy;

View File

@@ -20,9 +20,8 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
: base(httpClient, configService, diskProvider, logger)
{
_proxy = proxy;
}

View File

@@ -25,9 +25,8 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
: base(httpClient, configService, diskProvider, logger)
{
_proxy = proxy;
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Disk;
@@ -14,24 +16,20 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
{
public class Pneumatic : DownloadClientBase<PneumaticSettings>
{
private readonly IHttpClient _httpClient;
public Pneumatic(IHttpClient httpClient,
IConfigService configService,
public Pneumatic(IConfigService configService,
IDiskProvider diskProvider,
Logger logger)
: base(configService, diskProvider, logger)
{
_httpClient = httpClient;
}
public override string Name => "Pneumatic";
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
public override string Download(ReleaseInfo release, bool redirect)
public override async Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer)
{
var url = release.DownloadUrl;
var url = new Uri(release.DownloadUrl);
var title = release.Title;
title = StringUtil.CleanFileName(title);
@@ -40,7 +38,10 @@ 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);
var nzbData = await indexer.Download(url);
File.WriteAllBytes(nzbFile, nzbData);
_logger.Debug("NZB Download succeeded, saved to: {0}", nzbFile);

View File

@@ -22,9 +22,8 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
: base(httpClient, configService, diskProvider, logger)
{
_proxy = proxy;
}

View File

@@ -66,7 +66,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
{
_logger.Debug("rTorrent didn't add the torrent within {0} seconds: {1}.", tries * retryDelay / 1000, filename);
throw new ReleaseDownloadException(release, "Downloading torrent failed");
throw new ReleaseDownloadException("Downloading torrent failed");
}
return hash;

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Disk;
@@ -54,7 +55,7 @@ namespace NzbDrone.Core.Download
get;
}
public abstract string Download(ReleaseInfo release, bool redirect);
public abstract Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer);
public ValidationResult Test()
{

View File

@@ -6,7 +6,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Security;
namespace NzbDrone.Core.Indexers
namespace NzbDrone.Core.Download
{
public interface IDownloadMappingService
{

View File

@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Download
{
public interface IDownloadService
{
void SendReportToClient(ReleaseInfo release, string source, string host, bool redirect);
Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect);
Task<byte[]> DownloadReport(string link, int indexerId, string source, string host, string title);
void RecordRedirect(string link, int indexerId, string source, string host, string title);
}
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Download
_logger = logger;
}
public void SendReportToClient(ReleaseInfo release, string source, string host, bool redirect)
public async Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect)
{
var downloadTitle = release.Title;
var downloadClient = _downloadClientProvider.GetDownloadClient(release.DownloadProtocol);
@@ -69,10 +69,12 @@ namespace NzbDrone.Core.Download
_rateLimitService.WaitAndPulse(url.Host, TimeSpan.FromSeconds(2));
}
var indexer = _indexerFactory.GetInstance(_indexerFactory.Get(release.IndexerId));
string downloadClientId;
try
{
downloadClientId = downloadClient.Download(release, redirect);
downloadClientId = await downloadClient.Download(release, redirect, indexer);
_downloadClientStatusService.RecordSuccess(downloadClient.Definition.Id);
_indexerStatusService.RecordSuccess(release.IndexerId);
}

View File

@@ -1,3 +1,4 @@
using System.Threading.Tasks;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
@@ -7,6 +8,6 @@ namespace NzbDrone.Core.Download
public interface IDownloadClient : IProvider
{
DownloadProtocol Protocol { get; }
string Download(ReleaseInfo release, bool redirect);
Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer);
}
}

View File

@@ -8,12 +8,12 @@ namespace NzbDrone.Core.Download
{
public interface IValidateNzbs
{
void Validate(string filename, byte[] fileContent);
void Validate(byte[] fileContent);
}
public class NzbValidationService : IValidateNzbs
{
public void Validate(string filename, byte[] fileContent)
public void Validate(byte[] fileContent)
{
var reader = new StreamReader(new MemoryStream(fileContent));
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Download
if (nzb == null)
{
throw new InvalidNzbException("Invalid NZB: No Root element [{0}]", filename);
throw new InvalidNzbException("Invalid NZB: No Root element");
}
// nZEDb has an bug in their error reporting code spitting out invalid http status codes
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.Download
if (!nzb.Name.LocalName.Equals("nzb"))
{
throw new InvalidNzbException("Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}' [{1}]", nzb.Name.LocalName, filename);
throw new InvalidNzbException("Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}'", nzb.Name.LocalName);
}
var ns = nzb.Name.Namespace;
@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Download
if (files.Empty())
{
throw new InvalidNzbException("Invalid NZB: No files [{0}]", filename);
throw new InvalidNzbException("Invalid NZB: No files");
}
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using MonoTorrent;
using NLog;
using NzbDrone.Common.Disk;
@@ -39,7 +41,7 @@ namespace NzbDrone.Core.Download
protected abstract string AddFromTorrentFile(ReleaseInfo release, string hash, string filename, byte[] fileContent);
protected abstract string AddFromTorrentLink(ReleaseInfo release, string hash, string torrentLink);
public override string Download(ReleaseInfo release, bool redirect)
public override async Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer)
{
var torrentInfo = release as TorrentInfo;
@@ -66,7 +68,7 @@ namespace NzbDrone.Core.Download
{
try
{
return DownloadFromWebUrl(release, torrentUrl);
return await DownloadFromWebUrl(release, indexer, torrentUrl);
}
catch (Exception ex)
{
@@ -87,7 +89,7 @@ namespace NzbDrone.Core.Download
}
catch (NotSupportedException ex)
{
throw new ReleaseDownloadException(release, "Magnet not supported by download client. ({0})", ex.Message);
throw new ReleaseDownloadException("Magnet not supported by download client. ({0})", ex.Message);
}
}
}
@@ -103,7 +105,7 @@ namespace NzbDrone.Core.Download
{
if (torrentUrl.IsNullOrWhiteSpace())
{
throw new ReleaseDownloadException(release, "Magnet not supported by download client. ({0})", ex.Message);
throw new ReleaseDownloadException("Magnet not supported by download client. ({0})", ex.Message);
}
_logger.Debug("Magnet not supported by download client, trying torrent. ({0})", ex.Message);
@@ -112,74 +114,31 @@ namespace NzbDrone.Core.Download
if (torrentUrl.IsNotNullOrWhiteSpace())
{
return DownloadFromWebUrl(release, torrentUrl);
return await DownloadFromWebUrl(release, indexer, torrentUrl);
}
}
return null;
}
private string DownloadFromWebUrl(ReleaseInfo release, string torrentUrl)
private async Task<string> DownloadFromWebUrl(ReleaseInfo release, IIndexer indexer, string torrentUrl)
{
byte[] torrentFile = null;
try
torrentFile = await indexer.Download(new Uri(torrentUrl));
// handle magnet URLs
if (torrentFile.Length >= 7
&& torrentFile[0] == 0x6d
&& torrentFile[1] == 0x61
&& torrentFile[2] == 0x67
&& torrentFile[3] == 0x6e
&& torrentFile[4] == 0x65
&& torrentFile[5] == 0x74
&& torrentFile[6] == 0x3a)
{
var request = new HttpRequest(torrentUrl);
request.Headers.Accept = "application/x-bittorrent";
request.AllowAutoRedirect = false;
var response = _httpClient.Get(request);
if (response.StatusCode == HttpStatusCode.MovedPermanently ||
response.StatusCode == HttpStatusCode.Found ||
response.StatusCode == HttpStatusCode.SeeOther)
{
var locationHeader = response.Headers.GetSingleValue("Location");
_logger.Trace("Torrent request is being redirected to: {0}", locationHeader);
if (locationHeader != null)
{
if (locationHeader.StartsWith("magnet:"))
{
return DownloadFromMagnetUrl(release, locationHeader);
}
return DownloadFromWebUrl(release, locationHeader);
}
throw new WebException("Remote website tried to redirect without providing a location.");
}
torrentFile = response.ResponseData;
_logger.Debug("Downloading torrent for release '{0}' finished ({1} bytes from {2})", release.Title, torrentFile.Length, torrentUrl);
}
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.NotFound)
{
_logger.Error(ex, "Downloading torrent file for release '{0}' failed since it no longer exists ({1})", release.Title, torrentUrl);
throw new ReleaseUnavailableException(release, "Downloading torrent failed", ex);
}
if ((int)ex.Response.StatusCode == 429)
{
_logger.Error("API Grab Limit reached for {0}", torrentUrl);
}
else
{
_logger.Error(ex, "Downloading torrent file for release '{0}' failed ({1})", release.Title, torrentUrl);
}
throw new ReleaseDownloadException(release, "Downloading torrent failed", ex);
}
catch (WebException ex)
{
_logger.Error(ex, "Downloading torrent file for release '{0}' failed ({1})", release.Title, torrentUrl);
throw new ReleaseDownloadException(release, "Downloading torrent failed", ex);
var magnetUrl = Encoding.UTF8.GetString(torrentFile);
return DownloadFromMagnetUrl(release, magnetUrl);
}
var filename = string.Format("{0}.torrent", StringUtil.CleanFileName(release.Title));

View File

@@ -1,9 +1,9 @@
using System.Net;
using System;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
@@ -15,17 +15,14 @@ namespace NzbDrone.Core.Download
where TSettings : IProviderConfig, new()
{
protected readonly IHttpClient _httpClient;
private readonly IValidateNzbs _nzbValidationService;
protected UsenetClientBase(IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger)
: base(configService, diskProvider, logger)
{
_httpClient = httpClient;
_nzbValidationService = nzbValidationService;
}
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
@@ -33,9 +30,9 @@ namespace NzbDrone.Core.Download
protected abstract string AddFromNzbFile(ReleaseInfo release, string filename, byte[] fileContents);
protected abstract string AddFromLink(ReleaseInfo release);
public override string Download(ReleaseInfo release, bool redirect)
public override async Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer)
{
var url = release.DownloadUrl;
var url = new Uri(release.DownloadUrl);
if (redirect)
{
@@ -46,40 +43,7 @@ namespace NzbDrone.Core.Download
byte[] nzbData;
try
{
var request = new HttpRequest(url);
nzbData = _httpClient.Get(request).ResponseData;
_logger.Debug("Downloaded nzb for release '{0}' finished ({1} bytes from {2})", release.Title, nzbData.Length, url);
}
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.NotFound)
{
_logger.Error(ex, "Downloading nzb file for release '{0}' failed since it no longer exists ({1})", release.Title, url);
throw new ReleaseUnavailableException(release, "Downloading nzb failed", ex);
}
if ((int)ex.Response.StatusCode == 429)
{
_logger.Error("API Grab Limit reached for {0}", url);
}
else
{
_logger.Error(ex, "Downloading nzb for release '{0}' failed ({1})", release.Title, url);
}
throw new ReleaseDownloadException(release, "Downloading nzb failed", ex);
}
catch (WebException ex)
{
_logger.Error(ex, "Downloading nzb for release '{0}' failed ({1})", release.Title, url);
throw new ReleaseDownloadException(release, "Downloading nzb failed", ex);
}
_nzbValidationService.Validate(filename, nzbData);
nzbData = await indexer.Download(url);
_logger.Info("Adding report [{0}] to the queue.", release.Title);
return AddFromNzbFile(release, filename, nzbData);

View File

@@ -1,28 +1,33 @@
using System;
using System;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Exceptions
{
public class DownloadClientRejectedReleaseException : ReleaseDownloadException
{
public ReleaseInfo Release { get; set; }
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, params object[] args)
: base(release, message, args)
: base(message, args)
{
Release = release;
}
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message)
: base(release, message)
: base(message)
{
Release = release;
}
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, Exception innerException, params object[] args)
: base(release, message, innerException, args)
: base(message, innerException, args)
{
Release = release;
}
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, Exception innerException)
: base(release, message, innerException)
: base(message, innerException)
{
Release = release;
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using NzbDrone.Common.Exceptions;
using NzbDrone.Core.Parser.Model;
@@ -6,30 +6,24 @@ namespace NzbDrone.Core.Exceptions
{
public class ReleaseDownloadException : NzbDroneException
{
public ReleaseInfo Release { get; set; }
public ReleaseDownloadException(ReleaseInfo release, string message, params object[] args)
public ReleaseDownloadException(string message, params object[] args)
: base(message, args)
{
Release = release;
}
public ReleaseDownloadException(ReleaseInfo release, string message)
public ReleaseDownloadException(string message)
: base(message)
{
Release = release;
}
public ReleaseDownloadException(ReleaseInfo release, string message, Exception innerException, params object[] args)
public ReleaseDownloadException(string message, Exception innerException, params object[] args)
: base(message, innerException, args)
{
Release = release;
}
public ReleaseDownloadException(ReleaseInfo release, string message, Exception innerException)
public ReleaseDownloadException(string message, Exception innerException)
: base(message, innerException)
{
Release = release;
}
}
}

View File

@@ -1,27 +1,27 @@
using System;
using System;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Exceptions
{
public class ReleaseUnavailableException : ReleaseDownloadException
{
public ReleaseUnavailableException(ReleaseInfo release, string message, params object[] args)
: base(release, message, args)
public ReleaseUnavailableException(string message, params object[] args)
: base(message, args)
{
}
public ReleaseUnavailableException(ReleaseInfo release, string message)
: base(release, message)
public ReleaseUnavailableException(string message)
: base(message)
{
}
public ReleaseUnavailableException(ReleaseInfo release, string message, Exception innerException, params object[] args)
: base(release, message, innerException, args)
public ReleaseUnavailableException(string message, Exception innerException, params object[] args)
: base(message, innerException, args)
{
}
public ReleaseUnavailableException(ReleaseInfo release, string message, Exception innerException)
: base(release, message, innerException)
public ReleaseUnavailableException(string message, Exception innerException)
: base(message, innerException)
{
}
}

View File

@@ -44,14 +44,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
_localizationService.GetLocalizedString("ApplicationStatusCheckAllClientMessage"),
"#applications_are_unavailable_due_to_failures");
"#applications-are-unavailable-due-to-failures");
}
return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("ApplicationStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#applications_are_unavailable_due_to_failures");
"#applications-are-unavailable-due-to-failures");
}
}
}

View File

@@ -37,10 +37,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (backOffProviders.Count == enabledProviders.Count)
{
return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("DownloadClientStatusCheckAllClientMessage"), "#download_clients_are_unavailable_due_to_failures");
return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("DownloadClientStatusCheckAllClientMessage"), "#download-clients-are-unavailable-due-to-failures");
}
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("DownloadClientStatusCheckSingleClientMessage"), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), "#download_clients_are_unavailable_due_to_failures");
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("DownloadClientStatusCheckSingleClientMessage"), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), "#download-clients-are-unavailable-due-to-failures");
}
}
}

View File

@@ -46,14 +46,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
_localizationService.GetLocalizedString("IndexerLongTermStatusCheckAllClientMessage"),
"#indexers_are_unavailable_due_to_failures");
"#indexers-are-unavailable-due-to-failures");
}
return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerLongTermStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#indexers_are_unavailable_due_to_failures");
"#indexers-are-unavailable-due-to-failures");
}
}
}

View File

@@ -44,14 +44,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
_localizationService.GetLocalizedString("IndexerStatusCheckAllClientMessage"),
"#indexers_are_unavailable_due_to_failures");
"#indexers-are-unavailable-due-to-failures");
}
return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#indexers_are_unavailable_due_to_failures");
"#indexers-are-unavailable-due-to-failures");
}
}
}

View File

@@ -55,7 +55,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("NewznabVipCheckExpiringClientMessage"),
string.Join(", ", expiringProviders.Select(v => v.Definition.Name))),
"#newznab_vip_expiring");
"#newznab-vip-expiring");
}
if (!expiredProviders.Empty())
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("NewznabVipCheckExpiredClientMessage"),
string.Join(", ", expiredProviders.Select(v => v.Definition.Name))),
"#newznab_vip_expired");
"#newznab-vip-expired");
}
return new HealthCheck(GetType());

View File

@@ -38,7 +38,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerObsoleteCheckMessage"),
string.Join(", ", oldIndexers.Select(v => v.Name))),
"#indexers_are_obsolete");
"#indexers-are-obsolete");
}
public override bool CheckOnSchedule => false;

View File

@@ -23,12 +23,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (!Enum.GetNames(typeof(ReleaseBranches)).Any(x => x.ToLower() == currentBranch))
{
if (currentBranch == "develop")
{
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckPreviousVersionMessage"), _configFileService.Branch), "#branch_is_for_a_previous_version");
}
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckOfficialBranchMessage"), _configFileService.Branch), "#branch_is_not_a_valid_release_branch");
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckOfficialBranchMessage"), _configFileService.Branch), "#branch-is-not-a-valid-release-branch");
}
return new HealthCheck(GetType());
@@ -36,6 +31,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
public enum ReleaseBranches
{
Develop,
Nightly
}
}

View File

@@ -48,7 +48,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
string.Format(_localizationService.GetLocalizedString("UpdateCheckStartupTranslocationMessage"), startupFolder),
"#cannot_install_update_because_startup_folder_is_in_an_app_translocation_folder.");
"#cannot-install-update-because-startup-folder-is-in-an-app-translocation-folder.");
}
if (!_diskProvider.FolderWritable(startupFolder))
@@ -56,7 +56,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
string.Format(_localizationService.GetLocalizedString("UpdateCheckStartupNotWritableMessage"), startupFolder, Environment.UserName),
"#cannot_install_update_because_startup_folder_is_not_writable_by_the_user");
"#cannot-install-update-because-startup-folder-is-not-writable-by-the-user");
}
if (!_diskProvider.FolderWritable(uiFolder))
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
string.Format(_localizationService.GetLocalizedString("UpdateCheckUINotWritableMessage"), uiFolder, Environment.UserName),
"#cannot_install_update_because_ui_folder_is_not_writable_by_the_user");
"#cannot-install-update-because-ui-folder-is-not-writable-by-the-user");
}
}

View File

@@ -34,12 +34,12 @@ namespace NzbDrone.Core.HealthCheck
private static string MakeWikiFragment(string message)
{
return "#" + CleanFragmentRegex.Replace(message.ToLower(), string.Empty).Replace(' ', '_');
return "#" + CleanFragmentRegex.Replace(message.ToLower(), string.Empty).Replace(' ', '-');
}
private static HttpUri MakeWikiUrl(string fragment)
{
return new HttpUri("https://wiki.servarr.com/Prowlarr_System#") + new HttpUri(fragment);
return new HttpUri("https://wikijs.servarr.com/prowlarr/system#") + new HttpUri(fragment);
}
}

View File

@@ -119,14 +119,14 @@ namespace NzbDrone.Core.History
if (message.Query is MovieSearchCriteria)
{
history.Data.Add("ImdbId", ((MovieSearchCriteria)message.Query).ImdbId ?? string.Empty);
history.Data.Add("ImdbId", ((MovieSearchCriteria)message.Query).FullImdbId ?? string.Empty);
history.Data.Add("TmdbId", ((MovieSearchCriteria)message.Query).TmdbId?.ToString() ?? string.Empty);
history.Data.Add("TraktId", ((MovieSearchCriteria)message.Query).TraktId?.ToString() ?? string.Empty);
}
if (message.Query is TvSearchCriteria)
{
history.Data.Add("ImdbId", ((TvSearchCriteria)message.Query).ImdbId ?? string.Empty);
history.Data.Add("ImdbId", ((TvSearchCriteria)message.Query).FullImdbId ?? string.Empty);
history.Data.Add("TvdbId", ((TvSearchCriteria)message.Query).TvdbId?.ToString() ?? string.Empty);
history.Data.Add("TraktId", ((TvSearchCriteria)message.Query).TraktId?.ToString() ?? string.Empty);
history.Data.Add("RId", ((TvSearchCriteria)message.Query).RId?.ToString() ?? string.Empty);

View File

@@ -1,4 +1,5 @@
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.IndexerSearch.Definitions
{
@@ -20,5 +21,7 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
return false;
}
}
public string FullImdbId => ParseUtil.GetFullImdbId(ImdbId);
}
}

View File

@@ -19,6 +19,8 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
public string SanitizedTvSearchString => (SanitizedSearchTerm + " " + EpisodeSearchString).Trim();
public string EpisodeSearchString => GetEpisodeSearchString();
public string FullImdbId => ParseUtil.GetFullImdbId(ImdbId);
public override bool RssSearch
{
get

View File

@@ -80,13 +80,13 @@ namespace NzbDrone.Core.IndexerSearch
r.InfoUrl == null ? null : new XElement("comments", r.InfoUrl),
r.PublishDate == DateTime.MinValue ? new XElement("pubDate", XmlDateFormat(DateTime.Now)) : new XElement("pubDate", XmlDateFormat(r.PublishDate)),
new XElement("size", r.Size),
r.Category == null ? null : from c in r.Category select new XElement("category", c.Id),
r.Categories == null ? null : from c in r.Categories select new XElement("category", c.Id),
new XElement(
"enclosure",
new XAttribute("url", r.DownloadUrl ?? t.MagnetUrl ?? string.Empty),
r.Size == null ? null : new XAttribute("length", r.Size),
new XAttribute("type", protocol == DownloadProtocol.Torrent ? "application/x-bittorrent" : "application/x-nzb")),
r.Category == null ? null : from c in r.Category select GetNabElement("category", c.Id, protocol),
r.Categories == null ? null : from c in r.Categories select GetNabElement("category", c.Id, protocol),
r.IndexerFlags == null ? null : from f in r.IndexerFlags select GetNabElement("tag", f.Name, protocol),
GetNabElement("rageid", r.TvRageId, protocol),
GetNabElement("thetvdb", r.TvdbId, protocol),

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Events;
using NzbDrone.Core.IndexerSearch.Definitions;

View File

@@ -24,7 +24,7 @@ namespace NzbDrone.Core.IndexerVersions
public class IndexerDefinitionUpdateService : IIndexerDefinitionUpdateService, IExecute<IndexerDefinitionUpdateCommand>
{
private const int DEFINITION_VERSION = 1;
private readonly List<string> _defintionBlacklist = new List<string>() { "blutopia", "beyond-hd", "beyond-hd-oneurl", "hdbits" };
private readonly List<string> _defintionBlacklist = new List<string>() { "aither", "animeworld", "blutopia", "beyond-hd", "beyond-hd-oneurl", "hdbits" };
private readonly IHttpClient _httpClient;
private readonly IAppFolderInfo _appFolderInfo;
@@ -89,7 +89,8 @@ namespace NzbDrone.Core.IndexerVersions
{
var req = new HttpRequest($"https://indexers.prowlarr.com/master/{DEFINITION_VERSION}/{id}");
var response = _httpClient.Get(req);
return _deserializer.Deserialize<CardigannDefinition>(response.Content);
var definition = _deserializer.Deserialize<CardigannDefinition>(response.Content);
return CleanIndexerDefinition(definition);
}
private CardigannDefinition LoadIndexerDef(string fileKey)
@@ -118,42 +119,7 @@ namespace NzbDrone.Core.IndexerVersions
var definitionString = File.ReadAllText(file.FullName);
var definition = _deserializer.Deserialize<CardigannDefinition>(definitionString);
//defaults
if (definition.Settings == null)
{
definition.Settings = new List<SettingsField>
{
new SettingsField { Name = "username", Label = "Username", Type = "text" },
new SettingsField { Name = "password", Label = "Password", Type = "password" }
};
}
if (definition.Encoding == null)
{
definition.Encoding = "UTF-8";
}
if (definition.Login != null && definition.Login.Method == null)
{
definition.Login.Method = "form";
}
if (definition.Search.Paths == null)
{
definition.Search.Paths = new List<SearchPathBlock>();
}
// convert definitions with a single search Path to a Paths entry
if (definition.Search.Path != null)
{
definition.Search.Paths.Add(new SearchPathBlock
{
Path = definition.Search.Path,
Inheritinputs = true
});
}
return definition;
return CleanIndexerDefinition(definition);
}
catch (Exception e)
{
@@ -165,6 +131,45 @@ namespace NzbDrone.Core.IndexerVersions
return GetHttpDefinition(fileKey);
}
private CardigannDefinition CleanIndexerDefinition(CardigannDefinition definition)
{
if (definition.Settings == null)
{
definition.Settings = new List<SettingsField>
{
new SettingsField { Name = "username", Label = "Username", Type = "text" },
new SettingsField { Name = "password", Label = "Password", Type = "password" }
};
}
if (definition.Encoding == null)
{
definition.Encoding = "UTF-8";
}
if (definition.Login != null && definition.Login.Method == null)
{
definition.Login.Method = "form";
}
if (definition.Search.Paths == null)
{
definition.Search.Paths = new List<SearchPathBlock>();
}
// convert definitions with a single search Path to a Paths entry
if (definition.Search.Path != null)
{
definition.Search.Paths.Add(new SearchPathBlock
{
Path = definition.Search.Path,
Inheritinputs = true
});
}
return definition;
}
public void Execute(IndexerDefinitionUpdateCommand message)
{
UpdateLocalDefinitions();

View File

@@ -0,0 +1,59 @@
using System.Collections.Generic;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.UNIT3D;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
{
public class Aither : Unit3dBase
{
public override string Name => "Aither";
public override string BaseUrl => "https://aither.cc/";
public override string Description => "Aither is a Private Torrent Tracker for HD MOVIES / TV";
public override string Language => "en-us";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public Aither(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q
},
BookSearchParams = new List<BookSearchParam>
{
BookSearchParam.Q
}
};
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Movie");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TV, "TV");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Audio, "Music");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.PCGames, "Games");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.XXX, "XXX");
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.TVSport, "Sport");
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.PC, "Software/Apps");
caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.BooksEBook, "Ebooks/Magazines");
caps.Categories.AddCategoryMapping(14, NewznabStandardCategory.AudioAudiobook, "AudioBooks");
caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.Other, "Education");
return caps;
}
}
}

View File

@@ -23,7 +23,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class AnimeBytes : HttpIndexerBase<AnimeBytesSettings>
public class AnimeBytes : TorrentIndexerBase<AnimeBytesSettings>
{
public override string Name => "AnimeBytes";
public override string BaseUrl => "https://animebytes.tv/";
@@ -449,7 +449,7 @@ namespace NzbDrone.Core.Indexers.Definitions
Guid = guid.AbsoluteUri,
DownloadUrl = linkUri.AbsoluteUri,
PublishDate = publishDate,
Category = category,
Categories = category,
Description = description,
Size = size,
Seeders = seeders,

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class AnimeTorrents : HttpIndexerBase<AnimeTorrentsSettings>
public class AnimeTorrents : TorrentIndexerBase<AnimeTorrentsSettings>
{
public override string Name => "AnimeTorrents";
@@ -294,7 +294,7 @@ namespace NzbDrone.Core.Indexers.Definitions
rCat = rCat.Substring(rCatIdx + 4);
}
release.Category = _categories.MapTrackerCatToNewznab(rCat);
release.Categories = _categories.MapTrackerCatToNewznab(rCat);
if (row.QuerySelector("img[alt=\"Gold Torrent\"]") != null)
{

View File

@@ -0,0 +1,61 @@
using System.Collections.Generic;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.UNIT3D;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
{
public class AnimeWorld : Unit3dBase
{
public override string Name => "AnimeWorld";
public override string BaseUrl => "https://animeworld.cx/";
public override string Description => "AnimeWorld (AW) is a GERMAN Private site for ANIME / MANGA / HENTAI";
public override string Language => "de-de";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public AnimeWorld(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q
},
BookSearchParams = new List<BookSearchParam>
{
BookSearchParam.Q
}
};
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Anime Movie");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVAnime, "Anime Series");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Audio, "Anime Musik/OST");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.PCGames, "Anime Spiele");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.XXX, "Hentai");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.PCGames, "Spiele Linux");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Other, "Sonstiges");
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.Movies, "Filme");
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.TV, "Serien");
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.PCGames, "Spiele");
caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.Audio, "Musik");
caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.BooksComics, "Mangas");
return caps;
}
}
}

View File

@@ -9,7 +9,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions.Avistaz
{
public abstract class AvistazBase : HttpIndexerBase<AvistazSettings>
public abstract class AvistazBase : TorrentIndexerBase<AvistazSettings>
{
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override string BaseUrl => "";

View File

@@ -24,6 +24,11 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
{
var torrentInfos = new List<TorrentInfo>();
if (indexerResponse.HttpResponse.StatusCode == HttpStatusCode.NotFound)
{
return torrentInfos.ToArray();
}
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
@@ -50,7 +55,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
InfoHash = row.InfoHash,
InfoUrl = details,
Guid = details,
Category = cats,
Categories = cats,
PublishDate = row.CreatedAt,
Size = row.FileSize,
Files = row.FileCount,

View File

@@ -74,9 +74,9 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
{
var parameters = GetBasicSearchParameters(searchCriteria.Categories);
if (searchCriteria.ImdbId != null)
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
{
parameters.Add("imdb", searchCriteria.ImdbId);
parameters.Add("imdb", searchCriteria.FullImdbId);
}
else
{
@@ -103,9 +103,9 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
{
var parameters = GetBasicSearchParameters(searchCriteria.Categories);
if (searchCriteria.ImdbId != null)
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
{
parameters.Add("imdb", searchCriteria.ImdbId);
parameters.Add("imdb", searchCriteria.FullImdbId);
}
else
{

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class BakaBT : HttpIndexerBase<BakaBTSettings>
public class BakaBT : TorrentIndexerBase<BakaBTSettings>
{
public override string Name => "BakaBT";
@@ -297,7 +297,7 @@ namespace NzbDrone.Core.Indexers.Definitions
release.Title = release.Title.Substring(0, insertPoint) + " Season 1 " + release.Title.Substring(insertPoint);
}
release.Category = currentCategories;
release.Categories = currentCategories;
//release.Description = row.QuerySelector("span.tags")?.TextContent;
release.Guid = _baseUrl + qTitleLink.GetAttribute("href");

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class BeyondHD : HttpIndexerBase<BeyondHDSettings>
public class BeyondHD : TorrentIndexerBase<BeyondHDSettings>
{
public override string Name => "BeyondHD";
@@ -123,7 +123,7 @@ namespace NzbDrone.Core.Indexers.Definitions
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.ImdbId, searchCriteria.TmdbId.GetValueOrDefault()));
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId, searchCriteria.TmdbId.GetValueOrDefault()));
return pageableRequests;
}
@@ -141,7 +141,7 @@ namespace NzbDrone.Core.Indexers.Definitions
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.ImdbId));
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
return pageableRequests;
}
@@ -209,7 +209,7 @@ namespace NzbDrone.Core.Indexers.Definitions
InfoHash = row.InfoHash,
InfoUrl = details,
Guid = details,
Category = _categories.MapTrackerCatDescToNewznab(row.Category),
Categories = _categories.MapTrackerCatDescToNewznab(row.Category),
PublishDate = DateTime.Parse(row.CreatedAt, CultureInfo.InvariantCulture),
Size = row.Size,
Grabs = row.Grabs,

View File

@@ -6,7 +6,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.BroadcastheNet
{
public class BroadcastheNet : HttpIndexerBase<BroadcastheNetSettings>
public class BroadcastheNet : TorrentIndexerBase<BroadcastheNetSettings>
{
public override string Name => "BroadcasTheNet";

View File

@@ -104,12 +104,12 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
torrentInfo.DownloadVolumeFactor = 0;
torrentInfo.MinimumRatio = 1;
torrentInfo.Category = _categories.MapTrackerCatToNewznab(torrent.Resolution);
torrentInfo.Categories = _categories.MapTrackerCatToNewznab(torrent.Resolution);
// Default to TV if category could not be mapped
if (torrentInfo.Category == null || !torrentInfo.Category.Any())
if (torrentInfo.Categories == null || !torrentInfo.Categories.Any())
{
torrentInfo.Category = new List<IndexerCategory> { NewznabStandardCategory.TV };
torrentInfo.Categories = new List<IndexerCategory> { NewznabStandardCategory.TV };
}
results.Add(torrentInfo);

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using FluentValidation.Results;
@@ -7,6 +8,7 @@ using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.IndexerVersions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
@@ -14,7 +16,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Cardigann
{
public class Cardigann : HttpIndexerBase<CardigannSettings>
public class Cardigann : TorrentIndexerBase<CardigannSettings>
{
private readonly IIndexerDefinitionUpdateService _definitionService;
private readonly ICached<CardigannRequestGenerator> _generatorCache;
@@ -105,6 +107,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
{
Enable = true,
Name = definition.Name,
Language = definition.Language,
Implementation = GetType().Name,
Settings = new CardigannSettings { DefinitionFile = definition.File },
Protocol = DownloadProtocol.Torrent,
@@ -147,6 +150,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
if (request.Url.Scheme == "magnet")
{
ValidateMagnet(request.Url.FullUri);
return Encoding.UTF8.GetBytes(request.Url.FullUri);
}
@@ -159,10 +163,36 @@ namespace NzbDrone.Core.Indexers.Cardigann
var response = await _httpClient.ExecuteAsync(request);
downloadBytes = response.ResponseData;
}
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.NotFound)
{
_logger.Error(ex, "Downloading torrent file for release failed since it no longer exists ({0})", request.Url.FullUri);
throw new ReleaseUnavailableException("Downloading torrent failed", ex);
}
if ((int)ex.Response.StatusCode == 429)
{
_logger.Error("API Grab Limit reached for {0}", request.Url.FullUri);
}
else
{
_logger.Error(ex, "Downloading torrent file for release failed ({0})", request.Url.FullUri);
}
throw new ReleaseDownloadException("Downloading torrent failed", ex);
}
catch (WebException ex)
{
_logger.Error(ex, "Downloading torrent file for release failed ({0})", request.Url.FullUri);
throw new ReleaseDownloadException("Downloading torrent failed", ex);
}
catch (Exception)
{
_indexerStatusService.RecordFailure(Definition.Id);
_logger.Error("Download failed");
_logger.Error("Downloading torrent failed");
throw;
}
return downloadBytes;

View File

@@ -237,7 +237,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
}
else if (setting.Type == "checkbox")
{
variables[name] = ((bool)value) ? ".True" : ".False";
variables[name] = ((bool)value) ? ".True" : null;
}
else if (setting.Type == "select")
{

View File

@@ -36,7 +36,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
// Remove cookie cache
if (indexerResponse.HttpResponse.HasHttpRedirect && indexerResponse.HttpResponse.Headers["Location"]
if (indexerResponse.HttpResponse.HasHttpRedirect && indexerResponse.HttpResponse.RedirectUrl
.ContainsIgnoreCase("login.php"))
{
CookiesUpdater(null, null);
@@ -194,17 +194,17 @@ namespace NzbDrone.Core.Indexers.Cardigann
var cats = MapTrackerCatToNewznab(value);
if (cats.Any())
{
if (release.Category == null || fieldModifiers.Contains("noappend"))
if (release.Categories == null || fieldModifiers.Contains("noappend"))
{
release.Category = cats;
release.Categories = cats;
}
else
{
release.Category = release.Category.Union(cats).ToList();
release.Categories = release.Categories.Union(cats).ToList();
}
}
value = release.Category.ToString();
value = release.Categories.ToString();
break;
case "size":
release.Size = ReleaseInfo.GetBytes(value);

View File

@@ -43,8 +43,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
variables[".Query.Movie"] = null;
variables[".Query.Year"] = null;
variables[".Query.IMDBID"] = searchCriteria.ImdbId;
variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId?.TrimStart('t') ?? null;
variables[".Query.IMDBID"] = searchCriteria.FullImdbId;
variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId;
variables[".Query.TMDBID"] = searchCriteria.TmdbId;
variables[".Query.TraktID"] = searchCriteria.TraktId;
@@ -78,8 +78,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
variables[".Query.Series"] = null;
variables[".Query.Ep"] = searchCriteria.Episode;
variables[".Query.Season"] = searchCriteria.Season;
variables[".Query.IMDBID"] = searchCriteria.ImdbId;
variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId?.Replace("tt", "") ?? null;
variables[".Query.IMDBID"] = searchCriteria.FullImdbId;
variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId;
variables[".Query.TVDBID"] = searchCriteria.TvdbId;
variables[".Query.TVRageID"] = searchCriteria.RId;
variables[".Query.TVMazeID"] = searchCriteria.TvMazeId;
@@ -635,14 +635,14 @@ namespace NzbDrone.Core.Indexers.Cardigann
{
if (requestUrl.StartsWith(SiteLink) && !redirectUrl.StartsWith(SiteLink))
{
var uri = new Uri(redirectUrl);
var uri = new HttpUri(redirectUrl);
return uri.Scheme + "://" + uri.Host + "/";
}
return null;
}
protected string GetRedirectDomainHint(HttpResponse result) => GetRedirectDomainHint(result.Request.Url.ToString(), result.Headers.GetSingleValue("Location"));
protected string GetRedirectDomainHint(HttpResponse result) => GetRedirectDomainHint(result.Request.Url.ToString(), result.RedirectUrl);
protected async Task<HttpResponse> HandleRequest(RequestBlock request, Dictionary<string, object> variables = null, string referer = null)
{

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