Compare commits

...

36 Commits

Author SHA1 Message Date
Qstick
2e39c7340c Windows installer improvements
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
(cherry picked from commit 02c95658c4a5d38e1d0997ed5ef9bbd219ffc32c)
2023-08-19 18:41:06 +03:00
Qstick
f75add984f Cleanup distribution files
(cherry picked from commit 8b291d932f687297f18491469c44751e37e81173)
2023-08-19 18:41:06 +03:00
Bogdan
f0f6c3eb35 Update azure pipelines 2023-08-19 18:41:06 +03:00
Robin Dadswell
d94f866aeb Adds Pipeline testing for Postgres15 Databases 2023-08-19 18:41:06 +03:00
Robin Dadswell
618f07d138 Bump Npgsql to 7.0.4 2023-08-19 18:41:06 +03:00
Bogdan
3db33c988a Align logs filename with upstream 2023-08-19 18:33:37 +03:00
Bogdan
28e38b7f17 Prevent new builds on API docs updates 2023-08-19 14:12:03 +03:00
Servarr
ca403e6f31 Automated API Docs update [skip ci] 2023-08-19 13:56:48 +03:00
Weblate
51351dee1d Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Robert A. Viana <robert.abreu@outlook.com>
Co-authored-by: Weblate <noreply@weblate.org>
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/bn/
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/hu/
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/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/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/tr/
Translation: Servarr/Readarr
2023-08-19 13:56:29 +03:00
Mark McDowall
2081f2e321 Fixed: Allow decimals for Custom Format size
(cherry picked from commit 7f5ddff568ce9f87bd45420cbd36690b190bd633)

Closes #2839
2023-08-19 13:31:48 +03:00
Stevie Robinson
c10a32534c Add info box to Remote Path Mappings Settings
(cherry picked from commit d8f3d7d3eafeafd8d4372db0076a63f935a29218)

Closes #2835
2023-08-19 13:30:36 +03:00
Mark McDowall
0e415c6ce3 New: Status message when downloading metadata in qBittorrent
(cherry picked from commit 8aa872edf4737798d4836f68fbd0697ee0511c41)
2023-08-19 13:28:19 +03:00
Mark McDowall
a8eb674071 Fixed: Ignore IOException deleting download folder after import
(cherry picked from commit d05cb40088a51eef5a2830bf2b55f5d05955578f)
2023-08-19 13:28:08 +03:00
Mark McDowall
7f8a1cf849 New: Success check mark on blue buttons is now white instead of green
(cherry picked from commit 566fae9d5857a10bd69c718368e7847e5a733faa)
2023-08-19 13:27:57 +03:00
Stevie Robinson
a3c0d10240 Translate Updated and Connection Lost Modals in frontend
(cherry picked from commit 074aa6f4457bf83173e6ba7209c452a6e0659a35)

Closes #2806
2023-08-19 02:20:04 +03:00
Bogdan
3ddeaaefe2 Use named tokens in frontend translate function 2023-08-19 02:14:53 +03:00
Bogdan
6d99de4fe0 Add default update branches as autocomplete values 2023-08-19 02:14:53 +03:00
Bogdan
8b36a5ce92 Don't block update UI settings under docker 2023-08-19 02:14:53 +03:00
Bogdan
1202a43466 Show warning when using the docker update mechanism
(cherry picked from commit cc538c4b2d33a1734c45c0667776d946596107e9)

Closes #2805
2023-08-19 02:14:49 +03:00
Mark McDowall
82bc2d1aa4 Fixed: Don't block updates under docker unless configured in package_info
(cherry picked from commit 5a7e34e291c2715aa67161e5c455d25e80f498df)

Closes #2772
2023-08-19 02:14:18 +03:00
Bogdan
ed9af393b7 Fix flaky automation tests 2023-08-19 01:51:37 +03:00
bakerboy448
0799cfc885 Remove reddit from readme 2023-08-18 20:27:16 +03:00
Mark McDowall
331d0c9a9c New: Ignore inaccessible files with getting files
(cherry picked from commit e5aa8584100d96a2077c57f74ae5b2ceab63de19)
2023-08-18 18:37:26 +03:00
Bogdan
03c93c9c84 Fix test in DiskSpaceServiceFixture 2023-08-18 14:15:33 +03:00
Mark McDowall
60f6ed030b Fix GetBestRootFolderPath tests
(cherry picked from commit 63a911a9a549749b5460c2b9fea48a25e78c52a4)
2023-08-18 14:15:33 +03:00
Mark McDowall
cc70d61735 Fixed: UI loading when author or root folder path is for wrong OS
(cherry picked from commit 5f7217844533907d7fc6287a48efb31987736c4c)
2023-08-18 14:15:33 +03:00
Bogdan
a7b965100d Fix BookInfoProxySearchFixture test 2023-08-18 13:28:18 +03:00
Bogdan
8901118aef Add default schema values for root folders
(cherry picked from commit 7ae82a982ce17efa46c0a0b23256a26edf90babb)
2023-08-18 12:58:06 +03:00
Bogdan
99c17d7698 Improve messaging for Interactive Search
(cherry picked from commit 7893fdde104959c4f3c32d9e1000e2479f7a5b12)

Closes #2752
2023-08-18 12:49:56 +03:00
Bogdan
b84e83b082 Replace docker detection for cgroup v2
(cherry picked from commit 78d4dee4610c5f3f90cc69469004008aa64900b8)
2023-08-18 10:58:16 +03:00
Qstick
4249f5324a Cleanup other provider status code
(cherry picked from commit c281a7818adce8db728d2a104f4444cb9c0baf2c)
2023-08-16 12:18:20 +03:00
Qstick
9e1630e9a4 New: Notifications (Connect) Status
(cherry picked from commit e3545801721e00d4e5cac3fa534e66dcbe9d2d05)
(cherry picked from commit cb27b05a6c046ca0a6e4998f7e7ecd6b45add1a2)
2023-08-16 12:18:20 +03:00
Weblate
68b2773913 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: David Molero <contact@dolvem.com>
Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: deepserket <deepserket@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ro/
Translation: Servarr/Readarr
2023-08-15 20:08:44 +03:00
Bogdan
ad446b358e Fix combined search tests 2023-08-13 14:17:32 +03:00
Bogdan
423a8ecbe1 Bump version to 0.3.3 2023-08-13 13:40:33 +03:00
Bogdan
29a12aa3b0 Add one minute back-off level for all providers
(cherry picked from commit d8f314ff0ef64e8d90b21b7865e46be74db5e570)

Closes #2792
2023-08-12 10:52:44 +03:00
128 changed files with 1987 additions and 540 deletions

3
.gitattributes vendored
View File

@@ -3,8 +3,7 @@
# Explicitly set bash scripts to have unix endings
*.sh text eol=lf
distribution/debian/* text eol=lf
macOS/Readarr text eol=lf
distribution/osx/Readarr text eol=lf
# Custom for Visual Studio
*.cs diff=csharp

View File

@@ -30,7 +30,6 @@ Note that only one type of a given book is supported. If you want both an audiob
[![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wiki.servarr.com/readarr)
[![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60)](https://readarr.com/discord)
[![Reddit](https://img.shields.io/badge/reddit-discussion-FF4500.svg?maxAge=60)](https://www.reddit.com/r/readarr)
Note: GitHub Issues are for Bugs and Feature Requests Only

View File

@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '0.3.2'
majorVersion: '0.3.3'
minorVersion: $[counter('minorVersion', 1)]
readarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(readarrVersion)'
@@ -27,6 +27,10 @@ trigger:
include:
- develop
- master
paths:
exclude:
- .github
- src/Readarr.Api.*/openapi.json
pr:
branches:
@@ -34,82 +38,37 @@ pr:
- develop
paths:
exclude:
- .github
- src/NzbDrone.Core/Localization/Core
- src/Readarr.Api.*/openapi.json
stages:
- stage: Build_Backend_Windows
displayName: Build Backend
dependsOn: []
- stage: Setup
displayName: Setup
jobs:
- job: Backend
strategy:
matrix:
Windows:
osName: 'Windows'
imageName: ${{ variables.windowsImage }}
enableAnalysis: 'false'
- job:
displayName: Build Variables
pool:
vmImage: $(imageName)
variables:
# Disable stylecop here - linting errors get caught by the analyze task
EnableAnalyzers: $(enableAnalysis)
vmImage: ${{ variables.linuxImage }}
steps:
# Set the build name properly. The 'name' property won't recursively expand so hack here:
- bash: echo "##vso[build.updatebuildnumber]$READARRVERSION"
displayName: Set Build Name
- checkout: self
submodules: true
fetchDepth: 1
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- bash: |
SDK_PATH="${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}"
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
if ! grep -q freebsd-x64 $BUNDLEDVERSIONS; then
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
if [[ $BUILD_REASON == "PullRequest" ]]; then
git diff origin/develop...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
echo $? > not_backend_update
else
echo 0 > not_backend_update
fi
displayName: Extra Platform Support
- task: Cache@2
inputs:
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
path: $(nugetCacheFolder)
displayName: Cache NuGet packages
- bash: ./build.sh --backend --enable-bsd
displayName: Build Readarr Backend
env:
NUGET_PACKAGES: $(nugetCacheFolder)
- powershell: Get-ChildItem _output\net6.0*,_output\*.Update\* -Recurse | Where { $_.Fullname -notlike "*\publish\*" -and $_.attributes -notlike "*directory*" } | Remove-Item
displayName: Clean up intermediate output
- publish: $(outputFolder)
artifact: '$(osName)Backend'
displayName: Publish Backend
- publish: '$(testsFolder)/net6.0/win-x64/publish'
artifact: win-x64-tests
displayName: Publish win-x64 Test Package
- publish: '$(testsFolder)/net6.0/linux-x64/publish'
artifact: linux-x64-tests
displayName: Publish linux-x64 Test Package
- publish: '$(testsFolder)/net6.0/linux-x86/publish'
artifact: linux-x86-tests
displayName: Publish linux-x86 Test Package
- publish: '$(testsFolder)/net6.0/linux-musl-x64/publish'
artifact: linux-musl-x64-tests
displayName: Publish linux-musl-x64 Test Package
- publish: '$(testsFolder)/net6.0/freebsd-x64/publish'
artifact: freebsd-x64-tests
displayName: Publish freebsd-x64 Test Package
- publish: '$(testsFolder)/net6.0/osx-x64/publish'
artifact: osx-x64-tests
displayName: Publish osx-x64 Test Package
- stage: Build_Backend_Other
displayName: Build Backend (Other OS)
dependsOn: []
cat not_backend_update
displayName: Check for Backend File Changes
- publish: not_backend_update
artifact: not_backend_update
displayName: Publish update type
- stage: Build_Backend
displayName: Build Backend
dependsOn: Setup
jobs:
- job: Backend
strategy:
@@ -122,6 +81,10 @@ stages:
osName: 'Mac'
imageName: ${{ variables.macImage }}
enableAnalysis: 'false'
Windows:
osName: 'Windows'
imageName: ${{ variables.windowsImage }}
enableAnalysis: 'false'
pool:
vmImage: $(imageName)
@@ -137,22 +100,17 @@ stages:
inputs:
version: $(dotnetVersion)
- bash: |
SDK_PATH="${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}"
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
if ! grep -q freebsd-x64 $BUNDLEDVERSIONS; then
BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props
echo $BUNDLEDVERSIONS
if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
echo "Extra platforms already enabled"
else
echo "Enabling extra platform support"
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
fi
displayName: Extra Platform Support
- task: Cache@2
inputs:
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
path: $(nugetCacheFolder)
displayName: Cache NuGet packages
displayName: Enable Extra Platform Support
- bash: ./build.sh --backend --enable-extra-platforms
displayName: Build Readarr Backend
env:
NUGET_PACKAGES: $(nugetCacheFolder)
- bash: |
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
@@ -160,10 +118,38 @@ stages:
find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
displayName: Clean up intermediate output
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- publish: $(outputFolder)
artifact: '$(osName)Backend'
displayName: Publish Backend
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/win-x64/publish'
artifact: win-x64-tests
displayName: Publish win-x64 Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/linux-x64/publish'
artifact: linux-x64-tests
displayName: Publish linux-x64 Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/linux-x86/publish'
artifact: linux-x86-tests
displayName: Publish linux-x86 Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/linux-musl-x64/publish'
artifact: linux-musl-x64-tests
displayName: Publish linux-musl-x64 Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/freebsd-x64/publish'
artifact: freebsd-x64-tests
displayName: Publish freebsd-x64 Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/osx-x64/publish'
artifact: osx-x64-tests
displayName: Publish osx-x64 Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- stage: Build_Frontend
displayName: Frontend
dependsOn: []
dependsOn: Setup
jobs:
- job: Build
strategy:
@@ -192,7 +178,6 @@ stages:
key: 'yarn | "$(osName)" | yarn.lock'
restoreKeys: |
yarn | "$(osName)"
yarn
path: $(yarnCacheFolder)
displayName: Cache Yarn packages
- bash: ./build.sh --frontend
@@ -204,10 +189,10 @@ stages:
artifact: '$(osName)Frontend'
displayName: Publish Frontend
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- stage: Installer
dependsOn:
- Build_Backend_Windows
- Build_Backend
- Build_Frontend
jobs:
- job: Windows_Installer
@@ -231,8 +216,8 @@ stages:
displayName: Fetch Frontend
- bash: |
./build.sh --packages --installer
cp setup/output/Readarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x64-installer.exe
cp setup/output/Readarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x86-installer.exe
cp distribution/windows/setup/output/Readarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x64-installer.exe
cp distribution/windows/setup/output/Readarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x86-installer.exe
displayName: Create Installers
- publish: $(Build.ArtifactStagingDirectory)
artifact: 'WindowsInstaller'
@@ -240,7 +225,7 @@ stages:
- stage: Packages
dependsOn:
- Build_Backend_Windows
- Build_Backend
- Build_Frontend
jobs:
- job: Other_Packages
@@ -406,14 +391,29 @@ stages:
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
SENTRY_ORG: $(sentryOrg)
SENTRY_URL: $(sentryUrl)
- stage: Unit_Test
displayName: Unit Tests
dependsOn: Build_Backend_Windows
condition: succeeded()
dependsOn: Build_Backend
jobs:
- job: Prepare
pool:
vmImage: ${{ variables.linuxImage }}
steps:
- checkout: none
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'not_backend_update'
targetPath: '.'
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
name: setVar
- job: Unit
displayName: Unit Native
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
workspace:
clean: all
@@ -479,6 +479,8 @@ stages:
- job: Unit_Docker
displayName: Unit Docker
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
strategy:
matrix:
alpine:
@@ -492,11 +494,11 @@ stages:
pool:
vmImage: ${{ variables.linuxImage }}
container: $[ variables['containerImage'] ]
timeoutInMinutes: 10
steps:
- task: UseDotNet@2
displayName: 'Install .NET'
@@ -530,12 +532,14 @@ stages:
testResultsFiles: '**/TestResult.xml'
testRunTitle: '$(testName) Unit Tests'
failTaskOnFailedTests: true
- job: Unit_LinuxCore_Postgres
displayName: Unit Native LinuxCore with Postgres Database
- job: Unit_LinuxCore_Postgres14
displayName: Unit Native LinuxCore with Postgres14 Database
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
variables:
pattern: 'Readarr.*.linux-core-x64.tar.gz'
artifactName: LinuxCoreTests
artifactName: linux-x64-tests
Readarr__Postgres__Host: 'localhost'
Readarr__Postgres__Port: '5432'
Readarr__Postgres__User: 'readarr'
@@ -545,7 +549,7 @@ stages:
vmImage: ${{ variables.linuxImage }}
timeoutInMinutes: 10
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
@@ -556,7 +560,7 @@ stages:
displayName: Download Test Artifact
inputs:
buildType: 'current'
artifactName: 'linux-x64-Tests'
artifactName: $(artifactName)
targetPath: $(testsFolder)
- bash: find ${TESTSFOLDER} -name "Readarr.Test.Dummy" -exec chmod a+x {} \;
displayName: Make Test Dummy Executable
@@ -579,15 +583,84 @@ stages:
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: 'LinuxCore Postgres Unit Tests'
testRunTitle: 'LinuxCore Postgres14 Unit Tests'
failTaskOnFailedTests: true
- job: Unit_LinuxCore_Postgres15
displayName: Unit Native LinuxCore with Postgres15 Database
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
variables:
pattern: 'Readarr.*.linux-core-x64.tar.gz'
artifactName: linux-x64-tests
Readarr__Postgres__Host: 'localhost'
Readarr__Postgres__Port: '5432'
Readarr__Postgres__User: 'readarr'
Readarr__Postgres__Password: 'readarr'
pool:
vmImage: ${{ variables.linuxImage }}
timeoutInMinutes: 10
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: none
- task: DownloadPipelineArtifact@2
displayName: Download Test Artifact
inputs:
buildType: 'current'
artifactName: $(artifactName)
targetPath: $(testsFolder)
- bash: find ${TESTSFOLDER} -name "Readarr.Test.Dummy" -exec chmod a+x {} \;
displayName: Make Test Dummy Executable
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- bash: |
docker run -d --name=postgres15 \
-e POSTGRES_PASSWORD=readarr \
-e POSTGRES_USER=readarr \
-p 5432:5432/tcp \
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
postgres:15
displayName: Start postgres
- bash: |
chmod a+x ${TESTSFOLDER}/test.sh
ls -lR ${TESTSFOLDER}
${TESTSFOLDER}/test.sh Linux Unit Test
displayName: Run Tests
- task: PublishTestResults@2
displayName: Publish Test Results
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: 'LinuxCore Postgres15 Unit Tests'
failTaskOnFailedTests: true
- stage: Integration
displayName: Integration
dependsOn: Packages
jobs:
- job: Prepare
pool:
vmImage: ${{ variables.linuxImage }}
steps:
- checkout: none
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'not_backend_update'
targetPath: '.'
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
name: setVar
- job: Integration_Native
displayName: Integration Native
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
strategy:
matrix:
MacCore:
@@ -608,7 +681,7 @@ stages:
pool:
vmImage: $(imageName)
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
@@ -630,7 +703,7 @@ stages:
targetPath: $(Build.ArtifactStagingDirectory)
- task: ExtractFiles@1
inputs:
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
displayName: Extract Package
- bash: |
@@ -649,8 +722,10 @@ stages:
failTaskOnFailedTests: true
displayName: Publish Test Results
- job: Integration_LinuxCore_Postgres
displayName: Integration Native LinuxCore with Postgres Database
- job: Integration_LinuxCore_Postgres14
displayName: Integration Native LinuxCore with Postgres14 Database
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
variables:
pattern: 'Readarr.*.linux-core-x64.tar.gz'
Readarr__Postgres__Host: 'localhost'
@@ -682,7 +757,7 @@ stages:
targetPath: $(Build.ArtifactStagingDirectory)
- task: ExtractFiles@1
inputs:
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
displayName: Extract Package
- bash: |
@@ -705,12 +780,77 @@ stages:
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: 'Integration LinuxCore Postgres Database Integration Tests'
testRunTitle: 'Integration LinuxCore Postgres14 Database Integration Tests'
failTaskOnFailedTests: true
displayName: Publish Test Results
- job: Integration_LinuxCore_Postgres15
displayName: Integration Native LinuxCore with Postgres Database
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
variables:
pattern: 'Readarr.*.linux-core-x64.tar.gz'
Readarr__Postgres__Host: 'localhost'
Readarr__Postgres__Port: '5432'
Readarr__Postgres__User: 'readarr'
Readarr__Postgres__Password: 'readarr'
pool:
vmImage: ${{ variables.linuxImage }}
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: none
- task: DownloadPipelineArtifact@2
displayName: Download Test Artifact
inputs:
buildType: 'current'
artifactName: 'linux-x64-tests'
targetPath: $(testsFolder)
- task: DownloadPipelineArtifact@2
displayName: Download Build Artifact
inputs:
buildType: 'current'
artifactName: Packages
itemPattern: '**/$(pattern)'
targetPath: $(Build.ArtifactStagingDirectory)
- task: ExtractFiles@1
inputs:
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
displayName: Extract Package
- bash: |
mkdir -p ./bin/
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Readarr/. ./bin/
displayName: Move Package Contents
- bash: |
docker run -d --name=postgres15 \
-e POSTGRES_PASSWORD=readarr \
-e POSTGRES_USER=readarr \
-p 5432:5432/tcp \
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
postgres:15
displayName: Start postgres
- bash: |
chmod a+x ${TESTSFOLDER}/test.sh
${TESTSFOLDER}/test.sh Linux Integration Test
displayName: Run Integration Tests
- task: PublishTestResults@2
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: 'Integration LinuxCore Postgres15 Database Integration Tests'
failTaskOnFailedTests: true
displayName: Publish Test Results
- job: Integration_FreeBSD
displayName: Integration Native FreeBSD
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
workspace:
clean: all
variables:
@@ -755,6 +895,8 @@ stages:
- job: Integration_Docker
displayName: Integration Docker
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
strategy:
matrix:
alpine:
@@ -773,7 +915,7 @@ stages:
container: $[ variables['containerImage'] ]
timeoutInMinutes: 15
steps:
- task: UseDotNet@2
displayName: 'Install .NET'
@@ -801,7 +943,7 @@ stages:
targetPath: $(Build.ArtifactStagingDirectory)
- task: ExtractFiles@1
inputs:
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
displayName: Extract Package
- bash: |
@@ -823,7 +965,7 @@ stages:
- stage: Automation
displayName: Automation
dependsOn: Packages
jobs:
- job: Automation
strategy:
@@ -833,20 +975,23 @@ stages:
artifactName: 'linux-x64'
imageName: ${{ variables.linuxImage }}
pattern: 'Readarr.*.linux-core-x64.tar.gz'
failBuild: true
Mac:
osName: 'Mac'
artifactName: 'osx-x64'
imageName: ${{ variables.macImage }}
pattern: 'Readarr.*.osx-core-x64.tar.gz'
failBuild: true
Windows:
osName: 'Windows'
artifactName: 'win-x64'
imageName: ${{ variables.windowsImage }}
pattern: 'Readarr.*.windows-core-x64.zip'
failBuild: true
pool:
vmImage: $(imageName)
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
@@ -868,7 +1013,7 @@ stages:
targetPath: $(Build.ArtifactStagingDirectory)
- task: ExtractFiles@1
inputs:
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
displayName: Extract Package
- bash: |
@@ -888,20 +1033,35 @@ stages:
TargetFolder: '$(Build.ArtifactStagingDirectory)/screenshots'
- publish: $(Build.ArtifactStagingDirectory)/screenshots
artifact: '$(osName)AutomationScreenshots'
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
displayName: Publish Screenshot Bundle
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
- task: PublishTestResults@2
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: '$(osName) Automation Tests'
failTaskOnFailedTests: true
failTaskOnFailedTests: $(failBuild)
displayName: Publish Test Results
- stage: Analyze
dependsOn: []
dependsOn:
- Setup
displayName: Analyze
jobs:
- job: Prepare
pool:
vmImage: ${{ variables.linuxImage }}
steps:
- checkout: none
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'not_backend_update'
targetPath: '.'
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
name: setVar
- job: Lint_Frontend
displayName: Lint Frontend
strategy:
@@ -927,7 +1087,6 @@ stages:
key: 'yarn | "$(osName)" | yarn.lock'
restoreKeys: |
yarn | "$(osName)"
yarn
path: $(yarnCacheFolder)
displayName: Cache Yarn packages
- bash: ./build.sh --lint
@@ -956,11 +1115,16 @@ stages:
cliProjectVersion: '$(readarrVersion)'
cliSources: './frontend'
- task: SonarCloudAnalyze@1
- job: Api_Docs
displayName: API Docs
dependsOn: Prepare
condition: |
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
and
(
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
)
pool:
vmImage: ${{ variables.windowsImage }}
@@ -973,7 +1137,7 @@ stages:
- checkout: self
submodules: true
persistCredentials: true
fetchDepth: 1
fetchDepth: 1
- bash: ./docs.sh Windows
displayName: Create openapi.json
- bash: |
@@ -981,10 +1145,9 @@ stages:
git config --global user.name "Servarr"
git checkout -b api-docs
git add .
git status
if git status | grep modified
if git status | grep -q modified
then
git commit -am 'Automated API Docs update [skip ci]'
git commit -am 'Automated API Docs update'
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"}'
else
@@ -1008,33 +1171,25 @@ stages:
- job: Analyze_Backend
displayName: Backend
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
variables:
disable.coverage.autogenerate: 'true'
EnableAnalyzers: 'false'
pool:
vmImage: ${{ variables.linuxImage }}
vmImage: ${{ variables.windowsImage }}
steps:
- task: UseDotNet@2
displayName: 'Install .net core 2.1'
inputs:
version: 2.1.815
- task: UseDotNet@2
displayName: 'Install .net core 3.1'
inputs:
version: 3.1.413
- task: UseDotNet@2
displayName: 'Install .net core 5.0'
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: self # Need history for Sonar analysis
submodules: true
- task: Cache@2
inputs:
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
path: $(nugetCacheFolder)
displayName: Cache NuGet packages
- powershell: Set-Service SCardSvr -StartupType Manual
displayName: Enable Windows Test Service
- task: SonarCloudPrepare@1
condition: eq(variables['System.PullRequest.IsFork'], 'False')
inputs:
@@ -1045,16 +1200,14 @@ stages:
projectName: 'Readarr'
projectVersion: '$(readarrVersion)'
extraProperties: |
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,./src/Libraries/**
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
sonar.coverage.exclusions=**/Readarr.Api.V1/**/*
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
- bash: |
./build.sh --backend -f net6.0 -r linux-x64
TEST_DIR=_tests/net6.0/linux-x64/publish/ ./test.sh Linux Unit Coverage
./build.sh --backend -f net6.0 -r win-x64
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
displayName: Coverage Unit Tests
env:
NUGET_PACKAGES: $(nugetCacheFolder)
- task: SonarCloudAnalyze@1
condition: eq(variables['System.PullRequest.IsFork'], 'False')
displayName: Publish SonarCloud Results
@@ -1077,7 +1230,6 @@ stages:
- Unit_Test
- Integration
- Automation
- Build_Backend_Other
condition: eq(variables['system.pullrequest.isfork'], false)
displayName: Build Status Report
jobs:
@@ -1101,3 +1253,4 @@ stages:
DISCORDCHANNELID: $(discordChannelId)
DISCORDWEBHOOKKEY: $(discordWebhookKey)
DISCORDTHREADID: $(discordThreadId)

