mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-03-19 16:54:07 -04:00
Compare commits
1 Commits
v0.4.2.187
...
instance-n
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0be2aee9c3 |
@@ -9,13 +9,13 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '0.4.2'
|
||||
majorVersion: '0.4.0'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||
sentryOrg: 'servarr'
|
||||
sentryUrl: 'https://sentry.servarr.com'
|
||||
dotnetVersion: '6.0.301'
|
||||
dotnetVersion: '6.0.201'
|
||||
innoVersion: '6.2.0'
|
||||
nodeVersion: '16.x'
|
||||
windowsImage: 'windows-2022'
|
||||
@@ -506,58 +506,6 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(testName) Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
|
||||
- job: Unit_LinuxCore_Postgres
|
||||
displayName: Unit Native LinuxCore with Postgres Database
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
variables:
|
||||
pattern: 'Prowlarr.*.linux-core-x64.tar.gz'
|
||||
artifactName: LinuxCoreTests
|
||||
Prowlarr__Postgres__Host: 'localhost'
|
||||
Prowlarr__Postgres__Port: '5432'
|
||||
Prowlarr__Postgres__User: 'prowlarr'
|
||||
Prowlarr__Postgres__Password: 'prowlarr'
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
|
||||
timeoutInMinutes: 10
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: $(artifactName)
|
||||
targetPath: $(testsFolder)
|
||||
- bash: find ${TESTSFOLDER} -name "Prowlarr.Test.Dummy" -exec chmod a+x {} \;
|
||||
displayName: Make Test Dummy Executable
|
||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
||||
- bash: |
|
||||
docker run -d --name=postgres14 \
|
||||
-e POSTGRES_PASSWORD=prowlarr \
|
||||
-e POSTGRES_USER=prowlarr \
|
||||
-p 5432:5432/tcp \
|
||||
postgres:14
|
||||
displayName: Start postgres
|
||||
- bash: |
|
||||
chmod a+x ${TESTSFOLDER}/test.sh
|
||||
ls -lR ${TESTSFOLDER}
|
||||
${TESTSFOLDER}/test.sh Linux Unit Test
|
||||
displayName: Run Tests
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish Test Results
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'LinuxCore Postgres Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
|
||||
- stage: Integration
|
||||
displayName: Integration
|
||||
@@ -642,67 +590,6 @@ stages:
|
||||
failTaskOnFailedTests: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
- job: Integration_LinuxCore_Postgres
|
||||
displayName: Integration Native LinuxCore with Postgres Database
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
variables:
|
||||
pattern: 'Prowlarr.*.linux-core-x64.tar.gz'
|
||||
Prowlarr__Postgres__Host: 'localhost'
|
||||
Prowlarr__Postgres__Port: '5432'
|
||||
Prowlarr__Postgres__User: 'prowlarr'
|
||||
Prowlarr__Postgres__Password: 'prowlarr'
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'LinuxCoreTests'
|
||||
targetPath: $(testsFolder)
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Build Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: Packages
|
||||
itemPattern: '**/$(pattern)'
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- task: ExtractFiles@1
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||
displayName: Extract Package
|
||||
- bash: |
|
||||
mkdir -p ./bin/
|
||||
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Prowlarr/. ./bin/
|
||||
displayName: Move Package Contents
|
||||
- bash: |
|
||||
docker run -d --name=postgres14 \
|
||||
-e POSTGRES_PASSWORD=prowlarr \
|
||||
-e POSTGRES_USER=prowlarr \
|
||||
-p 5432:5432/tcp \
|
||||
postgres:14
|
||||
displayName: Start postgres
|
||||
- bash: |
|
||||
chmod a+x ${TESTSFOLDER}/test.sh
|
||||
${TESTSFOLDER}/test.sh Linux Integration Test
|
||||
displayName: Run Integration Tests
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'Integration LinuxCore Postgres Database Integration Tests'
|
||||
failTaskOnFailedTests: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
- job: Integration_FreeBSD
|
||||
displayName: Integration Native FreeBSD
|
||||
dependsOn: Prepare
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
border: 1px solid var(--inputBorderColor);
|
||||
border-radius: 4px;
|
||||
background-color: var(--inputBackgroundColor);
|
||||
background-color: var(--white);
|
||||
}
|
||||
|
||||
.loading {
|
||||
|
||||
@@ -74,7 +74,7 @@ class PageHeader extends Component {
|
||||
<IconButton
|
||||
className={styles.donate}
|
||||
name={icons.HEART}
|
||||
to="https://prowlarr.com/donate"
|
||||
to="https://opencollective.com/prowlarr"
|
||||
size={14}
|
||||
/>
|
||||
<IconButton
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
position: relative;
|
||||
|
||||
&.default {
|
||||
background-color: var(--popoverBodyBackgroundColor);
|
||||
background-color: var(--white);
|
||||
box-shadow: 0 5px 10px var(--popoverShadowColor);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ const searchOptions = [
|
||||
const seriesTokens = [
|
||||
{ token: '{ImdbId:tt1234567}', example: 'tt12345' },
|
||||
{ token: '{TvdbId:12345}', example: '12345' },
|
||||
{ token: '{TmdbId:12345}', example: '12345' },
|
||||
{ token: '{TvMazeId:12345}', example: '54321' },
|
||||
{ token: '{Season:00}', example: '01' },
|
||||
{ token: '{Episode:00}', example: '01' }
|
||||
|
||||
@@ -21,6 +21,7 @@ const requiresRestartKeys = [
|
||||
'bindAddress',
|
||||
'port',
|
||||
'urlBase',
|
||||
'instanceName',
|
||||
'enableSsl',
|
||||
'sslPort',
|
||||
'sslCertPath',
|
||||
|
||||
@@ -168,11 +168,10 @@ module.exports = {
|
||||
//
|
||||
// Popover
|
||||
|
||||
popoverTitleBackgroundColor: '#424242',
|
||||
popoverTitleBorderColor: '#2a2a2a',
|
||||
popoverBodyBackgroundColor: '#2a2a2a',
|
||||
popoverTitleBackgroundColor: '#f7f7f7',
|
||||
popoverTitleBorderColor: '#ebebeb',
|
||||
popoverShadowColor: 'rgba(0, 0, 0, 0.2)',
|
||||
popoverArrowBorderColor: '#2a2a2a',
|
||||
popoverArrowBorderColor: '#fff',
|
||||
|
||||
popoverTitleBackgroundInverseColor: '#595959',
|
||||
popoverTitleBorderInverseColor: '#707070',
|
||||
|
||||
@@ -170,7 +170,6 @@ module.exports = {
|
||||
|
||||
popoverTitleBackgroundColor: '#f7f7f7',
|
||||
popoverTitleBorderColor: '#ebebeb',
|
||||
popoverBodyBackgroundColor: '#e9e9e9',
|
||||
popoverShadowColor: 'rgba(0, 0, 0, 0.2)',
|
||||
popoverArrowBorderColor: '#fff',
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class Donations extends Component {
|
||||
return (
|
||||
<FieldSet legend={translate('Donations')}>
|
||||
<div className={styles.logoContainer} title="Radarr">
|
||||
<Link to="https://radarr.video/donate">
|
||||
<Link to="https://opencollective.com/radarr">
|
||||
<img
|
||||
className={styles.logo}
|
||||
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-radarr.png`}
|
||||
@@ -21,7 +21,7 @@ class Donations extends Component {
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles.logoContainer} title="Lidarr">
|
||||
<Link to="https://lidarr.audio/donate">
|
||||
<Link to="https://opencollective.com/lidarr">
|
||||
<img
|
||||
className={styles.logo}
|
||||
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-lidarr.png`}
|
||||
@@ -29,7 +29,7 @@ class Donations extends Component {
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles.logoContainer} title="Readarr">
|
||||
<Link to="https://readarr.com/donate">
|
||||
<Link to="https://opencollective.com/readarr">
|
||||
<img
|
||||
className={styles.logo}
|
||||
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-readarr.png`}
|
||||
@@ -37,7 +37,7 @@ class Donations extends Component {
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles.logoContainer} title="Prowlarr">
|
||||
<Link to="https://prowlarr.com/donate">
|
||||
<Link to="https://opencollective.com/prowlarr">
|
||||
<img
|
||||
className={styles.logo}
|
||||
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-prowlarr.png`}
|
||||
|
||||
44
package.json
44
package.json
@@ -30,7 +30,7 @@
|
||||
"@fortawesome/free-regular-svg-icons": "6.1.1",
|
||||
"@fortawesome/free-solid-svg-icons": "6.1.1",
|
||||
"@fortawesome/react-fontawesome": "0.1.18",
|
||||
"@microsoft/signalr": "6.0.6",
|
||||
"@microsoft/signalr": "6.0.3",
|
||||
"@sentry/browser": "6.19.2",
|
||||
"@sentry/integrations": "6.19.2",
|
||||
"chart.js": "3.7.1",
|
||||
@@ -78,38 +78,38 @@
|
||||
"reselect": "4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.18.2",
|
||||
"@babel/eslint-parser": "7.18.2",
|
||||
"@babel/plugin-proposal-class-properties": "7.17.12",
|
||||
"@babel/plugin-proposal-decorators": "7.18.2",
|
||||
"@babel/plugin-proposal-export-default-from": "7.17.12",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.17.12",
|
||||
"@babel/plugin-proposal-function-sent": "7.18.2",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.17.12",
|
||||
"@babel/core": "7.17.8",
|
||||
"@babel/eslint-parser": "7.17.0",
|
||||
"@babel/plugin-proposal-class-properties": "7.16.7",
|
||||
"@babel/plugin-proposal-decorators": "7.17.8",
|
||||
"@babel/plugin-proposal-export-default-from": "7.16.7",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.16.7",
|
||||
"@babel/plugin-proposal-function-sent": "7.16.7",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.16.7",
|
||||
"@babel/plugin-proposal-numeric-separator": "7.16.7",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.17.12",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.16.7",
|
||||
"@babel/plugin-proposal-throw-expressions": "7.16.7",
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@babel/preset-env": "7.18.2",
|
||||
"@babel/preset-react": "7.17.12",
|
||||
"autoprefixer": "10.4.7",
|
||||
"babel-loader": "8.2.5",
|
||||
"@babel/preset-env": "7.16.11",
|
||||
"@babel/preset-react": "7.16.7",
|
||||
"autoprefixer": "10.4.4",
|
||||
"babel-loader": "8.2.4",
|
||||
"babel-plugin-inline-classnames": "2.0.1",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
|
||||
"core-js": "3.22.8",
|
||||
"core-js": "3.21.1",
|
||||
"css-loader": "6.7.1",
|
||||
"eslint": "8.17.0",
|
||||
"eslint": "8.11.0",
|
||||
"eslint-plugin-filenames": "1.3.2",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-react": "7.30.0",
|
||||
"eslint-plugin-import": "2.25.4",
|
||||
"eslint-plugin-react": "7.29.4",
|
||||
"eslint-plugin-simple-import-sort": "7.0.0",
|
||||
"esprint": "3.6.0",
|
||||
"esprint": "3.3.0",
|
||||
"file-loader": "6.2.0",
|
||||
"filemanager-webpack-plugin": "6.1.7",
|
||||
"html-webpack-plugin": "5.5.0",
|
||||
"loader-utils": "^3.0.0",
|
||||
"mini-css-extract-plugin": "2.6.0",
|
||||
"postcss": "8.4.14",
|
||||
"postcss": "8.4.12",
|
||||
"postcss-color-function": "4.1.0",
|
||||
"postcss-loader": "6.2.1",
|
||||
"postcss-mixins": "9.0.2",
|
||||
@@ -121,10 +121,10 @@
|
||||
"run-sequence": "2.2.1",
|
||||
"streamqueue": "1.1.2",
|
||||
"style-loader": "3.3.1",
|
||||
"stylelint": "14.8.5",
|
||||
"stylelint": "14.6.0",
|
||||
"stylelint-order": "5.0.0",
|
||||
"url-loader": "4.1.1",
|
||||
"webpack": "5.73.0",
|
||||
"webpack": "5.70.0",
|
||||
"webpack-cli": "4.9.2",
|
||||
"webpack-livereload-plugin": "3.0.2"
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
<!-- Standard testing packages -->
|
||||
<ItemGroup Condition="'$(TestProject)'=='true'">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||
<PackageReference Include="NunitXml.TestLogger" Version="3.0.117" />
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace NzbDrone.Automation.Test
|
||||
|
||||
driver.Manage().Window.Size = new System.Drawing.Size(1920, 1080);
|
||||
|
||||
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger(), null);
|
||||
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger());
|
||||
_runner.KillAll();
|
||||
_runner.Start();
|
||||
|
||||
|
||||
@@ -10,16 +10,6 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
public abstract class DiskProviderFixtureBase<TSubject> : TestBase<TSubject>
|
||||
where TSubject : class, IDiskProvider
|
||||
{
|
||||
[Test]
|
||||
public void writealltext_should_truncate_existing()
|
||||
{
|
||||
var file = GetTempFilePath();
|
||||
|
||||
Subject.WriteAllText(file, "A pretty long string");
|
||||
Subject.WriteAllText(file, "A short string");
|
||||
Subject.ReadAllText(file).Should().Be("A short string");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Retry(5)]
|
||||
public void directory_exist_should_be_able_to_find_existing_folder()
|
||||
|
||||
@@ -402,40 +402,6 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
VerifyCopyFolder(source.FullName, destination.FullName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyFolder_should_detect_caseinsensitive_parents()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
WithRealDiskProvider();
|
||||
|
||||
var original = GetFilledTempFolder();
|
||||
var root = new DirectoryInfo(GetTempFilePath());
|
||||
var source = new DirectoryInfo(root.FullName + "A/series");
|
||||
var destination = new DirectoryInfo(root.FullName + "a/series");
|
||||
|
||||
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
|
||||
|
||||
Assert.Throws<IOException>(() => Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Copy));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyFolder_should_detect_caseinsensitive_folder()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
WithRealDiskProvider();
|
||||
|
||||
var original = GetFilledTempFolder();
|
||||
var root = new DirectoryInfo(GetTempFilePath());
|
||||
var source = new DirectoryInfo(root.FullName + "A/series");
|
||||
var destination = new DirectoryInfo(root.FullName + "A/Series");
|
||||
|
||||
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
|
||||
|
||||
Assert.Throws<IOException>(() => Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Copy));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyFolder_should_ignore_nfs_temp_file()
|
||||
{
|
||||
@@ -485,42 +451,6 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
VerifyMoveFolder(original.FullName, source.FullName, destination.FullName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MoveFolder_should_detect_caseinsensitive_parents()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
WithRealDiskProvider();
|
||||
|
||||
var original = GetFilledTempFolder();
|
||||
var root = new DirectoryInfo(GetTempFilePath());
|
||||
var source = new DirectoryInfo(root.FullName + "A/series");
|
||||
var destination = new DirectoryInfo(root.FullName + "a/series");
|
||||
|
||||
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
|
||||
|
||||
Assert.Throws<IOException>(() => Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Move));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MoveFolder_should_rename_caseinsensitive_folder()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
WithRealDiskProvider();
|
||||
|
||||
var original = GetFilledTempFolder();
|
||||
var root = new DirectoryInfo(GetTempFilePath());
|
||||
var source = new DirectoryInfo(root.FullName + "A/series");
|
||||
var destination = new DirectoryInfo(root.FullName + "A/Series");
|
||||
|
||||
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
|
||||
|
||||
Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Move);
|
||||
|
||||
source.FullName.GetActualCasing().Should().Be(destination.FullName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_destination_is_readonly()
|
||||
{
|
||||
@@ -623,23 +553,6 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
VerifyCopyFolder(original.FullName, destination.FullName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MirrorFolder_should_handle_trailing_slash()
|
||||
{
|
||||
WithRealDiskProvider();
|
||||
|
||||
var original = GetFilledTempFolder();
|
||||
var source = new DirectoryInfo(GetTempFilePath());
|
||||
var destination = new DirectoryInfo(GetTempFilePath());
|
||||
|
||||
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
|
||||
|
||||
var count = Subject.MirrorFolder(source.FullName + Path.DirectorySeparatorChar, destination.FullName);
|
||||
|
||||
count.Should().Equals(3);
|
||||
VerifyCopyFolder(original.FullName, destination.FullName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TransferFolder_should_use_movefolder_if_on_same_mount()
|
||||
{
|
||||
@@ -839,10 +752,6 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
.Setup(v => v.CreateFolder(It.IsAny<string>()))
|
||||
.Callback<string>(v => Directory.CreateDirectory(v));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.MoveFolder(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Callback<string, string, bool>((v, r, b) => Directory.Move(v, r));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.DeleteFolder(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Callback<string, bool>((v, r) => Directory.Delete(v, r));
|
||||
|
||||
@@ -28,6 +28,14 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
Subject.GetAvailableSpace(Path.Combine(path, "invalidFolder")).Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Ignore("Docker")]
|
||||
[Test]
|
||||
public void should_be_able_to_check_space_on_ramdrive()
|
||||
{
|
||||
PosixOnly();
|
||||
Subject.GetAvailableSpace("/run/").Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Ignore("Docker")]
|
||||
[Test]
|
||||
public void should_return_free_disk_space()
|
||||
@@ -36,6 +44,35 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
result.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_get_space_on_unc()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
var result = Subject.GetAvailableSpace(@"\\localhost\c$\Windows");
|
||||
result.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_drive_doesnt_exist()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
// Find a drive that doesn't exist.
|
||||
for (char driveletter = 'Z'; driveletter > 'D'; driveletter--)
|
||||
{
|
||||
if (new DriveInfo(driveletter.ToString()).IsReady)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.Throws<DirectoryNotFoundException>(() => Subject.GetAvailableSpace(driveletter + @":\NOT_A_REAL_PATH\DOES_NOT_EXIST".AsOsAgnostic()));
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.Inconclusive("No drive available for testing.");
|
||||
}
|
||||
|
||||
[Ignore("Docker")]
|
||||
[Test]
|
||||
public void should_be_able_to_get_space_on_folder_that_doesnt_exist()
|
||||
|
||||
@@ -28,12 +28,9 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
// Indexer and Download Client Responses
|
||||
|
||||
// avistaz response
|
||||
[TestCase(@"""download"":""https://avistaz.to/rss/download/2b51db35e1910123321025a12b9933d2/tb51db35e1910123321025a12b9933d2.torrent"",")]
|
||||
[TestCase(@"""download"":""https:\/\/avistaz.to\/rss\/download\/2b51db35e1910123321025a12b9933d2\/tb51db35e1910123321025a12b9933d2.torrent"",")]
|
||||
[TestCase(@",""info_hash"":""2b51db35e1910123321025a12b9933d2"",")]
|
||||
|
||||
// animebytes response
|
||||
[TestCase(@"""Link"":""https://animebytes.tv/torrent/994064/download/tb51db35e1910123321025a12b9933d2"",")]
|
||||
|
||||
// danish bytes response
|
||||
[TestCase(@",""rsskey"":""2b51db35e1910123321025a12b9933d2"",")]
|
||||
[TestCase(@",""passkey"":""2b51db35e1910123321025a12b9933d2"",")]
|
||||
@@ -80,24 +77,20 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
// Download Station
|
||||
[TestCase(@"webapi/entry.cgi?api=(removed)&version=2&method=login&account=01233210&passwd=mySecret&format=sid&session=DownloadStation")]
|
||||
|
||||
// Tracker Responses
|
||||
[TestCase(@"tracker"":""http://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui"",""info"":""http://xxx.yyy/info?a=b""")]
|
||||
|
||||
// BroadcastheNet
|
||||
[TestCase(@"method: ""getTorrents"", ""params"": [ ""mySecret"",")]
|
||||
[TestCase(@"getTorrents(""mySecret"", [asdfasdf], 100, 0)")]
|
||||
[TestCase(@"""DownloadURL"":""https://broadcasthe.net/torrents.php?action=download&id=123&authkey=mySecret&torrent_pass=mySecret""")]
|
||||
[TestCase(@"""DownloadURL"":""https:\/\/broadcasthe.net\/torrents.php?action=download&id=123&authkey=mySecret&torrent_pass=mySecret""")]
|
||||
|
||||
// Webhooks - Notifiarr
|
||||
[TestCase(@"https://xxx.yyy/api/v1/notification/prowlarr/9pr04sg6-0123-3210-imav-eql2tyu8xyui")]
|
||||
// Notifiarr
|
||||
[TestCase("https://notifiarr.com/notifier.php: api=1234530f-422f-4aac-b6b3-01233210aaaa&radarr_health_issue_message=Download")]
|
||||
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
|
||||
|
||||
// RSS
|
||||
[TestCase(@"<atom:link href = ""https://api.nzb.su/api?t=search&extended=1&cat=3030&apikey=mySecret&q=Diggers"" rel=""self"" type=""application/rss+xml"" />")]
|
||||
|
||||
// Internal
|
||||
[TestCase(@"[Info] MigrationController: *** Migrating Database=prowlarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;Enlist=False ***")]
|
||||
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
|
||||
|
||||
public void should_clean_message(string message)
|
||||
{
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace NzbDrone.Common.Test
|
||||
var processStarted = new ManualResetEventSlim();
|
||||
|
||||
string suffix;
|
||||
if (OsInfo.IsWindows)
|
||||
if (OsInfo.IsWindows || PlatformInfo.IsMono)
|
||||
{
|
||||
suffix = ".exe";
|
||||
}
|
||||
|
||||
@@ -4,13 +4,11 @@ using DryIoc.Microsoft.DependencyInjection;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Composition.Extensions;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -31,8 +29,7 @@ namespace NzbDrone.Common.Test
|
||||
.AddDummyDatabase()
|
||||
.AddStartupContext(new StartupContext("first", "second"));
|
||||
|
||||
container.RegisterInstance(new Mock<IHostLifetime>().Object);
|
||||
container.RegisterInstance(new Mock<IOptions<PostgresOptions>>().Object);
|
||||
container.RegisterInstance<IHostLifetime>(new Mock<IHostLifetime>().Object);
|
||||
|
||||
var serviceProvider = container.GetServiceProvider();
|
||||
|
||||
|
||||
@@ -30,8 +30,7 @@ namespace NzbDrone.Common.Disk
|
||||
public abstract long? GetAvailableSpace(string path);
|
||||
public abstract void InheritFolderPermissions(string filename);
|
||||
public abstract void SetEveryonePermissions(string filename);
|
||||
public abstract void SetFilePermissions(string path, string mask, string group);
|
||||
public abstract void SetPermissions(string path, string mask, string group);
|
||||
public abstract void SetPermissions(string path, string mask);
|
||||
public abstract void CopyPermissions(string sourcePath, string targetPath);
|
||||
public abstract long? GetTotalSize(string path);
|
||||
|
||||
@@ -131,7 +130,7 @@ namespace NzbDrone.Common.Disk
|
||||
{
|
||||
var testPath = Path.Combine(path, "prowlarr_write_test.txt");
|
||||
var testContent = string.Format("This file was created to verify if '{0}' is writable. It should've been automatically deleted. Feel free to delete it.", path);
|
||||
WriteAllText(testPath, testContent);
|
||||
File.WriteAllText(testPath, testContent);
|
||||
File.Delete(testPath);
|
||||
return true;
|
||||
}
|
||||
@@ -259,6 +258,17 @@ namespace NzbDrone.Common.Disk
|
||||
Ensure.That(source, () => source).IsValidPath();
|
||||
Ensure.That(destination, () => destination).IsValidPath();
|
||||
|
||||
if (source.PathEquals(destination))
|
||||
{
|
||||
throw new IOException(string.Format("Source and destination can't be the same {0}", source));
|
||||
}
|
||||
|
||||
if (FolderExists(destination) && overwrite)
|
||||
{
|
||||
DeleteFolder(destination, true);
|
||||
}
|
||||
|
||||
RemoveReadOnlyFolder(source);
|
||||
Directory.Move(source, destination);
|
||||
}
|
||||
|
||||
@@ -300,16 +310,7 @@ namespace NzbDrone.Common.Disk
|
||||
{
|
||||
Ensure.That(filename, () => filename).IsValidPath();
|
||||
RemoveReadOnly(filename);
|
||||
|
||||
// File.WriteAllText is broken on net core when writing to some CIFS mounts
|
||||
// This workaround from https://github.com/dotnet/runtime/issues/42790#issuecomment-700362617
|
||||
using (var fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
using (var writer = new StreamWriter(fs))
|
||||
{
|
||||
writer.Write(contents);
|
||||
}
|
||||
}
|
||||
File.WriteAllText(filename, contents);
|
||||
}
|
||||
|
||||
public void FolderSetLastWriteTime(string path, DateTime dateTime)
|
||||
@@ -549,7 +550,7 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsValidFolderPermissionMask(string mask)
|
||||
public virtual bool IsValidFilePermissionMask(string mask)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Common.Disk
|
||||
@@ -26,56 +27,11 @@ namespace NzbDrone.Common.Disk
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private string ResolveRealParentPath(string path)
|
||||
{
|
||||
var parentPath = path.GetParentPath();
|
||||
if (!_diskProvider.FolderExists(parentPath))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
var realParentPath = parentPath.GetActualCasing();
|
||||
|
||||
var partialChildPath = path.Substring(parentPath.Length);
|
||||
|
||||
return realParentPath + partialChildPath;
|
||||
}
|
||||
|
||||
public TransferMode TransferFolder(string sourcePath, string targetPath, TransferMode mode)
|
||||
{
|
||||
Ensure.That(sourcePath, () => sourcePath).IsValidPath();
|
||||
Ensure.That(targetPath, () => targetPath).IsValidPath();
|
||||
|
||||
sourcePath = ResolveRealParentPath(sourcePath);
|
||||
targetPath = ResolveRealParentPath(targetPath);
|
||||
|
||||
_logger.Debug("{0} Directory [{1}] > [{2}]", mode, sourcePath, targetPath);
|
||||
|
||||
if (sourcePath == targetPath)
|
||||
{
|
||||
throw new IOException(string.Format("Source and destination can't be the same {0}", sourcePath));
|
||||
}
|
||||
|
||||
if (mode == TransferMode.Move && sourcePath.PathEquals(targetPath, StringComparison.InvariantCultureIgnoreCase) && _diskProvider.FolderExists(targetPath))
|
||||
{
|
||||
// Move folder out of the way to allow case-insensitive renames
|
||||
var tempPath = sourcePath + ".backup~";
|
||||
_logger.Trace("Rename Intermediate Directory [{0}] > [{1}]", sourcePath, tempPath);
|
||||
_diskProvider.MoveFolder(sourcePath, tempPath);
|
||||
|
||||
if (!_diskProvider.FolderExists(targetPath))
|
||||
{
|
||||
_logger.Trace("Rename Intermediate Directory [{0}] > [{1}]", tempPath, targetPath);
|
||||
_logger.Debug("Rename Directory [{0}] > [{1}]", sourcePath, targetPath);
|
||||
_diskProvider.MoveFolder(tempPath, targetPath);
|
||||
return mode;
|
||||
}
|
||||
|
||||
// There were two separate folders, revert the intermediate rename and let the recursion deal with it
|
||||
_logger.Trace("Rename Intermediate Directory [{0}] > [{1}]", tempPath, sourcePath);
|
||||
_diskProvider.MoveFolder(tempPath, sourcePath);
|
||||
}
|
||||
|
||||
if (mode == TransferMode.Move && !_diskProvider.FolderExists(targetPath))
|
||||
{
|
||||
var sourceMount = _diskProvider.GetMount(sourcePath);
|
||||
@@ -84,7 +40,7 @@ namespace NzbDrone.Common.Disk
|
||||
// If we're on the same mount, do a simple folder move.
|
||||
if (sourceMount != null && targetMount != null && sourceMount.RootDirectory == targetMount.RootDirectory)
|
||||
{
|
||||
_logger.Debug("Rename Directory [{0}] > [{1}]", sourcePath, targetPath);
|
||||
_logger.Debug("Move Directory [{0}] > [{1}]", sourcePath, targetPath);
|
||||
_diskProvider.MoveFolder(sourcePath, targetPath);
|
||||
return mode;
|
||||
}
|
||||
@@ -123,13 +79,6 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
if (mode.HasFlag(TransferMode.Move))
|
||||
{
|
||||
var totalSize = _diskProvider.GetFileInfos(sourcePath).Sum(v => v.Length);
|
||||
|
||||
if (totalSize > (100 * 1024L * 1024L))
|
||||
{
|
||||
throw new IOException($"Large files still exist in {sourcePath} after folder move, not deleting source folder");
|
||||
}
|
||||
|
||||
_diskProvider.DeleteFolder(sourcePath, true);
|
||||
}
|
||||
|
||||
@@ -143,10 +92,7 @@ namespace NzbDrone.Common.Disk
|
||||
Ensure.That(sourcePath, () => sourcePath).IsValidPath();
|
||||
Ensure.That(targetPath, () => targetPath).IsValidPath();
|
||||
|
||||
sourcePath = ResolveRealParentPath(sourcePath);
|
||||
targetPath = ResolveRealParentPath(targetPath);
|
||||
|
||||
_logger.Debug("Mirror Folder [{0}] > [{1}]", sourcePath, targetPath);
|
||||
_logger.Debug("Mirror [{0}] > [{1}]", sourcePath, targetPath);
|
||||
|
||||
if (!_diskProvider.FolderExists(targetPath))
|
||||
{
|
||||
@@ -258,9 +204,6 @@ namespace NzbDrone.Common.Disk
|
||||
Ensure.That(sourcePath, () => sourcePath).IsValidPath();
|
||||
Ensure.That(targetPath, () => targetPath).IsValidPath();
|
||||
|
||||
sourcePath = ResolveRealParentPath(sourcePath);
|
||||
targetPath = ResolveRealParentPath(targetPath);
|
||||
|
||||
_logger.Debug("{0} [{1}] > [{2}]", mode, sourcePath, targetPath);
|
||||
|
||||
var originalSize = _diskProvider.GetFileSize(sourcePath);
|
||||
|
||||
@@ -11,8 +11,7 @@ namespace NzbDrone.Common.Disk
|
||||
long? GetAvailableSpace(string path);
|
||||
void InheritFolderPermissions(string filename);
|
||||
void SetEveryonePermissions(string filename);
|
||||
void SetFilePermissions(string path, string mask, string group);
|
||||
void SetPermissions(string path, string mask, string group);
|
||||
void SetPermissions(string path, string mask);
|
||||
void CopyPermissions(string sourcePath, string targetPath);
|
||||
long? GetTotalSize(string path);
|
||||
DateTime FolderGetCreationTime(string path);
|
||||
@@ -57,6 +56,6 @@ namespace NzbDrone.Common.Disk
|
||||
List<FileInfo> GetFileInfos(string path, SearchOption searchOption = SearchOption.TopDirectoryOnly);
|
||||
void RemoveEmptySubfolders(string path);
|
||||
void SaveStream(Stream stream, string path);
|
||||
bool IsValidFolderPermissionMask(string mask);
|
||||
bool IsValidFilePermissionMask(string mask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +1,15 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Common.Disk
|
||||
{
|
||||
public static class LongPathSupport
|
||||
{
|
||||
private static int MAX_PATH;
|
||||
private static int MAX_NAME;
|
||||
|
||||
public static void Enable()
|
||||
{
|
||||
// Mono has an issue with enabling long path support via app.config.
|
||||
// This works for both mono and .net on Windows.
|
||||
AppContext.SetSwitch("Switch.System.IO.UseLegacyPathHandling", false);
|
||||
AppContext.SetSwitch("Switch.System.IO.BlockLongPaths", false);
|
||||
|
||||
DetectLongPathLimits();
|
||||
}
|
||||
|
||||
private static void DetectLongPathLimits()
|
||||
{
|
||||
if (!int.TryParse(Environment.GetEnvironmentVariable("MAX_PATH"), out MAX_PATH))
|
||||
{
|
||||
if (OsInfo.IsLinux)
|
||||
{
|
||||
MAX_PATH = 4096;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// Windows paths can be up to 32,767 characters long, but each component of the path must be less than 255.
|
||||
// If the OS does not have Long Path enabled, then the following will throw an exception
|
||||
// ref: https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
|
||||
Path.GetDirectoryName($@"C:\{new string('a', 254)}\{new string('a', 254)}");
|
||||
MAX_PATH = 4096;
|
||||
}
|
||||
catch
|
||||
{
|
||||
MAX_PATH = 260 - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!int.TryParse(Environment.GetEnvironmentVariable("MAX_NAME"), out MAX_NAME))
|
||||
{
|
||||
MAX_NAME = 255;
|
||||
}
|
||||
}
|
||||
|
||||
public static int MaxFilePathLength
|
||||
{
|
||||
get
|
||||
{
|
||||
if (MAX_PATH == 0)
|
||||
{
|
||||
DetectLongPathLimits();
|
||||
}
|
||||
|
||||
return MAX_PATH;
|
||||
}
|
||||
}
|
||||
|
||||
public static int MaxFileNameLength
|
||||
{
|
||||
get
|
||||
{
|
||||
if (MAX_NAME == 0)
|
||||
{
|
||||
DetectLongPathLimits();
|
||||
}
|
||||
|
||||
return MAX_NAME;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
private static readonly Regex[] CleansingRules = new[]
|
||||
{
|
||||
// Url
|
||||
new Regex(@"(?<=[?&: ;])(apikey|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pwd)=(?<secret>[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=[?&: ;])(apikey|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pwd)=(?<secret>[^&=]+?)(?= |&|$|<)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=[?& ;])[^=]*?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"rss\.torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"rss\.torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
@@ -28,9 +28,6 @@ namespace NzbDrone.Common.Instrumentation
|
||||
new Regex(@"""C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"""/home/(?<secret>[^/""]+?)(/|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
|
||||
new Regex(@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"),
|
||||
|
||||
// NzbGet
|
||||
new Regex(@"""Name""\s*:\s*""[^""]*(username|password)""\s*,\s*""Value""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
@@ -54,8 +51,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
new Regex(@"(?<=\?|&)(X-Plex-Client-Identifier|X-Plex-Token)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Indexer Responses
|
||||
new Regex(@"(?:avistaz|exoticaz|cinemaz|privatehd)\.[a-z]{2,3}/rss/download/(?<secret>[^&=]+?)/(?<secret>[^&=]+?)\.torrent", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?:animebytes)\.[a-z]{2,3}/torrent/[0-9]+/download/(?<secret>[^&=]+?)[""]", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?:avistaz|exoticaz|cinemaz|privatehd)\.[a-z]{2,3}\\\/rss\\\/download\\\/(?<secret>[^&=]+?)\\\/(?<secret>[^&=]+?)\.torrent", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@",""info_hash"":""(?<secret>[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@",""pass[- _]?key"":""(?<secret>[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@",""rss[- _]?key"":""(?<secret>[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NLog.Fluent;
|
||||
|
||||
@@ -10,46 +8,47 @@ namespace NzbDrone.Common.Instrumentation.Extensions
|
||||
{
|
||||
public static readonly Logger SentryLogger = LogManager.GetLogger("Sentry");
|
||||
|
||||
public static LogEventBuilder SentryFingerprint(this LogEventBuilder logBuilder, params string[] fingerprint)
|
||||
public static LogBuilder SentryFingerprint(this LogBuilder logBuilder, params string[] fingerprint)
|
||||
{
|
||||
return logBuilder.Property("Sentry", fingerprint);
|
||||
}
|
||||
|
||||
public static LogEventBuilder WriteSentryDebug(this LogEventBuilder logBuilder, params string[] fingerprint)
|
||||
public static LogBuilder WriteSentryDebug(this LogBuilder logBuilder, params string[] fingerprint)
|
||||
{
|
||||
return LogSentryMessage(logBuilder, LogLevel.Debug, fingerprint);
|
||||
}
|
||||
|
||||
public static LogEventBuilder WriteSentryInfo(this LogEventBuilder logBuilder, params string[] fingerprint)
|
||||
public static LogBuilder WriteSentryInfo(this LogBuilder logBuilder, params string[] fingerprint)
|
||||
{
|
||||
return LogSentryMessage(logBuilder, LogLevel.Info, fingerprint);
|
||||
}
|
||||
|
||||
public static LogEventBuilder WriteSentryWarn(this LogEventBuilder logBuilder, params string[] fingerprint)
|
||||
public static LogBuilder WriteSentryWarn(this LogBuilder logBuilder, params string[] fingerprint)
|
||||
{
|
||||
return LogSentryMessage(logBuilder, LogLevel.Warn, fingerprint);
|
||||
}
|
||||
|
||||
public static LogEventBuilder WriteSentryError(this LogEventBuilder logBuilder, params string[] fingerprint)
|
||||
public static LogBuilder WriteSentryError(this LogBuilder logBuilder, params string[] fingerprint)
|
||||
{
|
||||
return LogSentryMessage(logBuilder, LogLevel.Error, fingerprint);
|
||||
}
|
||||
|
||||
private static LogEventBuilder LogSentryMessage(LogEventBuilder logBuilder, LogLevel level, string[] fingerprint)
|
||||
private static LogBuilder LogSentryMessage(LogBuilder logBuilder, LogLevel level, string[] fingerprint)
|
||||
{
|
||||
SentryLogger.ForLogEvent(level)
|
||||
.CopyLogEvent(logBuilder.LogEvent)
|
||||
SentryLogger.Log(level)
|
||||
.CopyLogEvent(logBuilder.LogEventInfo)
|
||||
.SentryFingerprint(fingerprint)
|
||||
.Log();
|
||||
.Write();
|
||||
|
||||
return logBuilder.Property<string>("Sentry", null);
|
||||
return logBuilder.Property("Sentry", null);
|
||||
}
|
||||
|
||||
private static LogEventBuilder CopyLogEvent(this LogEventBuilder logBuilder, LogEventInfo logEvent)
|
||||
private static LogBuilder CopyLogEvent(this LogBuilder logBuilder, LogEventInfo logEvent)
|
||||
{
|
||||
return logBuilder.TimeStamp(logEvent.TimeStamp)
|
||||
return logBuilder.LoggerName(logEvent.LoggerName)
|
||||
.TimeStamp(logEvent.TimeStamp)
|
||||
.Message(logEvent.Message, logEvent.Parameters)
|
||||
.Properties(logEvent.Properties.Select(p => new KeyValuePair<string, object>(p.Key.ToString(), p.Value)))
|
||||
.Properties(logEvent.Properties.ToDictionary(v => v.Key, v => v.Value))
|
||||
.Exception(logEvent.Exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NLog;
|
||||
using NLog.Targets;
|
||||
|
||||
namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
public class NzbDroneFileTarget : FileTarget
|
||||
{
|
||||
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
|
||||
protected override string GetFormattedMessage(LogEventInfo logEvent)
|
||||
{
|
||||
var result = CleanseLogMessage.Cleanse(Layout.Render(logEvent));
|
||||
target.Append(result);
|
||||
return CleanseLogMessage.Cleanse(Layout.Render(logEvent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@ namespace NzbDrone.Common.Instrumentation
|
||||
|
||||
var appFolderInfo = new AppFolderInfo(startupContext);
|
||||
|
||||
RegisterGlobalFilters();
|
||||
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
RegisterDebugger();
|
||||
@@ -99,21 +97,10 @@ namespace NzbDrone.Common.Instrumentation
|
||||
target.Layout = "[${level}] [${threadid}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
|
||||
var loggingRule = new LoggingRule("*", LogLevel.Trace, target);
|
||||
|
||||
LogManager.Configuration.AddTarget("debugger", target);
|
||||
LogManager.Configuration.LoggingRules.Add(loggingRule);
|
||||
}
|
||||
|
||||
private static void RegisterGlobalFilters()
|
||||
{
|
||||
LogManager.Setup().LoadConfiguration(c =>
|
||||
{
|
||||
c.ForLogger("Microsoft.Hosting.Lifetime*").WriteToNil(LogLevel.Info);
|
||||
c.ForLogger("System*").WriteToNil(LogLevel.Warn);
|
||||
c.ForLogger("Microsoft*").WriteToNil(LogLevel.Warn);
|
||||
});
|
||||
}
|
||||
|
||||
private static void RegisterConsole()
|
||||
{
|
||||
var level = LogLevel.Trace;
|
||||
|
||||
@@ -127,18 +127,7 @@ namespace NzbDrone.Common.Processes
|
||||
try
|
||||
{
|
||||
_logger.Trace("Setting environment variable '{0}' to '{1}'", environmentVariable.Key, environmentVariable.Value);
|
||||
|
||||
var key = environmentVariable.Key.ToString();
|
||||
var value = environmentVariable.Value?.ToString();
|
||||
|
||||
if (startInfo.EnvironmentVariables.ContainsKey(key))
|
||||
{
|
||||
startInfo.EnvironmentVariables[key] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
startInfo.EnvironmentVariables.Add(key, value);
|
||||
}
|
||||
startInfo.EnvironmentVariables.Add(environmentVariable.Key.ToString(), environmentVariable.Value.ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -9,10 +9,8 @@
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NLog" Version="5.0.1" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
|
||||
<PackageReference Include="Sentry" Version="3.18.0" />
|
||||
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
|
||||
<PackageReference Include="NLog" Version="4.7.14" />
|
||||
<PackageReference Include="Sentry" Version="3.15.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.Datastore
|
||||
public void SingleOrDefault_should_return_null_on_empty_db()
|
||||
{
|
||||
Mocker.Resolve<IDatabase>()
|
||||
.OpenConnection().Query<IndexerDefinition>("SELECT * FROM \"Indexers\"")
|
||||
.OpenConnection().Query<IndexerDefinition>("SELECT * FROM Indexers")
|
||||
.SingleOrDefault()
|
||||
.Should()
|
||||
.BeNull();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,14 +5,10 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Npgsql;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using NzbDrone.Test.Common.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Test.Framework
|
||||
{
|
||||
@@ -53,7 +49,6 @@ namespace NzbDrone.Core.Test.Framework
|
||||
public abstract class DbTest : CoreTest
|
||||
{
|
||||
private ITestDatabase _db;
|
||||
private DatabaseType _databaseType;
|
||||
|
||||
protected virtual MigrationType MigrationType => MigrationType.Main;
|
||||
|
||||
@@ -106,39 +101,17 @@ namespace NzbDrone.Core.Test.Framework
|
||||
|
||||
private IDatabase CreateDatabase(MigrationContext migrationContext)
|
||||
{
|
||||
if (_databaseType == DatabaseType.PostgreSQL)
|
||||
{
|
||||
CreatePostgresDb();
|
||||
}
|
||||
|
||||
var factory = Mocker.Resolve<DbFactory>();
|
||||
|
||||
// If a special migration test or log migration then create new
|
||||
if (migrationContext.BeforeMigration != null || _databaseType == DatabaseType.PostgreSQL)
|
||||
if (migrationContext.BeforeMigration != null)
|
||||
{
|
||||
return factory.Create(migrationContext);
|
||||
}
|
||||
|
||||
return CreateSqliteDatabase(factory, migrationContext);
|
||||
}
|
||||
|
||||
private void CreatePostgresDb()
|
||||
{
|
||||
var options = Mocker.Resolve<IOptions<PostgresOptions>>().Value;
|
||||
PostgresDatabase.Create(options, MigrationType);
|
||||
}
|
||||
|
||||
private void DropPostgresDb()
|
||||
{
|
||||
var options = Mocker.Resolve<IOptions<PostgresOptions>>().Value;
|
||||
PostgresDatabase.Drop(options, MigrationType);
|
||||
}
|
||||
|
||||
private IDatabase CreateSqliteDatabase(IDbFactory factory, MigrationContext migrationContext)
|
||||
{
|
||||
// Otherwise try to use a cached migrated db
|
||||
var cachedDb = SqliteDatabase.GetCachedDb(migrationContext.MigrationType);
|
||||
var testDb = GetTestSqliteDb(migrationContext.MigrationType);
|
||||
var cachedDb = GetCachedDatabase(migrationContext.MigrationType);
|
||||
var testDb = GetTestDb(migrationContext.MigrationType);
|
||||
if (File.Exists(cachedDb))
|
||||
{
|
||||
TestLogger.Info($"Using cached initial database {cachedDb}");
|
||||
@@ -158,7 +131,12 @@ namespace NzbDrone.Core.Test.Framework
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTestSqliteDb(MigrationType type)
|
||||
private string GetCachedDatabase(MigrationType type)
|
||||
{
|
||||
return Path.Combine(TestContext.CurrentContext.TestDirectory, $"cached_{type}.db");
|
||||
}
|
||||
|
||||
private string GetTestDb(MigrationType type)
|
||||
{
|
||||
return type == MigrationType.Main ? TestFolderInfo.GetDatabase() : TestFolderInfo.GetLogDatabase();
|
||||
}
|
||||
@@ -173,13 +151,6 @@ namespace NzbDrone.Core.Test.Framework
|
||||
WithTempAsAppPath();
|
||||
SetupLogging();
|
||||
|
||||
// populate the possible postgres options
|
||||
var postgresOptions = PostgresDatabase.GetTestOptions();
|
||||
_databaseType = postgresOptions.Host.IsNotNullOrWhiteSpace() ? DatabaseType.PostgreSQL : DatabaseType.SQLite;
|
||||
|
||||
// Set up remaining container services
|
||||
Mocker.SetConstant(Options.Create(postgresOptions));
|
||||
Mocker.SetConstant<IConfigFileProvider>(Mocker.Resolve<ConfigFileProvider>());
|
||||
Mocker.SetConstant<IConnectionStringFactory>(Mocker.Resolve<ConnectionStringFactory>());
|
||||
Mocker.SetConstant<IMigrationController>(Mocker.Resolve<MigrationController>());
|
||||
|
||||
@@ -200,17 +171,11 @@ namespace NzbDrone.Core.Test.Framework
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
SQLiteConnection.ClearAllPools();
|
||||
NpgsqlConnection.ClearAllPools();
|
||||
|
||||
if (TestFolderInfo != null)
|
||||
{
|
||||
DeleteTempFolder(TestFolderInfo.AppDataFolder);
|
||||
}
|
||||
|
||||
if (_databaseType == DatabaseType.PostgreSQL)
|
||||
{
|
||||
DropPostgresDb();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using NzbDrone.Test.Common.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Test
|
||||
{
|
||||
@@ -12,13 +10,13 @@ namespace NzbDrone.Core.Test
|
||||
[OneTimeTearDown]
|
||||
public void ClearCachedDatabase()
|
||||
{
|
||||
var mainCache = SqliteDatabase.GetCachedDb(MigrationType.Main);
|
||||
var mainCache = Path.Combine(TestContext.CurrentContext.TestDirectory, $"cached_Main.db");
|
||||
if (File.Exists(mainCache))
|
||||
{
|
||||
File.Delete(mainCache);
|
||||
}
|
||||
|
||||
var logCache = SqliteDatabase.GetCachedDb(MigrationType.Log);
|
||||
var logCache = Path.Combine(TestContext.CurrentContext.TestDirectory, $"cached_Log.db");
|
||||
if (File.Exists(logCache))
|
||||
{
|
||||
File.Delete(logCache);
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace NzbDrone.Core.Test.Framework
|
||||
where T : ModelBase, new();
|
||||
IDirectDataMapper GetDirectDataMapper();
|
||||
IDbConnection OpenConnection();
|
||||
DatabaseType DatabaseType { get; }
|
||||
}
|
||||
|
||||
public class TestDatabase : ITestDatabase
|
||||
@@ -31,8 +30,6 @@ namespace NzbDrone.Core.Test.Framework
|
||||
private readonly IDatabase _dbConnection;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public DatabaseType DatabaseType => _dbConnection.DatabaseType;
|
||||
|
||||
public TestDatabase(IDatabase dbConnection)
|
||||
{
|
||||
_eventAggregator = new Mock<IEventAggregator>().Object;
|
||||
|
||||
@@ -41,8 +41,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
|
||||
Subject.Clean();
|
||||
|
||||
// BeCloseTo handles Postgres rounding times
|
||||
AllStoredModels.ToList().ForEach(t => t.LastExecution.Should().BeCloseTo(expectedTime));
|
||||
AllStoredModels.ToList().ForEach(t => t.LastExecution.Should().Be(expectedTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Definitions;
|
||||
using NzbDrone.Core.Indexers.Definitions.Avistaz;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ExoticazFixture : CoreTest<ExoticaZ>
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
{
|
||||
Name = "ExoticaZ",
|
||||
Settings = new AvistazSettings() { Username = "someuser", Password = "somepass", Pid = "somepid" }
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task should_parse_recent_feed_from_ExoticaZ()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Exoticaz/recentfeed.json");
|
||||
|
||||
Mocker.GetMock<IIndexerHttpClient>()
|
||||
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
|
||||
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed)));
|
||||
|
||||
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(100);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
var torrentInfo = releases.First() as TorrentInfo;
|
||||
|
||||
torrentInfo.Title.Should().Be("[SSIS-419] My first experience is Yua Mikami. From the day I lost my virginity, I was devoted to sex.");
|
||||
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
torrentInfo.DownloadUrl.Should().Be("https://exoticaz.to/rss/download/(removed)/(removed).torrent");
|
||||
torrentInfo.InfoUrl.Should().Be("https://exoticaz.to/torrent/64040-ssis-419-my-first-experience-is-yua-mikami-from-the-day-i-lost-my-virginity-i-was-devoted-to-sex");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-06-11 11:04:50"));
|
||||
torrentInfo.Size.Should().Be(7085405541);
|
||||
torrentInfo.InfoHash.Should().Be("asdjfiasdf54asd7f4a2sdf544asdf");
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
torrentInfo.Peers.Should().Be(33);
|
||||
torrentInfo.Seeders.Should().Be(33);
|
||||
torrentInfo.Categories.First().Id.Should().Be(6040);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests.CardigannTests
|
||||
{
|
||||
public class ApplyGoTemplateTextFixture : CoreTest<CardigannBase>
|
||||
{
|
||||
private Dictionary<string, object> _variables;
|
||||
private CardigannDefinition _definition;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_variables = new Dictionary<string, object>
|
||||
{
|
||||
[".Config.sitelink"] = "https://somesite.com/",
|
||||
[".True"] = "True",
|
||||
[".False"] = null,
|
||||
[".Today.Year"] = DateTime.Today.Year.ToString(),
|
||||
[".Categories"] = new string[] { "tv", "movies" }
|
||||
};
|
||||
|
||||
_definition = Builder<CardigannDefinition>.CreateNew()
|
||||
.With(x => x.Encoding = "UTF-8")
|
||||
.With(x => x.Links = new List<string>
|
||||
{
|
||||
"https://somesite.com/"
|
||||
})
|
||||
.With(x => x.Caps = new CapabilitiesBlock
|
||||
{
|
||||
Modes = new Dictionary<string, List<string>>
|
||||
{
|
||||
{ "search", new List<string> { "q" } }
|
||||
}
|
||||
})
|
||||
.Build();
|
||||
|
||||
Mocker.SetConstant<CardigannDefinition>(_definition);
|
||||
}
|
||||
|
||||
[TestCase("{{ range .Categories}}&categories[]={{.}}{{end}}", "&categories[]=tv&categories[]=movies")]
|
||||
[TestCase("{{ range $i, $e := .Categories}}&categories[{{$i}}]={{.}}{{end}}", "&categories[0]=tv&categories[1]=movies")]
|
||||
[TestCase("{{ range $index, $element := .Categories}}&categories[{{$index}}]={{.}}+postIndex[{{$index}}]{{end}}", "&categories[0]=tv+postIndex[0]&categories[1]=movies+postIndex[1]")]
|
||||
public void should_handle_range_statements(string template, string expected)
|
||||
{
|
||||
var result = Subject.ApplyGoTemplateText(template, _variables);
|
||||
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("{{ re_replace .Query.Keywords \"[^a-zA-Z0-9]+\" \"%\" }}", "abc%def")]
|
||||
public void should_handle_re_replace_statements(string template, string expected)
|
||||
{
|
||||
_variables[".Query.Keywords"] = string.Join(" ", new List<string> { "abc", "def" });
|
||||
|
||||
var result = Subject.ApplyGoTemplateText(template, _variables);
|
||||
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("{{ join .Categories \", \" }}", "tv, movies")]
|
||||
public void should_handle_join_statements(string template, string expected)
|
||||
{
|
||||
var result = Subject.ApplyGoTemplateText(template, _variables);
|
||||
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("{{ .Today.Year }}", "2022")]
|
||||
public void should_handle_variables_statements(string template, string expected)
|
||||
{
|
||||
var result = Subject.ApplyGoTemplateText(template, _variables);
|
||||
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("{{if .False }}0{{else}}1{{end}}", "1")]
|
||||
[TestCase("{{if .True }}0{{else}}1{{end}}", "0")]
|
||||
public void should_handle_if_statements(string template, string expected)
|
||||
{
|
||||
var result = Subject.ApplyGoTemplateText(template, _variables);
|
||||
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.IndexerTests.PTPTests
|
||||
var torrents = (await Subject.Fetch(new MovieSearchCriteria())).Releases;
|
||||
|
||||
torrents.Should().HaveCount(293);
|
||||
torrents.First().Should().BeOfType<TorrentInfo>();
|
||||
torrents.First().Should().BeOfType<PassThePopcornInfo>();
|
||||
|
||||
var first = torrents.First() as TorrentInfo;
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Security;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.SecurityTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ProtectionServiceFixture : CoreTest<ProtectionService>
|
||||
{
|
||||
private string _protectionKey;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_protectionKey = Guid.NewGuid().ToString().Replace("-", "");
|
||||
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(s => s.DownloadProtectionKey)
|
||||
.Returns(_protectionKey);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_encrypt_and_decrypt_string()
|
||||
{
|
||||
const string plainText = "https://prowlarr.com";
|
||||
|
||||
var encrypted = Subject.Protect(plainText);
|
||||
var decrypted = Subject.UnProtect(encrypted);
|
||||
|
||||
decrypted.Should().Be(plainText);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,10 +68,6 @@ namespace NzbDrone.Core.Test.UpdateTests
|
||||
.Setup(c => c.FolderWritable(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.FileExists(It.Is<string>(s => s.EndsWith("Prowlarr.Update.exe"))))
|
||||
.Returns(true);
|
||||
|
||||
_sandboxFolder = Mocker.GetMock<IAppFolderInfo>().Object.GetUpdateSandboxFolder();
|
||||
}
|
||||
|
||||
@@ -153,7 +149,7 @@ namespace NzbDrone.Core.Test.UpdateTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_start_update_client_if_updater_exists()
|
||||
public void should_start_update_client()
|
||||
{
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
@@ -161,21 +157,6 @@ namespace NzbDrone.Core.Test.UpdateTests
|
||||
.Verify(c => c.Start(It.IsAny<string>(), It.Is<string>(s => s.StartsWith("12")), null, null, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_with_warning_if_updater_doesnt_exists()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.FileExists(It.Is<string>(s => s.EndsWith("Prowlarr.Update.exe"))))
|
||||
.Returns(false);
|
||||
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
Mocker.GetMock<IProcessProvider>()
|
||||
.Verify(c => c.Start(It.IsAny<string>(), It.IsAny<string>(), null, null, null), Times.Never());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_without_error_or_warnings_when_no_updates_are_available()
|
||||
{
|
||||
|
||||
@@ -28,13 +28,10 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
}
|
||||
|
||||
var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value;
|
||||
var apiPath = (string)Fields.FirstOrDefault(x => x.Name == "apiPath").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value;
|
||||
var cats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "categories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "categories").Value);
|
||||
|
||||
var apiPath = Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiPathCompare = apiPath == otherApiPath;
|
||||
|
||||
var minimumSeeders = Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var otherMinimumSeeders = other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var minimumSeedersCompare = minimumSeeders == otherMinimumSeeders;
|
||||
@@ -43,8 +40,8 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
var otherSeedTime = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime").Value);
|
||||
var seedTimeCompare = seedTime == otherSeedTime;
|
||||
|
||||
var discographySeedTime = Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime").Value);
|
||||
var otherDiscographySeedTime = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime").Value);
|
||||
var discographySeedTime = Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "seedCriteria.seasonPackSeedTime").Value);
|
||||
var otherDiscographySeedTime = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seasonPackSeedTime").Value);
|
||||
var discographySeedTimeCompare = discographySeedTime == otherDiscographySeedTime;
|
||||
|
||||
var seedRatio = Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio")?.Value == null ? null : (double?)Convert.ToDouble(Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value);
|
||||
@@ -58,7 +55,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKey && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare;
|
||||
apiKey && apiPath && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,13 +28,10 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
}
|
||||
|
||||
var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value;
|
||||
var apiPath = (string)Fields.FirstOrDefault(x => x.Name == "apiPath").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value;
|
||||
var cats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "categories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "categories").Value);
|
||||
|
||||
var apiPath = Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiPathCompare = apiPath == otherApiPath;
|
||||
|
||||
var minimumSeeders = Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var otherMinimumSeeders = other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var minimumSeedersCompare = minimumSeeders == otherMinimumSeeders;
|
||||
@@ -54,7 +51,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKey && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare;
|
||||
apiKey && apiPath && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,13 +28,10 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
}
|
||||
|
||||
var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value;
|
||||
var apiPath = (string)Fields.FirstOrDefault(x => x.Name == "apiPath").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value;
|
||||
var cats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "categories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "categories").Value);
|
||||
|
||||
var apiPath = Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiPathCompare = apiPath == otherApiPath;
|
||||
|
||||
var minimumSeeders = Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var otherMinimumSeeders = other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var minimumSeedersCompare = minimumSeeders == otherMinimumSeeders;
|
||||
@@ -43,8 +40,8 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
var otherSeedTime = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime").Value);
|
||||
var seedTimeCompare = seedTime == otherSeedTime;
|
||||
|
||||
var discographySeedTime = Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime").Value);
|
||||
var otherDiscographySeedTime = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime").Value);
|
||||
var discographySeedTime = Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "seedCriteria.seasonPackSeedTime").Value);
|
||||
var otherDiscographySeedTime = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seasonPackSeedTime").Value);
|
||||
var discographySeedTimeCompare = discographySeedTime == otherDiscographySeedTime;
|
||||
|
||||
var seedRatio = Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio")?.Value == null ? null : (double?)Convert.ToDouble(Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value);
|
||||
@@ -58,7 +55,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKey && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare;
|
||||
apiKey && apiPath && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +28,11 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
}
|
||||
|
||||
var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value;
|
||||
var apiPath = (string)Fields.FirstOrDefault(x => x.Name == "apiPath").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value;
|
||||
var cats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "categories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "categories").Value);
|
||||
var animeCats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "animeCategories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "animeCategories").Value);
|
||||
|
||||
var apiPath = Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiPathCompare = apiPath == otherApiPath;
|
||||
|
||||
var minimumSeeders = Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var otherMinimumSeeders = other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var minimumSeedersCompare = minimumSeeders == otherMinimumSeeders;
|
||||
@@ -59,7 +56,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKey && apiPathCompare && baseUrl && cats && animeCats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && seasonSeedTimeCompare;
|
||||
apiKey && apiPath && baseUrl && cats && animeCats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && seasonSeedTimeCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,13 +28,10 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
}
|
||||
|
||||
var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value;
|
||||
var apiPath = (string)Fields.FirstOrDefault(x => x.Name == "apiPath").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value;
|
||||
var cats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "categories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "categories").Value);
|
||||
|
||||
var apiPath = Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value;
|
||||
var apiPathCompare = apiPath == otherApiPath;
|
||||
|
||||
var minimumSeeders = Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var otherMinimumSeeders = other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value);
|
||||
var minimumSeedersCompare = minimumSeeders == otherMinimumSeeders;
|
||||
@@ -54,7 +51,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKey && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare;
|
||||
apiKey && apiPath && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,12 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration.Events;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -69,7 +67,6 @@ namespace NzbDrone.Core.Configuration
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly ICached<string> _cache;
|
||||
private readonly PostgresOptions _postgresOptions;
|
||||
|
||||
private readonly string _configFile;
|
||||
private static readonly Regex HiddenCharacterRegex = new Regex("[^a-z0-9]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
@@ -79,14 +76,12 @@ namespace NzbDrone.Core.Configuration
|
||||
public ConfigFileProvider(IAppFolderInfo appFolderInfo,
|
||||
ICacheManager cacheManager,
|
||||
IEventAggregator eventAggregator,
|
||||
IDiskProvider diskProvider,
|
||||
IOptions<PostgresOptions> postgresOptions)
|
||||
IDiskProvider diskProvider)
|
||||
{
|
||||
_cache = cacheManager.GetCache<string>(GetType());
|
||||
_eventAggregator = eventAggregator;
|
||||
_diskProvider = diskProvider;
|
||||
_configFile = appFolderInfo.GetConfigPath();
|
||||
_postgresOptions = postgresOptions.Value;
|
||||
}
|
||||
|
||||
public Dictionary<string, object> GetConfigDictionary()
|
||||
@@ -200,13 +195,13 @@ namespace NzbDrone.Core.Configuration
|
||||
|
||||
public string LogLevel => GetValue("LogLevel", "info").ToLowerInvariant();
|
||||
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
|
||||
public string PostgresHost => _postgresOptions?.Host ?? GetValue("PostgresHost", string.Empty, persist: false);
|
||||
public string PostgresUser => _postgresOptions?.User ?? GetValue("PostgresUser", string.Empty, persist: false);
|
||||
public string PostgresPassword => _postgresOptions?.Password ?? GetValue("PostgresPassword", string.Empty, persist: false);
|
||||
public string PostgresMainDb => _postgresOptions?.MainDb ?? GetValue("PostgresMainDb", "prowlarr-main", persist: false);
|
||||
public string PostgresLogDb => _postgresOptions?.LogDb ?? GetValue("PostgresLogDb", "prowlarr-log", persist: false);
|
||||
public int PostgresPort => (_postgresOptions?.Port ?? 0) != 0 ? _postgresOptions.Port : GetValueInt("PostgresPort", 5432, persist: false);
|
||||
public string PostgresHost => GetValue("PostgresHost", string.Empty, persist: false);
|
||||
public string PostgresUser => GetValue("PostgresUser", string.Empty, persist: false);
|
||||
public string PostgresPassword => GetValue("PostgresPassword", string.Empty, persist: false);
|
||||
public string PostgresMainDb => GetValue("PostgresMainDb", "prowlarr-main", persist: false);
|
||||
public string PostgresLogDb => GetValue("PostgresLogDb", "prowlarr-log", persist: false);
|
||||
public string Theme => GetValue("Theme", "light", persist: false);
|
||||
public int PostgresPort => GetValueInt("PostgresPort", 5432, persist: false);
|
||||
public bool LogSql => GetValueBoolean("LogSql", false, persist: false);
|
||||
public int LogRotate => GetValueInt("LogRotate", 50, persist: false);
|
||||
public bool FilterSentryEvents => GetValueBoolean("FilterSentryEvents", true, persist: false);
|
||||
|
||||
@@ -167,12 +167,14 @@ namespace NzbDrone.Core.Datastore
|
||||
}
|
||||
}
|
||||
|
||||
if (_database.DatabaseType == DatabaseType.PostgreSQL)
|
||||
if (_database.DatabaseType == DatabaseType.SQLite)
|
||||
{
|
||||
return $"INSERT INTO {_table} ({sbColumnList.ToString()}) VALUES ({sbParameterList.ToString()}); SELECT last_insert_rowid() id";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"INSERT INTO \"{_table}\" ({sbColumnList.ToString()}) VALUES ({sbParameterList.ToString()}) RETURNING \"Id\"";
|
||||
}
|
||||
|
||||
return $"INSERT INTO {_table} ({sbColumnList.ToString()}) VALUES ({sbParameterList.ToString()}); SELECT last_insert_rowid() id";
|
||||
}
|
||||
|
||||
private TModel Insert(IDbConnection connection, IDbTransaction transaction, TModel model)
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||
Delete.FromTable("Indexers").Row(new { Implementation = "ThePirateBay" });
|
||||
Delete.FromTable("Indexers").Row(new { Implementation = "TorrentLeech" });
|
||||
Delete.FromTable("Indexers").Row(new { Implementation = "TorrentSeeds" });
|
||||
Delete.FromTable("Indexers").Row(new { Implementation = "TorrentParadiseMI" });
|
||||
Delete.FromTable("Indexers").Row(new { Implementation = "YTS" });
|
||||
|
||||
//Change settings to shared classes
|
||||
|
||||
@@ -3,7 +3,7 @@ using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(018)]
|
||||
[Migration(18)]
|
||||
public class minimum_seeders : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(019)]
|
||||
public class remove_showrss : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
// Remove, YML version exists
|
||||
Delete.FromTable("Indexers").Row(new { Implementation = "ShowRSS" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(020)]
|
||||
public class remove_torrentparadiseml : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
// Remove, 017 incorrectly removes this using "TorrentParadiseMI"
|
||||
Delete.FromTable("Indexers").Row(new { Implementation = "TorrentParadiseMl" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
public class PostgresOptions
|
||||
{
|
||||
public string Host { get; set; }
|
||||
public int Port { get; set; }
|
||||
public string User { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string MainDb { get; set; }
|
||||
public string LogDb { get; set; }
|
||||
|
||||
public static PostgresOptions GetOptions()
|
||||
{
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
var postgresOptions = new PostgresOptions();
|
||||
config.GetSection("Prowlarr:Postgres").Bind(postgresOptions);
|
||||
|
||||
return postgresOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,20 +313,7 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
_sb.Append(" = ANY (");
|
||||
|
||||
// hardcode the integer list if it exists to bypass parameter limit
|
||||
if (item.Type == typeof(int) && TryGetRightValue(list, out var value))
|
||||
{
|
||||
var items = (IEnumerable<int>)value;
|
||||
_sb.Append("('{");
|
||||
_sb.Append(string.Join(", ", items));
|
||||
_sb.Append("}')");
|
||||
|
||||
_gotConcreteValue = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Visit(list);
|
||||
}
|
||||
Visit(list);
|
||||
|
||||
_sb.Append("))");
|
||||
}
|
||||
@@ -337,7 +324,7 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
Visit(body.Object);
|
||||
|
||||
_sb.Append(" ILIKE '%' || ");
|
||||
_sb.Append(" LIKE '%' || ");
|
||||
|
||||
Visit(body.Arguments[0]);
|
||||
|
||||
@@ -350,7 +337,7 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
Visit(body.Object);
|
||||
|
||||
_sb.Append(" ILIKE ");
|
||||
_sb.Append(" LIKE ");
|
||||
|
||||
Visit(body.Arguments[0]);
|
||||
|
||||
@@ -363,7 +350,7 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
Visit(body.Object);
|
||||
|
||||
_sb.Append(" ILIKE '%' || ");
|
||||
_sb.Append(" LIKE '%' || ");
|
||||
|
||||
Visit(body.Arguments[0]);
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@ namespace NzbDrone.Core.Download
|
||||
|
||||
public async Task<byte[]> DownloadReport(string link, int indexerId, string source, string host, string title)
|
||||
{
|
||||
_logger.Trace("Attempting download of {0}", link);
|
||||
var url = new Uri(link);
|
||||
|
||||
// Limit grabs to 2 per second.
|
||||
|
||||
@@ -23,10 +23,11 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var indexers = _indexerFactory.AllProviders(false);
|
||||
var enabled = _indexerFactory.Enabled(false);
|
||||
var expiringProviders = new List<IIndexer>();
|
||||
var expiredProviders = new List<IIndexer>();
|
||||
|
||||
foreach (var provider in indexers)
|
||||
foreach (var provider in enabled)
|
||||
{
|
||||
var settingsType = provider.Definition.Settings.GetType();
|
||||
var vipProp = settingsType.GetProperty("VipExpiration");
|
||||
@@ -43,6 +44,11 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DateTime.Parse(expiration).Before(DateTime.Now))
|
||||
{
|
||||
expiredProviders.Add(provider);
|
||||
}
|
||||
|
||||
if (DateTime.Parse(expiration).Between(DateTime.Now, DateTime.Now.AddDays(7)))
|
||||
{
|
||||
expiringProviders.Add(provider);
|
||||
@@ -58,6 +64,15 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
"#indexer-vip-expiring");
|
||||
}
|
||||
|
||||
if (!expiredProviders.Empty())
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
string.Format(_localizationService.GetLocalizedString("IndexerVipCheckExpiredClientMessage"),
|
||||
string.Join(", ", expiredProviders.Select(v => v.Definition.Name))),
|
||||
"#indexer-vip-expired");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
[CheckOn(typeof(ProviderAddedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderUpdatedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderDeletedEvent<IIndexer>))]
|
||||
public class IndexerVIPExpiredCheck : HealthCheckBase
|
||||
{
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
|
||||
public IndexerVIPExpiredCheck(IIndexerFactory indexerFactory, ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
{
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var indexers = _indexerFactory.AllProviders(false);
|
||||
var expiredProviders = new List<IIndexer>();
|
||||
|
||||
foreach (var provider in indexers)
|
||||
{
|
||||
var settingsType = provider.Definition.Settings.GetType();
|
||||
var vipProp = settingsType.GetProperty("VipExpiration");
|
||||
|
||||
if (vipProp == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var expiration = (string)vipProp.GetValue(provider.Definition.Settings);
|
||||
|
||||
if (expiration.IsNullOrWhiteSpace())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DateTime.Parse(expiration).Before(DateTime.Now))
|
||||
{
|
||||
expiredProviders.Add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
if (!expiredProviders.Empty())
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
string.Format(_localizationService.GetLocalizedString("IndexerVipCheckExpiredClientMessage"),
|
||||
string.Join(", ", expiredProviders.Select(v => v.Definition.Name))),
|
||||
"#indexer-vip-expired");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,15 +77,12 @@ namespace NzbDrone.Core.HealthCheck
|
||||
.ToDictionary(g => g.Key, g => g.ToArray());
|
||||
}
|
||||
|
||||
private void PerformHealthCheck(IProvideHealthCheck[] healthChecks, bool performServerChecks = false)
|
||||
private void PerformHealthCheck(IProvideHealthCheck[] healthChecks)
|
||||
{
|
||||
var results = healthChecks.Select(c => c.Check())
|
||||
.ToList();
|
||||
|
||||
if (performServerChecks)
|
||||
{
|
||||
results.AddRange(_serverSideNotificationService.GetServerChecks());
|
||||
}
|
||||
results.AddRange(_serverSideNotificationService.GetServerChecks());
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
@@ -111,17 +108,17 @@ namespace NzbDrone.Core.HealthCheck
|
||||
{
|
||||
if (message.Trigger == CommandTrigger.Manual)
|
||||
{
|
||||
PerformHealthCheck(_healthChecks, true);
|
||||
PerformHealthCheck(_healthChecks);
|
||||
}
|
||||
else
|
||||
{
|
||||
PerformHealthCheck(_scheduledHealthChecks, true);
|
||||
PerformHealthCheck(_scheduledHealthChecks);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleAsync(ApplicationStartedEvent message)
|
||||
{
|
||||
PerformHealthCheck(_startupHealthChecks, true);
|
||||
PerformHealthCheck(_startupHealthChecks);
|
||||
}
|
||||
|
||||
public void HandleAsync(IEvent message)
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Cloud;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Http;
|
||||
@@ -24,37 +23,24 @@ namespace NzbDrone.Core.HealthCheck
|
||||
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private readonly ICached<List<HealthCheck>> _cache;
|
||||
|
||||
public ServerSideNotificationService(IHttpClient client,
|
||||
IConfigFileProvider configFileProvider,
|
||||
IProwlarrCloudRequestBuilder cloudRequestBuilder,
|
||||
ICacheManager cacheManager,
|
||||
Logger logger)
|
||||
public ServerSideNotificationService(IHttpClient client, IConfigFileProvider configFileProvider, IProwlarrCloudRequestBuilder cloudRequestBuilder, Logger logger)
|
||||
{
|
||||
_client = client;
|
||||
_configFileProvider = configFileProvider;
|
||||
_cloudRequestBuilder = cloudRequestBuilder.Services;
|
||||
_logger = logger;
|
||||
|
||||
_cache = cacheManager.GetCache<List<HealthCheck>>(GetType());
|
||||
}
|
||||
|
||||
public List<HealthCheck> GetServerChecks()
|
||||
{
|
||||
return _cache.Get("ServerChecks", () => RetrieveServerChecks(), TimeSpan.FromHours(2));
|
||||
}
|
||||
|
||||
private List<HealthCheck> RetrieveServerChecks()
|
||||
{
|
||||
var request = _cloudRequestBuilder.Create()
|
||||
.Resource("/notification")
|
||||
.AddQueryParam("version", BuildInfo.Version)
|
||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
||||
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("branch", _configFileProvider.Branch)
|
||||
.Build();
|
||||
.Resource("/notification")
|
||||
.AddQueryParam("version", BuildInfo.Version)
|
||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
||||
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("branch", _configFileProvider.Branch)
|
||||
.Build();
|
||||
try
|
||||
{
|
||||
_logger.Trace("Getting server side health notifications");
|
||||
|
||||
@@ -136,7 +136,6 @@ namespace NzbDrone.Core.History
|
||||
{
|
||||
history.Data.Add("ImdbId", ((TvSearchCriteria)message.Query).FullImdbId ?? string.Empty);
|
||||
history.Data.Add("TvdbId", ((TvSearchCriteria)message.Query).TvdbId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("TmdbId", ((TvSearchCriteria)message.Query).TmdbId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("TraktId", ((TvSearchCriteria)message.Query).TraktId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("RId", ((TvSearchCriteria)message.Query).RId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("TvMazeId", ((TvSearchCriteria)message.Query).TvMazeId?.ToString() ?? string.Empty);
|
||||
|
||||
@@ -75,7 +75,6 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
let t = (r as TorrentInfo) ?? new TorrentInfo()
|
||||
select new XElement("item",
|
||||
new XElement("title", RemoveInvalidXMLChars(r.Title)),
|
||||
new XElement("description", RemoveInvalidXMLChars(r.Description)),
|
||||
new XElement("guid", r.Guid), // GUID and (Link or Magnet) are mandatory
|
||||
new XElement("prowlarrindexer", new XAttribute("id", r.IndexerId), r.Indexer),
|
||||
r.InfoUrl == null ? null : new XElement("comments", r.InfoUrl),
|
||||
@@ -98,7 +97,6 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
GetNabElement("imdb", r.ImdbId.ToString("D7"), protocol),
|
||||
GetNabElement("tmdbid", r.TmdbId, protocol),
|
||||
GetNabElement("traktid", r.TraktId, protocol),
|
||||
GetNabElement("doubanid", r.DoubanId, protocol),
|
||||
GetNabElement("seeders", t.Seeders, protocol),
|
||||
GetNabElement("files", r.Files, protocol),
|
||||
GetNabElement("grabs", r.Grabs, protocol),
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cloud;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
@@ -16,19 +14,17 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IHttpRequestBuilderFactory _requestBuilder;
|
||||
private readonly IAnalyticsService _analyticsService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ReleaseAnalyticsService(IHttpClient httpClient, IProwlarrCloudRequestBuilder requestBuilder, IAnalyticsService analyticsService, Logger logger)
|
||||
public ReleaseAnalyticsService(IHttpClient httpClient, IProwlarrCloudRequestBuilder requestBuilder, IAnalyticsService analyticsService)
|
||||
{
|
||||
_analyticsService = analyticsService;
|
||||
_requestBuilder = requestBuilder.Releases;
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void HandleAsync(IndexerQueryEvent message)
|
||||
{
|
||||
if (_analyticsService.IsEnabled && message.QueryResult?.Releases != null)
|
||||
if (_analyticsService.IsEnabled)
|
||||
{
|
||||
var request = _requestBuilder.Create().Resource("release/push").Build();
|
||||
request.Method = HttpMethod.Post;
|
||||
@@ -38,21 +34,14 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
var body = message.QueryResult.Releases.Select(x => new
|
||||
{
|
||||
Title = x.Title,
|
||||
Categories = x.Categories?.Where(c => c.Id < 10000).Select(c => c.Id) ?? new List<int>(),
|
||||
Categories = x.Categories.Where(c => c.Id < 10000).Select(c => c.Id),
|
||||
Protocol = x.DownloadProtocol.ToString(),
|
||||
Size = x.Size,
|
||||
PublishDate = x.PublishDate
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
request.SetContent(body.ToJson());
|
||||
_httpClient.Post(request);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.Trace("Analytics push failed");
|
||||
}
|
||||
request.SetContent(body.ToJson());
|
||||
_httpClient.Post(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.IndexerVersions
|
||||
/* Update Service will fall back if version # does not exist for an indexer per Ta */
|
||||
|
||||
private const string DEFINITION_BRANCH = "master";
|
||||
private const int DEFINITION_VERSION = 6;
|
||||
private const int DEFINITION_VERSION = 5;
|
||||
|
||||
//Used when moving yml to C#
|
||||
private readonly List<string> _defintionBlocklist = new List<string>()
|
||||
|
||||
@@ -6,9 +6,11 @@ using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
@@ -16,6 +18,8 @@ using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
|
||||
@@ -8,7 +8,6 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string Download { get; set; }
|
||||
public Dictionary<string, string> Category { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "movie_tv")]
|
||||
public AvistazIdInfo MovieTvinfo { get; set; }
|
||||
|
||||
@@ -201,11 +201,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var jsonResponse = new HttpResponse<BeyondHDResponse>(indexerHttpResponse);
|
||||
|
||||
if (jsonResponse.Resource.StatusCode == 0)
|
||||
{
|
||||
throw new IndexerException(indexerResponse, $"Indexer Error: {jsonResponse.Resource.StatusMessage}");
|
||||
}
|
||||
|
||||
foreach (var row in jsonResponse.Resource.Results)
|
||||
{
|
||||
var details = row.InfoUrl;
|
||||
@@ -277,11 +272,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class BeyondHDResponse
|
||||
{
|
||||
[JsonProperty(PropertyName = "status_code")]
|
||||
public int StatusCode { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "status_message")]
|
||||
public string StatusMessage { get; set; }
|
||||
public List<BeyondHDTorrent> Results { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
|
||||
protected virtual string SiteLink { get; private set; }
|
||||
|
||||
protected readonly IndexerCapabilitiesCategories _categories = new IndexerCapabilitiesCategories();
|
||||
protected readonly List<CategoryMapping> _categoryMapping = new List<CategoryMapping>();
|
||||
protected readonly List<string> _defaultCategories = new List<string>();
|
||||
|
||||
protected readonly string[] OptionalFields = new string[] { "imdb", "imdbid", "rageid", "tmdbid", "tvdbid", "poster", "banner", "description", "doubanid" };
|
||||
protected readonly string[] OptionalFields = new string[] { "imdb", "imdbid", "rageid", "tmdbid", "tvdbid", "poster", "banner", "description" };
|
||||
|
||||
protected static readonly string[] _SupportedLogicFunctions =
|
||||
{
|
||||
@@ -75,7 +75,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
continue;
|
||||
}
|
||||
|
||||
_categories.AddCategoryMapping(category.Key, cat);
|
||||
AddCategoryMapping(category.Key, cat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
}
|
||||
}
|
||||
|
||||
_categories.AddCategoryMapping(categorymapping.id, torznabCat, categorymapping.desc);
|
||||
AddCategoryMapping(categorymapping.id, torznabCat, categorymapping.desc);
|
||||
|
||||
if (categorymapping.Default)
|
||||
{
|
||||
@@ -105,6 +105,30 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
}
|
||||
}
|
||||
|
||||
public void AddCategoryMapping(string trackerCategory, IndexerCategory torznabCategory, string trackerCategoryDesc = null)
|
||||
{
|
||||
_categoryMapping.Add(new CategoryMapping(trackerCategory, trackerCategoryDesc, torznabCategory.Id));
|
||||
|
||||
if (trackerCategoryDesc == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// create custom cats (1:1 categories) if trackerCategoryDesc is defined
|
||||
// - if trackerCategory is "integer" we use that number to generate custom category id
|
||||
// - if trackerCategory is "string" we compute a hash to generate fixed integer id for the custom category
|
||||
// the hash is not perfect but it should work in most cases. we can't use sequential numbers because
|
||||
// categories are updated frequently and the id must be fixed to work in 3rd party apps
|
||||
if (!int.TryParse(trackerCategory, out var trackerCategoryInt))
|
||||
{
|
||||
var hashed = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(trackerCategory));
|
||||
trackerCategoryInt = BitConverter.ToUInt16(hashed, 0); // id between 0 and 65535 < 100000
|
||||
}
|
||||
|
||||
var customCat = new IndexerCategory(trackerCategoryInt + 100000, trackerCategoryDesc);
|
||||
_categoryMapping.Add(new CategoryMapping(trackerCategory, trackerCategoryDesc, customCat.Id));
|
||||
}
|
||||
|
||||
protected IElement QuerySelector(IElement element, string selector)
|
||||
{
|
||||
// AngleSharp doesn't support the :root pseudo selector, so we check for it manually
|
||||
@@ -338,9 +362,57 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
return variables;
|
||||
}
|
||||
|
||||
public delegate string TemplateTextModifier(string str);
|
||||
protected ICollection<IndexerCategory> MapTrackerCatToNewznab(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return new List<IndexerCategory>();
|
||||
}
|
||||
|
||||
public string ApplyGoTemplateText(string template, Dictionary<string, object> variables = null, TemplateTextModifier modifier = null)
|
||||
var cats = _categoryMapping
|
||||
.Where(m =>
|
||||
!string.IsNullOrWhiteSpace(m.TrackerCategory) &&
|
||||
string.Equals(m.TrackerCategory, input, StringComparison.InvariantCultureIgnoreCase))
|
||||
.Select(c => NewznabStandardCategory.AllCats.FirstOrDefault(n => n.Id == c.NewzNabCategory) ?? new IndexerCategory { Id = c.NewzNabCategory })
|
||||
.ToList();
|
||||
return cats;
|
||||
}
|
||||
|
||||
public List<string> MapTorznabCapsToTrackers(int[] searchCategories, bool mapChildrenCatsToParent = false)
|
||||
{
|
||||
if (searchCategories == null)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
var results = new List<string>();
|
||||
|
||||
results.AddRange(_categoryMapping
|
||||
.Where(c => searchCategories.Contains(c.NewzNabCategory))
|
||||
.Select(mapping => mapping.TrackerCategory).Distinct().ToList());
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public ICollection<IndexerCategory> MapTrackerCatDescToNewznab(string trackerCategoryDesc)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(trackerCategoryDesc))
|
||||
{
|
||||
return new List<IndexerCategory>();
|
||||
}
|
||||
|
||||
var cats = _categoryMapping
|
||||
.Where(m =>
|
||||
!string.IsNullOrWhiteSpace(m.TrackerCategoryDesc) &&
|
||||
string.Equals(m.TrackerCategoryDesc, trackerCategoryDesc, StringComparison.InvariantCultureIgnoreCase))
|
||||
.Select(c => NewznabStandardCategory.AllCats.FirstOrDefault(n => n.Id == c.NewzNabCategory) ?? new IndexerCategory { Id = c.NewzNabCategory })
|
||||
.ToList();
|
||||
return cats;
|
||||
}
|
||||
|
||||
protected delegate string TemplateTextModifier(string str);
|
||||
|
||||
protected string ApplyGoTemplateText(string template, Dictionary<string, object> variables = null, TemplateTextModifier modifier = null)
|
||||
{
|
||||
if (variables == null)
|
||||
{
|
||||
@@ -520,7 +592,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
}
|
||||
|
||||
// handle range expression
|
||||
var rangeRegex = new Regex(@"{{\s*range\s*(((?<index>\$.+?),)((\s*(?<element>.+?)\s*(:=)\s*)))?(?<variable>.+?)\s*}}(?<prefix>.*?){{\.}}(?<postfix>.*?){{end}}");
|
||||
var rangeRegex = new Regex(@"{{\s*range\s*(.+?)\s*}}(.*?){{\.}}(.*?){{end}}");
|
||||
var rangeRegexMatches = rangeRegex.Match(template);
|
||||
|
||||
while (rangeRegexMatches.Success)
|
||||
@@ -528,13 +600,9 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
var expanded = string.Empty;
|
||||
|
||||
var all = rangeRegexMatches.Groups[0].Value;
|
||||
var index = rangeRegexMatches.Groups["index"].Value;
|
||||
var variable = rangeRegexMatches.Groups["variable"].Value;
|
||||
var prefix = rangeRegexMatches.Groups["prefix"].Value;
|
||||
var postfix = rangeRegexMatches.Groups["postfix"].Value;
|
||||
|
||||
var arrayIndex = 0;
|
||||
var indexReplace = "{{" + index + "}}";
|
||||
var variable = rangeRegexMatches.Groups[1].Value;
|
||||
var prefix = rangeRegexMatches.Groups[2].Value;
|
||||
var postfix = rangeRegexMatches.Groups[3].Value;
|
||||
|
||||
foreach (var value in (ICollection<string>)variables[variable])
|
||||
{
|
||||
@@ -544,16 +612,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
newvalue = modifier(newvalue);
|
||||
}
|
||||
|
||||
var indexValue = arrayIndex++;
|
||||
|
||||
if (index.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
expanded += prefix.Replace(indexReplace, indexValue.ToString()) + newvalue + postfix.Replace(indexReplace, indexValue.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
expanded += prefix + newvalue + postfix;
|
||||
}
|
||||
expanded += prefix + newvalue + postfix;
|
||||
}
|
||||
|
||||
template = template.Replace(all, expanded);
|
||||
|
||||
@@ -60,9 +60,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
|
||||
if (request.SearchPath.Response != null && request.SearchPath.Response.Type.Equals("json"))
|
||||
{
|
||||
if (request.SearchPath.Response != null &&
|
||||
request.SearchPath.Response.NoResultsMessage != null &&
|
||||
((request.SearchPath.Response.NoResultsMessage != string.Empty && results.Contains(request.SearchPath.Response.NoResultsMessage)) || (request.SearchPath.Response.NoResultsMessage == string.Empty && results == string.Empty)))
|
||||
if (request.SearchPath.Response != null && request.SearchPath.Response.NoResultsMessage != null && (request.SearchPath.Response.NoResultsMessage.Equals(results) || (request.SearchPath.Response.NoResultsMessage == string.Empty && results == string.Empty)))
|
||||
{
|
||||
return releases;
|
||||
}
|
||||
@@ -453,7 +451,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
value = release.Description;
|
||||
break;
|
||||
case "category":
|
||||
var cats = _categories.MapTrackerCatToNewznab(value);
|
||||
var cats = MapTrackerCatToNewznab(value);
|
||||
if (cats.Any())
|
||||
{
|
||||
if (release.Categories == null || fieldModifiers.Contains("noappend"))
|
||||
@@ -469,7 +467,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
value = release.Categories.ToString();
|
||||
break;
|
||||
case "categorydesc":
|
||||
var catsDesc = _categories.MapTrackerCatDescToNewznab(value);
|
||||
var catsDesc = MapTrackerCatDescToNewznab(value);
|
||||
if (catsDesc.Any())
|
||||
{
|
||||
if (release.Categories == null || fieldModifiers.Contains("noappend"))
|
||||
@@ -577,13 +575,6 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
release.TvdbId = (int)ParseUtil.CoerceLong(tvdbId);
|
||||
value = release.TvdbId.ToString();
|
||||
break;
|
||||
case "doubanid":
|
||||
var doubanIDRegEx = new Regex(@"(\d+)", RegexOptions.Compiled);
|
||||
var doubanIDMatch = doubanIDRegEx.Match(value);
|
||||
var doubanID = doubanIDMatch.Groups[1].Value;
|
||||
release.DoubanId = (int)ParseUtil.CoerceLong(doubanID);
|
||||
value = release.DoubanId.ToString();
|
||||
break;
|
||||
case "poster":
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
|
||||
@@ -91,7 +91,6 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
variables[".Query.IMDBID"] = searchCriteria.FullImdbId;
|
||||
variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId;
|
||||
variables[".Query.TVDBID"] = searchCriteria.TvdbId?.ToString() ?? null;
|
||||
variables[".Query.TMDBID"] = searchCriteria.TmdbId?.ToString() ?? null;
|
||||
variables[".Query.TVRageID"] = searchCriteria.RId?.ToString() ?? null;
|
||||
variables[".Query.TVMazeID"] = searchCriteria.TvMazeId?.ToString() ?? null;
|
||||
variables[".Query.TraktID"] = searchCriteria.TraktId?.ToString() ?? null;
|
||||
@@ -976,7 +975,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
var search = _definition.Search;
|
||||
|
||||
var mappedCategories = _categories.MapTorznabCapsToTrackers((int[])variables[".Query.Categories"]);
|
||||
var mappedCategories = MapTorznabCapsToTrackers((int[])variables[".Query.Categories"]);
|
||||
if (mappedCategories.Count == 0)
|
||||
{
|
||||
mappedCategories = _defaultCategories;
|
||||
@@ -1001,7 +1000,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
}
|
||||
|
||||
variables[".Query.Keywords"] = string.Join(" ", keywordTokens);
|
||||
variables[".Keywords"] = ApplyFilters((string)variables[".Query.Keywords"], search.Keywordsfilters, variables);
|
||||
variables[".Keywords"] = ApplyFilters((string)variables[".Query.Keywords"], search.Keywordsfilters);
|
||||
|
||||
// TODO: prepare queries first and then send them parallel
|
||||
var searchPaths = search.Paths;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Definitions.Avistaz;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -30,14 +30,15 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
};
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new ExoticaZParser(Capabilities.Categories);
|
||||
}
|
||||
|
||||
protected override IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities();
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.XXXx264, "Video Clip");
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.XXXPack, "Video Pack");
|
||||
@@ -51,21 +52,4 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExoticaZParser : AvistazParser
|
||||
{
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public ExoticaZParser(IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
protected override List<IndexerCategory> ParseCategories(AvistazRelease row)
|
||||
{
|
||||
var cat = row.Category;
|
||||
|
||||
return cat.SelectMany(c => _categories.MapTrackerCatToNewznab(c.Key)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
[FieldDefinition(2, Label = "Username", HelpText = "Site Username", Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Passkey", HelpText = "Site Passkey (This is the alphanumeric string in the tracker url shown in your download client)", Privacy = PrivacyLevel.Password, Type = FieldType.Password)]
|
||||
[FieldDefinition(3, Label = "Passkey", HelpText = "Site Passkey", Privacy = PrivacyLevel.Password, Type = FieldType.Password)]
|
||||
public string Passkey { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
@@ -34,4 +34,28 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
|
||||
public enum FileListCategories
|
||||
{
|
||||
[FieldOption]
|
||||
Movie_SD = 1,
|
||||
[FieldOption]
|
||||
Movie_DVD = 2,
|
||||
[FieldOption]
|
||||
Movie_DVDRO = 3,
|
||||
[FieldOption]
|
||||
Movie_HD = 4,
|
||||
[FieldOption]
|
||||
Movie_HDRO = 19,
|
||||
[FieldOption]
|
||||
Movie_BluRay = 20,
|
||||
[FieldOption]
|
||||
Movie_BluRay4K = 26,
|
||||
[FieldOption]
|
||||
Movie_3D = 25,
|
||||
[FieldOption]
|
||||
Movie_4K = 6,
|
||||
[FieldOption]
|
||||
Xxx = 7
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
public int TotalSeeders { get; set; }
|
||||
public int TotalSnatched { get; set; }
|
||||
public long MaxSize { get; set; }
|
||||
public string GroupTime { get; set; }
|
||||
public long GroupTime { get; set; }
|
||||
public List<GazelleTorrent> Torrents { get; set; }
|
||||
public bool IsFreeLeech { get; set; }
|
||||
public bool IsNeutralLeech { get; set; }
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Net;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Gazelle
|
||||
@@ -121,7 +120,7 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
|
||||
Files = result.FileCount,
|
||||
Grabs = result.Snatches,
|
||||
PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime((string)result.GroupTime),
|
||||
PublishDate = DateTimeOffset.FromUnixTimeSeconds(result.GroupTime).UtcDateTime,
|
||||
PosterUrl = posterUrl,
|
||||
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1,
|
||||
UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using AngleSharp.Html.Parser;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
@@ -13,6 +14,7 @@ using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
@@ -198,11 +200,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
if (Settings.UserAgent.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.HttpRequest.Headers.UserAgent = Settings.UserAgent;
|
||||
}
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
@@ -358,9 +355,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
}
|
||||
|
||||
[FieldDefinition(2, Label = "Cookie User-Agent", Type = FieldType.Textbox, HelpText = "User-Agent associated with cookie used from Browser")]
|
||||
public string UserAgent { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "FreeLeech Only", Type = FieldType.Checkbox, Advanced = true, HelpText = "Search Freeleech torrents only")]
|
||||
public bool FreeLeechOnly { get; set; }
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
get
|
||||
{
|
||||
yield return GetDefinition("abNZB", GetSettings("https://abnzb.com"));
|
||||
yield return GetDefinition("altHUB", GetSettings("https://api.althub.co.za"));
|
||||
yield return GetDefinition("altHUB", GetSettings("https://althub.co.za"));
|
||||
yield return GetDefinition("AnimeTosho (Usenet)", GetSettings("https://feed.animetosho.org"));
|
||||
yield return GetDefinition("DOGnzb", GetSettings("https://api.dognzb.cr"));
|
||||
yield return GetDefinition("DrunkenSlug", GetSettings("https://drunkenslug.com"));
|
||||
@@ -187,7 +187,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
}
|
||||
|
||||
if (capabilities.TvSearchParams != null &&
|
||||
new[] { TvSearchParam.Q, TvSearchParam.TvdbId, TvSearchParam.TmdbId, TvSearchParam.RId }.Any(v => capabilities.TvSearchParams.Contains(v)) &&
|
||||
new[] { TvSearchParam.Q, TvSearchParam.TvdbId, TvSearchParam.RId }.Any(v => capabilities.TvSearchParams.Contains(v)) &&
|
||||
new[] { TvSearchParam.Season, TvSearchParam.Ep }.All(v => capabilities.TvSearchParams.Contains(v)))
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -125,11 +125,6 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
parameters.Add("tvdbid", searchCriteria.TvdbId.Value.ToString());
|
||||
}
|
||||
|
||||
if (searchCriteria.TmdbId.HasValue && capabilities.TvSearchTvdbAvailable)
|
||||
{
|
||||
parameters.Add("tmdbid", searchCriteria.TvdbId.Value.ToString());
|
||||
}
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace() && capabilities.TvSearchImdbAvailable)
|
||||
{
|
||||
parameters.Add("imdbid", searchCriteria.ImdbId);
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
|
||||
releaseInfo = base.ProcessItem(item, releaseInfo);
|
||||
releaseInfo.ImdbId = GetIntAttribute(item, "imdb");
|
||||
releaseInfo.TmdbId = GetIntAttribute(item, "tmdbid");
|
||||
releaseInfo.TmdbId = GetIntAttribute(item, "tmdb");
|
||||
releaseInfo.TvdbId = GetIntAttribute(item, "tvdbid");
|
||||
releaseInfo.TvRageId = GetIntAttribute(item, "rageid");
|
||||
releaseInfo.Grabs = GetIntAttribute(item, "grabs");
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
public class PassThePopcornInfo : TorrentInfo
|
||||
{
|
||||
public bool? Golden { get; set; }
|
||||
public bool? Scene { get; set; }
|
||||
public bool? Approved { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
// Only add approved torrents
|
||||
try
|
||||
{
|
||||
torrentInfos.Add(new TorrentInfo()
|
||||
torrentInfos.Add(new PassThePopcornInfo()
|
||||
{
|
||||
Guid = string.Format("PassThePopcorn-{0}", id),
|
||||
Title = title,
|
||||
@@ -104,6 +104,9 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.UploadTime.ToUniversalTime(),
|
||||
Golden = torrent.GoldenPopcorn,
|
||||
Scene = torrent.Scene,
|
||||
Approved = torrent.Checked,
|
||||
ImdbId = result.ImdbId.IsNotNullOrWhiteSpace() ? int.Parse(result.ImdbId) : 0,
|
||||
IndexerFlags = flags,
|
||||
MinimumRatio = 1,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
@@ -39,21 +37,9 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
|
||||
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
|
||||
{
|
||||
var queryParams = new NameValueCollection
|
||||
{
|
||||
{ "action", "advanced" },
|
||||
{ "json", "noredirect" },
|
||||
{ "searchstr", searchParameters }
|
||||
};
|
||||
|
||||
if (Settings.FreeleechOnly)
|
||||
{
|
||||
queryParams.Add("freetorrent", "1");
|
||||
}
|
||||
|
||||
var request =
|
||||
new IndexerRequest(
|
||||
$"{Settings.BaseUrl.Trim().TrimEnd('/')}/torrents.php?{queryParams.GetQueryString()}",
|
||||
$"{Settings.BaseUrl.Trim().TrimEnd('/')}/torrents.php?action=advanced&json=noredirect&searchstr={searchParameters}",
|
||||
HttpAccept.Json);
|
||||
|
||||
request.HttpRequest.Headers["ApiUser"] = Settings.APIUser;
|
||||
|
||||
@@ -22,15 +22,12 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
}
|
||||
|
||||
[FieldDefinition(2, Label = "API User", HelpText = "These settings are found in your PassThePopcorn security settings (Edit Profile > Security).", Privacy = PrivacyLevel.UserName)]
|
||||
[FieldDefinition(2, Label = "APIUser", HelpText = "These settings are found in your PassThePopcorn security settings (Edit Profile > Security).", Privacy = PrivacyLevel.UserName)]
|
||||
public string APIUser { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "API Key", HelpText = "Site API Key", Privacy = PrivacyLevel.ApiKey)]
|
||||
public string APIKey { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Freeleech Only", HelpText = "Return only freeleech torrents", Type = FieldType.Checkbox)]
|
||||
public bool FreeleechOnly { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
||||
@@ -16,7 +16,6 @@ using NzbDrone.Core.Indexers.Gazelle;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@@ -277,7 +276,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
InfoUrl = infoUrl,
|
||||
Seeders = int.Parse(result.Seeders),
|
||||
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
|
||||
PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime,
|
||||
PublishDate = DateTimeOffset.FromUnixTimeSeconds(result.GroupTime).UtcDateTime,
|
||||
Freeleech = result.IsFreeLeech || result.IsPersonalFreeLeech,
|
||||
Files = result.FileCount,
|
||||
Grabs = result.Snatches,
|
||||
|
||||
@@ -17,7 +17,6 @@ using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Gazelle;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@@ -183,7 +182,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
|
||||
Files = result.FileCount,
|
||||
Grabs = result.Snatches,
|
||||
PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime,
|
||||
PublishDate = DateTimeOffset.FromUnixTimeSeconds(result.GroupTime).UtcDateTime,
|
||||
};
|
||||
|
||||
var category = result.Category;
|
||||
|
||||
183
src/NzbDrone.Core/Indexers/Definitions/ShowRSS.cs
Normal file
183
src/NzbDrone.Core/Indexers/Definitions/ShowRSS.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class ShowRSS : TorrentIndexerBase<NoAuthTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "ShowRSS";
|
||||
public override string[] IndexerUrls => new string[] { "https://showrss.info/" };
|
||||
public override string Language => "en-US";
|
||||
public override string Description => "showRSS is a service that allows you to keep track of your favorite TV shows";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public ShowRSS(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new ShowRSSRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new ShowRSSParser(Settings);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.TV);
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVSD);
|
||||
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.TVHD);
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class ShowRSSRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public NoAuthTorrentBaseSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
public ShowRSSRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories)
|
||||
{
|
||||
var searchUrl = string.Format("{0}/other/all.rss", Settings.BaseUrl.TrimEnd('/'));
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class ShowRSSParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly NoAuthTorrentBaseSettings _settings;
|
||||
private string BrowseUrl => _settings.BaseUrl + "browse/";
|
||||
|
||||
public ShowRSSParser(NoAuthTorrentBaseSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
|
||||
var xmlDoc = new XmlDocument();
|
||||
xmlDoc.LoadXml(indexerResponse.Content);
|
||||
foreach (XmlNode node in xmlDoc.GetElementsByTagName("item"))
|
||||
{
|
||||
var title = node.SelectSingleNode(".//*[local-name()='raw_title']").InnerText;
|
||||
|
||||
// TODO: Make sure we don't return all sorts of trash
|
||||
//if (!query.MatchQueryStringAND(title))
|
||||
//{
|
||||
// continue;
|
||||
//}
|
||||
var category = title.Contains("720p") || title.Contains("1080p") ?
|
||||
NewznabStandardCategory.TVHD :
|
||||
NewznabStandardCategory.TVSD;
|
||||
|
||||
var magnetUri = node.SelectSingleNode("link")?.InnerText;
|
||||
var publishDate = DateTime.Parse(node.SelectSingleNode("pubDate").InnerText, CultureInfo.InvariantCulture);
|
||||
var infoHash = node.SelectSingleNode(".//*[local-name()='info_hash']").InnerText;
|
||||
var details = BrowseUrl + node.SelectSingleNode(".//*[local-name()='show_id']").InnerText;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Title = title,
|
||||
InfoUrl = details,
|
||||
Categories = new List<IndexerCategory> { category },
|
||||
Guid = magnetUri,
|
||||
PublishDate = publishDate,
|
||||
InfoHash = infoHash,
|
||||
MagnetUrl = magnetUri,
|
||||
Size = 512,
|
||||
Seeders = 1,
|
||||
Peers = 2,
|
||||
DownloadVolumeFactor = 0,
|
||||
UploadVolumeFactor = 1
|
||||
};
|
||||
|
||||
torrentInfos.Add(release);
|
||||
}
|
||||
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Indexers.Settings
|
||||
[FieldDefinition(4)]
|
||||
public IndexerTorrentBaseSettings TorrentBaseSettings { get; set; } = new IndexerTorrentBaseSettings();
|
||||
|
||||
public virtual NzbDroneValidationResult Validate()
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
@@ -117,6 +117,7 @@ namespace NzbDrone.Core.Instrumentation
|
||||
syslogTarget.MessageSend.Protocol = ProtocolType.Udp;
|
||||
syslogTarget.MessageSend.Udp.Port = syslogPort;
|
||||
syslogTarget.MessageSend.Udp.Server = syslogServer;
|
||||
syslogTarget.MessageSend.Udp.ReconnectInterval = 500;
|
||||
syslogTarget.MessageCreation.Rfc = RfcNumber.Rfc5424;
|
||||
syslogTarget.MessageCreation.Rfc5424.AppName = _configFileProvider.InstanceName;
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace NzbDrone.Core.Parser.Model
|
||||
public int ImdbId { get; set; }
|
||||
public int TmdbId { get; set; }
|
||||
public int TraktId { get; set; }
|
||||
public int DoubanId { get; set; }
|
||||
public int Year { get; set; }
|
||||
public string Author { get; set; }
|
||||
public string BookTitle { get; set; }
|
||||
|
||||
@@ -3,27 +3,27 @@
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp.Xml" Version="0.17.0" />
|
||||
<PackageReference Include="AngleSharp.Xml" Version="0.16.0" />
|
||||
<PackageReference Include="Dapper" Version="2.0.123" />
|
||||
<PackageReference Include="FluentMigrator.Runner" Version="3.3.2" />
|
||||
<PackageReference Include="MailKit" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
|
||||
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
|
||||
<PackageReference Include="NLog.Targets.Syslog" Version="6.0.3" />
|
||||
<PackageReference Include="Npgsql" Version="5.0.11" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
<PackageReference Include="System.ServiceModel.Syndication" Version="6.0.0" />
|
||||
<PackageReference Include="FluentMigrator.Runner.SQLite" Version="3.3.2" />
|
||||
<PackageReference Include="FluentMigrator.Runner.Postgres" Version="3.3.2" />
|
||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NLog" Version="5.0.1" />
|
||||
<PackageReference Include="NLog" Version="4.7.14" />
|
||||
<PackageReference Include="TinyTwitter" Version="1.1.2" />
|
||||
<PackageReference Include="Kveer.XmlRPC" Version="1.1.1" />
|
||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.5" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.2" />
|
||||
<PackageReference Include="MonoTorrent" Version="2.0.5" />
|
||||
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
||||
<PackageReference Include="AngleSharp" Version="0.17.1" />
|
||||
<PackageReference Include="AngleSharp" Version="0.16.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Common\Prowlarr.Common.csproj" />
|
||||
|
||||
@@ -146,24 +146,16 @@ namespace NzbDrone.Core.Update
|
||||
_logger.Info("Preparing client");
|
||||
_diskTransferService.TransferFolder(_appFolderInfo.GetUpdateClientFolder(), updateSandboxFolder, TransferMode.Move);
|
||||
|
||||
var updateClientExePath = _appFolderInfo.GetUpdateClientExePath(updatePackage.Runtime);
|
||||
|
||||
if (!_diskProvider.FileExists(updateClientExePath))
|
||||
{
|
||||
_logger.Warn("Update client {0} does not exist, aborting update.", updateClientExePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set executable flag on update app
|
||||
if (OsInfo.IsOsx || (OsInfo.IsLinux && PlatformInfo.IsNetCore))
|
||||
{
|
||||
_diskProvider.SetFilePermissions(updateClientExePath, "755", null);
|
||||
_diskProvider.SetPermissions(_appFolderInfo.GetUpdateClientExePath(updatePackage.Runtime), "0755");
|
||||
}
|
||||
|
||||
_logger.Info("Starting update client {0}", updateClientExePath);
|
||||
_logger.Info("Starting update client {0}", _appFolderInfo.GetUpdateClientExePath(updatePackage.Runtime));
|
||||
_logger.ProgressInfo("Prowlarr will restart shortly.");
|
||||
|
||||
_processProvider.Start(updateClientExePath, GetUpdaterArgs(updateSandboxFolder));
|
||||
_processProvider.Start(_appFolderInfo.GetUpdateClientExePath(updatePackage.Runtime), GetUpdaterArgs(updateSandboxFolder));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -306,6 +298,14 @@ namespace NzbDrone.Core.Update
|
||||
// Check if we have to do an application update on startup
|
||||
try
|
||||
{
|
||||
// Don't do a prestartup update check unless BuiltIn update is enabled
|
||||
if (_configFileProvider.UpdateAutomatically ||
|
||||
_configFileProvider.UpdateMechanism != UpdateMechanism.BuiltIn ||
|
||||
_deploymentInfoProvider.IsExternalUpdateMechanism)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var updateMarker = Path.Combine(_appFolderInfo.AppDataFolder, "update_required");
|
||||
if (!_diskProvider.FileExists(updateMarker))
|
||||
{
|
||||
@@ -314,15 +314,6 @@ namespace NzbDrone.Core.Update
|
||||
|
||||
_logger.Debug("Post-install update check requested");
|
||||
|
||||
// Don't do a prestartup update check unless BuiltIn update is enabled
|
||||
if (!_configFileProvider.UpdateAutomatically ||
|
||||
_configFileProvider.UpdateMechanism != UpdateMechanism.BuiltIn ||
|
||||
_deploymentInfoProvider.IsExternalUpdateMechanism)
|
||||
{
|
||||
_logger.Debug("Built-in updater disabled, skipping post-install update check");
|
||||
return;
|
||||
}
|
||||
|
||||
var latestAvailable = _checkUpdateService.AvailableUpdate();
|
||||
if (latestAvailable == null)
|
||||
{
|
||||
|
||||
@@ -3,11 +3,11 @@ using NzbDrone.Common.Disk;
|
||||
|
||||
namespace NzbDrone.Core.Validation
|
||||
{
|
||||
public class FolderChmodValidator : PropertyValidator
|
||||
public class FileChmodValidator : PropertyValidator
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
|
||||
public FolderChmodValidator(IDiskProvider diskProvider)
|
||||
public FileChmodValidator(IDiskProvider diskProvider)
|
||||
: base("Must contain a valid Unix permissions octal")
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Validation
|
||||
return false;
|
||||
}
|
||||
|
||||
return _diskProvider.IsValidFolderPermissionMask(context.PropertyValue.ToString());
|
||||
return _diskProvider.IsValidFilePermissionMask(context.PropertyValue.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,10 +62,10 @@ namespace NzbDrone.Core.Validation
|
||||
return ruleBuilder.WithState(v => NzbDroneValidationState.Warning);
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ContainsProwlarr<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
public static IRuleBuilderOptions<T, string> StartsOrEndsWithProwlarr<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
return ruleBuilder.SetValidator(new RegularExpressionValidator("prowlarr", RegexOptions.IgnoreCase)).WithMessage("Must contain Prowlarr");
|
||||
return ruleBuilder.SetValidator(new RegularExpressionValidator("^Prowlarr|Prowlarr$")).WithMessage("Must start or end with Prowlarr");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,13 @@ using DryIoc.Microsoft.DependencyInjection;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Composition.Extensions;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Jobs;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
@@ -46,7 +43,6 @@ namespace NzbDrone.App.Test
|
||||
// dummy lifetime and broadcaster so tests resolve
|
||||
container.RegisterInstance<IHostLifetime>(new Mock<IHostLifetime>().Object);
|
||||
container.RegisterInstance<IBroadcastSignalRMessage>(new Mock<IBroadcastSignalRMessage>().Object);
|
||||
container.RegisterInstance<IOptions<PostgresOptions>>(new Mock<IOptions<PostgresOptions>>().Object);
|
||||
|
||||
_container = container.GetServiceProvider();
|
||||
}
|
||||
@@ -57,12 +53,6 @@ namespace NzbDrone.App.Test
|
||||
_container.GetRequiredService<IEnumerable<IIndexer>>().Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_resolve_downloadclients()
|
||||
{
|
||||
_container.GetRequiredService<IEnumerable<IDownloadClient>>().Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void container_should_inject_itself()
|
||||
{
|
||||
|
||||
@@ -9,12 +9,8 @@ using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using DryIoc;
|
||||
using DryIoc.Microsoft.DependencyInjection;
|
||||
using FluentMigrator.Runner.Processors.Postgres;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Hosting.WindowsServices;
|
||||
using NLog;
|
||||
@@ -27,8 +23,6 @@ using NzbDrone.Common.Instrumentation;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using NzbDrone.Host;
|
||||
using PostgresOptions = NzbDrone.Core.Datastore.PostgresOptions;
|
||||
|
||||
namespace NzbDrone.Host
|
||||
{
|
||||
@@ -58,7 +52,6 @@ namespace NzbDrone.Host
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
var appMode = GetApplicationMode(startupContext);
|
||||
var config = GetConfiguration(startupContext);
|
||||
|
||||
switch (appMode)
|
||||
{
|
||||
@@ -87,22 +80,12 @@ namespace NzbDrone.Host
|
||||
// Utility mode
|
||||
default:
|
||||
{
|
||||
new HostBuilder()
|
||||
.UseServiceProviderFactory(new DryIocServiceProviderFactory(new Container(rules => rules.WithNzbDroneRules())))
|
||||
.ConfigureContainer<IContainer>(c =>
|
||||
{
|
||||
c.AutoAddServices(Bootstrap.ASSEMBLIES)
|
||||
.AddNzbDroneLogger()
|
||||
.AddDatabase()
|
||||
.AddStartupContext(startupContext)
|
||||
.Resolve<UtilityModeRouter>()
|
||||
.Route(appMode);
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.Configure<PostgresOptions>(config.GetSection("Prowlarr:Postgres"));
|
||||
}).Build();
|
||||
|
||||
new Container(rules => rules.WithNzbDroneRules())
|
||||
.AutoAddServices(ASSEMBLIES)
|
||||
.AddNzbDroneLogger()
|
||||
.AddStartupContext(startupContext)
|
||||
.Resolve<UtilityModeRouter>()
|
||||
.Route(appMode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -152,15 +135,6 @@ namespace NzbDrone.Host
|
||||
.AddDatabase()
|
||||
.AddStartupContext(context);
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.Configure<PostgresOptions>(config.GetSection("Prowlarr:Postgres"));
|
||||
services.Configure<FormOptions>(x =>
|
||||
{
|
||||
//Double the default multipart body length from 128 MB to 256 MB
|
||||
x.MultipartBodyLengthLimit = 268435456;
|
||||
});
|
||||
})
|
||||
.ConfigureWebHost(builder =>
|
||||
{
|
||||
builder.UseConfiguration(config);
|
||||
@@ -231,7 +205,6 @@ namespace NzbDrone.Host
|
||||
return new ConfigurationBuilder()
|
||||
.AddXmlFile(appFolder.GetConfigPath(), optional: true, reloadOnChange: false)
|
||||
.AddInMemoryCollection(new List<KeyValuePair<string, string>> { new ("dataProtectionFolder", appFolder.GetDataProtectionPath()) })
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.4" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.3.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.3.0" />
|
||||
<PackageReference Include="DryIoc.dll" Version="4.8.8" />
|
||||
<PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="5.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using NLog;
|
||||
using Npgsql;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using NzbDrone.Core.Indexers.FileList;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Test.Common.Datastore;
|
||||
using Prowlarr.Http.ClientSchema;
|
||||
|
||||
namespace NzbDrone.Integration.Test
|
||||
@@ -25,8 +19,6 @@ namespace NzbDrone.Integration.Test
|
||||
|
||||
protected int Port { get; private set; }
|
||||
|
||||
protected PostgresOptions PostgresOptions { get; set; } = new ();
|
||||
|
||||
protected override string RootUrl => $"http://localhost:{Port}/";
|
||||
|
||||
protected override string ApiKey => _runner.ApiKey;
|
||||
@@ -35,14 +27,7 @@ namespace NzbDrone.Integration.Test
|
||||
{
|
||||
Port = Interlocked.Increment(ref StaticPort);
|
||||
|
||||
PostgresOptions = PostgresDatabase.GetTestOptions();
|
||||
|
||||
if (PostgresOptions?.Host != null)
|
||||
{
|
||||
CreatePostgresDb(PostgresOptions);
|
||||
}
|
||||
|
||||
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger(), PostgresOptions, Port);
|
||||
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger(), Port);
|
||||
_runner.Kill();
|
||||
|
||||
_runner.Start();
|
||||
@@ -71,22 +56,6 @@ namespace NzbDrone.Integration.Test
|
||||
protected override void StopTestTarget()
|
||||
{
|
||||
_runner.Kill();
|
||||
if (PostgresOptions?.Host != null)
|
||||
{
|
||||
DropPostgresDb(PostgresOptions);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreatePostgresDb(PostgresOptions options)
|
||||
{
|
||||
PostgresDatabase.Create(options, MigrationType.Main);
|
||||
PostgresDatabase.Create(options, MigrationType.Log);
|
||||
}
|
||||
|
||||
private static void DropPostgresDb(PostgresOptions options)
|
||||
{
|
||||
PostgresDatabase.Drop(options, MigrationType.Main);
|
||||
PostgresDatabase.Drop(options, MigrationType.Log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Test.Common\Prowlarr.Test.Common.csproj" />
|
||||
|
||||
@@ -18,32 +18,11 @@ namespace NzbDrone.Mono.Test.DiskProviderTests
|
||||
[Platform(Exclude = "Win")]
|
||||
public class DiskProviderFixture : DiskProviderFixtureBase<DiskProvider>
|
||||
{
|
||||
private string _tempPath;
|
||||
|
||||
public DiskProviderFixture()
|
||||
{
|
||||
PosixOnly();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void MonoDiskProviderFixtureTearDown()
|
||||
{
|
||||
// Give ourselves back write permissions so we can delete it
|
||||
if (_tempPath != null)
|
||||
{
|
||||
if (Directory.Exists(_tempPath))
|
||||
{
|
||||
Syscall.chmod(_tempPath, FilePermissions.S_IRWXU);
|
||||
}
|
||||
else if (File.Exists(_tempPath))
|
||||
{
|
||||
Syscall.chmod(_tempPath, FilePermissions.S_IRUSR | FilePermissions.S_IWUSR);
|
||||
}
|
||||
|
||||
_tempPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SetWritePermissions(string path, bool writable)
|
||||
{
|
||||
if (Environment.UserName == "root")
|
||||
@@ -51,41 +30,16 @@ namespace NzbDrone.Mono.Test.DiskProviderTests
|
||||
Assert.Inconclusive("Need non-root user to test write permissions.");
|
||||
}
|
||||
|
||||
SetWritePermissionsInternal(path, writable, false);
|
||||
}
|
||||
|
||||
protected void SetWritePermissionsInternal(string path, bool writable, bool setgid)
|
||||
{
|
||||
// Remove Write permissions, we're still owner so we can clean it up, but we'll have to do that explicitly.
|
||||
Stat stat;
|
||||
Syscall.stat(path, out stat);
|
||||
FilePermissions mode = stat.st_mode;
|
||||
var entry = UnixFileSystemInfo.GetFileSystemEntry(path);
|
||||
|
||||
if (writable)
|
||||
{
|
||||
mode |= FilePermissions.S_IWUSR | FilePermissions.S_IWGRP | FilePermissions.S_IWOTH;
|
||||
entry.FileAccessPermissions |= FileAccessPermissions.UserWrite | FileAccessPermissions.GroupWrite | FileAccessPermissions.OtherWrite;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode &= ~(FilePermissions.S_IWUSR | FilePermissions.S_IWGRP | FilePermissions.S_IWOTH);
|
||||
}
|
||||
|
||||
if (setgid)
|
||||
{
|
||||
mode |= FilePermissions.S_ISGID;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode &= ~FilePermissions.S_ISGID;
|
||||
}
|
||||
|
||||
if (stat.st_mode != mode)
|
||||
{
|
||||
if (Syscall.chmod(path, mode) < 0)
|
||||
{
|
||||
var error = Stdlib.GetLastError();
|
||||
throw new LinuxPermissionsException("Error setting group: " + error);
|
||||
}
|
||||
entry.FileAccessPermissions &= ~(FileAccessPermissions.UserWrite | FileAccessPermissions.GroupWrite | FileAccessPermissions.OtherWrite);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,25 +165,24 @@ namespace NzbDrone.Mono.Test.DiskProviderTests
|
||||
var tempFile = GetTempFilePath();
|
||||
|
||||
File.WriteAllText(tempFile, "File1");
|
||||
SetWritePermissionsInternal(tempFile, false, false);
|
||||
_tempPath = tempFile;
|
||||
SetWritePermissions(tempFile, false);
|
||||
|
||||
// Verify test setup
|
||||
Syscall.stat(tempFile, out var fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0444");
|
||||
|
||||
Subject.SetPermissions(tempFile, "755", null);
|
||||
Subject.SetPermissions(tempFile, "644");
|
||||
Syscall.stat(tempFile, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0644");
|
||||
|
||||
Subject.SetPermissions(tempFile, "0755", null);
|
||||
Subject.SetPermissions(tempFile, "0644");
|
||||
Syscall.stat(tempFile, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0644");
|
||||
|
||||
if (OsInfo.Os != Os.Bsd)
|
||||
{
|
||||
// This is not allowed on BSD
|
||||
Subject.SetPermissions(tempFile, "1775", null);
|
||||
Subject.SetPermissions(tempFile, "1664");
|
||||
Syscall.stat(tempFile, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("1664");
|
||||
}
|
||||
@@ -241,118 +194,62 @@ namespace NzbDrone.Mono.Test.DiskProviderTests
|
||||
var tempPath = GetTempFilePath();
|
||||
|
||||
Directory.CreateDirectory(tempPath);
|
||||
SetWritePermissionsInternal(tempPath, false, false);
|
||||
_tempPath = tempPath;
|
||||
SetWritePermissions(tempPath, false);
|
||||
|
||||
// Verify test setup
|
||||
Syscall.stat(tempPath, out var fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0555");
|
||||
|
||||
Subject.SetPermissions(tempPath, "755", null);
|
||||
Subject.SetPermissions(tempPath, "644");
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0755");
|
||||
|
||||
Subject.SetPermissions(tempPath, "775", null);
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0775");
|
||||
|
||||
Subject.SetPermissions(tempPath, "750", null);
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0750");
|
||||
|
||||
Subject.SetPermissions(tempPath, "051", null);
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0051");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_preserve_setgid_on_set_folder_permissions()
|
||||
{
|
||||
var tempPath = GetTempFilePath();
|
||||
|
||||
Directory.CreateDirectory(tempPath);
|
||||
SetWritePermissionsInternal(tempPath, false, true);
|
||||
_tempPath = tempPath;
|
||||
|
||||
// Verify test setup
|
||||
Syscall.stat(tempPath, out var fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("2555");
|
||||
|
||||
Subject.SetPermissions(tempPath, "755", null);
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("2755");
|
||||
|
||||
Subject.SetPermissions(tempPath, "775", null);
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("2775");
|
||||
|
||||
Subject.SetPermissions(tempPath, "750", null);
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("2750");
|
||||
|
||||
Subject.SetPermissions(tempPath, "051", null);
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("2051");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_clear_setgid_on_set_folder_permissions()
|
||||
{
|
||||
var tempPath = GetTempFilePath();
|
||||
|
||||
Directory.CreateDirectory(tempPath);
|
||||
SetWritePermissionsInternal(tempPath, false, true);
|
||||
_tempPath = tempPath;
|
||||
|
||||
// Verify test setup
|
||||
Syscall.stat(tempPath, out var fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("2555");
|
||||
|
||||
Subject.SetPermissions(tempPath, "0755", null);
|
||||
Subject.SetPermissions(tempPath, "0644");
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0755");
|
||||
|
||||
Subject.SetPermissions(tempPath, "0775", null);
|
||||
Subject.SetPermissions(tempPath, "1664");
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("1775");
|
||||
|
||||
Subject.SetPermissions(tempPath, "775");
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0775");
|
||||
|
||||
Subject.SetPermissions(tempPath, "0750", null);
|
||||
Subject.SetPermissions(tempPath, "640");
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0750");
|
||||
|
||||
Subject.SetPermissions(tempPath, "0051", null);
|
||||
Subject.SetPermissions(tempPath, "0041");
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0051");
|
||||
|
||||
// reinstate sane permissions so fokder can be cleaned up
|
||||
Subject.SetPermissions(tempPath, "775");
|
||||
Syscall.stat(tempPath, out fileStat);
|
||||
NativeConvert.ToOctalPermissionString(fileStat.st_mode).Should().Be("0775");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsValidFolderPermissionMask_should_return_correct()
|
||||
public void IsValidFilePermissionMask_should_return_correct()
|
||||
{
|
||||
// Files may not be executable
|
||||
Subject.IsValidFilePermissionMask("0777").Should().BeFalse();
|
||||
Subject.IsValidFilePermissionMask("0544").Should().BeFalse();
|
||||
Subject.IsValidFilePermissionMask("0454").Should().BeFalse();
|
||||
Subject.IsValidFilePermissionMask("0445").Should().BeFalse();
|
||||
|
||||
// No special bits should be set
|
||||
Subject.IsValidFolderPermissionMask("1755").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("2755").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("4755").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("7755").Should().BeFalse();
|
||||
Subject.IsValidFilePermissionMask("1644").Should().BeFalse();
|
||||
Subject.IsValidFilePermissionMask("2644").Should().BeFalse();
|
||||
Subject.IsValidFilePermissionMask("4644").Should().BeFalse();
|
||||
Subject.IsValidFilePermissionMask("7644").Should().BeFalse();
|
||||
|
||||
// Folder should be readable and writeable by owner
|
||||
Subject.IsValidFolderPermissionMask("000").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("100").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("200").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("300").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("400").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("500").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("600").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("700").Should().BeTrue();
|
||||
|
||||
// Folder should be readable and writeable by owner
|
||||
Subject.IsValidFolderPermissionMask("0000").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("0100").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("0200").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("0300").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("0400").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("0500").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("0600").Should().BeFalse();
|
||||
Subject.IsValidFolderPermissionMask("0700").Should().BeTrue();
|
||||
// Files should be readable and writeable by owner
|
||||
Subject.IsValidFilePermissionMask("0400").Should().BeFalse();
|
||||
Subject.IsValidFilePermissionMask("0000").Should().BeFalse();
|
||||
Subject.IsValidFilePermissionMask("0200").Should().BeFalse();
|
||||
Subject.IsValidFilePermissionMask("0600").Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Test.DiskTests;
|
||||
using NzbDrone.Mono.Disk;
|
||||
@@ -10,20 +8,9 @@ namespace NzbDrone.Mono.Test.DiskProviderTests
|
||||
[Platform(Exclude = "Win")]
|
||||
public class FreeSpaceFixture : FreeSpaceFixtureBase<DiskProvider>
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public FreeSpaceFixture()
|
||||
{
|
||||
Mocker.SetConstant<IProcMountProvider>(new ProcMountProvider(TestLogger));
|
||||
Mocker.GetMock<ISymbolicLinkResolver>()
|
||||
.Setup(v => v.GetCompleteRealPath(It.IsAny<string>()))
|
||||
.Returns<string>(s => s);
|
||||
}
|
||||
|
||||
[Ignore("Docker")]
|
||||
[Test]
|
||||
public void should_be_able_to_check_space_on_ramdrive()
|
||||
{
|
||||
Subject.GetAvailableSpace("/run/").Should().NotBe(0);
|
||||
PosixOnly();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,14 @@ using NzbDrone.Test.Common;
|
||||
namespace NzbDrone.Mono.Test.DiskProviderTests
|
||||
{
|
||||
[TestFixture]
|
||||
[Platform(Exclude = "Win")]
|
||||
[Platform("Mono")]
|
||||
public class SymbolicLinkResolverFixture : TestBase<SymbolicLinkResolver>
|
||||
{
|
||||
public SymbolicLinkResolverFixture()
|
||||
{
|
||||
MonoOnly();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_follow_nested_symlinks()
|
||||
{
|
||||
|
||||
@@ -61,41 +61,15 @@ namespace NzbDrone.Mono.Disk
|
||||
{
|
||||
}
|
||||
|
||||
public override void SetFilePermissions(string path, string mask, string group)
|
||||
{
|
||||
var permissions = NativeConvert.FromOctalPermissionString(mask);
|
||||
|
||||
SetPermissions(path, mask, group, permissions);
|
||||
}
|
||||
|
||||
public override void SetPermissions(string path, string mask, string group)
|
||||
{
|
||||
var permissions = NativeConvert.FromOctalPermissionString(mask);
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
permissions = GetFilePermissions(permissions);
|
||||
}
|
||||
|
||||
SetPermissions(path, mask, group, permissions);
|
||||
}
|
||||
|
||||
protected void SetPermissions(string path, string mask, string group, FilePermissions permissions)
|
||||
public override void SetPermissions(string path, string mask)
|
||||
{
|
||||
_logger.Debug("Setting permissions: {0} on {1}", mask, path);
|
||||
|
||||
// Preserve non-access permissions
|
||||
if (Syscall.stat(path, out var curStat) < 0)
|
||||
{
|
||||
var error = Stdlib.GetLastError();
|
||||
var permissions = NativeConvert.FromOctalPermissionString(mask);
|
||||
|
||||
throw new LinuxPermissionsException("Error getting current permissions: " + error);
|
||||
}
|
||||
|
||||
// Preserve existing non-access permissions unless mask is 4 digits
|
||||
if (mask.Length < 4)
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
permissions |= curStat.st_mode & ~FilePermissions.ACCESSPERMS;
|
||||
permissions = GetFolderPermissions(permissions);
|
||||
}
|
||||
|
||||
if (Syscall.chmod(path, permissions) < 0)
|
||||
@@ -104,39 +78,33 @@ namespace NzbDrone.Mono.Disk
|
||||
|
||||
throw new LinuxPermissionsException("Error setting permissions: " + error);
|
||||
}
|
||||
|
||||
var groupId = GetGroupId(group);
|
||||
|
||||
if (Syscall.chown(path, unchecked((uint)-1), groupId) < 0)
|
||||
{
|
||||
var error = Stdlib.GetLastError();
|
||||
|
||||
throw new LinuxPermissionsException("Error setting group: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
private static FilePermissions GetFilePermissions(FilePermissions permissions)
|
||||
private static FilePermissions GetFolderPermissions(FilePermissions permissions)
|
||||
{
|
||||
permissions &= ~(FilePermissions.S_IXUSR | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH);
|
||||
permissions |= (FilePermissions)((int)(permissions & (FilePermissions.S_IRUSR | FilePermissions.S_IRGRP | FilePermissions.S_IROTH)) >> 2);
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public override bool IsValidFolderPermissionMask(string mask)
|
||||
public override bool IsValidFilePermissionMask(string mask)
|
||||
{
|
||||
try
|
||||
{
|
||||
var permissions = NativeConvert.FromOctalPermissionString(mask);
|
||||
|
||||
if ((permissions & ~FilePermissions.ACCESSPERMS) != 0)
|
||||
if ((permissions & (FilePermissions.S_ISUID | FilePermissions.S_ISGID | FilePermissions.S_ISVTX)) != 0)
|
||||
{
|
||||
// Only allow access permissions
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((permissions & FilePermissions.S_IRWXU) != FilePermissions.S_IRWXU)
|
||||
if ((permissions & (FilePermissions.S_IXUSR | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH)) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((permissions & (FilePermissions.S_IRUSR | FilePermissions.S_IWUSR)) != (FilePermissions.S_IRUSR | FilePermissions.S_IWUSR))
|
||||
{
|
||||
// We expect at least full owner permissions (700)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -313,7 +281,9 @@ namespace NzbDrone.Mono.Disk
|
||||
// Catch the exception and attempt to handle these edgecases
|
||||
|
||||
// Mono 6.x till 6.10 doesn't properly try use rename first.
|
||||
if (move && (PlatformInfo.Platform == PlatformType.NetCore))
|
||||
if (move &&
|
||||
((PlatformInfo.Platform == PlatformType.Mono && PlatformInfo.GetVersion() < new Version(6, 10)) ||
|
||||
(PlatformInfo.Platform == PlatformType.NetCore)))
|
||||
{
|
||||
if (Syscall.lstat(source, out var sourcestat) == 0 &&
|
||||
Syscall.lstat(destination, out var deststat) != 0 &&
|
||||
@@ -341,7 +311,32 @@ namespace NzbDrone.Mono.Disk
|
||||
var dstInfo = new FileInfo(destination);
|
||||
var exists = dstInfo.Exists && srcInfo.Exists;
|
||||
|
||||
if (PlatformInfo.Platform == PlatformType.NetCore && exists && dstInfo.Length == srcInfo.Length)
|
||||
if (PlatformInfo.Platform == PlatformType.Mono && PlatformInfo.GetVersion() >= new Version(6, 6) &&
|
||||
exists && dstInfo.Length == 0 && srcInfo.Length != 0)
|
||||
{
|
||||
// mono >=6.6 bug: zero length file since chmod happens at the start
|
||||
_logger.Debug("{3} failed to {2} file likely due to known {3} bug, attempting to {2} directly. '{0}' -> '{1}'", source, destination, move ? "move" : "copy", PlatformInfo.PlatformName);
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Trace("Copying content from {0} to {1} ({2} bytes)", source, destination, srcInfo.Length);
|
||||
using (var srcStream = new FileStream(source, FileMode.Open, FileAccess.Read))
|
||||
using (var dstStream = new FileStream(destination, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
srcStream.CopyTo(dstStream);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If it fails again then bail
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else if (((PlatformInfo.Platform == PlatformType.Mono &&
|
||||
PlatformInfo.GetVersion() >= new Version(6, 0) &&
|
||||
PlatformInfo.GetVersion() < new Version(6, 6)) ||
|
||||
PlatformInfo.Platform == PlatformType.NetCore) &&
|
||||
exists && dstInfo.Length == srcInfo.Length)
|
||||
{
|
||||
// mono 6.0, mono 6.4 and netcore 3.1 bug: full length file since utime and chmod happens at the end
|
||||
_logger.Debug("{3} failed to {2} file likely due to known {3} bug, attempting to {2} directly. '{0}' -> '{1}'", source, destination, move ? "move" : "copy", PlatformInfo.PlatformName);
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace NzbDrone.Mono.Disk
|
||||
{ "apfs", DriveType.Fixed },
|
||||
{ "fuse.mergerfs", DriveType.Fixed },
|
||||
{ "fuse.glusterfs", DriveType.Network },
|
||||
{ "nullfs", DriveType.Fixed },
|
||||
{ "zfs", DriveType.Fixed }
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="5.20.1.34-servarr18" />
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user