mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-05 13:21:25 -05:00
Compare commits
318 Commits
v2.0.0.353
...
v2.0.0.423
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab63c3e83d | ||
|
|
c1f59a55c6 | ||
|
|
c8474701a0 | ||
|
|
c206b83318 | ||
|
|
a8b9a47f5f | ||
|
|
3e9a159466 | ||
|
|
ba817557ba | ||
|
|
30ed3a4a80 | ||
|
|
006dc9202b | ||
|
|
c2a2746ccf | ||
|
|
2fa0729158 | ||
|
|
b3eee50892 | ||
|
|
f1c007c5fe | ||
|
|
7c8a8f8e55 | ||
|
|
a3ade09964 | ||
|
|
8e429239a8 | ||
|
|
4783803b6b | ||
|
|
b320a23bf8 | ||
|
|
32a347bdd2 | ||
|
|
a0b0f6162f | ||
|
|
6c287f118f | ||
|
|
8213f020ff | ||
|
|
d984dd41d6 | ||
|
|
fea5db3e4b | ||
|
|
c38973cce4 | ||
|
|
958153be55 | ||
|
|
d4bab775df | ||
|
|
ecf67e609e | ||
|
|
449b15331a | ||
|
|
f0437d1f22 | ||
|
|
902d6929c0 | ||
|
|
d81e03fcc0 | ||
|
|
abf8c684e7 | ||
|
|
7476d692aa | ||
|
|
c25bea6470 | ||
|
|
b9d67ae421 | ||
|
|
ff3fc8de2e | ||
|
|
e4e3770e54 | ||
|
|
498a86f850 | ||
|
|
f0ae908892 | ||
|
|
54c17de849 | ||
|
|
12a1865d4d | ||
|
|
9ea753011b | ||
|
|
4c39594a57 | ||
|
|
67ff871cf6 | ||
|
|
ea0982ecae | ||
|
|
4f5d79b189 | ||
|
|
aec3ed16d0 | ||
|
|
2e90ea9c19 | ||
|
|
a6b1a1fc0d | ||
|
|
fd42ddec1b | ||
|
|
229986033c | ||
|
|
c249ad5dbe | ||
|
|
e2d6d374ab | ||
|
|
d3adb7ac40 | ||
|
|
0f1afd416b | ||
|
|
2f3bc61af7 | ||
|
|
2b11ad4585 | ||
|
|
c82b90aca8 | ||
|
|
5fae8e7762 | ||
|
|
319b4f13b7 | ||
|
|
54fda3d648 | ||
|
|
2f6fded7c3 | ||
|
|
7934003b5e | ||
|
|
b479064abd | ||
|
|
9e7927acec | ||
|
|
f807e44a39 | ||
|
|
d68abc746c | ||
|
|
8773d38ddd | ||
|
|
6fdbb2b659 | ||
|
|
bfe134ee54 | ||
|
|
f16f097b3e | ||
|
|
7284ef50eb | ||
|
|
e9248e284e | ||
|
|
aff6af1806 | ||
|
|
c0c35a0eba | ||
|
|
072ca459bd | ||
|
|
0865306064 | ||
|
|
ac14444d34 | ||
|
|
8a6d1ef373 | ||
|
|
dc694b0f34 | ||
|
|
76f8cc81da | ||
|
|
14f737bd60 | ||
|
|
5942ddf9f1 | ||
|
|
ab7b427241 | ||
|
|
15120270b4 | ||
|
|
cc0406653a | ||
|
|
9f34127565 | ||
|
|
71ecc96c70 | ||
|
|
2fa3873503 | ||
|
|
96b7bd3b2b | ||
|
|
e1ea17cabf | ||
|
|
3a162be265 | ||
|
|
502298aab9 | ||
|
|
edea488dbe | ||
|
|
2fabe2d198 | ||
|
|
f2c8156c00 | ||
|
|
942be364dc | ||
|
|
44e09e2220 | ||
|
|
0fcd20ec4a | ||
|
|
4c5707bba8 | ||
|
|
947f494e72 | ||
|
|
3f74a87b45 | ||
|
|
da0bdc5750 | ||
|
|
3ea59cd91b | ||
|
|
9b42dc7082 | ||
|
|
8b1c022244 | ||
|
|
e5cb8bb0bd | ||
|
|
d37343bb7d | ||
|
|
9c91f11cdc | ||
|
|
444fcf5ae5 | ||
|
|
31f8e0a47a | ||
|
|
1e0fcc877b | ||
|
|
1293bab868 | ||
|
|
9de92d18e1 | ||
|
|
5a877cbd62 | ||
|
|
99aa25bf83 | ||
|
|
5f66c15b91 | ||
|
|
af220c7f7b | ||
|
|
59e71a4cd9 | ||
|
|
6b1a4c4198 | ||
|
|
1072c5247c | ||
|
|
6508e920fe | ||
|
|
1154e0eeb3 | ||
|
|
2d96914bfa | ||
|
|
70494c3674 | ||
|
|
d68ad98176 | ||
|
|
eb70a6419c | ||
|
|
22aa759abc | ||
|
|
19b8fb6d8b | ||
|
|
d4bc835b1c | ||
|
|
b99d82cccc | ||
|
|
25d481d5d9 | ||
|
|
23871503a2 | ||
|
|
7c54fa70d7 | ||
|
|
2ffbbb0e71 | ||
|
|
7818f0c59b | ||
|
|
03e2adc332 | ||
|
|
e6ab4196de | ||
|
|
f2784d3ec2 | ||
|
|
ef3d508b31 | ||
|
|
9ffc0ec521 | ||
|
|
b598add64e | ||
|
|
47446515d1 | ||
|
|
e5de7fb8cf | ||
|
|
55e870f295 | ||
|
|
1485c83ab6 | ||
|
|
e8d1623e96 | ||
|
|
3bc6bf9e99 | ||
|
|
949d8bf49b | ||
|
|
c29e49da95 | ||
|
|
79c565911c | ||
|
|
bb9a0371c5 | ||
|
|
e945231ab3 | ||
|
|
c1298d162e | ||
|
|
f005edfcf0 | ||
|
|
59c68ec6cc | ||
|
|
a5077b0b1b | ||
|
|
a22c0499d5 | ||
|
|
c38608e3cf | ||
|
|
3b57194d47 | ||
|
|
21c901eab4 | ||
|
|
9ad8311dd6 | ||
|
|
df84028c90 | ||
|
|
7cb1e91ba1 | ||
|
|
974a7276c3 | ||
|
|
f0ca2bc11e | ||
|
|
cb43888496 | ||
|
|
34d5fb1aa0 | ||
|
|
72f0085ef7 | ||
|
|
f25f5abced | ||
|
|
94323f79e7 | ||
|
|
bdb1076100 | ||
|
|
8818e39c63 | ||
|
|
6a90035a4c | ||
|
|
e01b2ef25c | ||
|
|
91d91bc673 | ||
|
|
1c92ea58da | ||
|
|
bd6a38173e | ||
|
|
5d05a85411 | ||
|
|
1a5eafd2b1 | ||
|
|
1603b06431 | ||
|
|
2396af4589 | ||
|
|
262b8daec1 | ||
|
|
d33efe59fc | ||
|
|
5550565d6a | ||
|
|
68540cb479 | ||
|
|
4038fa6907 | ||
|
|
f96f997506 | ||
|
|
4e84d1a17c | ||
|
|
97cdb6a4a5 | ||
|
|
f5b3d70641 | ||
|
|
db66d3da9e | ||
|
|
c20e0667d4 | ||
|
|
4771b1c1b2 | ||
|
|
8e07a39ec0 | ||
|
|
9895b5bc1d | ||
|
|
e89a1bc0fb | ||
|
|
9b0654c7f2 | ||
|
|
56da824e98 | ||
|
|
f84b7904e6 | ||
|
|
9f523bb167 | ||
|
|
f38d5de946 | ||
|
|
de379b2e47 | ||
|
|
64e90f35c8 | ||
|
|
bbfe8c27c6 | ||
|
|
2f50074123 | ||
|
|
5cfaed7b26 | ||
|
|
0d19f645e8 | ||
|
|
45d4371328 | ||
|
|
889933cb41 | ||
|
|
958b294152 | ||
|
|
cf25097cd1 | ||
|
|
902e0dd5d6 | ||
|
|
d943551a7f | ||
|
|
46304b8a71 | ||
|
|
6e2fc186ca | ||
|
|
845689401d | ||
|
|
4fb9cc5e8d | ||
|
|
487581a01a | ||
|
|
2bc771d91e | ||
|
|
8bd7969328 | ||
|
|
5876ab487c | ||
|
|
9c14ca0f39 | ||
|
|
81ca352b2f | ||
|
|
0edfed5b95 | ||
|
|
0ff053415c | ||
|
|
a0ee607ae6 | ||
|
|
2723e2a7b8 | ||
|
|
6e105ce2c6 | ||
|
|
9fc7fceda4 | ||
|
|
2b72c0e328 | ||
|
|
536aa350f0 | ||
|
|
7c382c0e0c | ||
|
|
dda0d3259f | ||
|
|
a96718f7b3 | ||
|
|
7ca67fe57a | ||
|
|
8373024f9d | ||
|
|
b62ef0c40c | ||
|
|
376481eda5 | ||
|
|
800fa42982 | ||
|
|
4e728c3a02 | ||
|
|
663d254ced | ||
|
|
8753c232c7 | ||
|
|
221f3ef08c | ||
|
|
b59175a87c | ||
|
|
37c621dcdb | ||
|
|
eaf3228bb7 | ||
|
|
c8debbf470 | ||
|
|
8dcd8d17b5 | ||
|
|
cfe121c777 | ||
|
|
b4f83d8a4e | ||
|
|
9039d7e694 | ||
|
|
7a25717da6 | ||
|
|
7e1c444c02 | ||
|
|
dc3f7c9bda | ||
|
|
de754169fb | ||
|
|
2d3c3bbb0c | ||
|
|
15cefe4a43 | ||
|
|
95da301975 | ||
|
|
e03906b294 | ||
|
|
7921dd3f96 | ||
|
|
9f066f7a6b | ||
|
|
81d131e732 | ||
|
|
0552b56b71 | ||
|
|
3952ee402b | ||
|
|
0b3e27cb44 | ||
|
|
4fa4b3507e | ||
|
|
8c211364e2 | ||
|
|
2d9917d074 | ||
|
|
d514699ab7 | ||
|
|
dc176a83b3 | ||
|
|
69e3516a89 | ||
|
|
c8a0f9fa7a | ||
|
|
c2b9504b15 | ||
|
|
2693a3df2e | ||
|
|
8062466ab8 | ||
|
|
6cde1dd5ae | ||
|
|
b6c4a97675 | ||
|
|
a9444cef30 | ||
|
|
bf217a7093 | ||
|
|
b6b5355261 | ||
|
|
bc37084ec4 | ||
|
|
0a1a30f2af | ||
|
|
7e023a7944 | ||
|
|
91f68de8a7 | ||
|
|
994e2a6c57 | ||
|
|
04da2d845a | ||
|
|
d3b87bc3e8 | ||
|
|
554c81f251 | ||
|
|
6de3f9dd0b | ||
|
|
e9692d5b9c | ||
|
|
1a74990e9b | ||
|
|
3b9ac8699d | ||
|
|
ea6ae85f7a | ||
|
|
b02b9f026f | ||
|
|
c419e7b710 | ||
|
|
cd9132520d | ||
|
|
08d19df3f7 | ||
|
|
b34879b4f6 | ||
|
|
6b9c4af591 | ||
|
|
c00c207517 | ||
|
|
6f7fea3591 | ||
|
|
83eebfe153 | ||
|
|
fce3f86be7 | ||
|
|
2d42c59d70 | ||
|
|
f0933b9786 | ||
|
|
e4e687c2a4 | ||
|
|
44de353b8b | ||
|
|
aac4938598 | ||
|
|
d37b24cd0b | ||
|
|
c9a36fe4b2 | ||
|
|
f01a21ce43 | ||
|
|
cc72699b8a | ||
|
|
04de0049fe | ||
|
|
330554edb0 | ||
|
|
a06a3fa5d6 | ||
|
|
e8d6d62fba |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -128,3 +128,4 @@ output/*
|
||||
._*
|
||||
|
||||
_start
|
||||
_temp_*/**/*
|
||||
|
||||
1
.idea/jsLibraryMappings.xml
generated
1
.idea/jsLibraryMappings.xml
generated
@@ -2,5 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="file://$PROJECT_DIR$" libraries="{Sonarr node_modules}" />
|
||||
<includedPredefinedLibrary name="ECMAScript 6" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,6 +1,6 @@
|
||||
# How to Contribute #
|
||||
|
||||
We're always looking for people to help make Sonarr even better, there are a number of ways to contribute. To get started, <a href="http://www.clahub.com/agreements/NzbDrone/NzbDrone">sign the Contributor License Agreement</a>.
|
||||
We're always looking for people to help make Sonarr even better, there are a number of ways to contribute.
|
||||
|
||||
## Documentation ##
|
||||
Setup guides, FAQ, the more information we have on the wiki the better.
|
||||
@@ -16,9 +16,11 @@ Setup guides, FAQ, the more information we have on the wiki the better.
|
||||
### Getting started ###
|
||||
|
||||
1. Fork Sonarr
|
||||
2. Clone (develop branch)
|
||||
2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)*
|
||||
3. Run `npm install`
|
||||
4. Run `gulp watch` - Used to compile the UI components and copy them (leave this window open)
|
||||
4. Run `npm start` - Used to compile the UI components and copy them.
|
||||
Leave this window open.
|
||||
If you have gulp globally installed you can use `gulp watch` instead
|
||||
5. Compile in Visual Studio
|
||||
|
||||
### Contributing Code ###
|
||||
|
||||
264
build.ps1
264
build.ps1
@@ -1,263 +1 @@
|
||||
$msBuild = 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe'
|
||||
$outputFolder = '.\_output'
|
||||
$outputFolderMono = '.\_output_mono'
|
||||
$outputFolderOsx = '.\_output_osx'
|
||||
$outputFolderOsxApp = '.\_output_osx_app'
|
||||
$testPackageFolder = '.\_tests\'
|
||||
$testSearchPattern = '*.Test\bin\x86\Release'
|
||||
$sourceFolder = '.\src'
|
||||
$updateFolder = $outputFolder + '\NzbDrone.Update'
|
||||
$updateFolderMono = $outputFolderMono + '\NzbDrone.Update'
|
||||
|
||||
Function Build()
|
||||
{
|
||||
Write-Host "##teamcity[progressStart 'Build']"
|
||||
|
||||
$clean = $msbuild + " src\nzbdrone.sln /t:Clean /m"
|
||||
$build = $msbuild + " src\nzbdrone.sln /p:Configuration=Release /p:Platform=x86 /t:Build /m"
|
||||
|
||||
if(Test-Path $outputFolder)
|
||||
{
|
||||
Remove-Item -Recurse -Force $outputFolder -ErrorAction Continue
|
||||
}
|
||||
|
||||
Invoke-Expression $clean
|
||||
CheckExitCode
|
||||
|
||||
Invoke-Expression $build
|
||||
CheckExitCode
|
||||
|
||||
CleanFolder $outputFolder
|
||||
|
||||
AddJsonNet
|
||||
|
||||
Write-Host "Removing Mono.Posix.dll"
|
||||
Remove-Item "$outputFolder\Mono.Posix.dll"
|
||||
|
||||
Write-Host "##teamcity[progressFinish 'Build']"
|
||||
}
|
||||
|
||||
Function CleanFolder($path, $keepConfigFiles)
|
||||
{
|
||||
Write-Host Removing XMLDoc files
|
||||
get-childitem $path -File -Filter *.xml -Recurse | foreach ($_) {
|
||||
|
||||
$filename = $_.FullName
|
||||
$exeFilename = $filename -replace "xml", "exe"
|
||||
$dllFilename = $filename -replace "xml", "dll"
|
||||
|
||||
if (Test-Path $exeFilename) {
|
||||
remove-item $_.fullname
|
||||
}
|
||||
|
||||
if (Test-Path $dllFilename) {
|
||||
remove-item $_.fullname
|
||||
}
|
||||
}
|
||||
|
||||
get-childitem $path -File -Filter *.transform -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
if($keepConfigFiles -ne $true)
|
||||
{
|
||||
get-childitem $path -File -Filter *.dll.config -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
}
|
||||
|
||||
Write-Host Removing FluentValidation.Resources files
|
||||
get-childitem $path -File -Filter FluentValidation.resources.dll -recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
get-childitem $path -File -Filter app.config -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
Write-Host Removing .less files
|
||||
get-childitem $path -File -Filter *.less -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
Write-Host Removing vshost files
|
||||
get-childitem $path -File -Filter *.vshost.exe -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
if(Test-Path $$path\NuGet)
|
||||
{
|
||||
Write-Host Removing NuGet
|
||||
Remove-Item -Recurse -Force "$path\NuGet"
|
||||
}
|
||||
|
||||
Write-Host Removing Empty folders
|
||||
while (Get-ChildItem $path -recurse | where {!@(Get-ChildItem -force $_.fullname)} | Test-Path)
|
||||
{
|
||||
Get-ChildItem $path -Directory -recurse | where {!@(Get-ChildItem -force $_.fullname)} | Remove-Item
|
||||
}
|
||||
}
|
||||
|
||||
Function PackageMono()
|
||||
{
|
||||
Write-Host "##teamcity[progressStart 'Creating Mono Package']"
|
||||
|
||||
if(Test-Path $outputFolderMono)
|
||||
{
|
||||
Remove-Item -Recurse -Force $outputFolderMono -ErrorAction Continue
|
||||
}
|
||||
|
||||
Copy-Item $outputFolder $outputFolderMono -recurse
|
||||
|
||||
Write-Host Creating MDBs
|
||||
get-childitem $outputFolderMono -File -Include @("*.exe", "*.dll") -Exclude @("MediaInfo.dll", "sqlite3.dll") -Recurse | foreach ($_) {
|
||||
Write-Host "Creating .mdb for $_"
|
||||
& "tools\pdb2mdb\pdb2mdb.exe" $_.fullname
|
||||
}
|
||||
|
||||
Write-Host Removing PDBs
|
||||
get-childitem $outputFolderMono -File -Filter *.pdb -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
Write-Host Removing Service helpers
|
||||
get-childitem $outputFolderMono -File -Filter ServiceUninstall.* -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
get-childitem $outputFolderMono -File -Filter ServiceInstall.* -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
Write-Host Removing native windows binaries Sqlite, MediaInfo
|
||||
get-childitem $outputFolderMono -File -Filter sqlite3.* -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
get-childitem $outputFolderMono -File -Filter MediaInfo.* -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
Write-Host "Adding NzbDrone.Core.dll.config (for dllmap)"
|
||||
Copy-Item "$sourceFolder\NzbDrone.Core\NzbDrone.Core.dll.config" $outputFolderMono
|
||||
|
||||
Write-Host "Adding CurlSharp.dll.config (for dllmap)"
|
||||
Copy-Item "$sourceFolder\NzbDrone.Common\CurlSharp.dll.config" $outputFolderMono
|
||||
|
||||
Write-Host Renaming NzbDrone.Console.exe to NzbDrone.exe
|
||||
Get-ChildItem $outputFolderMono -File -Filter "NzbDrone.exe*" -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
Write-Host Removing NzbDrone.Windows
|
||||
get-childitem $outputFolderMono -File -Filter NzbDrone.Windows.* -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
Get-ChildItem $outputFolderMono -File -Filter "NzbDrone.Console.exe*" -Recurse | foreach ($_) {
|
||||
$newName = $_.fullname -Replace ".Console",""
|
||||
|
||||
Rename-Item $_.fullname $newName
|
||||
}
|
||||
|
||||
Write-Host Adding NzbDrone.Mono to UpdatePackage
|
||||
Copy-Item $outputFolderMono\* $updateFolderMono -Filter NzbDrone.Mono.*
|
||||
|
||||
Write-Host "##teamcity[progressFinish 'Creating Mono Package']"
|
||||
}
|
||||
|
||||
Function PackageOsx()
|
||||
{
|
||||
Write-Host "##teamcity[progressStart 'Creating OS X Package']"
|
||||
|
||||
if(Test-Path $outputFolderOsx)
|
||||
{
|
||||
Remove-Item -Recurse -Force $outputFolderOsx -ErrorAction Continue
|
||||
}
|
||||
|
||||
Copy-Item $outputFolderMono $outputFolderOsx -recurse
|
||||
|
||||
Write-Host "Adding sqlite dylibs"
|
||||
Copy-Item "$sourceFolder\Libraries\sqlite\*.dylib" "$outputFolderOsx"
|
||||
|
||||
Write-Host "Adding MediaInfo dylib"
|
||||
Copy-Item "$sourceFolder\Libraries\MediaInfo\*.dylib" "$outputFolderOsx"
|
||||
|
||||
Write-Host "Adding Startup script"
|
||||
Copy-Item .\osx\Sonarr "$outputFolderOsx"
|
||||
|
||||
Write-Host "##teamcity[progressFinish 'Creating OS X Package']"
|
||||
}
|
||||
|
||||
|
||||
Function PackageOsxApp()
|
||||
{
|
||||
Write-Host "##teamcity[progressStart 'Creating OS X App Package']"
|
||||
|
||||
if(Test-Path $outputFolderOsxApp)
|
||||
{
|
||||
Remove-Item -Recurse -Force $outputFolderOsxApp -ErrorAction Continue
|
||||
}
|
||||
|
||||
Copy-Item .\osx\Sonarr.app $outputFolderOsxApp\Sonarr.app -recurse
|
||||
Copy-Item $outputFolderOsx $outputFolderOsxApp\Sonarr.app\Contents\MacOS -recurse
|
||||
|
||||
Write-Host "##teamcity[progressFinish 'Creating OS X App Package']"
|
||||
}
|
||||
|
||||
Function AddJsonNet()
|
||||
{
|
||||
get-childitem $outputFolder -File -Filter Newtonsoft.Json.* -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
Copy-Item .\src\packages\Newtonsoft.Json.*.*\lib\net35\*.dll -Destination $outputFolder
|
||||
Copy-Item .\src\packages\Newtonsoft.Json.*.*\lib\net35\*.dll -Destination $outputFolder\NzbDrone.Update
|
||||
}
|
||||
|
||||
Function PackageTests()
|
||||
{
|
||||
|
||||
Write-Host Packaging Tests
|
||||
Write-Host "##teamcity[progressStart 'Creating Test Package']"
|
||||
|
||||
if(Test-Path $testPackageFolder)
|
||||
{
|
||||
Remove-Item -Recurse -Force $testPackageFolder -ErrorAction Continue
|
||||
}
|
||||
|
||||
Get-ChildItem -Recurse -Directory | Where-Object {$_.FullName -like $testSearchPattern} | foreach($_){
|
||||
Copy-Item -Recurse ($_.FullName + "\*") $testPackageFolder -ErrorAction Ignore
|
||||
}
|
||||
|
||||
.\src\.nuget\NuGet.exe install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder
|
||||
|
||||
Copy-Item $outputFolder\*.dll -Destination $testPackageFolder -Force
|
||||
Copy-Item $outputFolder\*.pdb -Destination $testPackageFolder -Force
|
||||
Copy-Item .\*.sh -Destination $testPackageFolder -Force
|
||||
|
||||
Write-Host Creating MDBs for tests
|
||||
get-childitem $testPackageFolder -File -Include @("*.exe", "*.dll") -Exclude @("MediaInfo.dll", "sqlite3.dll") -Recurse | foreach ($_) {
|
||||
Write-Host "Creating .mdb for $_"
|
||||
& "tools\pdb2mdb\pdb2mdb.exe" $_.fullname
|
||||
}
|
||||
|
||||
get-childitem $testPackageFolder -File -Filter *log.config | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
CleanFolder $testPackageFolder $true
|
||||
|
||||
Write-Host "Adding NzbDrone.Core.dll.config (for dllmap)"
|
||||
Copy-Item "$sourceFolder\NzbDrone.Core\NzbDrone.Core.dll.config" -Destination $testPackageFolder -Force
|
||||
|
||||
Write-Host "Copying CurlSharp libraries"
|
||||
Copy-Item $sourceFolder\ExternalModules\CurlSharp\libs\i386\* $testPackageFolder
|
||||
|
||||
Write-Host "##teamcity[progressFinish 'Creating Test Package']"
|
||||
}
|
||||
|
||||
Function RunGulp()
|
||||
{
|
||||
Write-Host "##teamcity[progressStart 'Running Gulp']"
|
||||
Invoke-Expression 'npm install'
|
||||
CheckExitCode
|
||||
|
||||
Invoke-Expression 'gulp build' -ErrorAction Continue -Verbose
|
||||
CheckExitCode
|
||||
|
||||
Write-Host "##teamcity[progressFinish 'Running Gulp']"
|
||||
}
|
||||
|
||||
Function CheckExitCode()
|
||||
{
|
||||
if ($lastexitcode -ne 0)
|
||||
{
|
||||
Write-Host $errorMessage
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
Function CleanupWindowsPackage()
|
||||
{
|
||||
Write-Host Removing NzbDrone.Mono
|
||||
get-childitem $outputFolder -File -Filter NzbDrone.Mono.* -Recurse | foreach ($_) {remove-item $_.fullname}
|
||||
|
||||
Write-Host Adding NzbDrone.Windows to UpdatePackage
|
||||
Copy-Item $outputFolder\* $updateFolder -Filter NzbDrone.Windows.*
|
||||
}
|
||||
|
||||
Build
|
||||
RunGulp
|
||||
PackageMono
|
||||
PackageOsx
|
||||
PackageOsxApp
|
||||
PackageTests
|
||||
CleanupWindowsPackage
|
||||
Write-Warning "DEPRECATED -- Please use build.sh instead."
|
||||
65
build.sh
65
build.sh
@@ -1,15 +1,17 @@
|
||||
#! /bin/bash
|
||||
msBuild='/c/Windows/Microsoft.NET/Framework64/v4.0.30319/'
|
||||
msBuild='/c/Program Files (x86)/MSBuild/14.0/Bin'
|
||||
outputFolder='./_output'
|
||||
outputFolderMono='./_output_mono'
|
||||
outputFolderOsx='./_output_osx'
|
||||
outputFolderOsxApp='./_output_osx_app'
|
||||
testPackageFolder='./_tests/'
|
||||
testSearchPattern='*.Test/bin/x86/Release/*'
|
||||
testSearchPattern='*.Test/bin/x86/Release'
|
||||
sourceFolder='./src'
|
||||
slnFile=$sourceFolder/NzbDrone.sln
|
||||
updateFolder=$outputFolder/NzbDrone.Update
|
||||
updateFolderMono=$outputFolderMono/NzbDrone.Update
|
||||
|
||||
nuget='tools/nuget/nuget.exe';
|
||||
CheckExitCode()
|
||||
{
|
||||
"$@"
|
||||
@@ -26,14 +28,6 @@ CleanFolder()
|
||||
local path=$1
|
||||
local keepConfigFiles=$2
|
||||
|
||||
echo "Removing XMLDoc files"
|
||||
local xmlfiles=( $(find $path -name "*.xml") )
|
||||
for filename in "${xmlfiles[@]}"
|
||||
do
|
||||
if [ -e ${filename%.xml}.dll ] || [ -e ${filename%.xml}.exe ] ; then
|
||||
rm $filename
|
||||
fi
|
||||
done
|
||||
|
||||
find $path -name "*.transform" -exec rm "{}" \;
|
||||
|
||||
@@ -51,10 +45,8 @@ CleanFolder()
|
||||
echo "Removing vshost files"
|
||||
find $path -name "*.vshost.exe" -exec rm "{}" \;
|
||||
|
||||
if [ -d $path/NuGet ] ; then
|
||||
echo "Removing NuGet"
|
||||
rm -rf $path/NuGet
|
||||
fi
|
||||
echo "Removing dylib files"
|
||||
find $path -name "*.dylib" -exec rm "{}" \;
|
||||
|
||||
echo "Removing Empty folders"
|
||||
find $path -depth -empty -type d -exec rm -r "{}" \;
|
||||
@@ -72,15 +64,17 @@ AddJsonNet()
|
||||
BuildWithMSBuild()
|
||||
{
|
||||
export PATH=$msBuild:$PATH
|
||||
CheckExitCode MSBuild.exe $sourceFolder/NzbDrone.sln //t:Clean //m
|
||||
CheckExitCode MSBuild.exe $sourceFolder/NzbDrone.sln //p:Configuration=Release //p:Platform=x86 //t:Build //m
|
||||
CheckExitCode MSBuild.exe $slnFile //t:Clean //m
|
||||
$nuget restore $slnFile
|
||||
CheckExitCode MSBuild.exe $slnFile //p:Configuration=Release //p:Platform=x86 //t:Build //m //p:AllowedReferenceRelatedFileExtensions=.pdb
|
||||
}
|
||||
|
||||
BuildWithXbuild()
|
||||
{
|
||||
export MONO_IOMAP=case
|
||||
CheckExitCode xbuild /t:Clean $sourceFolder/NzbDrone.sln
|
||||
CheckExitCode xbuild /p:Configuration=Release /p:Platform=x86 /t:Build $sourceFolder/NzbDrone.sln
|
||||
CheckExitCode xbuild /t:Clean $slnFile
|
||||
mono $nuget restore $slnFile
|
||||
CheckExitCode xbuild /p:Configuration=Release /p:Platform=x86 /t:Build /p:AllowedReferenceRelatedFileExtensions=.pdb $slnFile
|
||||
}
|
||||
|
||||
Build()
|
||||
@@ -94,9 +88,9 @@ Build()
|
||||
else
|
||||
BuildWithXbuild
|
||||
fi
|
||||
|
||||
|
||||
CleanFolder $outputFolder false
|
||||
|
||||
|
||||
AddJsonNet
|
||||
|
||||
echo "Removing Mono.Posix.dll"
|
||||
@@ -107,19 +101,29 @@ Build()
|
||||
|
||||
RunGulp()
|
||||
{
|
||||
echo "##teamcity[progressStart 'Running Gulp']"
|
||||
echo "##teamcity[progressStart 'npm install']"
|
||||
npm-cache install npm || CheckExitCode npm install
|
||||
echo "##teamcity[progressFinish 'npm install']"
|
||||
|
||||
CheckExitCode npm install
|
||||
CheckExitCode gulp build
|
||||
|
||||
echo "##teamcity[progressFinish 'Running Gulp']"
|
||||
echo "##teamcity[progressStart 'Running gulp']"
|
||||
CheckExitCode npm run build
|
||||
echo "##teamcity[progressFinish 'Running gulp']"
|
||||
}
|
||||
|
||||
CreateMdbs()
|
||||
{
|
||||
local path=$1
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
find $path \( -name "*.exe" -o -name "*.dll" \) -not -name "MediaInfo.dll" -not -name "sqlite3.dll" -exec tools/pdb2mdb/pdb2mdb.exe "{}" \;
|
||||
local pdbFiles=( $(find $path -name "*.pdb") )
|
||||
for filename in "${pdbFiles[@]}"
|
||||
do
|
||||
if [ -e ${filename%.pdb}.dll ] ; then
|
||||
tools/pdb2mdb/pdb2mdb.exe ${filename%.pdb}.dll
|
||||
fi
|
||||
if [ -e ${filename%.pdb}.exe ] ; then
|
||||
tools/pdb2mdb/pdb2mdb.exe ${filename%.pdb}.exe
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -160,6 +164,8 @@ PackageMono()
|
||||
|
||||
echo "Adding NzbDrone.Mono to UpdatePackage"
|
||||
cp $outputFolderMono/NzbDrone.Mono.* $updateFolderMono
|
||||
|
||||
echo "##teamcity[progressFinish 'Creating Mono Package']"
|
||||
}
|
||||
|
||||
PackageOsx()
|
||||
@@ -199,13 +205,12 @@ PackageTests()
|
||||
rm -rf $testPackageFolder
|
||||
mkdir $testPackageFolder
|
||||
|
||||
find . -maxdepth 6 -path $testSearchPattern -exec cp -r "{}" $testPackageFolder \;
|
||||
find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \;
|
||||
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
$sourceFolder/.nuget/NuGet.exe install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder
|
||||
cp $outputFolder/*.pdb $testPackageFolder
|
||||
$nuget install NUnit.ConsoleRunner -Version 3.2.0 -Output $testPackageFolder
|
||||
else
|
||||
mono $sourceFolder/.nuget/NuGet.exe install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder
|
||||
mono $nuget install NUnit.ConsoleRunner -Version 3.2.0 -Output $testPackageFolder
|
||||
fi
|
||||
|
||||
cp $outputFolder/*.dll $testPackageFolder
|
||||
|
||||
2
debian/copyright
vendored
2
debian/copyright
vendored
@@ -3,7 +3,7 @@ Upstream-Name: nzbdrone
|
||||
Source: https://github.com/Sonarr/Sonarr
|
||||
|
||||
Files: *
|
||||
Copyright: 2010-2014 Sonarr <hello@sonarr.tv>
|
||||
Copyright: 2010-2016 Sonarr <hello@sonarr.tv>
|
||||
|
||||
License: GPL-3.0+
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ var autoprefixer = require('autoprefixer-core');
|
||||
var livereload = require('gulp-livereload');
|
||||
|
||||
var print = require('gulp-print');
|
||||
var phantom = require('./phantom');
|
||||
var paths = require('./paths');
|
||||
var errorHandler = require('./errorHandler');
|
||||
|
||||
@@ -29,14 +28,6 @@ gulp.task('less', function() {
|
||||
paths.src.root + 'System/Info/info.less'
|
||||
];
|
||||
|
||||
if (phantom) {
|
||||
src = [
|
||||
paths.src.content + 'Bootstrap/bootstrap.less',
|
||||
paths.src.content + 'Vendor/vendor.less',
|
||||
paths.src.content + 'sonarr.less'
|
||||
];
|
||||
}
|
||||
|
||||
return gulp.src(src)
|
||||
.pipe(print())
|
||||
.pipe(sourcemaps.init())
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
var phantom = require('./phantom');
|
||||
|
||||
var paths = {
|
||||
src : {
|
||||
root : './src/UI/',
|
||||
@@ -20,26 +18,4 @@ var paths = {
|
||||
}
|
||||
};
|
||||
|
||||
if (phantom) {
|
||||
paths = {
|
||||
src : {
|
||||
root : './UI.Phantom/',
|
||||
templates : './UI.Phantom/**/*.hbs',
|
||||
html : './UI.Phantom/*.html',
|
||||
partials : './UI.Phantom/**/*Partial.hbs',
|
||||
scripts : './UI.Phantom/**/*.js',
|
||||
less : ['./UI.Phantom/**/*.less'],
|
||||
content : './UI.Phantom/Content/',
|
||||
images : './UI.Phantom/Content/Images/**/*',
|
||||
exclude : {
|
||||
libs : '!./UI.Phantom/JsLibraries/**'
|
||||
}
|
||||
},
|
||||
dest : {
|
||||
root : './_output/UI.Phantom/',
|
||||
content : './_output/UI.Phantom/Content/'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = paths;
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// Switch to phantom.
|
||||
// Example:
|
||||
// gulp --phantom
|
||||
|
||||
var phantom = false;
|
||||
process.argv.forEach(function(val, index, array) {
|
||||
if (val === '--phantom') {
|
||||
phantom = true;
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Phantom:', phantom);
|
||||
|
||||
module.exports = phantom;
|
||||
@@ -1,8 +0,0 @@
|
||||
EXCLUDE="-exclude:Windows -include:IntegrationTest"
|
||||
TESTDIR="."
|
||||
NUNIT="$TESTDIR/NUnit.Runners.2.6.1/tools/nunit-console-x86.exe"
|
||||
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Api.Result.xml $TESTDIR/NzbDrone.Api.Test.dll
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Core.Result.xml $TESTDIR/NzbDrone.Core.Test.dll
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Integration.Result.xml $TESTDIR/NzbDrone.Integration.Test.dll
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Common.Result.xml $TESTDIR/NzbDrone.Common.Test.dll
|
||||
29
osx/Sonarr
29
osx/Sonarr
@@ -1,17 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
|
||||
#get the bundle's MacOS directory full path
|
||||
DIR=$(cd "$(dirname "$0")"; pwd)
|
||||
|
||||
|
||||
#change these values to match your app
|
||||
EXE_PATH="$DIR/NzbDrone.exe"
|
||||
APPNAME="Sonarr"
|
||||
|
||||
#set up environment
|
||||
MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DIR:$MONO_FRAMEWORK_PATH/lib:/lib:/usr/lib"
|
||||
export PATH="$MONO_FRAMEWORK_PATH/bin:$PATH"
|
||||
|
||||
if [[ -x '/opt/local/bin/mono' ]]; then
|
||||
export PATH="/opt/local/bin:$PATH"
|
||||
fi
|
||||
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DIR"
|
||||
|
||||
if [ -e /Library/Frameworks/Mono.framework ]; then
|
||||
MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
|
||||
export PATH="$MONO_FRAMEWORK_PATH/bin:$PATH"
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DYLD_FALLBACK_LIBRARY_PATH:$MONO_FRAMEWORK_PATH/lib"
|
||||
fi
|
||||
|
||||
if [[ -f '/opt/local/lib/libsqlite3.0.dylib' ]]; then
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="/opt/local/lib:$DYLD_FALLBACK_LIBRARY_PATH"
|
||||
fi
|
||||
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DYLD_FALLBACK_LIBRARY_PATH:$HOME/lib:/usr/local/lib:/lib:/usr/lib"
|
||||
|
||||
#mono version check
|
||||
REQUIRED_MAJOR=3
|
||||
REQUIRED_MINOR=10
|
||||
@@ -21,6 +35,9 @@ VERSION_MSG="$APPNAME requires Mono Runtime Environment(MRE) $REQUIRED_MAJOR.$RE
|
||||
DOWNLOAD_URL="http://www.mono-project.com/download/#download-mac"
|
||||
|
||||
MONO_VERSION="$(mono --version | grep 'Mono JIT compiler version ' | cut -f5 -d\ )"
|
||||
# if [[ -o DEBUG ]]; then osascript -e "display dialog \"MONO_VERSION: $MONO_VERSION\""; fi
|
||||
|
||||
|
||||
MONO_VERSION_MAJOR="$(echo $MONO_VERSION | cut -f1 -d.)"
|
||||
MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
|
||||
if [ -z "$MONO_VERSION" ] \
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
"description": "Sonarr",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"preinstall": ""
|
||||
"build": "gulp build",
|
||||
"start": "gulp watch"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Sonarr/Sonarr.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "BSD",
|
||||
"license": "GPL-3.0",
|
||||
"gitHead": "9ff7aa1bf7fe38c4c5bdb92f56c8ad556916ed67",
|
||||
"readmeFilename": "readme.md",
|
||||
"dependencies": {
|
||||
|
||||
@@ -21,10 +21,9 @@ Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS fee
|
||||
## Configuring Development Environment: ##
|
||||
|
||||
### Requirements ###
|
||||
- Visual Studio 2013 [Free Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx)
|
||||
- Visual Studio 2015 [Free Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx)
|
||||
- [Git](http://git-scm.com/downloads)
|
||||
- [NodeJS](http://nodejs.org/download/)
|
||||
- [Gulp](http://gulpjs.com)
|
||||
|
||||
### Setup ###
|
||||
|
||||
@@ -32,8 +31,7 @@ Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS fee
|
||||
- Clone the repository into your development machine. [*info*](https://help.github.com/articles/working-with-repositories)
|
||||
- Grab the submodules `git submodule init && git submodule update`
|
||||
- install the required Node Packages `npm install`
|
||||
- install gulp `npm install gulp -g`
|
||||
- start gulp to monitor your dev environment for any changes that need post processing using `gulp watch` command.
|
||||
- start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
|
||||
|
||||
*Please note gulp must be running at all times while you are working with Sonarr client source files.*
|
||||
|
||||
@@ -45,7 +43,7 @@ Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS fee
|
||||
|
||||
### License ###
|
||||
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||
Copyright 2010-2015
|
||||
Copyright 2010-2016
|
||||
|
||||
|
||||
### Sponsors ###
|
||||
|
||||
17
rename.ps1
17
rename.ps1
@@ -1,17 +0,0 @@
|
||||
Param(
|
||||
[Parameter(Mandatory=$true, Position=0, HelpMessage="A branch name is #requires required")]
|
||||
[string]$branch,
|
||||
[Parameter(Mandatory=$true, Position=1, HelpMessage="A version is required")]
|
||||
[string]$version
|
||||
)
|
||||
|
||||
if ($branch -eq "<default>")
|
||||
{
|
||||
$branch = "teamcity";
|
||||
}
|
||||
|
||||
Write-Host $branch;
|
||||
Write-Host $version;
|
||||
Write-Host "NzbDrone.$branch.$version.zip";
|
||||
|
||||
Rename-Item "nzbdrone.zip" "NzbDrone.$branch.$version.zip"
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<solution>
|
||||
<add key="disableSourceControlIntegration" value="true" />
|
||||
</solution>
|
||||
</configuration>
|
||||
Binary file not shown.
@@ -1,136 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
|
||||
|
||||
<!-- Enable the restore command to run before builds -->
|
||||
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
|
||||
|
||||
<!-- Property that enables building a package from a project -->
|
||||
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
|
||||
|
||||
<!-- Determines if package restore consent is required to restore packages -->
|
||||
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
|
||||
|
||||
<!-- Download NuGet.exe if it does not already exist -->
|
||||
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(PackageSources)' == '' ">
|
||||
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
|
||||
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
|
||||
<!--
|
||||
<PackageSource Include="https://www.nuget.org/api/v2/" />
|
||||
<PackageSource Include="https://my-nuget-source/nuget/" />
|
||||
-->
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
|
||||
<!-- Windows specific commands -->
|
||||
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
|
||||
<PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
|
||||
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
|
||||
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
|
||||
<PackagesConfig>packages.config</PackagesConfig>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- NuGet command -->
|
||||
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
|
||||
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
|
||||
|
||||
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
|
||||
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(NuGetExePath)</NuGetCommand>
|
||||
|
||||
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
|
||||
|
||||
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
|
||||
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
|
||||
|
||||
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
|
||||
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
|
||||
|
||||
<!-- Commands -->
|
||||
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
|
||||
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
|
||||
|
||||
<!-- We need to ensure packages are restored prior to assembly resolve -->
|
||||
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
|
||||
RestorePackages;
|
||||
$(BuildDependsOn);
|
||||
</BuildDependsOn>
|
||||
|
||||
<!-- Make the build depend on restore packages -->
|
||||
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
|
||||
$(BuildDependsOn);
|
||||
BuildPackage;
|
||||
</BuildDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="CheckPrerequisites">
|
||||
<!-- Raise an error if we're unable to locate nuget.exe -->
|
||||
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
|
||||
<!--
|
||||
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
|
||||
This effectively acts as a lock that makes sure that the download operation will only happen once and all
|
||||
parallel builds will have to wait for it to complete.
|
||||
-->
|
||||
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="_DownloadNuGet">
|
||||
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
|
||||
<Exec Command="$(RestoreCommand)"
|
||||
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
|
||||
|
||||
<Exec Command="$(RestoreCommand)"
|
||||
LogStandardErrorAsError="true"
|
||||
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
|
||||
<Exec Command="$(BuildCommand)"
|
||||
Condition=" '$(OS)' != 'Windows_NT' " />
|
||||
|
||||
<Exec Command="$(BuildCommand)"
|
||||
LogStandardErrorAsError="true"
|
||||
Condition=" '$(OS)' == 'Windows_NT' " />
|
||||
</Target>
|
||||
|
||||
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
||||
<ParameterGroup>
|
||||
<OutputFilename ParameterType="System.String" Required="true" />
|
||||
</ParameterGroup>
|
||||
<Task>
|
||||
<Reference Include="System.Core" />
|
||||
<Using Namespace="System" />
|
||||
<Using Namespace="System.IO" />
|
||||
<Using Namespace="System.Net" />
|
||||
<Using Namespace="Microsoft.Build.Framework" />
|
||||
<Using Namespace="Microsoft.Build.Utilities" />
|
||||
<Code Type="Fragment" Language="cs">
|
||||
<![CDATA[
|
||||
try {
|
||||
OutputFilename = Path.GetFullPath(OutputFilename);
|
||||
|
||||
Log.LogMessage("Downloading latest version of NuGet.exe...");
|
||||
WebClient webClient = new WebClient();
|
||||
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Log.LogErrorFromException(ex);
|
||||
return false;
|
||||
}
|
||||
]]>
|
||||
</Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
</Project>
|
||||
Binary file not shown.
@@ -1,9 +0,0 @@
|
||||
<?xml version ="1.0"?>
|
||||
<!-- This allows mt.exe to run on machines with the CLR v4 installed but not 1.1 or 2.0 -->
|
||||
<configuration>
|
||||
<startup useLegacyV2RuntimeActivationPolicy="true">
|
||||
<supportedRuntime version="v4.0"/>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
<supportedRuntime version="v1.1.4322"/>
|
||||
</startup>
|
||||
</configuration>
|
||||
@@ -296,7 +296,7 @@ namespace LogentriesCore
|
||||
WriteDebugMessages("HostName parameter is not defined - trying to get it from System.Environment.MachineName");
|
||||
m_HostName = "HostName=" + System.Environment.MachineName + " ";
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// Cannot get host name automatically, so assume that HostName is not used
|
||||
// and log message is sent without it.
|
||||
|
||||
@@ -73,13 +73,6 @@
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
@@ -51,8 +51,9 @@
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="NLog">
|
||||
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.3.4\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -79,19 +80,13 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="fastJSON\license.txt" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NLog" version="2.1.0" targetFramework="net40" />
|
||||
</packages>
|
||||
<package id="NLog" version="4.3.4" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -144,7 +144,6 @@
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
@@ -276,10 +276,9 @@
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
|
||||
@@ -102,7 +102,6 @@
|
||||
</ItemGroup>
|
||||
<Import Project="..\Common\Microsoft.AspNet.SignalR.targets" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
@@ -110,4 +109,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -774,7 +774,8 @@ namespace MonoTorrent
|
||||
break;
|
||||
|
||||
case ("nodes"):
|
||||
this.nodes = (BEncodedList)keypair.Value;
|
||||
if (keypair.Value.ToString().Length != 0)
|
||||
this.nodes = (BEncodedList)keypair.Value;
|
||||
break;
|
||||
|
||||
case ("comment.utf-8"):
|
||||
|
||||
@@ -38,17 +38,17 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="FluentAssertions, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.dll</HintPath>
|
||||
<Reference Include="FluentAssertions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FluentAssertions.Core, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
<Reference Include="FluentAssertions.Core, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
||||
<Reference Include="nunit.framework, Version=3.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.2.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -104,7 +104,6 @@
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FluentAssertions" version="3.4.0" targetFramework="net40" />
|
||||
<package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
|
||||
<package id="Moq" version="4.0.10827" />
|
||||
<package id="NBuilder" version="3.0.1.1" targetFramework="net40" />
|
||||
<package id="NUnit" version="2.6.3" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.2.0" targetFramework="net40" />
|
||||
<package id="ValueInjecter" version="2.3.3" targetFramework="net40" />
|
||||
</packages>
|
||||
</packages>
|
||||
@@ -3,6 +3,7 @@ using Nancy;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.ModelBinding;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
@@ -23,6 +24,11 @@ namespace NzbDrone.Api.Authentication
|
||||
|
||||
private Response Login(LoginResource resource)
|
||||
{
|
||||
Ensure.That(resource.Username, () => resource.Username).IsNotNullOrWhiteSpace();
|
||||
|
||||
// TODO: A null or empty password should not be allowed, uncomment in v3
|
||||
//Ensure.That(resource.Password, () => resource.Password).IsNotNullOrWhiteSpace();
|
||||
|
||||
var user = _userService.FindUser(resource.Username, resource.Password);
|
||||
|
||||
if (user == null)
|
||||
|
||||
@@ -22,15 +22,32 @@ namespace NzbDrone.Api.Calendar
|
||||
|
||||
private Response GetCalendarFeed()
|
||||
{
|
||||
var start = DateTime.Today.AddDays(-7);
|
||||
var end = DateTime.Today.AddDays(28);
|
||||
var pastDays = 7;
|
||||
var futureDays = 28;
|
||||
var start = DateTime.Today.AddDays(-pastDays);
|
||||
var end = DateTime.Today.AddDays(futureDays);
|
||||
|
||||
// TODO: Remove start/end parameters in v3, they don't work well for iCal
|
||||
var queryStart = Request.Query.Start;
|
||||
var queryEnd = Request.Query.End;
|
||||
var queryPastDays = Request.Query.PastDays;
|
||||
var queryFutureDays = Request.Query.FutureDays;
|
||||
|
||||
if (queryStart.HasValue) start = DateTime.Parse(queryStart.Value);
|
||||
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
|
||||
|
||||
if (queryPastDays.HasValue)
|
||||
{
|
||||
pastDays = int.Parse(queryPastDays.Value);
|
||||
start = DateTime.Today.AddDays(-pastDays);
|
||||
}
|
||||
|
||||
if (queryFutureDays.HasValue)
|
||||
{
|
||||
futureDays = int.Parse(queryFutureDays.Value);
|
||||
end = DateTime.Today.AddDays(futureDays);
|
||||
}
|
||||
|
||||
var episodes = _episodeService.EpisodesBetweenDates(start, end, false);
|
||||
var icalCalendar = new iCalendar();
|
||||
|
||||
|
||||
@@ -15,12 +15,14 @@ namespace NzbDrone.Api.Config
|
||||
public class HostConfigModule : NzbDroneRestModule<HostConfigResource>
|
||||
{
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public HostConfigModule(IConfigFileProvider configFileProvider, IUserService userService)
|
||||
public HostConfigModule(IConfigFileProvider configFileProvider, IConfigService configService, IUserService userService)
|
||||
: base("/config/host")
|
||||
{
|
||||
_configFileProvider = configFileProvider;
|
||||
_configService = configService;
|
||||
_userService = userService;
|
||||
|
||||
GetResourceSingle = GetHostConfig;
|
||||
@@ -49,7 +51,7 @@ namespace NzbDrone.Api.Config
|
||||
private HostConfigResource GetHostConfig()
|
||||
{
|
||||
var resource = new HostConfigResource();
|
||||
resource.InjectFrom(_configFileProvider);
|
||||
resource.InjectFrom(_configFileProvider, _configService);
|
||||
resource.Id = 1;
|
||||
|
||||
var user = _userService.FindUser();
|
||||
@@ -75,6 +77,7 @@ namespace NzbDrone.Api.Config
|
||||
.ToDictionary(prop => prop.Name, prop => prop.GetValue(resource, null));
|
||||
|
||||
_configFileProvider.SaveConfigDictionary(dictionary);
|
||||
_configService.SaveConfigDictionary(dictionary);
|
||||
|
||||
if (resource.Username.IsNotNullOrWhiteSpace() && resource.Password.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Update;
|
||||
using NzbDrone.Common.Http.Proxy;
|
||||
|
||||
namespace NzbDrone.Api.Config
|
||||
{
|
||||
@@ -25,5 +26,13 @@ namespace NzbDrone.Api.Config
|
||||
public bool UpdateAutomatically { get; set; }
|
||||
public UpdateMechanism UpdateMechanism { get; set; }
|
||||
public string UpdateScriptPath { get; set; }
|
||||
public bool ProxyEnabled { get; set; }
|
||||
public ProxyType ProxyType { get; set; }
|
||||
public string ProxyHostname { get; set; }
|
||||
public int ProxyPort { get; set; }
|
||||
public string ProxyUsername { get; set; }
|
||||
public string ProxyPassword { get; set; }
|
||||
public string ProxyBypassFilter { get; set; }
|
||||
public bool ProxyBypassLocalAddresses { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Api.Validation;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Api.Config
|
||||
@@ -16,8 +17,7 @@ namespace NzbDrone.Api.Config
|
||||
.GreaterThanOrEqualTo(0);
|
||||
|
||||
SharedValidator.RuleFor(c => c.RssSyncInterval)
|
||||
.InclusiveBetween(10, 120)
|
||||
.When(c => c.RssSyncInterval > 0);
|
||||
.IsValidRssSyncInterval();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,16 +52,16 @@ namespace NzbDrone.Api.EpisodeFiles
|
||||
|
||||
private List<EpisodeFileResource> GetEpisodeFiles()
|
||||
{
|
||||
var seriesId = (int?)Request.Query.SeriesId;
|
||||
|
||||
if (seriesId == null)
|
||||
if (!Request.Query.SeriesId.HasValue)
|
||||
{
|
||||
throw new BadRequestException("seriesId is missing");
|
||||
}
|
||||
|
||||
var series = _seriesService.GetSeries(seriesId.Value);
|
||||
var seriesId = (int)Request.Query.SeriesId;
|
||||
|
||||
return _mediaFileService.GetFilesBySeries(seriesId.Value)
|
||||
var series = _seriesService.GetSeries(seriesId);
|
||||
|
||||
return _mediaFileService.GetFilesBySeries(seriesId)
|
||||
.Select(f => MapToResource(series, f)).ToList();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,14 @@ namespace NzbDrone.Api.Episodes
|
||||
|
||||
private List<EpisodeResource> GetEpisodes()
|
||||
{
|
||||
var seriesId = (int?)Request.Query.SeriesId;
|
||||
|
||||
if (seriesId == null)
|
||||
if (!Request.Query.SeriesId.HasValue)
|
||||
{
|
||||
throw new BadRequestException("seriesId is missing");
|
||||
}
|
||||
|
||||
var resources = ToListResource(_episodeService.GetEpisodeBySeries(seriesId.Value));
|
||||
var seriesId = (int)Request.Query.SeriesId;
|
||||
|
||||
var resources = ToListResource(_episodeService.GetEpisodeBySeries(seriesId));
|
||||
|
||||
return resources;
|
||||
}
|
||||
@@ -36,5 +36,10 @@ namespace NzbDrone.Api.Episodes
|
||||
{
|
||||
_episodeService.SetEpisodeMonitored(episodeResource.Id, episodeResource.Monitored);
|
||||
}
|
||||
|
||||
protected override List<EpisodeResource> LoadSeries(List<EpisodeResource> resources)
|
||||
{
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,8 @@ namespace NzbDrone.Api.Episodes
|
||||
{
|
||||
var resources = base.ToListResource(modelList);
|
||||
|
||||
return resources.LoadSubtype<EpisodeResource, SeriesResource, Core.Tv.Series>(e => e.SeriesId, _seriesService.GetSeries).ToList();
|
||||
return LoadSeries(resources);
|
||||
|
||||
}
|
||||
|
||||
public void Handle(EpisodeGrabbedEvent message)
|
||||
@@ -100,5 +101,10 @@ namespace NzbDrone.Api.Episodes
|
||||
BroadcastResourceChange(ModelAction.Updated, episode.Id);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual List<EpisodeResource> LoadSeries(List<EpisodeResource> resources)
|
||||
{
|
||||
return resources.LoadSubtype<EpisodeResource, SeriesResource, Core.Tv.Series>(e => e.SeriesId, _seriesService.GetSeries).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,13 @@ namespace NzbDrone.Api.Episodes
|
||||
|
||||
private List<RenameEpisodeResource> GetEpisodes()
|
||||
{
|
||||
int seriesId;
|
||||
|
||||
if (Request.Query.SeriesId.HasValue)
|
||||
{
|
||||
seriesId = (int)Request.Query.SeriesId;
|
||||
}
|
||||
|
||||
else
|
||||
if (!Request.Query.SeriesId.HasValue)
|
||||
{
|
||||
throw new BadRequestException("seriesId is missing");
|
||||
}
|
||||
|
||||
var seriesId = (int)Request.Query.SeriesId;
|
||||
|
||||
if (Request.Query.SeasonNumber.HasValue)
|
||||
{
|
||||
var seasonNumber = (int)Request.Query.SeasonNumber;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace NzbDrone.Api.ErrorManagement
|
||||
|
||||
if (apiException != null)
|
||||
{
|
||||
_logger.WarnException("API Error", apiException);
|
||||
_logger.Warn(apiException, "API Error");
|
||||
return apiException.ToErrorResponse();
|
||||
}
|
||||
|
||||
@@ -65,10 +65,10 @@ namespace NzbDrone.Api.ErrorManagement
|
||||
|
||||
var sqlErrorMessage = string.Format("[{0} {1}]", context.Request.Method, context.Request.Path);
|
||||
|
||||
_logger.ErrorException(sqlErrorMessage, sqLiteException);
|
||||
_logger.Error(sqLiteException, sqlErrorMessage);
|
||||
}
|
||||
|
||||
_logger.FatalException("Request Failed", exception);
|
||||
_logger.Fatal(exception, "Request Failed");
|
||||
|
||||
return new ErrorModel
|
||||
{
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Unable to gzip response", ex);
|
||||
_logger.Error(ex, "Unable to gzip response");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Nancy;
|
||||
using Nancy.Bootstrapper;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Api.Extensions.Pipelines
|
||||
{
|
||||
public class RequestLoggingPipeline : IRegisterNancyPipeline
|
||||
{
|
||||
private static readonly Logger _loggerHttp = LogManager.GetLogger("Http");
|
||||
private static readonly Logger _loggerApi = LogManager.GetLogger("Api");
|
||||
|
||||
private static int _requestSequenceID;
|
||||
|
||||
public void Register(IPipelines pipelines)
|
||||
{
|
||||
pipelines.BeforeRequest.AddItemToStartOfPipeline(LogStart);
|
||||
pipelines.AfterRequest.AddItemToEndOfPipeline(LogEnd);
|
||||
}
|
||||
|
||||
private Response LogStart(NancyContext context)
|
||||
{
|
||||
var id = Interlocked.Increment(ref _requestSequenceID);
|
||||
|
||||
context.Items["ApiRequestSequenceID"] = id;
|
||||
context.Items["ApiRequestStartTime"] = DateTime.UtcNow;
|
||||
|
||||
var reqPath = GetRequestPathAndQuery(context.Request);
|
||||
|
||||
_loggerHttp.Trace("Req: {0} [{1}] {2}", id, context.Request.Method, reqPath);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void LogEnd(NancyContext context)
|
||||
{
|
||||
var id = (int)context.Items["ApiRequestSequenceID"];
|
||||
var startTime = (DateTime)context.Items["ApiRequestStartTime"];
|
||||
|
||||
var endTime = DateTime.UtcNow;
|
||||
var duration = endTime - startTime;
|
||||
|
||||
var reqPath = GetRequestPathAndQuery(context.Request);
|
||||
|
||||
_loggerHttp.Trace("Res: {0} [{1}] {2}: {3}.{4} ({5} ms)", id, context.Request.Method, reqPath, (int)context.Response.StatusCode, context.Response.StatusCode, (int)duration.TotalMilliseconds);
|
||||
|
||||
if (context.Request.IsApiRequest())
|
||||
{
|
||||
_loggerApi.Debug("[{0}] {1}: {2}.{3} ({4} ms)", context.Request.Method, reqPath, (int)context.Response.StatusCode, context.Response.StatusCode, (int)duration.TotalMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetRequestPathAndQuery(Request request)
|
||||
{
|
||||
if (request.Url.Query.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return string.Concat(request.Url.Path, "?", request.Url.Query);
|
||||
}
|
||||
else
|
||||
{
|
||||
return request.Url.Path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.HealthCheck;
|
||||
|
||||
namespace NzbDrone.Api.Health
|
||||
@@ -8,6 +9,6 @@ namespace NzbDrone.Api.Health
|
||||
{
|
||||
public HealthCheckResult Type { get; set; }
|
||||
public string Message { get; set; }
|
||||
public Uri WikiUrl { get; set; }
|
||||
public HttpUri WikiUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace NzbDrone.Api.Indexers
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
{
|
||||
_logger.ErrorException(ex.Message, ex);
|
||||
_logger.Error(ex, ex.Message);
|
||||
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed");
|
||||
}
|
||||
|
||||
@@ -89,14 +89,14 @@ namespace NzbDrone.Api.Indexers
|
||||
{
|
||||
try
|
||||
{
|
||||
var decisions = _nzbSearchService.EpisodeSearch(episodeId);
|
||||
var decisions = _nzbSearchService.EpisodeSearch(episodeId, true);
|
||||
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
|
||||
|
||||
return MapDecisions(prioritizedDecisions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Episode search failed: " + ex.Message, ex);
|
||||
_logger.Error(ex, "Episode search failed: " + ex.Message);
|
||||
}
|
||||
|
||||
return new List<ReleaseResource>();
|
||||
|
||||
@@ -9,6 +9,7 @@ using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
||||
namespace NzbDrone.Api.Indexers
|
||||
{
|
||||
@@ -30,7 +31,7 @@ namespace NzbDrone.Api.Indexers
|
||||
|
||||
PostValidator.RuleFor(s => s.Title).NotEmpty();
|
||||
PostValidator.RuleFor(s => s.DownloadUrl).NotEmpty();
|
||||
PostValidator.RuleFor(s => s.DownloadProtocol).NotEmpty();
|
||||
PostValidator.RuleFor(s => s.Protocol).NotEmpty();
|
||||
PostValidator.RuleFor(s => s.PublishDate).NotEmpty();
|
||||
}
|
||||
|
||||
@@ -38,11 +39,14 @@ namespace NzbDrone.Api.Indexers
|
||||
{
|
||||
_logger.Info("Release pushed: {0} - {1}", release.Title, release.DownloadUrl);
|
||||
|
||||
var info = release.InjectTo<ReleaseInfo>();
|
||||
var info = release.Protocol == DownloadProtocol.Usenet ?
|
||||
release.InjectTo<ReleaseInfo>() :
|
||||
release.InjectTo<TorrentInfo>();
|
||||
|
||||
info.Guid = "PUSH-" + info.DownloadUrl;
|
||||
|
||||
var decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
|
||||
var processed = _downloadDecisionProcessor.ProcessDecisions(decisions);
|
||||
_downloadDecisionProcessor.ProcessDecisions(decisions);
|
||||
|
||||
return MapDecisions(decisions).First().AsResponse();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Qualities;
|
||||
@@ -48,8 +49,25 @@ namespace NzbDrone.Api.Indexers
|
||||
public int? Leechers { get; set; }
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
|
||||
//TODO: besides a test I don't think this is used...
|
||||
public DownloadProtocol DownloadProtocol { get; set; }
|
||||
|
||||
// TODO: Remove in v3
|
||||
// Used to support the original Release Push implementation
|
||||
// JsonIgnore so we don't serialize it, but can still parse it
|
||||
[JsonIgnore]
|
||||
public DownloadProtocol DownloadProtocol
|
||||
{
|
||||
get
|
||||
{
|
||||
return Protocol;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value > 0 && Protocol == 0)
|
||||
{
|
||||
Protocol = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDaily { get; set; }
|
||||
public bool IsAbsoluteNumbering { get; set; }
|
||||
|
||||
@@ -30,10 +30,13 @@ namespace NzbDrone.Api.ManualImport
|
||||
|
||||
private ManualImportResource AddQualityWeight(ManualImportResource item)
|
||||
{
|
||||
item.QualityWeight = Quality.DefaultQualityDefinitions.Single(q => q.Quality == item.Quality.Quality).Weight;
|
||||
item.QualityWeight += item.Quality.Revision.Real * 10;
|
||||
item.QualityWeight += item.Quality.Revision.Version;
|
||||
|
||||
if (item.Quality != null)
|
||||
{
|
||||
item.QualityWeight = Quality.DefaultQualityDefinitions.Single(q => q.Quality == item.Quality.Quality).Weight;
|
||||
item.QualityWeight += item.Quality.Revision.Real * 10;
|
||||
item.QualityWeight += item.Quality.Revision.Version;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="FluentValidation, Version=5.5.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\FluentValidation.5.5.0.0\lib\Net40\FluentValidation.dll</HintPath>
|
||||
<Reference Include="FluentValidation, Version=6.2.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentValidation.6.2.1.0\lib\portable-net40+sl50+wp80+win8+wpa81\FluentValidation.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Nancy, Version=0.23.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
@@ -59,6 +59,10 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.3.4\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
@@ -66,9 +70,6 @@
|
||||
<Reference Include="DDay.iCal">
|
||||
<HintPath>..\packages\DDay.iCal.1.0.2.575\lib\DDay.iCal.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog">
|
||||
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Omu.ValueInjecter">
|
||||
<HintPath>..\packages\ValueInjecter.2.3.3\lib\net35\Omu.ValueInjecter.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -99,6 +100,7 @@
|
||||
<Compile Include="Commands\CommandResource.cs" />
|
||||
<Compile Include="Extensions\AccessControlHeaders.cs" />
|
||||
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
|
||||
<Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" />
|
||||
<Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" />
|
||||
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
|
||||
<Compile Include="Indexers\ReleaseModuleBase.cs" />
|
||||
@@ -238,6 +240,7 @@
|
||||
<Compile Include="TinyIoCNancyBootstrapper.cs" />
|
||||
<Compile Include="Update\UpdateModule.cs" />
|
||||
<Compile Include="Update\UpdateResource.cs" />
|
||||
<Compile Include="Validation\RssSyncIntervalValidator.cs" />
|
||||
<Compile Include="Validation\EmptyCollectionValidator.cs" />
|
||||
<Compile Include="Validation\RuleBuilderExtensions.cs" />
|
||||
<Compile Include="Wanted\CutoffModule.cs" />
|
||||
@@ -270,7 +273,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
@@ -92,11 +92,6 @@ namespace NzbDrone.Api
|
||||
{
|
||||
var providerDefinition = GetDefinition(providerResource, false);
|
||||
|
||||
if (providerDefinition.Enable)
|
||||
{
|
||||
Test(providerDefinition, false);
|
||||
}
|
||||
|
||||
_providerFactory.Update(providerDefinition);
|
||||
}
|
||||
|
||||
@@ -160,8 +155,10 @@ namespace NzbDrone.Api
|
||||
|
||||
private Response Test(TProviderResource providerResource)
|
||||
{
|
||||
var providerDefinition = GetDefinition(providerResource, true);
|
||||
// Don't validate when getting the definition so we can validate afterwards (avoids validation being skipped because the provider is disabled)
|
||||
var providerDefinition = GetDefinition(providerResource, true, false);
|
||||
|
||||
Validate(providerDefinition, true);
|
||||
Test(providerDefinition, true);
|
||||
|
||||
return "{}";
|
||||
|
||||
@@ -16,7 +16,9 @@ namespace NzbDrone.Api.RootFolders
|
||||
RootFolderValidator rootFolderValidator,
|
||||
PathExistsValidator pathExistsValidator,
|
||||
DroneFactoryValidator droneFactoryValidator,
|
||||
MappedNetworkDriveValidator mappedNetworkDriveValidator)
|
||||
MappedNetworkDriveValidator mappedNetworkDriveValidator,
|
||||
StartupFolderValidator startupFolderValidator,
|
||||
FolderWritableValidator folderWritableValidator)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_rootFolderService = rootFolderService;
|
||||
@@ -32,7 +34,9 @@ namespace NzbDrone.Api.RootFolders
|
||||
.SetValidator(rootFolderValidator)
|
||||
.SetValidator(droneFactoryValidator)
|
||||
.SetValidator(mappedNetworkDriveValidator)
|
||||
.SetValidator(pathExistsValidator);
|
||||
.SetValidator(startupFolderValidator)
|
||||
.SetValidator(pathExistsValidator)
|
||||
.SetValidator(folderWritableValidator);
|
||||
}
|
||||
|
||||
private RootFolderResource GetRootFolder(int id)
|
||||
|
||||
@@ -6,5 +6,6 @@ namespace NzbDrone.Api.Series
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public int SceneSeasonNumber { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.SeriesStats;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Api.Validation;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Validation;
|
||||
using NzbDrone.SignalR;
|
||||
|
||||
namespace NzbDrone.Api.Series
|
||||
@@ -43,7 +43,8 @@ namespace NzbDrone.Api.Series
|
||||
SeriesPathValidator seriesPathValidator,
|
||||
SeriesExistsValidator seriesExistsValidator,
|
||||
DroneFactoryValidator droneFactoryValidator,
|
||||
SeriesAncestorValidator seriesAncestorValidator
|
||||
SeriesAncestorValidator seriesAncestorValidator,
|
||||
ProfileExistsValidator profileExistsValidator
|
||||
)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
@@ -59,7 +60,7 @@ namespace NzbDrone.Api.Series
|
||||
UpdateResource = UpdateSeries;
|
||||
DeleteResource = DeleteSeries;
|
||||
|
||||
SharedValidator.RuleFor(s => s.ProfileId).ValidId();
|
||||
Validation.RuleBuilderExtensions.ValidId(SharedValidator.RuleFor(s => s.ProfileId));
|
||||
|
||||
SharedValidator.RuleFor(s => s.Path)
|
||||
.Cascade(CascadeMode.StopOnFirstFailure)
|
||||
@@ -70,6 +71,8 @@ namespace NzbDrone.Api.Series
|
||||
.SetValidator(seriesAncestorValidator)
|
||||
.When(s => !s.Path.IsNullOrWhiteSpace());
|
||||
|
||||
SharedValidator.RuleFor(s => s.ProfileId).SetValidator(profileExistsValidator);
|
||||
|
||||
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
|
||||
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
|
||||
PostValidator.RuleFor(s => s.Title).NotEmpty();
|
||||
|
||||
@@ -93,7 +93,6 @@ namespace NzbDrone.Api
|
||||
break;
|
||||
case Lifetime.PerRequest:
|
||||
throw new InvalidOperationException("Unable to directly register a per request lifetime.");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
@@ -120,7 +119,6 @@ namespace NzbDrone.Api
|
||||
break;
|
||||
case Lifetime.PerRequest:
|
||||
throw new InvalidOperationException("Unable to directly register a per request lifetime.");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
34
src/NzbDrone.Api/Validation/RssSyncIntervalValidator.cs
Normal file
34
src/NzbDrone.Api/Validation/RssSyncIntervalValidator.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using FluentValidation.Validators;
|
||||
|
||||
namespace NzbDrone.Api.Validation
|
||||
{
|
||||
public class RssSyncIntervalValidator : PropertyValidator
|
||||
{
|
||||
public RssSyncIntervalValidator()
|
||||
: base("Must be between 10 and 120 or 0 to disable")
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
if (context.PropertyValue == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var value = (int)context.PropertyValue;
|
||||
|
||||
if (value == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value >= 10 && value <= 120)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,5 +31,10 @@ namespace NzbDrone.Api.Validation
|
||||
{
|
||||
return ruleBuilder.SetValidator(new EmptyCollectionValidator<TProp>());
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, int> IsValidRssSyncInterval<T>(this IRuleBuilder<T, int> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.SetValidator(new RssSyncIntervalValidator());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="DDay.iCal" version="1.0.2.575" targetFramework="net40" />
|
||||
<package id="FluentValidation" version="5.5.0.0" targetFramework="net40" />
|
||||
<package id="FluentValidation" version="6.2.1.0" targetFramework="net40" />
|
||||
<package id="Nancy" version="0.23.2" targetFramework="net40" />
|
||||
<package id="Nancy.Authentication.Basic" version="0.23.2" targetFramework="net40" />
|
||||
<package id="Nancy.Authentication.Forms" version="0.23.2" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
|
||||
<package id="NLog" version="2.1.0" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.4" targetFramework="net40" />
|
||||
<package id="ValueInjecter" version="2.3.3" targetFramework="net40" />
|
||||
</packages>
|
||||
</packages>
|
||||
@@ -37,17 +37,21 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="FluentAssertions, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.dll</HintPath>
|
||||
<Reference Include="FluentAssertions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FluentAssertions.Core, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
<Reference Include="FluentAssertions.Core, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.3.4\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.2.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -60,9 +64,6 @@
|
||||
<Reference Include="Moq">
|
||||
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog">
|
||||
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ContainerFixture.cs" />
|
||||
@@ -104,7 +105,6 @@
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent Condition="('$(OS)' == 'Windows_NT')">
|
||||
xcopy /s /y "$(SolutionDir)\..\_output\NzbDrone.Mono.*" "$(TargetDir)"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FluentAssertions" version="3.4.0" targetFramework="net40" />
|
||||
<package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
|
||||
<package id="Moq" version="4.0.10827" />
|
||||
<package id="NBuilder" version="3.0.1.1" />
|
||||
<package id="NLog" version="2.1.0" targetFramework="net40" />
|
||||
<package id="NUnit" version="2.6.3" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.4" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.2.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -38,17 +38,21 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="FluentAssertions, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.dll</HintPath>
|
||||
<Reference Include="FluentAssertions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FluentAssertions.Core, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
<Reference Include="FluentAssertions.Core, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.3.4\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.2.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -58,9 +62,6 @@
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="NLog">
|
||||
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="WebDriver, Version=2.48.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Selenium.WebDriver.2.48.0\lib\net40\WebDriver.dll</HintPath>
|
||||
@@ -95,7 +96,6 @@
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FluentAssertions" version="3.4.0" targetFramework="net40" />
|
||||
<package id="NLog" version="2.1.0" targetFramework="net40" />
|
||||
<package id="NUnit" version="2.6.3" targetFramework="net40" />
|
||||
<package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.4" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.2.0" targetFramework="net40" />
|
||||
<package id="Selenium.Support" version="2.48.0" targetFramework="net40" />
|
||||
<package id="Selenium.WebDriver" version="2.48.0" targetFramework="net40" />
|
||||
</packages>
|
||||
102
src/NzbDrone.Common.Test/CacheTests/CachedDictionaryFixture.cs
Normal file
102
src/NzbDrone.Common.Test/CacheTests/CachedDictionaryFixture.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Cache;
|
||||
|
||||
namespace NzbDrone.Common.Test.CacheTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class CachedDictionaryFixture
|
||||
{
|
||||
private CachedDictionary<string> _cachedString;
|
||||
private DictionaryWorker _worker;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_worker = new DictionaryWorker();
|
||||
_cachedString = new CachedDictionary<string>(_worker.GetDict, TimeSpan.FromMilliseconds(100));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_fetch_on_create()
|
||||
{
|
||||
_worker.HitCount.Should().Be(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fetch_on_first_call()
|
||||
{
|
||||
var result = _cachedString.Get("Hi");
|
||||
|
||||
_worker.HitCount.Should().Be(1);
|
||||
|
||||
result.Should().Be("Value");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fetch_once()
|
||||
{
|
||||
var result1 = _cachedString.Get("Hi");
|
||||
var result2 = _cachedString.Get("HitCount");
|
||||
|
||||
_worker.HitCount.Should().Be(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_auto_refresh_after_lifetime()
|
||||
{
|
||||
var result1 = _cachedString.Get("Hi");
|
||||
|
||||
Thread.Sleep(200);
|
||||
|
||||
var result2 = _cachedString.Get("Hi");
|
||||
|
||||
_worker.HitCount.Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_refresh_early_if_requested()
|
||||
{
|
||||
var result1 = _cachedString.Get("Hi");
|
||||
|
||||
Thread.Sleep(10);
|
||||
|
||||
_cachedString.RefreshIfExpired(TimeSpan.FromMilliseconds(1));
|
||||
|
||||
var result2 = _cachedString.Get("Hi");
|
||||
|
||||
_worker.HitCount.Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_refresh_early_if_not_expired()
|
||||
{
|
||||
var result1 = _cachedString.Get("Hi");
|
||||
|
||||
_cachedString.RefreshIfExpired(TimeSpan.FromMilliseconds(50));
|
||||
|
||||
var result2 = _cachedString.Get("Hi");
|
||||
|
||||
_worker.HitCount.Should().Be(1);
|
||||
}
|
||||
}
|
||||
|
||||
public class DictionaryWorker
|
||||
{
|
||||
public int HitCount { get; private set; }
|
||||
|
||||
public Dictionary<string, string> GetDict()
|
||||
{
|
||||
HitCount++;
|
||||
|
||||
var result = new Dictionary<string, string>();
|
||||
result["Hi"] = "Value";
|
||||
result["HitCount"] = "Hit count is " + HitCount;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ namespace NzbDrone.Common.Test.CacheTests
|
||||
int hitCount = 0;
|
||||
_cachedString = new Cached<string>();
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
_cachedString.Get("key", () =>
|
||||
{
|
||||
@@ -97,7 +97,7 @@ namespace NzbDrone.Common.Test.CacheTests
|
||||
return null;
|
||||
}, TimeSpan.FromMilliseconds(300));
|
||||
|
||||
Thread.Sleep(10);
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
hitCount.Should().BeInRange(3, 6);
|
||||
|
||||
@@ -17,17 +17,18 @@ namespace NzbDrone.Common.Test
|
||||
public class ConfigFileProviderTest : TestBase<ConfigFileProvider>
|
||||
{
|
||||
private string _configFileContents;
|
||||
private string _configFilePath;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
WithTempAsAppPath();
|
||||
|
||||
var configFile = Mocker.Resolve<IAppFolderInfo>().GetConfigPath();
|
||||
_configFilePath = Mocker.Resolve<IAppFolderInfo>().GetConfigPath();
|
||||
|
||||
_configFileContents = null;
|
||||
|
||||
WithMockConfigFile(configFile);
|
||||
WithMockConfigFile(_configFilePath);
|
||||
}
|
||||
|
||||
protected void WithMockConfigFile(string configFile)
|
||||
@@ -187,5 +188,30 @@ namespace NzbDrone.Common.Test
|
||||
Subject.SslPort.Should().Be(sslPort);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_config_file_is_empty()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.FileExists(_configFilePath))
|
||||
.Returns(true);
|
||||
|
||||
Assert.Throws<InvalidConfigFileException>(() => Subject.GetValue("key", "value"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_config_file_contains_only_null_character()
|
||||
{
|
||||
_configFileContents = "\0";
|
||||
|
||||
Assert.Throws<InvalidConfigFileException>(() => Subject.GetValue("key", "value"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_config_file_contains_invalid_xml()
|
||||
{
|
||||
_configFileContents = "{ \"key\": \"value\" }";
|
||||
|
||||
Assert.Throws<InvalidConfigFileException>(() => Subject.GetValue("key", "value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,10 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>(MockBehavior.Strict);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetMount(It.IsAny<string>()))
|
||||
.Returns((IMount)null);
|
||||
|
||||
WithEmulatedDiskProvider();
|
||||
|
||||
WithExistingFile(_sourcePath);
|
||||
@@ -34,7 +38,7 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
Subject.VerificationMode.Should().Be(DiskTransferVerificationMode.Transactional);
|
||||
Subject.VerificationMode.Should().Be(DiskTransferVerificationMode.TryTransactional);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -199,9 +203,6 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.CopyFile(_sourcePath, _targetPath, false), Times.Once());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.GetFileSize(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -213,9 +214,6 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.MoveFile(_sourcePath, _targetPath, false), Times.Once());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.GetFileSize(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -377,6 +375,52 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void mode_transactional_should_move_and_verify_if_same_folder()
|
||||
{
|
||||
Subject.VerificationMode = DiskTransferVerificationMode.Transactional;
|
||||
|
||||
var targetPath = _sourcePath + ".test";
|
||||
|
||||
var result = Subject.TransferFile(_sourcePath, targetPath, TransferMode.Move);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.TryCreateHardLink(_sourcePath, _backupPath), Times.Never());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.CopyFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()), Times.Never());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.MoveFile(_sourcePath, targetPath, false), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void mode_trytransactional_should_revert_to_verifyonly_if_hardlink_fails()
|
||||
{
|
||||
Subject.VerificationMode = DiskTransferVerificationMode.TryTransactional;
|
||||
|
||||
WithFailedHardlink();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.MoveFile(_sourcePath, _targetPath, false))
|
||||
.Callback(() =>
|
||||
{
|
||||
WithExistingFile(_sourcePath, false);
|
||||
WithExistingFile(_targetPath, true);
|
||||
});
|
||||
|
||||
var result = Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.TryCreateHardLink(_sourcePath, _backupPath), Times.Once());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.CopyFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()), Times.Never());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.MoveFile(_sourcePath, _targetPath, false), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void mode_transactional_should_delete_old_backup_on_move()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Common.Test.ExtensionTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class FromOctalStringFixture
|
||||
{
|
||||
[TestCase("\\040", " ")]
|
||||
[TestCase("\\043", "#")]
|
||||
[TestCase("\\101", "A")]
|
||||
public void should_convert_octal_character_string_to_ascii_string(string octalString, string expected)
|
||||
{
|
||||
octalString.FromOctalString().Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NLog;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Http.Dispatchers;
|
||||
using NzbDrone.Common.Http.Proxy;
|
||||
using NzbDrone.Common.TPL;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
using NLog;
|
||||
using NzbDrone.Common.TPL;
|
||||
using Moq;
|
||||
using NzbDrone.Common.Http.Dispatchers;
|
||||
|
||||
namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
@@ -19,20 +22,33 @@ namespace NzbDrone.Common.Test.Http
|
||||
[TestFixture(typeof(ManagedHttpDispatcher))]
|
||||
[TestFixture(typeof(CurlHttpDispatcher))]
|
||||
public class HttpClientFixture<TDispatcher> : TestBase<HttpClient> where TDispatcher : IHttpDispatcher
|
||||
{
|
||||
{
|
||||
private static string[] _httpBinHosts = new[] { "eu.httpbin.org", "httpbin.org" };
|
||||
private static int _httpBinRandom;
|
||||
private string _httpBinHost;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Mocker.SetConstant<ICacheManager>(Mocker.Resolve<CacheManager>());
|
||||
Mocker.SetConstant<ICreateManagedWebProxy>(Mocker.Resolve<ManagedWebProxyFactory>());
|
||||
Mocker.SetConstant<IRateLimitService>(Mocker.Resolve<RateLimitService>());
|
||||
Mocker.SetConstant<IEnumerable<IHttpRequestInterceptor>>(new IHttpRequestInterceptor[0]);
|
||||
Mocker.SetConstant<IHttpDispatcher>(Mocker.Resolve<TDispatcher>());
|
||||
|
||||
// Used for manual testing of socks proxies.
|
||||
//Mocker.GetMock<IHttpProxySettingsProvider>()
|
||||
// .Setup(v => v.GetProxySettings(It.IsAny<HttpRequest>()))
|
||||
// .Returns(new HttpProxySettings(ProxyType.Socks5, "127.0.0.1", 5476, "", false));
|
||||
|
||||
// Roundrobin over the two servers, to reduce the chance of hitting the ratelimiter.
|
||||
_httpBinHost = _httpBinHosts[_httpBinRandom++ % _httpBinHosts.Length];
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_simple_get()
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
|
||||
var response = Subject.Execute(request);
|
||||
|
||||
@@ -42,7 +58,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_execute_https_get()
|
||||
{
|
||||
var request = new HttpRequest("https://eu.httpbin.org/get");
|
||||
var request = new HttpRequest(string.Format("https://{0}/get", _httpBinHost));
|
||||
|
||||
var response = Subject.Execute(request);
|
||||
|
||||
@@ -52,28 +68,30 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_execute_typed_get()
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Url.Should().Be(request.Url.ToString());
|
||||
response.Resource.Url.Should().Be(request.Url.FullUri);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_simple_post()
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/post");
|
||||
request.Body = "{ my: 1 }";
|
||||
var message = "{ my: 1 }";
|
||||
|
||||
var request = new HttpRequest(string.Format("http://{0}/post", _httpBinHost));
|
||||
request.SetContent(message);
|
||||
|
||||
var response = Subject.Post<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Data.Should().Be(request.Body);
|
||||
response.Resource.Data.Should().Be(message);
|
||||
}
|
||||
|
||||
[TestCase("gzip")]
|
||||
public void should_execute_get_using_gzip(string compression)
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/" + compression);
|
||||
var request = new HttpRequest(string.Format("http://{0}/{1}", _httpBinHost, compression));
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
@@ -89,7 +107,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
[TestCase(HttpStatusCode.BadGateway)]
|
||||
public void should_throw_on_unsuccessful_status_codes(int statusCode)
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/status/" + statusCode);
|
||||
var request = new HttpRequest(string.Format("http://{0}/status/{1}", _httpBinHost, statusCode));
|
||||
|
||||
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
|
||||
|
||||
@@ -101,7 +119,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_not_follow_redirects_when_not_in_production()
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/redirect/1");
|
||||
var request = new HttpRequest(string.Format("http://{0}/redirect/1", _httpBinHost));
|
||||
|
||||
Subject.Get(request);
|
||||
|
||||
@@ -111,7 +129,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_follow_redirects()
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/redirect/1");
|
||||
var request = new HttpRequest(string.Format("http://{0}/redirect/1", _httpBinHost));
|
||||
request.AllowAutoRedirect = true;
|
||||
|
||||
Subject.Get(request);
|
||||
@@ -122,7 +140,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_send_user_agent()
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
@@ -136,7 +154,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
[TestCase("Accept", "text/xml, text/rss+xml, application/rss+xml")]
|
||||
public void should_send_headers(string header, string value)
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
request.Headers.Add(header, value);
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
@@ -159,8 +177,8 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_send_cookie()
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
request.AddCookie("my", "cookie");
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
request.Cookies["my"] = "cookie";
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
@@ -174,7 +192,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
public void GivenOldCookie()
|
||||
{
|
||||
var oldRequest = new HttpRequest("http://eu.httpbin.org/get");
|
||||
oldRequest.AddCookie("my", "cookie");
|
||||
oldRequest.Cookies["my"] = "cookie";
|
||||
|
||||
var oldClient = new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<ICacheManager>(), Mocker.Resolve<IRateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.Resolve<Logger>());
|
||||
|
||||
@@ -216,12 +234,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_not_store_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest("http://eu.httpbin.org/cookies/set?my=cookie");
|
||||
var requestSet = new HttpRequest(string.Format("http://{0}/cookies/set?my=cookie", _httpBinHost));
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
@@ -233,13 +251,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_store_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest("http://eu.httpbin.org/cookies/set?my=cookie");
|
||||
var requestSet = new HttpRequest(string.Format("http://{0}/cookies/set?my=cookie", _httpBinHost));
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
@@ -255,14 +273,14 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_overwrite_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest("http://eu.httpbin.org/cookies/set?my=cookie");
|
||||
var requestSet = new HttpRequest(string.Format("http://{0}/cookies/set?my=cookie", _httpBinHost));
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
requestSet.AddCookie("my", "oldcookie");
|
||||
requestSet.Cookies["my"] = "oldcookie";
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
@@ -278,7 +296,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_throw_on_http429_too_many_requests()
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/status/429");
|
||||
var request = new HttpRequest(string.Format("http://{0}/status/429", _httpBinHost));
|
||||
|
||||
Assert.Throws<TooManyRequestsException>(() => Subject.Get(request));
|
||||
|
||||
@@ -298,7 +316,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
.Setup(v => v.PostResponse(It.IsAny<HttpResponse>()))
|
||||
.Returns<HttpResponse>(r => r);
|
||||
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
|
||||
Subject.Get(request);
|
||||
|
||||
@@ -309,30 +327,70 @@ namespace NzbDrone.Common.Test.Http
|
||||
.Verify(v => v.PostResponse(It.IsAny<HttpResponse>()), Times.Once());
|
||||
}
|
||||
|
||||
public void should_parse_malformed_cloudflare_cookie()
|
||||
[TestCase("en-US")]
|
||||
[TestCase("es-ES")]
|
||||
public void should_parse_malformed_cloudflare_cookie(string culture)
|
||||
{
|
||||
// the date is bad in the below - should be 13-Jul-2016
|
||||
string malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Wed, 13-Jul-16 16:19:50 GMT; path=/; HttpOnly";
|
||||
string url = "http://eu.httpbin.org/response-headers?Set-Cookie=" +
|
||||
System.Uri.EscapeUriString(malformedCookie);
|
||||
var origCulture = Thread.CurrentThread.CurrentCulture;
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
|
||||
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture);
|
||||
try
|
||||
{
|
||||
// the date is bad in the below - should be 13-Jul-2016
|
||||
string malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Wed, 13-Jul-16 16:19:50 GMT; path=/; HttpOnly";
|
||||
var requestSet = new HttpRequestBuilder(string.Format("http://{0}/response-headers", _httpBinHost))
|
||||
.AddQueryParam("Set-Cookie", malformedCookie)
|
||||
.Build();
|
||||
|
||||
var requestSet = new HttpRequest(url);
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("Cookie");
|
||||
response.Resource.Headers.Should().ContainKey("Cookie");
|
||||
|
||||
var cookie = response.Resource.Headers["Cookie"].ToString();
|
||||
var cookie = response.Resource.Headers["Cookie"].ToString();
|
||||
|
||||
cookie.Should().Contain("__cfduid=d29e686a9d65800021c66faca0a29b4261436890790");
|
||||
cookie.Should().Contain("__cfduid=d29e686a9d65800021c66faca0a29b4261436890790");
|
||||
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = origCulture;
|
||||
Thread.CurrentThread.CurrentUICulture = origCulture;
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("lang_code=en; expires=Fri, 23-Dec-2016 18:09:14 GMT; Max-Age=31536000; path=/; domain=.abc.com")]
|
||||
public void should_reject_malformed_domain_cookie(string malformedCookie)
|
||||
{
|
||||
try
|
||||
{
|
||||
// the date is bad in the below - should be 13-Jul-2016
|
||||
string url = string.Format("http://{0}/response-headers?Set-Cookie={1}", _httpBinHost, Uri.EscapeUriString(malformedCookie));
|
||||
|
||||
var requestSet = new HttpRequest(url);
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
|
||||
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().NotContainKey("Cookie");
|
||||
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Test.Common;
|
||||
@@ -8,14 +9,32 @@ namespace NzbDrone.Common.Test.Http
|
||||
[TestFixture]
|
||||
public class HttpRequestBuilderFixture : TestBase
|
||||
{
|
||||
[TestCase("http://host/{seg}/some", "http://host/dir/some")]
|
||||
[TestCase("http://host/some/{seg}", "http://host/some/dir")]
|
||||
public void should_add_single_segment_url_segments(string url, string result)
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(url);
|
||||
|
||||
requestBuilder.SetSegment("seg", "dir");
|
||||
|
||||
requestBuilder.Build().Url.Should().Be(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void shouldnt_add_value_for_nonexisting_segment()
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder("http://host/{seg}/some");
|
||||
Assert.Throws<InvalidOperationException>(() => requestBuilder.SetSegment("seg2", "dir"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_remove_duplicated_slashes()
|
||||
{
|
||||
var builder = new HttpRequestBuilder("http://domain/");
|
||||
|
||||
var request = builder.Build("/v1/");
|
||||
var request = builder.Resource("/v1/").Build();
|
||||
|
||||
request.Url.ToString().Should().Be("http://domain/v1/");
|
||||
request.Url.FullUri.Should().Be("http://domain/v1/");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,22 +8,5 @@ namespace NzbDrone.Common.Test.Http
|
||||
[TestFixture]
|
||||
public class HttpRequestFixture
|
||||
{
|
||||
[TestCase("http://host/{seg}/some", "http://host/dir/some")]
|
||||
[TestCase("http://host/some/{seg}", "http://host/some/dir")]
|
||||
public void should_add_single_segment_url_segments(string url, string result)
|
||||
{
|
||||
var request = new HttpRequest(url);
|
||||
|
||||
request.AddSegment("seg", "dir");
|
||||
|
||||
request.Url.Should().Be(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void shouldnt_add_value_for_nonexisting_segment()
|
||||
{
|
||||
var request = new HttpRequest("http://host/{seg}/some");
|
||||
Assert.Throws<InvalidOperationException>(() => request.AddSegment("seg2", "dir"));
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/NzbDrone.Common.Test/Http/HttpUriFixture.cs
Normal file
84
src/NzbDrone.Common.Test/Http/HttpUriFixture.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
public class HttpUriFixture : TestBase
|
||||
{
|
||||
[TestCase("", "", "")]
|
||||
[TestCase("/", "", "/")]
|
||||
[TestCase("base", "", "base")]
|
||||
[TestCase("/base", "", "/base")]
|
||||
[TestCase("/base/", "", "/base/")]
|
||||
[TestCase("", "relative", "relative")]
|
||||
[TestCase("", "/relative", "/relative")]
|
||||
[TestCase("/", "relative", "/relative")]
|
||||
[TestCase("/", "/relative", "/relative")]
|
||||
[TestCase("base", "relative", "relative")]
|
||||
[TestCase("base", "/relative", "/relative")]
|
||||
[TestCase("/base", "relative", "/relative")]
|
||||
[TestCase("/base", "/relative", "/relative")]
|
||||
[TestCase("/base/", "relative", "/base/relative")]
|
||||
[TestCase("/base/", "/relative", "/relative")]
|
||||
[TestCase("base/sub", "relative", "base/relative")]
|
||||
[TestCase("base/sub", "/relative", "/relative")]
|
||||
[TestCase("/base/sub", "relative", "/base/relative")]
|
||||
[TestCase("/base/sub", "/relative", "/relative")]
|
||||
[TestCase("/base/sub/", "relative", "/base/sub/relative")]
|
||||
[TestCase("/base/sub/", "/relative", "/relative")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml", "relative/path", "abc://host.com:8080/root/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml", "/relative/path", "abc://host.com:8080/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml?query=1#fragment", "relative/path", "abc://host.com:8080/root/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml?query=1#fragment", "/relative/path", "abc://host.com:8080/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api", "relative/path", "abc://host.com:8080/root/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api", "/relative/path", "abc://host.com:8080/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api/", "relative/path", "abc://host.com:8080/root/api/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api/", "/relative/path", "abc://host.com:8080/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api/", "//otherhost.com/path", "abc://otherhost.com/path")]
|
||||
public void should_combine_uri(string basePath, string relativePath, string expected)
|
||||
{
|
||||
var newUri = new HttpUri(basePath) + new HttpUri(relativePath);
|
||||
newUri.FullUri.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("", "", "")]
|
||||
[TestCase("/", "", "/")]
|
||||
[TestCase("base", "", "base")]
|
||||
[TestCase("/base", "", "/base")]
|
||||
[TestCase("/base/", "", "/base/")]
|
||||
[TestCase("", "relative", "relative")]
|
||||
[TestCase("", "/relative", "/relative")]
|
||||
[TestCase("/", "relative", "/relative")]
|
||||
[TestCase("/", "/relative", "/relative")]
|
||||
[TestCase("base", "relative", "base/relative")]
|
||||
[TestCase("base", "/relative", "base/relative")]
|
||||
[TestCase("/base", "relative", "/base/relative")]
|
||||
[TestCase("/base", "/relative", "/base/relative")]
|
||||
[TestCase("/base/", "relative", "/base/relative")]
|
||||
[TestCase("/base/", "/relative", "/base/relative")]
|
||||
[TestCase("base/sub", "relative", "base/sub/relative")]
|
||||
[TestCase("base/sub", "/relative", "base/sub/relative")]
|
||||
[TestCase("/base/sub", "relative", "/base/sub/relative")]
|
||||
[TestCase("/base/sub", "/relative", "/base/sub/relative")]
|
||||
[TestCase("/base/sub/", "relative", "/base/sub/relative")]
|
||||
[TestCase("/base/sub/", "/relative", "/base/sub/relative")]
|
||||
[TestCase("/base/sub/", "relative/", "/base/sub/relative/")]
|
||||
[TestCase("/base/sub/", "/relative/", "/base/sub/relative/")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml", "relative/path", "abc://host.com:8080/root/file.xml/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml", "/relative/path", "abc://host.com:8080/root/file.xml/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml?query=1#fragment", "relative/path", "abc://host.com:8080/root/file.xml/relative/path?query=1#fragment")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml?query=1#fragment", "/relative/path", "abc://host.com:8080/root/file.xml/relative/path?query=1#fragment")]
|
||||
[TestCase("abc://host.com:8080/root/api", "relative/path", "abc://host.com:8080/root/api/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api", "/relative/path", "abc://host.com:8080/root/api/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api/", "relative/path", "abc://host.com:8080/root/api/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api/", "/relative/path", "abc://host.com:8080/root/api/relative/path")]
|
||||
public void should_combine_relative_path(string basePath, string relativePath, string expected)
|
||||
{
|
||||
var newUri = new HttpUri(basePath).CombinePath(relativePath);
|
||||
|
||||
newUri.FullUri.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,11 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
// Indexer Urls
|
||||
[TestCase(@"https://iptorrents.com/torrents/rss?u=mySecret;tp=mySecret;l5;download")]
|
||||
[TestCase(@"http://rss.torrentleech.org/mySecret")]
|
||||
[TestCase(@"http://rss.torrentleech.org/rss/download/12345/01233210/filename.torrent")]
|
||||
[TestCase(@"http://www.bitmetv.org/rss.php?uid=mySecret&passkey=mySecret")]
|
||||
[TestCase(@"https://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user=sonarr&api=mySecret&eng=1")]
|
||||
[TestCase(@"https://dognzb.cr/fetch/2b51db35e1912ffc138825a12b9933d2/2b51db35e1910123321025a12b9933d2")]
|
||||
[TestCase(@"https://baconbits.org/feeds.php?feed=torrents_tv&user=12345&auth=2b51db35e1910123321025a12b9933d2&passkey=mySecret&authkey=2b51db35e1910123321025a12b9933d2")]
|
||||
// NzbGet
|
||||
[TestCase(@"{ ""Name"" : ""ControlUsername"", ""Value"" : ""mySecret"" }, { ""Name"" : ""ControlPassword"", ""Value"" : ""mySecret"" }, ")]
|
||||
[TestCase(@"{ ""Name"" : ""Server1.Username"", ""Value"" : ""mySecret"" }, { ""Name"" : ""Server1.Password"", ""Value"" : ""mySecret"" }, ")]
|
||||
@@ -44,6 +47,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
||||
|
||||
cleansedMessage.Should().NotContain("mySecret");
|
||||
cleansedMessage.Should().NotContain("01233210");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -37,17 +37,21 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="FluentAssertions, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.dll</HintPath>
|
||||
<Reference Include="FluentAssertions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FluentAssertions.Core, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
<Reference Include="FluentAssertions.Core, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.3.4\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.2.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -60,11 +64,9 @@
|
||||
<Reference Include="Moq">
|
||||
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog">
|
||||
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CacheTests\CachedDictionaryFixture.cs" />
|
||||
<Compile Include="CacheTests\CachedFixture.cs" />
|
||||
<Compile Include="CacheTests\CachedManagerFixture.cs" />
|
||||
<Compile Include="ConfigFileProviderTest.cs" />
|
||||
@@ -77,10 +79,12 @@
|
||||
<Compile Include="EnvironmentProviderTest.cs" />
|
||||
<Compile Include="EnvironmentTests\EnvironmentProviderTest.cs" />
|
||||
<Compile Include="EnvironmentTests\StartupArgumentsFixture.cs" />
|
||||
<Compile Include="ExtensionTests\FromOctalStringFixture.cs" />
|
||||
<Compile Include="ExtensionTests\Int64ExtensionFixture.cs" />
|
||||
<Compile Include="Http\HttpClientFixture.cs" />
|
||||
<Compile Include="Http\HttpRequestBuilderFixture.cs" />
|
||||
<Compile Include="Http\HttpRequestFixture.cs" />
|
||||
<Compile Include="Http\HttpUriFixture.cs" />
|
||||
<Compile Include="InstrumentationTests\CleanseLogMessageFixture.cs" />
|
||||
<Compile Include="LevenshteinDistanceFixture.cs" />
|
||||
<Compile Include="OsPathFixture.cs" />
|
||||
@@ -141,15 +145,14 @@
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent Condition="'$(Configuration)|$(OS)' == 'Debug|Windows_NT'">xcopy /s /y "$(SolutionDir)\ExternalModules\CurlSharp\libs\i386\*" "$(TargetDir)"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -24,6 +24,7 @@ namespace NzbDrone.Common.Test
|
||||
[TestCase("/rooted/linux/path", OsPathKind.Unix)]
|
||||
[TestCase("/", OsPathKind.Unix)]
|
||||
[TestCase("linux/path", OsPathKind.Unix)]
|
||||
[TestCase(@"Castle:unrooted+linux+path", OsPathKind.Unknown)]
|
||||
public void should_auto_detect_kind(string path, OsPathKind kind)
|
||||
{
|
||||
var result = new OsPath(path);
|
||||
@@ -94,6 +95,8 @@ namespace NzbDrone.Common.Test
|
||||
[TestCase(@"rooted\windows\path")]
|
||||
[TestCase(@"path")]
|
||||
[TestCase("linux/path")]
|
||||
[TestCase(@"Castle:unrooted+linux+path")]
|
||||
[TestCase(@"C:unrooted+linux+path")]
|
||||
public void should_detect_unrooted_ospaths(string path)
|
||||
{
|
||||
var osPath = new OsPath(path);
|
||||
|
||||
@@ -6,6 +6,7 @@ using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
|
||||
namespace NzbDrone.Common.Test
|
||||
{
|
||||
@@ -153,7 +154,7 @@ namespace NzbDrone.Common.Test
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore]
|
||||
[Ignore("Parent, not Grandparent")]
|
||||
public void should_not_be_parent_when_it_is_grandparent()
|
||||
{
|
||||
var path = Path.Combine(_parent, "parent", "child");
|
||||
@@ -205,7 +206,7 @@ namespace NzbDrone.Common.Test
|
||||
public void get_actual_casing_should_return_actual_casing_for_local_dir_in_windows()
|
||||
{
|
||||
WindowsOnly();
|
||||
var path = Directory.GetCurrentDirectory().Replace("c:\\","C:\\");
|
||||
var path = Directory.GetCurrentDirectory().Replace("c:\\","C:\\").Replace("system32", "System32");
|
||||
|
||||
path.ToUpper().GetActualCasing().Should().Be(path);
|
||||
path.ToLower().GetActualCasing().Should().Be(path);
|
||||
@@ -222,6 +223,7 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
[Test]
|
||||
[Explicit]
|
||||
[ManualTest]
|
||||
public void get_actual_casing_should_return_original_casing_for_shares()
|
||||
{
|
||||
var path = @"\\server\Pool\Apps";
|
||||
|
||||
@@ -31,10 +31,20 @@ namespace NzbDrone.Common.Test
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Process.GetProcessesByName(DummyApp.DUMMY_PROCCESS_NAME).ToList().ForEach(c => c.Kill());
|
||||
Process.GetProcessesByName(DummyApp.DUMMY_PROCCESS_NAME).ToList().ForEach(c =>
|
||||
{
|
||||
try
|
||||
{
|
||||
c.Kill();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
TestLogger.Warn(ex, "{0} when killing process", ex.Message);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void GetById_should_return_null_if_process_doesnt_exist()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
using System.ServiceProcess;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
|
||||
namespace NzbDrone.Common.Test
|
||||
{
|
||||
@@ -13,7 +15,6 @@ namespace NzbDrone.Common.Test
|
||||
private const string ALWAYS_INSTALLED_SERVICE = "SCardSvr"; //Smart Card
|
||||
private const string TEMP_SERVICE_NAME = "NzbDrone_Nunit";
|
||||
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
@@ -24,8 +25,10 @@ namespace NzbDrone.Common.Test
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
WindowsOnly();
|
||||
CleanupService();
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
CleanupService();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +73,7 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
[Test]
|
||||
[Explicit]
|
||||
[ManualTest]
|
||||
public void UnInstallService()
|
||||
{
|
||||
Subject.UnInstall(ServiceProvider.NZBDRONE_SERVICE_NAME);
|
||||
@@ -78,6 +82,7 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
[Test]
|
||||
[Explicit]
|
||||
[ManualTest]
|
||||
public void Should_be_able_to_start_and_stop_service()
|
||||
{
|
||||
Subject.GetService(ALWAYS_INSTALLED_SERVICE).Status
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void should_hold_the_call_for_debounce_duration()
|
||||
{
|
||||
var counter = new Counter();
|
||||
@@ -40,6 +41,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void should_throttle_calls()
|
||||
{
|
||||
var counter = new Counter();
|
||||
@@ -65,6 +67,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void should_hold_the_call_while_paused()
|
||||
{
|
||||
var counter = new Counter();
|
||||
@@ -98,6 +101,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void should_handle_pause_reentrancy()
|
||||
{
|
||||
var counter = new Counter();
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void should_wait_for_existing()
|
||||
{
|
||||
GivenExisting("me", _epoch + TimeSpan.FromMilliseconds(200));
|
||||
@@ -70,7 +71,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
Subject.WaitAndPulse("me", TimeSpan.FromMilliseconds(400));
|
||||
watch.Stop();
|
||||
|
||||
watch.ElapsedMilliseconds.Should().BeInRange(195, 250);
|
||||
watch.ElapsedMilliseconds.Should().BeInRange(175, 250);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FluentAssertions" version="3.4.0" targetFramework="net40" />
|
||||
<package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
|
||||
<package id="Moq" version="4.0.10827" />
|
||||
<package id="NLog" version="2.1.0" targetFramework="net40" />
|
||||
<package id="NUnit" version="2.6.3" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.4" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.2.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -6,8 +6,9 @@ namespace NzbDrone.Common.Cache
|
||||
{
|
||||
public interface ICacheManager
|
||||
{
|
||||
ICached<T> GetCache<T>(Type host, string name);
|
||||
ICached<T> GetCache<T>(Type host);
|
||||
ICached<T> GetCache<T>(Type host, string name);
|
||||
ICachedDictionary<T> GetCacheDictionary<T>(Type host, string name, Func<IDictionary<string, T>> fetchFunc = null, TimeSpan? lifeTime = null);
|
||||
void Clear();
|
||||
ICollection<ICached> Caches { get; }
|
||||
}
|
||||
@@ -22,12 +23,6 @@ namespace NzbDrone.Common.Cache
|
||||
|
||||
}
|
||||
|
||||
public ICached<T> GetCache<T>(Type host)
|
||||
{
|
||||
Ensure.That(host, () => host).IsNotNull();
|
||||
return GetCache<T>(host, host.FullName);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_cache.Clear();
|
||||
@@ -35,6 +30,12 @@ namespace NzbDrone.Common.Cache
|
||||
|
||||
public ICollection<ICached> Caches { get { return _cache.Values; } }
|
||||
|
||||
public ICached<T> GetCache<T>(Type host)
|
||||
{
|
||||
Ensure.That(host, () => host).IsNotNull();
|
||||
return GetCache<T>(host, host.FullName);
|
||||
}
|
||||
|
||||
public ICached<T> GetCache<T>(Type host, string name)
|
||||
{
|
||||
Ensure.That(host, () => host).IsNotNull();
|
||||
@@ -42,5 +43,13 @@ namespace NzbDrone.Common.Cache
|
||||
|
||||
return (ICached<T>)_cache.Get(host.FullName + "_" + name, () => new Cached<T>());
|
||||
}
|
||||
|
||||
public ICachedDictionary<T> GetCacheDictionary<T>(Type host, string name, Func<IDictionary<string, T>> fetchFunc = null, TimeSpan? lifeTime = null)
|
||||
{
|
||||
Ensure.That(host, () => host).IsNotNull();
|
||||
Ensure.That(name, () => name).IsNotNullOrWhiteSpace();
|
||||
|
||||
return (ICachedDictionary<T>)_cache.Get("dict_" + host.FullName + "_" + name, () => new CachedDictionary<T>(fetchFunc, lifeTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
137
src/NzbDrone.Common/Cache/CachedDictionary.cs
Normal file
137
src/NzbDrone.Common/Cache/CachedDictionary.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Common.Cache
|
||||
{
|
||||
|
||||
public class CachedDictionary<TValue> : ICachedDictionary<TValue>
|
||||
{
|
||||
private readonly Func<IDictionary<string, TValue>> _fetchFunc;
|
||||
private readonly TimeSpan? _ttl;
|
||||
|
||||
private DateTime _lastRefreshed = DateTime.MinValue;
|
||||
private ConcurrentDictionary<string, TValue> _items = new ConcurrentDictionary<string, TValue>();
|
||||
|
||||
public CachedDictionary(Func<IDictionary<string, TValue>> fetchFunc = null, TimeSpan? ttl = null)
|
||||
{
|
||||
_fetchFunc = fetchFunc;
|
||||
_ttl = ttl;
|
||||
}
|
||||
|
||||
public bool IsExpired(TimeSpan ttl)
|
||||
{
|
||||
return _lastRefreshed.Add(ttl) < DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void RefreshIfExpired()
|
||||
{
|
||||
if (_ttl.HasValue && _fetchFunc != null)
|
||||
{
|
||||
RefreshIfExpired(_ttl.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshIfExpired(TimeSpan ttl)
|
||||
{
|
||||
if (IsExpired(ttl))
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
if (_fetchFunc == null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot update cache without data source.");
|
||||
}
|
||||
|
||||
Update(_fetchFunc());
|
||||
ExtendTTL();
|
||||
}
|
||||
|
||||
public void Update(IDictionary<string, TValue> items)
|
||||
{
|
||||
_items = new ConcurrentDictionary<string, TValue>(items);
|
||||
ExtendTTL();
|
||||
}
|
||||
|
||||
public void ExtendTTL()
|
||||
{
|
||||
_lastRefreshed = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||||
public ICollection<TValue> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
RefreshIfExpired();
|
||||
return _items.Values;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
RefreshIfExpired();
|
||||
return _items.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public TValue Get(string key)
|
||||
{
|
||||
RefreshIfExpired();
|
||||
|
||||
TValue result;
|
||||
|
||||
if (!_items.TryGetValue(key, out result))
|
||||
{
|
||||
throw new KeyNotFoundException(string.Format("Item {0} not found in cache.", key));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public TValue Find(string key)
|
||||
{
|
||||
RefreshIfExpired();
|
||||
|
||||
TValue result;
|
||||
|
||||
_items.TryGetValue(key, out result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_items.Clear();
|
||||
_lastRefreshed = DateTime.MinValue;
|
||||
}
|
||||
|
||||
public void ClearExpired()
|
||||
{
|
||||
if (!_ttl.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException("Checking expiry without ttl not possible.");
|
||||
}
|
||||
|
||||
if (IsExpired(_ttl.Value))
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(string key)
|
||||
{
|
||||
TValue item;
|
||||
_items.TryRemove(key, out item);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/NzbDrone.Common/Cache/ICachedDictionary.cs
Normal file
18
src/NzbDrone.Common/Cache/ICachedDictionary.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Common.Cache
|
||||
{
|
||||
public interface ICachedDictionary<TValue> : ICached
|
||||
{
|
||||
void RefreshIfExpired();
|
||||
void RefreshIfExpired(TimeSpan ttl);
|
||||
void Refresh();
|
||||
void Update(IDictionary<string, TValue> items);
|
||||
void ExtendTTL();
|
||||
TValue Get(string key);
|
||||
TValue Find(string key);
|
||||
bool IsExpired(TimeSpan ttl);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Common.Cloud
|
||||
{
|
||||
public interface IDroneServicesRequestBuilder
|
||||
{
|
||||
HttpRequest Build(string path);
|
||||
}
|
||||
|
||||
public class DroneServicesHttpRequestBuilder : HttpRequestBuilder, IDroneServicesRequestBuilder
|
||||
{
|
||||
private const string ROOT_URL = "http://services.sonarr.tv/v1/";
|
||||
|
||||
public DroneServicesHttpRequestBuilder()
|
||||
: base(ROOT_URL)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs
Normal file
28
src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Common.Cloud
|
||||
{
|
||||
public interface ISonarrCloudRequestBuilder
|
||||
{
|
||||
IHttpRequestBuilderFactory Services { get; }
|
||||
IHttpRequestBuilderFactory SkyHookTvdb { get; }
|
||||
}
|
||||
|
||||
public class SonarrCloudRequestBuilder : ISonarrCloudRequestBuilder
|
||||
{
|
||||
public SonarrCloudRequestBuilder()
|
||||
{
|
||||
Services = new HttpRequestBuilder("http://services.sonarr.tv/v1/")
|
||||
.CreateFactory();
|
||||
|
||||
SkyHookTvdb = new HttpRequestBuilder("http://skyhook.sonarr.tv/v1/tvdb/{route}/{language}/")
|
||||
.SetSegment("language", "en")
|
||||
.CreateFactory();
|
||||
}
|
||||
|
||||
public IHttpRequestBuilderFactory Services { get; private set; }
|
||||
|
||||
public IHttpRequestBuilderFactory SkyHookTvdb { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,16 @@ namespace NzbDrone.Common.Crypto
|
||||
|
||||
public static int GetHashInt31(string target)
|
||||
{
|
||||
byte[] hash;
|
||||
var hash = GetHash(target);
|
||||
return BitConverter.ToInt32(hash, 0) & 0x7fffffff;
|
||||
}
|
||||
|
||||
public static byte[] GetHash(string target)
|
||||
{
|
||||
lock (Sha1)
|
||||
{
|
||||
hash = Sha1.ComputeHash(Encoding.Default.GetBytes(target));
|
||||
return Sha1.ComputeHash(Encoding.Default.GetBytes(target));
|
||||
}
|
||||
return BitConverter.ToInt32(hash, 0) & 0x7fffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,18 +293,30 @@ namespace NzbDrone.Common.Disk
|
||||
var sid = new SecurityIdentifier(accountSid, null);
|
||||
|
||||
var directoryInfo = new DirectoryInfo(filename);
|
||||
var directorySecurity = directoryInfo.GetAccessControl();
|
||||
var directorySecurity = directoryInfo.GetAccessControl(AccessControlSections.Access);
|
||||
|
||||
var rules = directorySecurity.GetAccessRules(true, false, typeof(SecurityIdentifier));
|
||||
|
||||
if (rules.OfType<FileSystemAccessRule>().Any(acl => acl.AccessControlType == controlType && (acl.FileSystemRights & rights) == rights && acl.IdentityReference.Equals(sid)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var accessRule = new FileSystemAccessRule(sid, rights,
|
||||
InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
|
||||
PropagationFlags.None, controlType);
|
||||
PropagationFlags.InheritOnly, controlType);
|
||||
|
||||
directorySecurity.AddAccessRule(accessRule);
|
||||
directoryInfo.SetAccessControl(directorySecurity);
|
||||
bool modified;
|
||||
directorySecurity.ModifyAccessRule(AccessControlModification.Add, accessRule, out modified);
|
||||
|
||||
if (modified)
|
||||
{
|
||||
directoryInfo.SetAccessControl(directorySecurity);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.WarnException(string.Format("Couldn't set permission for {0}. account:{1} rights:{2} accessControlType:{3}", filename, accountSid, rights, controlType), e);
|
||||
Logger.Warn(e, "Couldn't set permission for {0}. account:{1} rights:{2} accessControlType:{3}", filename, accountSid, rights, controlType);
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -346,12 +358,12 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
public string[] GetFixedDrives()
|
||||
{
|
||||
return (DriveInfo.GetDrives().Where(x => x.DriveType == DriveType.Fixed).Select(x => x.Name)).ToArray();
|
||||
return GetMounts().Where(x => x.DriveType == DriveType.Fixed).Select(x => x.RootDirectory).ToArray();
|
||||
}
|
||||
|
||||
public string GetVolumeLabel(string path)
|
||||
{
|
||||
var driveInfo = DriveInfo.GetDrives().SingleOrDefault(d => d.Name == path);
|
||||
var driveInfo = GetMounts().SingleOrDefault(d => d.RootDirectory.PathEquals(path));
|
||||
|
||||
if (driveInfo == null)
|
||||
{
|
||||
@@ -376,11 +388,36 @@ namespace NzbDrone.Common.Disk
|
||||
return new FileStream(path, FileMode.Create);
|
||||
}
|
||||
|
||||
public List<DriveInfo> GetDrives()
|
||||
public virtual List<IMount> GetMounts()
|
||||
{
|
||||
return GetDriveInfoMounts();
|
||||
}
|
||||
|
||||
public virtual IMount GetMount(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var mounts = GetMounts();
|
||||
|
||||
return mounts.Where(drive => drive.RootDirectory.PathEquals(path) ||
|
||||
drive.RootDirectory.IsParentPath(path))
|
||||
.OrderByDescending(drive => drive.RootDirectory.Length)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Debug(ex, string.Format("Failed to get mount for path {0}", path));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected List<IMount> GetDriveInfoMounts()
|
||||
{
|
||||
return DriveInfo.GetDrives()
|
||||
.Where(d => d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network)
|
||||
.Where(d => d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network || d.DriveType == DriveType.Removable)
|
||||
.Where(d => d.IsReady)
|
||||
.Select(d => new DriveInfoMount(d))
|
||||
.Cast<IMount>()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
@@ -401,5 +438,19 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
return di.GetFiles().ToList();
|
||||
}
|
||||
|
||||
public void RemoveEmptySubfolders(string path)
|
||||
{
|
||||
var subfolders = GetDirectories(path);
|
||||
var files = GetFiles(path, SearchOption.AllDirectories);
|
||||
|
||||
foreach (var subfolder in subfolders)
|
||||
{
|
||||
if (files.None(f => subfolder.IsParentPath(f)))
|
||||
{
|
||||
DeleteFolder(subfolder, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace NzbDrone.Common.Disk
|
||||
{
|
||||
None,
|
||||
VerifyOnly,
|
||||
TryTransactional,
|
||||
Transactional
|
||||
}
|
||||
|
||||
@@ -40,19 +41,21 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
// TODO: Atm we haven't seen partial transfers on windows so we disable verified transfer.
|
||||
// (If enabled in the future, be sure to check specifically for ReFS, which doesn't support hardlinks.)
|
||||
VerificationMode = OsInfo.IsWindows ? DiskTransferVerificationMode.VerifyOnly : DiskTransferVerificationMode.Transactional;
|
||||
VerificationMode = OsInfo.IsWindows ? DiskTransferVerificationMode.VerifyOnly : DiskTransferVerificationMode.TryTransactional;
|
||||
}
|
||||
|
||||
public TransferMode TransferFolder(string sourcePath, string targetPath, TransferMode mode, bool verified = true)
|
||||
{
|
||||
var verificationMode = verified ? VerificationMode : DiskTransferVerificationMode.VerifyOnly;
|
||||
|
||||
return TransferFolder(sourcePath, targetPath, mode, verificationMode);
|
||||
}
|
||||
|
||||
public TransferMode TransferFolder(string sourcePath, string targetPath, TransferMode mode, DiskTransferVerificationMode verificationMode)
|
||||
{
|
||||
Ensure.That(sourcePath, () => sourcePath).IsValidPath();
|
||||
Ensure.That(targetPath, () => targetPath).IsValidPath();
|
||||
|
||||
if (VerificationMode != DiskTransferVerificationMode.Transactional)
|
||||
{
|
||||
verified = false;
|
||||
}
|
||||
|
||||
if (!_diskProvider.FolderExists(targetPath))
|
||||
{
|
||||
_diskProvider.CreateFolder(targetPath);
|
||||
@@ -62,14 +65,14 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
foreach (var subDir in _diskProvider.GetDirectoryInfos(sourcePath))
|
||||
{
|
||||
result &= TransferFolder(subDir.FullName, Path.Combine(targetPath, subDir.Name), mode, verified);
|
||||
result &= TransferFolder(subDir.FullName, Path.Combine(targetPath, subDir.Name), mode, verificationMode);
|
||||
}
|
||||
|
||||
foreach (var sourceFile in _diskProvider.GetFileInfos(sourcePath))
|
||||
{
|
||||
var destFile = Path.Combine(targetPath, sourceFile.Name);
|
||||
|
||||
result &= TransferFile(sourceFile.FullName, destFile, mode, true, verified);
|
||||
result &= TransferFile(sourceFile.FullName, destFile, mode, true, verificationMode);
|
||||
}
|
||||
|
||||
if (mode.HasFlag(TransferMode.Move))
|
||||
@@ -81,16 +84,20 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
|
||||
public TransferMode TransferFile(string sourcePath, string targetPath, TransferMode mode, bool overwrite = false, bool verified = true)
|
||||
{
|
||||
var verificationMode = verified ? VerificationMode : DiskTransferVerificationMode.None;
|
||||
|
||||
return TransferFile(sourcePath, targetPath, mode, overwrite, verificationMode);
|
||||
}
|
||||
|
||||
public TransferMode TransferFile(string sourcePath, string targetPath, TransferMode mode, bool overwrite, DiskTransferVerificationMode verificationMode)
|
||||
{
|
||||
Ensure.That(sourcePath, () => sourcePath).IsValidPath();
|
||||
Ensure.That(targetPath, () => targetPath).IsValidPath();
|
||||
|
||||
if (VerificationMode != DiskTransferVerificationMode.Transactional)
|
||||
{
|
||||
verified = false;
|
||||
}
|
||||
|
||||
_logger.Debug("{0} [{1}] > [{2}]", mode, sourcePath, targetPath);
|
||||
|
||||
var originalSize = _diskProvider.GetFileSize(sourcePath);
|
||||
|
||||
if (sourcePath == targetPath)
|
||||
{
|
||||
@@ -127,6 +134,15 @@ namespace NzbDrone.Common.Disk
|
||||
return TransferMode.None;
|
||||
}
|
||||
|
||||
if (sourcePath.GetParentPath() == targetPath.GetParentPath())
|
||||
{
|
||||
if (mode.HasFlag(TransferMode.Move))
|
||||
{
|
||||
TryMoveFileVerified(sourcePath, targetPath, originalSize);
|
||||
return TransferMode.Move;
|
||||
}
|
||||
}
|
||||
|
||||
if (sourcePath.IsParentPath(targetPath))
|
||||
{
|
||||
throw new IOException(string.Format("Destination cannot be a child of the source [{0}] => [{1}]", sourcePath, targetPath));
|
||||
@@ -147,81 +163,59 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
}
|
||||
|
||||
if (verified)
|
||||
// We force a transactional transfer if the transfer occurs between mounts and one of the mounts is cifs, it would be a copy anyway.
|
||||
if (verificationMode == DiskTransferVerificationMode.TryTransactional && OsInfo.IsNotWindows)
|
||||
{
|
||||
if (mode.HasFlag(TransferMode.Copy))
|
||||
var sourceMount = _diskProvider.GetMount(sourcePath);
|
||||
var targetMount = _diskProvider.GetMount(targetPath);
|
||||
|
||||
if (sourceMount != null && targetMount != null && sourceMount.RootDirectory != targetMount.RootDirectory &&
|
||||
(sourceMount.DriveFormat == "cifs" || targetMount.DriveFormat == "cifs"))
|
||||
{
|
||||
if (TryCopyFile(sourcePath, targetPath))
|
||||
verificationMode = DiskTransferVerificationMode.Transactional;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode.HasFlag(TransferMode.Copy))
|
||||
{
|
||||
if (verificationMode == DiskTransferVerificationMode.Transactional || verificationMode == DiskTransferVerificationMode.TryTransactional)
|
||||
{
|
||||
if (TryCopyFileTransactional(sourcePath, targetPath, originalSize))
|
||||
{
|
||||
return TransferMode.Copy;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode.HasFlag(TransferMode.Move))
|
||||
throw new IOException(string.Format("Failed to completely transfer [{0}] to [{1}], aborting.", sourcePath, targetPath));
|
||||
}
|
||||
else if (verificationMode == DiskTransferVerificationMode.VerifyOnly)
|
||||
{
|
||||
if (TryMoveFile(sourcePath, targetPath))
|
||||
{
|
||||
return TransferMode.Move;
|
||||
}
|
||||
TryCopyFileVerified(sourcePath, targetPath, originalSize);
|
||||
return TransferMode.Copy;
|
||||
}
|
||||
|
||||
throw new IOException(string.Format("Failed to completely transfer [{0}] to [{1}], aborting.", sourcePath, targetPath));
|
||||
}
|
||||
else if (VerificationMode == DiskTransferVerificationMode.VerifyOnly)
|
||||
{
|
||||
var originalSize = _diskProvider.GetFileSize(sourcePath);
|
||||
|
||||
if (mode.HasFlag(TransferMode.Copy))
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.CopyFile(sourcePath, targetPath);
|
||||
|
||||
var targetSize = _diskProvider.GetFileSize(targetPath);
|
||||
if (targetSize != originalSize)
|
||||
{
|
||||
throw new IOException(string.Format("File copy incomplete. [{0}] was {1} bytes long instead of {2} bytes.", targetPath, targetSize, originalSize));
|
||||
}
|
||||
|
||||
return TransferMode.Copy;
|
||||
}
|
||||
catch
|
||||
{
|
||||
RollbackCopy(sourcePath, targetPath);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode.HasFlag(TransferMode.Move))
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.MoveFile(sourcePath, targetPath);
|
||||
|
||||
var targetSize = _diskProvider.GetFileSize(targetPath);
|
||||
if (targetSize != originalSize)
|
||||
{
|
||||
throw new IOException(string.Format("File copy incomplete, data loss may have occured. [{0}] was {1} bytes long instead of the expected {2}.", targetPath, targetSize, originalSize));
|
||||
}
|
||||
|
||||
return TransferMode.Move;
|
||||
}
|
||||
catch
|
||||
{
|
||||
RollbackPartialMove(sourcePath, targetPath);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode.HasFlag(TransferMode.Copy))
|
||||
else
|
||||
{
|
||||
_diskProvider.CopyFile(sourcePath, targetPath);
|
||||
return TransferMode.Copy;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode.HasFlag(TransferMode.Move))
|
||||
if (mode.HasFlag(TransferMode.Move))
|
||||
{
|
||||
if (verificationMode == DiskTransferVerificationMode.Transactional || verificationMode == DiskTransferVerificationMode.TryTransactional)
|
||||
{
|
||||
if (TryMoveFileTransactional(sourcePath, targetPath, originalSize, verificationMode))
|
||||
{
|
||||
return TransferMode.Move;
|
||||
}
|
||||
|
||||
throw new IOException(string.Format("Failed to completely transfer [{0}] to [{1}], aborting.", sourcePath, targetPath));
|
||||
}
|
||||
else if (verificationMode == DiskTransferVerificationMode.VerifyOnly)
|
||||
{
|
||||
TryMoveFileVerified(sourcePath, targetPath, originalSize);
|
||||
return TransferMode.Move;
|
||||
}
|
||||
else
|
||||
{
|
||||
_diskProvider.MoveFile(sourcePath, targetPath);
|
||||
return TransferMode.Move;
|
||||
@@ -265,7 +259,7 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException(string.Format("Failed to properly rollback the file move [{0}] to [{1}], incomplete file may be left in target path.", sourcePath, targetPath), ex);
|
||||
_logger.Error(ex, string.Format("Failed to properly rollback the file move [{0}] to [{1}], incomplete file may be left in target path.", sourcePath, targetPath));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +275,7 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException(string.Format("Failed to properly rollback the file move [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath), ex);
|
||||
_logger.Error(ex, string.Format("Failed to properly rollback the file move [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +294,7 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException(string.Format("Failed to properly rollback the file copy [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath), ex);
|
||||
_logger.Error(ex, string.Format("Failed to properly rollback the file copy [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,10 +304,8 @@ namespace NzbDrone.Common.Disk
|
||||
Thread.Sleep(3000);
|
||||
}
|
||||
|
||||
private bool TryCopyFile(string sourcePath, string targetPath)
|
||||
private bool TryCopyFileTransactional(string sourcePath, string targetPath, long originalSize)
|
||||
{
|
||||
var originalSize = _diskProvider.GetFileSize(sourcePath);
|
||||
|
||||
var tempTargetPath = targetPath + ".partial~";
|
||||
|
||||
if (_diskProvider.FileExists(tempTargetPath))
|
||||
@@ -367,10 +359,8 @@ namespace NzbDrone.Common.Disk
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryMoveFile(string sourcePath, string targetPath)
|
||||
private bool TryMoveFileTransactional(string sourcePath, string targetPath, long originalSize, DiskTransferVerificationMode verificationMode)
|
||||
{
|
||||
var originalSize = _diskProvider.GetFileSize(sourcePath);
|
||||
|
||||
var backupPath = sourcePath + ".backup~";
|
||||
var tempTargetPath = targetPath + ".partial~";
|
||||
|
||||
@@ -423,16 +413,63 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Trace("Hardlink move failed, reverting to copy.");
|
||||
if (TryCopyFile(sourcePath, targetPath))
|
||||
if (verificationMode == DiskTransferVerificationMode.Transactional)
|
||||
{
|
||||
_logger.Trace("Copy succeeded, deleting source.");
|
||||
_diskProvider.DeleteFile(sourcePath);
|
||||
_logger.Trace("Hardlink move failed, reverting to copy.");
|
||||
if (TryCopyFileTransactional(sourcePath, targetPath, originalSize))
|
||||
{
|
||||
_logger.Trace("Copy succeeded, deleting source.");
|
||||
_diskProvider.DeleteFile(sourcePath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Trace("Hardlink move failed, reverting to move.");
|
||||
TryMoveFileVerified(sourcePath, targetPath, originalSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
_logger.Trace("Copy failed.");
|
||||
_logger.Trace("Move failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
private void TryCopyFileVerified(string sourcePath, string targetPath, long originalSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.CopyFile(sourcePath, targetPath);
|
||||
|
||||
var targetSize = _diskProvider.GetFileSize(targetPath);
|
||||
if (targetSize != originalSize)
|
||||
{
|
||||
throw new IOException(string.Format("File copy incomplete. [{0}] was {1} bytes long instead of {2} bytes.", targetPath, targetSize, originalSize));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
RollbackCopy(sourcePath, targetPath);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void TryMoveFileVerified(string sourcePath, string targetPath, long originalSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.MoveFile(sourcePath, targetPath);
|
||||
|
||||
var targetSize = _diskProvider.GetFileSize(targetPath);
|
||||
if (targetSize != originalSize)
|
||||
{
|
||||
throw new IOException(string.Format("File move incomplete, data loss may have occurred. [{0}] was {1} bytes long instead of the expected {2}.", targetPath, targetSize, originalSize));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
RollbackPartialMove(sourcePath, targetPath);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
76
src/NzbDrone.Common/Disk/DriveInfoMount.cs
Normal file
76
src/NzbDrone.Common/Disk/DriveInfoMount.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Common.Disk
|
||||
{
|
||||
public class DriveInfoMount : IMount
|
||||
{
|
||||
private readonly DriveInfo _driveInfo;
|
||||
|
||||
public DriveInfoMount(DriveInfo driveInfo)
|
||||
{
|
||||
_driveInfo = driveInfo;
|
||||
}
|
||||
|
||||
public long AvailableFreeSpace
|
||||
{
|
||||
get { return _driveInfo.AvailableFreeSpace; }
|
||||
}
|
||||
|
||||
public string DriveFormat
|
||||
{
|
||||
get { return _driveInfo.DriveFormat; }
|
||||
}
|
||||
|
||||
public DriveType DriveType
|
||||
{
|
||||
get { return _driveInfo.DriveType; }
|
||||
}
|
||||
|
||||
public bool IsReady
|
||||
{
|
||||
get { return _driveInfo.IsReady; }
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _driveInfo.Name; }
|
||||
}
|
||||
|
||||
public string RootDirectory
|
||||
{
|
||||
get { return _driveInfo.RootDirectory.FullName; }
|
||||
}
|
||||
|
||||
public long TotalFreeSpace
|
||||
{
|
||||
get { return _driveInfo.TotalFreeSpace; }
|
||||
}
|
||||
|
||||
public long TotalSize
|
||||
{
|
||||
get { return _driveInfo.TotalSize; }
|
||||
}
|
||||
|
||||
public string VolumeLabel
|
||||
{
|
||||
get { return _driveInfo.VolumeLabel; }
|
||||
}
|
||||
|
||||
public string VolumeName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (VolumeLabel.IsNullOrWhiteSpace())
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
return string.Format("{0} ({1})", Name, VolumeLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
private readonly HashSet<string> _setToRemove = new HashSet<string>
|
||||
{
|
||||
//Windows
|
||||
// Windows
|
||||
"boot",
|
||||
"bootmgr",
|
||||
"cache",
|
||||
@@ -32,14 +32,20 @@ namespace NzbDrone.Common.Disk
|
||||
"temporary internet files",
|
||||
"windows",
|
||||
|
||||
//OS X
|
||||
// OS X
|
||||
".fseventd",
|
||||
".spotlight",
|
||||
".trashes",
|
||||
".vol",
|
||||
"cachedmessages",
|
||||
"caches",
|
||||
"trash"
|
||||
"trash",
|
||||
|
||||
// QNAP
|
||||
".@__thumb",
|
||||
|
||||
// Synology
|
||||
"@eadir"
|
||||
};
|
||||
|
||||
public FileSystemLookupService(IDiskProvider diskProvider, Logger logger)
|
||||
@@ -103,12 +109,12 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
private List<FileSystemModel> GetDrives()
|
||||
{
|
||||
return _diskProvider.GetDrives()
|
||||
return _diskProvider.GetMounts()
|
||||
.Select(d => new FileSystemModel
|
||||
{
|
||||
Type = FileSystemEntityType.Drive,
|
||||
Name = GetVolumeName(d),
|
||||
Path = d.Name,
|
||||
Name = d.VolumeLabel,
|
||||
Path = d.RootDirectory,
|
||||
LastModified = null
|
||||
})
|
||||
.ToList();
|
||||
@@ -157,16 +163,6 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private string GetVolumeName(DriveInfo driveInfo)
|
||||
{
|
||||
if (driveInfo.VolumeLabel.IsNullOrWhiteSpace())
|
||||
{
|
||||
return driveInfo.Name;
|
||||
}
|
||||
|
||||
return string.Format("{0} ({1})", driveInfo.Name, driveInfo.VolumeLabel);
|
||||
}
|
||||
|
||||
private string GetParent(string path)
|
||||
{
|
||||
|
||||
@@ -40,12 +40,13 @@ namespace NzbDrone.Common.Disk
|
||||
void SetPermissions(string filename, WellKnownSidType accountSid, FileSystemRights rights, AccessControlType controlType);
|
||||
FileAttributes GetFileAttributes(string path);
|
||||
void EmptyFolder(string path);
|
||||
string[] GetFixedDrives();
|
||||
string GetVolumeLabel(string path);
|
||||
FileStream OpenReadStream(string path);
|
||||
FileStream OpenWriteStream(string path);
|
||||
List<DriveInfo> GetDrives();
|
||||
List<IMount> GetMounts();
|
||||
IMount GetMount(string path);
|
||||
List<DirectoryInfo> GetDirectoryInfos(string path);
|
||||
List<FileInfo> GetFileInfos(string path);
|
||||
void RemoveEmptySubfolders(string path);
|
||||
}
|
||||
}
|
||||
|
||||
17
src/NzbDrone.Common/Disk/IMount.cs
Normal file
17
src/NzbDrone.Common/Disk/IMount.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NzbDrone.Common.Disk
|
||||
{
|
||||
public interface IMount
|
||||
{
|
||||
long AvailableFreeSpace { get; }
|
||||
string DriveFormat { get; }
|
||||
DriveType DriveType { get; }
|
||||
bool IsReady { get; }
|
||||
string Name { get; }
|
||||
string RootDirectory { get; }
|
||||
long TotalFreeSpace { get; }
|
||||
long TotalSize { get; }
|
||||
string VolumeLabel { get; }
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace NzbDrone.Common.Disk
|
||||
{
|
||||
return OsPathKind.Unix;
|
||||
}
|
||||
if (path.Contains(':') || path.Contains('\\'))
|
||||
if (HasWindowsDriveLetter(path) || path.Contains('\\'))
|
||||
{
|
||||
return OsPathKind.Windows;
|
||||
}
|
||||
@@ -55,6 +55,15 @@ namespace NzbDrone.Common.Disk
|
||||
return OsPathKind.Unknown;
|
||||
}
|
||||
|
||||
private static bool HasWindowsDriveLetter(string path)
|
||||
{
|
||||
if (path.Length < 2) return false;
|
||||
if (!char.IsLetter(path[0]) || path[1] != ':') return false;
|
||||
if (path.Length > 2 && path[2] != '\\' && path[2] != '/') return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string FixSlashes(string path, OsPathKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
@@ -97,7 +106,7 @@ namespace NzbDrone.Common.Disk
|
||||
{
|
||||
if (IsWindowsPath)
|
||||
{
|
||||
return _path.StartsWith(@"\\") || _path.Contains(':');
|
||||
return _path.StartsWith(@"\\") || HasWindowsDriveLetter(_path);
|
||||
}
|
||||
if (IsUnixPath)
|
||||
{
|
||||
@@ -153,6 +162,14 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return _path.IsPathValid();
|
||||
}
|
||||
}
|
||||
|
||||
private int GetFileNameIndex()
|
||||
{
|
||||
if (_path.Length < 2)
|
||||
|
||||
@@ -39,11 +39,11 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.SetPermissions(_appFolderInfo.AppDataFolder, WellKnownSidType.WorldSid, FileSystemRights.FullControl, AccessControlType.Allow);
|
||||
_diskProvider.SetPermissions(_appFolderInfo.AppDataFolder, WellKnownSidType.WorldSid, FileSystemRights.Modify, AccessControlType.Allow);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.WarnException("Coudn't set app folder permission", ex);
|
||||
_logger.Warn(ex, "Coudn't set app folder permission");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.WarnException("Error checking if the current user is an administrator.", ex);
|
||||
_logger.Warn(ex, "Error checking if the current user is an administrator.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
public static void Add<TKey, TValue>(this ICollection<KeyValuePair<TKey, TValue>> collection, TKey key, TValue value)
|
||||
{
|
||||
collection.Add(key, value);
|
||||
collection.Add(new KeyValuePair<TKey, TValue>(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,14 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
public static bool IsParentPath(this string parentPath, string childPath)
|
||||
{
|
||||
parentPath = parentPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
childPath = childPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
if (parentPath != "/")
|
||||
{
|
||||
parentPath = parentPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
}
|
||||
if (childPath != "/")
|
||||
{
|
||||
childPath = childPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
var parent = new DirectoryInfo(parentPath);
|
||||
var child = new DirectoryInfo(childPath);
|
||||
|
||||
@@ -92,5 +92,30 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
return "\"" + text + "\"";
|
||||
}
|
||||
|
||||
public static byte[] HexToByteArray(this string input)
|
||||
{
|
||||
return Enumerable.Range(0, input.Length)
|
||||
.Where(x => x%2 == 0)
|
||||
.Select(x => Convert.ToByte(input.Substring(x, 2), 16))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static string ToHexString(this byte[] input)
|
||||
{
|
||||
return string.Concat(Array.ConvertAll(input, x => x.ToString("X2")));
|
||||
}
|
||||
|
||||
public static string FromOctalString(this string octalValue)
|
||||
{
|
||||
octalValue = octalValue.TrimStart('\\');
|
||||
|
||||
var first = int.Parse(octalValue.Substring(0, 1));
|
||||
var second = int.Parse(octalValue.Substring(1, 1));
|
||||
var third = int.Parse(octalValue.Substring(2, 1));
|
||||
var byteResult = (byte)((first << 6) | (second << 3) | (third));
|
||||
|
||||
return Encoding.ASCII.GetString(new [] { byteResult });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -10,7 +12,7 @@ using CurlSharp;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
using NzbDrone.Common.Http.Proxy;
|
||||
|
||||
namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
@@ -18,9 +20,31 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
private static readonly Regex ExpiryDate = new Regex(@"(expires=)([^;]+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Logger _logger = NzbDroneLogger.GetLogger(typeof(CurlHttpDispatcher));
|
||||
private readonly IHttpProxySettingsProvider _proxySettingsProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public static bool CheckAvailability()
|
||||
private const string _caBundleFileName = "curl-ca-bundle.crt";
|
||||
private static readonly string _caBundleFilePath;
|
||||
|
||||
static CurlHttpDispatcher()
|
||||
{
|
||||
if (Assembly.GetExecutingAssembly().Location.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_caBundleFilePath = Path.Combine(Assembly.GetExecutingAssembly().Location, "..", _caBundleFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_caBundleFilePath = _caBundleFileName;
|
||||
}
|
||||
}
|
||||
|
||||
public CurlHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, Logger logger)
|
||||
{
|
||||
_proxySettingsProvider = proxySettingsProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool CheckAvailability()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -28,7 +52,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.TraceException("Initializing curl failed", ex);
|
||||
_logger.Trace(ex, "Initializing curl failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -40,11 +64,6 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
throw new ApplicationException("Curl failed to initialize.");
|
||||
}
|
||||
|
||||
if (request.NetworkCredential != null)
|
||||
{
|
||||
throw new NotImplementedException("Credentials not supported for curl dispatcher.");
|
||||
}
|
||||
|
||||
lock (CurlGlobalHandle.Instance)
|
||||
{
|
||||
Stream responseStream = new MemoryStream();
|
||||
@@ -63,8 +82,11 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
headerStream.Write(b, 0, s * n);
|
||||
return s * n;
|
||||
};
|
||||
|
||||
curlEasy.Url = request.Url.AbsoluteUri;
|
||||
|
||||
AddProxy(curlEasy, request);
|
||||
|
||||
curlEasy.Url = request.Url.FullUri;
|
||||
|
||||
switch (request.Method)
|
||||
{
|
||||
case HttpMethod.GET:
|
||||
@@ -85,21 +107,25 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
curlEasy.UserAgent = UserAgentBuilder.UserAgent;
|
||||
curlEasy.FollowLocation = request.AllowAutoRedirect;
|
||||
|
||||
if (request.RequestTimeout != TimeSpan.Zero)
|
||||
{
|
||||
curlEasy.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalSeconds);
|
||||
}
|
||||
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
curlEasy.CaInfo = "curl-ca-bundle.crt";
|
||||
curlEasy.CaInfo = _caBundleFilePath;
|
||||
}
|
||||
|
||||
if (cookies != null)
|
||||
{
|
||||
curlEasy.Cookie = cookies.GetCookieHeader(request.Url);
|
||||
curlEasy.Cookie = cookies.GetCookieHeader((Uri)request.Url);
|
||||
}
|
||||
|
||||
if (!request.Body.IsNullOrWhiteSpace())
|
||||
if (request.ContentData != null)
|
||||
{
|
||||
// TODO: This might not go well with encoding.
|
||||
curlEasy.PostFieldSize = request.Body.Length;
|
||||
curlEasy.SetOpt(CurlOption.CopyPostFields, request.Body);
|
||||
curlEasy.PostFieldSize = request.ContentData.Length;
|
||||
curlEasy.SetOpt(CurlOption.CopyPostFields, new string(Array.ConvertAll(request.ContentData, v => (char)v)));
|
||||
}
|
||||
|
||||
// Yes, we have to keep a ref to the object to prevent corrupting the unmanaged state
|
||||
@@ -111,7 +137,15 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
if (result != CurlCode.Ok)
|
||||
{
|
||||
throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url));
|
||||
switch (result)
|
||||
{
|
||||
case CurlCode.SslCaCert:
|
||||
case (CurlCode)77:
|
||||
throw new WebException(string.Format("Curl Error {0} for Url {1}, issues with your operating system SSL Root Certificate Bundle (ca-bundle).", result, curlEasy.Url));
|
||||
default:
|
||||
throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +159,34 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
}
|
||||
}
|
||||
|
||||
private void AddProxy(CurlEasy curlEasy, HttpRequest request)
|
||||
{
|
||||
var proxySettings = _proxySettingsProvider.GetProxySettings(request);
|
||||
if (proxySettings != null)
|
||||
|
||||
{
|
||||
switch (proxySettings.Type)
|
||||
{
|
||||
case ProxyType.Http:
|
||||
curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Http);
|
||||
curlEasy.SetOpt(CurlOption.ProxyAuth, CurlHttpAuth.Basic);
|
||||
curlEasy.SetOpt(CurlOption.ProxyUserPwd, proxySettings.Username + ":" + proxySettings.Password.ToString());
|
||||
break;
|
||||
case ProxyType.Socks4:
|
||||
curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Socks4);
|
||||
curlEasy.SetOpt(CurlOption.ProxyUsername, proxySettings.Username);
|
||||
curlEasy.SetOpt(CurlOption.ProxyPassword, proxySettings.Password);
|
||||
break;
|
||||
case ProxyType.Socks5:
|
||||
curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Socks5);
|
||||
curlEasy.SetOpt(CurlOption.ProxyUsername, proxySettings.Username);
|
||||
curlEasy.SetOpt(CurlOption.ProxyPassword, proxySettings.Password);
|
||||
break;
|
||||
}
|
||||
curlEasy.SetOpt(CurlOption.Proxy, proxySettings.Host + ":" + proxySettings.Port.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private CurlSlist SerializeHeaders(HttpRequest request)
|
||||
{
|
||||
if (!request.Headers.ContainsKey("Accept-Encoding"))
|
||||
@@ -164,7 +226,14 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
var setCookie = webHeaderCollection.Get("Set-Cookie");
|
||||
if (setCookie != null && setCookie.Length > 0 && cookies != null)
|
||||
{
|
||||
cookies.SetCookies(request.Url, FixSetCookieHeader(setCookie));
|
||||
try
|
||||
{
|
||||
cookies.SetCookies((Uri)request.Url, FixSetCookieHeader(setCookie));
|
||||
}
|
||||
catch (CookieException ex)
|
||||
{
|
||||
_logger.Debug("Rejected cookie {0}: {1}", ex.InnerException.Message, setCookie);
|
||||
}
|
||||
}
|
||||
|
||||
return webHeaderCollection;
|
||||
@@ -175,9 +244,15 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
// fix up the date if it was malformed
|
||||
var setCookieClean = ExpiryDate.Replace(setCookie, delegate(Match match)
|
||||
{
|
||||
string format = "ddd, dd-MMM-yyyy HH:mm:ss";
|
||||
DateTime dt = Convert.ToDateTime(match.Groups[2].Value);
|
||||
return match.Groups[1].Value + dt.ToUniversalTime().ToString(format) + " GMT";
|
||||
string shortFormat = "ddd, dd-MMM-yy HH:mm:ss";
|
||||
string longFormat = "ddd, dd-MMM-yyyy HH:mm:ss";
|
||||
DateTime dt;
|
||||
if (DateTime.TryParseExact(match.Groups[2].Value, longFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt) ||
|
||||
DateTime.TryParseExact(match.Groups[2].Value, shortFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt) ||
|
||||
DateTime.TryParse(match.Groups[2].Value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt))
|
||||
return match.Groups[1].Value + dt.ToUniversalTime().ToString(longFormat, CultureInfo.InvariantCulture) + " GMT";
|
||||
else
|
||||
return match.Value;
|
||||
});
|
||||
return setCookieClean;
|
||||
}
|
||||
|
||||
@@ -8,17 +8,18 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
public class FallbackHttpDispatcher : IHttpDispatcher
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly ICached<bool> _curlTLSFallbackCache;
|
||||
private readonly ManagedHttpDispatcher _managedDispatcher;
|
||||
private readonly CurlHttpDispatcher _curlDispatcher;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public FallbackHttpDispatcher(ICached<bool> curlTLSFallbackCache, Logger logger)
|
||||
private readonly ICached<bool> _curlTLSFallbackCache;
|
||||
|
||||
public FallbackHttpDispatcher(ManagedHttpDispatcher managedDispatcher, CurlHttpDispatcher curlDispatcher, ICacheManager cacheManager, Logger logger)
|
||||
{
|
||||
_managedDispatcher = managedDispatcher;
|
||||
_curlDispatcher = curlDispatcher;
|
||||
_curlTLSFallbackCache = cacheManager.GetCache<bool>(GetType(), "curlTLSFallback");
|
||||
_logger = logger;
|
||||
_curlTLSFallbackCache = curlTLSFallbackCache;
|
||||
_managedDispatcher = new ManagedHttpDispatcher();
|
||||
_curlDispatcher = new CurlHttpDispatcher();
|
||||
}
|
||||
|
||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||
@@ -46,7 +47,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
}
|
||||
}
|
||||
|
||||
if (CurlHttpDispatcher.CheckAvailability())
|
||||
if (_curlDispatcher.CheckAvailability())
|
||||
{
|
||||
return _curlDispatcher.GetResponse(request, cookies);
|
||||
}
|
||||
|
||||
@@ -1,41 +1,54 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http.Proxy;
|
||||
|
||||
namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
public class ManagedHttpDispatcher : IHttpDispatcher
|
||||
{
|
||||
private readonly IHttpProxySettingsProvider _proxySettingsProvider;
|
||||
private readonly ICreateManagedWebProxy _createManagedWebProxy;
|
||||
|
||||
public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, ICreateManagedWebProxy createManagedWebProxy)
|
||||
{
|
||||
_proxySettingsProvider = proxySettingsProvider;
|
||||
_createManagedWebProxy = createManagedWebProxy;
|
||||
}
|
||||
|
||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||
{
|
||||
var webRequest = (HttpWebRequest)WebRequest.Create(request.Url);
|
||||
var webRequest = (HttpWebRequest)WebRequest.Create((Uri)request.Url);
|
||||
|
||||
// Deflate is not a standard and could break depending on implementation.
|
||||
// we should just stick with the more compatible Gzip
|
||||
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
|
||||
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
|
||||
webRequest.Credentials = request.NetworkCredential;
|
||||
webRequest.Method = request.Method.ToString();
|
||||
webRequest.UserAgent = UserAgentBuilder.UserAgent;
|
||||
webRequest.KeepAlive = false;
|
||||
webRequest.KeepAlive = request.ConnectionKeepAlive;
|
||||
webRequest.AllowAutoRedirect = request.AllowAutoRedirect;
|
||||
webRequest.ContentLength = 0;
|
||||
webRequest.CookieContainer = cookies;
|
||||
|
||||
if (request.RequestTimeout != TimeSpan.Zero)
|
||||
{
|
||||
webRequest.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalMilliseconds);
|
||||
}
|
||||
|
||||
AddProxy(webRequest, request);
|
||||
|
||||
if (request.Headers != null)
|
||||
{
|
||||
AddRequestHeaders(webRequest, request.Headers);
|
||||
}
|
||||
|
||||
if (!request.Body.IsNullOrWhiteSpace())
|
||||
if (request.ContentData != null)
|
||||
{
|
||||
var bytes = request.Headers.GetEncodingFromContentType().GetBytes(request.Body.ToCharArray());
|
||||
|
||||
webRequest.ContentLength = bytes.Length;
|
||||
webRequest.ContentLength = request.ContentData.Length;
|
||||
using (var writeStream = webRequest.GetRequestStream())
|
||||
{
|
||||
writeStream.Write(bytes, 0, bytes.Length);
|
||||
writeStream.Write(request.ContentData, 0, request.ContentData.Length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +81,15 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
return new HttpResponse(request, new HttpHeader(httpWebResponse.Headers), data, httpWebResponse.StatusCode);
|
||||
}
|
||||
|
||||
protected virtual void AddProxy(HttpWebRequest webRequest, HttpRequest request)
|
||||
{
|
||||
var proxySettings = _proxySettingsProvider.GetProxySettings(request);
|
||||
if (proxySettings != null)
|
||||
{
|
||||
webRequest.Proxy = _createManagedWebProxy.GetWebProxy(proxySettings);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void AddRequestHeaders(HttpWebRequest webRequest, HttpHeader headers)
|
||||
{
|
||||
foreach (var header in headers)
|
||||
@@ -75,45 +97,43 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
switch (header.Key)
|
||||
{
|
||||
case "Accept":
|
||||
webRequest.Accept = header.Value.ToString();
|
||||
webRequest.Accept = header.Value;
|
||||
break;
|
||||
case "Connection":
|
||||
webRequest.Connection = header.Value.ToString();
|
||||
webRequest.Connection = header.Value;
|
||||
break;
|
||||
case "Content-Length":
|
||||
webRequest.ContentLength = Convert.ToInt64(header.Value);
|
||||
break;
|
||||
case "Content-Type":
|
||||
webRequest.ContentType = header.Value.ToString();
|
||||
webRequest.ContentType = header.Value;
|
||||
break;
|
||||
case "Date":
|
||||
webRequest.Date = (DateTime)header.Value;
|
||||
webRequest.Date = HttpHeader.ParseDateTime(header.Value);
|
||||
break;
|
||||
case "Expect":
|
||||
webRequest.Expect = header.Value.ToString();
|
||||
webRequest.Expect = header.Value;
|
||||
break;
|
||||
case "Host":
|
||||
webRequest.Host = header.Value.ToString();
|
||||
webRequest.Host = header.Value;
|
||||
break;
|
||||
case "If-Modified-Since":
|
||||
webRequest.IfModifiedSince = (DateTime)header.Value;
|
||||
webRequest.IfModifiedSince = HttpHeader.ParseDateTime(header.Value);
|
||||
break;
|
||||
case "Range":
|
||||
throw new NotImplementedException();
|
||||
break;
|
||||
case "Referer":
|
||||
webRequest.Referer = header.Value.ToString();
|
||||
webRequest.Referer = header.Value;
|
||||
break;
|
||||
case "Transfer-Encoding":
|
||||
webRequest.TransferEncoding = header.Value.ToString();
|
||||
webRequest.TransferEncoding = header.Value;
|
||||
break;
|
||||
case "User-Agent":
|
||||
throw new NotSupportedException("User-Agent other than Sonarr not allowed.");
|
||||
case "Proxy-Connection":
|
||||
throw new NotImplementedException();
|
||||
break;
|
||||
default:
|
||||
webRequest.Headers.Add(header.Key, header.Value.ToString());
|
||||
webRequest.Headers.Add(header.Key, header.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
public sealed class HttpAccept
|
||||
{
|
||||
public static readonly HttpAccept Rss = new HttpAccept("application/rss+xml, text/rss+xml, text/xml");
|
||||
public static readonly HttpAccept Rss = new HttpAccept("application/rss+xml, text/rss+xml, application/xml, text/xml");
|
||||
public static readonly HttpAccept Json = new HttpAccept("application/json");
|
||||
public static readonly HttpAccept Html = new HttpAccept("text/html");
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http.Dispatchers;
|
||||
using NzbDrone.Common.TPL;
|
||||
|
||||
@@ -28,27 +30,20 @@ namespace NzbDrone.Common.Http
|
||||
private readonly Logger _logger;
|
||||
private readonly IRateLimitService _rateLimitService;
|
||||
private readonly ICached<CookieContainer> _cookieContainerCache;
|
||||
private readonly ICached<bool> _curlTLSFallbackCache;
|
||||
private readonly List<IHttpRequestInterceptor> _requestInterceptors;
|
||||
private readonly IHttpDispatcher _httpDispatcher;
|
||||
|
||||
public HttpClient(IEnumerable<IHttpRequestInterceptor> requestInterceptors, ICacheManager cacheManager, IRateLimitService rateLimitService, IHttpDispatcher httpDispatcher, Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_rateLimitService = rateLimitService;
|
||||
_requestInterceptors = requestInterceptors.ToList();
|
||||
ServicePointManager.DefaultConnectionLimit = 12;
|
||||
_rateLimitService = rateLimitService;
|
||||
_httpDispatcher = httpDispatcher;
|
||||
_logger = logger;
|
||||
|
||||
ServicePointManager.DefaultConnectionLimit = 12;
|
||||
_cookieContainerCache = cacheManager.GetCache<CookieContainer>(typeof(HttpClient));
|
||||
}
|
||||
|
||||
public HttpClient(IEnumerable<IHttpRequestInterceptor> requestInterceptors, ICacheManager cacheManager, IRateLimitService rateLimitService, Logger logger)
|
||||
: this(requestInterceptors, cacheManager, rateLimitService, null, logger)
|
||||
{
|
||||
_httpDispatcher = new FallbackHttpDispatcher(cacheManager.GetCache<bool>(typeof(HttpClient), "curlTLSFallback"), _logger);
|
||||
}
|
||||
|
||||
public HttpResponse Execute(HttpRequest request)
|
||||
{
|
||||
foreach (var interceptor in _requestInterceptors)
|
||||
@@ -73,13 +68,18 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
stopWatch.Stop();
|
||||
|
||||
_logger.Trace("{0} ({1:n0} ms)", response, stopWatch.ElapsedMilliseconds);
|
||||
_logger.Trace("{0} ({1} ms)", response, stopWatch.ElapsedMilliseconds);
|
||||
|
||||
foreach (var interceptor in _requestInterceptors)
|
||||
{
|
||||
response = interceptor.PostResponse(response);
|
||||
}
|
||||
|
||||
if (request.LogResponseContent)
|
||||
{
|
||||
_logger.Trace("Response content ({0} bytes): {1}", response.ResponseData.Length, response.Content);
|
||||
}
|
||||
|
||||
if (!RuntimeInfoBase.IsProduction &&
|
||||
(response.StatusCode == HttpStatusCode.Moved ||
|
||||
response.StatusCode == HttpStatusCode.MovedPermanently ||
|
||||
@@ -124,7 +124,7 @@ namespace NzbDrone.Common.Http
|
||||
}
|
||||
}
|
||||
|
||||
var requestCookies = persistentCookieContainer.GetCookies(request.Url);
|
||||
var requestCookies = persistentCookieContainer.GetCookies((Uri)request.Url);
|
||||
|
||||
var cookieContainer = new CookieContainer();
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
var persistentCookieContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
|
||||
|
||||
var cookies = cookieContainer.GetCookies(request.Url);
|
||||
var cookies = cookieContainer.GetCookies((Uri)request.Url);
|
||||
|
||||
persistentCookieContainer.Add(cookies);
|
||||
}
|
||||
@@ -177,7 +177,7 @@ namespace NzbDrone.Common.Http
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.WarnException("Failed to get response from: " + url, e);
|
||||
_logger.Warn(e, "Failed to get response from: " + url);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user