View File

@@ -23,7 +23,7 @@ UpdateVersionNumber()
echo "Updating Version Info"
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$READARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$READARRVERSION<\/string>/g" macOS/Readarr.app/Contents/Info.plist
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$READARRVERSION<\/string>/g" distribution/osx/Readarr.app/Contents/Info.plist
fi
}
@@ -183,7 +183,7 @@ PackageMacOSApp()
rm -rf $folder
mkdir -p $folder
cp -r macOS/Readarr.app $folder
cp -r distribution/osx/Readarr.app $folder
mkdir -p $folder/Readarr.app/Contents/MacOS
echo "Copying Binaries"
@@ -245,7 +245,7 @@ BuildInstaller()
local framework="$1"
local runtime="$2"
./_inno/ISCC.exe setup/readarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
./_inno/ISCC.exe distribution/windows/setup/readarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
}
InstallInno()

View File

@@ -44,16 +44,16 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopIcon"; Description: "{cm:CreateDesktopIcon}"
Name: "windowsService"; Description: "Install Windows Service (Starts when the computer starts as the LocalService user, you will need to change the user to access network shares)"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts when you log into Windows)"; GroupDescription: "Start automatically"; Flags: exclusive
Name: "windowsService"; Description: "Install Windows Service (Starts when the computer starts as the LocalService user, you will need to change the user to access network shares)"; GroupDescription: "Start automatically"; Flags: exclusive
Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts when you log into Windows)"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
[Dirs]
Name: "{app}"; Permissions: users-modify
[Files]
Source: "..\_artifacts\{#Runtime}\{#Framework}\Readarr\Readarr.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "..\_artifacts\{#Runtime}\{#Framework}\Readarr\*"; Excludes: "Readarr.Update"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Readarr\Readarr.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Readarr\*"; Excludes: "Readarr.Update"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
@@ -72,12 +72,13 @@ Filename: "{app}\bin\Readarr.exe"; Description: "Open Readarr Web UI"; Flags: po
Filename: "{app}\bin\Readarr.exe"; Description: "Start Readarr"; Flags: postinstall skipifsilent nowait; Tasks: startupShortcut none;
[UninstallRun]
Filename: "{app}\bin\Readarr.Console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist
Filename: "{app}\bin\readarr.console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist
[Code]
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
ResultCode: Integer;
begin
Exec(ExpandConstant('{commonappdata}\Readarr\bin\Readarr.Console.exe'), '/u', '', 0, ewWaitUntilTerminated, ResultCode)
Exec('net', 'stop readarr', '', 0, ewWaitUntilTerminated, ResultCode)
Exec('sc', 'delete readarr', '', 0, ewWaitUntilTerminated, ResultCode)
end;

View File

@@ -1,6 +1,7 @@
.version {
margin: 0 3px;
font-weight: bold;
font-family: var(--defaultFontFamily);
}
.maintenance {

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
@@ -64,12 +65,12 @@ function AppUpdatedModalContent(props) {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Readarr Updated
{translate('AppUpdated', { appName: 'Readarr' })}
</ModalHeader>
<ModalBody>
<div>
Version <span className={styles.version}>{version}</span> of Readarr has been installed, in order to get the latest changes you'll need to reload Readarr.
<InlineMarkdown data={translate('AppUpdatedVersion', { appName: 'Readarr', version })} blockClassName={styles.version} />
</div>
{
@@ -77,16 +78,14 @@ function AppUpdatedModalContent(props) {
<div>
{
!update.changes &&
<div className={styles.maintenance}>
{translate('MaintenanceRelease')}
</div>
<div className={styles.maintenance}>{translate('MaintenanceRelease')}</div>
}
{
!!update.changes &&
<div>
<div className={styles.changes}>
What's new?
{translate('WhatsNew')}
</div>
<UpdateChanges
@@ -113,14 +112,14 @@ function AppUpdatedModalContent(props) {
<Button
onPress={onSeeChangesPress}
>
Recent Changes
{translate('RecentChanges')}
</Button>
<Button
kind={kinds.PRIMARY}
onPress={onModalClose}
>
Reload
{translate('Reload')}
</Button>
</ModalFooter>
</ModalContent>

View File

@@ -7,6 +7,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './ConnectionLostModal.css';
function ConnectionLostModal(props) {
@@ -22,16 +23,16 @@ function ConnectionLostModal(props) {
>
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Connection Lost
{translate('ConnectionLost')}
</ModalHeader>
<ModalBody>
<div>
Readarr has lost its connection to the backend and will need to be reloaded to restore functionality.
{translate('ConnectionLostToBackend', { appName: 'Readarr' })}
</div>
<div className={styles.automatic}>
Readarr will try to connect automatically, or you can click reload below.
{translate('ConnectionLostReconnect', { appName: 'Readarr' })}
</div>
</ModalBody>
<ModalFooter>
@@ -39,7 +40,7 @@ function ConnectionLostModal(props) {
kind={kinds.PRIMARY}
onPress={onModalClose}
>
Reload
{translate('Reload')}
</Button>
</ModalFooter>
</ModalContent>

View File

@@ -143,7 +143,7 @@ class BookshelfFooter extends Component {
<div>
<div className={styles.label}>
{selectedCount} Author(s) Selected
{translate('CountAuthorsSelected', { selectedCount })}
</div>
<SpinnerButton

View File

@@ -41,7 +41,7 @@ class NumberInput extends Component {
componentDidUpdate(prevProps, prevState) {
const { value } = this.props;
if (value !== prevProps.value && !this.state.isFocused) {
if (!isNaN(value) && value !== prevProps.value && !this.state.isFocused) {
this.setState({
value: value == null ? '' : value.toString()
});

View File

@@ -97,6 +97,7 @@ class SpinnerErrorButton extends Component {
render() {
const {
kind,
isSpinning,
error,
children,
@@ -112,7 +113,7 @@ class SpinnerErrorButton extends Component {
const showIcon = wasSuccessful || hasWarning || hasError;
let iconName = icons.CHECK;
let iconKind = kinds.SUCCESS;
let iconKind = kind === kinds.PRIMARY ? kinds.DEFAULT : kinds.SUCCESS;
if (hasWarning) {
iconName = icons.WARNING;
@@ -126,6 +127,7 @@ class SpinnerErrorButton extends Component {
return (
<SpinnerButton
kind={kind}
isSpinning={isSpinning}
{...otherProps}
>
@@ -154,6 +156,7 @@ class SpinnerErrorButton extends Component {
}
SpinnerErrorButton.propTypes = {
kind: PropTypes.oneOf(kinds.all),
isSpinning: PropTypes.bool.isRequired,
error: PropTypes.object,
children: PropTypes.node.isRequired

View File

@@ -10,7 +10,8 @@ class InlineMarkdown extends Component {
render() {
const {
className,
data
data,
blockClassName
} = this.props;
// For now only replace links or code blocks (not both)
@@ -47,7 +48,7 @@ class InlineMarkdown extends Component {
markdownBlocks.push(data.substr(endIndex, match.index - endIndex));
}
markdownBlocks.push(<code key={`code-${match.index}`}>{match[0].substring(1, match[0].length - 1)}</code>);
markdownBlocks.push(<code key={`code-${match.index}`} className={blockClassName ?? null}>{match[0].substring(1, match[0].length - 1)}</code>);
endIndex = match.index + match[0].length;
}
@@ -66,7 +67,8 @@ class InlineMarkdown extends Component {
InlineMarkdown.propTypes = {
className: PropTypes.string,
data: PropTypes.string
data: PropTypes.string,
blockClassName: PropTypes.string
};
export default InlineMarkdown;

View File

@@ -6,6 +6,7 @@ export const BOOKSHELF = 'bookshelf';
export const KEY_VALUE_LIST = 'keyValueList';
export const MONITOR_BOOKS_SELECT = 'monitorBooksSelect';
export const MONITOR_NEW_ITEMS_SELECT = 'monitorNewItemsSelect';
export const FLOAT = 'float';
export const NUMBER = 'number';
export const OAUTH = 'oauth';
export const PASSWORD = 'password';
@@ -34,6 +35,7 @@ export const all = [
KEY_VALUE_LIST,
MONITOR_BOOKS_SELECT,
MONITOR_NEW_ITEMS_SELECT,
FLOAT,
NUMBER,
OAUTH,
PASSWORD,

View File

@@ -1,10 +1,11 @@
import PropTypes from 'prop-types';
import React from 'react';
import Alert from 'Components/Alert';
import Icon from 'Components/Icon';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { icons, sortDirections } from 'Helpers/Props';
import { icons, kinds, sortDirections } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import InteractiveSearchRow from './InteractiveSearchRow';
import styles from './InteractiveSearch.css';
@@ -112,17 +113,17 @@ function InteractiveSearch(props) {
{
!isFetching && isPopulated && !totalReleasesCount ?
<div className={styles.blankpad}>
No results found
</div> :
<Alert kind={kinds.INFO}>
{translate('NoResultsFound')}
</Alert> :
null
}
{
!!totalReleasesCount && isPopulated && !items.length ?
<div className={styles.blankpad}>
All results are hidden by the applied filter
</div> :
<Alert kind={kinds.WARNING}>
{translate('AllResultsAreHiddenByTheAppliedFilter')}
</Alert> :
null
}
@@ -157,7 +158,7 @@ function InteractiveSearch(props) {
{
totalReleasesCount !== items.length && !!items.length ?
<div className={styles.filteredMessage}>
Some results are hidden by the applied filter
{translate('SomeResultsAreHiddenByTheAppliedFilter')}
</div> :
null
}

View File

@@ -152,7 +152,7 @@ class CustomFormat extends Component {
isOpen={this.state.isDeleteCustomFormatModalOpen}
kind={kinds.DANGER}
title={translate('DeleteCustomFormat')}
message={translate('DeleteCustomFormatMessageText', [name])}
message={translate('DeleteCustomFormatMessageText', { name })}
confirmLabel={translate('Delete')}
isSpinning={isDeleting}
onConfirm={this.onConfirmDeleteCustomFormat}

View File

@@ -115,7 +115,7 @@ class Specification extends Component {
isOpen={this.state.isDeleteSpecificationModalOpen}
kind={kinds.DANGER}
title={translate('DeleteCondition')}
message={translate('DeleteConditionMessageText', [name])}
message={translate('DeleteConditionMessageText', { name })}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteSpecification}
onCancel={this.onDeleteSpecificationModalClose}

View File

@@ -113,7 +113,7 @@ class DownloadClient extends Component {
isOpen={this.state.isDeleteDownloadClientModalOpen}
kind={kinds.DANGER}
title={translate('DeleteDownloadClient')}
message={translate('DeleteDownloadClientMessageText', [name])}
message={translate('DeleteDownloadClientMessageText', { name })}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteDownloadClient}
onCancel={this.onDeleteDownloadClientModalClose}

View File

@@ -180,7 +180,7 @@ function ManageDownloadClientsEditModalContent(
<ModalFooter className={styles.modalFooter}>
<div className={styles.selected}>
{translate('CountDownloadClientsSelected', [selectedCount])}
{translate('CountDownloadClientsSelected', { selectedCount })}
</div>
<div>

View File

@@ -286,9 +286,9 @@ function ManageDownloadClientsModalContent(
isOpen={isDeleteModalOpen}
kind={kinds.DANGER}
title={translate('DeleteSelectedDownloadClients')}
message={translate('DeleteSelectedDownloadClientsMessageText', [
selectedIds.length,
])}
message={translate('DeleteSelectedDownloadClientsMessageText', {
count: selectedIds.length,
})}
confirmLabel={translate('Delete')}
onConfirm={onConfirmDelete}
onCancel={onDeleteModalClose}

View File

@@ -1,10 +1,12 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import FieldSet from 'Components/FieldSet';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import PageSectionContent from 'Components/Page/PageSectionContent';
import { icons } from 'Helpers/Props';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import EditRemotePathMappingModalConnector from './EditRemotePathMappingModalConnector';
import RemotePathMapping from './RemotePathMapping';
@@ -50,6 +52,11 @@ class RemotePathMappings extends Component {
errorMessage={translate('UnableToLoadRemotePathMappings')}
{...otherProps}
>
<Alert kind={kinds.INFO}>
<InlineMarkdown data={translate('RemotePathMappingsInfo', { app: 'Readarr', wikiLink: 'https://wiki.servarr.com/readarr/settings#remote-path-mappings' })} />
</Alert>
<div className={styles.remotePathMappingsHeader}>
<div className={styles.host}>
{translate('Host')}

View File

@@ -103,7 +103,6 @@ class GeneralSettings extends Component {
isResettingApiKey,
isWindows,
isWindowsService,
isDocker,
mode,
packageUpdateMechanism,
onInputChange,
@@ -171,7 +170,6 @@ class GeneralSettings extends Component {
settings={settings}
isWindows={isWindows}
packageUpdateMechanism={packageUpdateMechanism}
isDocker={isDocker}
onInputChange={onInputChange}
/>
@@ -214,7 +212,6 @@ GeneralSettings.propTypes = {
hasSettings: PropTypes.bool.isRequired,
isWindows: PropTypes.bool.isRequired,
isWindowsService: PropTypes.bool.isRequired,
isDocker: PropTypes.bool.isRequired,
mode: PropTypes.string.isRequired,
packageUpdateMechanism: PropTypes.string.isRequired,
onInputChange: PropTypes.func.isRequired,

View File

@@ -26,7 +26,6 @@ function createMapStateToProps() {
isResettingApiKey,
isWindows: systemStatus.isWindows,
isWindowsService: systemStatus.isWindows && systemStatus.mode === 'service',
isDocker: systemStatus.isDocker,
mode: systemStatus.mode,
packageUpdateMechanism: systemStatus.packageUpdateMechanism,
...sectionSettings

View File

@@ -8,12 +8,17 @@ import { inputTypes, sizes } from 'Helpers/Props';
import titleCase from 'Utilities/String/titleCase';
import translate from 'Utilities/String/translate';
const branchValues = [
'master',
'develop',
'nightly'
];
function UpdateSettings(props) {
const {
advancedSettings,
settings,
isWindows,
isDocker,
packageUpdateMechanism,
onInputChange
} = props;
@@ -44,32 +49,21 @@ function UpdateSettings(props) {
updateOptions.push({ key: 'script', value: 'Script' });
if (isDocker) {
return (
<FieldSet legend={translate('Updates')}>
<div>
{translate('UpdatingIsDisabledInsideADockerContainerUpdateTheContainerImageInstead')}
</div>
</FieldSet>
);
}
return (
<FieldSet legend={translate('Updates')}>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>
{translate('Branch')}
</FormLabel>
<FormLabel>{translate('Branch')}</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
type={inputTypes.AUTO_COMPLETE}
name="branch"
helpText={usingExternalUpdateMechanism ? translate('UsingExternalUpdateMechanismBranchUsedByExternalUpdateMechanism') : translate('UsingExternalUpdateMechanismBranchToUseToUpdateReadarr')}
helpLink="https://wiki.servarr.com/readarr/faq#how-do-I-update-my-readarr"
{...branch}
values={branchValues}
onChange={onInputChange}
readOnly={usingExternalUpdateMechanism}
/>
@@ -83,14 +77,13 @@ function UpdateSettings(props) {
isAdvanced={true}
size={sizes.MEDIUM}
>
<FormLabel>
{translate('Automatic')}
</FormLabel>
<FormLabel>{translate('Automatic')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="updateAutomatically"
helpText={translate('UpdateAutomaticallyHelpText')}
helpTextWarning={updateMechanism.value === 'docker' ? translate('AutomaticUpdatesDisabledDocker', { appName: 'Readarr' }) : undefined}
onChange={onInputChange}
{...updateAutomatically}
/>
@@ -100,9 +93,7 @@ function UpdateSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>
{translate('Mechanism')}
</FormLabel>
<FormLabel>{translate('Mechanism')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@@ -121,9 +112,7 @@ function UpdateSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>
{translate('ScriptPath')}
</FormLabel>
<FormLabel>{translate('ScriptPath')}</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
@@ -144,7 +133,6 @@ UpdateSettings.propTypes = {
advancedSettings: PropTypes.bool.isRequired,
settings: PropTypes.object.isRequired,
isWindows: PropTypes.bool.isRequired,
isDocker: PropTypes.bool.isRequired,
packageUpdateMechanism: PropTypes.string.isRequired,
onInputChange: PropTypes.func.isRequired
};

View File

@@ -107,7 +107,7 @@ class ImportList extends Component {
isOpen={this.state.isDeleteImportListModalOpen}
kind={kinds.DANGER}
title={translate('DeleteImportList')}
message={translate('DeleteImportListMessageText', [name])}
message={translate('DeleteImportListMessageText', { name })}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteImportList}
onCancel={this.onDeleteImportListModalClose}

View File

@@ -184,7 +184,7 @@ function ManageImportListsEditModalContent(
<ModalFooter className={styles.modalFooter}>
<div className={styles.selected}>
{translate('CountImportListsSelected', [selectedCount])}
{translate('CountImportListsSelected', { selectedCount })}
</div>
<div>

View File

@@ -283,9 +283,9 @@ function ManageImportListsModalContent(
isOpen={isDeleteModalOpen}
kind={kinds.DANGER}
title={translate('DeleteSelectedImportLists')}
message={translate('DeleteSelectedImportListsMessageText', [
selectedIds.length,
])}
message={translate('DeleteSelectedImportListsMessageText', {
count: selectedIds.length,
})}
confirmLabel={translate('Delete')}
onConfirm={onConfirmDelete}
onCancel={onDeleteModalClose}

View File

@@ -152,7 +152,7 @@ class Indexer extends Component {
isOpen={this.state.isDeleteIndexerModalOpen}
kind={kinds.DANGER}
title={translate('DeleteIndexer')}
message={translate('DeleteIndexerMessageText', [name])}
message={translate('DeleteIndexerMessageText', { name })}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteIndexer}
onCancel={this.onDeleteIndexerModalClose}

View File

@@ -178,7 +178,7 @@ function ManageIndexersEditModalContent(
<ModalFooter className={styles.modalFooter}>
<div className={styles.selected}>
{translate('CountIndexersSelected', [selectedCount])}
{translate('CountIndexersSelected', { selectedCount })}
</div>
<div>

View File

@@ -281,9 +281,9 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
isOpen={isDeleteModalOpen}
kind={kinds.DANGER}
title={translate('DeleteSelectedIndexers')}
message={translate('DeleteSelectedIndexersMessageText', [
selectedIds.length,
])}
message={translate('DeleteSelectedIndexersMessageText', {
count: selectedIds.length,
})}
confirmLabel={translate('Delete')}
onConfirm={onConfirmDelete}
onCancel={onDeleteModalClose}

View File

@@ -95,7 +95,7 @@ class RootFolder extends Component {
isOpen={this.state.isDeleteRootFolderModalOpen}
kind={kinds.DANGER}
title={translate('DeleteRootFolder')}
message={translate('DeleteRootFolderMessageText', [name])}
message={translate('DeleteRootFolderMessageText', { name })}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteRootFolder}
onCancel={this.onDeleteRootFolderModalClose}

View File

@@ -227,7 +227,7 @@ class Notification extends Component {
isOpen={this.state.isDeleteNotificationModalOpen}
kind={kinds.DANGER}
title={translate('DeleteNotification')}
message={translate('DeleteNotificationMessageText', [name])}
message={translate('DeleteNotificationMessageText', { name })}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteNotification}
onCancel={this.onDeleteNotificationModalClose}

View File

@@ -144,7 +144,7 @@ class MetadataProfile extends Component {
isOpen={this.state.isDeleteMetadataProfileModalOpen}
kind={kinds.DANGER}
title={translate('DeleteMetadataProfile')}
message={translate('DeleteMetadataProfileMessageText', [name])}
message={translate('DeleteMetadataProfileMessageText', { name })}
confirmLabel={translate('Delete')}
isSpinning={isDeleting}
onConfirm={this.onConfirmDeleteMetadataProfile}

View File

@@ -162,7 +162,7 @@ class QualityProfile extends Component {
isOpen={this.state.isDeleteQualityProfileModalOpen}
kind={kinds.DANGER}
title={translate('DeleteQualityProfile')}
message={translate('DeleteQualityProfileMessageText', [name])}
message={translate('DeleteQualityProfileMessageText', { name })}
confirmLabel={translate('Delete')}
isSpinning={isDeleting}
onConfirm={this.onConfirmDeleteQualityProfile}

View File

@@ -4,6 +4,8 @@ import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHand
import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler';
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
import { createThunk } from 'Store/thunks';
import monitorNewItemsOptions from 'Utilities/Author/monitorNewItemsOptions';
import monitorOptions from 'Utilities/Author/monitorOptions';
//
// Variables
@@ -51,6 +53,10 @@ export default {
port: 8080,
useSsl: false,
outputProfile: 'default',
defaultQualityProfileId: 0,
defaultMetadataProfileId: 0,
defaultMonitorOption: monitorOptions[0].key,
defaultNewItemMonitorOption: monitorNewItemsOptions[0].key,
defaultTags: []
},
isSaving: false,

View File

@@ -138,7 +138,7 @@ class BackupRow extends Component {
isOpen={isConfirmDeleteModalOpen}
kind={kinds.DANGER}
title={translate('DeleteBackup')}
message={translate('DeleteBackupMessageText', [name])}
message={translate('DeleteBackupMessageText', { name })}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeletePress}
onCancel={this.onConfirmDeleteModalClose}

View File

@@ -146,7 +146,7 @@ class RestoreBackupModalContent extends Component {
<ModalBody>
{
!!id && `Would you like to restore the backup '${name}'?`
!!id && translate('WouldYouLikeToRestoreBackup', { name })
}
{

View File

@@ -39,6 +39,14 @@ function getInternalLink(source) {
to="/settings/downloadclients"
/>
);
case 'NotificationStatusCheck':
return (
<IconButton
name={icons.SETTINGS}
title={translate('Settings')}
to="/settings/connect"
/>
);
case 'RootFolderCheck':
return (
<IconButton

View File

@@ -25,18 +25,19 @@ export async function fetchTranslations(): Promise<boolean> {
export default function translate(
key: string,
args?: (string | number | boolean)[]
tokens?: Record<string, string | number | boolean>
) {
if (!(key in translations)) {
console.debug(key);
}
const translation = translations[key] || key;
if (args) {
return translation.replace(/\{(\d+)\}/g, (match, index) => {
return String(args[index]) ?? match;
if (tokens) {
// Fallback to the old behaviour for translations not yet updated to use named tokens
Object.values(tokens).forEach((value, index) => {
tokens[index] = value;
});
return translation.replace(/\{([a-z0-9]+?)\}/gi, (match, tokenMatch) =>
String(tokens[tokenMatch] ?? match)
);
}
return translation;

View File

@@ -32,7 +32,7 @@
<PackageVersion Include="NLog.Extensions.Logging" Version="5.2.3" />
<PackageVersion Include="NLog" Version="5.1.4" />
<PackageVersion Include="NLog.Targets.Syslog" Version="7.0.0" />
<PackageVersion Include="Npgsql" Version="6.0.9" />
<PackageVersion Include="Npgsql" Version="7.0.4" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="NunitXml.TestLogger" Version="3.0.117" />

View File

@@ -40,6 +40,10 @@ namespace NzbDrone.Automation.Test.PageModel
var element = d.FindElement(By.ClassName("followingBalls"));
return !element.Displayed;
}
catch (StaleElementReferenceException)
{
return true;
}
catch (NoSuchElementException)
{
return true;

View File

@@ -838,7 +838,7 @@ namespace NzbDrone.Common.Test.DiskTests
// Note: never returns anything.
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetFileInfos(It.IsAny<string>(), It.IsAny<SearchOption>()))
.Setup(v => v.GetFileInfos(It.IsAny<string>(), It.IsAny<bool>()))
.Returns(new List<IFileInfo>());
Mocker.GetMock<IDiskProvider>()
@@ -878,8 +878,8 @@ namespace NzbDrone.Common.Test.DiskTests
.Returns<string>(v => fileSystem.DirectoryInfo.FromDirectoryName(v).GetDirectories().ToList());
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetFileInfos(It.IsAny<string>(), It.IsAny<SearchOption>()))
.Returns((string v, SearchOption option) => fileSystem.DirectoryInfo.FromDirectoryName(v).GetFiles("*", option).ToList());
.Setup(v => v.GetFileInfos(It.IsAny<string>(), It.IsAny<bool>()))
.Returns((string v, bool recursive) => fileSystem.DirectoryInfo.FromDirectoryName(v).GetFiles("*", new EnumerationOptions { RecurseSubdirectories = recursive }).ToList());
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetFileSize(It.IsAny<string>()))

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.GZip;
@@ -11,7 +12,7 @@ namespace NzbDrone.Common
public interface IArchiveService
{
void Extract(string compressedFile, string destination);
void CreateZip(string path, params string[] files);
void CreateZip(string path, IEnumerable<string> files);
}
public class ArchiveService : IArchiveService
@@ -39,7 +40,7 @@ namespace NzbDrone.Common
_logger.Debug("Extraction complete.");
}
public void CreateZip(string path, params string[] files)
public void CreateZip(string path, IEnumerable<string> files)
{
using (var zipFile = ZipFile.Create(path))
{

View File

@@ -53,7 +53,7 @@ namespace NzbDrone.Common.Disk
{
CheckFolderExists(path);
var dirFiles = GetFiles(path, SearchOption.AllDirectories).ToList();
var dirFiles = GetFiles(path, true).ToList();
if (!dirFiles.Any())
{
@@ -156,11 +156,11 @@ namespace NzbDrone.Common.Disk
return _fileSystem.Directory.EnumerateFileSystemEntries(path).Empty();
}
public string[] GetDirectories(string path)
public IEnumerable<string> GetDirectories(string path)
{
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
return _fileSystem.Directory.GetDirectories(path);
return _fileSystem.Directory.EnumerateDirectories(path);
}
public string[] GetDirectories(string path, SearchOption searchOption)
@@ -170,18 +170,22 @@ namespace NzbDrone.Common.Disk
return _fileSystem.Directory.GetDirectories(path, "*", searchOption);
}
public string[] GetFiles(string path, SearchOption searchOption)
public IEnumerable<string> GetFiles(string path, bool recursive)
{
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
return _fileSystem.Directory.GetFiles(path, "*.*", searchOption);
return _fileSystem.Directory.EnumerateFiles(path, "*", new EnumerationOptions
{
RecurseSubdirectories = recursive,
IgnoreInaccessible = true
});
}
public long GetFolderSize(string path)
{
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
return GetFiles(path, SearchOption.AllDirectories).Sum(e => _fileSystem.FileInfo.FromFileName(e).Length);
return GetFiles(path, true).Sum(e => _fileSystem.FileInfo.FromFileName(e).Length);
}
public long GetFileSize(string path)
@@ -302,8 +306,9 @@ namespace NzbDrone.Common.Disk
{
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
var files = _fileSystem.Directory.GetFiles(path, "*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
Array.ForEach(files, RemoveReadOnly);
var files = GetFiles(path, recursive);
files.ToList().ForEach(RemoveReadOnly);
_fileSystem.Directory.Delete(path, recursive);
}
@@ -404,7 +409,7 @@ namespace NzbDrone.Common.Disk
{
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
foreach (var file in GetFiles(path, SearchOption.TopDirectoryOnly))
foreach (var file in GetFiles(path, false))
{
DeleteFile(file);
}
@@ -504,13 +509,17 @@ namespace NzbDrone.Common.Disk
return _fileSystem.DirectoryInfo.FromDirectoryName(path);
}
public List<IFileInfo> GetFileInfos(string path, SearchOption searchOption = SearchOption.TopDirectoryOnly)
public List<IFileInfo> GetFileInfos(string path, bool recursive = false)
{
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
var di = _fileSystem.DirectoryInfo.FromDirectoryName(path);
return di.GetFiles("*", searchOption).ToList();
return di.EnumerateFiles("*", new EnumerationOptions
{
RecurseSubdirectories = recursive,
IgnoreInaccessible = true
}).ToList();
}
public IFileInfo GetFileInfo(string path)

View File

@@ -23,8 +23,8 @@ namespace NzbDrone.Common.Disk
bool FileExists(string path, StringComparison stringComparison);
bool FolderWritable(string path);
bool FolderEmpty(string path);
string[] GetDirectories(string path);
string[] GetFiles(string path, SearchOption searchOption);
IEnumerable<string> GetDirectories(string path);
IEnumerable<string> GetFiles(string path, bool recursive);
long GetFolderSize(string path);
long GetFileSize(string path);
void CreateFolder(string path);
@@ -54,7 +54,7 @@ namespace NzbDrone.Common.Disk
IDirectoryInfo GetDirectoryInfo(string path);
List<IDirectoryInfo> GetDirectoryInfos(string path);
IFileInfo GetFileInfo(string path);
List<IFileInfo> GetFileInfos(string path, SearchOption searchOption = SearchOption.TopDirectoryOnly);
List<IFileInfo> GetFileInfos(string path, bool recursive = false);
void RemoveEmptySubfolders(string path);
void SaveStream(Stream stream, string path);
bool IsValidFolderPermissionMask(string mask);

View File

@@ -78,8 +78,8 @@ namespace NzbDrone.Common.EnvironmentInfo
}
if (IsLinux &&
((File.Exists("/proc/1/cgroup") && File.ReadAllText("/proc/1/cgroup").Contains("/docker/")) ||
(File.Exists("/proc/1/mountinfo") && File.ReadAllText("/proc/1/mountinfo").Contains("/docker/"))))
(File.Exists("/.dockerenv") ||
(File.Exists("/proc/1/cgroup") && File.ReadAllText("/proc/1/cgroup").Contains("/docker/"))))
{
IsDocker = true;
}

View File

@@ -140,7 +140,7 @@ namespace NzbDrone.Common.Extensions
public static bool IsPathValid(this string path, PathValidationType validationType)
{
if (path.ContainsInvalidPathChars() || string.IsNullOrWhiteSpace(path))
if (string.IsNullOrWhiteSpace(path) || path.ContainsInvalidPathChars())
{
return false;
}
@@ -160,6 +160,11 @@ namespace NzbDrone.Common.Extensions
public static bool ContainsInvalidPathChars(this string text)
{
if (text.IsNullOrWhiteSpace())
{
throw new ArgumentNullException("text");
}
return text.IndexOfAny(Path.GetInvalidPathChars()) >= 0;
}

View File

@@ -131,9 +131,9 @@ namespace NzbDrone.Common.Instrumentation
private static void RegisterAppFile(IAppFolderInfo appFolderInfo)
{
RegisterAppFile(appFolderInfo, "appFileInfo", "Readarr.txt", 5, LogLevel.Info);
RegisterAppFile(appFolderInfo, "appFileDebug", "Readarr.debug.txt", 50, LogLevel.Off);
RegisterAppFile(appFolderInfo, "appFileTrace", "Readarr.trace.txt", 50, LogLevel.Off);
RegisterAppFile(appFolderInfo, "appFileInfo", "readarr.txt", 5, LogLevel.Info);
RegisterAppFile(appFolderInfo, "appFileDebug", "readarr.debug.txt", 50, LogLevel.Off);
RegisterAppFile(appFolderInfo, "appFileTrace", "readarr.trace.txt", 50, LogLevel.Off);
}
private static void RegisterAppFile(IAppFolderInfo appFolderInfo, string name, string fileName, int maxArchiveFiles, LogLevel minLogLevel)

View File

@@ -31,6 +31,10 @@ namespace NzbDrone.Core.Test.DiskSpace
.Setup(x => x.All())
.Returns(new List<RootFolder>() { _rootDir });
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FolderExists(_rootDir.Path))
.Returns(true);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetMounts())
.Returns(new List<IMount>());

View File

@@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
.Returns(new[] { targetDir });
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.GetFiles(targetDir, SearchOption.AllDirectories))
.Setup(c => c.GetFiles(targetDir, true))
.Returns(new[] { Path.Combine(targetDir, "somefile.flac") });
Mocker.GetMock<IDiskProvider>()

View File

@@ -82,7 +82,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
.Returns(new[] { targetDir });
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.GetFiles(targetDir, SearchOption.AllDirectories))
.Setup(c => c.GetFiles(targetDir, true))
.Returns(new[] { Path.Combine(targetDir, "somefile.flac") });
Mocker.GetMock<IDiskProvider>()

View File

@@ -74,7 +74,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
.Returns(new[] { targetDir });
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.GetFiles(targetDir, SearchOption.AllDirectories))
.Setup(c => c.GetFiles(targetDir, true))
.Returns(new[] { Path.Combine(targetDir, "somefile.flac") });
Mocker.GetMock<IDiskProvider>()

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.HealthCheck.Checks
{
[TestFixture]
public class NotificationStatusCheckFixture : CoreTest<NotificationStatusCheck>
{
private List<INotification> _notifications = new List<INotification>();
private List<NotificationStatus> _blockedNotifications = new List<NotificationStatus>();
[SetUp]
public void SetUp()
{
Mocker.GetMock<INotificationFactory>()
.Setup(v => v.GetAvailableProviders())
.Returns(_notifications);
Mocker.GetMock<INotificationStatusService>()
.Setup(v => v.GetBlockedProviders())
.Returns(_blockedNotifications);
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
}
private Mock<INotification> GivenNotification(int id, double backoffHours, double failureHours)
{
var mockNotification = new Mock<INotification>();
mockNotification.SetupGet(s => s.Definition).Returns(new NotificationDefinition { Id = id });
_notifications.Add(mockNotification.Object);
if (backoffHours != 0.0)
{
_blockedNotifications.Add(new NotificationStatus
{
ProviderId = id,
InitialFailure = DateTime.UtcNow.AddHours(-failureHours),
MostRecentFailure = DateTime.UtcNow.AddHours(-0.1),
EscalationLevel = 5,
DisabledTill = DateTime.UtcNow.AddHours(backoffHours)
});
}
return mockNotification;
}
[Test]
public void should_not_return_error_when_no_notifications()
{
Subject.Check().ShouldBeOk();
}
[Test]
public void should_return_warning_if_notification_unavailable()
{
GivenNotification(1, 10.0, 24.0);
GivenNotification(2, 0.0, 0.0);
Subject.Check().ShouldBeWarning();
}
[Test]
public void should_return_error_if_all_notifications_unavailable()
{
GivenNotification(1, 10.0, 24.0);
Subject.Check().ShouldBeError();
}
[Test]
public void should_return_warning_if_few_notifications_unavailable()
{
GivenNotification(1, 10.0, 24.0);
GivenNotification(2, 10.0, 24.0);
GivenNotification(3, 0.0, 0.0);
Subject.Check().ShouldBeWarning();
}
}
}

View File

@@ -8,7 +8,9 @@ using NzbDrone.Core.Books;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.ImportLists;
using NzbDrone.Core.Localization;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.HealthCheck.Checks
{
@@ -23,7 +25,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns("Some Warning Message");
}
private void GivenMissingRootFolder()
private void GivenMissingRootFolder(string rootFolderPath)
{
var author = Builder<Author>.CreateListOfSize(1)
.Build()
@@ -41,9 +43,9 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Setup(s => s.All())
.Returns(importList);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetParentFolder(author.First().Path))
.Returns(@"C:\Books");
Mocker.GetMock<IRootFolderService>()
.Setup(s => s.GetBestRootFolderPath(It.IsAny<string>()))
.Returns(rootFolderPath);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderExists(It.IsAny<string>()))
@@ -67,7 +69,25 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
[Test]
public void should_return_error_if_book_parent_is_missing()
{
GivenMissingRootFolder();
GivenMissingRootFolder(@"C:\Books".AsOsAgnostic());
Subject.Check().ShouldBeError();
}
[Test]
public void should_return_error_if_series_path_is_for_posix_os()
{
WindowsOnly();
GivenMissingRootFolder("/mnt/books");
Subject.Check().ShouldBeError();
}
[Test]
public void should_return_error_if_series_path_is_for_windows()
{
PosixOnly();
GivenMissingRootFolder(@"C:\Books");
Subject.Check().ShouldBeError();
}

View File

@@ -0,0 +1,56 @@
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.Notifications.Join;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{
[TestFixture]
public class CleanupOrphanedNotificationStatusFixture : DbTest<CleanupOrphanedNotificationStatus, NotificationStatus>
{
private NotificationDefinition _notification;
[SetUp]
public void Setup()
{
_notification = Builder<NotificationDefinition>.CreateNew()
.With(s => s.Settings = new JoinSettings { })
.BuildNew();
}
private void GivenNotification()
{
Db.Insert(_notification);
}
[Test]
public void should_delete_orphaned_notificationstatus()
{
var status = Builder<NotificationStatus>.CreateNew()
.With(h => h.ProviderId = _notification.Id)
.BuildNew();
Db.Insert(status);
Subject.Clean();
AllStoredModels.Should().BeEmpty();
}
[Test]
public void should_not_delete_unorphaned_notificationstatus()
{
GivenNotification();
var status = Builder<NotificationStatus>.CreateNew()
.With(h => h.ProviderId = _notification.Id)
.BuildNew();
Db.Insert(status);
Subject.Clean();
AllStoredModels.Should().HaveCount(1);
AllStoredModels.Should().Contain(h => h.ProviderId == _notification.Id);
}
}
}

View File

@@ -9,6 +9,7 @@ using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Books;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.MediaFiles;
@@ -66,10 +67,26 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
private void GivenRootFolder(params string[] subfolders)
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderExists(_rootFolder))
.Returns(true);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetDirectories(_rootFolder))
.Returns(subfolders);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderEmpty(_rootFolder))
.Returns(subfolders.Empty());
FileSystem.AddDirectory(_rootFolder);
foreach (var folder in subfolders)
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderExists(folder))
.Returns(true);
FileSystem.AddDirectory(folder);
}
}
@@ -79,7 +96,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
GivenRootFolder(_author.Path);
}
private List<IFileInfo> GivenFiles(IEnumerable<string> files, DateTimeOffset? lastWrite = null)
private void GivenFiles(IEnumerable<string> files, DateTimeOffset? lastWrite = null)
{
if (lastWrite == null)
{
@@ -92,7 +109,9 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
FileSystem.AddFile(file, new MockFileData(string.Empty) { LastWriteTime = lastWrite.Value });
}
return files.Select(x => DiskProvider.GetFileInfo(x)).ToList();
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFileInfos(It.IsAny<string>(), true))
.Returns(files.Select(x => DiskProvider.GetFileInfo(x)).ToList());
}
private void GivenKnownFiles(IEnumerable<string> files, DateTimeOffset? lastWrite = null)
@@ -139,7 +158,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
ExceptionVerification.ExpectedWarns(1);
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.GetFiles(_author.Path, SearchOption.AllDirectories), Times.Never());
.Verify(v => v.GetFiles(_author.Path, true), Times.Never());
Mocker.GetMock<IMediaFileTableCleanupService>()
.Verify(v => v.Clean(It.IsAny<string>(), It.IsAny<List<string>>()), Times.Never());
@@ -194,6 +213,9 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(new List<string> { _author.Path });
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.GetFileInfos(It.IsAny<string>(), It.IsAny<bool>()), Times.Once());
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<IFileInfo>>(l => l.Count == 1), It.IsAny<IdentificationOverrides>(), It.IsAny<ImportDecisionMakerInfo>(), It.IsAny<ImportDecisionMakerConfig>()), Times.Once());
}
@@ -384,6 +406,8 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
[Test]
public void should_insert_new_unmatched_files_when_all_new()
{
GivenAuthorFolder();
var files = new List<string>
{
Path.Combine(_author.Path, "Season 1", "file1.mobi"),
@@ -404,6 +428,8 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
[Test]
public void should_insert_new_unmatched_files_when_some_known()
{
GivenAuthorFolder();
var files = new List<string>
{
Path.Combine(_author.Path, "Season 1", "file1.mobi"),
@@ -424,6 +450,8 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
[Test]
public void should_not_insert_files_when_all_known()
{
GivenAuthorFolder();
var files = new List<string>
{
Path.Combine(_author.Path, "Season 1", "file1.mobi"),
@@ -448,6 +476,8 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
[Test]
public void should_not_update_info_for_unchanged_known_files()
{
GivenAuthorFolder();
var files = new List<string>
{
Path.Combine(_author.Path, "Season 1", "file1.mobi"),
@@ -472,6 +502,8 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
[Test]
public void should_update_info_for_changed_known_files()
{
GivenAuthorFolder();
var files = new List<string>
{
Path.Combine(_author.Path, "Season 1", "file1.mobi"),

View File

@@ -85,8 +85,8 @@ namespace NzbDrone.Core.Test.MetadataSource.Goodreads
ExceptionVerification.IgnoreWarns();
}
[TestCase("Catherine Butler", 0, typeof(Author), new[] { "Catherine Butler" }, TestName = "author")]
[TestCase("Catherine Butler", 1, typeof(Book), new[] { "Twisted Winter", "Shattered Dreams" }, TestName = "book")]
[TestCase("Roald Dahl", 0, typeof(Author), new[] { "Roald Dahl" }, TestName = "author")]
[TestCase("Roald Dahl", 1, typeof(Book), new[] { "Going Solo", "Boy: Tales of Childhood" }, TestName = "book")]
public void successful_combined_search(string query, int position, Type resultType, string[] expected)
{
var result = Subject.SearchForNewEntity(query);

View File

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

View File

@@ -32,7 +32,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
};
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFileInfos(It.IsAny<string>(), It.IsAny<SearchOption>()))
.Setup(s => s.GetFileInfos(It.IsAny<string>(), It.IsAny<bool>()))
.Returns(new List<IFileInfo>());
}
@@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
}
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFileInfos(It.IsAny<string>(), SearchOption.AllDirectories))
.Setup(s => s.GetFileInfos(It.IsAny<string>(), true))
.Returns(filesToReturn);
}
@@ -60,8 +60,8 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
{
Subject.GetBookFiles(_path);
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, SearchOption.AllDirectories), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, SearchOption.TopDirectoryOnly), Times.Never());
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, true), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, false), Times.Never());
}
[Test]
@@ -69,8 +69,8 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
{
Subject.GetBookFiles(_path, true);
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, SearchOption.AllDirectories), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, SearchOption.TopDirectoryOnly), Times.Never());
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, true), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, false), Times.Never());
}
[Test]
@@ -78,8 +78,8 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
{
Subject.GetBookFiles(_path, false);
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, SearchOption.AllDirectories), Times.Never());
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, SearchOption.TopDirectoryOnly), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, true), Times.Never());
Mocker.GetMock<IDiskProvider>().Verify(s => s.GetFileInfos(_path, false), Times.Once());
}
[Test]

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
using System;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
@@ -42,7 +41,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetDirectories(RecycleBin))
.Returns(new[] { @"C:\Test\RecycleBin\Folder1", @"C:\Test\RecycleBin\Folder2", @"C:\Test\RecycleBin\Folder3" });
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(RecycleBin, SearchOption.AllDirectories))
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(RecycleBin, true))
.Returns(new[] { @"C:\Test\RecycleBin\File1.avi", @"C:\Test\RecycleBin\File2.mkv" });
}

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
using System;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
@@ -68,7 +67,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
WithRecycleBin();
var path = @"C:\Test\TV\30 Rock".AsOsAgnostic();
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(@"C:\Test\Recycle Bin\30 Rock".AsOsAgnostic(), SearchOption.AllDirectories))
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(@"C:\Test\Recycle Bin\30 Rock".AsOsAgnostic(), true))
.Returns(new[] { "File1", "File2", "File3" });
Mocker.Resolve<RecycleBinProvider>().DeleteFolder(path);

View File

@@ -1,4 +1,3 @@
using System.IO;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
@@ -22,7 +21,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetDirectories(RecycleBin))
.Returns(new[] { @"C:\Test\RecycleBin\Folder1", @"C:\Test\RecycleBin\Folder2", @"C:\Test\RecycleBin\Folder3" });
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(RecycleBin, SearchOption.TopDirectoryOnly))
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(RecycleBin, false))
.Returns(new[] { @"C:\Test\RecycleBin\File1.avi", @"C:\Test\RecycleBin\File2.mkv" });
}

View File

@@ -1,8 +1,6 @@
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
@@ -22,27 +20,46 @@ namespace NzbDrone.Core.Test.RootFolderTests
[Test]
public void should_return_root_folder_that_is_parent_path()
{
GivenRootFolders(@"C:\Test\Music".AsOsAgnostic(), @"D:\Test\Music".AsOsAgnostic());
Subject.GetBestRootFolderPath(@"C:\Test\Music\Series Title".AsOsAgnostic()).Should().Be(@"C:\Test\Music".AsOsAgnostic());
GivenRootFolders(@"C:\Test\Books".AsOsAgnostic(), @"D:\Test\Books".AsOsAgnostic());
Subject.GetBestRootFolderPath(@"C:\Test\Books\Author Title".AsOsAgnostic()).Should().Be(@"C:\Test\Books".AsOsAgnostic());
}
[Test]
public void should_return_root_folder_that_is_grandparent_path()
{
GivenRootFolders(@"C:\Test\Music".AsOsAgnostic(), @"D:\Test\Music".AsOsAgnostic());
Subject.GetBestRootFolderPath(@"C:\Test\Music\S\Series Title".AsOsAgnostic()).Should().Be(@"C:\Test\Music".AsOsAgnostic());
GivenRootFolders(@"C:\Test\Books".AsOsAgnostic(), @"D:\Test\Books".AsOsAgnostic());
Subject.GetBestRootFolderPath(@"C:\Test\Books\S\Author Title".AsOsAgnostic()).Should().Be(@"C:\Test\Books".AsOsAgnostic());
}
[Test]
public void should_get_parent_path_from_diskProvider_if_matching_root_folder_is_not_found()
public void should_get_parent_path_from_os_path_if_matching_root_folder_is_not_found()
{
var seriesPath = @"T:\Test\Music\Series Title".AsOsAgnostic();
var artistPath = @"T:\Test\Books\Author Title".AsOsAgnostic();
GivenRootFolders(@"C:\Test\Music".AsOsAgnostic(), @"D:\Test\Music".AsOsAgnostic());
Subject.GetBestRootFolderPath(seriesPath);
GivenRootFolders(@"C:\Test\Books".AsOsAgnostic(), @"D:\Test\Books".AsOsAgnostic());
Subject.GetBestRootFolderPath(artistPath).Should().Be(@"T:\Test\Books".AsOsAgnostic());
}
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.GetParentFolder(seriesPath), Times.Once);
[Test]
public void should_get_parent_path_from_os_path_if_matching_root_folder_is_not_found_for_posix_path()
{
WindowsOnly();
var artistPath = "/mnt/books/Author Title";
GivenRootFolders(@"C:\Test\Books".AsOsAgnostic(), @"D:\Test\Books".AsOsAgnostic());
Subject.GetBestRootFolderPath(artistPath).Should().Be(@"/mnt/books");
}
[Test]
public void should_get_parent_path_from_os_path_if_matching_root_folder_is_not_found_for_windows_path()
{
PosixOnly();
var artistPath = @"T:\Test\Books\Author Title";
GivenRootFolders(@"C:\Test\Books".AsOsAgnostic(), @"D:\Test\Books".AsOsAgnostic());
Subject.GetBestRootFolderPath(artistPath).Should().Be(@"T:\Test\Books");
}
}
}

View File

@@ -34,6 +34,7 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
public class ProviderStatusServiceFixture : CoreTest<MockProviderStatusService>
{
private readonly TimeSpan _disabledTillPrecision = TimeSpan.FromMilliseconds(500);
private DateTime _epoch;
[SetUp]
@@ -90,7 +91,7 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(5), 500);
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(1), _disabledTillPrecision);
}
[Test]
@@ -133,7 +134,7 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(15), 500);
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(5), _disabledTillPrecision);
}
[Test]
@@ -160,7 +161,7 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
status.Should().NotBeNull();
origStatus.EscalationLevel.Should().Be(3);
status.DisabledTill.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(5), 500);
status.DisabledTill.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(5), _disabledTillPrecision);
}
}
}

