Compare commits

...

42 Commits

Author SHA1 Message Date
bakerboy448
749684e24a New: Indexer Messaging and Error Improvements
(cherry picked from commit 3b505d8734dcbe3fa53acba7f94f1361151e6a44)
2023-06-19 16:12:15 +03:00
Bogdan
3a0ca45aa9 Fix sorting queue items by size 2023-06-18 15:00:49 +03:00
Bogdan
595efd498e Close database connections in housekeeping tasks
Co-authored-by: ferencmarkizay <ferencmarkizay@gmail.com>
2023-06-18 15:00:11 +03:00
Bogdan
dea1060d61 Bump version to 0.2.0 2023-06-18 07:18:42 +03:00
Weblate
f6049b8bf2 Translated using Weblate (Portuguese (Brazil)) [skip ci]
Currently translated at 100.0% (942 of 942 strings)

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pt_BR/
Translation: Servarr/Readarr
2023-06-18 04:53:21 +03:00
Bogdan
53ced38221 New: Improved page loading errors
Closes #2605
2023-06-17 00:51:10 +03:00
Tristan Kennedy
3a3cf8511e Added padding to search tab to maintain visual consistancy
(cherry picked from commit 55ef505d740a9aadc7f161274006e150b0d9cf8f)
2023-06-17 00:50:55 +03:00
Servarr
9ec913337d Automated API Docs update [skip ci] 2023-06-15 13:26:07 +03:00
Weblate
9a2120ae92 Update translation files [skip ci]
Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Norwegian Bokmål) [skip ci]

