mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-03-05 13:40:08 -05:00
Compare commits
216 Commits
v1.26.0.48
...
v2.0.1.510
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86b81948af | ||
|
|
54918e0c30 | ||
|
|
01dd793c0a | ||
|
|
950949e4bc | ||
|
|
fe198352a3 | ||
|
|
88502cd020 | ||
|
|
4924b45b56 | ||
|
|
aea8b7cd7e | ||
|
|
aafadb6111 | ||
|
|
c82f904d49 | ||
|
|
60740fa259 | ||
|
|
d36b32f414 | ||
|
|
14ccd6d2a5 | ||
|
|
bdc3b63df2 | ||
|
|
8eec321a0e | ||
|
|
06de2313ab | ||
|
|
a3f713bad8 | ||
|
|
7a1fca5e23 | ||
|
|
21c408a7da | ||
|
|
0e92108970 | ||
|
|
7d813ef97a | ||
|
|
c87995250a | ||
|
|
a9f7a376c7 | ||
|
|
c3ee3f2320 | ||
|
|
e8c26d0fea | ||
|
|
9c936121e8 | ||
|
|
40d2e40d94 | ||
|
|
837f50c91c | ||
|
|
f0a0202e5c | ||
|
|
708c94bc56 | ||
|
|
5ed82eaf09 | ||
|
|
7d77ad68fd | ||
|
|
6725358db5 | ||
|
|
c410e23460 | ||
|
|
903b86b9a2 | ||
|
|
52a49e6a34 | ||
|
|
a7d99f351c | ||
|
|
b0212dd780 | ||
|
|
c8f5099423 | ||
|
|
5cc4c3f302 | ||
|
|
c0d2cb42e9 | ||
|
|
8081f13052 | ||
|
|
84b672e617 | ||
|
|
ed586c2d72 | ||
|
|
233176e321 | ||
|
|
d1e3390bae | ||
|
|
1cd60c7a40 | ||
|
|
c61cfcd312 | ||
|
|
5eb4d112ca | ||
|
|
70f2361d69 | ||
|
|
1d6babaa15 | ||
|
|
0427add8d0 | ||
|
|
010c2b836d | ||
|
|
22c4c1fc9a | ||
|
|
d5f6cc94b8 | ||
|
|
411e96ef2a | ||
|
|
2b0e52ebca | ||
|
|
c6fa26ca7b | ||
|
|
c85f170d41 | ||
|
|
48a658571b | ||
|
|
0b3a5c9bc4 | ||
|
|
356d07ef34 | ||
|
|
0322d70d63 | ||
|
|
362f3fe223 | ||
|
|
075fd24f96 | ||
|
|
4ba72ea7f3 | ||
|
|
46f73c51bb | ||
|
|
3287d45661 | ||
|
|
71937fa44c | ||
|
|
6aefd46cd4 | ||
|
|
c8370c9e00 | ||
|
|
6be4203b41 | ||
|
|
1339373e43 | ||
|
|
fc9dfb0cf7 | ||
|
|
48301055ea | ||
|
|
8a9518c9c1 | ||
|
|
de099c6770 | ||
|
|
07711da4e0 | ||
|
|
7cb70716d0 | ||
|
|
548dedad5c | ||
|
|
7008626358 | ||
|
|
f6f2a3b00d | ||
|
|
2b16d93095 | ||
|
|
e63ee13d23 | ||
|
|
5c5a163151 | ||
|
|
023eec0ec0 | ||
|
|
5bc5f0e6b8 | ||
|
|
5cbacc01eb | ||
|
|
f4f1b38324 | ||
|
|
758dddd4ad | ||
|
|
73ee695633 | ||
|
|
27fbd7ef7e | ||
|
|
5125f256fb | ||
|
|
b99e8d0d65 | ||
|
|
d20b2cc9c0 | ||
|
|
8a1787bdb6 | ||
|
|
a19b8ea997 | ||
|
|
10ea6cd753 | ||
|
|
2c1b464715 | ||
|
|
3263454041 | ||
|
|
015db4a916 | ||
|
|
49268f3b8d | ||
|
|
f02a6f3e2c | ||
|
|
46b6124b97 | ||
|
|
53bc97b3be | ||
|
|
b09d4927cc | ||
|
|
328f3c0423 | ||
|
|
635e76526a | ||
|
|
790feed5ab | ||
|
|
59b5d2fc78 | ||
|
|
d5b12cf51a | ||
|
|
2d584f7eb6 | ||
|
|
0f1d647cd7 | ||
|
|
d6e8d89be4 | ||
|
|
8672129d5a | ||
|
|
44bdff8b8f | ||
|
|
4df8fc02f1 | ||
|
|
e101129cff | ||
|
|
147e732c9c | ||
|
|
a12381fb1d | ||
|
|
3a4de9cca1 | ||
|
|
43c988d951 | ||
|
|
a036e0fc37 | ||
|
|
56b9da16cf | ||
|
|
887c262589 | ||
|
|
12ff612775 | ||
|
|
0d3d27e46f | ||
|
|
d1846fde61 | ||
|
|
e6901506a0 | ||
|
|
08b4eddbc5 | ||
|
|
979db70e68 | ||
|
|
22834a852a | ||
|
|
f0540a5f8b | ||
|
|
1f7ac7d7d6 | ||
|
|
8ac68240ad | ||
|
|
b463a3f54b | ||
|
|
e15e57329e | ||
|
|
d8354408a4 | ||
|
|
6d2d49f7bd | ||
|
|
37610eec40 | ||
|
|
ed51208116 | ||
|
|
26e4dcad65 | ||
|
|
6eb21a02a1 | ||
|
|
8c2d5a404d | ||
|
|
3b83a00eaf | ||
|
|
a5a86a6f86 | ||
|
|
e7ed09a43d | ||
|
|
547bc2e58c | ||
|
|
8eb674c8d7 | ||
|
|
2c3621d25e | ||
|
|
2648f2c639 | ||
|
|
f4d621063b | ||
|
|
73494c462c | ||
|
|
36f6896f30 | ||
|
|
e01741a69e | ||
|
|
1dbff1235e | ||
|
|
1a9ad6b363 | ||
|
|
c88249300c | ||
|
|
7b8e352d87 | ||
|
|
81f7a6cbab | ||
|
|
523e46af2a | ||
|
|
2b4a6def2a | ||
|
|
9097c0ef6d | ||
|
|
4321c1d40c | ||
|
|
bb2548a08d | ||
|
|
3a9b841fad | ||
|
|
31203d1370 | ||
|
|
c8a910eaf4 | ||
|
|
9ab3c3e6c7 | ||
|
|
4659cb706a | ||
|
|
500759bf1f | ||
|
|
43c7c43257 | ||
|
|
9c2fced391 | ||
|
|
52ec5b6ff6 | ||
|
|
b46e657976 | ||
|
|
51fd30ba10 | ||
|
|
5fbb347108 | ||
|
|
54d3d44620 | ||
|
|
5ca18683ca | ||
|
|
6bdf5f5d69 | ||
|
|
7cba7152f1 | ||
|
|
cf012eb001 | ||
|
|
6b8a7993ff | ||
|
|
c6440bb21b | ||
|
|
b95eac98b9 | ||
|
|
0eb19ce834 | ||
|
|
4b8016d95d | ||
|
|
31d8d2419a | ||
|
|
d29ccd7749 | ||
|
|
e789f4ec54 | ||
|
|
58d495d618 | ||
|
|
f3328863e1 | ||
|
|
a23d792781 | ||
|
|
f066cf399d | ||
|
|
61e863cb31 | ||
|
|
b2afbc6872 | ||
|
|
aace65f88e | ||
|
|
9ab2d8b444 | ||
|
|
bc314061ef | ||
|
|
87b3dcd780 | ||
|
|
f3b99f68f6 | ||
|
|
c4a90e8ba4 | ||
|
|
41320ca2dc | ||
|
|
b8b32f8708 | ||
|
|
30c4bb24e8 | ||
|
|
b447db5d08 | ||
|
|
299001a513 | ||
|
|
2871f1f2a2 | ||
|
|
a9b93df0c9 | ||
|
|
2726787ee9 | ||
|
|
b917932f19 | ||
|
|
06ae85e6d1 | ||
|
|
b1c7e98664 | ||
|
|
62479737a7 | ||
|
|
8e69415d64 | ||
|
|
222dfb1821 |
@@ -2,11 +2,11 @@
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
|
||||
{
|
||||
"name": "Prowlarr",
|
||||
"image": "mcr.microsoft.com/devcontainers/dotnet:1-6.0",
|
||||
"image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"nodeGypDependencies": true,
|
||||
"version": "16",
|
||||
"version": "20",
|
||||
"nvmVersion": "latest"
|
||||
}
|
||||
},
|
||||
|
||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -10,7 +10,7 @@
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build dotnet",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/_output/net6.0/Prowlarr",
|
||||
"program": "${workspaceFolder}/_output/net8.0/Prowlarr",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
|
||||
@@ -78,6 +78,6 @@ Thank you to [<img src="https://resources.jetbrains.com/storage/products/company
|
||||
### License
|
||||
|
||||
- [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||
- Copyright 2010-2024
|
||||
- Copyright 2010-2025
|
||||
|
||||
Icon Credit - [Box vector created by freepik - www.freepik.com](https://www.freepik.com/vectors/box)
|
||||
|
||||
@@ -9,17 +9,17 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '1.26.0'
|
||||
majorVersion: '2.0.1'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||
sentryOrg: 'servarr'
|
||||
sentryUrl: 'https://sentry.servarr.com'
|
||||
dotnetVersion: '6.0.427'
|
||||
dotnetVersion: '8.0.405'
|
||||
nodeVersion: '20.X'
|
||||
innoVersion: '6.2.2'
|
||||
windowsImage: 'windows-2022'
|
||||
linuxImage: 'ubuntu-20.04'
|
||||
linuxImage: 'ubuntu-22.04'
|
||||
macImage: 'macOS-13'
|
||||
|
||||
trigger:
|
||||
@@ -106,7 +106,7 @@ stages:
|
||||
echo "Extra platforms already enabled"
|
||||
else
|
||||
echo "Enabling extra platform support"
|
||||
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
|
||||
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64/' "$BUNDLEDVERSIONS"
|
||||
fi
|
||||
displayName: Enable Extra Platform Support
|
||||
- bash: ./build.sh --backend --enable-extra-platforms
|
||||
@@ -122,27 +122,23 @@ stages:
|
||||
artifact: '$(osName)Backend'
|
||||
displayName: Publish Backend
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/net6.0/win-x64/publish'
|
||||
- publish: '$(testsFolder)/net8.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'
|
||||
- publish: '$(testsFolder)/net8.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'
|
||||
- publish: '$(testsFolder)/net8.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'
|
||||
- publish: '$(testsFolder)/net8.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'
|
||||
- publish: '$(testsFolder)/net8.0/osx-x64/publish'
|
||||
artifact: osx-x64-tests
|
||||
displayName: Publish osx-x64 Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
@@ -189,7 +185,7 @@ stages:
|
||||
artifact: '$(osName)Frontend'
|
||||
displayName: Publish Frontend
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
|
||||
|
||||
- stage: Installer
|
||||
dependsOn:
|
||||
- Build_Backend
|
||||
@@ -259,21 +255,21 @@ stages:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).windows-core-x64.zip'
|
||||
archiveType: 'zip'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/win-x64/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/win-x64/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create win-x86 zip
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).windows-core-x86.zip'
|
||||
archiveType: 'zip'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/win-x86/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/win-x86/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create osx-x64 app
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).osx-app-core-x64.zip'
|
||||
archiveType: 'zip'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/osx-x64-app/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/osx-x64-app/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create osx-x64 tar
|
||||
inputs:
|
||||
@@ -281,14 +277,14 @@ stages:
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/osx-x64/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/osx-x64/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create osx-arm64 app
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).osx-app-core-arm64.zip'
|
||||
archiveType: 'zip'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/osx-arm64-app/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/osx-arm64-app/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create osx-arm64 tar
|
||||
inputs:
|
||||
@@ -296,7 +292,7 @@ stages:
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/osx-arm64/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/osx-arm64/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create linux-x64 tar
|
||||
inputs:
|
||||
@@ -304,7 +300,7 @@ stages:
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-x64/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-x64/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create linux-musl-x64 tar
|
||||
inputs:
|
||||
@@ -312,15 +308,7 @@ stages:
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/net6.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create linux-x86 tar
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-core-x86.tar.gz'
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-x86/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create linux-arm tar
|
||||
inputs:
|
||||
@@ -328,7 +316,7 @@ stages:
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-arm/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-arm/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create linux-musl-arm tar
|
||||
inputs:
|
||||
@@ -336,7 +324,7 @@ stages:
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create linux-arm64 tar
|
||||
inputs:
|
||||
@@ -344,7 +332,7 @@ stages:
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-arm64/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-arm64/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create linux-musl-arm64 tar
|
||||
inputs:
|
||||
@@ -352,7 +340,7 @@ stages:
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/net8.0
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create freebsd-x64 tar
|
||||
inputs:
|
||||
@@ -360,7 +348,7 @@ stages:
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/freebsd-x64/net6.0
|
||||
rootFolderOrFile: $(artifactsFolder)/freebsd-x64/net8.0
|
||||
- publish: $(Build.ArtifactStagingDirectory)
|
||||
artifact: 'Packages'
|
||||
displayName: Publish Packages
|
||||
@@ -391,7 +379,7 @@ stages:
|
||||
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
|
||||
SENTRY_ORG: $(sentryOrg)
|
||||
SENTRY_URL: $(sentryUrl)
|
||||
|
||||
|
||||
- stage: Unit_Test
|
||||
displayName: Unit Tests
|
||||
dependsOn: Build_Backend
|
||||
@@ -476,6 +464,7 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(testName) Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
failTaskOnMissingResultsFile: ne(variables['testName'], 'freebsd-x64')
|
||||
|
||||
- job: Unit_Docker
|
||||
displayName: Unit Docker
|
||||
@@ -487,29 +476,19 @@ stages:
|
||||
testName: 'Musl Net Core'
|
||||
artifactName: linux-musl-x64-tests
|
||||
containerImage: ghcr.io/servarr/testimages:alpine
|
||||
linux-x86:
|
||||
testName: 'linux-x86'
|
||||
artifactName: linux-x86-tests
|
||||
containerImage: ghcr.io/servarr/testimages:linux-x86
|
||||
|
||||
pool:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
|
||||
|
||||
container: $[ variables['containerImage'] ]
|
||||
|
||||
timeoutInMinutes: 10
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .NET'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
condition: and(succeeded(), ne(variables['testName'], 'linux-x86'))
|
||||
- bash: |
|
||||
SDKURL=$(curl -s https://api.github.com/repos/Servarr/dotnet-linux-x86/releases | jq -rc '.[].assets[].browser_download_url' | grep sdk-${DOTNETVERSION}.*gz$)
|
||||
curl -fsSL $SDKURL | tar xzf - -C /opt/dotnet
|
||||
displayName: 'Install .NET'
|
||||
condition: and(succeeded(), eq(variables['testName'], 'linux-x86'))
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
@@ -532,7 +511,8 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(testName) Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
|
||||
failTaskOnMissingResultsFile: true
|
||||
|
||||
- job: Unit_LinuxCore_Postgres14
|
||||
displayName: Unit Native LinuxCore with Postgres14 Database
|
||||
dependsOn: Prepare
|
||||
@@ -549,7 +529,7 @@ stages:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
|
||||
timeoutInMinutes: 10
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
@@ -585,6 +565,7 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'LinuxCore Postgres14 Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
failTaskOnMissingResultsFile: true
|
||||
|
||||
- job: Unit_LinuxCore_Postgres15
|
||||
displayName: Unit Native LinuxCore with Postgres15 Database
|
||||
@@ -597,12 +578,12 @@ stages:
|
||||
Prowlarr__Postgres__Port: '5432'
|
||||
Prowlarr__Postgres__User: 'prowlarr'
|
||||
Prowlarr__Postgres__Password: 'prowlarr'
|
||||
|
||||
|
||||
pool:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
|
||||
timeoutInMinutes: 10
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
@@ -638,6 +619,7 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'LinuxCore Postgres15 Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
failTaskOnMissingResultsFile: true
|
||||
|
||||
- stage: Integration
|
||||
displayName: Integration
|
||||
@@ -681,7 +663,7 @@ stages:
|
||||
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
@@ -703,7 +685,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: |
|
||||
@@ -720,6 +702,7 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(testName) Integration Tests'
|
||||
failTaskOnFailedTests: true
|
||||
failTaskOnMissingResultsFile: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
- job: Integration_LinuxCore_Postgres14
|
||||
@@ -757,7 +740,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: |
|
||||
@@ -782,6 +765,7 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'Integration LinuxCore Postgres14 Database Integration Tests'
|
||||
failTaskOnFailedTests: true
|
||||
failTaskOnMissingResultsFile: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
|
||||
@@ -820,7 +804,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: |
|
||||
@@ -845,6 +829,7 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'Integration LinuxCore Postgres15 Database Integration Tests'
|
||||
failTaskOnFailedTests: true
|
||||
failTaskOnMissingResultsFile: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
- job: Integration_FreeBSD
|
||||
@@ -891,6 +876,7 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'FreeBSD Integration Tests'
|
||||
failTaskOnFailedTests: true
|
||||
failTaskOnMissingResultsFile: false
|
||||
displayName: Publish Test Results
|
||||
|
||||
- job: Integration_Docker
|
||||
@@ -904,29 +890,18 @@ stages:
|
||||
artifactName: linux-musl-x64-tests
|
||||
containerImage: ghcr.io/servarr/testimages:alpine
|
||||
pattern: 'Prowlarr.*.linux-musl-core-x64.tar.gz'
|
||||
linux-x86:
|
||||
testName: 'linux-x86'
|
||||
artifactName: linux-x86-tests
|
||||
containerImage: ghcr.io/servarr/testimages:linux-x86
|
||||
pattern: 'Prowlarr.*.linux-core-x86.tar.gz'
|
||||
pool:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
|
||||
container: $[ variables['containerImage'] ]
|
||||
|
||||
timeoutInMinutes: 15
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .NET'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
condition: and(succeeded(), ne(variables['testName'], 'linux-x86'))
|
||||
- bash: |
|
||||
SDKURL=$(curl -s https://api.github.com/repos/Servarr/dotnet-linux-x86/releases | jq -rc '.[].assets[].browser_download_url' | grep sdk-${DOTNETVERSION}.*gz$)
|
||||
curl -fsSL $SDKURL | tar xzf - -C /opt/dotnet
|
||||
displayName: 'Install .NET'
|
||||
condition: and(succeeded(), eq(variables['testName'], 'linux-x86'))
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
@@ -943,7 +918,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: |
|
||||
@@ -960,12 +935,13 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(testName) Integration Tests'
|
||||
failTaskOnFailedTests: true
|
||||
failTaskOnMissingResultsFile: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
- stage: Automation
|
||||
displayName: Automation
|
||||
dependsOn: Packages
|
||||
|
||||
|
||||
jobs:
|
||||
- job: Automation
|
||||
strategy:
|
||||
@@ -991,7 +967,7 @@ stages:
|
||||
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
@@ -1013,7 +989,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: |
|
||||
@@ -1041,6 +1017,7 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(osName) Automation Tests'
|
||||
failTaskOnFailedTests: $(failBuild)
|
||||
failTaskOnMissingResultsFile: $(failBuild)
|
||||
displayName: Publish Test Results
|
||||
|
||||
- stage: Analyze
|
||||
@@ -1116,7 +1093,7 @@ stages:
|
||||
- checkout: self
|
||||
submodules: true
|
||||
persistCredentials: true
|
||||
fetchDepth: 1
|
||||
fetchDepth: 1
|
||||
- bash: ./docs.sh Windows
|
||||
displayName: Create openapi.json
|
||||
- bash: |
|
||||
@@ -1169,12 +1146,12 @@ stages:
|
||||
submodules: true
|
||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||
displayName: Enable Windows Test Service
|
||||
- task: SonarCloudPrepare@2
|
||||
- task: SonarCloudPrepare@3
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
inputs:
|
||||
SonarCloud: 'SonarCloud'
|
||||
organization: 'prowlarr'
|
||||
scannerMode: 'MSBuild'
|
||||
scannerMode: 'dotnet'
|
||||
projectKey: 'Prowlarr_Prowlarr'
|
||||
projectName: 'Prowlarr'
|
||||
projectVersion: '$(prowlarrVersion)'
|
||||
@@ -1184,10 +1161,10 @@ stages:
|
||||
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 win-x64
|
||||
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
|
||||
./build.sh --backend -f net8.0 -r win-x64
|
||||
TEST_DIR=_tests/net8.0/win-x64/publish/ ./test.sh Windows Unit Coverage
|
||||
displayName: Coverage Unit Tests
|
||||
- task: SonarCloudAnalyze@2
|
||||
- task: SonarCloudAnalyze@3
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
displayName: Publish SonarCloud Results
|
||||
- task: reportgenerator@5
|
||||
@@ -1228,4 +1205,3 @@ stages:
|
||||
DISCORDCHANNELID: $(discordChannelId)
|
||||
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
||||
DISCORDTHREADID: $(discordThreadId)
|
||||
|
||||
|
||||
52
build.sh
52
build.sh
@@ -33,14 +33,14 @@ EnableExtraPlatformsInSDK()
|
||||
echo "Extra platforms already enabled"
|
||||
else
|
||||
echo "Enabling extra platform support"
|
||||
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
|
||||
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64/' "$BUNDLEDVERSIONS"
|
||||
fi
|
||||
}
|
||||
|
||||
EnableExtraPlatforms()
|
||||
{
|
||||
if grep -qv freebsd-x64 src/Directory.Build.props; then
|
||||
sed -i'' -e "s^<RuntimeIdentifiers>\(.*\)</RuntimeIdentifiers>^<RuntimeIdentifiers>\1;freebsd-x64;linux-x86</RuntimeIdentifiers>^g" src/Directory.Build.props
|
||||
sed -i'' -e "s^<RuntimeIdentifiers>\(.*\)</RuntimeIdentifiers>^<RuntimeIdentifiers>\1;freebsd-x64</RuntimeIdentifiers>^g" src/Directory.Build.props
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ Build()
|
||||
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform=$platform -t:PublishAllRids
|
||||
dotnet msbuild -restore $slnFile -p:SelfContained=True -p:Configuration=Release -p:Platform=$platform -t:PublishAllRids
|
||||
else
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform=$platform -p:RuntimeIdentifiers=$RID -t:PublishAllRids
|
||||
dotnet msbuild -restore $slnFile -p:SelfContained=True -p:Configuration=Release -p:Platform=$platform -p:RuntimeIdentifiers=$RID -t:PublishAllRids
|
||||
fi
|
||||
|
||||
ProgressEnd 'Build'
|
||||
@@ -137,7 +137,7 @@ PackageLinux()
|
||||
|
||||
echo "Adding Prowlarr.Mono to UpdatePackage"
|
||||
cp $folder/Prowlarr.Mono.* $folder/Prowlarr.Update
|
||||
if [ "$framework" = "net6.0" ]; then
|
||||
if [ "$framework" = "net8.0" ]; then
|
||||
cp $folder/Mono.Posix.NETStandard.* $folder/Prowlarr.Update
|
||||
cp $folder/libMonoPosixHelper.* $folder/Prowlarr.Update
|
||||
fi
|
||||
@@ -165,7 +165,7 @@ PackageMacOS()
|
||||
|
||||
echo "Adding Prowlarr.Mono to UpdatePackage"
|
||||
cp $folder/Prowlarr.Mono.* $folder/Prowlarr.Update
|
||||
if [ "$framework" = "net6.0" ]; then
|
||||
if [ "$framework" = "net8.0" ]; then
|
||||
cp $folder/Mono.Posix.NETStandard.* $folder/Prowlarr.Update
|
||||
cp $folder/libMonoPosixHelper.* $folder/Prowlarr.Update
|
||||
fi
|
||||
@@ -377,15 +377,14 @@ then
|
||||
Build
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
PackageTests "net6.0" "win-x64"
|
||||
PackageTests "net6.0" "win-x86"
|
||||
PackageTests "net6.0" "linux-x64"
|
||||
PackageTests "net6.0" "linux-musl-x64"
|
||||
PackageTests "net6.0" "osx-x64"
|
||||
PackageTests "net8.0" "win-x64"
|
||||
PackageTests "net8.0" "win-x86"
|
||||
PackageTests "net8.0" "linux-x64"
|
||||
PackageTests "net8.0" "linux-musl-x64"
|
||||
PackageTests "net8.0" "osx-x64"
|
||||
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ];
|
||||
then
|
||||
PackageTests "net6.0" "freebsd-x64"
|
||||
PackageTests "net6.0" "linux-x86"
|
||||
PackageTests "net8.0" "freebsd-x64"
|
||||
fi
|
||||
else
|
||||
PackageTests "$FRAMEWORK" "$RID"
|
||||
@@ -413,20 +412,19 @@ then
|
||||
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
Package "net6.0" "win-x64"
|
||||
Package "net6.0" "win-x86"
|
||||
Package "net6.0" "linux-x64"
|
||||
Package "net6.0" "linux-musl-x64"
|
||||
Package "net6.0" "linux-arm64"
|
||||
Package "net6.0" "linux-musl-arm64"
|
||||
Package "net6.0" "linux-arm"
|
||||
Package "net6.0" "linux-musl-arm"
|
||||
Package "net6.0" "osx-x64"
|
||||
Package "net6.0" "osx-arm64"
|
||||
Package "net8.0" "win-x64"
|
||||
Package "net8.0" "win-x86"
|
||||
Package "net8.0" "linux-x64"
|
||||
Package "net8.0" "linux-musl-x64"
|
||||
Package "net8.0" "linux-arm64"
|
||||
Package "net8.0" "linux-musl-arm64"
|
||||
Package "net8.0" "linux-arm"
|
||||
Package "net8.0" "linux-musl-arm"
|
||||
Package "net8.0" "osx-x64"
|
||||
Package "net8.0" "osx-arm64"
|
||||
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ];
|
||||
then
|
||||
Package "net6.0" "freebsd-x64"
|
||||
Package "net6.0" "linux-x86"
|
||||
Package "net8.0" "freebsd-x64"
|
||||
fi
|
||||
else
|
||||
Package "$FRAMEWORK" "$RID"
|
||||
@@ -436,7 +434,7 @@ fi
|
||||
if [ "$INSTALLER" = "YES" ];
|
||||
then
|
||||
InstallInno
|
||||
BuildInstaller "net6.0" "win-x64"
|
||||
BuildInstaller "net6.0" "win-x86"
|
||||
BuildInstaller "net8.0" "win-x64"
|
||||
BuildInstaller "net8.0" "win-x86"
|
||||
RemoveInno
|
||||
fi
|
||||
|
||||
13
docs.sh
13
docs.sh
@@ -1,17 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
FRAMEWORK="net6.0"
|
||||
FRAMEWORK="net8.0"
|
||||
PLATFORM=$1
|
||||
ARCHITECTURE="${2:-x64}"
|
||||
|
||||
if [ "$PLATFORM" = "Windows" ]; then
|
||||
RUNTIME="win-x64"
|
||||
RUNTIME="win-$ARCHITECTURE"
|
||||
elif [ "$PLATFORM" = "Linux" ]; then
|
||||
RUNTIME="linux-x64"
|
||||
RUNTIME="linux-$ARCHITECTURE"
|
||||
elif [ "$PLATFORM" = "Mac" ]; then
|
||||
RUNTIME="osx-x64"
|
||||
RUNTIME="osx-$ARCHITECTURE"
|
||||
else
|
||||
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
|
||||
echo "Platform must be provided as first argument: Windows, Linux or Mac"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -37,7 +38,7 @@ dotnet clean $slnFile -c Release
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
|
||||
|
||||
dotnet new tool-manifest
|
||||
dotnet tool install --version 6.6.2 Swashbuckle.AspNetCore.Cli
|
||||
dotnet tool install --version 8.1.4 Swashbuckle.AspNetCore.Cli
|
||||
|
||||
dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/$application" v1 &
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ module.exports = (env) => {
|
||||
const config = {
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
devtool: isProduction ? 'source-map' : 'eval-source-map',
|
||||
target: 'web',
|
||||
|
||||
stats: {
|
||||
children: false
|
||||
@@ -169,7 +170,7 @@ module.exports = (env) => {
|
||||
loose: true,
|
||||
debug: false,
|
||||
useBuiltIns: 'entry',
|
||||
corejs: 3
|
||||
corejs: '3.42'
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -20,6 +20,8 @@ import HintedSelectInputSelectedValue from './HintedSelectInputSelectedValue';
|
||||
import TextInput from './TextInput';
|
||||
import styles from './EnhancedSelectInput.css';
|
||||
|
||||
const MINIMUM_DISTANCE_FROM_EDGE = 10;
|
||||
|
||||
function isArrowKey(keyCode) {
|
||||
return keyCode === keyCodes.UP_ARROW || keyCode === keyCodes.DOWN_ARROW;
|
||||
}
|
||||
@@ -137,18 +139,9 @@ class EnhancedSelectInput extends Component {
|
||||
// Listeners
|
||||
|
||||
onComputeMaxHeight = (data) => {
|
||||
const {
|
||||
top,
|
||||
bottom
|
||||
} = data.offsets.reference;
|
||||
|
||||
const windowHeight = window.innerHeight;
|
||||
|
||||
if ((/^botton/).test(data.placement)) {
|
||||
data.styles.maxHeight = windowHeight - bottom;
|
||||
} else {
|
||||
data.styles.maxHeight = top;
|
||||
}
|
||||
data.styles.maxHeight = windowHeight - MINIMUM_DISTANCE_FROM_EDGE;
|
||||
|
||||
return data;
|
||||
};
|
||||
@@ -460,6 +453,10 @@ class EnhancedSelectInput extends Component {
|
||||
order: 851,
|
||||
enabled: true,
|
||||
fn: this.onComputeMaxHeight
|
||||
},
|
||||
preventOverflow: {
|
||||
enabled: true,
|
||||
boundariesElement: 'viewport'
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
composes: link;
|
||||
|
||||
padding: 10px 24px;
|
||||
padding-left: 35px;
|
||||
}
|
||||
|
||||
.isActiveLink {
|
||||
@@ -41,10 +42,6 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.noIcon {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.status {
|
||||
float: right;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ interface CssExports {
|
||||
'isActiveParentLink': string;
|
||||
'item': string;
|
||||
'link': string;
|
||||
'noIcon': string;
|
||||
'status': string;
|
||||
}
|
||||
export const cssExports: CssExports;
|
||||
|
||||
@@ -63,9 +63,7 @@ class PageSidebarItem extends Component {
|
||||
</span>
|
||||
}
|
||||
|
||||
<span className={isChildItem ? styles.noIcon : null}>
|
||||
{typeof title === 'function' ? title() : title}
|
||||
</span>
|
||||
{typeof title === 'function' ? title() : title}
|
||||
|
||||
{
|
||||
!!StatusComponent &&
|
||||
|
||||
@@ -22,11 +22,14 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.label {
|
||||
padding: 0 3px;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
color: var(--toolbarLabelColor);
|
||||
font-size: $extraSmallFontSize;
|
||||
line-height: calc($extraSmallFontSize + 1px);
|
||||
|
||||
@@ -23,6 +23,7 @@ function PageToolbarButton(props) {
|
||||
isDisabled && styles.isDisabled
|
||||
)}
|
||||
isDisabled={isDisabled || isSpinning}
|
||||
title={label}
|
||||
{...otherProps}
|
||||
>
|
||||
<Icon
|
||||
|
||||
@@ -65,17 +65,30 @@ class VirtualTable extends Component {
|
||||
|
||||
if (this._grid && scrollTop !== undefined && scrollTop !== 0 && !scrollRestored) {
|
||||
this.setState({ scrollRestored: true });
|
||||
this._grid.scrollToPosition({ scrollTop });
|
||||
this._gridScrollToPosition({ scrollTop });
|
||||
}
|
||||
|
||||
if (scrollIndex != null && scrollIndex !== prevProps.scrollIndex) {
|
||||
this._grid.scrollToCell({
|
||||
this._gridScrollToCell({
|
||||
rowIndex: scrollIndex,
|
||||
columnIndex: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_gridScrollToCell = ({ rowIndex = 0, columnIndex = 0 }) => {
|
||||
const scrollOffset = this._grid.getOffsetForCell({
|
||||
rowIndex,
|
||||
columnIndex
|
||||
});
|
||||
|
||||
this._gridScrollToPosition(scrollOffset);
|
||||
};
|
||||
|
||||
_gridScrollToPosition = ({ scrollTop = 0, scrollLeft = 0 }) => {
|
||||
this.props.scroller?.scrollTo({ top: scrollTop, left: scrollLeft });
|
||||
};
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
|
||||
@@ -257,6 +257,7 @@ class HistoryRow extends Component {
|
||||
key={parameter.key}
|
||||
title={parameter.title}
|
||||
value={data[parameter.key]}
|
||||
queryType={data.queryType}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import React from 'react';
|
||||
import Link from 'Components/Link/Link';
|
||||
import { HistoryQueryType } from 'typings/History';
|
||||
import styles from './HistoryRowParameter.css';
|
||||
|
||||
interface HistoryRowParameterProps {
|
||||
title: string;
|
||||
value: string;
|
||||
queryType: HistoryQueryType;
|
||||
}
|
||||
|
||||
function HistoryRowParameter(props: HistoryRowParameterProps) {
|
||||
const { title, value } = props;
|
||||
const { title, value, queryType } = props;
|
||||
|
||||
const type = title.toLowerCase();
|
||||
|
||||
@@ -18,7 +20,13 @@ function HistoryRowParameter(props: HistoryRowParameterProps) {
|
||||
link = <Link to={`https://imdb.com/title/${value}/`}>{value}</Link>;
|
||||
} else if (type === 'tmdb') {
|
||||
link = (
|
||||
<Link to={`https://www.themoviedb.org/movie/${value}`}>{value}</Link>
|
||||
<Link
|
||||
to={`https://www.themoviedb.org/${
|
||||
queryType === 'tvsearch' ? 'tv' : 'movie'
|
||||
}/${value}`}
|
||||
>
|
||||
{value}
|
||||
</Link>
|
||||
);
|
||||
} else if (type === 'tvdb') {
|
||||
link = (
|
||||
|
||||
@@ -68,6 +68,7 @@ function IndexerHistoryRow(props: IndexerHistoryRowProps) {
|
||||
key={parameter.key}
|
||||
title={parameter.title}
|
||||
value={data[parameter.key as keyof HistoryData].toString()}
|
||||
queryType={data.queryType}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -21,7 +21,7 @@ function createMapStateToProps() {
|
||||
) => {
|
||||
|
||||
// If a release is deleted this selector may fire before the parent
|
||||
// selecors, which will result in an undefined release, if that happens
|
||||
// selectors, which will result in an undefined release, if that happens
|
||||
// we want to return early here and again in the render function to avoid
|
||||
// trying to show a release that has no information available.
|
||||
|
||||
|
||||
@@ -30,7 +30,9 @@ export const authenticationMethodOptions = [
|
||||
key: 'basic',
|
||||
get value() {
|
||||
return translate('AuthBasic');
|
||||
}
|
||||
},
|
||||
isDisabled: true,
|
||||
isHidden: true
|
||||
},
|
||||
{
|
||||
key: 'forms',
|
||||
|
||||
@@ -419,7 +419,7 @@ export const reducers = createHandleActions({
|
||||
const items = newState.items;
|
||||
const index = items.findIndex((item) => item.guid === guid);
|
||||
|
||||
// Don't try to update if there isnt a matching item (the user closed the modal)
|
||||
// Don't try to update if there isn't a matching item (the user closed the modal)
|
||||
if (index >= 0) {
|
||||
const item = Object.assign({}, items[index], payload);
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import { createSelector } from 'reselect';
|
||||
import { isCommandExecuting } from 'Utilities/Command';
|
||||
import createCommandSelector from './createCommandSelector';
|
||||
|
||||
function createCommandExecutingSelector(name: string, contraints = {}) {
|
||||
return createSelector(createCommandSelector(name, contraints), (command) => {
|
||||
function createCommandExecutingSelector(name: string, constraints = {}) {
|
||||
return createSelector(createCommandSelector(name, constraints), (command) => {
|
||||
return isCommandExecuting(command);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import { createSelector } from 'reselect';
|
||||
import { findCommand } from 'Utilities/Command';
|
||||
import createCommandsSelector from './createCommandsSelector';
|
||||
|
||||
function createCommandSelector(name: string, contraints = {}) {
|
||||
function createCommandSelector(name: string, constraints = {}) {
|
||||
return createSelector(createCommandsSelector(), (commands) => {
|
||||
return findCommand(commands, { name, ...contraints });
|
||||
return findCommand(commands, { name, ...constraints });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export async function fetchTranslations(): Promise<boolean> {
|
||||
translations = data.Strings;
|
||||
|
||||
resolve(true);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import ModelBase from 'App/ModelBase';
|
||||
|
||||
export type HistoryQueryType =
|
||||
| 'search'
|
||||
| 'tvsearch'
|
||||
| 'movie'
|
||||
| 'book'
|
||||
| 'music';
|
||||
|
||||
export interface HistoryData {
|
||||
source: string;
|
||||
host: string;
|
||||
@@ -7,7 +14,7 @@ export interface HistoryData {
|
||||
offset: number;
|
||||
elapsedTime: number;
|
||||
query: string;
|
||||
queryType: string;
|
||||
queryType: HistoryQueryType;
|
||||
}
|
||||
|
||||
interface History extends ModelBase {
|
||||
|
||||
5
global.json
Normal file
5
global.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "8.0.405"
|
||||
}
|
||||
}
|
||||
38
package.json
38
package.json
@@ -23,17 +23,17 @@
|
||||
"defaults"
|
||||
],
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "6.6.0",
|
||||
"@fortawesome/fontawesome-svg-core": "6.6.0",
|
||||
"@fortawesome/free-regular-svg-icons": "6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "6.6.0",
|
||||
"@fortawesome/fontawesome-free": "6.7.2",
|
||||
"@fortawesome/fontawesome-svg-core": "6.7.2",
|
||||
"@fortawesome/free-regular-svg-icons": "6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "6.7.2",
|
||||
"@fortawesome/react-fontawesome": "0.2.2",
|
||||
"@juggle/resize-observer": "3.4.0",
|
||||
"@microsoft/signalr": "6.0.25",
|
||||
"@microsoft/signalr": "8.0.7",
|
||||
"@sentry/browser": "7.119.1",
|
||||
"@sentry/integrations": "7.119.1",
|
||||
"@types/node": "20.16.11",
|
||||
"@types/react": "18.2.79",
|
||||
"@types/react": "18.3.21",
|
||||
"@types/react-dom": "18.2.25",
|
||||
"chart.js": "4.4.4",
|
||||
"classnames": "2.5.1",
|
||||
@@ -71,9 +71,9 @@
|
||||
"react-router-dom": "5.2.0",
|
||||
"react-tabs": "4.3.0",
|
||||
"react-text-truncate": "0.19.0",
|
||||
"react-use-measure": "2.1.1",
|
||||
"react-virtualized": "9.21.1",
|
||||
"react-window": "1.8.10",
|
||||
"react-use-measure": "2.1.7",
|
||||
"react-virtualized": "9.22.6",
|
||||
"react-window": "1.8.11",
|
||||
"redux": "4.2.1",
|
||||
"redux-actions": "2.6.5",
|
||||
"redux-batched-actions": "0.5.0",
|
||||
@@ -81,30 +81,30 @@
|
||||
"redux-thunk": "2.4.2",
|
||||
"reselect": "4.1.8",
|
||||
"stacktrace-js": "2.0.2",
|
||||
"typescript": "5.1.6"
|
||||
"typescript": "5.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.25.8",
|
||||
"@babel/eslint-parser": "7.25.8",
|
||||
"@babel/plugin-proposal-export-default-from": "7.25.8",
|
||||
"@babel/core": "7.27.1",
|
||||
"@babel/eslint-parser": "7.27.1",
|
||||
"@babel/plugin-proposal-export-default-from": "7.27.1",
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@babel/preset-env": "7.25.8",
|
||||
"@babel/preset-react": "7.25.7",
|
||||
"@babel/preset-typescript": "7.25.7",
|
||||
"@babel/preset-env": "7.27.2",
|
||||
"@babel/preset-react": "7.27.1",
|
||||
"@babel/preset-typescript": "7.27.1",
|
||||
"@types/lodash": "4.14.195",
|
||||
"@types/react-document-title": "2.0.10",
|
||||
"@types/react-router-dom": "5.3.3",
|
||||
"@types/react-text-truncate": "0.19.0",
|
||||
"@types/react-window": "1.8.8",
|
||||
"@types/webpack-livereload-plugin": "2.3.6",
|
||||
"@typescript-eslint/eslint-plugin": "6.21.0",
|
||||
"@typescript-eslint/parser": "6.21.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.18.1",
|
||||
"@typescript-eslint/parser": "8.18.1",
|
||||
"are-you-es5": "2.1.2",
|
||||
"autoprefixer": "10.4.20",
|
||||
"babel-loader": "9.2.1",
|
||||
"babel-plugin-inline-classnames": "2.0.1",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
|
||||
"core-js": "3.38.1",
|
||||
"core-js": "3.42.0",
|
||||
"css-loader": "6.7.3",
|
||||
"css-modules-typescript-loader": "4.0.1",
|
||||
"eslint": "8.57.1",
|
||||
|
||||
@@ -99,13 +99,6 @@
|
||||
<RootNamespace Condition="'$(ProwlarrProject)'=='true'">$(MSBuildProjectName.Replace('Prowlarr','NzbDrone'))</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TestProject)'!='true'">
|
||||
<!-- Annotates .NET assemblies with repository information including SHA -->
|
||||
<!-- Sentry uses this to link directly to GitHub at the exact version/file/line -->
|
||||
<!-- This is built-in on .NET 8 and can be removed once the project is updated -->
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Sentry specific configuration: Only in Release mode -->
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<!-- https://docs.sentry.io/platforms/dotnet/configuration/msbuild/ -->
|
||||
@@ -136,7 +129,7 @@
|
||||
<PackageReference Include="NunitXml.TestLogger" Version="3.0.131" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TestProject)'=='true' and '$(TargetFramework)'=='net6.0'">
|
||||
<ItemGroup Condition="'$(TestProject)'=='true' and '$(TargetFramework)'=='net8.0'">
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.4-preview.27.ge7cb7c3b40" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -148,9 +141,9 @@
|
||||
<!-- Set up stylecop -->
|
||||
<ItemGroup Condition="'$(ProwlarrProject)'=='true' and '$(EnableAnalyzers)'!='false'">
|
||||
<!-- StyleCop analysis -->
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
||||
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.556">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<AdditionalFiles Include="$(SolutionDir)stylecop.json" />
|
||||
</ItemGroup>
|
||||
@@ -221,7 +214,7 @@
|
||||
<PropertyGroup Condition="'$(IsOSX)' == 'true' and
|
||||
'$(RuntimeIdentifier)' == ''">
|
||||
<_UsingDefaultRuntimeIdentifier>true</_UsingDefaultRuntimeIdentifier>
|
||||
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
||||
<RuntimeIdentifier>osx-$(Architecture)</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -39,15 +39,16 @@ namespace NzbDrone.Automation.Test
|
||||
var service = ChromeDriverService.CreateDefaultService();
|
||||
|
||||
// Timeout as windows automation tests seem to take alot longer to get going
|
||||
driver = new ChromeDriver(service, options, new TimeSpan(0, 3, 0));
|
||||
driver = new ChromeDriver(service, options, TimeSpan.FromMinutes(3));
|
||||
|
||||
driver.Manage().Window.Size = new System.Drawing.Size(1920, 1080);
|
||||
driver.Manage().Window.FullScreen();
|
||||
|
||||
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger(), null);
|
||||
_runner.KillAll();
|
||||
_runner.Start(true);
|
||||
|
||||
driver.Url = "http://localhost:9696";
|
||||
driver.Navigate().GoToUrl("http://localhost:9696");
|
||||
|
||||
var page = new PageBase(driver);
|
||||
page.WaitForNoSpinner();
|
||||
@@ -67,7 +68,7 @@ namespace NzbDrone.Automation.Test
|
||||
{
|
||||
try
|
||||
{
|
||||
var image = ((ITakesScreenshot)driver).GetScreenshot();
|
||||
var image = (driver as ITakesScreenshot).GetScreenshot();
|
||||
image.SaveAsFile($"./{name}_test_screenshot.png", ScreenshotImageFormat.Png);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -7,12 +7,11 @@ namespace NzbDrone.Automation.Test.PageModel
|
||||
{
|
||||
public class PageBase
|
||||
{
|
||||
private readonly WebDriver _driver;
|
||||
private readonly IWebDriver _driver;
|
||||
|
||||
public PageBase(WebDriver driver)
|
||||
public PageBase(IWebDriver driver)
|
||||
{
|
||||
_driver = driver;
|
||||
driver.Manage().Window.Maximize();
|
||||
}
|
||||
|
||||
public IWebElement FindByClass(string className, int timeout = 5)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Selenium.Support" Version="4.1.0" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="99.0.4844.5100" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="134.0.6998.16500" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Test.Common\Prowlarr.Test.Common.csproj" />
|
||||
|
||||
@@ -10,13 +10,13 @@ namespace NzbDrone.Common.Test.EnvironmentInfo
|
||||
[Test]
|
||||
public void should_return_version()
|
||||
{
|
||||
BuildInfo.Version.Major.Should().BeOneOf(0, 1, 10);
|
||||
BuildInfo.Version.Major.Should().BeOneOf(0, 2, 10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_get_branch()
|
||||
{
|
||||
BuildInfo.Branch.Should().NotBe("unknow");
|
||||
BuildInfo.Branch.Should().NotBe("unknown");
|
||||
BuildInfo.Branch.Should().NotBeNullOrWhiteSpace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,28 @@ namespace NzbDrone.Common.Test.ExtensionTests
|
||||
[TestCase("1.2.3.4")]
|
||||
[TestCase("172.55.0.1")]
|
||||
[TestCase("192.55.0.1")]
|
||||
[TestCase("100.64.0.1")]
|
||||
[TestCase("100.127.255.254")]
|
||||
public void should_return_false_for_public_ip_address(string ipAddress)
|
||||
{
|
||||
IPAddress.Parse(ipAddress).IsLocalAddress().Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("100.64.0.1")]
|
||||
[TestCase("100.127.255.254")]
|
||||
[TestCase("100.100.100.100")]
|
||||
public void should_return_true_for_cgnat_ip_address(string ipAddress)
|
||||
{
|
||||
IPAddress.Parse(ipAddress).IsCgnatIpAddress().Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("1.2.3.4")]
|
||||
[TestCase("192.168.5.1")]
|
||||
[TestCase("100.63.255.255")]
|
||||
[TestCase("100.128.0.0")]
|
||||
public void should_return_false_for_non_cgnat_ip_address(string ipAddress)
|
||||
{
|
||||
IPAddress.Parse(ipAddress).IsCgnatIpAddress().Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
private string _httpBinHost;
|
||||
private string _httpBinHost2;
|
||||
|
||||
private System.Net.Http.HttpClient _httpClient = new ();
|
||||
private System.Net.Http.HttpClient _httpClient = new();
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void FixtureSetUp()
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace NzbDrone.Common.Test.Http
|
||||
[TestCase("Readarr/1.0.0.2300 (ubuntu 20.04)", "Readarr")]
|
||||
[TestCase("Sonarr/3.0.6.9999 (ubuntu 20.04)", "Sonarr")]
|
||||
[TestCase("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", "Other")]
|
||||
[TestCase("appbrr", "appbrr")]
|
||||
[TestCase(" appbrr ", "appbrr")]
|
||||
public void should_parse_user_agent(string userAgent, string parsedAgent)
|
||||
{
|
||||
UserAgentParser.ParseSource(userAgent).Should().Be(parsedAgent);
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
[TestCase(@"https://anthelion.me/api.php?api_key=2b51db35e1910123321025a12b9933d2&o=json&t=movie&q=&tmdb=&imdb=&cat=&limit=100&offset=0")]
|
||||
[TestCase(@"https://avistaz.to/api/v1/jackett/auth: username=mySecret&password=mySecret&pid=mySecret")]
|
||||
[TestCase(@"https://www.sharewood.tv/api/2b51db35e1910123321025a12b9933d2/last-torrents")]
|
||||
[TestCase(@"https://example.org/rss/torrents?rsskey=2b51db35e1910123321025a12b9933d2&search=")]
|
||||
|
||||
// Indexer and Download Client Responses
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Host\Prowlarr.Host.csproj" />
|
||||
|
||||
@@ -42,17 +42,18 @@ namespace NzbDrone.Common
|
||||
|
||||
public void CreateZip(string path, IEnumerable<string> files)
|
||||
{
|
||||
using (var zipFile = ZipFile.Create(path))
|
||||
_logger.Debug("Creating archive {0}", path);
|
||||
|
||||
using var zipFile = ZipFile.Create(path);
|
||||
|
||||
zipFile.BeginUpdate();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
zipFile.BeginUpdate();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
zipFile.Add(file, Path.GetFileName(file));
|
||||
}
|
||||
|
||||
zipFile.CommitUpdate();
|
||||
zipFile.Add(file, Path.GetFileName(file));
|
||||
}
|
||||
|
||||
zipFile.CommitUpdate();
|
||||
}
|
||||
|
||||
private void ExtractZip(string compressedFile, string destination)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace NzbDrone.Common.Disk
|
||||
{
|
||||
@@ -24,10 +23,5 @@ namespace NzbDrone.Common.Disk
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected DestinationAlreadyExistsException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +189,25 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
|
||||
var fi = new FileInfo(path);
|
||||
|
||||
try
|
||||
{
|
||||
// If the file is a symlink, resolve the target path and get the size of the target file.
|
||||
if (fi.Attributes.HasFlag(FileAttributes.ReparsePoint))
|
||||
{
|
||||
var targetPath = fi.ResolveLinkTarget(true)?.FullName;
|
||||
|
||||
if (targetPath != null)
|
||||
{
|
||||
fi = new FileInfo(targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.Trace(ex, "Unable to resolve symlink target for {0}", path);
|
||||
}
|
||||
|
||||
return fi.Length;
|
||||
}
|
||||
|
||||
|
||||
@@ -295,7 +295,7 @@ namespace NzbDrone.Common.Disk
|
||||
return _path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public static OsPath Null => new (null);
|
||||
public static OsPath Null => new(null);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -75,6 +75,17 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
{
|
||||
try
|
||||
{
|
||||
if (OsInfo.IsOsx)
|
||||
{
|
||||
var userAppDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile, Environment.SpecialFolderOption.DoNotVerify), ".config", "Prowlarr");
|
||||
|
||||
if (_diskProvider.FolderExists(userAppDataFolder) && !_diskProvider.FileExists(_appFolderInfo.GetConfigPath()))
|
||||
{
|
||||
_diskTransferService.MirrorFolder(userAppDataFolder, _appFolderInfo.AppDataFolder);
|
||||
_diskProvider.DeleteFolder(userAppDataFolder, true);
|
||||
}
|
||||
}
|
||||
|
||||
var oldDbFile = Path.Combine(_appFolderInfo.AppDataFolder, "nzbdrone.db");
|
||||
|
||||
if (_startupContext.Args.ContainsKey(StartupContext.APPDATA))
|
||||
@@ -115,7 +126,7 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Debug(ex, ex.Message);
|
||||
throw new ProwlarrStartupException("Unable to migrate DB from nzbdrone.db to {0}. Migrate manually", _appFolderInfo.GetDatabase());
|
||||
throw new ProwlarrStartupException(ex, "Unable to migrate DB from nzbdrone.db to {0}. Migrate manually", _appFolderInfo.GetDatabase());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
|
||||
var attributes = assembly.GetCustomAttributes(true);
|
||||
|
||||
Branch = "unknow";
|
||||
Branch = "unknown";
|
||||
|
||||
var config = attributes.OfType<AssemblyConfigurationAttribute>().FirstOrDefault();
|
||||
if (config != null)
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace NzbDrone.Common.Extensions
|
||||
}
|
||||
|
||||
public static IDictionary<TNewKey, TNewValue> SelectDictionary<TKey, TValue, TNewKey, TNewValue>(this IDictionary<TKey, TValue> dictionary,
|
||||
Func<KeyValuePair<TKey, TValue>, ValueTuple<TNewKey, TNewValue>> selection)
|
||||
Func<KeyValuePair<TKey, TValue>, (TNewKey Item1, TNewValue Item2)> selection)
|
||||
{
|
||||
return dictionary.Select(selection).ToDictionary(t => t.Item1, t => t.Item2);
|
||||
}
|
||||
|
||||
@@ -39,18 +39,24 @@ namespace NzbDrone.Common.Extensions
|
||||
private static bool IsLocalIPv4(byte[] ipv4Bytes)
|
||||
{
|
||||
// Link local (no IP assigned by DHCP): 169.254.0.0 to 169.254.255.255 (169.254.0.0/16)
|
||||
bool IsLinkLocal() => ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254;
|
||||
var isLinkLocal = ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254;
|
||||
|
||||
// Class A private range: 10.0.0.0 – 10.255.255.255 (10.0.0.0/8)
|
||||
bool IsClassA() => ipv4Bytes[0] == 10;
|
||||
var isClassA = ipv4Bytes[0] == 10;
|
||||
|
||||
// Class B private range: 172.16.0.0 – 172.31.255.255 (172.16.0.0/12)
|
||||
bool IsClassB() => ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31;
|
||||
var isClassB = ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31;
|
||||
|
||||
// Class C private range: 192.168.0.0 – 192.168.255.255 (192.168.0.0/16)
|
||||
bool IsClassC() => ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168;
|
||||
var isClassC = ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168;
|
||||
|
||||
return IsLinkLocal() || IsClassA() || IsClassC() || IsClassB();
|
||||
return isLinkLocal || isClassA || isClassC || isClassB;
|
||||
}
|
||||
|
||||
public static bool IsCgnatIpAddress(this IPAddress ipAddress)
|
||||
{
|
||||
var bytes = ipAddress.GetAddressBytes();
|
||||
return bytes.Length == 4 && bytes[0] == 100 && bytes[1] >= 64 && bytes[1] <= 127;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
||||
// NOTE: we are not checking non-ascii characters and we should
|
||||
private static readonly Regex CookieRegex = new (@"([^\(\)<>@,;:\\""/\[\]\?=\{\}\s]+)=([^,;\\""\s]+)");
|
||||
private static readonly Regex CookieRegex = new(@"([^\(\)<>@,;:\\""/\[\]\?=\{\}\s]+)=([^,;\\""\s]+)");
|
||||
private static readonly string[] FilterProps = { "COMMENT", "COMMENTURL", "DISCORD", "DOMAIN", "EXPIRES", "MAX-AGE", "PATH", "PORT", "SECURE", "VERSION", "HTTPONLY", "SAMESITE" };
|
||||
private static readonly char[] InvalidKeyChars = { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t', '\n' };
|
||||
private static readonly char[] InvalidValueChars = { '"', ',', ';', '\\', ' ', '\t', '\n' };
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
}
|
||||
catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
|
||||
{
|
||||
throw new WebException("Http request timed out", ex.InnerException, WebExceptionStatus.Timeout, null);
|
||||
throw new WebException("Http request timed out", ex, WebExceptionStatus.Timeout, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace NzbDrone.Common.Http
|
||||
// Save to add to final response
|
||||
var responseCookies = response.Cookies;
|
||||
|
||||
// Update cookiecontainer for next request with any cookies recieved on last request
|
||||
// Update cookie container for next request with any cookies received on last request
|
||||
var responseContainer = HandleRedirectCookies(request, response);
|
||||
|
||||
response = await ExecuteRequestAsync(request, responseContainer);
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class HttpResponse
|
||||
{
|
||||
private static readonly Regex RegexRefresh = new ("^(.*?url)=(.*?)(?:;|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex RegexRefresh = new("^(.*?url)=(.*?)(?:;|$)", RegexOptions.Compiled);
|
||||
|
||||
public HttpResponse(HttpRequest request, HttpHeader headers, CookieCollection cookies, byte[] binaryData, long elapsedTime = 0, HttpStatusCode statusCode = HttpStatusCode.OK, Version version = null)
|
||||
{
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public static class UserAgentParser
|
||||
{
|
||||
private static readonly Regex AppSourceRegex = new Regex(@"(?<agent>[a-z0-9]*)\/.*(?:\(.*\))?",
|
||||
private static readonly Regex AppSourceRegex = new(@"^(?<agent>[a-z0-9]+)(?:\/.+(?:\(.*\))?|$)",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
public static string SimplifyUserAgent(string userAgent)
|
||||
{
|
||||
if (userAgent == null || userAgent.StartsWith("Mozilla/5.0"))
|
||||
if (userAgent == null || userAgent.StartsWith("Mozilla/5.0", StringComparison.Ordinal))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -19,14 +20,9 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
public static string ParseSource(string userAgent)
|
||||
{
|
||||
var match = AppSourceRegex.Match(SimplifyUserAgent(userAgent) ?? string.Empty);
|
||||
var match = AppSourceRegex.Match(SimplifyUserAgent(userAgent?.Trim()) ?? string.Empty);
|
||||
|
||||
if (match.Groups["agent"].Success)
|
||||
{
|
||||
return match.Groups["agent"].Value;
|
||||
}
|
||||
|
||||
return "Other";
|
||||
return match.Groups["agent"].Success ? match.Groups["agent"].Value : "Other";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,64 +10,64 @@ namespace NzbDrone.Common.Instrumentation
|
||||
private static readonly Regex[] CleansingRules =
|
||||
{
|
||||
// Url
|
||||
new (@"(?<=[?&: ;])(apikey|api_key|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pid|pwd)=(?<secret>[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"(?<=[?& ;])[^=]*?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"rss(24h)?\.torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
|
||||
new (@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"(?<=authkey = "")(?<secret>[^&=]+?)(?="")", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"(?<=beyond-hd\.[a-z]+/api/torrents/)(?<secret>[^&=][a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"(?<=beyond-hd\.[a-z]+/torrent/download/[\w\d-]+[.]\d+[.])(?<secret>[a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"(?:sharewood)\.[a-z]{2,3}/api/(?<secret>[a-z0-9]{16,})/", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?<=[?&: ;])(apikey|api_key|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|rsskey|user|u?id|api|[a-z_]*apikey|account|pid|pwd)=(?<secret>[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?<=[?& ;])[^=]*?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"rss(24h)?\.torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
|
||||
new(@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?<=authkey = "")(?<secret>[^&=]+?)(?="")", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?<=beyond-hd\.[a-z]+/api/torrents/)(?<secret>[^&=][a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?<=beyond-hd\.[a-z]+/torrent/download/[\w\d-]+[.]\d+[.])(?<secret>[a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?:sharewood)\.[a-z]{2,3}/api/(?<secret>[a-z0-9]{16,})/", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// UNIT3D
|
||||
new (@"(?<=[a-z0-9-]+\.[a-z]+/torrent/download/\d+\.)(?<secret>[^&=][a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?<=[a-z0-9-]+\.[a-z]+/torrent/download/\d+\.)(?<secret>[^&=][a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Path
|
||||
new (@"""C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"""/(home|Users)/(?<secret>[^/""]+?)(/|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"""C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"""/(home|Users)/(?<secret>[^/""]+?)(/|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
|
||||
new (@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// NzbGet
|
||||
new (@"""Name""\s*:\s*""[^""]*(username|password)""\s*,\s*""Value""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"""Name""\s*:\s*""[^""]*(username|password)""\s*,\s*""Value""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Sabnzbd
|
||||
new (@"""[^""]*(username|password|api_?key|nzb_key)""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"""email_(account|to|from|pwd)""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"""[^""]*(username|password|api_?key|nzb_key)""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"""email_(account|to|from|pwd)""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// uTorrent
|
||||
new (@"\[""[a-z._]*(username|password)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"\[""(boss_key|boss_key_salt|proxy\.proxy)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"\[""[a-z._]*(username|password)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"\[""(boss_key|boss_key_salt|proxy\.proxy)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Deluge
|
||||
new (@"auth.login\(""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"auth.login\(""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// BroadcastheNet (;torrent_pass|torrents_notify_ is for MTV)
|
||||
new (@"""?method""?\s*:\s*""(getTorrents)"",\s*""?params""?\s*:\s*\[\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"getTorrents\(""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"(?<=\?|&|;|=)(authkey|torrent_pass|torrents_notify)[_=](?<secret>[^&=]+?)(?=""|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"""?method""?\s*:\s*""(getTorrents)"",\s*""?params""?\s*:\s*\[\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"getTorrents\(""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?<=\?|&|;|=)(authkey|torrent_pass|torrents_notify)[_=](?<secret>[^&=]+?)(?=""|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Plex
|
||||
new (@"(?<=\?|&)(X-Plex-Client-Identifier|X-Plex-Token)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?<=\?|&)(X-Plex-Client-Identifier|X-Plex-Token)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Indexer Responses
|
||||
new (@"(?:avistaz|exoticaz|cinemaz|privatehd)\.[a-z]{2,3}/rss/download/(?<secret>[^&=]+?)/(?<secret>[^&=]+?)\.torrent", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"(?:animebytes)\.[a-z]{2,3}/torrent/[0-9]+/download/(?<secret>[^&=]+?)[""]", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"""(info_hash|token|((pass|rss)[- _]?key))"":""(?<secret>[^&=]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?:avistaz|exoticaz|cinemaz|privatehd)\.[a-z]{2,3}/rss/download/(?<secret>[^&=]+?)/(?<secret>[^&=]+?)\.torrent", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"(?:animebytes)\.[a-z]{2,3}/torrent/[0-9]+/download/(?<secret>[^&=]+?)[""]", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"""(info_hash|token|((pass|rss)[- _]?key))"":""(?<secret>[^&=]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Applications
|
||||
new (@"""name"":""apikey"",""value"":""(?<secret>[^&=]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new(@"""name"":""apikey"",""value"":""(?<secret>[^&=]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Discord
|
||||
new (@"discord.com/api/webhooks/((?<secret>[\w-]+)/)?(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase)
|
||||
new(@"discord.com/api/webhooks/((?<secret>[\w-]+)/)?(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase)
|
||||
};
|
||||
|
||||
private static readonly Regex CleanseRemoteIPRegex = new (@"(?:Auth-\w+(?<!Failure|Unauthorized) ip|from) (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Compiled);
|
||||
private static readonly Regex CleanseRemoteIPRegex = new(@"(?:Auth-\w+(?<!Failure|Unauthorized) ip|from) (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Compiled);
|
||||
|
||||
public static string Cleanse(string message)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NLog.Layouts.ClefJsonLayout;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Common.Instrumentation;
|
||||
|
||||
public class CleansingClefLogLayout : CompactJsonLayout
|
||||
{
|
||||
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
|
||||
{
|
||||
base.RenderFormattedMessage(logEvent, target);
|
||||
|
||||
if (RuntimeInfo.IsProduction)
|
||||
{
|
||||
var result = CleanseLogMessage.Cleanse(target.ToString());
|
||||
target.Clear();
|
||||
target.Append(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NLog.Layouts;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Common.Instrumentation;
|
||||
|
||||
public class CleansingConsoleLogLayout : SimpleLayout
|
||||
{
|
||||
public CleansingConsoleLogLayout(string format)
|
||||
: base(format)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
|
||||
{
|
||||
base.RenderFormattedMessage(logEvent, target);
|
||||
|
||||
if (RuntimeInfo.IsProduction)
|
||||
{
|
||||
var result = CleanseLogMessage.Cleanse(target.ToString());
|
||||
target.Clear();
|
||||
target.Append(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using NLog.Targets;
|
||||
|
||||
namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
public class NzbDroneFileTarget : FileTarget
|
||||
public class CleansingFileTarget : FileTarget
|
||||
{
|
||||
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
|
||||
{
|
||||
@@ -4,27 +4,27 @@ namespace NzbDrone.Common.Instrumentation.Extensions
|
||||
{
|
||||
public static class LoggerExtensions
|
||||
{
|
||||
[MessageTemplateFormatMethod("message")]
|
||||
public static void ProgressInfo(this Logger logger, string message, params object[] args)
|
||||
{
|
||||
var formattedMessage = string.Format(message, args);
|
||||
LogProgressMessage(logger, LogLevel.Info, formattedMessage);
|
||||
LogProgressMessage(logger, LogLevel.Info, message, args);
|
||||
}
|
||||
|
||||
[MessageTemplateFormatMethod("message")]
|
||||
public static void ProgressDebug(this Logger logger, string message, params object[] args)
|
||||
{
|
||||
var formattedMessage = string.Format(message, args);
|
||||
LogProgressMessage(logger, LogLevel.Debug, formattedMessage);
|
||||
LogProgressMessage(logger, LogLevel.Debug, message, args);
|
||||
}
|
||||
|
||||
[MessageTemplateFormatMethod("message")]
|
||||
public static void ProgressTrace(this Logger logger, string message, params object[] args)
|
||||
{
|
||||
var formattedMessage = string.Format(message, args);
|
||||
LogProgressMessage(logger, LogLevel.Trace, formattedMessage);
|
||||
LogProgressMessage(logger, LogLevel.Trace, message, args);
|
||||
}
|
||||
|
||||
private static void LogProgressMessage(Logger logger, LogLevel level, string message)
|
||||
private static void LogProgressMessage(Logger logger, LogLevel level, string message, object[] parameters)
|
||||
{
|
||||
var logEvent = new LogEventInfo(level, logger.Name, message);
|
||||
var logEvent = new LogEventInfo(level, logger.Name, null, message, parameters);
|
||||
logEvent.Properties.Add("Status", "");
|
||||
|
||||
logger.Log(logEvent);
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Layouts.ClefJsonLayout;
|
||||
using NLog.Targets;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -13,9 +12,11 @@ namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
public static class NzbDroneLogger
|
||||
{
|
||||
private const string FILE_LOG_LAYOUT = @"${date:format=yyyy-MM-dd HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
public const string ConsoleLogLayout = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
public static CompactJsonLayout ClefLogLayout = new CompactJsonLayout();
|
||||
private const string FileLogLayout = @"${date:format=yyyy-MM-dd HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
private const string ConsoleFormat = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
|
||||
private static readonly CleansingConsoleLogLayout CleansingConsoleLayout = new(ConsoleFormat);
|
||||
private static readonly CleansingClefLogLayout ClefLogLayout = new();
|
||||
|
||||
private static bool _isConfigured;
|
||||
|
||||
@@ -119,11 +120,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
? formatEnumValue
|
||||
: ConsoleLogFormat.Standard;
|
||||
|
||||
coloredConsoleTarget.Layout = logFormat switch
|
||||
{
|
||||
ConsoleLogFormat.Clef => ClefLogLayout,
|
||||
_ => ConsoleLogLayout
|
||||
};
|
||||
ConfigureConsoleLayout(coloredConsoleTarget, logFormat);
|
||||
|
||||
var loggingRule = new LoggingRule("*", level, coloredConsoleTarget);
|
||||
|
||||
@@ -140,7 +137,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
|
||||
private static void RegisterAppFile(IAppFolderInfo appFolderInfo, string name, string fileName, int maxArchiveFiles, LogLevel minLogLevel)
|
||||
{
|
||||
var fileTarget = new NzbDroneFileTarget();
|
||||
var fileTarget = new CleansingFileTarget();
|
||||
|
||||
fileTarget.Name = name;
|
||||
fileTarget.FileName = Path.Combine(appFolderInfo.GetLogFolder(), fileName);
|
||||
@@ -153,7 +150,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
fileTarget.MaxArchiveFiles = maxArchiveFiles;
|
||||
fileTarget.EnableFileDelete = true;
|
||||
fileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling;
|
||||
fileTarget.Layout = FILE_LOG_LAYOUT;
|
||||
fileTarget.Layout = FileLogLayout;
|
||||
|
||||
var loggingRule = new LoggingRule("*", minLogLevel, fileTarget);
|
||||
|
||||
@@ -172,7 +169,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
fileTarget.ConcurrentWrites = false;
|
||||
fileTarget.ConcurrentWriteAttemptDelay = 50;
|
||||
fileTarget.ConcurrentWriteAttempts = 100;
|
||||
fileTarget.Layout = FILE_LOG_LAYOUT;
|
||||
fileTarget.Layout = FileLogLayout;
|
||||
|
||||
var loggingRule = new LoggingRule("*", LogLevel.Trace, fileTarget);
|
||||
|
||||
@@ -217,6 +214,15 @@ namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
return GetLogger(obj.GetType());
|
||||
}
|
||||
|
||||
public static void ConfigureConsoleLayout(ColoredConsoleTarget target, ConsoleLogFormat format)
|
||||
{
|
||||
target.Layout = format switch
|
||||
{
|
||||
ConsoleLogFormat.Clef => NzbDroneLogger.ClefLogLayout,
|
||||
_ => NzbDroneLogger.CleansingConsoleLayout
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConsoleLogFormat
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
o.Environment = BuildInfo.Branch;
|
||||
|
||||
// Crash free run statistics (sends a ping for healthy and for crashes sessions)
|
||||
o.AutoSessionTracking = true;
|
||||
o.AutoSessionTracking = false;
|
||||
|
||||
// Caches files in the event device is offline
|
||||
// Sentry creates a 'sentry' sub directory, no need to concat here
|
||||
@@ -148,7 +148,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
_debounce = new SentryDebounce();
|
||||
|
||||
// initialize to true and reconfigure later
|
||||
// Otherwise it will default to false and any errors occuring
|
||||
// Otherwise it will default to false and any errors occurring
|
||||
// before config file gets read will not be filtered
|
||||
FilterEvents = true;
|
||||
SentryEnabled = true;
|
||||
@@ -207,9 +207,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
|
||||
private void OnError(Exception ex)
|
||||
{
|
||||
var webException = ex as WebException;
|
||||
|
||||
if (webException != null)
|
||||
if (ex is WebException webException)
|
||||
{
|
||||
var response = webException.Response as HttpWebResponse;
|
||||
var statusCode = response?.StatusCode;
|
||||
|
||||
@@ -6,4 +6,5 @@ public class AuthOptions
|
||||
public bool? Enabled { get; set; }
|
||||
public string Method { get; set; }
|
||||
public string Required { get; set; }
|
||||
public bool? TrustCgnatIpAddresses { get; set; }
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Model;
|
||||
@@ -117,7 +118,9 @@ namespace NzbDrone.Common.Processes
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardInput = true
|
||||
RedirectStandardInput = true,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
StandardErrorEncoding = Encoding.UTF8
|
||||
};
|
||||
|
||||
if (environmentVariables != null)
|
||||
@@ -313,7 +316,7 @@ namespace NzbDrone.Common.Processes
|
||||
processInfo = new ProcessInfo();
|
||||
processInfo.Id = process.Id;
|
||||
processInfo.Name = process.ProcessName;
|
||||
processInfo.StartPath = process.MainModule.FileName;
|
||||
processInfo.StartPath = process.MainModule?.FileName;
|
||||
|
||||
if (process.Id != GetCurrentProcessId() && process.HasExited)
|
||||
{
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
<DefineConstants Condition="'$(RuntimeIdentifier)' == 'linux-musl-x64' or '$(RuntimeIdentifier)' == 'linux-musl-arm64'">ISMUSL</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DryIoc.dll" Version="5.4.3" />
|
||||
<PackageReference Include="IPAddressRange" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.2" />
|
||||
<PackageReference Include="IPAddressRange" Version="6.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NLog" Version="5.3.3" />
|
||||
<PackageReference Include="NLog.Layouts.ClefJsonLayout" Version="1.0.0" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.12" />
|
||||
<PackageReference Include="Npgsql" Version="7.0.8" />
|
||||
<PackageReference Include="NLog" Version="5.4.0" />
|
||||
<PackageReference Include="NLog.Layouts.ClefJsonLayout" Version="1.0.3" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.4.0" />
|
||||
<PackageReference Include="Npgsql" Version="9.0.3" />
|
||||
<PackageReference Include="Sentry" Version="4.0.2" />
|
||||
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
|
||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.1" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="6.0.1" />
|
||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="EnsureThat\Resources\ExceptionMessages.Designer.cs">
|
||||
|
||||
@@ -94,7 +94,8 @@ namespace NzbDrone.Common.TPL
|
||||
{
|
||||
_currentThreadIsProcessingItems = false;
|
||||
}
|
||||
}, null);
|
||||
},
|
||||
null);
|
||||
}
|
||||
|
||||
/// <summary>Attempts to execute the specified task on the current thread.</summary>
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace NzbDrone.Common.TPL
|
||||
Logger.Error(exception, "Task Error");
|
||||
}
|
||||
}
|
||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
||||
},
|
||||
TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
|
||||
<ApplicationIcon>..\NzbDrone.Host\Prowlarr.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Data.SQLite;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters;
|
||||
|
||||
[TestFixture]
|
||||
public class TimeSpanConverterFixture : CoreTest<TimeSpanConverter>
|
||||
{
|
||||
private SQLiteParameter _param;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_param = new SQLiteParameter();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_string_when_saving_timespan_to_db()
|
||||
{
|
||||
var span = TimeSpan.FromMilliseconds(10);
|
||||
|
||||
Subject.SetValue(_param, span);
|
||||
_param.Value.Should().Be(span.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_timespan_when_getting_string_from_db()
|
||||
{
|
||||
var span = TimeSpan.FromMilliseconds(10);
|
||||
|
||||
Subject.Parse(span.ToString()).Should().Be(span);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_zero_timespan_for_db_null_value_when_getting_from_db()
|
||||
{
|
||||
Subject.Parse(null).Should().Be(TimeSpan.Zero);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore;
|
||||
|
||||
[TestFixture]
|
||||
public class DatabaseVersionParserFixture
|
||||
{
|
||||
[TestCase("3.44.2", 3, 44, 2)]
|
||||
public void should_parse_sqlite_database_version(string serverVersion, int majorVersion, int minorVersion, int buildVersion)
|
||||
{
|
||||
var version = DatabaseVersionParser.ParseServerVersion(serverVersion);
|
||||
|
||||
version.Should().NotBeNull();
|
||||
version.Major.Should().Be(majorVersion);
|
||||
version.Minor.Should().Be(minorVersion);
|
||||
version.Build.Should().Be(buildVersion);
|
||||
}
|
||||
|
||||
[TestCase("14.8 (Debian 14.8-1.pgdg110+1)", 14, 8, null)]
|
||||
[TestCase("16.3 (Debian 16.3-1.pgdg110+1)", 16, 3, null)]
|
||||
[TestCase("16.3 - Percona Distribution", 16, 3, null)]
|
||||
[TestCase("17.0 - Percona Server", 17, 0, null)]
|
||||
public void should_parse_postgres_database_version(string serverVersion, int majorVersion, int minorVersion, int? buildVersion)
|
||||
{
|
||||
var version = DatabaseVersionParser.ParseServerVersion(serverVersion);
|
||||
|
||||
version.Should().NotBeNull();
|
||||
version.Major.Should().Be(majorVersion);
|
||||
version.Minor.Should().Be(minorVersion);
|
||||
|
||||
if (buildVersion.HasValue)
|
||||
{
|
||||
version.Build.Should().Be(buildVersion.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,15 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<caps>
|
||||
<server version="1.0" title="Anime Tosho" strapline="Anime NZB/DDL mirror" url="https://animetosho.org/"/>
|
||||
<limits max="200" default="75"/>
|
||||
<retention days="9999"/>
|
||||
<registration available="no" open="yes" />
|
||||
<searching>
|
||||
<search available="yes" supportedParams="q" />
|
||||
<tv-search available="no" supportedParams="q" />
|
||||
<movie-search available="no" supportedParams="q" />
|
||||
</searching>
|
||||
<categories>
|
||||
<category id="5070" name="Anime" description="Anime"/>
|
||||
</categories>
|
||||
</caps>
|
||||
<server version="1.0" title="Anime Tosho" strapline="Anime NZB/DDL mirror" url="https://animetosho.org/" />
|
||||
<limits max="200" default="75" />
|
||||
<retention days="9999" />
|
||||
<registration available="no" open="yes" />
|
||||
<searching>
|
||||
<search available="yes" supportedParams="q" />
|
||||
<tv-search available="no" supportedParams="q" />
|
||||
<movie-search available="no" supportedParams="q" />
|
||||
</searching>
|
||||
<categories>
|
||||
<category id="5070" name="Anime" description="Anime" />
|
||||
<category id="2020" name="Movies/Other" description="Movies (Anime)" />
|
||||
</categories>
|
||||
</caps>
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests
|
||||
|
||||
var fifthTorrentInfo = releases.ElementAt(28) as TorrentInfo;
|
||||
|
||||
fifthTorrentInfo.Title.Should().Be("[-ZR-] Dr. STONE: STONE WARS S02 [Web][MKV][h264][1080p][AAC 2.0][Dual Audio][Softsubs (-ZR-)]");
|
||||
fifthTorrentInfo.Title.Should().Be("[-ZR-] Dr. STONE: STONE WARS 2021 S02 [Web][MKV][h264][1080p][AAC 2.0][Dual Audio][Softsubs (-ZR-)]");
|
||||
fifthTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
fifthTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/944509/download/somepass");
|
||||
fifthTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/944509/group");
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://filelist.io/details.php?id=665873");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2020-01-25 20:20:19"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2020-01-25 19:20:19"));
|
||||
torrentInfo.Size.Should().Be(8300512414);
|
||||
torrentInfo.InfoHash.Should().Be(null);
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -26,15 +26,15 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
Subject.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "HdBits",
|
||||
Settings = new HDBitsSettings() { ApiKey = "fakekey" }
|
||||
Settings = new HDBitsSettings { ApiKey = "fakekey" }
|
||||
};
|
||||
|
||||
_movieSearchCriteria = new MovieSearchCriteria
|
||||
{
|
||||
Categories = new int[] { 2000, 2010 },
|
||||
Categories = new[] { 2000, 2010 },
|
||||
ImdbId = "0076759"
|
||||
};
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
|
||||
var torrents = (await Subject.Fetch(_movieSearchCriteria)).Releases;
|
||||
|
||||
torrents.Should().HaveCount(2);
|
||||
torrents.First().Should().BeOfType<HDBitsInfo>();
|
||||
torrents.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
var first = torrents.First() as TorrentInfo;
|
||||
|
||||
|
||||
@@ -83,17 +83,18 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
bookCats.Should().Contain("8000");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_find_sub_categories_as_main_categories()
|
||||
[TestCase(5070)]
|
||||
[TestCase(2020)]
|
||||
public void should_find_sub_categories_as_main_categories(int category)
|
||||
{
|
||||
GivenCapsResponse(ReadAllText("Files/Indexers/Torznab/torznab_animetosho_caps.xml"));
|
||||
|
||||
var caps = Subject.GetCapabilities(_settings, _definition);
|
||||
|
||||
var bookCats = caps.Categories.MapTrackerCatToNewznab("5070");
|
||||
var indexerCategories = caps.Categories.MapTrackerCatToNewznab(category.ToString());
|
||||
|
||||
bookCats.Count.Should().Be(2);
|
||||
bookCats.First().Id.Should().Be(5070);
|
||||
indexerCategories.Count.Should().Be(2);
|
||||
indexerCategories.First().Id.Should().Be(category);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace NzbDrone.Core.Test.IndexerTests.PTPTests
|
||||
first.Guid.Should().Be("PassThePopcorn-452135");
|
||||
first.Title.Should().Be("The.Night.Of.S01.BluRay.AAC2.0.x264-DEPTH");
|
||||
first.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
first.DownloadUrl.Should().Be("https://passthepopcorn.me/torrents.php?action=download&id=452135&authkey=00000000000000000000000000000000&torrent_pass=00000000000000000000000000000000");
|
||||
first.DownloadUrl.Should().Be("https://passthepopcorn.me/torrents.php?action=download&id=452135");
|
||||
first.InfoUrl.Should().Be("https://passthepopcorn.me/torrents.php?id=148131&torrentid=452135");
|
||||
|
||||
//first.PublishDate.Should().Be(DateTime.Parse("2017-04-17T12:13:42+0000").ToUniversalTime()); stupid timezones
|
||||
|
||||
@@ -46,8 +46,8 @@ namespace NzbDrone.Core.Test.IndexerTests.RedactedTests
|
||||
|
||||
torrentInfo.Title.Should().Be("Red Hot Chili Peppers - Californication (1999) [Album] [US / Reissue 2020] [FLAC 24bit Lossless / Vinyl]");
|
||||
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
torrentInfo.DownloadUrl.Should().Be("https://redacted.ch/ajax.php?action=download&id=3892313");
|
||||
torrentInfo.InfoUrl.Should().Be("https://redacted.ch/torrents.php?id=16720&torrentid=3892313");
|
||||
torrentInfo.DownloadUrl.Should().Be("https://redacted.sh/ajax.php?action=download&id=3892313");
|
||||
torrentInfo.InfoUrl.Should().Be("https://redacted.sh/torrents.php?id=16720&torrentid=3892313");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-12-17 08:02:35"));
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" Version="2.0.151" />
|
||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||
<PackageReference Include="NBuilder" Version="6.1.0" />
|
||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
|
||||
<PackageReference Include="YamlDotNet" Version="13.1.1" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Test.Common\Prowlarr.Test.Common.csproj" />
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace NzbDrone.Core.Applications
|
||||
protected readonly IAppIndexerMapService _appIndexerMapService;
|
||||
protected readonly Logger _logger;
|
||||
|
||||
protected static readonly Regex AppIndexerRegex = new (@"\/(?<indexer>\d{1,3})(?:\/(?:api)?\/?)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
protected static readonly Regex AppIndexerRegex = new(@"\/(?<indexer>\d{1,3})(?:\/(?:api)?\/?)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
public abstract string Name { get; }
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace NzbDrone.Core.Applications
|
||||
|
||||
foreach (var application in applications)
|
||||
{
|
||||
if (blockedApplications.TryGetValue(application.Definition.Id, out var blockedApplicationStatus))
|
||||
if (blockedApplications.TryGetValue(application.Definition.Id, out var blockedApplicationStatus) && blockedApplicationStatus.DisabledTill.HasValue)
|
||||
{
|
||||
_logger.Debug("Temporarily ignoring application {0} till {1} due to recent failures.", application.Definition.Name, blockedApplicationStatus.DisabledTill.Value.ToLocalTime());
|
||||
continue;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
|
||||
public class LazyLibrarianSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly LazyLibrarianSettingsValidator Validator = new ();
|
||||
private static readonly LazyLibrarianSettingsValidator Validator = new();
|
||||
|
||||
public LazyLibrarianSettings()
|
||||
{
|
||||
|
||||
@@ -121,9 +121,16 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
var indexerCapabilities = GetIndexerCapabilities(indexer);
|
||||
|
||||
if (!indexerCapabilities.MusicSearchAvailable && !indexerCapabilities.SearchAvailable)
|
||||
{
|
||||
_logger.Debug("Skipping add for indexer {0} [{1}] due to missing music or basic search support by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
_logger.Debug("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -178,7 +185,8 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
_logger.Debug("Syncing remote indexer with current settings");
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if ((indexerCapabilities.MusicSearchAvailable || indexerCapabilities.SearchAvailable) &&
|
||||
indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
// Retain user fields not-affiliated with Prowlarr
|
||||
lidarrIndexer.Fields.AddRange(remoteIndexer.Fields.Where(f => lidarrIndexer.Fields.All(s => s.Name != f.Name)));
|
||||
@@ -204,7 +212,8 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if ((indexerCapabilities.MusicSearchAvailable || indexerCapabilities.SearchAvailable) &&
|
||||
indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to Lidarr", indexer.Name, indexer.Id);
|
||||
lidarrIndexer.Id = 0;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
public class LidarrSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly LidarrSettingsValidator Validator = new ();
|
||||
private static readonly LidarrSettingsValidator Validator = new();
|
||||
|
||||
public LidarrSettings()
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
public class LidarrV1Proxy : ILidarrV1Proxy
|
||||
{
|
||||
private static Version MinimumApplicationVersion => new (1, 0, 2, 0);
|
||||
private static Version MinimumApplicationVersion => new(1, 0, 2, 0);
|
||||
|
||||
private const string AppApiRoute = "/api/v1";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
|
||||
public class MylarSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly MylarSettingsValidator Validator = new ();
|
||||
private static readonly MylarSettingsValidator Validator = new();
|
||||
|
||||
public MylarSettings()
|
||||
{
|
||||
|
||||
@@ -121,9 +121,16 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
var indexerCapabilities = GetIndexerCapabilities(indexer);
|
||||
|
||||
if (!indexerCapabilities.MovieSearchAvailable && !indexerCapabilities.SearchAvailable)
|
||||
{
|
||||
_logger.Debug("Skipping add for indexer {0} [{1}] due to missing movie or basic search support by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
_logger.Debug("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -176,7 +183,8 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
if (!radarrIndexer.Equals(remoteIndexer) || forceSync)
|
||||
{
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if ((indexerCapabilities.MovieSearchAvailable || indexerCapabilities.SearchAvailable) &&
|
||||
indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
// Retain user fields not-affiliated with Prowlarr
|
||||
radarrIndexer.Fields.AddRange(remoteIndexer.Fields.Where(f => radarrIndexer.Fields.All(s => s.Name != f.Name)));
|
||||
@@ -202,7 +210,8 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if ((indexerCapabilities.MovieSearchAvailable || indexerCapabilities.SearchAvailable) &&
|
||||
indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to Radarr", indexer.Name, indexer.Id);
|
||||
radarrIndexer.Id = 0;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
public class RadarrSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly RadarrSettingsValidator Validator = new ();
|
||||
private static readonly RadarrSettingsValidator Validator = new();
|
||||
|
||||
public RadarrSettings()
|
||||
{
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
public class RadarrV3Proxy : IRadarrV3Proxy
|
||||
{
|
||||
private static Version MinimumApplicationV4Version => new (4, 0, 4, 0);
|
||||
private static Version MinimumApplicationV3Version => new (3, 1, 1, 0);
|
||||
private static Version MinimumApplicationV4Version => new(4, 0, 4, 0);
|
||||
private static Version MinimumApplicationV3Version => new(3, 1, 1, 0);
|
||||
|
||||
private const string AppApiRoute = "/api/v3";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
|
||||
@@ -121,9 +121,16 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
var indexerCapabilities = GetIndexerCapabilities(indexer);
|
||||
|
||||
if (!indexerCapabilities.BookSearchAvailable && !indexerCapabilities.SearchAvailable)
|
||||
{
|
||||
_logger.Debug("Skipping add for indexer {0} [{1}] due to missing book or basic search support by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
_logger.Debug("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -178,7 +185,8 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
_logger.Debug("Syncing remote indexer with current settings");
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if ((indexerCapabilities.BookSearchAvailable || indexerCapabilities.SearchAvailable) &&
|
||||
indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
// Retain user fields not-affiliated with Prowlarr
|
||||
readarrIndexer.Fields.AddRange(remoteIndexer.Fields.Where(f => readarrIndexer.Fields.All(s => s.Name != f.Name)));
|
||||
@@ -204,7 +212,8 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if ((indexerCapabilities.BookSearchAvailable || indexerCapabilities.SearchAvailable) &&
|
||||
indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to Readarr", indexer.Name, indexer.Id);
|
||||
readarrIndexer.Id = 0;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
public class ReadarrSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly ReadarrSettingsValidator Validator = new ();
|
||||
private static readonly ReadarrSettingsValidator Validator = new();
|
||||
|
||||
public ReadarrSettings()
|
||||
{
|
||||
|
||||
@@ -125,10 +125,17 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
var indexerCapabilities = GetIndexerCapabilities(indexer);
|
||||
|
||||
if (!indexerCapabilities.TvSearchAvailable && !indexerCapabilities.SearchAvailable)
|
||||
{
|
||||
_logger.Debug("Skipping add for indexer {0} [{1}] due to missing TV or basic search support by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty() &&
|
||||
indexerCapabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
_logger.Debug("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -183,7 +190,8 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
_logger.Debug("Syncing remote indexer with current settings");
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any() || indexerCapabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Any())
|
||||
if ((indexerCapabilities.TvSearchAvailable || indexerCapabilities.SearchAvailable) &&
|
||||
(indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any() || indexerCapabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Any()))
|
||||
{
|
||||
// Retain user fields not-affiliated with Prowlarr
|
||||
sonarrIndexer.Fields.AddRange(remoteIndexer.Fields.Where(f => sonarrIndexer.Fields.All(s => s.Name != f.Name)));
|
||||
@@ -210,7 +218,8 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any() || indexerCapabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Any())
|
||||
if ((indexerCapabilities.TvSearchAvailable || indexerCapabilities.SearchAvailable) &&
|
||||
(indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any() || indexerCapabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Any()))
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to Sonarr", indexer.Name, indexer.Id);
|
||||
sonarrIndexer.Id = 0;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
public class SonarrSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly SonarrSettingsValidator Validator = new ();
|
||||
private static readonly SonarrSettingsValidator Validator = new();
|
||||
|
||||
public SonarrSettings()
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
public class SonarrV3Proxy : ISonarrV3Proxy
|
||||
{
|
||||
private static Version MinimumApplicationVersion => new (3, 0, 5, 0);
|
||||
private static Version MinimumApplicationVersion => new(3, 0, 5, 0);
|
||||
|
||||
private const string AppApiRoute = "/api/v3";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
public class WhisparrSettings : IApplicationSettings
|
||||
{
|
||||
private static readonly WhisparrSettingsValidator Validator = new ();
|
||||
private static readonly WhisparrSettingsValidator Validator = new();
|
||||
|
||||
public WhisparrSettings()
|
||||
{
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.Authentication
|
||||
{
|
||||
public enum AuthenticationType
|
||||
{
|
||||
None = 0,
|
||||
[Obsolete("Use Forms authentication instead")]
|
||||
Basic = 1,
|
||||
Forms = 2,
|
||||
External = 3
|
||||
|
||||
@@ -66,12 +66,19 @@ namespace NzbDrone.Core.Backup
|
||||
{
|
||||
_logger.ProgressInfo("Starting Backup");
|
||||
|
||||
var backupFolder = GetBackupFolder(backupType);
|
||||
|
||||
_diskProvider.EnsureFolder(_backupTempFolder);
|
||||
_diskProvider.EnsureFolder(GetBackupFolder(backupType));
|
||||
_diskProvider.EnsureFolder(backupFolder);
|
||||
|
||||
if (!_diskProvider.FolderWritable(backupFolder))
|
||||
{
|
||||
throw new UnauthorizedAccessException($"Backup folder {backupFolder} is not writable");
|
||||
}
|
||||
|
||||
var dateNow = DateTime.Now;
|
||||
var backupFilename = $"prowlarr_backup_v{BuildInfo.Version}_{dateNow:yyyy.MM.dd_HH.mm.ss}.zip";
|
||||
var backupPath = Path.Combine(GetBackupFolder(backupType), backupFilename);
|
||||
var backupPath = Path.Combine(backupFolder, backupFilename);
|
||||
|
||||
Cleanup();
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace NzbDrone.Core.Configuration
|
||||
string PostgresPassword { get; }
|
||||
string PostgresMainDb { get; }
|
||||
string PostgresLogDb { get; }
|
||||
bool TrustCgnatIpAddresses { get; }
|
||||
}
|
||||
|
||||
public class ConfigFileProvider : IConfigFileProvider
|
||||
@@ -206,13 +207,24 @@ namespace NzbDrone.Core.Configuration
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
SetValue("AuthenticationMethod", AuthenticationType.Basic);
|
||||
return AuthenticationType.Basic;
|
||||
SetValue("AuthenticationMethod", AuthenticationType.Forms);
|
||||
return AuthenticationType.Forms;
|
||||
}
|
||||
|
||||
return Enum.TryParse<AuthenticationType>(_authOptions.Method, out var enumValue)
|
||||
var value = Enum.TryParse<AuthenticationType>(_authOptions.Method, out var enumValue)
|
||||
? enumValue
|
||||
: GetValueEnum("AuthenticationMethod", AuthenticationType.None);
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (value == AuthenticationType.Basic)
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
{
|
||||
SetValue("AuthenticationMethod", AuthenticationType.Forms);
|
||||
|
||||
return AuthenticationType.Forms;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +285,7 @@ namespace NzbDrone.Core.Configuration
|
||||
{
|
||||
var instanceName = _appOptions.InstanceName ?? GetValue("InstanceName", BuildInfo.AppName);
|
||||
|
||||
if (instanceName.ContainsIgnoreCase(BuildInfo.AppName))
|
||||
if (instanceName.Contains(BuildInfo.AppName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return instanceName;
|
||||
}
|
||||
@@ -388,6 +400,12 @@ namespace NzbDrone.Core.Configuration
|
||||
{
|
||||
SetValue("EnableSsl", false);
|
||||
}
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (AuthenticationMethod == AuthenticationType.Basic)
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
{
|
||||
SetValue("AuthenticationMethod", AuthenticationType.Forms);
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteOldValues()
|
||||
@@ -479,5 +497,7 @@ namespace NzbDrone.Core.Configuration
|
||||
SetValue("ApiKey", GenerateApiKey());
|
||||
_eventAggregator.PublishEvent(new ApiKeyChangedEvent());
|
||||
}
|
||||
|
||||
public bool TrustCgnatIpAddresses => _authOptions.TrustCgnatIpAddresses ?? GetValueBoolean("TrustCgnatIpAddresses", false, persist: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +183,12 @@ namespace NzbDrone.Core.Configuration
|
||||
|
||||
public string ApplicationUrl => GetValue("ApplicationUrl", string.Empty);
|
||||
|
||||
public bool TrustCgnatIpAddresses
|
||||
{
|
||||
get { return GetValueBoolean("TrustCgnatIpAddresses", false); }
|
||||
set { SetValue("TrustCgnatIpAddresses", value); }
|
||||
}
|
||||
|
||||
private string GetValue(string key)
|
||||
{
|
||||
return GetValue(key, string.Empty);
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
protected void Delete(SqlBuilder builder)
|
||||
{
|
||||
var sql = builder.AddDeleteTemplate(typeof(TModel)).LogQuery();
|
||||
var sql = builder.AddDeleteTemplate(typeof(TModel));
|
||||
|
||||
using (var conn = _database.OpenConnection())
|
||||
{
|
||||
|
||||
@@ -2,18 +2,17 @@ using System;
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class DapperTimeSpanConverter : SqlMapper.TypeHandler<TimeSpan>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, TimeSpan value)
|
||||
{
|
||||
parameter.Value = value.ToString();
|
||||
}
|
||||
namespace NzbDrone.Core.Datastore.Converters;
|
||||
|
||||
public override TimeSpan Parse(object value)
|
||||
{
|
||||
return TimeSpan.Parse((string)value);
|
||||
}
|
||||
public class TimeSpanConverter : SqlMapper.TypeHandler<TimeSpan>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, TimeSpan value)
|
||||
{
|
||||
parameter.Value = value.ToString();
|
||||
}
|
||||
|
||||
public override TimeSpan Parse(object value)
|
||||
{
|
||||
return value is string str ? TimeSpan.Parse(str) : TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SQLite;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dapper;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
@@ -52,9 +51,8 @@ namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
using var db = _datamapperFactory();
|
||||
var dbConnection = db as DbConnection;
|
||||
var version = Regex.Replace(dbConnection.ServerVersion, @"\(.*?\)", "");
|
||||
|
||||
return new Version(version);
|
||||
return DatabaseVersionParser.ParseServerVersion(dbConnection.ServerVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
16
src/NzbDrone.Core/Datastore/DatabaseVersionParser.cs
Normal file
16
src/NzbDrone.Core/Datastore/DatabaseVersionParser.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore;
|
||||
|
||||
public static class DatabaseVersionParser
|
||||
{
|
||||
private static readonly Regex VersionRegex = new(@"^[^ ]+", RegexOptions.Compiled);
|
||||
|
||||
public static Version ParseServerVersion(string serverVersion)
|
||||
{
|
||||
var match = VersionRegex.Match(serverVersion);
|
||||
|
||||
return match.Success ? new Version(match.Value) : null;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Datastore
|
||||
switch (expression.NodeType)
|
||||
{
|
||||
case ExpressionType.Lambda:
|
||||
return VisitLamda((LambdaExpression)expression);
|
||||
return VisitLambda((LambdaExpression)expression);
|
||||
case ExpressionType.ArrayLength:
|
||||
case ExpressionType.Convert:
|
||||
case ExpressionType.ConvertChecked:
|
||||
@@ -87,7 +87,7 @@ namespace NzbDrone.Core.Datastore
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visits the memeber access expression. To be implemented by user.
|
||||
/// Visits the member access expression. To be implemented by user.
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
@@ -130,11 +130,11 @@ namespace NzbDrone.Core.Datastore
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visits the lamda expression.
|
||||
/// Visits the lambda expression.
|
||||
/// </summary>
|
||||
/// <param name="lambdaExpression"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Expression VisitLamda(LambdaExpression lambdaExpression)
|
||||
protected virtual Expression VisitLambda(LambdaExpression lambdaExpression)
|
||||
{
|
||||
Visit(lambdaExpression.Body);
|
||||
return lambdaExpression;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
using FluentMigrator;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(042)]
|
||||
public class myanonamouse_freeleech_wedge_options : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(MigrateIndexersToWedgeOptions);
|
||||
}
|
||||
|
||||
private void MigrateIndexersToWedgeOptions(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var updated = new List<object>();
|
||||
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.Transaction = tran;
|
||||
cmd.CommandText = "SELECT \"Id\", \"Settings\" FROM \"Indexers\" WHERE \"Implementation\" = 'MyAnonamouse'";
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var id = reader.GetInt32(0);
|
||||
var settings = Json.Deserialize<JObject>(reader.GetString(1));
|
||||
|
||||
if (settings.ContainsKey("freeleech") && settings.Value<JToken>("freeleech").Type == JTokenType.Boolean)
|
||||
{
|
||||
var optionValue = settings.Value<bool>("freeleech") switch
|
||||
{
|
||||
true => 2, // Required
|
||||
_ => 0 // Never
|
||||
};
|
||||
|
||||
settings.Remove("freeleech");
|
||||
settings.Add("useFreeleechWedge", optionValue);
|
||||
}
|
||||
|
||||
updated.Add(new
|
||||
{
|
||||
Id = id,
|
||||
Settings = settings.ToJson()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updateSql = "UPDATE \"Indexers\" SET \"Settings\" = @Settings WHERE \"Id\" = @Id";
|
||||
conn.Execute(updateSql, updated, transaction: tran);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,6 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
SqlMapper.RemoveTypeMap(typeof(DateTime));
|
||||
SqlMapper.AddTypeHandler(new DapperUtcConverter());
|
||||
SqlMapper.AddTypeHandler(new DapperTimeSpanConverter());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<Dictionary<string, string>>());
|
||||
SqlMapper.AddTypeHandler(new CookieConverter());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<int>>());
|
||||
@@ -123,6 +122,9 @@ namespace NzbDrone.Core.Datastore
|
||||
SqlMapper.RemoveTypeMap(typeof(Guid));
|
||||
SqlMapper.RemoveTypeMap(typeof(Guid?));
|
||||
SqlMapper.AddTypeHandler(new GuidConverter());
|
||||
SqlMapper.RemoveTypeMap(typeof(TimeSpan));
|
||||
SqlMapper.RemoveTypeMap(typeof(TimeSpan?));
|
||||
SqlMapper.AddTypeHandler(new TimeSpanConverter());
|
||||
SqlMapper.AddTypeHandler(new CommandConverter());
|
||||
SqlMapper.AddTypeHandler(new SystemVersionConverter());
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user