View File

@@ -90,17 +90,6 @@ namespace NzbDrone.Core.Test.UpdateTests
.Returns(true);
}
[Test]
public void should_not_update_if_inside_docker()
{
Mocker.GetMock<IOsInfo>().Setup(x => x.IsDocker).Returns(true);
Subject.Execute(new ApplicationUpdateCommand());
Mocker.GetMock<IProcessProvider>()
.Verify(c => c.Start(It.IsAny<string>(), It.Is<string>(s => s.StartsWith("12")), null, null, null), Times.Never());
}
[Test]
public void should_delete_sandbox_before_update_if_folder_exists()
{
@@ -339,6 +328,28 @@ namespace NzbDrone.Core.Test.UpdateTests
.Verify(v => v.SaveConfigDictionary(It.Is<Dictionary<string, object>>(d => d.ContainsKey("Branch") && (string)d["Branch"] == "fake")), Times.Once());
}
[Test]
public void should_not_update_with_built_in_updater_inside_docker_container()
{
Mocker.GetMock<IDeploymentInfoProvider>().Setup(x => x.PackageUpdateMechanism).Returns(UpdateMechanism.Docker);
Subject.Execute(new ApplicationUpdateCommand());
Mocker.GetMock<IProcessProvider>()
.Verify(c => c.Start(It.IsAny<string>(), It.Is<string>(s => s.StartsWith("12")), null, null, null), Times.Never());
}
[Test]
public void should_not_update_with_built_in_updater_when_external_updater_is_configured()
{
Mocker.GetMock<IDeploymentInfoProvider>().Setup(x => x.IsExternalUpdateMechanism).Returns(true);
Subject.Execute(new ApplicationUpdateCommand());
Mocker.GetMock<IProcessProvider>()
.Verify(c => c.Start(It.IsAny<string>(), It.Is<string>(s => s.StartsWith("12")), null, null, null), Times.Never());
}
[TearDown]
public void TearDown()
{

View File

@@ -89,7 +89,7 @@ namespace NzbDrone.Core.Backup
// Delete journal file created during database backup
_diskProvider.DeleteFile(Path.Combine(_backupTempFolder, "readarr.db-journal"));
_archiveService.CreateZip(backupPath, _diskProvider.GetFiles(_backupTempFolder, SearchOption.TopDirectoryOnly));
_archiveService.CreateZip(backupPath, _diskProvider.GetFiles(_backupTempFolder, false));
Cleanup();
@@ -128,7 +128,7 @@ namespace NzbDrone.Core.Backup
_archiveService.Extract(backupFileName, temporaryPath);
foreach (var file in _diskProvider.GetFiles(temporaryPath, SearchOption.TopDirectoryOnly))
foreach (var file in _diskProvider.GetFiles(temporaryPath, false))
{
var fileName = Path.GetFileName(file);
@@ -237,7 +237,7 @@ namespace NzbDrone.Core.Backup
private IEnumerable<string> GetBackupFiles(string path)
{
var files = _diskProvider.GetFiles(path, SearchOption.TopDirectoryOnly);
var files = _diskProvider.GetFiles(path, false);
return files.Where(f => BackupFileRegex.IsMatch(f));
}

View File

@@ -0,0 +1,19 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(037)]
public class add_notification_status : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Create.TableForModel("NotificationStatus")
.WithColumn("ProviderId").AsInt32().NotNullable().Unique()
.WithColumn("InitialFailure").AsDateTimeOffset().Nullable()
.WithColumn("MostRecentFailure").AsDateTimeOffset().Nullable()
.WithColumn("EscalationLevel").AsInt32().NotNullable()
.WithColumn("DisabledTill").AsDateTimeOffset().Nullable();
}
}
}