Currently translated at 16.0% (151 of 942 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (942 of 942 strings)

Translated using Weblate (Turkish) [skip ci]

Currently translated at 61.6% (581 of 942 strings)

Translated using Weblate (Romanian) [skip ci]

Currently translated at 59.9% (565 of 942 strings)

Translated using Weblate (Polish) [skip ci]

Currently translated at 65.9% (621 of 942 strings)

Translated using Weblate (Korean) [skip ci]

Currently translated at 60.9% (574 of 942 strings)

Translated using Weblate (Japanese) [skip ci]

Currently translated at 61.4% (579 of 942 strings)

Translated using Weblate (Icelandic) [skip ci]

Currently translated at 61.5% (580 of 942 strings)

Translated using Weblate (Hindi) [skip ci]

Currently translated at 61.4% (579 of 942 strings)

Translated using Weblate (Hebrew) [skip ci]

Currently translated at 64.0% (603 of 942 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 78.5% (740 of 942 strings)

Translated using Weblate (Finnish) [skip ci]

Currently translated at 88.1% (830 of 942 strings)

Translated using Weblate (Danish) [skip ci]

Currently translated at 61.9% (584 of 942 strings)

Translated using Weblate (Czech) [skip ci]

Currently translated at 61.6% (581 of 942 strings)

Translated using Weblate (Bulgarian) [skip ci]

Currently translated at 61.5% (580 of 942 strings)

Translated using Weblate (Arabic) [skip ci]

Currently translated at 61.6% (581 of 942 strings)

Translated using Weblate (Spanish) [skip ci]

Currently translated at 68.2% (643 of 942 strings)

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fatalicus <fatalicus@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/tr/
Translation: Servarr/Readarr
2023-06-15 13:16:52 +03:00
Bogdan
818d3a94d5 Add skip ci to API docs update commit 2023-06-15 13:15:37 +03:00
Bogdan
4e493b74e6 Update cleansing rules for RSS TL feed and homedir for Mac
(cherry picked from commit e5ff4aafa3f0b855fec332788e9fc490a03dfce3)

Closes #2593
2023-06-15 13:06:26 +03:00
Bogdan
c7eaf1e85c Update translations
(cherry picked from commit 26031389757f6b5270bbe5591101b08e58debb73)

Closes #2599
2023-06-15 13:02:50 +03:00
Bogdan
31fe15c911 Add HelpTextWarning support in FieldDefinition
(cherry picked from commit 0e07d54ee77d5f83716e17b6757e23f38ff73694)

Closes #2595
2023-06-15 13:00:42 +03:00
Bogdan
2c36a6c25f Require ApiKey for all actions in SonarrImport
(cherry picked from commit 19b8fbe13bf584b915a05fe9fc87622adbaee0b7)

Closes #2600
2023-06-15 12:58:43 +03:00
Bogdan
6af56f7a15 Fixed: Treat redirects as errors in Readarr Import List
(cherry picked from commit 059a156f4a34c6b9cbe139fa1973b814e8a534ae)

Closes #2601
2023-06-15 12:57:09 +03:00
Qstick
6e13191c25 Fixed: Correctly handle 302 and 303 redirects in HttpClient
(cherry picked from commit ed7c5a937f4b50fcdf819e8fe347c8c0bc6bd2e7)

(cherry picked from commit 11bd764a75d3b97117098738d3489c4b3329738f)
2023-06-14 08:13:20 +03:00
bakerboy448
921ddfc962 Fixed: Handle checkingResumeData state form qBittorrent
(cherry picked from commit 8d8a16225ff7772ccb57784f272ca31e28bb8455)
2023-06-14 08:12:58 +03:00
Weblate
22f977401a Update translation files [skip ci]
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/
Translation: Servarr/Readarr
2023-06-13 01:38:53 +03:00
Bogdan
113d9a07ef Update translations 2023-06-13 01:35:03 +03:00
Qstick
0560d65ea1 Update Remote Path Mapping delete modal title
(cherry picked from commit 18716a00516a971f7f2eb369b920266bea24fe08)

Closes #2588
Fixes #2587
2023-06-12 22:31:04 +03:00
Weblate
94ff105104 Translated using Weblate (Indonesian) [skip ci]
Currently translated at 3.7% (35 of 928 strings)

Co-authored-by: liimee <git.taaa@fedora.email>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/id/
Translation: Servarr/Readarr
2023-06-12 21:58:09 +03:00
Bogdan
9bcf258aa9 Bump version to 0.1.9 2023-06-11 09:38:16 +03:00
Bogdan
54985bd4ca Use more specific styling for kinds in ProgressBar
(cherry picked from commit dd31c913d2a974d95f3be251714ce749cfd99a72)
2023-06-10 02:07:40 +03:00
Weblate
9e4d551f08 Translated using Weblate (Russian) [skip ci]
Currently translated at 67.7% (629 of 928 strings)

Co-authored-by: Андрей <andryfly7@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ru/
Translation: Servarr/Readarr
2023-06-10 02:02:58 +03:00
Weblate
8390da1c2a Translated using Weblate (Greek) [skip ci]
Currently translated at 99.8% (927 of 928 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 78.4% (728 of 928 strings)

Translated using Weblate (Portuguese) [skip ci]

Currently translated at 77.0% (715 of 928 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 98.3% (913 of 928 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 98.3% (913 of 928 strings)

Translated using Weblate (Indonesian) [skip ci]

Currently translated at 3.5% (33 of 928 strings)

Translated using Weblate (Croatian) [skip ci]

Currently translated at 15.7% (146 of 928 strings)

Translated using Weblate (Ukrainian) [skip ci]

Currently translated at 66.7% (619 of 928 strings)

Translated using Weblate (Norwegian Bokmål) [skip ci]

Currently translated at 15.7% (146 of 928 strings)

Translated using Weblate (Catalan) [skip ci]

Currently translated at 67.7% (629 of 928 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (928 of 928 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (928 of 928 strings)

Translated using Weblate (Portuguese) [skip ci]

Currently translated at 76.9% (714 of 928 strings)

Translated using Weblate (Vietnamese) [skip ci]

Currently translated at 61.5% (571 of 928 strings)

Translated using Weblate (Turkish) [skip ci]

Currently translated at 61.6% (572 of 928 strings)

Translated using Weblate (Swedish) [skip ci]

Currently translated at 86.7% (805 of 928 strings)

Translated using Weblate (Russian) [skip ci]

Currently translated at 67.6% (628 of 928 strings)

Translated using Weblate (Polish) [skip ci]

Currently translated at 65.7% (610 of 928 strings)

Translated using Weblate (Japanese) [skip ci]

Currently translated at 61.5% (571 of 928 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 73.0% (678 of 928 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 98.0% (910 of 928 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 78.3% (727 of 928 strings)

Translated using Weblate (Greek) [skip ci]

Currently translated at 98.5% (915 of 928 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 98.1% (911 of 928 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 98.1% (911 of 928 strings)

Translated using Weblate (Dutch) [skip ci]

Currently translated at 68.2% (633 of 928 strings)

Translated using Weblate (Dutch) [skip ci]

Currently translated at 68.2% (633 of 928 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 78.3% (727 of 928 strings)

Translated using Weblate (Thai) [skip ci]

Currently translated at 61.5% (571 of 928 strings)

Translated using Weblate (Romanian) [skip ci]

Currently translated at 60.0% (557 of 928 strings)

Translated using Weblate (Icelandic) [skip ci]

Currently translated at 61.5% (571 of 928 strings)

Translated using Weblate (Hindi) [skip ci]

Currently translated at 61.5% (571 of 928 strings)

Translated using Weblate (Hebrew) [skip ci]

Currently translated at 64.0% (594 of 928 strings)

Translated using Weblate (Finnish) [skip ci]

Currently translated at 88.2% (819 of 928 strings)

Translated using Weblate (Danish) [skip ci]

Currently translated at 61.9% (575 of 928 strings)

Translated using Weblate (Bulgarian) [skip ci]

Currently translated at 61.5% (571 of 928 strings)

Translated using Weblate (Spanish) [skip ci]

Currently translated at 67.9% (631 of 928 strings)

Translated using Weblate (Arabic) [skip ci]

Currently translated at 61.6% (572 of 928 strings)

Translated using Weblate (Czech) [skip ci]

Currently translated at 61.6% (572 of 928 strings)

Update translation files [skip ci]

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Jens <jensmahnke@me.com>
Co-authored-by: MoowGlax <matthieu.derouet.pro@gmail.com>
Co-authored-by: Thijs Waalen <contact@thijswaalen.com>
Co-authored-by: Thodoris Kalatzis <teo.kal@hotmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: emacsdias <emacs.dias@gmail.com>
Co-authored-by: foXaCe <foxace66@gmail.com>
Co-authored-by: reloxx <reloxx@interia.pl>
Co-authored-by: splifter <a.strahlke@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/id/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/vi/
Translation: Servarr/Readarr
2023-06-07 19:21:06 +03:00
Qstick
44ae043c58 Bump version to 0.1.8 2023-06-04 14:48:35 -05:00
Bogdan
bb7e2fc70c Fixed: Don't log handled exceptions in API 2023-05-31 06:51:57 +03:00
Bogdan
b05938a9a8 Revert "Fixed: Don't log handled exceptions in API"
This reverts commit fecb3895ed.
2023-05-31 06:51:56 +03:00
Weblate
1e42ac572e Update translation files [skip ci]
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/
Translation: Servarr/Readarr
2023-05-28 22:28:24 +03:00
Servarr
649dd0bda0 Automated API Docs update 2023-05-28 22:27:52 +03:00
Bogdan
de24aef059 Deserialize asynchronously in LocalizationService
(cherry picked from commit 86a7f7bd54aa733b0e3abd3ec9463a85dd348118)

Closes #2561
2023-05-28 22:15:01 +03:00
Qstick
10766dd227 Faster tag view in UI for large libraries
(cherry picked from commit b050e1d2eb3bff9e28e7a1545d121be091789308)

Closes #2571
2023-05-28 22:08:34 +03:00
Bogdan
257d279e43 Fixed: Enforce validation warnings
(cherry picked from commit 48ee1158ad4213fd0690842e2672f52d08f7ad26)

Closes #2570
2023-05-28 22:02:41 +03:00
Bogdan
1db333088a Add minimum length as const in ApiKeyValidationCheck
(cherry picked from commit b06269544cfa11015f3fb938fd8c2ef07d9cac4a)

Closes #2565
2023-05-28 22:00:31 +03:00
Bogdan
d1aff31593 Use 'var' instead of explicit type
(cherry picked from commit 12374f7f0038e5b25548f5ab3f71122410832393)

Closes #2559
2023-05-28 21:59:25 +03:00
Bogdan
89dd4d3271 Inline 'out' variable declarations
(cherry picked from commit 281add47de1d3940990156c841362125dea9cc7d)

Closes #2558
2023-05-28 21:55:43 +03:00
Bogdan
fc6c78a54e Standardize variable declaration
(cherry picked from commit 909f2ded6b75998fa8e1addd0dcf849279e7b120)

Closes #2556
Closes #2557
2023-05-28 21:47:48 +03:00
Bogdan
c98f4512df Enforce rule IDE0005 on build
(cherry picked from commit 6b1e4ef81938d264a2ddc8b626b0502f799aa640)

Closes #2555
2023-05-28 21:46:59 +03:00
Matthew Strapp
0a43481aed Fixed: Use relative paths instead of absolute paths for webmanifest
(cherry picked from commit 8e771f95ade919a8f1ed7b48675f032a6c508cb2)
2023-05-28 21:42:27 +03:00
Bogdan
df6c142250 Simplify ShouldHaveApiKey and HasErrors
(cherry picked from commit 7343616a47cd538bba4c9128d2c1094561f9b3a5)
2023-05-28 21:41:07 +03:00
Weblate
58cf93e360 Update translation files [skip ci]
Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Icelandic) [skip ci]

Currently translated at 61.4% (567 of 922 strings)

Translated using Weblate (Hindi) [skip ci]

Currently translated at 61.4% (567 of 922 strings)

Translated using Weblate (Hebrew) [skip ci]

Currently translated at 63.9% (590 of 922 strings)

Translated using Weblate (Finnish) [skip ci]

Currently translated at 88.3% (815 of 922 strings)

Translated using Weblate (Danish) [skip ci]

Currently translated at 61.9% (571 of 922 strings)

Translated using Weblate (Czech) [skip ci]

Currently translated at 61.6% (568 of 922 strings)

Translated using Weblate (Bulgarian) [skip ci]

Currently translated at 61.4% (567 of 922 strings)

Translated using Weblate (Arabic) [skip ci]

Currently translated at 61.6% (568 of 922 strings)

Translated using Weblate (Spanish) [skip ci]

Currently translated at 68.0% (627 of 922 strings)

Update translation files [skip ci]

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/is/
Translation: Servarr/Readarr
2023-05-28 10:25:24 -05:00
Qstick
7be282ad12 Bump version to 0.1.7 2023-05-28 09:27:02 -05:00
218 changed files with 1257 additions and 819 deletions

View File

@@ -36,12 +36,18 @@ dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _ dotnet_naming_style.instance_field_style.required_prefix = _
# Prefer "var" everywhere # Prefer "var" everywhere
csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true:suggestion csharp_style_var_when_type_is_apparent = true
csharp_style_var_elsewhere = true:suggestion csharp_style_var_elsewhere = true
# Prefer "out" variables to be declared inline
csharp_style_inlined_variable_declaration = true
# Using directive is unnecessary. # Using directive is unnecessary.
dotnet_diagnostic.IDE0005.severity = error dotnet_diagnostic.IDE0005.severity = error
# Use var instead of explicit type
dotnet_diagnostic.IDE0007.severity = error
# Inline variable declaration
dotnet_diagnostic.IDE0018.severity = error
# Stylecop Rules # Stylecop Rules
dotnet_diagnostic.SA0001.severity = none dotnet_diagnostic.SA0001.severity = none

View File

@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests' testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '0.1.6' majorVersion: '0.2.0'
minorVersion: $[counter('minorVersion', 1)] minorVersion: $[counter('minorVersion', 1)]
readarrVersion: '$(majorVersion).$(minorVersion)' readarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(readarrVersion)' buildName: '$(Build.SourceBranchName).$(readarrVersion)'
@@ -984,7 +984,7 @@ stages:
git status git status
if git status | grep modified if git status | grep modified
then then
git commit -am 'Automated API Docs update' git commit -am 'Automated API Docs update [skip ci]'
git push -f --set-upstream origin api-docs git push -f --set-upstream origin api-docs
curl -X POST -H "Authorization: token ${GITHUBTOKEN}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/readarr/readarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}' curl -X POST -H "Authorization: token ${GITHUBTOKEN}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/readarr/readarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
else else

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ConfirmModal from 'Components/Modal/ConfirmModal'; import ConfirmModal from 'Components/Modal/ConfirmModal';
import PageContent from 'Components/Page/PageContent'; import PageContent from 'Components/Page/PageContent';
@@ -161,16 +162,16 @@ class Blocklist extends Component {
{ {
!isAnyFetching && !!error && !isAnyFetching && !!error &&
<div> <Alert kind={kinds.DANGER}>
{translate('UnableToLoadBlocklist')} {translate('UnableToLoadBlocklist')}
</div> </Alert>
} }
{ {
isAllPopulated && !error && !items.length && isAllPopulated && !error && !items.length &&
<div> <Alert kind={kinds.INFO}>
{translate('NoHistoryBlocklist')} {translate('NoHistoryBlocklist')}
</div> </Alert>
} }
{ {
@@ -214,7 +215,7 @@ class Blocklist extends Component {
isOpen={isConfirmRemoveModalOpen} isOpen={isConfirmRemoveModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title={translate('RemoveSelected')} title={translate('RemoveSelected')}
message={translate('RemoveSelectedMessageText')} message={translate('RemoveSelectedItemBlocklistMessageText')}
confirmLabel={translate('RemoveSelected')} confirmLabel={translate('RemoveSelected')}
onConfirm={this.onRemoveSelectedConfirmed} onConfirm={this.onRemoveSelectedConfirmed}
onCancel={this.onConfirmRemoveModalClose} onCancel={this.onConfirmRemoveModalClose}

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import FilterMenu from 'Components/Menu/FilterMenu'; import FilterMenu from 'Components/Menu/FilterMenu';
import PageContent from 'Components/Page/PageContent'; import PageContent from 'Components/Page/PageContent';
@@ -11,7 +12,7 @@ import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody'; import TableBody from 'Components/Table/TableBody';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper'; import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import TablePager from 'Components/Table/TablePager'; import TablePager from 'Components/Table/TablePager';
import { align, icons } from 'Helpers/Props'; import { align, icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import HistoryRowConnector from './HistoryRowConnector'; import HistoryRowConnector from './HistoryRowConnector';
@@ -85,9 +86,9 @@ class History extends Component {
{ {
!isFetchingAny && hasError && !isFetchingAny && hasError &&
<div> <Alert kind={kinds.DANGER}>
{translate('UnableToLoadHistory')} {translate('UnableToLoadHistory')}
</div> </Alert>
} }
{ {
@@ -95,9 +96,9 @@ class History extends Component {
// wait for the books to populate because they are never coming. // wait for the books to populate because they are never coming.
isPopulated && !hasError && !items.length && isPopulated && !hasError && !items.length &&
<div> <Alert kind={kinds.INFO}>
No history found {translate('NoHistory')}
</div> </Alert>
} }
{ {

View File

@@ -1,6 +1,7 @@
import _ from 'lodash'; import _ from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent'; import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody'; import PageContentBody from 'Components/Page/PageContentBody';
@@ -12,7 +13,7 @@ import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody'; import TableBody from 'Components/Table/TableBody';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper'; import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import TablePager from 'Components/Table/TablePager'; import TablePager from 'Components/Table/TablePager';
import { align, icons } from 'Helpers/Props'; import { align, icons, kinds } from 'Helpers/Props';
import getRemovedItems from 'Utilities/Object/getRemovedItems'; import getRemovedItems from 'Utilities/Object/getRemovedItems';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
@@ -233,17 +234,17 @@ class Queue extends Component {
{ {
!isRefreshing && hasError ? !isRefreshing && hasError ?
<div> <Alert kind={kinds.DANGER}>
{translate('FailedToLoadQueue')} {translate('FailedToLoadQueue')}
</div> : </Alert> :
null null
} }
{ {
isAllPopulated && !hasError && !items.length ? isAllPopulated && !hasError && !items.length ?
<div> <Alert kind={kinds.INFO}>
{translate('QueueIsEmpty')} {translate('QueueIsEmpty')}
</div> : </Alert> :
null null
} }

View File

@@ -89,12 +89,12 @@ class RemoveQueueItemsModal extends Component {
onModalClose={this.onModalClose} onModalClose={this.onModalClose}
> >
<ModalHeader> <ModalHeader>
Remove Selected Item{selectedCount > 1 ? 's' : ''} {selectedCount > 1 ? translate('RemoveSelectedItems') : translate('RemoveSelectedItem')}
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
<div className={styles.message}> <div className={styles.message}>
Are you sure you want to remove {selectedCount} item{selectedCount > 1 ? 's' : ''} from the queue? {selectedCount > 1 ? translate('RemoveSelectedItemsQueueMessageText', selectedCount) : translate('RemoveSelectedItemQueueMessageText')}
</div> </div>
{ {
@@ -118,14 +118,14 @@ class RemoveQueueItemsModal extends Component {
<FormGroup> <FormGroup>
<FormLabel> <FormLabel>
Add Release{selectedCount > 1 ? 's' : ''} To Blocklist {selectedCount > 1 ? translate('BlocklistReleases') : translate('BlocklistRelease')}
</FormLabel> </FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="blocklist" name="blocklist"
value={blocklist} value={blocklist}
helpText={translate('BlocklistHelpText')} helpText={translate('BlocklistReleaseHelpText')}
onChange={this.onBlocklistChange} onChange={this.onBlocklistChange}
/> />
</FormGroup> </FormGroup>
@@ -150,14 +150,14 @@ class RemoveQueueItemsModal extends Component {
<ModalFooter> <ModalFooter>
<Button onPress={this.onModalClose}> <Button onPress={this.onModalClose}>
Close {translate('Close')}
</Button> </Button>
<Button <Button
kind={kinds.DANGER} kind={kinds.DANGER}
onPress={this.onRemoveConfirmed} onPress={this.onRemoveConfirmed}
> >
Remove {translate('Remove')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

@@ -1,8 +1,10 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import Table from 'Components/Table/Table'; import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody'; import TableBody from 'Components/Table/TableBody';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import AuthorHistoryRowConnector from './AuthorHistoryRowConnector'; import AuthorHistoryRowConnector from './AuthorHistoryRowConnector';
@@ -70,9 +72,9 @@ class AuthorHistoryTableContent extends Component {
{ {
!isFetching && !!error && !isFetching && !!error &&
<div> <Alert kind={kinds.DANGER}>
{translate('UnableToLoadHistory')} {translate('UnableToLoadHistory')}
</div> </Alert>
} }
{ {

View File

@@ -1,6 +1,8 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import AgendaConnector from './Agenda/AgendaConnector'; import AgendaConnector from './Agenda/AgendaConnector';
import * as calendarViews from './calendarViews'; import * as calendarViews from './calendarViews';
@@ -31,9 +33,9 @@ class Calendar extends Component {
{ {
!isFetching && !!error && !isFetching && !!error &&
<div> <Alert kind={kinds.DANGER}>
{translate('UnableToLoadTheCalendar')} {translate('UnableToLoadTheCalendar')}
</div> </Alert>
} }
{ {

View File

@@ -62,6 +62,7 @@ function ProviderFieldFormGroup(props) {
name, name,
label, label,
helpText, helpText,
helpTextWarning,
helpLink, helpLink,
placeholder, placeholder,
value, value,
@@ -95,6 +96,7 @@ function ProviderFieldFormGroup(props) {
name={name} name={name}
label={label} label={label}
helpText={helpText} helpText={helpText}
helpTextWarning={helpTextWarning}
helpLink={helpLink} helpLink={helpLink}
placeholder={placeholder} placeholder={placeholder}
value={value} value={value}
@@ -121,6 +123,7 @@ ProviderFieldFormGroup.propTypes = {
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired, label: PropTypes.string.isRequired,
helpText: PropTypes.string, helpText: PropTypes.string,
helpTextWarning: PropTypes.string,
helpLink: PropTypes.string, helpLink: PropTypes.string,
placeholder: PropTypes.string, placeholder: PropTypes.string,
value: PropTypes.any, value: PropTypes.any,

View File

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

View File

@@ -16,6 +16,38 @@
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
color: var(--white); color: var(--white);
transition: width 0.6s ease; transition: width 0.6s ease;
&.primary {
background-color: var(--primaryColor);
}
&.danger {
background-color: var(--dangerColor);
&:global(.colorImpaired) {
background: repeating-linear-gradient(90deg, color(#f05050 shade(5%)), color(#f05050 shade(5%)) 5px, color(#f05050 shade(15%)) 5px, color(#f05050 shade(15%)) 10px);
}
}
&.success {
background-color: var(--successColor);
}
&.purple {
background-color: var(--purple);
}
&.warning {
background-color: var(--warningColor);
&:global(.colorImpaired) {
background: repeating-linear-gradient(45deg, #ffa500, #ffa500 5px, color(#ffa500 tint(15%)) 5px, color(#ffa500 tint(15%)) 10px);
}
}
&.info {
background-color: var(--infoColor);
}
} }
.frontTextContainer { .frontTextContainer {
@@ -45,38 +77,6 @@
cursor: default; cursor: default;
} }
.primary {
background-color: var(--primaryColor);
}
.danger {
background-color: var(--dangerColor);
&:global(.colorImpaired) {
background: repeating-linear-gradient(90deg, color(#f05050 shade(5%)), color(#f05050 shade(5%)) 5px, color(#f05050 shade(15%)) 5px, color(#f05050 shade(15%)) 10px);
}
}
.success {
background-color: var(--successColor);
}
.purple {
background-color: var(--purple);
}
.warning {
background-color: var(--warningColor);
&:global(.colorImpaired) {
background: repeating-linear-gradient(45deg, #ffa500, #ffa500 5px, color(#ffa500 tint(15%)) 5px, color(#ffa500 tint(15%)) 10px);
}
}
.info {
background-color: var(--infoColor);
}
.small { .small {
height: $progressBarSmallHeight; height: $progressBarSmallHeight;

View File

@@ -38,7 +38,7 @@ function ProgressBar(props) {
{ {
showText && width ? showText && width ?
<div <div
className={styles.backTextContainer} className={classNames(styles.backTextContainer, styles[kind])}
style={{ width: actualWidth }} style={{ width: actualWidth }}
> >
<div className={styles.backText}> <div className={styles.backText}>
@@ -67,7 +67,7 @@ function ProgressBar(props) {
{ {
showText ? showText ?
<div <div
className={styles.frontTextContainer} className={classNames(styles.frontTextContainer, styles[kind])}
style={{ width: progressPercent }} style={{ width: progressPercent }}
> >
<div <div

View File

@@ -1,18 +1,19 @@
{ {
"name": "", "name": "Readarr",
"icons": [ "icons": [
{ {
"src": "/Content/Images/Icons/android-chrome-192x192.png", "src": "android-chrome-192x192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "/Content/Images/Icons/android-chrome-512x512.png", "src": "android-chrome-512x512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
} }
], ],
"start_url": "../../../../",
"theme_color": "#3a3f51", "theme_color": "#3a3f51",
"background_color": "#3a3f51", "background_color": "#3a3f51",
"display": "standalone" "display": "standalone"
} }

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Alert from 'Components/Alert';
import Form from 'Components/Form/Form'; import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup'; import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup'; import FormInputGroup from 'Components/Form/FormInputGroup';
@@ -92,9 +93,9 @@ class SelectQualityModalContent extends Component {
{ {
!isFetching && !!error && !isFetching && !!error &&
<div> <Alert kind={kinds.DANGER}>
{translate('UnableToLoadQualities')} {translate('UnableToLoadQualities')}
</div> </Alert>
} }
{ {

View File

@@ -7,3 +7,9 @@
.filteredMessage { .filteredMessage {
margin-top: 10px; margin-top: 10px;
} }
.blankpad {
padding-top: 10px;
padding-bottom: 10px;
padding-left: 2em;
}

View File

@@ -1,6 +1,7 @@
// This file is automatically generated. // This file is automatically generated.
// Please do not change this file! // Please do not change this file!
interface CssExports { interface CssExports {
'blankpad': string;
'filterMenuContainer': string; 'filterMenuContainer': string;
'filteredMessage': string; 'filteredMessage': string;
} }

View File

@@ -104,7 +104,7 @@ function InteractiveSearch(props) {
{ {
!isFetching && error ? !isFetching && error ?
<div> <div className={styles.blankpad}>
Unable to load results for this book search. Try again later Unable to load results for this book search. Try again later
</div> : </div> :
null null
@@ -112,7 +112,7 @@ function InteractiveSearch(props) {
{ {
!isFetching && isPopulated && !totalReleasesCount ? !isFetching && isPopulated && !totalReleasesCount ?
<div> <div className={styles.blankpad}>
No results found No results found
</div> : </div> :
null null
@@ -120,7 +120,7 @@ function InteractiveSearch(props) {
{ {
!!totalReleasesCount && isPopulated && !items.length ? !!totalReleasesCount && isPopulated && !items.length ?
<div> <div className={styles.blankpad}>
All results are hidden by the applied filter All results are hidden by the applied filter
</div> : </div> :
null null

View File

@@ -78,7 +78,7 @@ class Specification extends Component {
<IconButton <IconButton
className={styles.cloneButton} className={styles.cloneButton}
title={translate('Clone')} title={translate('CloneCondition')}
name={icons.CLONE} name={icons.CLONE}
onPress={this.onCloneSpecificationPress} onPress={this.onCloneSpecificationPress}
/> />
@@ -92,14 +92,14 @@ class Specification extends Component {
{ {
negate && negate &&
<Label kind={kinds.DANGER}> <Label kind={kinds.DANGER}>
Negated {translate('Negated')}
</Label> </Label>
} }
{ {
required && required &&
<Label kind={kinds.SUCCESS}> <Label kind={kinds.SUCCESS}>
Required {translate('Required')}
</Label> </Label>
} }
</div> </div>
@@ -114,8 +114,8 @@ class Specification extends Component {
<ConfirmModal <ConfirmModal
isOpen={this.state.isDeleteSpecificationModalOpen} isOpen={this.state.isDeleteSpecificationModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title={translate('DeleteFormat')} title={translate('DeleteCondition')}
message={translate('DeleteFormatMessageText', [name])} message={translate('DeleteConditionMessageText', [name])}
confirmLabel={translate('Delete')} confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteSpecification} onConfirm={this.onConfirmDeleteSpecification}
onCancel={this.onDeleteSpecificationModalClose} onCancel={this.onDeleteSpecificationModalClose}

View File

@@ -1,12 +1,13 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Alert from 'Components/Alert';
import FieldSet from 'Components/FieldSet'; import FieldSet from 'Components/FieldSet';
import Form from 'Components/Form/Form'; import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup'; import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup'; import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel'; import FormLabel from 'Components/Form/FormLabel';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import { inputTypes, sizes } from 'Helpers/Props'; import { inputTypes, kinds, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
function DownloadClientOptions(props) { function DownloadClientOptions(props) {
@@ -28,9 +29,9 @@ function DownloadClientOptions(props) {
{ {
!isFetching && error && !isFetching && error &&
<div> <Alert kind={kinds.DANGER}>
{translate('UnableToLoadDownloadClientOptions')} {translate('UnableToLoadDownloadClientOptions')}
</div> </Alert>
} }
{ {

View File

@@ -88,8 +88,8 @@ class RemotePathMapping extends Component {
<ConfirmModal <ConfirmModal
isOpen={this.state.isDeleteRemotePathMappingModalOpen} isOpen={this.state.isDeleteRemotePathMappingModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title={translate('DeleteDelayProfile')} title={translate('DeleteRemotePathMapping')}
message={translate('DeleteDelayProfileMessageText')} message={translate('DeleteRemotePathMappingMessageText')}
confirmLabel={translate('Delete')} confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteRemotePathMapping} onConfirm={this.onConfirmDeleteRemotePathMapping}
onCancel={this.onDeleteRemotePathMappingModalClose} onCancel={this.onDeleteRemotePathMappingModalClose}

View File

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

View File

@@ -1,12 +1,13 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Alert from 'Components/Alert';
import FieldSet from 'Components/FieldSet'; import FieldSet from 'Components/FieldSet';
import Form from 'Components/Form/Form'; import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup'; import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup'; import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel'; import FormLabel from 'Components/Form/FormLabel';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import { inputTypes } from 'Helpers/Props'; import { inputTypes, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
function IndexerOptions(props) { function IndexerOptions(props) {
@@ -28,9 +29,9 @@ function IndexerOptions(props) {
{ {
!isFetching && error && !isFetching && error &&
<div> <Alert kind={kinds.DANGER}>
{translate('UnableToLoadIndexerOptions')} {translate('UnableToLoadIndexerOptions')}
</div> </Alert>
} }
{ {

View File

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

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Alert from 'Components/Alert';
import FieldSet from 'Components/FieldSet'; import FieldSet from 'Components/FieldSet';
import Form from 'Components/Form/Form'; import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup'; import FormGroup from 'Components/Form/FormGroup';
@@ -7,7 +8,7 @@ import FormInputButton from 'Components/Form/FormInputButton';
import FormInputGroup from 'Components/Form/FormInputGroup'; import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel'; import FormLabel from 'Components/Form/FormLabel';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import { inputTypes, sizes } from 'Helpers/Props'; import { inputTypes, kinds, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import NamingModal from './NamingModal'; import NamingModal from './NamingModal';
import styles from './Naming.css'; import styles from './Naming.css';
@@ -118,9 +119,9 @@ class Naming extends Component {
{ {
!isFetching && error && !isFetching && error &&
<div> <Alert kind={kinds.DANGER}>
{translate('UnableToLoadNamingSettings')} {translate('UnableToLoadNamingSettings')}
</div> </Alert>
} }
{ {

View File

@@ -1,12 +1,13 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Alert from 'Components/Alert';
import FieldSet from 'Components/FieldSet'; import FieldSet from 'Components/FieldSet';
import Form from 'Components/Form/Form'; import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup'; import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup'; import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel'; import FormLabel from 'Components/Form/FormLabel';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import { inputTypes } from 'Helpers/Props'; import { inputTypes, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
const writeAudioTagOptions = [ const writeAudioTagOptions = [
@@ -41,9 +42,9 @@ function MetadataProvider(props) {
{ {
!isFetching && error && !isFetching && error &&
<div> <Alert kind={kinds.DANGER}>
{translate('UnableToLoadMetadataProviderSettings')} {translate('UnableToLoadMetadataProviderSettings')}
</div> </Alert>
} }
{ {

View File

@@ -55,12 +55,12 @@ class ResetQualityDefinitionsModalContent extends Component {
onModalClose={onModalClose} onModalClose={onModalClose}
> >
<ModalHeader> <ModalHeader>
Reset Quality Definitions {translate('ResetQualityDefinitions')}
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
<div className={styles.messageContainer}> <div className={styles.messageContainer}>
Are you sure you want to reset quality definitions? {translate('ResetQualityDefinitionsMessageText')}
</div> </div>
<FormGroup> <FormGroup>
@@ -81,7 +81,7 @@ class ResetQualityDefinitionsModalContent extends Component {
<ModalFooter> <ModalFooter>
<Button onPress={onModalClose}> <Button onPress={onModalClose}>
Cancel {translate('Cancel')}
</Button> </Button>
<Button <Button
@@ -89,7 +89,7 @@ class ResetQualityDefinitionsModalContent extends Component {
onPress={this.onResetQualityDefinitionsConfirmed} onPress={this.onResetQualityDefinitionsConfirmed}
isDisabled={isResettingQualityDefinitions} isDisabled={isResettingQualityDefinitions}
> >
Reset {translate('Reset')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

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

View File

@@ -32,9 +32,9 @@ function createSaveProviderHandler(section, url, options = {}, removeStale = fal
const params = { ...queryParams }; const params = { ...queryParams };
// If the user is re-saving the same provider without changes // If the user is re-saving the same provider without changes
// force it to be saved. Only applies to editing existing providers. // force it to be saved.
if (id && _.isEqual(saveData, lastSaveData)) { if (_.isEqual(saveData, lastSaveData)) {
params.forceSave = true; params.forceSave = true;
} }

View File

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

View File

@@ -30,6 +30,13 @@
<!-- A test project gets the test sdk packages automatically added --> <!-- A test project gets the test sdk packages automatically added -->
<TestProject>false</TestProject> <TestProject>false</TestProject>
<TestProject Condition="$(MSBuildProjectName.EndsWith('.Test'))">true</TestProject> <TestProject Condition="$(MSBuildProjectName.EndsWith('.Test'))">true</TestProject>
<!-- XML documentation comments are needed to enforce rule IDE0005 on build -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!--
CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
-->
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -27,35 +27,36 @@ namespace NzbDrone.Api.Test.ClientSchemaTests
var schema = SchemaBuilder.ToSchema(model); var schema = SchemaBuilder.ToSchema(model);
schema.Should().Contain(c => schema.Should().Contain(c => c.Order == 1 && c.Name == "lastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && c.HelpTextWarning == "Mandatory Last Name" && (string)c.Value == "Poop");
c.Order == 1 && c.Name == "lastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && schema.Should().Contain(c => c.Order == 0 && c.Name == "firstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && c.HelpTextWarning == "Mandatory First Name" && (string)c.Value == "Bob");
(string)c.Value == "Poop");
schema.Should().Contain(c =>
c.Order == 0 && c.Name == "firstName" && c.Label == "First Name" && c.HelpText == "Your First Name" &&
(string)c.Value == "Bob");
} }
[Test] [Test]
public void schema_should_have_nested_fields() public void schema_should_have_nested_fields()
{ {
var model = new NestedTestModel(); var model = new NestedTestModel
model.Name.FirstName = "Bob"; {
model.Name.LastName = "Poop"; Name =
{
FirstName = "Bob",
LastName = "Poop"
}
};
var schema = SchemaBuilder.ToSchema(model); var schema = SchemaBuilder.ToSchema(model);
schema.Should().Contain(c => c.Order == 0 && c.Name == "name.firstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && (string)c.Value == "Bob"); schema.Should().Contain(c => c.Order == 0 && c.Name == "name.firstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && c.HelpTextWarning == "Mandatory First Name" && (string)c.Value == "Bob");
schema.Should().Contain(c => c.Order == 1 && c.Name == "name.lastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && (string)c.Value == "Poop"); schema.Should().Contain(c => c.Order == 1 && c.Name == "name.lastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && c.HelpTextWarning == "Mandatory Last Name" && (string)c.Value == "Poop");
schema.Should().Contain(c => c.Order == 2 && c.Name == "quote" && c.Label == "Quote" && c.HelpText == "Your Favorite Quote"); schema.Should().Contain(c => c.Order == 2 && c.Name == "quote" && c.Label == "Quote" && c.HelpText == "Your Favorite Quote");
} }
} }
public class TestModel public class TestModel
{ {
[FieldDefinition(0, Label = "First Name", HelpText = "Your First Name")] [FieldDefinition(0, Label = "First Name", HelpText = "Your First Name", HelpTextWarning = "Mandatory First Name")]
public string FirstName { get; set; } public string FirstName { get; set; }
[FieldDefinition(1, Label = "Last Name", HelpText = "Your Last Name")] [FieldDefinition(1, Label = "Last Name", HelpText = "Your Last Name", HelpTextWarning = "Mandatory Last Name")]
public string LastName { get; set; } public string LastName { get; set; }
public string Other { get; set; } public string Other { get; set; }

View File

@@ -68,7 +68,7 @@ namespace NzbDrone.Automation.Test
{ {
try try
{ {
Screenshot image = ((ITakesScreenshot)driver).GetScreenshot(); var image = ((ITakesScreenshot)driver).GetScreenshot();
image.SaveAsFile($"./{name}_test_screenshot.png", ScreenshotImageFormat.Png); image.SaveAsFile($"./{name}_test_screenshot.png", ScreenshotImageFormat.Png);
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -37,7 +37,7 @@ namespace NzbDrone.Automation.Test.PageModel
{ {
try try
{ {
IWebElement element = d.FindElement(By.ClassName("followingBalls")); var element = d.FindElement(By.ClassName("followingBalls"));
return !element.Displayed; return !element.Displayed;
} }
catch (NoSuchElementException) catch (NoSuchElementException)

View File

@@ -65,9 +65,9 @@ namespace NzbDrone.Common.Test.CacheTests
[Test] [Test]
public void should_store_null() public void should_store_null()
{ {
int hitCount = 0; var hitCount = 0;
for (int i = 0; i < 10; i++) for (var i = 0; i < 10; i++)
{ {
_cachedString.Get("key", () => _cachedString.Get("key", () =>
{ {
@@ -83,10 +83,10 @@ namespace NzbDrone.Common.Test.CacheTests
[Platform(Exclude = "MacOsX")] [Platform(Exclude = "MacOsX")]
public void should_honor_ttl() public void should_honor_ttl()
{ {
int hitCount = 0; var hitCount = 0;
_cachedString = new Cached<string>(); _cachedString = new Cached<string>();
for (int i = 0; i < 10; i++) for (var i = 0; i < 10; i++)
{ {
_cachedString.Get("key", _cachedString.Get("key",
() => () =>
@@ -107,10 +107,10 @@ namespace NzbDrone.Common.Test.CacheTests
[Platform(Exclude = "MacOsX")] [Platform(Exclude = "MacOsX")]
public void should_clear_expired_when_they_expire() public void should_clear_expired_when_they_expire()
{ {
int hitCount = 0; var hitCount = 0;
_cachedString = new Cached<string>(rollingExpiry: true); _cachedString = new Cached<string>(rollingExpiry: true);
for (int i = 0; i < 10; i++) for (var i = 0; i < 10; i++)
{ {
_cachedString.Get("key", _cachedString.Get("key",
() => () =>

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@@ -142,7 +142,7 @@ namespace NzbDrone.Common.Test
[Test] [Test]
public void SaveDictionary_should_save_proper_value() public void SaveDictionary_should_save_proper_value()
{ {
int port = 20555; var port = 20555;
var dic = Subject.GetConfigDictionary(); var dic = Subject.GetConfigDictionary();
dic["Port"] = 20555; dic["Port"] = 20555;
@@ -155,9 +155,9 @@ namespace NzbDrone.Common.Test
[Test] [Test]
public void SaveDictionary_should_only_save_specified_values() public void SaveDictionary_should_only_save_specified_values()
{ {
int port = 20555; var port = 20555;
int origSslPort = 20551; var origSslPort = 20551;
int sslPort = 20552; var sslPort = 20552;
var dic = Subject.GetConfigDictionary(); var dic = Subject.GetConfigDictionary();
dic["Port"] = port; dic["Port"] = port;

View File

@@ -43,7 +43,7 @@ namespace NzbDrone.Common.Test.DiskTests
[Test] [Test]
public void should_not_contain_recycling_bin_for_root_of_drive() public void should_not_contain_recycling_bin_for_root_of_drive()
{ {
string root = @"C:\".AsOsAgnostic(); var root = @"C:\".AsOsAgnostic();
SetupFolders(root); SetupFolders(root);
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
@@ -56,7 +56,7 @@ namespace NzbDrone.Common.Test.DiskTests
[Test] [Test]
public void should_not_contain_system_volume_information() public void should_not_contain_system_volume_information()
{ {
string root = @"C:\".AsOsAgnostic(); var root = @"C:\".AsOsAgnostic();
SetupFolders(root); SetupFolders(root);
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
@@ -69,7 +69,7 @@ namespace NzbDrone.Common.Test.DiskTests
[Test] [Test]
public void should_not_contain_recycling_bin_or_system_volume_information_for_root_of_drive() public void should_not_contain_recycling_bin_or_system_volume_information_for_root_of_drive()
{ {
string root = @"C:\".AsOsAgnostic(); var root = @"C:\".AsOsAgnostic();
SetupFolders(root); SetupFolders(root);
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()

View File

@@ -791,7 +791,7 @@ namespace NzbDrone.Common.Test.Http
try try
{ {
// the date is bad in the below - should be 13-Jul-2026 // the date is bad in the below - should be 13-Jul-2026
string malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Mon, 13-Jul-26 16:19:50 GMT; path=/; HttpOnly"; var malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Mon, 13-Jul-26 16:19:50 GMT; path=/; HttpOnly";
var requestSet = new HttpRequestBuilder($"https://{_httpBinHost}/response-headers") var requestSet = new HttpRequestBuilder($"https://{_httpBinHost}/response-headers")
.AddQueryParam("Set-Cookie", malformedCookie) .AddQueryParam("Set-Cookie", malformedCookie)
.Build(); .Build();
@@ -825,7 +825,7 @@ namespace NzbDrone.Common.Test.Http
{ {
try try
{ {
string url = $"https://{_httpBinHost}/response-headers?Set-Cookie={Uri.EscapeDataString(malformedCookie)}"; var url = $"https://{_httpBinHost}/response-headers?Set-Cookie={Uri.EscapeDataString(malformedCookie)}";
var requestSet = new HttpRequest(url); var requestSet = new HttpRequest(url);
requestSet.AllowAutoRedirect = false; requestSet.AllowAutoRedirect = false;

View File

@@ -10,7 +10,9 @@ namespace NzbDrone.Common.Test.InstrumentationTests
// Indexer Urls // Indexer Urls
[TestCase(@"https://iptorrents.com/torrents/rss?u=mySecret;tp=mySecret;l5;download")] [TestCase(@"https://iptorrents.com/torrents/rss?u=mySecret;tp=mySecret;l5;download")]
[TestCase(@"http://rss.torrentleech.org/mySecret")] [TestCase(@"http://rss.torrentleech.org/mySecret")]
[TestCase(@"http://rss.torrentleech.org/rss/download/12345/01233210/filename.torrent")] [TestCase(@"https://rss24h.torrentleech.org/mySecret")]
[TestCase(@"http://rss.torrentleech.org/rss/download/12345/01233210/file.name-RLSGRP.torrent")]
[TestCase(@"https://www.torrentleech.org/rss/download/12345/01233210/file.name-RLSGRP.torrent")]
[TestCase(@"http://www.bitmetv.org/rss.php?uid=mySecret&passkey=mySecret")] [TestCase(@"http://www.bitmetv.org/rss.php?uid=mySecret&passkey=mySecret")]
[TestCase(@"https://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user=Readarr&api=mySecret&eng=1")] [TestCase(@"https://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user=Readarr&api=mySecret&eng=1")]
[TestCase(@"https://dognzb.cr/fetch/2b51db35e1912ffc138825a12b9933d2/2b51db35e1910123321025a12b9933d2")] [TestCase(@"https://dognzb.cr/fetch/2b51db35e1912ffc138825a12b9933d2/2b51db35e1910123321025a12b9933d2")]
@@ -44,6 +46,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
// Deluge // Deluge
[TestCase(@",{""download_location"": ""C:\Users\\mySecret mySecret\\Downloads""}")] [TestCase(@",{""download_location"": ""C:\Users\\mySecret mySecret\\Downloads""}")]
[TestCase(@",{""download_location"": ""/home/mySecret/Downloads""}")] [TestCase(@",{""download_location"": ""/home/mySecret/Downloads""}")]
[TestCase(@",{""download_location"": ""/Users/mySecret/Downloads""}")]
[TestCase(@"auth.login(""mySecret"")")] [TestCase(@"auth.login(""mySecret"")")]
// Download Station // Download Station
@@ -56,8 +59,11 @@ namespace NzbDrone.Common.Test.InstrumentationTests
// Internal // Internal
[TestCase(@"OutputPath=/home/mySecret/Downloads")] [TestCase(@"OutputPath=/home/mySecret/Downloads")]
[TestCase(@"OutputPath=/Users/mySecret/Downloads")]
[TestCase("Hardlinking episode file: /home/mySecret/Downloads to /media/abc.mkv")] [TestCase("Hardlinking episode file: /home/mySecret/Downloads to /media/abc.mkv")]
[TestCase("Hardlinking episode file: /Users/mySecret/Downloads to /media/abc.mkv")]
[TestCase("Hardlink '/home/mySecret/Downloads/abs.mkv' to '/media/abc.mkv' failed.")] [TestCase("Hardlink '/home/mySecret/Downloads/abs.mkv' to '/media/abc.mkv' failed.")]
[TestCase("Hardlink '/Users/mySecret/Downloads/abs.mkv' to '/media/abc.mkv' failed.")]
[TestCase("https://notifiarr.com/notifier.php: api=1234530f-422f-4aac-b6b3-01233210aaaa&radarr_health_issue_message=Download")] [TestCase("https://notifiarr.com/notifier.php: api=1234530f-422f-4aac-b6b3-01233210aaaa&radarr_health_issue_message=Download")]
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")] [TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
[TestCase(@"[Info] MigrationController: *** Migrating Database=readarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;Enlist=False ***")] [TestCase(@"[Info] MigrationController: *** Migrating Database=readarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;Enlist=False ***")]

View File

@@ -74,17 +74,17 @@ namespace NzbDrone.Common
continue; // Ignore directories continue; // Ignore directories
} }
string entryFileName = zipEntry.Name; var entryFileName = zipEntry.Name;
// to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName); // to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
// Optionally match entrynames against a selection list here to skip as desired. // Optionally match entrynames against a selection list here to skip as desired.
// The unpacked length is available in the zipEntry.Size property. // The unpacked length is available in the zipEntry.Size property.
byte[] buffer = new byte[4096]; // 4K is optimum var buffer = new byte[4096]; // 4K is optimum
Stream zipStream = zipFile.GetInputStream(zipEntry); var zipStream = zipFile.GetInputStream(zipEntry);
// Manipulate the output filename here as desired. // Manipulate the output filename here as desired.
string fullZipToPath = Path.Combine(destination, entryFileName); var fullZipToPath = Path.Combine(destination, entryFileName);
string directoryName = Path.GetDirectoryName(fullZipToPath); var directoryName = Path.GetDirectoryName(fullZipToPath);
if (directoryName.Length > 0) if (directoryName.Length > 0)
{ {
Directory.CreateDirectory(directoryName); Directory.CreateDirectory(directoryName);
@@ -93,7 +93,7 @@ namespace NzbDrone.Common
// Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size // Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
// of the file, but does not waste memory. // of the file, but does not waste memory.
// The "using" will close the stream even if an exception occurs. // The "using" will close the stream even if an exception occurs.
using (FileStream streamWriter = File.Create(fullZipToPath)) using (var streamWriter = File.Create(fullZipToPath))
{ {
StreamUtils.Copy(zipStream, streamWriter, buffer); StreamUtils.Copy(zipStream, streamWriter, buffer);
} }
@@ -106,7 +106,7 @@ namespace NzbDrone.Common
Stream inStream = File.OpenRead(compressedFile); Stream inStream = File.OpenRead(compressedFile);
Stream gzipStream = new GZipInputStream(inStream); Stream gzipStream = new GZipInputStream(inStream);
TarArchive tarArchive = TarArchive.CreateInputTarArchive(gzipStream, null); var tarArchive = TarArchive.CreateInputTarArchive(gzipStream, null);
tarArchive.ExtractContents(destination); tarArchive.ExtractContents(destination);
tarArchive.Close(); tarArchive.Close();

View File

@@ -54,8 +54,7 @@ namespace NzbDrone.Common.Cache
public T Find(string key) public T Find(string key)
{ {
CacheItem cacheItem; if (!_store.TryGetValue(key, out var cacheItem))
if (!_store.TryGetValue(key, out cacheItem))
{ {
return default(T); return default(T);
} }
@@ -84,8 +83,7 @@ namespace NzbDrone.Common.Cache
public void Remove(string key) public void Remove(string key)
{ {
CacheItem value; _store.TryRemove(key, out _);
_store.TryRemove(key, out value);
} }
public int Count => _store.Count; public int Count => _store.Count;
@@ -96,9 +94,7 @@ namespace NzbDrone.Common.Cache
lifeTime = lifeTime ?? _defaultLifeTime; lifeTime = lifeTime ?? _defaultLifeTime;
CacheItem cacheItem; if (_store.TryGetValue(key, out var cacheItem) && !cacheItem.IsExpired())
if (_store.TryGetValue(key, out cacheItem) && !cacheItem.IsExpired())
{ {
if (_rollingExpiry && lifeTime.HasValue) if (_rollingExpiry && lifeTime.HasValue)
{ {

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -86,9 +86,7 @@ namespace NzbDrone.Common.Cache
{ {
RefreshIfExpired(); RefreshIfExpired();
TValue result; if (!_items.TryGetValue(key, out var result))
if (!_items.TryGetValue(key, out result))
{ {
throw new KeyNotFoundException(string.Format("Item {0} not found in cache.", key)); throw new KeyNotFoundException(string.Format("Item {0} not found in cache.", key));
} }
@@ -100,9 +98,7 @@ namespace NzbDrone.Common.Cache
{ {
RefreshIfExpired(); RefreshIfExpired();
TValue result; _items.TryGetValue(key, out var result);
_items.TryGetValue(key, out result);
return result; return result;
} }
@@ -128,8 +124,7 @@ namespace NzbDrone.Common.Cache
public void Remove(string key) public void Remove(string key)
{ {
TValue item; _items.TryRemove(key, out _);
_items.TryRemove(key, out item);
} }
} }
} }

View File

@@ -1,4 +1,4 @@
namespace NzbDrone.Common namespace NzbDrone.Common
{ {
public static class ConvertBase32 public static class ConvertBase32
{ {
@@ -6,17 +6,17 @@
public static byte[] FromBase32String(string str) public static byte[] FromBase32String(string str)
{ {
int numBytes = str.Length * 5 / 8; var numBytes = str.Length * 5 / 8;
byte[] bytes = new byte[numBytes]; var bytes = new byte[numBytes];
// all UPPERCASE chars // all UPPERCASE chars
str = str.ToUpper(); str = str.ToUpper();
int bitBuffer = 0; var bitBuffer = 0;
int bitBufferCount = 0; var bitBufferCount = 0;
int index = 0; var index = 0;
for (int i = 0; i < str.Length; i++) for (var i = 0; i < str.Length; i++)
{ {
bitBuffer = (bitBuffer << 5) | ValidChars.IndexOf(str[i]); bitBuffer = (bitBuffer << 5) | ValidChars.IndexOf(str[i]);
bitBufferCount += 5; bitBufferCount += 5;

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@@ -255,7 +255,7 @@ namespace NzbDrone.Common.Disk
var stringComparison = (Kind == OsPathKind.Windows || other.Kind == OsPathKind.Windows) ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture; var stringComparison = (Kind == OsPathKind.Windows || other.Kind == OsPathKind.Windows) ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture;
for (int i = 0; i < leftFragments.Length; i++) for (var i = 0; i < leftFragments.Length; i++)
{ {
if (!string.Equals(leftFragments[i], rightFragments[i], stringComparison)) if (!string.Equals(leftFragments[i], rightFragments[i], stringComparison))
{ {
@@ -372,12 +372,12 @@ namespace NzbDrone.Common.Disk
var newFragments = new List<string>(); var newFragments = new List<string>();
for (int j = i; j < rightFragments.Length; j++) for (var j = i; j < rightFragments.Length; j++)
{ {
newFragments.Add(".."); newFragments.Add("..");
} }
for (int j = i; j < leftFragments.Length; j++) for (var j = i; j < leftFragments.Length; j++)
{ {
newFragments.Add(leftFragments[j]); newFragments.Add(leftFragments[j]);
} }

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@@ -107,7 +107,7 @@ namespace NzbDrone.Common.EnvironmentInfo
private static string RunAndCapture(string filename, string args) private static string RunAndCapture(string filename, string args)
{ {
Process p = new Process(); var p = new Process();
p.StartInfo.FileName = filename; p.StartInfo.FileName = filename;
p.StartInfo.Arguments = args; p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false; p.StartInfo.UseShellExecute = false;
@@ -117,7 +117,7 @@ namespace NzbDrone.Common.EnvironmentInfo
p.Start(); p.Start();
// To avoid deadlocks, always read the output stream first and then wait. // To avoid deadlocks, always read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd(); var output = p.StandardOutput.ReadToEnd();
p.WaitForExit(1000); p.WaitForExit(1000);
return output; return output;

View File

@@ -356,34 +356,34 @@ namespace NzbDrone.Common.Extensions
} }
/** /**
* Computes the highest row in which the distance {@code p} appears * Computes the highest row in which the distance {@code p} appears
* in diagonal {@code k} of the edit distance computation for * in diagonal {@code k} of the edit distance computation for
* strings {@code a} and {@code b}. The diagonal number is * strings {@code a} and {@code b}. The diagonal number is
* represented by the difference in the indices for the two strings; * represented by the difference in the indices for the two strings;
* it can range from {@code -b.length()} through {@code a.length()}. * it can range from {@code -b.length()} through {@code a.length()}.
* *
* More precisely, this computes the highest value x such that * More precisely, this computes the highest value x such that
* <pre> * <pre>
* p = edit-distance(a[0:(x+k)), b[0:x)). * p = edit-distance(a[0:(x+k)), b[0:x)).
* </pre> * </pre>
* *
* This is the "f" function described by Ukkonen. * This is the "f" function described by Ukkonen.
* *
* The caller must assure that abs(k) &le; p, the only values for * The caller must assure that abs(k) &#x2264; p, the only values for
* which this is well-defined. * which this is well-defined.
* *
* The implementation depends on the cached results of prior * The implementation depends on the cached results of prior
* computeRow calls for diagonals k-1, k, and k+1 for distance p-1. * computeRow calls for diagonals k-1, k, and k+1 for distance p-1.
* These must be supplied in {@code knownLeft}, {@code knownAbove}, * These must be supplied in {@code knownLeft}, {@code knownAbove},
* and {@code knownRight}, respectively. * and {@code knownRight}, respectively.
* @param k diagonal number * @param k diagonal number
* @param p edit distance * @param p edit distance
* @param a one string to be compared * @param a one string to be compared
* @param b other string to be compared * @param b other string to be compared
* @param knownLeft value of {@code computeRow(k-1, p-1, ...)} * @param knownLeft value of {@code computeRow(k-1, p-1, ...)}
* @param knownAbove value of {@code computeRow(k, p-1, ...)} * @param knownAbove value of {@code computeRow(k, p-1, ...)}
* @param knownRight value of {@code computeRow(k+1, p-1, ...)} * @param knownRight value of {@code computeRow(k+1, p-1, ...)}
*/ */
private static int ComputeRow(int k, private static int ComputeRow(int k,
int p, int p,
char[] a, char[] a,

View File

@@ -136,7 +136,7 @@ namespace NzbDrone.Common.Extensions
for (var j = finish; j >= start; j--) for (var j = finish; j >= start; j--)
{ {
T charMatch = charMatches[j - 1]; var charMatch = charMatches[j - 1];
if (d == 0) if (d == 0)
{ {
@@ -181,7 +181,7 @@ namespace NzbDrone.Common.Extensions
// match. But check anyway. // match. But check anyway.
var score = BitapScore(d, pattern); var score = BitapScore(d, pattern);
bool isOnWordBoundary = true; var isOnWordBoundary = true;
if (wordDelimiters != null) if (wordDelimiters != null)
{ {
@@ -233,8 +233,8 @@ namespace NzbDrone.Common.Extensions
return new List<char>(); return new List<char>();
} }
char curr = text[j - 1]; var curr = text[j - 1];
bool take = true; var take = true;
if (!s.TryGetValue(curr, out var charMatch)) if (!s.TryGetValue(curr, out var charMatch))
{ {

View File

@@ -255,13 +255,13 @@ namespace NzbDrone.Common.Extensions
var firstPath = paths.First(); var firstPath = paths.First();
var length = firstPath.Length; var length = firstPath.Length;
for (int i = 1; i < paths.Count; i++) for (var i = 1; i < paths.Count; i++)
{ {
var path = paths[i]; var path = paths[i];
length = Math.Min(length, path.Length); length = Math.Min(length, path.Length);
for (int characterIndex = 0; characterIndex < length; characterIndex++) for (var characterIndex = 0; characterIndex < length; characterIndex++)
{ {
if (path[characterIndex] != firstPath[characterIndex]) if (path[characterIndex] != firstPath[characterIndex])
{ {

View File

@@ -188,11 +188,11 @@ namespace NzbDrone.Common.Extensions
{ {
double weightDenom = Math.Max(a.Length, b.Length); double weightDenom = Math.Max(a.Length, b.Length);
double sum = 0; double sum = 0;
for (int i = 0; i < a.Length; i++) for (var i = 0; i < a.Length; i++)
{ {
double high = 0.0; var high = 0.0;
int indexDistance = 0; var indexDistance = 0;
for (int x = 0; x < b.Length; x++) for (var x = 0; x < b.Length; x++)
{ {
var coef = LevenshteinCoefficient(a[i], b[x]); var coef = LevenshteinCoefficient(a[i], b[x]);
if (coef > high) if (coef > high)

View File

@@ -6,9 +6,7 @@ namespace NzbDrone.Common.Extensions
{ {
public static int? ParseInt32(this string source) public static int? ParseInt32(this string source)
{ {
int result; if (int.TryParse(source, out var result))
if (int.TryParse(source, out result))
{ {
return result; return result;
} }
@@ -18,9 +16,7 @@ namespace NzbDrone.Common.Extensions
public static long? ParseInt64(this string source) public static long? ParseInt64(this string source)
{ {
long result; if (long.TryParse(source, out var result))
if (long.TryParse(source, out result))
{ {
return result; return result;
} }
@@ -30,9 +26,7 @@ namespace NzbDrone.Common.Extensions
public static double? ParseDouble(this string source) public static double? ParseDouble(this string source)
{ {
double result; if (double.TryParse(source.Replace(',', '.'), NumberStyles.Number, CultureInfo.InvariantCulture, out var result))
if (double.TryParse(source.Replace(',', '.'), NumberStyles.Number, CultureInfo.InvariantCulture, out result))
{ {
return result; return result;
} }

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Text; using System.Text;
namespace NzbDrone.Common namespace NzbDrone.Common
@@ -7,9 +7,9 @@ namespace NzbDrone.Common
{ {
public static string CalculateCrc(string input) public static string CalculateCrc(string input)
{ {
uint mCrc = 0xffffffff; var mCrc = 0xffffffff;
byte[] bytes = Encoding.UTF8.GetBytes(input); var bytes = Encoding.UTF8.GetBytes(input);
foreach (byte myByte in bytes) foreach (var myByte in bytes)
{ {
mCrc ^= (uint)myByte << 24; mCrc ^= (uint)myByte << 24;
for (var i = 0; i < 8; i++) for (var i = 0; i < 8; i++)

View File

@@ -60,8 +60,7 @@ namespace NzbDrone.Common.Http
if (request.AllowAutoRedirect && response.HasHttpRedirect) if (request.AllowAutoRedirect && response.HasHttpRedirect)
{ {
var autoRedirectChain = new List<string>(); var autoRedirectChain = new List<string> { request.Url.ToString() };
autoRedirectChain.Add(request.Url.ToString());
do do
{ {
@@ -75,6 +74,14 @@ namespace NzbDrone.Common.Http
throw new WebException($"Too many automatic redirections were attempted for {autoRedirectChain.Join(" -> ")}", WebExceptionStatus.ProtocolError); 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 (RequestRequiresForceGet(response.StatusCode, response.Request.Method))
{
request.Method = HttpMethod.Get;
request.ContentData = null;
request.ContentSummary = null;
}
response = ExecuteRequest(request, cookieContainer); response = ExecuteRequest(request, cookieContainer);
} }
while (response.HasHttpRedirect); while (response.HasHttpRedirect);
@@ -105,6 +112,16 @@ namespace NzbDrone.Common.Http
return response; return response;
} }
private static bool RequestRequiresForceGet(HttpStatusCode statusCode, HttpMethod requestMethod)
{
return statusCode switch
{
HttpStatusCode.Moved or HttpStatusCode.Found or HttpStatusCode.MultipleChoices => requestMethod == HttpMethod.Post,
HttpStatusCode.SeeOther => requestMethod != HttpMethod.Get && requestMethod != HttpMethod.Head,
_ => false,
};
}
private HttpResponse ExecuteRequest(HttpRequest request, CookieContainer cookieContainer) private HttpResponse ExecuteRequest(HttpRequest request, CookieContainer cookieContainer)
{ {
foreach (var interceptor in _requestInterceptors) foreach (var interceptor in _requestInterceptors)

View File

@@ -50,6 +50,8 @@ namespace NzbDrone.Common.Http
public bool HasHttpError => (int)StatusCode >= 400; public bool HasHttpError => (int)StatusCode >= 400;
public bool HasHttpServerError => (int)StatusCode >= 500;
public bool HasHttpRedirect => StatusCode == HttpStatusCode.Moved || public bool HasHttpRedirect => StatusCode == HttpStatusCode.Moved ||
StatusCode == HttpStatusCode.MovedPermanently || StatusCode == HttpStatusCode.MovedPermanently ||
StatusCode == HttpStatusCode.Found || StatusCode == HttpStatusCode.Found ||

View File

@@ -22,7 +22,7 @@ namespace NzbDrone.Common.Http
public HttpUri(string scheme, string host, int? port, string path, string query, string fragment) public HttpUri(string scheme, string host, int? port, string path, string query, string fragment)
{ {
StringBuilder builder = new StringBuilder(); var builder = new StringBuilder();
if (scheme.IsNotNullOrWhiteSpace()) if (scheme.IsNotNullOrWhiteSpace())
{ {

View File

@@ -1,4 +1,4 @@
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Http.Proxy namespace NzbDrone.Common.Http.Proxy
{ {
@@ -30,7 +30,7 @@ namespace NzbDrone.Common.Http.Proxy
if (!string.IsNullOrWhiteSpace(BypassFilter)) if (!string.IsNullOrWhiteSpace(BypassFilter))
{ {
var hostlist = BypassFilter.Split(','); var hostlist = BypassFilter.Split(',');
for (int i = 0; i < hostlist.Length; i++) for (var i = 0; i < hostlist.Length; i++)
{ {
if (hostlist[i].StartsWith("*")) if (hostlist[i].StartsWith("*"))
{ {

View File

@@ -12,14 +12,12 @@ namespace NzbDrone.Common.Http
if (response.Headers.ContainsKey("Retry-After")) if (response.Headers.ContainsKey("Retry-After"))
{ {
var retryAfter = response.Headers["Retry-After"].ToString(); var retryAfter = response.Headers["Retry-After"].ToString();
int seconds;
DateTime date;
if (int.TryParse(retryAfter, out seconds)) if (int.TryParse(retryAfter, out var seconds))
{ {
RetryAfter = TimeSpan.FromSeconds(seconds); RetryAfter = TimeSpan.FromSeconds(seconds);
} }
else if (DateTime.TryParse(retryAfter, out date)) else if (DateTime.TryParse(retryAfter, out var date))
{ {
RetryAfter = date.ToUniversalTime() - DateTime.UtcNow; RetryAfter = date.ToUniversalTime() - DateTime.UtcNow;
} }

View File

@@ -7,56 +7,56 @@ namespace NzbDrone.Common.Instrumentation
{ {
public class CleanseLogMessage public class CleanseLogMessage
{ {
private static readonly Regex[] CleansingRules = new[] private static readonly Regex[] CleansingRules =
{ {
// Url // Url
new Regex(@"(?<=\?|&|: )((?:api|auth|pass)?key|(?:access[-_]?)?token|auth|user|uid|api|[a-z_]*apikey|account|passwd)=(?<secret>[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"(?<=\?|&|: )((?:api|auth|pass)?key|(?:access[-_]?)?token|auth|user|uid|api|[a-z_]*apikey|account|passwd)=(?<secret>[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=\?|&)[^=]*?(username|password)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"(?<=\?|&)[^=]*?(username|password)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"rss(24h)?\.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 (@"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), new (@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled), new (@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
new Regex(@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory // Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
new Regex(@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"), new (@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"),
// Path // Path
new Regex(@"C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"/home/(?<secret>[^/""]+?)(/|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"/(home|Users)/(?<secret>[^/""]+?)(/|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// NzbGet // NzbGet
new Regex(@"""Name""\s*:\s*""[^""]*(username|password)""\s*,\s*""Value""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"""Name""\s*:\s*""[^""]*(username|password)""\s*,\s*""Value""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Sabnzbd // Sabnzbd
new Regex(@"""[^""]*(username|password|api_?key|nzb_key)""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"""[^""]*(username|password|api_?key|nzb_key)""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"""email_(account|to|from|pwd)""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"""email_(account|to|from|pwd)""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// uTorrent // uTorrent
new Regex(@"\[""[a-z._]*(username|password)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"\[""[a-z._]*(username|password)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"\[""(boss_key|boss_key_salt|proxy\.proxy)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"\[""(boss_key|boss_key_salt|proxy\.proxy)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Deluge // Deluge
new Regex(@"auth.login\(""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"auth.login\(""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// BroadcastheNet // BroadcastheNet
new Regex(@"""?method""?\s*:\s*""(getTorrents)"",\s*""?params""?\s*:\s*\[\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"""?method""?\s*:\s*""(getTorrents)"",\s*""?params""?\s*:\s*\[\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"getTorrents\(""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"getTorrents\(""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=\?|&)(authkey|torrent_pass)=(?<secret>[^&=]+?)(?=""|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"(?<=\?|&)(authkey|torrent_pass)=(?<secret>[^&=]+?)(?=""|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Good Reads // Good Reads
new Regex(@"(?<=""(token|tokensecret)"":\s)""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"(?<=""(token|tokensecret)"":\s)""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Webhooks // Webhooks
// Notifiarr // Notifiarr
new Regex(@"api/v[0-9]/notification/readarr/(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"api/v[0-9]/notification/readarr/(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Discord // Discord
new Regex(@"discord.com/api/webhooks/((?<secret>[\w-]+)/)?(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase) new (@"discord.com/api/webhooks/((?<secret>[\w-]+)/)?(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase)
}; };
private static readonly Regex CleanseRemoteIPRegex = new Regex(@"(?:Auth-\w+(?<!Failure|Unauthorized) ip|from) (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Compiled); private static readonly Regex CleanseRemoteIPRegex = new (@"(?:Auth-\w+(?<!Failure|Unauthorized) ip|from) (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Compiled);
public static string Cleanse(string message) public static string Cleanse(string message)
{ {
@@ -68,15 +68,15 @@ namespace NzbDrone.Common.Instrumentation
foreach (var regex in CleansingRules) foreach (var regex in CleansingRules)
{ {
message = regex.Replace(message, m => message = regex.Replace(message, m =>
{
var value = m.Value;
foreach (var capture in m.Groups["secret"].Captures.OfType<Capture>().Reverse())
{ {
var value = m.Value; value = value.Replace(capture.Index - m.Index, capture.Length, "(removed)");
foreach (var capture in m.Groups["secret"].Captures.OfType<Capture>().Reverse()) }
{
value = value.Replace(capture.Index - m.Index, capture.Length, "(removed)");
}
return value; return value;
}); });
} }
message = CleanseRemoteIPRegex.Replace(message, CleanseRemoteIP); message = CleanseRemoteIPRegex.Replace(message, CleanseRemoteIP);
@@ -87,7 +87,6 @@ namespace NzbDrone.Common.Instrumentation
private static string CleanseRemoteIP(Match match) private static string CleanseRemoteIP(Match match)
{ {
var group = match.Groups[1]; var group = match.Groups[1];
var valueAll = match.Value;
var valueIP = group.Value; var valueIP = group.Value;
if (IPAddress.TryParse(valueIP, out var address) && !address.IsLocalAddress()) if (IPAddress.TryParse(valueIP, out var address) && !address.IsLocalAddress())

View File

@@ -1,4 +1,4 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
namespace NzbDrone.Common.Instrumentation namespace NzbDrone.Common.Instrumentation
@@ -16,7 +16,7 @@ namespace NzbDrone.Common.Instrumentation
} }
} }
foreach (JToken token in json) foreach (var token in json)
{ {
Visit(token); Visit(token);
} }

View File

@@ -94,7 +94,7 @@ namespace NzbDrone.Common.Instrumentation
private static void RegisterDebugger() private static void RegisterDebugger()
{ {
DebuggerTarget target = new DebuggerTarget(); var target = new DebuggerTarget();
target.Name = "debuggerLogger"; target.Name = "debuggerLogger";
target.Layout = "[${level}] [${threadid}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}"; target.Layout = "[${level}] [${threadid}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
@@ -107,9 +107,10 @@ namespace NzbDrone.Common.Instrumentation
{ {
LogManager.Setup().LoadConfiguration(c => LogManager.Setup().LoadConfiguration(c =>
{ {
c.ForLogger("System.*").WriteToNil(LogLevel.Warn);
c.ForLogger("Microsoft.*").WriteToNil(LogLevel.Warn);
c.ForLogger("Microsoft.Hosting.Lifetime*").WriteToNil(LogLevel.Info); c.ForLogger("Microsoft.Hosting.Lifetime*").WriteToNil(LogLevel.Info);
c.ForLogger("System*").WriteToNil(LogLevel.Warn); c.ForLogger("Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware").WriteToNil(LogLevel.Fatal);
c.ForLogger("Microsoft*").WriteToNil(LogLevel.Warn);
}); });
} }

View File

@@ -29,7 +29,7 @@ namespace NzbDrone.Common.OAuth
public virtual string Version { get; set; } public virtual string Version { get; set; }
public virtual string SessionHandle { get; set; } public virtual string SessionHandle { get; set; }
/// <seealso cref="http://oauth.net/core/1.0#request_urls"/> /// <seealso href="http://oauth.net/core/1.0#request_urls"/>
public virtual string RequestUrl { get; set; } public virtual string RequestUrl { get; set; }
public virtual Dictionary<string, string> Parameters { get; set; } public virtual Dictionary<string, string> Parameters { get; set; }

View File

@@ -1,4 +1,4 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace NzbDrone.Common.Serializer namespace NzbDrone.Common.Serializer
{ {
@@ -60,7 +60,7 @@ namespace NzbDrone.Common.Serializer
public virtual void Visit(JArray json) public virtual void Visit(JArray json)
{ {
foreach (JToken token in json) foreach (var token in json)
{ {
Visit(token); Visit(token);
} }
@@ -72,7 +72,7 @@ namespace NzbDrone.Common.Serializer
public virtual void Visit(JObject json) public virtual void Visit(JObject json)
{ {
foreach (JProperty property in json.Properties()) foreach (var property in json.Properties())
{ {
Visit(property); Visit(property);
} }

View File

@@ -42,7 +42,7 @@ namespace NzbDrone.Common.Serializer
var enumText = value.ToString(); var enumText = value.ToString();
var builder = new StringBuilder(enumText.Length + 4); var builder = new StringBuilder(enumText.Length + 4);
builder.Append(char.ToLower(enumText[0])); builder.Append(char.ToLower(enumText[0]));
for (int i = 1; i < enumText.Length; i++) for (var i = 1; i < enumText.Length; i++)
{ {
if (char.IsUpper(enumText[i])) if (char.IsUpper(enumText[i]))
{ {

View File

@@ -18,7 +18,7 @@ namespace NzbDrone.Common.Serializer
{ {
try try
{ {
Version v = new Version(reader.GetString()); var v = new Version(reader.GetString());
return v; return v;
} }
catch (Exception) catch (Exception)

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@@ -137,7 +137,7 @@ namespace NzbDrone.Common.TPL
/// <returns>An enumerable of the tasks currently scheduled.</returns> /// <returns>An enumerable of the tasks currently scheduled.</returns>
protected sealed override IEnumerable<Task> GetScheduledTasks() protected sealed override IEnumerable<Task> GetScheduledTasks()
{ {
bool lockTaken = false; var lockTaken = false;
try try
{ {
Monitor.TryEnter(_tasks, ref lockTaken); Monitor.TryEnter(_tasks, ref lockTaken);

View File

@@ -110,7 +110,7 @@ namespace NzbDrone.Console
} }
System.Console.WriteLine("Non-recoverable failure, waiting for user intervention..."); System.Console.WriteLine("Non-recoverable failure, waiting for user intervention...");
for (int i = 0; i < 3600; i++) for (var i = 0; i < 3600; i++)
{ {
System.Threading.Thread.Sleep(1000); System.Threading.Thread.Sleep(1000);

View File

@@ -197,7 +197,7 @@ namespace NzbDrone.Core.Test.Datastore
Subject.SetFields(_basicList, x => x.Interval); Subject.SetFields(_basicList, x => x.Interval);
for (int i = 0; i < _basicList.Count; i++) for (var i = 0; i < _basicList.Count; i++)
{ {
_basicList[i].LastExecution = executionBackup[i]; _basicList[i].LastExecution = executionBackup[i];
} }

View File

@@ -87,7 +87,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_return_true_if_cutoffs_are_met_but_is_a_revision_upgrade() public void should_return_true_if_cutoffs_are_met_but_is_a_revision_upgrade()
{ {
QualityProfile profile = new QualityProfile var profile = new QualityProfile
{ {
Cutoff = Quality.MP3.Id, Cutoff = Quality.MP3.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
@@ -104,7 +104,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_return_false_if_quality_profile_does_not_allow_upgrades_but_cutoff_is_set_to_highest_quality() public void should_return_false_if_quality_profile_does_not_allow_upgrades_but_cutoff_is_set_to_highest_quality()
{ {
QualityProfile profile = new QualityProfile var profile = new QualityProfile
{ {
Cutoff = Quality.FLAC.Id, Cutoff = Quality.FLAC.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),

View File

@@ -83,7 +83,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
VerifySingleItem(DownloadItemStatus.Downloading); VerifySingleItem(DownloadItemStatus.Downloading);
// If we keep changing the file every 20ms we should stay Downloading. // If we keep changing the file every 20ms we should stay Downloading.
for (int i = 0; i < 10; i++) for (var i = 0; i < 10; i++)
{ {
TestLogger.Info("Iteration {0}", i); TestLogger.Info("Iteration {0}", i);

View File

@@ -225,6 +225,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
[TestCase("checkingDL")] [TestCase("checkingDL")]
[TestCase("checkingUP")] [TestCase("checkingUP")]
[TestCase("metaDL")] [TestCase("metaDL")]
[TestCase("checkingResumeData")]
public void queued_item_should_have_required_properties(string state) public void queued_item_should_have_required_properties(string state)
{ {
var torrent = new QBittorrentTorrent var torrent = new QBittorrentTorrent

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using FluentAssertions; using FluentAssertions;
@@ -57,7 +57,7 @@ namespace NzbDrone.Core.Test
[Test] [Test]
public void ToBestDateTime_DayOfWeek() public void ToBestDateTime_DayOfWeek()
{ {
for (int i = 2; i < 7; i++) for (var i = 2; i < 7; i++)
{ {
var dateTime = DateTime.Today.AddDays(i); var dateTime = DateTime.Today.AddDays(i);

View File

@@ -79,7 +79,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
[Test] [Test]
public void should_return_ok_if_not_downloading_to_root_folder() public void should_return_ok_if_not_downloading_to_root_folder()
{ {
string rootFolderPath = "c:\\Test2".AsOsAgnostic(); var rootFolderPath = "c:\\Test2".AsOsAgnostic();
GivenRootFolder(rootFolderPath); GivenRootFolder(rootFolderPath);

View File

@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.Instrumentation
public void write_long_log() public void write_long_log()
{ {
var message = string.Empty; var message = string.Empty;
for (int i = 0; i < 100; i++) for (var i = 0; i < 100; i++)
{ {
message += Guid.NewGuid(); message += Guid.NewGuid();
} }

View File

@@ -100,7 +100,7 @@ namespace NzbDrone.Core.Test.MediaFiles.BookImport.Aggregation.Aggregators
{ {
get get
{ {
int i = 0; var i = 0;
foreach (var tokens in tokenList) foreach (var tokens in tokenList)
{ {
@@ -128,7 +128,7 @@ namespace NzbDrone.Core.Test.MediaFiles.BookImport.Aggregation.Aggregators
private List<string> GivenFilenames(string[] fields, string fieldSeparator, string whitespace) private List<string> GivenFilenames(string[] fields, string fieldSeparator, string whitespace)
{ {
var outp = new List<string>(); var outp = new List<string>();
for (int i = 1; i <= 3; i++) for (var i = 1; i <= 3; i++)
{ {
var components = new List<string>(); var components = new List<string>();
foreach (var field in fields) foreach (var field in fields)
@@ -161,7 +161,7 @@ namespace NzbDrone.Core.Test.MediaFiles.BookImport.Aggregation.Aggregators
private void VerifyDataAuto(List<LocalBook> tracks, string[] tokens, string whitespace) private void VerifyDataAuto(List<LocalBook> tracks, string[] tokens, string whitespace)
{ {
for (int i = 1; i <= tracks.Count; i++) for (var i = 1; i <= tracks.Count; i++)
{ {
var info = tracks[i - 1].FileTrackInfo; var info = tracks[i - 1].FileTrackInfo;

View File

@@ -71,7 +71,7 @@ namespace NzbDrone.Core.Test.MediaFiles.BookImport.Identification
Mocker.SetConstant<ICandidateService>(Mocker.Resolve<CandidateService>()); Mocker.SetConstant<ICandidateService>(Mocker.Resolve<CandidateService>());
// set up the augmenters // set up the augmenters
List<IAggregate<LocalEdition>> aggregators = new List<IAggregate<LocalEdition>> var aggregators = new List<IAggregate<LocalEdition>>
{ {
Mocker.Resolve<AggregateFilenameInfo>() Mocker.Resolve<AggregateFilenameInfo>()
}; };
@@ -89,7 +89,7 @@ namespace NzbDrone.Core.Test.MediaFiles.BookImport.Identification
private List<Author> GivenAuthors(List<AuthorTestCase> authors) private List<Author> GivenAuthors(List<AuthorTestCase> authors)
{ {
var outp = new List<Author>(); var outp = new List<Author>();
for (int i = 0; i < authors.Count; i++) for (var i = 0; i < authors.Count; i++)
{ {
var meta = authors[i].MetadataProfile; var meta = authors[i].MetadataProfile;
meta.Id = i + 1; meta.Id = i + 1;

View File

@@ -30,17 +30,17 @@ namespace NzbDrone.Core.Test.MediaFiles.BookImport.Identification
static RandomValueNamerShortStrings() static RandomValueNamerShortStrings()
{ {
AllowedChars = new List<char>(); AllowedChars = new List<char>();
for (char c = 'a'; c < 'z'; c++) for (var c = 'a'; c < 'z'; c++)
{ {
AllowedChars.Add(c); AllowedChars.Add(c);
} }
for (char c = 'A'; c < 'Z'; c++) for (var c = 'A'; c < 'Z'; c++)
{ {
AllowedChars.Add(c); AllowedChars.Add(c);
} }
for (char c = '0'; c < '9'; c++) for (var c = '0'; c < '9'; c++)
{ {
AllowedChars.Add(c); AllowedChars.Add(c);
} }
@@ -48,17 +48,17 @@ namespace NzbDrone.Core.Test.MediaFiles.BookImport.Identification
protected override string GetString(MemberInfo memberInfo) protected override string GetString(MemberInfo memberInfo)
{ {
int length = _generator.Next(1, 100); var length = _generator.Next(1, 100);
char[] chars = new char[length]; var chars = new char[length];
for (int i = 0; i < length; i++) for (var i = 0; i < length; i++)
{ {
int index = _generator.Next(0, AllowedChars.Count - 1); var index = _generator.Next(0, AllowedChars.Count - 1);
chars[i] = AllowedChars[index]; chars[i] = AllowedChars[index];
} }
byte[] bytes = Encoding.UTF8.GetBytes(chars); var bytes = Encoding.UTF8.GetBytes(chars);
return Encoding.UTF8.GetString(bytes, 0, bytes.Length); return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
} }
} }
@@ -90,7 +90,7 @@ namespace NzbDrone.Core.Test.MediaFiles.BookImport.Identification
{ {
var outp = new List<LocalBook>(); var outp = new List<LocalBook>();
for (int i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
var track = Builder<LocalBook> var track = Builder<LocalBook>
.CreateNew() .CreateNew()
@@ -283,7 +283,7 @@ namespace NzbDrone.Core.Test.MediaFiles.BookImport.Identification
public void should_separate_many_books_in_same_directory() public void should_separate_many_books_in_same_directory()
{ {
var tracks = new List<LocalBook>(); var tracks = new List<LocalBook>();
for (int i = 0; i < 100; i++) for (var i = 0; i < 100; i++)
{ {
tracks.AddRange(GivenTracks($"C:\\music".AsOsAgnostic(), "author" + i, "book" + i, 10)); tracks.AddRange(GivenTracks($"C:\\music".AsOsAgnostic(), "author" + i, "book" + i, 10));
} }

View File

@@ -14,8 +14,8 @@ namespace NzbDrone.Core.Test.MetadataSource.Goodreads.Resources
[Test] [Test]
public void parse_non_work() public void parse_non_work()
{ {
XElement element = new XElement("Dummy", "entry"); var element = new XElement("Dummy", "entry");
WorkResource work = new WorkResource(); var work = new WorkResource();
Assert.Throws<NullReferenceException>(() => work.Parse(element)); Assert.Throws<NullReferenceException>(() => work.Parse(element));
@@ -27,11 +27,11 @@ namespace NzbDrone.Core.Test.MetadataSource.Goodreads.Resources
[Test] [Test]
public void parse_minimal_work() public void parse_minimal_work()
{ {
XElement element = new XElement("work", var element = new XElement("work",
new XElement("original_title", "Book Title"), new XElement("original_title", "Book Title"),
new XElement("id", "123456789")); new XElement("id", "123456789"));
WorkResource work = new WorkResource(); var work = new WorkResource();
work.Parse(element); work.Parse(element);
@@ -44,12 +44,12 @@ namespace NzbDrone.Core.Test.MetadataSource.Goodreads.Resources
[Test] [Test]
public void parse_minimal_work_with_surrounding_tags() public void parse_minimal_work_with_surrounding_tags()
{ {
XElement element = new XElement("series_works", var element = new XElement("series_works",
new XElement("work", new XElement("work",
new XElement("original_title", "Book Title"), new XElement("original_title", "Book Title"),
new XElement("id", "123456789"))); new XElement("id", "123456789")));
WorkResource work = new WorkResource(); var work = new WorkResource();
work.Parse(element); work.Parse(element);

View File

@@ -115,7 +115,7 @@ namespace NzbDrone.Core.Test.MusicTests.AuthorRepositoryTests
{ {
GivenAuthors(); GivenAuthors();
string name = "Alice Cooper"; var name = "Alice Cooper";
AddAuthor(name, "ee58c59f-8e7f-4430-b8ca-236c4d3745ae"); AddAuthor(name, "ee58c59f-8e7f-4430-b8ca-236c4d3745ae");
AddAuthor(name, "4d7928cd-7ed2-4282-8c29-c0c9f966f1bd"); AddAuthor(name, "4d7928cd-7ed2-4282-8c29-c0c9f966f1bd");

View File

@@ -39,13 +39,13 @@ namespace NzbDrone.Core.Test.ParserTests
[Test] [Test]
public void should_not_parse_md5() public void should_not_parse_md5()
{ {
string hash = "CRAPPY TEST SEED"; var hash = "CRAPPY TEST SEED";
var hashAlgo = System.Security.Cryptography.MD5.Create(); var hashAlgo = System.Security.Cryptography.MD5.Create();
var repetitions = 100; var repetitions = 100;
var success = 0; var success = 0;
for (int i = 0; i < repetitions; i++) for (var i = 0; i < repetitions; i++)
{ {
var hashData = hashAlgo.ComputeHash(System.Text.Encoding.Default.GetBytes(hash)); var hashData = hashAlgo.ComputeHash(System.Text.Encoding.Default.GetBytes(hash));
@@ -64,17 +64,17 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase(40)] [TestCase(40)]
public void should_not_parse_random(int length) public void should_not_parse_random(int length)
{ {
string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; var charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
var hashAlgo = new Random(); var hashAlgo = new Random();
var repetitions = 500; var repetitions = 500;
var success = 0; var success = 0;
for (int i = 0; i < repetitions; i++) for (var i = 0; i < repetitions; i++)
{ {
StringBuilder hash = new StringBuilder(length); var hash = new StringBuilder(length);
for (int x = 0; x < length; x++) for (var x = 0; x < length; x++)
{ {
hash.Append(charset[hashAlgo.Next() % charset.Length]); hash.Append(charset[hashAlgo.Next() % charset.Length]);
} }

View File

@@ -67,7 +67,7 @@ namespace NzbDrone.Core.Test.Profiles.Delay
var moving = _last; var moving = _last;
var result = Subject.Reorder(moving.Id, null).OrderBy(d => d.Order).ToList(); var result = Subject.Reorder(moving.Id, null).OrderBy(d => d.Order).ToList();
for (int i = 1; i < result.Count; i++) for (var i = 1; i < result.Count; i++)
{ {
var delayProfile = result[i]; var delayProfile = result[i];

View File

@@ -15,6 +15,7 @@ namespace NzbDrone.Core.Annotations
public string Label { get; set; } public string Label { get; set; }
public string Unit { get; set; } public string Unit { get; set; }
public string HelpText { get; set; } public string HelpText { get; set; }
public string HelpTextWarning { get; set; }
public string HelpLink { get; set; } public string HelpLink { get; set; }
public FieldType Type { get; set; } public FieldType Type { get; set; }
public bool Advanced { get; set; } public bool Advanced { get; set; }

View File

@@ -32,7 +32,7 @@ namespace NzbDrone.Core.Books
var existingMetadata = FindById(data.Select(x => x.ForeignAuthorId).ToList()); var existingMetadata = FindById(data.Select(x => x.ForeignAuthorId).ToList());
var updateMetadataList = new List<AuthorMetadata>(); var updateMetadataList = new List<AuthorMetadata>();
var addMetadataList = new List<AuthorMetadata>(); var addMetadataList = new List<AuthorMetadata>();
int upToDateMetadataCount = 0; var upToDateMetadataCount = 0;
foreach (var meta in data) foreach (var meta in data)
{ {

View File

@@ -13,6 +13,7 @@ namespace NzbDrone.Core.Books
Author FindByName(string cleanName); Author FindByName(string cleanName);
Author FindById(string foreignAuthorId); Author FindById(string foreignAuthorId);
Dictionary<int, string> AllAuthorPaths(); Dictionary<int, string> AllAuthorPaths();
Dictionary<int, List<int>> AllAuthorTags();
Author GetAuthorByMetadataId(int authorMetadataId); Author GetAuthorByMetadataId(int authorMetadataId);
List<Author> GetAuthorsByMetadataId(IEnumerable<int> authorMetadataId); List<Author> GetAuthorsByMetadataId(IEnumerable<int> authorMetadataId);
} }
@@ -65,6 +66,15 @@ namespace NzbDrone.Core.Books
} }
} }
public Dictionary<int, List<int>> AllAuthorTags()
{
using (var conn = _database.OpenConnection())
{
var strSql = "SELECT \"Id\" AS \"Key\", \"Tags\" AS \"Value\" FROM \"Authors\" WHERE \"Tags\" IS NOT NULL";
return conn.Query<KeyValuePair<int, List<int>>>(strSql).ToDictionary(x => x.Key, x => x.Value);
}
}
public Author GetAuthorByMetadataId(int authorMetadataId) public Author GetAuthorByMetadataId(int authorMetadataId)
{ {
return Query(s => s.AuthorMetadataId == authorMetadataId).SingleOrDefault(); return Query(s => s.AuthorMetadataId == authorMetadataId).SingleOrDefault();

View File

@@ -132,7 +132,7 @@ namespace NzbDrone.Core.Books
if (_authorService.AuthorPathExists(path)) if (_authorService.AuthorPathExists(path))
{ {
var basepath = path; var basepath = path;
int i = 0; var i = 0;
do do
{ {
i++; i++;

View File

@@ -24,6 +24,7 @@ namespace NzbDrone.Core.Books
List<Author> GetReportCandidates(string reportTitle); List<Author> GetReportCandidates(string reportTitle);
void DeleteAuthor(int authorId, bool deleteFiles, bool addImportListExclusion = false); void DeleteAuthor(int authorId, bool deleteFiles, bool addImportListExclusion = false);
List<Author> GetAllAuthors(); List<Author> GetAllAuthors();
Dictionary<int, List<int>> GetAllAuthorTags();
List<Author> AllForTag(int tagId); List<Author> AllForTag(int tagId);
Author UpdateAuthor(Author author); Author UpdateAuthor(Author author);
List<Author> UpdateAuthors(List<Author> authors, bool useExistingRelativeFolder); List<Author> UpdateAuthors(List<Author> authors, bool useExistingRelativeFolder);
@@ -185,6 +186,11 @@ namespace NzbDrone.Core.Books
return _cache.Get("GetAllAuthors", () => _authorRepository.All().ToList(), TimeSpan.FromSeconds(30)); return _cache.Get("GetAllAuthors", () => _authorRepository.All().ToList(), TimeSpan.FromSeconds(30));
} }
public Dictionary<int, List<int>> GetAllAuthorTags()
{
return _authorRepository.AllAuthorTags();
}
public Dictionary<int, string> AllAuthorPaths() public Dictionary<int, string> AllAuthorPaths()
{ {
return _authorRepository.AllAuthorPaths(); return _authorRepository.AllAuthorPaths();

View File

@@ -117,8 +117,7 @@ namespace NzbDrone.Core.Configuration
continue; continue;
} }
object currentValue; allWithDefaults.TryGetValue(configValue.Key, out var currentValue);
allWithDefaults.TryGetValue(configValue.Key, out currentValue);
if (currentValue == null) if (currentValue == null)
{ {
continue; continue;
@@ -141,7 +140,7 @@ namespace NzbDrone.Core.Configuration
{ {
const string defaultValue = "*"; const string defaultValue = "*";
string bindAddress = GetValue("BindAddress", defaultValue); var bindAddress = GetValue("BindAddress", defaultValue);
if (string.IsNullOrWhiteSpace(bindAddress)) if (string.IsNullOrWhiteSpace(bindAddress))
{ {
return defaultValue; return defaultValue;

View File

@@ -56,8 +56,7 @@ namespace NzbDrone.Core.Configuration
foreach (var configValue in configValues) foreach (var configValue in configValues)
{ {
object currentValue; allWithDefaults.TryGetValue(configValue.Key, out var currentValue);
allWithDefaults.TryGetValue(configValue.Key, out currentValue);
if (currentValue == null || configValue.Value == null) if (currentValue == null || configValue.Value == null)
{ {
continue; continue;
@@ -439,9 +438,7 @@ namespace NzbDrone.Core.Configuration
EnsureCache(); EnsureCache();
string dbValue; if (_cache.TryGetValue(key, out var dbValue) && dbValue != null && !string.IsNullOrEmpty(dbValue))
if (_cache.TryGetValue(key, out dbValue) && dbValue != null && !string.IsNullOrEmpty(dbValue))
{ {
return dbValue; return dbValue;
} }

View File

@@ -215,7 +215,7 @@ namespace NzbDrone.Core.Datastore
using (var conn = _database.OpenConnection()) using (var conn = _database.OpenConnection())
{ {
using (IDbTransaction tran = conn.BeginTransaction(IsolationLevel.ReadCommitted)) using (var tran = conn.BeginTransaction(IsolationLevel.ReadCommitted))
{ {
foreach (var model in models) foreach (var model in models)
{ {

View File

@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Datastore.Converters
} }
string contract; string contract;
using (JsonDocument body = JsonDocument.Parse(stringValue)) using (var body = JsonDocument.Parse(stringValue))
{ {
contract = body.RootElement.GetProperty("name").GetString(); contract = body.RootElement.GetProperty("name").GetString();
} }

View File

@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Datastore.Migration
private void ConvertFileChmodToFolderChmod(IDbConnection conn, IDbTransaction tran) private void ConvertFileChmodToFolderChmod(IDbConnection conn, IDbTransaction tran)
{ {
using (IDbCommand getFileChmodCmd = conn.CreateCommand()) using (var getFileChmodCmd = conn.CreateCommand())
{ {
getFileChmodCmd.Transaction = tran; getFileChmodCmd.Transaction = tran;
getFileChmodCmd.CommandText = @"SELECT ""Value"" FROM ""Config"" WHERE ""Key"" = 'filechmod'"; getFileChmodCmd.CommandText = @"SELECT ""Value"" FROM ""Config"" WHERE ""Key"" = 'filechmod'";
@@ -32,7 +32,7 @@ namespace NzbDrone.Core.Datastore.Migration
var folderChmodNum = fileChmodNum | ((fileChmodNum & 0x124) >> 2); var folderChmodNum = fileChmodNum | ((fileChmodNum & 0x124) >> 2);
var folderChmod = Convert.ToString(folderChmodNum, 8).PadLeft(3, '0'); var folderChmod = Convert.ToString(folderChmodNum, 8).PadLeft(3, '0');
using (IDbCommand insertCmd = conn.CreateCommand()) using (var insertCmd = conn.CreateCommand())
{ {
insertCmd.Transaction = tran; insertCmd.Transaction = tran;
insertCmd.CommandText = "INSERT INTO \"Config\" (\"Key\", \"Value\") VALUES ('chmodfolder', ?)"; insertCmd.CommandText = "INSERT INTO \"Config\" (\"Key\", \"Value\") VALUES ('chmodfolder', ?)";
@@ -42,7 +42,7 @@ namespace NzbDrone.Core.Datastore.Migration
} }
} }
using (IDbCommand deleteCmd = conn.CreateCommand()) using (var deleteCmd = conn.CreateCommand())
{ {
deleteCmd.Transaction = tran; deleteCmd.Transaction = tran;
deleteCmd.CommandText = "DELETE FROM \"Config\" WHERE \"Key\" = 'filechmod'"; deleteCmd.CommandText = "DELETE FROM \"Config\" WHERE \"Key\" = 'filechmod'";

View File

@@ -231,11 +231,11 @@ namespace NzbDrone.Core.Datastore.Migration
{ {
var updatedNamingConfigs = new List<object>(); var updatedNamingConfigs = new List<object>();
using (IDbCommand namingConfigCmd = conn.CreateCommand()) using (var namingConfigCmd = conn.CreateCommand())
{ {
namingConfigCmd.Transaction = tran; namingConfigCmd.Transaction = tran;
namingConfigCmd.CommandText = @"SELECT * FROM ""NamingConfig"" LIMIT 1"; namingConfigCmd.CommandText = @"SELECT * FROM ""NamingConfig"" LIMIT 1";
using (IDataReader namingConfigReader = namingConfigCmd.ExecuteReader()) using (var namingConfigReader = namingConfigCmd.ExecuteReader())
{ {
var standardBookFormatIndex = namingConfigReader.GetOrdinal("StandardBookFormat"); var standardBookFormatIndex = namingConfigReader.GetOrdinal("StandardBookFormat");

View File

@@ -201,7 +201,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
public virtual IList<TableDefinition> ReadDbSchema() public virtual IList<TableDefinition> ReadDbSchema()
{ {
IList<TableDefinition> tables = ReadTables(); var tables = ReadTables();
foreach (var table in tables) foreach (var table in tables)
{ {
table.Indexes = ReadIndexes(table.SchemaName, table.Name); table.Indexes = ReadIndexes(table.SchemaName, table.Name);
@@ -270,7 +270,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
protected virtual IList<IndexDefinition> ReadIndexes(string schemaName, string tableName) protected virtual IList<IndexDefinition> ReadIndexes(string schemaName, string tableName)
{ {
var sqlCommand = string.Format(@"SELECT type, name, sql FROM sqlite_master WHERE tbl_name = '{0}' AND type = 'index' AND name NOT LIKE 'sqlite_auto%';", tableName); var sqlCommand = string.Format(@"SELECT type, name, sql FROM sqlite_master WHERE tbl_name = '{0}' AND type = 'index' AND name NOT LIKE 'sqlite_auto%';", tableName);
DataTable table = Read(sqlCommand).Tables[0]; var table = Read(sqlCommand).Tables[0];
IList<IndexDefinition> indexes = new List<IndexDefinition>(); IList<IndexDefinition> indexes = new List<IndexDefinition>();

View File

@@ -202,7 +202,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
private JsonRpcRequestBuilder BuildRequest(DelugeSettings settings) private JsonRpcRequestBuilder BuildRequest(DelugeSettings settings)
{ {
string url = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase); var url = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase);
var requestBuilder = new JsonRpcRequestBuilder(url); var requestBuilder = new JsonRpcRequestBuilder(url);
requestBuilder.LogResponseContent = true; requestBuilder.LogResponseContent = true;

View File

@@ -254,9 +254,8 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
protected long GetRemainingSize(DownloadStationTask torrent) protected long GetRemainingSize(DownloadStationTask torrent)
{ {
var downloadedString = torrent.Additional.Transfer["size_downloaded"]; var downloadedString = torrent.Additional.Transfer["size_downloaded"];
long downloadedSize;
if (downloadedString.IsNullOrWhiteSpace() || !long.TryParse(downloadedString, out downloadedSize)) if (downloadedString.IsNullOrWhiteSpace() || !long.TryParse(downloadedString, out var downloadedSize))
{ {
_logger.Debug("Torrent {0} has invalid size_downloaded: {1}", torrent.Title, downloadedString); _logger.Debug("Torrent {0} has invalid size_downloaded: {1}", torrent.Title, downloadedString);
downloadedSize = 0; downloadedSize = 0;
@@ -268,9 +267,8 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
protected TimeSpan? GetRemainingTime(DownloadStationTask torrent) protected TimeSpan? GetRemainingTime(DownloadStationTask torrent)
{ {
var speedString = torrent.Additional.Transfer["speed_download"]; var speedString = torrent.Additional.Transfer["speed_download"];
long downloadSpeed;
if (speedString.IsNullOrWhiteSpace() || !long.TryParse(speedString, out downloadSpeed)) if (speedString.IsNullOrWhiteSpace() || !long.TryParse(speedString, out var downloadSpeed))
{ {
_logger.Debug("Torrent {0} has invalid speed_download: {1}", torrent.Title, speedString); _logger.Debug("Torrent {0} has invalid speed_download: {1}", torrent.Title, speedString);
downloadSpeed = 0; downloadSpeed = 0;

View File

@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
var items = new List<DownloadClientItem>(); var items = new List<DownloadClientItem>();
long totalRemainingSize = 0; long totalRemainingSize = 0;
long globalSpeed = nzbTasks.Where(t => t.Status == DownloadStationTaskStatus.Downloading) var globalSpeed = nzbTasks.Where(t => t.Status == DownloadStationTaskStatus.Downloading)
.Select(GetDownloadSpeed) .Select(GetDownloadSpeed)
.Sum(); .Sum();
@@ -351,9 +351,8 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
protected long GetRemainingSize(DownloadStationTask task) protected long GetRemainingSize(DownloadStationTask task)
{ {
var downloadedString = task.Additional.Transfer["size_downloaded"]; var downloadedString = task.Additional.Transfer["size_downloaded"];
long downloadedSize;
if (downloadedString.IsNullOrWhiteSpace() || !long.TryParse(downloadedString, out downloadedSize)) if (downloadedString.IsNullOrWhiteSpace() || !long.TryParse(downloadedString, out var downloadedSize))
{ {
_logger.Debug("Task {0} has invalid size_downloaded: {1}", task.Title, downloadedString); _logger.Debug("Task {0} has invalid size_downloaded: {1}", task.Title, downloadedString);
downloadedSize = 0; downloadedSize = 0;
@@ -365,9 +364,8 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
protected long GetDownloadSpeed(DownloadStationTask task) protected long GetDownloadSpeed(DownloadStationTask task)
{ {
var speedString = task.Additional.Transfer["speed_download"]; var speedString = task.Additional.Transfer["speed_download"];
long downloadSpeed;
if (speedString.IsNullOrWhiteSpace() || !long.TryParse(speedString, out downloadSpeed)) if (speedString.IsNullOrWhiteSpace() || !long.TryParse(speedString, out var downloadSpeed))
{ {
_logger.Debug("Task {0} has invalid speed_download: {1}", task.Title, speedString); _logger.Debug("Task {0} has invalid speed_download: {1}", task.Title, speedString);
downloadSpeed = 0; downloadSpeed = 0;

View File

@@ -15,8 +15,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex.JsonConverters
{ {
var result = reader.Value.ToString().Replace("_", string.Empty); var result = reader.Value.ToString().Replace("_", string.Empty);
NzbVortexLoginResultType output; Enum.TryParse(result, true, out NzbVortexLoginResultType output);
Enum.TryParse(result, true, out output);
return output; return output;
} }

View File

@@ -15,8 +15,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex.JsonConverters
{ {
var result = reader.Value.ToString().Replace("_", string.Empty); var result = reader.Value.ToString().Replace("_", string.Empty);
NzbVortexResultType output; Enum.TryParse(result, true, out NzbVortexResultType output);
Enum.TryParse(result, true, out output);
return output; return output;
} }

View File

@@ -113,9 +113,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
public override void RemoveItem(DownloadClientItem item, bool deleteData) public override void RemoveItem(DownloadClientItem item, bool deleteData)
{ {
// Try to find the download by numerical ID, otherwise try by AddUUID // Try to find the download by numerical ID, otherwise try by AddUUID
int id; if (int.TryParse(item.DownloadId, out var id))
if (int.TryParse(item.DownloadId, out id))
{ {
_proxy.Remove(id, deleteData, Settings); _proxy.Remove(id, deleteData, Settings);
} }

View File

@@ -223,7 +223,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
protected IEnumerable<NzbgetCategory> GetCategories(Dictionary<string, string> config) protected IEnumerable<NzbgetCategory> GetCategories(Dictionary<string, string> config)
{ {
for (int i = 1; i < 100; i++) for (var i = 1; i < 100; i++)
{ {
var name = config.GetValueOrDefault("Category" + i + ".Name"); var name = config.GetValueOrDefault("Category" + i + ".Name");
@@ -310,8 +310,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
var config = _proxy.GetConfig(Settings); var config = _proxy.GetConfig(Settings);
var keepHistory = config.GetValueOrDefault("KeepHistory", "7"); var keepHistory = config.GetValueOrDefault("KeepHistory", "7");
int value; if (!int.TryParse(keepHistory, NumberStyles.None, CultureInfo.InvariantCulture, out var value) || value == 0)
if (!int.TryParse(keepHistory, NumberStyles.None, CultureInfo.InvariantCulture, out value) || value == 0)
{ {
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0") return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0")
{ {

View File

@@ -164,11 +164,10 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
var queue = GetQueue(settings); var queue = GetQueue(settings);
var history = GetHistory(settings); var history = GetHistory(settings);
int nzbId;
NzbgetQueueItem queueItem; NzbgetQueueItem queueItem;
NzbgetHistoryItem historyItem; NzbgetHistoryItem historyItem;
if (id.Length < 10 && int.TryParse(id, out nzbId)) if (id.Length < 10 && int.TryParse(id, out var nzbId))
{ {
// Download wasn't grabbed by Readarr, so the id is the NzbId reported by nzbget. // Download wasn't grabbed by Readarr, so the id is the NzbId reported by nzbget.
queueItem = queue.SingleOrDefault(h => h.NzbId == nzbId); queueItem = queue.SingleOrDefault(h => h.NzbId == nzbId);

View File

@@ -253,6 +253,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
case "queuedDL": // queuing is enabled and torrent is queued for download case "queuedDL": // queuing is enabled and torrent is queued for download
case "checkingDL": // same as checkingUP, but torrent has NOT finished downloading case "checkingDL": // same as checkingUP, but torrent has NOT finished downloading
case "checkingUP": // torrent has finished downloading and is being checked. Set when `recheck torrent on completion` is enabled. In the event the check fails we shouldn't treat it as completed. case "checkingUP": // torrent has finished downloading and is being checked. Set when `recheck torrent on completion` is enabled. In the event the check fails we shouldn't treat it as completed.
case "checkingResumeData": // torrent is checking resume data on load
item.Status = DownloadItemStatus.Queued; item.Status = DownloadItemStatus.Queued;
break; break;
@@ -496,7 +497,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
return null; return null;
} }
Dictionary<string, QBittorrentLabel> labels = Proxy.GetLabels(Settings); var labels = Proxy.GetLabels(Settings);
if (Settings.MusicCategory.IsNotNullOrWhiteSpace() && !labels.ContainsKey(Settings.MusicCategory)) if (Settings.MusicCategory.IsNotNullOrWhiteSpace() && !labels.ContainsKey(Settings.MusicCategory))
{ {

View File

@@ -15,8 +15,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd.JsonConverters
{ {
var queuePriority = reader.Value.ToString(); var queuePriority = reader.Value.ToString();
SabnzbdPriority output; Enum.TryParse(queuePriority, out SabnzbdPriority output);
Enum.TryParse(queuePriority, out output);
return output; return output;
} }

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