View File

@@ -202,6 +202,7 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<IndexerStatus>("IndexerStatus").RegisterModel();
Mapper.Entity<DownloadClientStatus>("DownloadClientStatus").RegisterModel();
Mapper.Entity<ImportListStatus>("ImportListStatus").RegisterModel();
Mapper.Entity<NotificationStatus>("NotificationStatus").RegisterModel();
Mapper.Entity<CustomFilter>("CustomFilters").RegisterModel();
Mapper.Entity<ImportListExclusion>("ImportListExclusions").RegisterModel();

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.RootFolders;
namespace NzbDrone.Core.DiskSpace
@@ -33,7 +34,7 @@ namespace NzbDrone.Core.DiskSpace
public List<DiskSpace> GetFreeSpace()
{
var importantRootFolders = _rootFolderService.All().Select(x => x.Path).ToList();
var importantRootFolders = GetRootPaths().Distinct().ToList();
var optionalRootFolders = GetFixedDisksRootPaths().Except(importantRootFolders).Distinct().ToList();
@@ -42,6 +43,14 @@ namespace NzbDrone.Core.DiskSpace
return diskSpace;
}
private IEnumerable<string> GetRootPaths()
{
return _rootFolderService.All()
.Select(x => x.Path)
.Where(path => path.IsPathValid(PathValidationType.CurrentOs) && _diskProvider.FolderExists(path))
.Distinct();
}
private IEnumerable<string> GetFixedDisksRootPaths()
{
return _diskProvider.GetMounts()

View File

@@ -69,7 +69,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
if (PreCheckWatchItemExpiry(newWatchItem, oldWatchItem))
{
var files = _diskProvider.GetFiles(folder, SearchOption.AllDirectories);
var files = _diskProvider.GetFiles(folder, true);
newWatchItem.TotalSize = files.Select(_diskProvider.GetFileSize).Sum();
newWatchItem.Hash = GetHash(folder, files);
@@ -153,7 +153,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
}
}
private string GetHash(string folder, string[] files)
private string GetHash(string folder, IEnumerable<string> files)
{
var data = new StringBuilder();

View File

@@ -61,7 +61,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
public override IEnumerable<DownloadClientItem> GetItems()
{
foreach (var file in _diskProvider.GetFiles(Settings.StrmFolder, SearchOption.TopDirectoryOnly))
foreach (var file in _diskProvider.GetFiles(Settings.StrmFolder, false))
{
if (Path.GetExtension(file) != ".strm")
{

View File

@@ -280,6 +280,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
if (config.DhtEnabled)
{
item.Status = DownloadItemStatus.Queued;
item.Message = "qBittorrent is downloading metadata";
}
else
{

View File

@@ -56,11 +56,11 @@ namespace NzbDrone.Core.Download
private IEnumerable<IDownloadClient> FilterBlockedClients(IEnumerable<IDownloadClient> clients)
{
var blockedIndexers = _downloadClientStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);
var blockedClients = _downloadClientStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);
foreach (var client in clients)
{
if (blockedIndexers.TryGetValue(client.Definition.Id, out var downloadClientStatus))
if (blockedClients.TryGetValue(client.Definition.Id, out var downloadClientStatus))
{
_logger.Debug("Temporarily ignoring download client {0} till {1} due to recent failures.", client.Definition.Name, downloadClientStatus.DisabledTill.Value.ToLocalTime());
continue;

View File

@@ -64,7 +64,7 @@ namespace NzbDrone.Core.Extras
var sourcePath = localBook.Path;
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
var files = _diskProvider.GetFiles(sourceFolder, SearchOption.TopDirectoryOnly);
var files = _diskProvider.GetFiles(sourceFolder, false);
var wantedExtensions = _configService.ExtraFileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(e => e.Trim(' ', '.'))

View File

@@ -0,0 +1,52 @@
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{
[CheckOn(typeof(ProviderUpdatedEvent<INotification>))]
[CheckOn(typeof(ProviderDeletedEvent<INotification>))]
[CheckOn(typeof(ProviderStatusChangedEvent<INotification>))]
public class NotificationStatusCheck : HealthCheckBase
{
private readonly INotificationFactory _providerFactory;
private readonly INotificationStatusService _providerStatusService;
public NotificationStatusCheck(INotificationFactory providerFactory, INotificationStatusService providerStatusService, ILocalizationService localizationService)
: base(localizationService)
{
_providerFactory = providerFactory;
_providerStatusService = providerStatusService;
}
public override HealthCheck Check()
{
var enabledProviders = _providerFactory.GetAvailableProviders();
var backOffProviders = enabledProviders.Join(_providerStatusService.GetBlockedProviders(),
i => i.Definition.Id,
s => s.ProviderId,
(i, s) => new { Provider = i, Status = s })
.ToList();
if (backOffProviders.Empty())
{
return new HealthCheck(GetType());
}
if (backOffProviders.Count == enabledProviders.Count)
{
return new HealthCheck(GetType(),
HealthCheckResult.Error,
_localizationService.GetLocalizedString("NotificationStatusAllClientHealthCheckMessage"),
"#notifications-are-unavailable-due-to-failures");
}
return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("NotificationStatusSingleClientHealthCheckMessage"), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#notifications-are-unavailable-due-to-failures");
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Linq;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Books;
using NzbDrone.Core.Books.Events;
using NzbDrone.Core.ImportLists;
@@ -35,8 +36,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
.Select(s => _rootFolderService.GetBestRootFolderPath(s.Value))
.Distinct();
var missingRootFolders = rootFolders.Where(s => !_diskProvider.FolderExists(s))
.ToList();
var missingRootFolders = rootFolders.Where(s => !s.IsPathValid(PathValidationType.CurrentOs) || !_diskProvider.FolderExists(s))
.ToList();
missingRootFolders.AddRange(_importListFactory.All()
.Select(s => s.RootFolderPath)

View File

@@ -1,4 +1,4 @@
using Dapper;
using Dapper;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Housekeeping.Housekeepers

View File

@@ -0,0 +1,27 @@
using Dapper;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class CleanupOrphanedNotificationStatus : IHousekeepingTask
{
private readonly IMainDatabase _database;
public CleanupOrphanedNotificationStatus(IMainDatabase database)
{
_database = database;
}
public void Clean()
{
using var mapper = _database.OpenConnection();
mapper.Execute(@"DELETE FROM ""NotificationStatus""
WHERE ""Id"" IN (
SELECT ""NotificationStatus"".""Id"" FROM ""NotificationStatus""
LEFT OUTER JOIN ""Notifications""
ON ""NotificationStatus"".""ProviderId"" = ""Notifications"".""Id""
WHERE ""Notifications"".""Id"" IS NULL)");
}
}
}

View File

@@ -0,0 +1,12 @@
using NzbDrone.Core.Notifications;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class FixFutureNotificationStatusTimes : FixFutureProviderStatusTimes<NotificationStatus>, IHousekeepingTask
{
public FixFutureNotificationStatusTimes(INotificationStatusRepository notificationStatusRepository)
: base(notificationStatusRepository)
{
}
}
}

View File

@@ -603,5 +603,30 @@
"SetTags": "تعيين العلامات",
"Yes": "نعم",
"ApplyTagsHelpTextHowToApplyAuthors": "كيفية تطبيق العلامات على الأفلام المختارة",
"ApplyTagsHelpTextHowToApplyDownloadClients": "كيفية تطبيق العلامات على الأفلام المختارة"
"ApplyTagsHelpTextHowToApplyDownloadClients": "كيفية تطبيق العلامات على الأفلام المختارة",
"Small": "صغير",
"TotalSpace": "المساحة الكلية",
"Ui": "واجهة المستخدم",
"RecentChanges": "التغييرات الأخيرة",
"FreeSpace": "مساحة فارغة",
"LastExecution": "آخر تنفيذ",
"LastWriteTime": "وقت الكتابة الأخير",
"Location": "موقعك",
"Medium": "متوسط",
"NextExecution": "التنفيذ القادم",
"NoResultsFound": "لم يتم العثور على نتائج",
"NotificationStatusAllClientHealthCheckMessage": "جميع القوائم غير متاحة بسبب الإخفاقات",
"NotificationStatusSingleClientHealthCheckMessage": "القوائم غير متاحة بسبب الإخفاقات: {0}",
"SomeResultsAreHiddenByTheAppliedFilter": "بعض النتائج مخفية بواسطة عامل التصفية المطبق",
"ConnectionLost": "انقطع الاتصال",
"ConnectionLostReconnect": "سيحاول Radarr الاتصال تلقائيًا ، أو يمكنك النقر فوق إعادة التحميل أدناه.",
"LastDuration": "المدة الماضية",
"Large": "كبير",
"WhatsNew": "ما هو الجديد؟",
"Events": "الأحداث",
"Activity": "نشاط",
"AddNew": "اضف جديد",
"System": "النظام",
"AllResultsAreHiddenByTheAppliedFilter": "يتم إخفاء جميع النتائج بواسطة عامل التصفية المطبق",
"Backup": "دعم"
}

View File

@@ -602,5 +602,29 @@
"SetTags": "Задаване на маркери",
"Yes": "Да",
"ApplyTagsHelpTextHowToApplyAuthors": "Как да приложите тагове към избраните филми",
"ApplyTagsHelpTextHowToApplyDownloadClients": "Как да приложите тагове към избраните филми"
"ApplyTagsHelpTextHowToApplyDownloadClients": "Как да приложите тагове към избраните филми",
"NotificationStatusAllClientHealthCheckMessage": "Всички списъци са недостъпни поради неуспехи",
"NotificationStatusSingleClientHealthCheckMessage": "Списъци, недостъпни поради неуспехи: {0}",
"Small": "Малък",
"System": "Система",
"Ui": "Потребителски интерфейс",
"ConnectionLostReconnect": "Radarr ще се опита да се свърже автоматично или можете да щракнете върху презареждане по-долу.",
"TotalSpace": "Общо пространство",
"Events": "Събития",
"Large": "Голям",
"LastDuration": "lastDuration",
"LastExecution": "Последно изпълнение",
"LastWriteTime": "Време за последно писане",
"Location": "Местоположение",
"NoResultsFound": "Няма намерени резултати",
"SomeResultsAreHiddenByTheAppliedFilter": "Някои резултати са скрити от прилагания филтър",
"RecentChanges": "Последни промени",
"WhatsNew": "Какво ново?",
"FreeSpace": "Свободно пространство",
"Medium": "Среден",
"Activity": "Дейност",
"AddNew": "Добави нов",
"NextExecution": "Следващо изпълнение",
"AllResultsAreHiddenByTheAppliedFilter": "Всички резултати са скрити от приложения филтър",
"Backup": "Архивиране"
}

View File

@@ -1,4 +1,5 @@
{
"About": "সম্পর্কিত",
"Actions": "ক্রিয়াকাণ্ড"
"Actions": "ক্রিয়াকাণ্ড",
"Activity": "কার্যকলাপ"
}

View File

@@ -605,5 +605,31 @@
"ApplyTagsHelpTextReplace": "Nahradit: Nahradit tagy zadanými tagy (pro vymazání všech tagů zadejte žádné tagy)",
"DeleteSelectedDownloadClients": "Odstranit staženého klienta",
"DeleteSelectedIndexersMessageText": "Opravdu chcete odstranit indexer „{0}“?",
"Yes": "Ano"
"Yes": "Ano",
"NotificationStatusAllClientHealthCheckMessage": "Všechny seznamy nejsou k dispozici z důvodu selhání",
"Small": "Malý",
"LastExecution": "Poslední poprava",
"LastWriteTime": "Čas posledního zápisu",
"Location": "Umístění",
"NotificationStatusSingleClientHealthCheckMessage": "Seznamy nejsou k dispozici z důvodu selhání: {0}",
"RecentChanges": "Nedávné změny",
"Backup": "Záloha",
"Medium": "Střední",
"NoResultsFound": "Nebyly nalezeny žádné výsledky",
"SomeResultsAreHiddenByTheAppliedFilter": "Některé výsledky jsou použitým filtrem skryty",
"AllResultsAreHiddenByTheAppliedFilter": "Všechny výsledky jsou skryty použitým filtrem",
"Events": "Události",
"FreeSpace": "Volný prostor",
"System": "Systém",
"TotalSpace": "Celkový prostor",
"ConnectionLost": "Spojení ztraceno",
"ConnectionLostReconnect": "Radarr se pokusí připojit automaticky, nebo můžete kliknout na znovu načíst níže.",
"ConnectionLostToBackend": "Radarr ztratil spojení s back-endem a pro obnovení funkčnosti bude nutné jej znovu načíst.",
"Large": "Velký",
"LastDuration": "lastDuration",
"Ui": "UI",
"WhatsNew": "Co je nového?",
"Activity": "Aktivita",
"AddNew": "Přidat nový",
"NextExecution": "Další spuštění"
}

View File

@@ -608,5 +608,31 @@
"DeleteSelectedIndexers": "Slet Indexer",
"ExistingTag": "Eksisterende mærke",
"SetTags": "Indstil tags",
"Yes": "Ja"
"Yes": "Ja",
"NotificationStatusAllClientHealthCheckMessage": "Alle lister er utilgængelige på grund af fejl",
"NotificationStatusSingleClientHealthCheckMessage": "Lister utilgængelige på grund af fejl: {0}",
"Small": "Lille",
"TotalSpace": "Samlet plads",
"Ui": "UI",
"Events": "Begivenheder",
"LastDuration": "Seneste varighed",
"LastExecution": "Seneste udførelse",
"LastWriteTime": "Sidste Skrive Tid",
"Medium": "Medium",
"System": "System",
"NextExecution": "Næste udførelse",
"NoResultsFound": "Ingen resultater fundet",
"ConnectionLost": "Forbindelse Mistet",
"ConnectionLostReconnect": "Radarr vil prøve at tilslutte automatisk, eller du kan klikke genindlæs forneden.",
"Location": "Beliggenhed",
"RecentChanges": "Seneste ændringer",
"SomeResultsAreHiddenByTheAppliedFilter": "Nogle resultater skjules af det anvendte filter",
"WhatsNew": "Hvad er nyt?",
"FreeSpace": "Frit Plads",
"Backup": "Backup",
"Activity": "Aktivitet",
"AddNew": "Tilføj Ny",
"Large": "Stor",
"Library": "Bibliotek",
"AllResultsAreHiddenByTheAppliedFilter": "Alle resultater skjules af det anvendte filter"
}

View File

@@ -968,5 +968,32 @@
"NoChange": "Keine Änderung",
"NoDownloadClientsFound": "Keine Download Clienten gefunden",
"SetTags": "Tags setzen",
"Loading": "Lade"
"Loading": "Lade",
"ConnectionLostReconnect": "Radarr wird automatisch versuchen zu verbinden oder klicke unten auf neuladen.",
"ConnectionLostToBackend": "Radarr hat die Verbindung zum Backend verloren und muss neugeladen werden.",
"NotificationStatusAllClientHealthCheckMessage": "Wegen Fehlern sind keine Applikationen verfügbar",
"NotificationStatusSingleClientHealthCheckMessage": "Applikationen wegen folgender Fehler nicht verfügbar: {0}",
"TotalSpace": "Speicherkapazität",
"Ui": "Oberfläche",
"FreeSpace": "Freier Speicher",
"Large": "Groß",
"LastExecution": "Letzte Ausführung",
"LastWriteTime": "Zuletzt beschrieben",
"Library": "Bibliothek",
"Location": "Speicherort",
"Small": "Klein",
"ConnectionLost": "Verbindung unterbrochen",
"Events": "Events",
"LastDuration": "Letzte Dauer",
"RecentChanges": "Neuste Änderungen",
"System": "System",
"WhatsNew": "Was gibt's Neues?",
"NextExecution": "Nächste Ausführung",
"NoResultsFound": "Keine Ergebnisse gefunden",
"SomeResultsAreHiddenByTheAppliedFilter": "Einige Ergebnisse werden wegen der aktiven Filter nicht angezeigt",
"Medium": "Medium",
"Activity": "Aktivität",
"AddNew": "Hinzufügen",
"Backup": "Backups",
"AllResultsAreHiddenByTheAppliedFilter": "Keine Ergebnisse mit den ausgewählten Filtern"
}

View File

@@ -965,5 +965,33 @@
"RemoveFailed": "Η αφαίρεση απέτυχε",
"RemoveFailedDownloads": "Αφαίρεση Αποτυχημένων Λήψεων",
"SetTags": "Ορισμός ετικετών",
"Yes": "Ναί"
"Yes": "Ναί",
"ConnectionLostToBackend": "Το Radarr έχασε τη σύνδεσή του με το backend και θα χρειαστεί να επαναφορτωθεί για να αποκαταστήσει τη λειτουργικότητά του.",
"AllResultsAreHiddenByTheAppliedFilter": "Όλα τα αποτελέσματα αποκρύπτονται από το εφαρμοσμένο φίλτρο",
"NotificationStatusAllClientHealthCheckMessage": "Όλες οι λίστες δεν είναι διαθέσιμες λόγω αστοχιών",
"NotificationStatusSingleClientHealthCheckMessage": "Μη διαθέσιμες λίστες λόγω αποτυχιών: {0}",
"TotalSpace": "Συνολικός χώρος",
"Ui": "Διεπαφή χρήστη",
"NoResultsFound": "Δεν βρέθηκαν αποτελέσματα",
"Events": "Γεγονότα",
"Large": "Μεγάλο",
"LastDuration": "Τελευταία Διάρκεια",
"LastExecution": "Τελευταία εκτέλεση",
"LastWriteTime": "Τελευταία ώρα εγγραφής",
"Library": "Βιβλιοθήκη",
"Location": "Τοποθεσία",
"System": "Σύστημα",
"FreeSpace": "Ελεύθερος Χώρος",
"SkipRedownloadHelpText": "Αποτρέπει το Lidarr από το να δοκιμάσει τη λήψη εναλλακτικών εκδόσεων για τα αφαιρεμένα στοιχεία",
"Medium": "Μεσαίο",
"ConnectionLost": "Η σύνδεση χάθηκε",
"ConnectionLostReconnect": "Το Radarr θα προσπαθήσει να συνδεθεί αυτόματα, αλλιώς μπορείτε να κάνετε reload απο κάτω.",
"RecentChanges": "Πρόσφατες αλλαγές",
"SomeResultsAreHiddenByTheAppliedFilter": "Ορισμένα αποτελέσματα αποκρύπτονται από το εφαρμοσμένο φίλτρο",
"WhatsNew": "Τι νέα?",
"Activity": "Δραστηριότητα",
"AddNew": "Προσθήκη Νέων",
"Backup": "Αντίγραφο Ασφαλείας",
"NextExecution": "Επόμενη εκτέλεση",
"Small": "Μικρό"
}

View File

@@ -21,6 +21,7 @@
"AllBooks": "All Books",
"AllExpandedCollapseAll": "Collapse All",
"AllExpandedExpandAll": "Expand All",
"AllResultsAreHiddenByTheAppliedFilter": "All results are hidden by the applied filter",
"AllowAuthorChangeClickToChangeAuthor": "Click to change author",
"AllowFingerprinting": "Allow Fingerprinting",
"AllowFingerprintingHelpText": "Use fingerprinting to improve accuracy of book matching",
@@ -36,6 +37,8 @@
"ApiKeyValidationHealthCheckMessage": "Please update your API key to be at least {0} characters long. You can do this via settings or the config file",
"AppDataDirectory": "AppData directory",
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
"AppUpdated": "{appName} Updated",
"AppUpdatedVersion": "{appName} has been updated to version `{version}`, in order to get the latest changes you'll need to reload {appName}",
"ApplicationURL": "Application URL",
"ApplicationUrlHelpText": "This application's external URL including http(s)://, port and URL base",
"ApplyChanges": "Apply Changes",
@@ -62,6 +65,7 @@
"AutoUnmonitorPreviouslyDownloadedBooksHelpText": "Books deleted from disk are automatically unmonitored in Readarr",
"Automatic": "Automatic",
"AutomaticAdd": "Automatic Add",
"AutomaticUpdatesDisabledDocker": "Automatic updates are not directly supported when using the Docker update mechanism. You will need to update the container image outside of {appName} or use a script",
"AutomaticallySwitchEdition": "Automatically Switch Edition",
"Backup": "Backup",
"BackupFolderHelpText": "Relative paths will be under Readarr's AppData directory",
@@ -148,6 +152,9 @@
"Connect": "Connect",
"ConnectSettings": "Connect Settings",
"ConnectSettingsSummary": "Notifications, connections to media servers/players and custom scripts",
"ConnectionLost": "Connection Lost",
"ConnectionLostReconnect": "{appName} will try to connect automatically, or you can click reload below.",
"ConnectionLostToBackend": "{appName} has lost its connection to the backend and will need to be reloaded to restore functionality.",
"Connections": "Connections",
"ConsoleLogLevel": "Console Log Level",
"Continuing": "Continuing",
@@ -159,9 +166,10 @@
"CopyUsingHardlinksHelpText": "Hardlinks allow Readarr to import seeding torrents to the the series folder without taking extra disk space or copying the entire contents of the file. Hardlinks will only work if the source and destination are on the same volume",
"CopyUsingHardlinksHelpTextWarning": "Occasionally, file locks may prevent renaming files that are being seeded. You may temporarily disable seeding and use Readarr's rename function as a work around.",
"CouldntFindAnyResultsForTerm": "Couldn't find any results for '{0}'",
"CountDownloadClientsSelected": "{0} download client(s) selected",
"CountImportListsSelected": "{0} import list(s) selected",
"CountIndexersSelected": "{0} indexer(s) selected",
"CountAuthorsSelected": "{selectedCount} author(s) selected",
"CountDownloadClientsSelected": "{selectedCount} download client(s) selected",
"CountImportListsSelected": "{selectedCount} import list(s) selected",
"CountIndexersSelected": "{selectedCount} indexer(s) selected",
"Country": "Country",
"CreateEmptyAuthorFolders": "Create empty author folders",
"CreateEmptyAuthorFoldersHelpText": "Create missing author folders during disk scan",
@@ -200,48 +208,48 @@
"DelayingDownloadUntilInterp": "Delaying download until {0} at {1}",
"Delete": "Delete",
"DeleteBackup": "Delete Backup",
"DeleteBackupMessageText": "Are you sure you want to delete the backup '{0}'?",
"DeleteBackupMessageText": "Are you sure you want to delete the backup '{name}'?",
"DeleteBookFile": "Delete Book File",
"DeleteBookFileMessageText": "Are you sure you want to delete {0}?",
"DeleteCondition": "Delete Condition",
"DeleteConditionMessageText": "Are you sure you want to delete condition '{0}'?",
"DeleteConditionMessageText": "Are you sure you want to delete condition '{name}'?",
"DeleteCustomFormat": "Delete Custom Format",
"DeleteCustomFormatMessageText": "Are you sure you want to delete the custom format '{0}'?",
"DeleteCustomFormatMessageText": "Are you sure you want to delete the custom format '{name}'?",
"DeleteDelayProfile": "Delete Delay Profile",
"DeleteDelayProfileMessageText": "Are you sure you want to delete this delay profile?",
"DeleteDownloadClient": "Delete Download Client",
"DeleteDownloadClientMessageText": "Are you sure you want to delete the download client '{0}'?",
"DeleteDownloadClientMessageText": "Are you sure you want to delete the download client '{name}'?",
"DeleteEmptyFolders": "Delete empty folders",
"DeleteEmptyFoldersHelpText": "Delete empty author folders during disk scan and when book files are deleted",
"DeleteFilesHelpText": "Delete the book files and author folder",
"DeleteFormat": "Delete Format",
"DeleteFormatMessageText": "Are you sure you want to delete format tag {0} ?",
"DeleteFormatMessageText": "Are you sure you want to delete format tag '{0}'?",
"DeleteImportList": "Delete Import List",
"DeleteImportListExclusion": "Delete Import List Exclusion",
"DeleteImportListExclusionMessageText": "Are you sure you want to delete this import list exclusion?",
"DeleteImportListMessageText": "Are you sure you want to delete the list '{0}'?",
"DeleteImportListMessageText": "Are you sure you want to delete the list '{name}'?",
"DeleteIndexer": "Delete Indexer",
"DeleteIndexerMessageText": "Are you sure you want to delete the indexer '{0}'?",
"DeleteIndexerMessageText": "Are you sure you want to delete the indexer '{name}'?",
"DeleteMetadataProfile": "Delete Metadata Profile",
"DeleteMetadataProfileMessageText": "Are you sure you want to delete the metadata profile '{0}'?",
"DeleteMetadataProfileMessageText": "Are you sure you want to delete the metadata profile '{name}'?",
"DeleteNotification": "Delete Notification",
"DeleteNotificationMessageText": "Are you sure you want to delete the notification '{0}'?",
"DeleteNotificationMessageText": "Are you sure you want to delete the notification '{name}'?",
"DeleteQualityProfile": "Delete Quality Profile",
"DeleteQualityProfileMessageText": "Are you sure you want to delete the quality profile '{0}'?",
"DeleteQualityProfileMessageText": "Are you sure you want to delete the quality profile '{name}'?",
"DeleteReleaseProfile": "Delete Release Profile",
"DeleteReleaseProfileMessageText": "Are you sure you want to delete this Release Profile?",
"DeleteRemotePathMapping": "Delete Remote Path Mapping",
"DeleteRemotePathMappingMessageText": "Are you sure you want to delete this remote path mapping?",
"DeleteRootFolder": "Delete Root Folder",
"DeleteRootFolderMessageText": "Are you sure you want to delete the root folder '{0}'?",
"DeleteRootFolderMessageText": "Are you sure you want to delete the root folder '{name}'?",
"DeleteSelectedBookFiles": "Delete Selected Book Files",
"DeleteSelectedBookFilesMessageText": "Are you sure you want to delete the selected book files?",
"DeleteSelectedDownloadClients": "Delete Download Client(s)",
"DeleteSelectedDownloadClientsMessageText": "Are you sure you want to delete {0} selected download client(s)?",
"DeleteSelectedDownloadClientsMessageText": "Are you sure you want to delete {count} selected download client(s)?",
"DeleteSelectedImportLists": "Delete Import List(s)",
"DeleteSelectedImportListsMessageText": "Are you sure you want to delete {0} selected import list(s)?",
"DeleteSelectedImportListsMessageText": "Are you sure you want to delete {count} selected import list(s)?",
"DeleteSelectedIndexers": "Delete Indexer(s)",
"DeleteSelectedIndexersMessageText": "Are you sure you want to delete {0} selected indexer(s)?",
"DeleteSelectedIndexersMessageText": "Are you sure you want to delete {count} selected indexer(s)?",
"DeleteTag": "Delete Tag",
"DeleteTagMessageText": "Are you sure you want to delete the tag '{0}'?",
"DestinationPath": "Destination Path",
@@ -571,11 +579,14 @@
"NoMinimumForAnyRuntime": "No minimum for any runtime",
"NoMissingItems": "No missing items",
"NoName": "Do not show name",
"NoResultsFound": "No results found",
"NoTagsHaveBeenAddedYet": "No tags have been added yet. Add tags to link authors with delay profiles, restrictions, or notifications. Click {0} to find out more about tags in Readarr.",
"NoUpdatesAreAvailable": "No updates are available",
"None": "None",
"NotAvailable": "Not Available",
"NotMonitored": "Not Monitored",
"NotificationStatusAllClientHealthCheckMessage": "All notifications are unavailable due to failures",
"NotificationStatusSingleClientHealthCheckMessage": "Notifications unavailable due to failures: {0}",
"NotificationTriggers": "Notification Triggers",
"OnApplicationUpdate": "On Application Update",
"OnApplicationUpdateHelpText": "On Application Update",
@@ -664,6 +675,7 @@
"ReadarrTags": "Readarr Tags",
"Real": "Real",
"Reason": "Reason",
"RecentChanges": "Recent Changes",
"RecycleBinCleanupDaysHelpText": "Set to 0 to disable automatic cleanup",
"RecycleBinCleanupDaysHelpTextWarning": "Files in the recycle bin older than the selected number of days will be cleaned up automatically",
"RecycleBinHelpText": "Book files will go here when deleted instead of being permanently deleted",
@@ -702,6 +714,7 @@
"RemotePathMappingCheckRemoteDownloadClient": "Remote download client {0} reported files in {1} but this directory does not appear to exist. Likely missing remote path mapping.",
"RemotePathMappingCheckWrongOSPath": "Remote download client {0} places downloads in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.",
"RemotePathMappings": "Remote Path Mappings",
"RemotePathMappingsInfo": "Remote Path Mappings are very rarely required, if {app} and your download client are on the same system it is better to match your paths. For more information see the [wiki]({wikiLink}).",
"Remove": "Remove",
"RemoveCompleted": "Remove Completed",
"RemoveCompletedDownloads": "Remove Completed Downloads",
@@ -844,6 +857,7 @@
"SkipSecondarySeriesBooks": "Skip secondary series books",
"Small": "Small",
"SmartReplace": "Smart Replace",
"SomeResultsAreHiddenByTheAppliedFilter": "Some results are hidden by the applied filter",
"SorryThatAuthorCannotBeFound": "Sorry, that author cannot be found.",
"SorryThatBookCannotBeFound": "Sorry, that book cannot be found.",
"Source": "Source",
@@ -989,6 +1003,8 @@
"WatchLibraryForChangesHelpText": "Rescan automatically when files change in a root folder",
"WatchRootFoldersForFileChanges": "Watch Root Folders for file changes",
"WeekColumnHeader": "Week Column Header",
"WhatsNew": "What's New?",
"WouldYouLikeToRestoreBackup": "Would you like to restore the backup '{name}'?",
"WriteAudioTags": "Tag Audio Files with Metadata",
"WriteAudioTagsScrub": "Scrub Existing Tags",
"WriteAudioTagsScrubHelp": "Remove existing tags from files, leaving only those added by Readarr.",

View File

@@ -1,6 +1,6 @@
{
"ApiKeyHelpTextWarning": "Requiere reiniciar para que surta efecto",
"DeleteRootFolderMessageText": "Seguro que quieres eliminar el indexer '{0}'?",
"DeleteRootFolderMessageText": "¿Está seguro de querer eliminar la carpeta raíz '{0}'?",
"LoadingBooksFailed": "La carga de los archivos ha fallado",
"ProxyUsernameHelpText": "Tienes que introducir tu nombre de usuario y contraseña sólo si son requeridos. Si no, déjalos vacios.",
"SslPortHelpTextWarning": "Requiere reiniciar para que surta efecto",
@@ -13,16 +13,16 @@
"Tags": "Etiquetas",
"60MinutesSixty": "60 Minutos: {0}",
"APIKey": "Clave API",
"About": "Acerca",
"About": "Acerca de",
"AddListExclusion": "Añadir Exclusión De Lista",
"AddingTag": "Añadiendo etiqueta",
"AgeWhenGrabbed": "Edad (cuando capturada)",
"AlreadyInYourLibrary": "Ya esta en tu librería",
"AddingTag": "Añadir etiqueta",
"AgeWhenGrabbed": "Antigüedad (cuando se añadió)",
"AlreadyInYourLibrary": "Ya en tu biblioteca",
"AlternateTitles": "Título alternativo",
"Analytics": "Analíticas",
"AnalyticsEnabledHelpText": "Envíe información anónima de uso y error a los servidores de Radarr. Esto incluye información sobre su navegador, qué páginas de Radarr WebUI utiliza, informes de errores, así como el sistema operativo y la versión en tiempo de ejecución. Usaremos esta información para priorizar funciones y correcciones de errores.",
"AnalyticsEnabledHelpTextWarning": "Requiere reiniciar para que surta efecto",
"AppDataDirectory": "Directorio de AppData",
"AppDataDirectory": "Directorio AppData",
"ApplyTags": "Aplicar Etiquetas",
"45MinutesFourtyFive": "45 Minutos: {0}",
"Authentication": "Autenticación",
@@ -32,11 +32,11 @@
"AutoUnmonitorPreviouslyDownloadedBooksHelpText": "Las películas eliminadas del disco son automáticamente desmonitorizadas en Radarr",
"Automatic": "Automático",
"BackupFolderHelpText": "Las rutas relativas estarán en el directorio AppData de Radarr",
"BackupNow": "Backup Ahora",
"BackupRetentionHelpText": "Backups automáticos anteriores al período de retención serán borrados automáticamente",
"BackupNow": "Hacer copia de seguridad ahora",
"BackupRetentionHelpText": "Las copias de seguridad automáticas anteriores al período de retención serán borradas automáticamente",
"Backups": "Copias de seguridad",
"BindAddress": "Dirección de Ligado",
"BindAddressHelpText": "Dirección IP4 válida o '*' para todas las interfaces",
"BindAddressHelpText": "Dirección IP4 válida, localhost o '*' para todas las interfaces",
"BindAddressHelpTextWarning": "Requiere reiniciar para que surta efecto",
"BookIsDownloadingInterp": "La película está descargando - {0}% {1}",
"Branch": "Rama",
@@ -216,7 +216,7 @@
"MustNotContain": "No Debe Contener",
"Name": "Nombre",
"NamingSettings": "Ajustes de Renombrado",
"New": "Nueva",
"New": "Nuevo",
"NoBackupsAreAvailable": "No hay copias de seguridad disponibles",
"NoHistory": "Sin historia",
"NoLeaveIt": "No, Déjalo",
@@ -300,7 +300,7 @@
"RequiredPlaceHolder": "Añadir nueva restricción",
"RescanAfterRefreshHelpTextWarning": "Radarr no detectará los cambios automáticamente en los ficheros si no se ajusta a 'Siempre'",
"RescanAuthorFolderAfterRefresh": "Reescanear la Carpeta de Películas después de Actualizar",
"Reset": "Reajustar",
"Reset": "Reiniciar",
"ResetAPIKey": "Reajustar API",
"ResetAPIKeyMessageText": "¿Está seguro de que desea restablecer su clave API?",
"Restart": "Reiniciar",
@@ -348,7 +348,7 @@
"SkipFreeSpaceCheckWhenImportingHelpText": "Usar cuando Radarr no pueda detectar el espacio disponible en la carpeta de películas",
"SorryThatAuthorCannotBeFound": "Lo siento, no he encontrado esa película.",
"SorryThatBookCannotBeFound": "Lo siento, no he encontrado esa película.",
"Source": "Origen",
"Source": "Fuente",
"SourcePath": "Ruta de Origen",
"SslCertPasswordHelpText": "Contraseña para el archivo pfx",
"SslCertPasswordHelpTextWarning": "Requiere reiniciar para que surta efecto",
@@ -416,7 +416,7 @@
"UnableToLoadTheCalendar": "No se ha podido cargar el calendario",
"UnableToLoadUISettings": "No se han podido cargar los ajustes de UI",
"Ungroup": "Desagrupar",
"Unmonitored": "No monitoreadas",
"Unmonitored": "Sin monitorizar",
"UnmonitoredHelpText": "Incluir las peliculas no monitoreadas en el feed de iCal",
"UpdateAll": "Actualizar Todo",
"UpdateAutomaticallyHelpText": "Descargar e instalar actualizaciones automáticamente. Se podrán instalar desde Sistema: Actualizaciones también",
@@ -437,7 +437,7 @@
"Version": "Versión",
"WeekColumnHeader": "Encabezado de la columna semanal",
"Year": "Año",
"YesCancel": "Si, cancelar",
"YesCancel": "Sí, Cancelar",
"20MinutesTwenty": "20 Minutos: {0}",
"DownloadClientCheckDownloadingToRoot": "El cliente de descargas {0} coloca las descargas en la carpeta raíz {1}. No debe descargar a una carpeta raíz.",
"MaintenanceRelease": "Lanzamiento de mantenimiento",
@@ -457,7 +457,7 @@
"TheAuthorFolderAndAllOfItsContentWillBeDeleted": "Se eliminará la carpeta de películas '{0}' y todo su contenido.",
"Component": "Componente",
"RemoveFromBlocklist": "Eliminar de lista de bloqueados",
"Time": "Fecha",
"Time": "Tiempo",
"UnableToLoadBlocklist": "No se han podido cargar las bloqueadas",
"Level": "Nivel",
"ReleaseBranchCheckOfficialBranchMessage": "Las versión {0} no es una versión válida de Radarr, no recibirás actualizaciones",
@@ -467,7 +467,7 @@
"SelectAll": "Seleccionar Todas",
"SelectedCountBooksSelectedInterp": "{0} Película(s) Seleccionada(s)",
"ThisCannotBeCancelled": "Esto no puede ser cancelado una vez iniciado sin deshabilitar todos sus indexadores.",
"All": "Todas",
"All": "Todo",
"RescanAfterRefreshHelpText": "Reescanear la carpeta de películas después de actualizar la película",
"ShowUnknownAuthorItems": "Mostrar Elementos Desconocidos",
"UnselectAll": "Deseleccionar Todo",
@@ -492,7 +492,7 @@
"Yesterday": "Ayer",
"UpdateAvailable": "La nueva actualización está disponible",
"Duration": "Duración",
"AppDataLocationHealthCheckMessage": "No será posible actualizar para prevenir que AppData se borre durante la actualización",
"AppDataLocationHealthCheckMessage": "No será posible actualizar para prevenir la eliminación de AppData al Actualizar",
"Lists": "Listas",
"SizeLimit": "Tamaño límite",
"IndexerJackettAll": "Indexadores que utilizan el Endpoint Jackett 'all' no están soportados: {0}",
@@ -550,7 +550,7 @@
"ProxyCheckBadRequestMessage": "Fallo al comprobar el proxy. StatusCode: {0}",
"ProxyCheckFailedToTestMessage": "Fallo al comprobar el proxy: {0}",
"QualitySettingsSummary": "Tamaños de calidad y nombrado",
"Queued": "En Cola",
"Queued": "Encolado",
"QueueIsEmpty": "La cola está vacía",
"RefreshAndScan": "Actualizar y Escanear",
"RestartReloadNote": "Nota: Radarr se reiniciará y recargará automáticamente la IU durante el proceso de restauración.",
@@ -582,7 +582,7 @@
"FileWasDeletedByUpgrade": "Se eliminó el archivo para importar una actualización",
"IndexersSettingsSummary": "Indexers y restricciones de lanzamientos",
"RestartRequiredHelpTextWarning": "Requiere reiniciar para que surta efecto",
"AddList": "Añadir lista",
"AddList": "Añadir Lista",
"RenameFiles": "Renombrar Archivos",
"Test": "Test",
"InstanceName": "Nombre de Instancia",
@@ -650,19 +650,19 @@
"NoEventsFound": "No se encontraron eventos",
"ApplyTagsHelpTextHowToApplyAuthors": "Cómo añadir etiquetas a las películas seleccionadas",
"DeleteSelectedIndexersMessageText": "Seguro que quieres eliminar el indexer '{0}'?",
"Yes": "si",
"Yes": "",
"RedownloadFailed": "La descarga ha fallado",
"RemoveCompleted": "Eliminación completada",
"RemoveDownloadsAlert": "Los ajustes de eliminación se han trasladado a los ajustes individuales del cliente de descarga en la tabla anterior.",
"RemoveFailed": "La eliminación falló",
"ApplyTagsHelpTextAdd": "Añadir: Añadir a las etiquetas la lista existente de etiquetas",
"ApplyTagsHelpTextHowToApplyDownloadClients": "Cómo añadir etiquetas a las películas seleccionadas",
"ApplyTagsHelpTextHowToApplyImportLists": "Cómo añadir etiquetas a las películas seleccionadas",
"ApplyTagsHelpTextHowToApplyIndexers": "Cómo añadir etiquetas a las películas seleccionadas",
"ApplyTagsHelpTextHowToApplyDownloadClients": "Cómo añadir etiquetas a los clientes de descargas seleccionados",
"ApplyTagsHelpTextHowToApplyImportLists": "Cómo añadir etiquetas a las listas de importación seleccionadas",
"ApplyTagsHelpTextHowToApplyIndexers": "Cómo añadir etiquetas a los indexadores seleccionados",
"ApplyTagsHelpTextRemove": "Eliminar: Eliminar las etiquetas introducidas",
"ApplyTagsHelpTextReplace": "Reemplazar: Reemplazar las etiquetas con las etiquetas introducidas (no introducir etiquetas para eliminar todas las etiquetas)",
"DeleteSelectedDownloadClients": "Borrar Gestor de Descargas",
"DeleteSelectedDownloadClientsMessageText": "Seguro que quieres eliminar el indexer '{0}'?",
"DeleteSelectedDownloadClientsMessageText": "¿Está seguro de querer eliminar {0} cliente(s) de descarga seleccionado(s)?",
"DeleteSelectedImportListsMessageText": "Seguro que quieres eliminar el indexer '{0}'?",
"DeleteSelectedIndexers": "Borrar Indexer",
"DownloadClientTagHelpText": "Solo utilizar este indexador para películas que coincidan con al menos una etiqueta. Déjelo en blanco para utilizarlo con todas las películas.",
@@ -670,5 +670,63 @@
"No": "No",
"NoChange": "Sin Cambio",
"RemovingTag": "Eliminando etiqueta",
"SetTags": "Poner Etiquetas"
"SetTags": "Poner Etiquetas",
"DeleteRemotePathMappingMessageText": "¿Está seguro de querer eliminar esta asignación de ruta remota?",
"ApplicationURL": "URL de la aplicación",
"ApplicationUrlHelpText": "La URL externa de la aplicación incluyendo http(s)://, puerto y URL base",
"AutomaticAdd": "Añadir Automáticamente",
"AutoAdd": "Añadir Automáticamente",
"EditSelectedIndexers": "Editar Indexadores Seleccionados",
"DeleteRootFolder": "Eliminar Carpeta Raíz",
"Clone": "Clonar",
"CloneCondition": "Clonar Condición",
"ApplyChanges": "Aplicar Cambios",
"CountDownloadClientsSelected": "{0} cliente(s) de descarga seleccionado(s)",
"CountImportListsSelected": "{0} lista(s) de importación seleccionada(s)",
"CountIndexersSelected": "{0} indexador(es) seleccionado(s)",
"DeleteCondition": "Eliminar Condición",
"DeleteSelectedImportLists": "Eliminar Lista(s) de Importación",
"EditSelectedDownloadClients": "Editar Clientes de Descarga Seleccionados",
"EditSelectedImportLists": "Editar Listas de Importación Seleccionadas",
"Implementation": "Implementación",
"ApiKeyValidationHealthCheckMessage": "Actualice su clave de API para que tenga al menos {0} carácteres. Puede hacerlo en los ajustes o en el archivo de configuración",
"ListRefreshInterval": "Intervalo de Refresco de Lista",
"ListWillRefreshEveryInterp": "La lista será refrescada cada {0}",
"Activity": "Actividad",
"Location": "Ubicación",
"Ui": "UI",
"AddNew": "Añadir Nuevo",
"Backup": "Copia de seguridad",
"ManageClients": "Gestionar Clientes",
"ManageDownloadClients": "Gestionar Clientes de Descarga",
"ManageIndexers": "Gestionar Indexadores",
"ManageLists": "Gestionar Listas",
"System": "Sistema",
"TotalSpace": "Espacio Total",
"IndexerDownloadClientHealthCheckMessage": "Indexadores con clientes de descarga inválidos: {0}.",
"ManageImportLists": "Gestionar Listas de Importación",
"ConnectionLostToBackend": "Radarr ha perdido su conexión con el backend y tendrá que ser recargado para recuperar su funcionalidad.",
"NotificationStatusSingleClientHealthCheckMessage": "Listas no disponibles debido a errores: {0}",
"NotificationStatusAllClientHealthCheckMessage": "Las listas no están disponibles debido a errores",
"ReleaseProfiles": "perfil de lanzamiento",
"Small": "Pequeña",
"DeleteImportList": "Eliminar Lista(s) de Importación",
"Large": "Grande",
"Library": "Biblioteca",
"SomeResultsAreHiddenByTheAppliedFilter": "Algunos resultados están ocultos por el filtro aplicado",
"FreeSpace": "Espacio Libre",
"LastDuration": "Duración",
"LastExecution": "Última ejecución",
"LastWriteTime": "Última Fecha de Escritura",
"ConnectionLost": "Conexión perdida",
"ConnectionLostReconnect": "Radarr intentará conectarse automáticamente, o haz clic en el botón de recarga abajo.",
"NextExecution": "Siguiente ejecución",
"NoResultsFound": "No se han encontrado resultados",
"RecentChanges": "Cambios recientes",
"WhatsNew": "¿Qué hay de nuevo?",
"Loading": "Cargando",
"Events": "Eventos",
"Medium": "Medio",
"AllResultsAreHiddenByTheAppliedFilter": "Todos los resultados están ocultos por el filtro aplicado",
"CatalogNumber": "número de catálogo"
}

View File

@@ -852,5 +852,32 @@
"RemovingTag": "Tunniste poistetaan",
"RemoveCompleted": "Poista valmistuneet",
"RemoveDownloadsAlert": "Poistoasetukset on siirretty yllä olevassa taulukossa yksittäisten lataustyökalujen alle.",
"SetTags": "Tunnisteiden määritys"
"SetTags": "Tunnisteiden määritys",
"ConnectionLostToBackend": "Radarr on menettänyt yhteyden taustajärjestelmään ja sivu on päivitettävä toiminnallisuuden palauttamiseksi.",
"NotificationStatusAllClientHealthCheckMessage": "Sovellukset eivät ole käytettävissä virheiden vuoksi",
"NotificationStatusSingleClientHealthCheckMessage": "Sovellukset eivät ole käytettävissä virheiden vuoksi: {0}",
"System": "Järjestelmä",
"TotalSpace": "Kokonaistila",
"Ui": "Käyttöliittymä",
"Library": "Kirjasto",
"Medium": "Keskitaso",
"Small": "Pieni",
"AllResultsAreHiddenByTheAppliedFilter": "Kaikki tulokset on piilotettu aktiivisen suodattimen johdosta.",
"Events": "Tapahtumat",
"FreeSpace": "Vapaa tila",
"ConnectionLost": "Yhteys on katkennut",
"ConnectionLostReconnect": "Radarr pyrkii muodostamaan yhteyden automaattisesti tai voit painaa alta \"Lataa uudelleen\".",
"RecentChanges": "Viimeaikaiset muutokset",
"WhatsNew": "Mikä on uutta?",
"Large": "Suuri",
"LastDuration": "Edellinen kesto",
"LastExecution": "Edellinen suoritus",
"LastWriteTime": "Viimeisin kirjoitusaika",
"Location": "Sijainti",
"NoResultsFound": "Ei tuloksia",
"SomeResultsAreHiddenByTheAppliedFilter": "Joitakin tuloksia piilottaa käytetty suodatin",
"Activity": "Tapahtumat",
"AddNew": "Lisää uusi",
"Backup": "Varmuuskopio",
"NextExecution": "Seuraava suoritus"
}

View File

@@ -763,5 +763,32 @@
"SetTags": "Définir Tags",
"CountDownloadClientsSelected": "{0} client(s) de téléchargement sélectionné(s)",
"EditSelectedDownloadClients": "Modifier les clients de téléchargement sélectionnés",
"EditSelectedIndexers": "Modifier les indexeurs sélectionnés"
"EditSelectedIndexers": "Modifier les indexeurs sélectionnés",
"ConnectionLostReconnect": "Radarr essaiera de se connecter automatiquement, ou vous pouvez cliquer sur \"Recharger\" en bas.",
"NotificationStatusAllClientHealthCheckMessage": "Toutes les applications sont indisponibles en raison de dysfonctionnements",
"NotificationStatusSingleClientHealthCheckMessage": "Applications indisponibles en raison de dysfonctionnements : {0}",
"SomeResultsAreHiddenByTheAppliedFilter": "Tous les résultats ont été dissimulés par le filtre actuellement appliqué",
"ConnectionLost": "Connexion perdue",
"ConnectionLostToBackend": "Radarr a perdu sa connexion au backend et devra être rechargé pour fonctionner à nouveau.",
"RecentChanges": "Changements récents",
"System": "Système",
"WhatsNew": "Quoi de neuf ?",
"AllResultsAreHiddenByTheAppliedFilter": "Tous les résultats ont été dissimulés par le filtre actuellement appliqué",
"Location": "Emplacement",
"NoResultsFound": "Aucun résultat trouvé",
"Events": "Événements",
"FreeSpace": "Espace libre",
"Large": "Grand",
"LastDuration": "Dernière durée",
"LastExecution": "Dernière exécution",
"Library": "Bibliothèque",
"LastWriteTime": "Heure de la dernière écriture",
"Medium": "Moyen",
"NextExecution": "Prochaine exécution",
"Small": "Petit",
"TotalSpace": "Espace total",
"Ui": "UI",
"Activity": "Activité",
"AddNew": "Ajouter un nouveau",
"Backup": "Sauvegarde"
}

View File

@@ -629,5 +629,32 @@
"ExistingTag": "תג קיים",
"RemovingTag": "הסרת התג",
"SetTags": "הגדר תגים",
"Yes": "כן"
"Yes": "כן",
"Events": "אירועים",
"RecentChanges": "שינויים אחרונים",
"WhatsNew": "מה חדש?",
"NotificationStatusAllClientHealthCheckMessage": "כל הרשימות אינן זמינות בגלל כשלים",
"NotificationStatusSingleClientHealthCheckMessage": "רשימות לא זמינות בגלל כשלים: {0}",
"NoResultsFound": "לא נמצאו תוצאות",
"Ui": "ממשק משתמש",
"CatalogNumber": "מספר קטלוג",
"ConnectionLost": "החיבור אבד",
"ConnectionLostReconnect": "Radarr ינסה להתחבר אוטומטית, או שתלחץ על טען מחדש למטה.",
"AllResultsAreHiddenByTheAppliedFilter": "כל התוצאות מוסתרות על ידי המסנן שהוחל",
"FreeSpace": "מקום פנוי",
"LastExecution": "ביצוע אחרון",
"LastWriteTime": "זמן כתיבה אחרון",
"NextExecution": "הביצוע הבא",
"SomeResultsAreHiddenByTheAppliedFilter": "חלק מהתוצאות מוסתרות על ידי המסנן שהוחל",
"Activity": "פעילות",
"AddNew": "הוסף חדש",
"Backup": "גיבוי",
"Large": "גָדוֹל",
"LastDuration": "lastDuration",
"Location": "מקום",
"Medium": "בינוני",
"Small": "קָטָן",
"System": "מערכת",
"Theme": "ערכת נושא",
"TotalSpace": "השטח הכולל"
}

View File

@@ -948,5 +948,33 @@
"No": "Nem",
"NoChange": "Nincs változtatás",
"RemoveCompletedDownloads": "Befejezett letöltések eltávolítása",
"RemovingTag": "Címke eltávolítása"
"RemovingTag": "Címke eltávolítása",
"NextExecution": "Következő végrehajtás",
"NotificationStatusAllClientHealthCheckMessage": "Összes alkalmazás elérhetetlen hiba miatt",
"NotificationStatusSingleClientHealthCheckMessage": "Az alkalmazás nem áll rendelkezésre az alábbi hibák miatt: {0}",
"Small": "kicsi",
"SkipRedownloadHelpText": "Megakadályozza, hogy a Lidarr megpróbálja letölteni az eltávolított elemek alternatív kiadásait",
"Events": "Események",
"FreeSpace": "Szabad Tárhely",
"NoResultsFound": "Nem eredményezett találatot",
"SomeResultsAreHiddenByTheAppliedFilter": "Néhány találat nem látható az alkalmazott szűrők miatt",
"System": "Rendszer",
"TotalSpace": "Összes szabad hely",
"Ui": "Felület",
"ConnectionLost": "Kapcsolódás Elveszett",
"ConnectionLostReconnect": "A Radarr megpróbál automatikusan csatlakozni, vagy kattints a frissítés gombra.",
"ConnectionLostToBackend": "A Radarr elvesztette kapcsolatát a háttérrendszerrel, a funkciók helyreállításához frissíts.",
"RecentChanges": "Friss változtatások",
"WhatsNew": "Mi az újdonság?",
"Large": "Óriási",
"LastDuration": "Utolsó időtartam",
"LastExecution": "Utolsó végrehajtás",
"LastWriteTime": "Utolsó írási idő",
"Library": "Könyvtár",
"Location": "Lokáció",
"AllResultsAreHiddenByTheAppliedFilter": "Az alkalmazott szűrők miatt, az összes keresési eredmény rejtve marad",
"Activity": "Aktivitás",
"AddNew": "Új hozzáadása",
"Backup": "Biztonsági Mentés",
"Medium": "Közepes"
}

View File

@@ -6,7 +6,7 @@
"APIKey": "Chiave API",
"About": "Info",
"AddListExclusion": "Aggiungi Lista Esclusioni",
"AddingTag": "Aggiungi etichetta",
"AddingTag": "Aggiungendo etichetta",
"Fixed": "Fissato",
"Local": "Locale",
"Remove": "Rimuovi",
@@ -40,7 +40,7 @@
"BackupFolderHelpText": "I percorsi relativi saranno nella cartella AppData di Readarr",
"BackupNow": "Esegui backup ora",
"BackupRetentionHelpText": "I backup automatici più vecchi del periodo di conservazione verranno eliminati automaticamente",
"Backups": "I Backup",
"Backups": "Backups",
"BindAddress": "Indirizzo di Bind",
"BindAddressHelpText": "Indirizzo IP valido, localhost o '*' per tutte le interfacce di rete",
"BindAddressHelpTextWarning": "Richiede il riavvio per avere effetto",
@@ -717,5 +717,12 @@
"RedownloadFailed": "Download fallito",
"RemoveSelectedItems": "Rimuovi elementi selezionati",
"RemovingTag": "Sto eliminando il tag",
"SetTags": "Imposta Etichette"
"SetTags": "Imposta Etichette",
"Activity": "Attività",
"AutoAdd": "Aggiungi Automaticamente",
"ApplyChanges": "Applica Cambiamenti",
"ApiKeyValidationHealthCheckMessage": "Aggiorna la tua chiave API in modo che abbia una lunghezza di almeno {0} caratteri. Puoi farlo dalle impostazioni o dal file di configurazione",
"AddNew": "Aggiungi Nuovo",
"AutomaticAdd": "Aggiungi Automaticamente",
"Backup": "Backup"
}

View File

@@ -601,5 +601,30 @@
"RedownloadFailed": "ダウンロードに失敗しました",
"ApplyTagsHelpTextHowToApplyDownloadClients": "選択した映画にタグを適用する方法",
"ApplyTagsHelpTextAdd": "追加:既存のタグリストにタグを追加します",
"DeleteSelectedDownloadClients": "ダウンロードクライアントを削除する"
"DeleteSelectedDownloadClients": "ダウンロードクライアントを削除する",
"ConnectionLost": "接続切断",
"ConnectionLostReconnect": "Radarrは自動的に接続を試みます。または、下の[再読み込み]をクリックしてください。",
"NotificationStatusAllClientHealthCheckMessage": "障害のため、すべてのリストを利用できません",
"NotificationStatusSingleClientHealthCheckMessage": "失敗のため利用できないリスト:{0}",
"System": "システム",
"TotalSpace": "総スペース",
"Large": "大",
"LastDuration": "lastDuration",
"LastExecution": "最後の実行",
"LastWriteTime": "最終書き込み時間",
"Location": "ロケーション",
"NextExecution": "次の実行",
"Small": "小さい",
"SomeResultsAreHiddenByTheAppliedFilter": "一部の結果は、適用されたフィルターによって非表示になります",
"Ui": "UI",
"RecentChanges": "最近の変化",
"WhatsNew": "新着情報?",
"AllResultsAreHiddenByTheAppliedFilter": "すべての結果は、適用されたフィルターによって非表示になります",
"Backup": "バックアップ",
"Events": "イベント",
"FreeSpace": "フリースペース",
"Medium": "中",
"NoResultsFound": "結果が見つかりません",
"Activity": "アクティビティ",
"AddNew": "新しく追加する"
}

View File

@@ -596,5 +596,26 @@
"RemovingTag": "태그 제거",
"SetTags": "태그 설정",
"Yes": "예",
"RedownloadFailed": "다운로드 실패함"
"RedownloadFailed": "다운로드 실패함",
"LastWriteTime": "마지막 쓰기 시간",
"Location": "위치",
"ConnectionLostReconnect": "Radarr가 자동으로 연결을 시도하거나 아래에서 새로고침을 클릭할 수 있습니다.",
"ConnectionLostToBackend": "Radarr는 백엔드와의 연결이 끊어졌으며 기능을 복원하려면 다시 로딩해야 합니다.",
"NotificationStatusAllClientHealthCheckMessage": "실패로 인해 모든 목록을 사용할 수 없습니다.",
"NotificationStatusSingleClientHealthCheckMessage": "실패로 인해 사용할 수없는 목록 : {0}",
"TotalSpace": "총 공간",
"Ui": "UI",
"FreeSpace": "여유 공간",
"NextExecution": "다음 실행",
"SomeResultsAreHiddenByTheAppliedFilter": "적용된 필터에 의해 모든 결과가 숨겨집니다.",
"System": "체계",
"Medium": "매질",
"ConnectionLost": "연결이 끊어졌습니다.",
"AllResultsAreHiddenByTheAppliedFilter": "적용된 필터에 의해 모든 결과가 숨겨집니다.",
"Events": "이벤트",
"LastDuration": "lastDuration",
"LastExecution": "마지막 실행",
"Activity": "활동",
"AddNew": "새로 추가",
"Backup": "백업"
}

View File

@@ -650,5 +650,31 @@
"RemoveFailed": "Usuń nieudane",
"Yes": "tak",
"ApplyChanges": "Zastosuj zmiany",
"ApiKeyValidationHealthCheckMessage": "Zaktualizuj swój klucz API aby był długi na co najmniej {0} znaków. Możesz to zrobić poprzez ustawienia lub plik konfiguracyjny"
"ApiKeyValidationHealthCheckMessage": "Zaktualizuj swój klucz API aby był długi na co najmniej {0} znaków. Możesz to zrobić poprzez ustawienia lub plik konfiguracyjny",
"ConnectionLostToBackend": "Radarr utracił połączenie z silnikiem programu, aby przywrócić funkcjonalność musi zostać zrestartowany.",
"Large": "Duży",
"LastDuration": "Ostatni czas trwania",
"LastExecution": "Ostatnia egzekucja",
"LastWriteTime": "Czas ostatniego zapisu",
"Location": "Lokalizacja",
"NoResultsFound": "Nie znaleziono wyników",
"SomeResultsAreHiddenByTheAppliedFilter": "Niektóre wyniki są ukrywane przez zastosowany filtr",
"Events": "Wydarzenia",
"NotificationStatusAllClientHealthCheckMessage": "Wszystkie listy są niedostępne z powodu błędów",
"NotificationStatusSingleClientHealthCheckMessage": "Listy niedostępne z powodu błędów: {0}",
"System": "System",
"TotalSpace": "Powierzchnia całkowita",
"Ui": "UI",
"Medium": "Średni",
"Small": "Mały",
"ConnectionLost": "Utracono połączenie",
"ConnectionLostReconnect": "Radarr spróbuje połączyć się automatycznie lub możesz kliknąć przycisk przeładuj poniżej.",
"RecentChanges": "Ostatnie zmiany",
"WhatsNew": "Co nowego?",
"NextExecution": "Następne wykonanie",
"FreeSpace": "Wolna przestrzeń",
"Activity": "Aktywność",
"AddNew": "Dodaj nowy",
"Backup": "Kopia zapasowa",
"AllResultsAreHiddenByTheAppliedFilter": "Wszystkie wyniki są ukrywane przez zastosowany filtr"
}

View File

@@ -14,7 +14,7 @@
"45MinutesFourtyFive": "45 Minutos: {0}",
"60MinutesSixty": "60 Minutos: {0}",
"APIKey": "Chave API",
"AgeWhenGrabbed": "Tempo de vida (quando obtido)",
"AgeWhenGrabbed": "Idade (quando baixado)",
"ApiKeyHelpTextWarning": "Requer reinício para ter efeito",
"LoadingBooksFailed": "Falha ao carregar livros",
"Logs": "Registros",
@@ -37,14 +37,14 @@
"AutoUnmonitorPreviouslyDownloadedBooksHelpText": "Livros excluídos do disco deixam de ser monitorados no Readarr automaticamente",
"Automatic": "Automático",
"BackupFolderHelpText": "Os caminhos relativos estarão no diretório AppData do Readarr",
"BackupNow": "Fazer Backup Agora",
"BackupNow": "Fazer backup agora",
"BackupRetentionHelpText": "Backups automáticos anteriores ao período de retenção serão limpos automaticamente",
"Backups": "Backups",
"BindAddress": "Fixar Endereço",
"BindAddressHelpText": "Endereço IP válido, localhost ou '*' para todas as interfaces",
"BindAddressHelpTextWarning": "Requer reiniciar para ter efeito",
"BookIsDownloading": "O livro está baixando",
"DiskSpace": "Espaço em Disco",
"DiskSpace": "Espaço em disco",
"Docker": "Docker",
"DownloadClient": "Cliente de Download",
"DownloadClientSettings": "Configurações do Cliente de Download",
@@ -59,7 +59,7 @@
"Enable": "Habilitar",
"GrabRelease": "Capturar Versão",
"GrabReleaseMessageText": "O Readarr não conseguiu determinar a qual autor e livro esse lançamento está relacionado. O Readarr pode não conseguir importar automaticamente este lançamento. Quer obter \"{0}\"?",
"GrabSelected": "Obter selecionado",
"GrabSelected": "Obter Selecionado",
"Group": "Grupo",
"HasPendingChangesNoChanges": "Sem alterações",
"HasPendingChangesSaveChanges": "Salvar alterações",
@@ -100,7 +100,7 @@
"CreateEmptyAuthorFoldersHelpText": "Criar pastas de autor ausente durante a verificação do disco",
"CreateGroup": "Criar grupo",
"CutoffHelpText": "Assim que esta qualidade for alcançada, o Readarr não baixará mais livros",
"CutoffUnmet": "Corte não alcançado",
"CutoffUnmet": "Corte não atendido",
"DBMigration": "Migração de banco de dados",
"Dates": "Datas",
"DelayProfile": "Perfil de Atraso",
@@ -108,34 +108,34 @@
"DelayingDownloadUntilInterp": "Atrasando o download até {0} às {1}",
"Delete": "Excluir",
"DeleteBackup": "Excluir Backup",
"DeleteBackupMessageText": "Tem certeza que deseja excluir o backup \"{0}\"?",
"DeleteBackupMessageText": "Tem certeza de que deseja excluir o backup '{name}'?",
"DeleteDelayProfile": "Excluir Perfil de Atraso",
"DeleteDelayProfileMessageText": "Tem certeza de que deseja excluir este perfil de atraso?",
"DeleteDownloadClient": "Excluir Cliente de Download",
"DeleteDownloadClientMessageText": "Tem certeza que deseja excluir o cliente de download \"{0}\"?",
"DeleteDownloadClientMessageText": "Tem certeza de que deseja excluir o cliente de download '{name}'?",
"DeleteEmptyFolders": "Excluir pastas vazias",
"DeleteEmptyFoldersHelpText": "Excluir pastas de autor vazias durante a verificação do disco e quando os arquivos de livros forem excluídos",
"DeleteImportListExclusion": "Excluir Exclusão da Lista de Importação",
"DeleteImportListExclusionMessageText": "Tem certeza de que deseja excluir esta exclusão da lista de importação?",
"DeleteImportListMessageText": "Tem certeza de que deseja excluir a lista \"{0}\"?",
"DeleteImportListMessageText": "Tem certeza de que deseja excluir a lista '{name}'?",
"DeleteIndexer": "Excluir Indexador",
"DeleteIndexerMessageText": "Tem certeza de que deseja excluir o indexador \"{0}\"?",
"DeleteMetadataProfileMessageText": "Tem certeza que deseja excluir o perfil de metadados \"{0}\"?",
"DeleteIndexerMessageText": "Tem certeza de que deseja excluir o indexador '{name}'?",
"DeleteMetadataProfileMessageText": "Tem certeza de que deseja excluir o perfil de metadados '{name}'?",
"DeleteNotification": "Excluir Notificação",
"DeleteNotificationMessageText": "Tem certeza de que deseja excluir a notificação \"{0}\"?",
"DeleteNotificationMessageText": "Tem certeza de que deseja excluir o perfil de metadados '{name}'?",
"DeleteQualityProfile": "Excluir Perfil de Qualidade",
"DeleteQualityProfileMessageText": "Tem certeza que deseja excluir o perfil de qualidade \"{0}\"?",
"DeleteQualityProfileMessageText": "Tem certeza de que deseja excluir o perfil de qualidade '{name}'?",
"DeleteReleaseProfile": "Excluir Perfil de Lançamento",
"DeleteReleaseProfileMessageText": "Tem certeza de que deseja excluir este Perfil de Lançamento?",
"DeleteRootFolderMessageText": "Tem certeza de que deseja excluir a pasta raiz '{0}'?",
"DeleteRootFolderMessageText": "Tem certeza de que deseja excluir a pasta raiz '{name}'?",
"DeleteSelectedBookFiles": "Excluir arquivos do livro selecionado",
"DeleteSelectedBookFilesMessageText": "Tem certeza de que deseja excluir os arquivos do livro selecionado?",
"DeleteTag": "Excluir tag",
"DeleteTagMessageText": "Tem certeza de que deseja excluir a tag \"{0}\"?",
"DestinationPath": "Caminho de destino",
"DestinationPath": "Caminho de Destino",
"DetailedProgressBarHelpText": "Mostrar texto em barra de progresso",
"EnableAutomaticAdd": "Habilitar Adição Automática",
"EnableAutomaticSearch": "Ativar pesquisa automática",
"EnableAutomaticSearch": "Ativar a pesquisa automática",
"EnableColorImpairedMode": "Habilitar Modo para Deficientes Visuais",
"EnableColorImpairedModeHelpText": "Estilo alterado para permitir que usuários com deficiência de cor distingam melhor as informações codificadas por cores",
"EnableCompletedDownloadHandlingHelpText": "Importar automaticamente downloads concluídos do cliente de download",
@@ -163,12 +163,12 @@
"ForMoreInformationOnTheIndividualDownloadClientsClickOnTheInfoButtons": "Para obter mais informações sobre os clientes de download individuais, clique nos botões de informações.",
"ForMoreInformationOnTheIndividualIndexersClickOnTheInfoButtons": "Para saber mais sobre cada indexador, clique nos botões de informações.",
"ForMoreInformationOnTheIndividualListsClickOnTheInfoButtons": "Para saber mais sobre cada lista, clique nos botões de informação.",
"GeneralSettings": "Configurações Gerais",
"GeneralSettings": "Configurações gerais",
"Global": "Global",
"GoToInterp": "Ir para {0}",
"Grab": "Obter",
"GrabID": "Obter ID",
"IconForCutoffUnmet": "Ícone para Limite não atingido",
"IconForCutoffUnmet": "Ícone para Corte Não Atendido",
"IconTooltip": "Agendado",
"IgnoredAddresses": "Endereços Ignorados",
"IgnoredHelpText": "O lançamento será rejeitado se contiver um ou mais desses termos (não diferencia maiúsculas de minúsculas)",
@@ -179,7 +179,7 @@
"Mode": "Modo",
"ImportExtraFilesHelpText": "Importar arquivos adicionais correspondentes (legendas, nfo, etc.) após importar um arquivo de livro",
"ImportFailedInterp": "Falha na importação: {0}",
"ImportedTo": "Importado para",
"ImportedTo": "Importado Para",
"Importing": "Importando",
"IncludeHealthWarningsHelpText": "Incluir avisos de integridade",
"IncludeUnknownAuthorItemsHelpText": "Mostrar itens sem autor na fila, isso pode incluir autores removidos, livros ou qualquer outra coisa na categoria de Readarr",
@@ -203,7 +203,7 @@
"LongDateFormat": "Formato de Data Longa",
"MIA": "Desaparecidos",
"ManualImport": "Importação manual",
"MarkAsFailed": "Marcar como falhado",
"MarkAsFailed": "Marcar como Falha",
"MarkAsFailedMessageText": "Tem certeza que deseja marcar \"{0}\" como falhado?",
"MaximumLimits": "Limites Máximos",
"MaximumSize": "Tamanho Máximo",
@@ -225,11 +225,11 @@
"New": "Novo",
"NoBackupsAreAvailable": "Não há backups disponíveis",
"NoHistory": "Sem histórico.",
"NoLeaveIt": "Não, deixe-o",
"NoLeaveIt": "Não, deixe",
"NoLimitForAnyRuntime": "Sem limite para qualquer tempo de execução",
"NoLogFiles": "Nenhum arquivo de log",
"NoLogFiles": "Nenhum arquivo de registro",
"NoMinimumForAnyRuntime": "Sem mínimo para qualquer tempo de execução",
"NoUpdatesAreAvailable": "Nenhuma atualização está disponível",
"NoUpdatesAreAvailable": "Não há atualizações disponíveis",
"None": "Vazio",
"NotificationTriggers": "Gatilhos de Notificação",
"OnGrabHelpText": "Ao obter",
@@ -260,20 +260,20 @@
"ProxyBypassFilterHelpText": "Use ',' como separador e '*.' como curinga para subdomínios",
"ProxyType": "Tipo de Proxy",
"ProxyUsernameHelpText": "Você só precisa digitar um nome de usuário e senha se for necessário. Caso contrário, deixe-os em branco.",
"PublishedDate": "Data de publicação",
"PublishedDate": "Data de Publicação",
"Quality": "Qualidade",
"QualityDefinitions": "Definições de Qualidade",
"QualityProfile": "Perfil de Qualidade",
"QualityProfile": "Perfil de qualidade",
"QualityProfiles": "Perfis de Qualidade",
"QualitySettings": "Configurações de Qualidade",
"Queue": "Fila",
"RSSSync": "Sincronização RSS",
"RSSSyncInterval": "Intervalo da sincronização RSS",
"ReadTheWikiForMoreInformation": "Leia o Wiki para mais informações",
"ReadTheWikiForMoreInformation": "Leia o Wiki para obter mais informações",
"ReadarrSupportsAnyIndexerThatUsesTheNewznabStandardAsWellAsOtherIndexersListedBelow": "O Readarr oferece suporte a qualquer indexador que usa o padrão Newznab, além de outros indexadores, listados abaixo.",
"ReadarrTags": "Tags do Readarr",
"Real": "Real",
"Reason": "Motivo",
"Reason": "Razão",
"RecycleBinCleanupDaysHelpText": "Defina como 0 para desabilitar a limpeza automática",
"RecycleBinCleanupDaysHelpTextWarning": "Os arquivos na lixeira mais antigos do que o número de dias selecionado serão limpos automaticamente",
"RecycleBinHelpText": "Arquivos de livros vêm para cá quando excluídos, em vez de serem apagados permanentemente",
@@ -283,7 +283,7 @@
"Refresh": "Atualizar",
"RefreshInformationAndScanDisk": "Atualizar as informações e verificar o disco",
"ReleaseDate": "Data de lançamento",
"ReleaseGroup": "Grupo do Lançamento",
"ReleaseGroup": "Grupo de lançamento",
"ReleaseRejected": "Versão rejeitada",
"ReleaseWillBeProcessedInterp": "A versão será processada {0}",
"Reload": "Recarregar",
@@ -292,13 +292,13 @@
"RemoveCompletedDownloadsHelpText": "Remover downloads importados do histórico do cliente de download",
"RemoveFailedDownloadsHelpText": "Remova downloads com falha do histórico do cliente de download",
"RemoveFilter": "Remover filtro",
"RemoveFromDownloadClient": "Remover Do Cliente de Download",
"RemoveFromDownloadClient": "Remover do cliente de download",
"RemoveFromQueue": "Remover da fila",
"RemoveHelpTextWarning": "Isso removerá o download e o(s) arquivo(s) do cliente de download.",
"RemoveSelected": "Remover selecionado(s)",
"RemoveSelected": "Remover Selecionado",
"RemoveTagExistingTag": "Tag existente",
"RemoveTagRemovingTag": "Removendo tag",
"RemovedFromTaskQueue": "Removido da fila de tarefas",
"RemovedFromTaskQueue": "Removido da Fila de Tarefas",
"RenameBooksHelpText": "O Readarr usará o nome de arquivo existente se a renomeação estiver desativada",
"Reorder": "Reordenar",
"ReplaceIllegalCharacters": "Substituir Caracteres Ilegais",
@@ -318,17 +318,17 @@
"Retention": "Retenção",
"RetentionHelpText": "Somente Usenet: defina como zero para definir a retenção ilimitada",
"RetryingDownloadInterp": "Tentando novamente o download {0} em {1}",
"RootFolder": "Pasta Raiz",
"RootFolder": "Pasta raiz",
"RootFolders": "Pastas Raiz",
"RssSyncIntervalHelpText": "Intervalo em minutos. Defina como zero para desativar (isso interromperá todas as capturas de lançamentos automáticas)",
"SSLCertPassword": "Senha do certificado SSL",
"SSLCertPath": "Caminho do certificado SSL",
"SSLPort": "Porta SSL",
"Scheduled": "Agendado",
"Scheduled": "Programado",
"ScriptPath": "Caminho do Script",
"Search": "Pesquisar",
"SearchAll": "Pesquisar tudo",
"SearchForMissing": "Pesquisar ausentes",
"SearchForMissing": "Pesquisar por Ausentes",
"SearchSelected": "Pesquisar selecionado(s)",
"Security": "Segurança",
"SendAnonymousUsageData": "Enviar dados de uso anônimos",
@@ -355,15 +355,15 @@
"SkipFreeSpaceCheckWhenImportingHelpText": "Use quando o Readarr não conseguir detectar espaço livre na pasta raiz do autor",
"SorryThatAuthorCannotBeFound": "Desculpe, esse autor não pode ser encontrado.",
"SorryThatBookCannotBeFound": "Desculpe, esse livro não pode ser encontrado.",
"Source": "Fonte",
"SourcePath": "Caminho da fonte",
"Source": "Origem",
"SourcePath": "Caminho da Fonte",
"SslCertPasswordHelpText": "Senha para arquivo pfx",
"SslCertPathHelpText": "Caminho para o arquivo pfx",
"SslPortHelpTextWarning": "Requer reinício para ter efeito",
"StandardBookFormat": "Formato de livro padrão",
"StartTypingOrSelectAPathBelow": "Comece a digitar ou selecione um caminho abaixo",
"StartupDirectory": "Diretório de inicialização",
"Status": "Estado",
"Status": "Status",
"StatusEndedEnded": "Terminado",
"Style": "Estilo",
"SuccessMyWorkIsDoneNoFilesToRename": "Êba, já terminei! Não há arquivos a renomear.",
@@ -373,9 +373,9 @@
"SupportsSearchvalueWillBeUsedWhenAutomaticSearchesArePerformedViaTheUIOrByReadarr": "Será usado quando pesquisas automáticas forem realizadas pela interface ou pelo Readarr",
"SupportsSearchvalueWillBeUsedWhenInteractiveSearchIsUsed": "Será usado com a pesquisa interativa",
"TagIsNotUsedAndCanBeDeleted": "A tag não é usada e pode ser excluída",
"Tags": "Tags",
"Tags": "Etiquetas",
"Tasks": "Tarefas",
"TestAll": "Testar Tudo",
"TestAll": "Testar tudo",
"TestAllClients": "Testar todos os clientes",
"TestAllIndexers": "Testar todos os indexadores",
"TestAllLists": "Testar todas as listas",
@@ -391,7 +391,7 @@
"UILanguageHelpTextWarning": "É necessário recarregar o navegador",
"UISettings": "Configurações da interface",
"UnableToAddANewRootFolderPleaseTryAgain": "Não foi possível adicionar uma nova pasta raiz, tente novamente.",
"UnableToLoadBackups": "Não foi possível carregar os backups",
"UnableToLoadBackups": "Não é possível carregar backups",
"UnableToLoadDelayProfiles": "Não foi possível carregar os perfis de atraso",
"UnableToLoadDownloadClientOptions": "Não foi possível carregar as opções do cliente de download",
"UnableToLoadDownloadClients": "Não foi possível carregar os clientes de download",
@@ -448,7 +448,7 @@
"UpdatingIsDisabledInsideADockerContainerUpdateTheContainerImageInstead": "A atualização está desabilitada no contêiner do Docker. Atualize a imagem do contêiner.",
"UpdateCoversHelpText": "Fazer com que as capas de livros do Calibre correspondam às do Readarr",
"UpdateCovers": "Atualizar capas",
"UnmappedFiles": "Arquivos não mapeados",
"UnmappedFiles": "Arquivos Não Mapeados",
"UnableToLoadMetadataProviderSettings": "Não foi possível carregar as configurações do provedor de metadados",
"TrackTitle": "Título da faixa",
"TrackNumber": "Número da faixa",
@@ -528,7 +528,7 @@
"MinimumPages": "Mínimo de páginas",
"MinPopularityHelpText": "Popularidade = Classificação média × Número de votos",
"MetadataSourceHelpText": "Fonte alternativa de metadados (deixe em branco para usar o padrão)",
"MetadataSource": "Fonte de Metadados",
"MetadataSource": "Fonte de metadados",
"MetadataProviderSource": "Fonte do provedor de metadados",
"MetadataProfiles": "Perfis de metadados",
"MetadataProfileIdHelpText": "Adicionar itens da lista do Perfil de metadados com",
@@ -581,7 +581,7 @@
"EmbedMetadataInBookFiles": "Incorporar metadados nos arquivos do livro",
"EmbedMetadataHelpText": "Pedir ao Calibre para gravar metadados no arquivo do livro",
"EditionsHelpText": "Alterar edição para este livro",
"MaintenanceRelease": "Versão de manutenção: correções de bugs e outras melhorias. Veja Github Commit History para mais detalhes",
"MaintenanceRelease": "Versão de manutenção: correções de bugs e outros aprimoramentos. Consulte o Histórico de Commit do Github para obter mais detalhes",
"EditAuthor": "Editar autor",
"DownloadPropersAndRepacksHelpTexts2": "Use \"Não preferir\" para classificar por pontuação de palavra preferida em relação a Propers/Repacks",
"DownloadClientCheckDownloadingToRoot": "O cliente de download {0} coloca os downloads na pasta raiz {1}. Você não deve baixar para uma pasta raiz.",
@@ -663,7 +663,7 @@
"BookTitle": "Título do livro",
"OutputPath": "Caminho de saída",
"Progress": "Progresso",
"ReleaseTitle": "Título do Lançamento",
"ReleaseTitle": "Título da versão",
"Actions": "Ações",
"FilterAuthor": "Filtro de Autor",
"NameFirstLast": "Primeiro Nome Último Nome",
@@ -687,12 +687,12 @@
"SeriesTotal": "Séries ({0})",
"TooManyBooks": "Livros ausentes ou muitos? Modifique ou crie um novo",
"BlocklistRelease": "Lançamento na lista de bloqueio",
"NoHistoryBlocklist": "Nenhum histórico na lista de bloqueio",
"NoHistoryBlocklist": "Sem histórico na lista de bloqueio",
"Blocklist": "Lista de Bloqueio",
"RemoveFromBlocklist": "Remover da lista de bloqueio",
"UnableToLoadBlocklist": "Incapaz de carregar a lista de bloqueio",
"ReleaseBranchCheckOfficialBranchMessage": "Ramo {0} não é um ramo válido de lançamentos do Readarr, você não irá receber atualizações",
"Time": "Horário",
"Time": "Tempo",
"IgnoredMetaHelpText": "Livros irão ser ignorados se eles conterem um ou mais termos (não diferenciando maiúscula de minuscula)",
"Component": "Componente",
"Level": "Nível",
@@ -783,7 +783,7 @@
"IndexerStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a falhas",
"IndexerStatusCheckSingleClientMessage": "Indexadores indisponíveis devido a falhas: {0}",
"Lists": "Listas",
"MediaManagement": "Gerenciamento de Mídia",
"MediaManagement": "Gerenciamento de mídia",
"MediaManagementSettingsSummary": "Nomenclatura, configurações de gerenciamento de arquivos e pastas raiz",
"Metadata": "Metadados",
"MissingFromDisk": "Readarr não conseguiu encontrar o arquivo no disco, então o arquivo foi desvinculado do livro no banco de dados",
@@ -794,8 +794,8 @@
"ProxyCheckBadRequestMessage": "Falha ao testar o proxy. Código de status: {0}",
"ProxyCheckFailedToTestMessage": "Falha ao testar o proxy: {0}",
"QualitySettingsSummary": "Tamanhos de qualidade e nomenclatura",
"Queued": "Enfileirados",
"QueueIsEmpty": "Fila está vazia",
"Queued": "Na fila",
"QueueIsEmpty": "A fila está vazia",
"RefreshAndScan": "Atualizar & Escanear",
"RefreshBook": "Atualizar Livro",
"ReadarrSupportsAnyDownloadClient": "Readarr suporta muitos clientes populares de download de torrent e usenet.",
@@ -878,7 +878,7 @@
"ApiKeyValidationHealthCheckMessage": "Atualize sua chave de API para ter pelo menos {0} caracteres. Você pode fazer isso através das configurações ou do arquivo de configuração",
"DeleteFormat": "Excluir Formato",
"DataFutureBooks": "Monitorar livros que ainda não foram lançados",
"DeleteFormatMessageText": "Tem certeza de que deseja excluir a etiqueta de formato {0} ?",
"DeleteFormatMessageText": "Tem certeza de que deseja excluir a tag de formato '{0}'?",
"IncludeCustomFormatWhenRenamingHelpText": "'Incluir em {Formatos Personalizados} formato de renomeação'",
"IndexerTagsHelpText": "Use este indexador apenas para autores com pelo menos uma etiqueta correspondente. Deixe em branco para usar com todos os autores.",
"MinFormatScoreHelpText": "Pontuação mínima de formato personalizado permitida para download",
@@ -892,22 +892,22 @@
"CustomFormats": "Formatos personalizados",
"CutoffFormatScoreHelpText": "Depois que essa pontuação de formato personalizado for alcançada, Readarr não obterá mais lançamentos de livros",
"DeleteCustomFormat": "Excluir formato personalizado",
"ExportCustomFormat": "Exportar Formato Personalizado",
"ExportCustomFormat": "Exportar formato personalizado",
"Formats": "Formatos",
"Loading": "carregando",
"NegateHelpText": "Se marcado, o formato personalizado não será aplicado se esta condição {0} corresponder.",
"ThereWasAnErrorLoadingThisItem": "Ocorreu um erro ao carregar este item",
"ThereWasAnErrorLoadingThisPage": "Ocorreu um erro ao carregar esta página",
"UpgradesAllowed": "Atualizações Permitidas",
"DeleteCustomFormatMessageText": "Tem certeza de que deseja excluir o formato personalizado '{0}'?",
"DeleteCustomFormatMessageText": "Tem certeza de que deseja excluir o formato personalizado '{name}'?",
"ImportListMissingRoot": "Pasta raiz ausente para lista(s) de importação: {0}",
"ImportListMultipleMissingRoots": "Várias pastas raiz estão ausentes para listas de importação: {0}",
"IndexerDownloadClientHelpText": "Especifique qual cliente de download é usado para baixar deste indexador",
"ListRefreshInterval": "Intervalo de atualização da lista",
"ListWillRefreshEveryInterp": "A lista será atualizada a cada {0}",
"ResetDefinitionTitlesHelpText": "Redefinir títulos de definição, bem como valores",
"ResetDefinitionTitlesHelpText": "Redefinir títulos de definição e valores",
"ResetDefinitions": "Redefinir Definições",
"ResetTitles": "Redefinir Títulos",
"ResetTitles": "Redefinir títulos",
"UnableToLoadCustomFormats": "Não foi possível carregar formatos personalizados",
"HiddenClickToShow": "Oculto, clique para mostrar",
"HideAdvanced": "Ocultar Avançado",
@@ -922,24 +922,24 @@
"DeleteRemotePathMapping": "Excluir Mapeamento de Caminho Remoto",
"BlocklistReleases": "Lançamentos na lista de bloqueio",
"CloneCondition": "Condição de clone",
"DeleteConditionMessageText": "Tem certeza de que deseja excluir a condição '{0}'?",
"DeleteConditionMessageText": "Tem certeza de que deseja excluir a condição '{name}'?",
"DeleteRemotePathMappingMessageText": "Tem certeza de que deseja excluir este mapeamento de caminho remoto?",
"DeleteCondition": "Excluir condição",
"Negated": "Negado",
"RemoveSelectedItem": "Remover Item Selecionado",
"RemoveSelectedItem": "Remover item selecionado",
"RemoveSelectedItemBlocklistMessageText": "Tem certeza de que deseja remover os itens selecionados da lista de bloqueio?",
"RemoveSelectedItems": "Remover Itens Selecionados",
"RemoveSelectedItems": "Remover itens selecionados",
"RemoveSelectedItemQueueMessageText": "Tem certeza de que deseja remover 1 item da fila?",
"RemoveSelectedItemsQueueMessageText": "Tem certeza de que deseja remover {0} itens da fila?",
"Required": "Requerido",
"ResetQualityDefinitions": "Redefinir Definições de Qualidade",
"Required": "Necessário",
"ResetQualityDefinitions": "Redefinir definições de qualidade",
"ResetQualityDefinitionsMessageText": "Tem certeza de que deseja redefinir as definições de qualidade?",
"BlocklistReleaseHelpText": "Evita que o Readarr pegue automaticamente esses arquivos novamente",
"NoCutoffUnmetItems": "Nenhum item de corte não atendido",
"NoEventsFound": "Nenhum evento encontrado",
"NoEventsFound": "Não foram encontrados eventos",
"NoMissingItems": "Nenhum item faltando",
"CountImportListsSelected": "{0} lista(s) de importação selecionada(s)",
"CountIndexersSelected": "{0} indexador(es) selecionado(s)",
"CountImportListsSelected": "{selectedCount} lista(s) de importação selecionada(s)",
"CountIndexersSelected": "{selectedCount} indexador(es) selecionado(s)",
"EditSelectedDownloadClients": "Editar clientes de download selecionados",
"EditSelectedImportLists": "Editar listas de importação selecionadas",
"EditSelectedIndexers": "Editar indexadores selecionados",
@@ -970,12 +970,12 @@
"ApplyTagsHelpTextHowToApplyDownloadClients": "Como aplicar tags aos clientes de download selecionados",
"ApplyTagsHelpTextHowToApplyImportLists": "Como aplicar tags às listas de importação selecionadas",
"ApplyTagsHelpTextHowToApplyIndexers": "Como aplicar tags aos indexadores selecionados",
"CountDownloadClientsSelected": "{0} cliente(s) de download selecionado(s)",
"DeleteSelectedIndexersMessageText": "Tem certeza de que deseja excluir {0} indexador(es) selecionado(s)?",
"CountDownloadClientsSelected": "{selectedCount} cliente(s) de download selecionado(s)",
"DeleteSelectedIndexersMessageText": "Tem certeza de que deseja excluir {count} indexadores selecionados?",
"DeleteSelectedDownloadClients": "Excluir cliente(s) de download",
"DeleteSelectedDownloadClientsMessageText": "Tem certeza de que deseja excluir {0} cliente(s) de download selecionado(s)?",
"DeleteSelectedDownloadClientsMessageText": "Tem certeza de que deseja excluir {count} cliente(s) de download selecionado(s)?",
"DeleteSelectedImportLists": "Excluir lista(s) de importação",
"DeleteSelectedImportListsMessageText": "Tem certeza de que deseja excluir {0} lista(s) de importação selecionada(s)?",
"DeleteSelectedImportListsMessageText": "Tem certeza de que deseja excluir {count} lista(s) de importação selecionada(s)?",
"DeleteSelectedIndexers": "Excluir indexador(es)",
"DownloadClientTagHelpText": "Use este cliente de download apenas para autores com pelo menos uma etiqueta correspondente. Deixe em branco para usar com todos os autores.",
"ExistingTag": "Etiqueta existente",
@@ -983,5 +983,38 @@
"RemoveCompletedDownloads": "Remover downloads concluídos",
"RemovingTag": "Removendo etiqueta",
"SkipRedownloadHelpText": "Impede Readarr de tentar baixar versões alternativas para os itens removidos",
"IndexerDownloadClientHealthCheckMessage": "Indexadores com clientes de download inválidos: {0}."
"IndexerDownloadClientHealthCheckMessage": "Indexadores com clientes de download inválidos: {0}.",
"Activity": "Atividade",
"Bookshelf": "Prateleira",
"Events": "Eventos",
"FreeSpace": "Espaço Livre",
"NextExecution": "Próxima Execução",
"Small": "Pequeno",
"TotalSpace": "Espaço Total",
"Location": "Localização",
"Medium": "Médio",
"AddNew": "Adicionar Novo",
"Backup": "Backup",
"Large": "Grande",
"LastDuration": "Última Duração",
"LastExecution": "Última Execução",
"LastWriteTime": "Hora da Última Gravação",
"Library": "Biblioteca",
"System": "Sistema",
"Ui": "IU",
"NotificationStatusAllClientHealthCheckMessage": "Todas as notificações estão indisponíveis devido a falhas",
"NotificationStatusSingleClientHealthCheckMessage": "Notificações indisponíveis devido a falhas: {0}",
"AllResultsAreHiddenByTheAppliedFilter": "Todos os resultados são ocultados pelo filtro aplicado",
"SomeResultsAreHiddenByTheAppliedFilter": "Alguns resultados estão ocultos pelo filtro aplicado",
"NoResultsFound": "Nenhum resultado encontrado",
"ConnectionLostReconnect": "{appName} tentará se conectar automaticamente ou você pode clicar em recarregar abaixo.",
"AutomaticUpdatesDisabledDocker": "As atualizações automáticas não têm suporte direto ao usar o mecanismo de atualização do Docker. Você precisará atualizar a imagem do contêiner fora de {appName} ou usar um script",
"WouldYouLikeToRestoreBackup": "Gostaria de restaurar o backup '{name}'?",
"AppUpdated": "{appName} Atualizado",
"AppUpdatedVersion": "{appName} foi atualizado para a versão `{version}`, para obter as alterações mais recentes, você precisará recarregar {appName}",
"ConnectionLost": "Conexão Perdida",
"ConnectionLostToBackend": "{appName} perdeu sua conexão com o backend e precisará ser recarregado para restaurar a funcionalidade.",
"CountAuthorsSelected": "{selectedCount} autor(es) selecionado(s)",
"RecentChanges": "Mudanças Recentes",
"WhatsNew": "O quê há de novo?"
}

View File

@@ -587,7 +587,7 @@
"RemoveSelectedItemsQueueMessageText": "Sigur doriți să eliminați {0} elementul {1} din coadă?",
"ApplyTagsHelpTextHowToApplyAuthors": "Cum se aplică etichete indexatoarelor selectate",
"CountIndexersSelected": "{0} Indexatoare selectate",
"DeleteSelectedDownloadClients": "Ștergeți clientul de descărcare",
"DeleteSelectedDownloadClients": "Ștergere clienți de descărcare",
"ExistingTag": "Etichetă existentă",
"No": "Nu",
"NoChange": "Nicio Modificare",
@@ -613,5 +613,34 @@
"CloneCondition": "Clonați condiție",
"InstanceName": "Nume instanță",
"Publisher": "Editor",
"Implementation": "Implementarea"
"Implementation": "Implementarea",
"Authentication": "Autentificare",
"ResetQualityDefinitions": "Resetare definitii de calitate",
"ChooseImportMethod": "Alege modul de import",
"NotificationStatusSingleClientHealthCheckMessage": "Aplicații indisponibile datorită erorilor: {0}",
"Small": "Mic",
"Ui": "Interfață Grafica",
"Events": "Evenimente",
"Large": "Mare",
"Library": "Bibliotecă",
"SomeResultsAreHiddenByTheAppliedFilter": "Unele rezultate sunt ascunse de filtrul aplicat",
"ConnectionLostReconnect": "{appName} va încerca să se conecteze automat, sau poți apăsa reîncarcă mai jos.",
"ConnectionLostToBackend": "{appName} a pierdut conexiunea cu backend-ul și trebuie reîncărcat pentru a restabili funcționalitatea.",
"FreeSpace": "Spațiu Liber",
"LastDuration": "Ultima durată",
"LastExecution": "Ultima executare",
"LastWriteTime": "Data ultimei scrieri",
"Location": "Locație",
"NextExecution": "Următoarea execuție",
"RecentChanges": "Schimbări recente",
"ResetDefinitions": "Resetare definitii",
"System": "Sistem",
"TotalSpace": "Spațiu Total",
"WhatsNew": "Ce mai e nou?",
"NoResultsFound": "Nici un rezultat gasit",
"Activity": "Activitate",
"AddNew": "Adaugă nou",
"Medium": "Mediu",
"AllResultsAreHiddenByTheAppliedFilter": "Toate rezultatele sunt ascunse de filtrul aplicat",
"Backup": "Copie de rezervă"
}

View File

@@ -669,5 +669,32 @@
"DeleteSelectedIndexers": "Удалить индексер",
"DownloadClientTagHelpText": "Используйте этот индексатор только для фильмов с хотя бы одним совпадающим тегом. Оставьте пустым, чтобы использовать для всех фильмов.",
"EditSelectedImportLists": "Редактировать выбранные списки импорта",
"ExistingTag": "Существующий тэг"
"ExistingTag": "Существующий тэг",
"ConnectionLost": "Соединение прервано",
"ConnectionLostReconnect": "Radarr попытается соединиться автоматически или нажмите кнопку внизу.",
"Large": "Большой",
"LastDuration": "Последняя длительность",
"LastExecution": "Последнее выполнение",
"Activity": "Активность",
"AddNew": "Добавить",
"Medium": "Средний",
"NotificationStatusAllClientHealthCheckMessage": "Все листы недоступны из-за ошибок",
"TotalSpace": "Общее сводное место",
"Ui": "Пользовательский интерфейс",
"Backup": "Резервное копирование",
"Events": "События",
"FreeSpace": "Свободное место",
"LastWriteTime": "Последнее время записи",
"Library": "Библиотека",
"NoResultsFound": "Нет результатов",
"NotificationStatusSingleClientHealthCheckMessage": "Листы недоступны из-за ошибок: {0}",
"System": "Система",
"AllResultsAreHiddenByTheAppliedFilter": "Все результаты скрыты фильтром",
"ConnectionLostToBackend": "Radarr потерял связь с сервером и его необходимо перезагрузить, чтобы восстановить работоспособность.",
"Location": "Месторасположение",
"RecentChanges": "Последние изменения",
"SomeResultsAreHiddenByTheAppliedFilter": "Некоторые результаты скрыты примененным фильтром",
"WhatsNew": "Что нового?",
"NextExecution": "Следующее выполнение",
"Small": "Маленький"
}

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