mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-18 21:35:51 -04:00
Compare commits
315 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d835c168d3 | |||
| 329786365d | |||
| 4f6380a73c | |||
| cde1217356 | |||
| 2eedfca78a | |||
| 0d65984991 | |||
| 2a932fe7e8 | |||
| 0e02171938 | |||
| 2a3b0304cb | |||
| 16e35f68bb | |||
| 74b1c846a5 | |||
| 1f930c18e4 | |||
| d9e60eff6b | |||
| 097982334c | |||
| be6e6b910e | |||
| 31b9ec1116 | |||
| ff6c3b70d3 | |||
| 0fd0b31a60 | |||
| 76e6ebc63c | |||
| 2ea35adb98 | |||
| 782f63f510 | |||
| c874122fc0 | |||
| 2efda4933d | |||
| b7c70d750a | |||
| 5ebfac6cc8 | |||
| 74ca6149e3 | |||
| 40d7590f80 | |||
| 2cb27240dc | |||
| 837c2683df | |||
| 0b278c7db8 | |||
| 0b765d10fe | |||
| 20dbdfb344 | |||
| 426448ed98 | |||
| 03b83ed226 | |||
| eafe79450e | |||
| f286dba40a | |||
| 448f579723 | |||
| 1562081235 | |||
| 0214c8e0f0 | |||
| f16dd069b5 | |||
| aba613acd1 | |||
| 2e36538dcd | |||
| c42e4d682c | |||
| 327536b684 | |||
| 7dbacf105d | |||
| 8d776abb48 | |||
| 7f8093de92 | |||
| c1de7f26d1 | |||
| 4a82d30d3d | |||
| da52e60f36 | |||
| cf58e52f40 | |||
| 2a7ae96906 | |||
| 053f6fcaeb | |||
| 0c75d0bb03 | |||
| 0c9b5dc97e | |||
| c99e92e6af | |||
| 3f64c01d5b | |||
| f022dae1fa | |||
| 52ad8cf37f | |||
| 3d20fd8f96 | |||
| cf662291d5 | |||
| 43d85bf59d | |||
| 4a149c356b | |||
| 740fc9154f | |||
| 0a657302f7 | |||
| 7b09b259a8 | |||
| b093be3f4e | |||
| 3c8b263694 | |||
| 43c5d03f9a | |||
| 9fbe06ad68 | |||
| db899a9bb8 | |||
| d3890bd712 | |||
| 1a61796092 | |||
| 1251e294cd | |||
| 0411b82e65 | |||
| 9519f3137c | |||
| f8d97cac7d | |||
| f2ecbe776b | |||
| 1ac442d0e6 | |||
| 5f2aeb0cea | |||
| 2ece05cd1e | |||
| 25a3f83ebc | |||
| cdce65a922 | |||
| 8f73a51522 | |||
| bc438a6a63 | |||
| 4167ffe11a | |||
| 6fb1aa85d0 | |||
| eb8ef6c337 | |||
| c076f1ddb1 | |||
| eeff79b288 | |||
| 697a62da0a | |||
| f1a289cc74 | |||
| c39a26d9e0 | |||
| f2ccf94835 | |||
| 19d625c6c5 | |||
| cd3b6000a0 | |||
| ff33f15bac | |||
| 50a0e9514e | |||
| 7ef1ca8a00 | |||
| e0d1e08f94 | |||
| 9fae76015a | |||
| 17bf438cad | |||
| c0b0567c23 | |||
| edc1e0b8d1 | |||
| cd79b42f5f | |||
| dc82e66dde | |||
| b034d0c1b3 | |||
| 36a3e86882 | |||
| e76fb8c90b | |||
| e6288148ad | |||
| bf8d68a873 | |||
| 080e2e9eff | |||
| e3310e590c | |||
| a0b4d3a38d | |||
| a72b856fb8 | |||
| 522ef9d8d5 | |||
| a486bff40b | |||
| 0de1f3f17a | |||
| 755fdce227 | |||
| cd8659e684 | |||
| a621f0d49b | |||
| 2e96c4e798 | |||
| 816cf608fc | |||
| 713e109bc9 | |||
| 4bf3ef45b0 | |||
| 09530b238f | |||
| 5414dadffc | |||
| 5482fa3ae0 | |||
| 6e8480d7cb | |||
| 7c7cfc0b7d | |||
| 4e051bfde2 | |||
| 356e14ac5b | |||
| 97d0ddb6e9 | |||
| 17f6841426 | |||
| a6a7732cd5 | |||
| aa37b65842 | |||
| 6ac9e5ec18 | |||
| 2d54ca5d47 | |||
| 422371d118 | |||
| 77574ec555 | |||
| 3b385e8738 | |||
| 7f6101a6bc | |||
| 28f6777f9a | |||
| e7275af073 | |||
| 5fcd65ef57 | |||
| 222ed1eb4b | |||
| 4486317888 | |||
| 206ff12b71 | |||
| 73fb216e6f | |||
| e9eab0ae48 | |||
| 626d94d435 | |||
| e4adc1d3a1 | |||
| c3f9a0336c | |||
| 04ca1e4569 | |||
| 459715b9b4 | |||
| db4b0de5e2 | |||
| 914f799f9d | |||
| e2272dcca3 | |||
| 2b1c97ffa4 | |||
| b80d6c74ad | |||
| 6f2dd5d2fa | |||
| 1218dd255f | |||
| 5bf9b069fc | |||
| 2ab9bb4fce | |||
| 87d00abdf1 | |||
| 19aded7a15 | |||
| a2536deef0 | |||
| ff6737314f | |||
| 4fc150f77b | |||
| 90b5947a19 | |||
| c7d445d1c1 | |||
| c4a3bc3d2f | |||
| 8966e5a403 | |||
| bc94a7f921 | |||
| f3cbc2bdd2 | |||
| f681d43601 | |||
| b232cc3081 | |||
| 857d661ff1 | |||
| 8255fb0b28 | |||
| 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 |
@@ -0,0 +1,25 @@
|
|||||||
|
# This file is for unifying the coding style for different editors and IDEs
|
||||||
|
# editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*.{cs,html,js,hbs}]
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.less]
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# They have troubles with TABS. Use 2 spaces
|
||||||
|
[{package.json,.travis.yml}]
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Provide a description of the feature request or bug, the more details the better.
|
||||||
|
Please use https://forums.sonarr.tv/ for support or other questions. (When in doubt, use the forums)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#### Database Migration
|
||||||
|
YES | NO
|
||||||
|
|
||||||
|
#### Description
|
||||||
|
A few sentences describing the overall goals of the pull request's commits.
|
||||||
|
|
||||||
|
#### Todos
|
||||||
|
- [ ] Tests
|
||||||
|
- [ ] Documentation
|
||||||
|
|
||||||
|
|
||||||
|
#### Issues Fixed or Closed by this PR
|
||||||
|
|
||||||
|
*
|
||||||
@@ -10,6 +10,7 @@ src/**/[Oo]bj/
|
|||||||
*.suo
|
*.suo
|
||||||
*.user
|
*.user
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
.vs/
|
||||||
|
|
||||||
# Build results
|
# Build results
|
||||||
*_i.c
|
*_i.c
|
||||||
@@ -41,6 +42,9 @@ src/**/[Oo]bj/
|
|||||||
_ReSharper*
|
_ReSharper*
|
||||||
_dotCover*
|
_dotCover*
|
||||||
|
|
||||||
|
# DevExpress CodeRush
|
||||||
|
src/.cr/
|
||||||
|
|
||||||
# NCrunch
|
# NCrunch
|
||||||
*.ncrunch*
|
*.ncrunch*
|
||||||
.*crunch*.local.xml
|
.*crunch*.local.xml
|
||||||
@@ -126,5 +130,7 @@ output/*
|
|||||||
|
|
||||||
#OS X metadata files
|
#OS X metadata files
|
||||||
._*
|
._*
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
_start
|
_start
|
||||||
|
_temp_*/**/*
|
||||||
|
|||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="PROJECT" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
-1
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="JavaScriptLibraryMappings">
|
<component name="JavaScriptLibraryMappings">
|
||||||
<file url="file://$PROJECT_DIR$" libraries="{Sonarr node_modules}" />
|
|
||||||
<includedPredefinedLibrary name="ECMAScript 6" />
|
<includedPredefinedLibrary name="ECMAScript 6" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
language: csharp
|
||||||
|
solution: src/NzbDrone.sln
|
||||||
|
script: # the following commands are just examples, use whatever your build process requires
|
||||||
|
- ./build.sh
|
||||||
|
- chmod +x test.sh
|
||||||
|
# - ./test.sh Linux Unit Takes far too long, maybe even crashes travis :/
|
||||||
|
install:
|
||||||
|
- sudo apt-get install nodejs
|
||||||
|
- sudo apt-get install npm
|
||||||
+5
-3
@@ -8,8 +8,8 @@ Setup guides, FAQ, the more information we have on the wiki the better.
|
|||||||
## Development ##
|
## Development ##
|
||||||
|
|
||||||
### Tools required ###
|
### Tools required ###
|
||||||
- Visual Studio 2013
|
- Visual Studio 2015
|
||||||
- HTML/Javascript editor of choice (Sublime Text/Webstorm/etc)
|
- HTML/Javascript editor of choice (Sublime Text/Webstorm/Atom/etc)
|
||||||
- npm (node package manager)
|
- npm (node package manager)
|
||||||
- git
|
- git
|
||||||
|
|
||||||
@@ -18,7 +18,9 @@ Setup guides, FAQ, the more information we have on the wiki the better.
|
|||||||
1. Fork Sonarr
|
1. Fork Sonarr
|
||||||
2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)*
|
2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)*
|
||||||
3. Run `npm install`
|
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
|
5. Compile in Visual Studio
|
||||||
|
|
||||||
### Contributing Code ###
|
### Contributing Code ###
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
msBuild='/c/Windows/Microsoft.NET/Framework64/v4.0.30319/'
|
msBuild='/c/Program Files (x86)/MSBuild/14.0/Bin'
|
||||||
outputFolder='./_output'
|
outputFolder='./_output'
|
||||||
outputFolderMono='./_output_mono'
|
outputFolderMono='./_output_mono'
|
||||||
outputFolderOsx='./_output_osx'
|
outputFolderOsx='./_output_osx'
|
||||||
@@ -102,12 +102,12 @@ Build()
|
|||||||
RunGulp()
|
RunGulp()
|
||||||
{
|
{
|
||||||
echo "##teamcity[progressStart 'npm install']"
|
echo "##teamcity[progressStart 'npm install']"
|
||||||
CheckExitCode npm install
|
npm-cache install npm || CheckExitCode npm install
|
||||||
echo "##teamcity[progressFinish 'npm install']"
|
echo "##teamcity[progressFinish 'npm install']"
|
||||||
|
|
||||||
echo "##teamcity[progressStart 'Running Gulp']"
|
echo "##teamcity[progressStart 'Running gulp']"
|
||||||
CheckExitCode gulp build
|
CheckExitCode npm run build
|
||||||
echo "##teamcity[progressFinish 'Running Gulp']"
|
echo "##teamcity[progressFinish 'Running gulp']"
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateMdbs()
|
CreateMdbs()
|
||||||
@@ -208,9 +208,9 @@ PackageTests()
|
|||||||
find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \;
|
find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \;
|
||||||
|
|
||||||
if [ $runtime = "dotnet" ] ; then
|
if [ $runtime = "dotnet" ] ; then
|
||||||
$nuget install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder
|
$nuget install NUnit.ConsoleRunner -Version 3.2.0 -Output $testPackageFolder
|
||||||
else
|
else
|
||||||
mono $nuget install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder
|
mono $nuget install NUnit.ConsoleRunner -Version 3.2.0 -Output $testPackageFolder
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp $outputFolder/*.dll $testPackageFolder
|
cp $outputFolder/*.dll $testPackageFolder
|
||||||
|
|||||||
+4
-1
@@ -19,13 +19,16 @@ gulp.task('less', function() {
|
|||||||
paths.src.root + 'Series/series.less',
|
paths.src.root + 'Series/series.less',
|
||||||
paths.src.root + 'Activity/activity.less',
|
paths.src.root + 'Activity/activity.less',
|
||||||
paths.src.root + 'AddSeries/addSeries.less',
|
paths.src.root + 'AddSeries/addSeries.less',
|
||||||
|
paths.src.root + 'AddMovies/addMovies.less',
|
||||||
paths.src.root + 'Calendar/calendar.less',
|
paths.src.root + 'Calendar/calendar.less',
|
||||||
paths.src.root + 'Cells/cells.less',
|
paths.src.root + 'Cells/cells.less',
|
||||||
paths.src.root + 'ManualImport/manualimport.less',
|
paths.src.root + 'ManualImport/manualimport.less',
|
||||||
paths.src.root + 'Settings/settings.less',
|
paths.src.root + 'Settings/settings.less',
|
||||||
paths.src.root + 'System/Logs/logs.less',
|
paths.src.root + 'System/Logs/logs.less',
|
||||||
paths.src.root + 'System/Update/update.less',
|
paths.src.root + 'System/Update/update.less',
|
||||||
paths.src.root + 'System/Info/info.less'
|
paths.src.root + 'System/Info/info.less',
|
||||||
|
paths.src.root + 'Movies/movies.less',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return gulp.src(src)
|
return gulp.src(src)
|
||||||
|
|||||||
@@ -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
|
|
||||||
+23
-6
@@ -1,17 +1,31 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
#get the bundle's MacOS directory full path
|
#get the bundle's MacOS directory full path
|
||||||
DIR=$(cd "$(dirname "$0")"; pwd)
|
DIR=$(cd "$(dirname "$0")"; pwd)
|
||||||
|
|
||||||
#change these values to match your app
|
#change these values to match your app
|
||||||
EXE_PATH="$DIR/NzbDrone.exe"
|
EXE_PATH="$DIR/NzbDrone.exe"
|
||||||
APPNAME="Sonarr"
|
APPNAME="Sonarr"
|
||||||
|
|
||||||
#set up environment
|
#set up environment
|
||||||
MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
|
if [[ -x '/opt/local/bin/mono' ]]; then
|
||||||
export DYLD_FALLBACK_LIBRARY_PATH="$DIR:$MONO_FRAMEWORK_PATH/lib:/lib:/usr/lib"
|
export PATH="/opt/local/bin:$PATH"
|
||||||
export PATH="$MONO_FRAMEWORK_PATH/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
|
#mono version check
|
||||||
REQUIRED_MAJOR=3
|
REQUIRED_MAJOR=3
|
||||||
REQUIRED_MINOR=10
|
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"
|
DOWNLOAD_URL="http://www.mono-project.com/download/#download-mac"
|
||||||
|
|
||||||
MONO_VERSION="$(mono --version | grep 'Mono JIT compiler version ' | cut -f5 -d\ )"
|
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_MAJOR="$(echo $MONO_VERSION | cut -f1 -d.)"
|
||||||
MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
|
MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
|
||||||
if [ -z "$MONO_VERSION" ] \
|
if [ -z "$MONO_VERSION" ] \
|
||||||
|
|||||||
+3
-2
@@ -4,14 +4,15 @@
|
|||||||
"description": "Sonarr",
|
"description": "Sonarr",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"preinstall": ""
|
"build": "gulp build",
|
||||||
|
"start": "gulp watch"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/Sonarr/Sonarr.git"
|
"url": "git://github.com/Sonarr/Sonarr.git"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "BSD",
|
"license": "GPL-3.0",
|
||||||
"gitHead": "9ff7aa1bf7fe38c4c5bdb92f56c8ad556916ed67",
|
"gitHead": "9ff7aa1bf7fe38c4c5bdb92f56c8ad556916ed67",
|
||||||
"readmeFilename": "readme.md",
|
"readmeFilename": "readme.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,7 +1,20 @@
|
|||||||
# Sonarr #
|
# Radarr [](https://travis-ci.org/galli-leo/Radarr)#
|
||||||
|
|
||||||
|
This fork of Sonarr aims to turn it into something like Couchpotato.
|
||||||
|
|
||||||
Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.
|
## Currently working:
|
||||||
|
* Adding new movies (Note: Movies are currently added as one series with one season and one episode. This will change in the future)
|
||||||
|
* Manually searching for releases of movies.
|
||||||
|
* Automatically searching for releases.
|
||||||
|
* Rarbg.to indexer (Other indexers are coming, I just need to find the right categories)
|
||||||
|
* Everything that has nothing to do with series from Sonarr should be working as well.
|
||||||
|
|
||||||
|
## Planned Features:
|
||||||
|
* Scanning PreDB to know when a new release is available.
|
||||||
|
* Fixing the other Indexers.
|
||||||
|
* Fixing how movies are stored and displayed.
|
||||||
|
* Importing of Sonarr config.
|
||||||
|
* New TorrentPotato Indexer.
|
||||||
|
|
||||||
## Major Features Include: ##
|
## Major Features Include: ##
|
||||||
|
|
||||||
@@ -17,6 +30,8 @@ Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS fee
|
|||||||
* Full support for specials and multi-episode releases
|
* Full support for specials and multi-episode releases
|
||||||
* And a beautiful UI
|
* And a beautiful UI
|
||||||
|
|
||||||
|
## Download
|
||||||
|
The latest precompiled binary versions can be found here: https://github.com/galli-leo/Radarr/releases.
|
||||||
|
|
||||||
## Configuring Development Environment: ##
|
## Configuring Development Environment: ##
|
||||||
|
|
||||||
@@ -24,7 +39,6 @@ Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS fee
|
|||||||
- Visual Studio 2015 [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)
|
- [Git](http://git-scm.com/downloads)
|
||||||
- [NodeJS](http://nodejs.org/download/)
|
- [NodeJS](http://nodejs.org/download/)
|
||||||
- [Gulp](http://gulpjs.com)
|
|
||||||
|
|
||||||
### Setup ###
|
### Setup ###
|
||||||
|
|
||||||
@@ -32,8 +46,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)
|
- 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`
|
- Grab the submodules `git submodule init && git submodule update`
|
||||||
- install the required Node Packages `npm install`
|
- 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 `npm start` command.
|
||||||
- start gulp to monitor your dev environment for any changes that need post processing using `gulp watch` command.
|
|
||||||
|
|
||||||
*Please note gulp must be running at all times while you are working with Sonarr client source files.*
|
*Please note gulp must be running at all times while you are working with Sonarr client source files.*
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,8 @@
|
|||||||
<xs:enumeration value="seedtype" /> <!-- TBD, which criteria must be met. was going for 'ratio,seedtime,both' but afaik it's always 'either' -->
|
<xs:enumeration value="seedtype" /> <!-- TBD, which criteria must be met. was going for 'ratio,seedtime,both' but afaik it's always 'either' -->
|
||||||
<xs:enumeration value="minimumratio" />
|
<xs:enumeration value="minimumratio" />
|
||||||
<xs:enumeration value="minimumseedtime" />
|
<xs:enumeration value="minimumseedtime" />
|
||||||
|
<xs:enumeration value="downloadvolumefactor" /> <!-- factor for the download volume, in most cases it should be set to 1, if a torrent is set to freeleech set it to 0, if only 50% is counted set it to 0.5 -->
|
||||||
|
<xs:enumeration value="uploadvolumefactor" /> <!-- factor for the upload volume, in most cases it should be set to 1, if a torrent is set to neutral leech (upload is not counted) set it to 0, if it's set to double upload set it to 2 -->
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
<xs:element name="attr">
|
<xs:element name="attr">
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,4 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
// General Information about an assembly is controlled through the following
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.4.3.11\lib\net40\NLog.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
// General Information about an assembly is controlled through the following
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace LogentriesNLog.fastJSON
|
|||||||
SerializeNullValues = false;
|
SerializeNullValues = false;
|
||||||
UseOptimizedDatasetSchema = false;
|
UseOptimizedDatasetSchema = false;
|
||||||
UsingGlobalTypes = false;
|
UsingGlobalTypes = false;
|
||||||
|
UseUTCDateTime = true;
|
||||||
}
|
}
|
||||||
public bool UseOptimizedDatasetSchema = true;
|
public bool UseOptimizedDatasetSchema = true;
|
||||||
public bool UseFastGuid = true;
|
public bool UseFastGuid = true;
|
||||||
@@ -39,7 +40,7 @@ namespace LogentriesNLog.fastJSON
|
|||||||
return ToJSON(obj, UseSerializerExtension, UseFastGuid, UseOptimizedDatasetSchema, SerializeNullValues);
|
return ToJSON(obj, UseSerializerExtension, UseFastGuid, UseOptimizedDatasetSchema, SerializeNullValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public string ToJSON(object obj,
|
public string ToJSON(object obj,
|
||||||
bool enableSerializerExtensions,
|
bool enableSerializerExtensions,
|
||||||
bool enableFastGuid,
|
bool enableFastGuid,
|
||||||
@@ -49,13 +50,13 @@ namespace LogentriesNLog.fastJSON
|
|||||||
return new JSONSerializer(enableOptimizedDatasetSchema, enableFastGuid, enableSerializerExtensions, serializeNullValues, IndentOutput).ConvertToJSON(obj);
|
return new JSONSerializer(enableOptimizedDatasetSchema, enableFastGuid, enableSerializerExtensions, serializeNullValues, IndentOutput).ConvertToJSON(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public T ToObject<T>(string json)
|
public T ToObject<T>(string json)
|
||||||
{
|
{
|
||||||
return (T)ToObject(json, typeof(T));
|
return (T)ToObject(json, typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public object ToObject(string json, Type type)
|
public object ToObject(string json, Type type)
|
||||||
{
|
{
|
||||||
var ht = new JsonParser(json).Decode() as Dictionary<string, object>;
|
var ht = new JsonParser(json).Decode() as Dictionary<string, object>;
|
||||||
@@ -320,7 +321,7 @@ namespace LogentriesNLog.fastJSON
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_getterscache.Add(type, getters);
|
_getterscache.Add(type, getters);
|
||||||
return getters;
|
return getters;
|
||||||
}
|
}
|
||||||
@@ -448,7 +449,7 @@ namespace LogentriesNLog.fastJSON
|
|||||||
#if !SILVERLIGHT
|
#if !SILVERLIGHT
|
||||||
else if (pi.isDictionary || pi.isHashtable)
|
else if (pi.isDictionary || pi.isHashtable)
|
||||||
oset = CreateDictionary((ArrayList)v, pi.pt, pi.GenericTypes, globaltypes);
|
oset = CreateDictionary((ArrayList)v, pi.pt, pi.GenericTypes, globaltypes);
|
||||||
#else
|
#else
|
||||||
else if (pi.isDictionary)
|
else if (pi.isDictionary)
|
||||||
oset = CreateDictionary((List<object>)v, pi.pt, pi.GenericTypes, globaltypes);
|
oset = CreateDictionary((List<object>)v, pi.pt, pi.GenericTypes, globaltypes);
|
||||||
#endif
|
#endif
|
||||||
@@ -817,4 +818,4 @@ namespace LogentriesNLog.fastJSON
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,6 +170,8 @@ namespace LogentriesNLog.fastJSON
|
|||||||
_output.Append(dt.Minute.ToString("00", NumberFormatInfo.InvariantInfo));
|
_output.Append(dt.Minute.ToString("00", NumberFormatInfo.InvariantInfo));
|
||||||
_output.Append(":");
|
_output.Append(":");
|
||||||
_output.Append(dt.Second.ToString("00", NumberFormatInfo.InvariantInfo));
|
_output.Append(dt.Second.ToString("00", NumberFormatInfo.InvariantInfo));
|
||||||
|
_output.Append(".");
|
||||||
|
_output.Append(dt.Millisecond.ToString("000", NumberFormatInfo.InvariantInfo));
|
||||||
|
|
||||||
if (JSON.Instance.UseUTCDateTime)
|
if (JSON.Instance.UseUTCDateTime)
|
||||||
_output.Append("Z");
|
_output.Append("Z");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="NLog" version="4.2.3" targetFramework="net40" />
|
<package id="NLog" version="4.3.11" targetFramework="net40" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -198,7 +198,8 @@ namespace Marr.Data.Mapping
|
|||||||
{
|
{
|
||||||
return AutoMapPropertiesWhere(m =>
|
return AutoMapPropertiesWhere(m =>
|
||||||
m.MemberType == MemberTypes.Property &&
|
m.MemberType == MemberTypes.Property &&
|
||||||
!DataHelper.IsSimpleType((m as PropertyInfo).PropertyType));
|
!DataHelper.IsSimpleType((m as PropertyInfo).PropertyType) &&
|
||||||
|
!MapRepository.Instance.TypeConverters.ContainsKey((m as PropertyInfo).PropertyType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.SignalR.Infrastructure
|
namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Diagnostics;
|
|||||||
|
|
||||||
namespace Microsoft.AspNet.SignalR.Infrastructure
|
namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||||
{
|
{
|
||||||
internal class NoOpPerformanceCounter : IPerformanceCounter
|
public class NoOpPerformanceCounter : IPerformanceCounter
|
||||||
{
|
{
|
||||||
public string CounterName
|
public string CounterName
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using System.Diagnostics;
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.SignalR.Configuration;
|
using Microsoft.AspNet.SignalR.Configuration;
|
||||||
using Microsoft.AspNet.SignalR.Hosting;
|
using Microsoft.AspNet.SignalR.Hosting;
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.SignalR.Infrastructure;
|
using Microsoft.AspNet.SignalR.Infrastructure;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.SignalR.Hosting;
|
using Microsoft.AspNet.SignalR.Hosting;
|
||||||
using Microsoft.AspNet.SignalR.Infrastructure;
|
using Microsoft.AspNet.SignalR.Infrastructure;
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ using System.Security.Principal;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.SignalR.Owin.Infrastructure;
|
using Microsoft.AspNet.SignalR.Owin.Infrastructure;
|
||||||
using Microsoft.AspNet.SignalR.Hosting;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.SignalR.Owin
|
namespace Microsoft.AspNet.SignalR.Owin
|
||||||
{
|
{
|
||||||
using WebSocketFunc = Func<IDictionary<string, object>, Task>;
|
|
||||||
public partial class ServerRequest :
|
public partial class ServerRequest :
|
||||||
#if NET45
|
#if NET45
|
||||||
IWebSocketRequest
|
IWebSocketRequest
|
||||||
|
|||||||
@@ -1,168 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using FizzWare.NBuilder;
|
|
||||||
using FluentAssertions;
|
|
||||||
using Marr.Data;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using NzbDrone.Api.Commands;
|
|
||||||
using NzbDrone.Api.Config;
|
|
||||||
using NzbDrone.Api.Episodes;
|
|
||||||
using NzbDrone.Api.History;
|
|
||||||
using NzbDrone.Api.Indexers;
|
|
||||||
using NzbDrone.Api.Logs;
|
|
||||||
using NzbDrone.Api.Mapping;
|
|
||||||
using NzbDrone.Api.Profiles;
|
|
||||||
using NzbDrone.Api.RootFolders;
|
|
||||||
using NzbDrone.Api.Series;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using NzbDrone.Core.Instrumentation;
|
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
|
||||||
using NzbDrone.Core.Organizer;
|
|
||||||
using NzbDrone.Core.Parser.Model;
|
|
||||||
using NzbDrone.Core.Profiles;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
using NzbDrone.Core.RootFolders;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using NzbDrone.Core.Update.Commands;
|
|
||||||
using NzbDrone.Test.Common;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Test.MappingTests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class ResourceMappingFixture : TestBase
|
|
||||||
{
|
|
||||||
[TestCase(typeof(Core.Tv.Series), typeof(SeriesResource))]
|
|
||||||
[TestCase(typeof(Episode), typeof(EpisodeResource))]
|
|
||||||
[TestCase(typeof(RootFolder), typeof(RootFolderResource))]
|
|
||||||
[TestCase(typeof(NamingConfig), typeof(NamingConfigResource))]
|
|
||||||
// [TestCase(typeof(IndexerDefinition), typeof(IndexerResource))] //TODO: Ignoring because we don't have a good way to ignore properties with value injector
|
|
||||||
[TestCase(typeof(ReleaseInfo), typeof(ReleaseResource))]
|
|
||||||
[TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))]
|
|
||||||
[TestCase(typeof(DownloadDecision), typeof(ReleaseResource))]
|
|
||||||
[TestCase(typeof(Core.History.History), typeof(HistoryResource))]
|
|
||||||
[TestCase(typeof(Profile), typeof(ProfileResource))]
|
|
||||||
[TestCase(typeof(ProfileQualityItem), typeof(ProfileQualityItemResource))]
|
|
||||||
[TestCase(typeof(Log), typeof(LogResource))]
|
|
||||||
[TestCase(typeof(Command), typeof(CommandResource))]
|
|
||||||
public void matching_fields(Type modelType, Type resourceType)
|
|
||||||
{
|
|
||||||
MappingValidation.ValidateMapping(modelType, resourceType);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_map_lazy_loaded_values_should_not_be_inject_if_not_loaded()
|
|
||||||
{
|
|
||||||
var modelWithLazy = new ModelWithLazy()
|
|
||||||
{
|
|
||||||
Guid = new TestLazyLoaded<Guid>()
|
|
||||||
};
|
|
||||||
|
|
||||||
modelWithLazy.InjectTo<ModelWithNoLazy>().Guid.Should().BeEmpty();
|
|
||||||
|
|
||||||
modelWithLazy.Guid.IsLoaded.Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_map_lay_loaded_values_should_be_inject_if_loaded()
|
|
||||||
{
|
|
||||||
|
|
||||||
var guid = Guid.NewGuid();
|
|
||||||
|
|
||||||
var modelWithLazy = new ModelWithLazy()
|
|
||||||
{
|
|
||||||
Guid = new LazyLoaded<Guid>(guid)
|
|
||||||
};
|
|
||||||
|
|
||||||
modelWithLazy.InjectTo<ModelWithNoLazy>().Guid.Should().Be(guid);
|
|
||||||
|
|
||||||
modelWithLazy.Guid.IsLoaded.Should().BeTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_be_able_to_map_lists()
|
|
||||||
{
|
|
||||||
var modelList = Builder<TestModel>.CreateListOfSize(10).Build();
|
|
||||||
|
|
||||||
var resourceList = modelList.InjectTo<List<TestResource>>();
|
|
||||||
|
|
||||||
resourceList.Should().HaveSameCount(modelList);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_map_wrapped_models()
|
|
||||||
{
|
|
||||||
var modelList = Builder<TestModel>.CreateListOfSize(10).Build().ToList();
|
|
||||||
|
|
||||||
var wrapper = new TestModelWrapper
|
|
||||||
{
|
|
||||||
TestlList = modelList
|
|
||||||
};
|
|
||||||
|
|
||||||
wrapper.InjectTo<TestResourceWrapper>();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_map_profile()
|
|
||||||
{
|
|
||||||
var profileResource = new ProfileResource
|
|
||||||
{
|
|
||||||
Cutoff = Quality.WEBDL1080p,
|
|
||||||
Items = new List<ProfileQualityItemResource> { new ProfileQualityItemResource { Quality = Quality.WEBDL1080p, Allowed = true } }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
profileResource.InjectTo<Profile>();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_map_tracked_command()
|
|
||||||
{
|
|
||||||
var commandResource = new CommandModel { Body = new ApplicationUpdateCommand() };
|
|
||||||
commandResource.InjectTo<CommandResource>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ModelWithLazy
|
|
||||||
{
|
|
||||||
public LazyLoaded<Guid> Guid { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ModelWithNoLazy
|
|
||||||
{
|
|
||||||
public Guid Guid { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestLazyLoaded<T> : LazyLoaded<T>
|
|
||||||
{
|
|
||||||
public override void Prepare(Func<IDataMapper> dataMapperFactory, object parent)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class TestModelWrapper
|
|
||||||
{
|
|
||||||
public List<TestModel> TestlList { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestResourceWrapper
|
|
||||||
{
|
|
||||||
public List<TestResource> TestList { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestModel
|
|
||||||
{
|
|
||||||
public string Field1 { get; set; }
|
|
||||||
public string Field2 { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestResource
|
|
||||||
{
|
|
||||||
public string Field1 { get; set; }
|
|
||||||
public string Field2 { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -38,17 +38,21 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="FluentAssertions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
<Reference Include="FizzWare.NBuilder, Version=4.0.0.115, Culture=neutral, PublicKeyToken=5651b03e12e42c12, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.dll</HintPath>
|
<HintPath>..\packages\NBuilder.4.0.0\lib\net40\FizzWare.NBuilder.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="FluentAssertions.Core, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
<Reference Include="FluentAssertions, Version=4.18.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.Core.dll</HintPath>
|
<HintPath>..\packages\FluentAssertions.4.18.0\lib\net40\FluentAssertions.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
<Reference Include="FluentAssertions.Core, Version=4.18.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<HintPath>..\packages\FluentAssertions.4.18.0\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\NUnit.3.5.0\lib\net40\nunit.framework.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@@ -57,19 +61,12 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="FizzWare.NBuilder">
|
|
||||||
<HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Moq">
|
<Reference Include="Moq">
|
||||||
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
|
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Omu.ValueInjecter">
|
|
||||||
<HintPath>..\packages\ValueInjecter.2.3.3\lib\net35\Omu.ValueInjecter.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ClientSchemaTests\SchemaBuilderFixture.cs" />
|
<Compile Include="ClientSchemaTests\SchemaBuilderFixture.cs" />
|
||||||
<Compile Include="MappingTests\ResourceMappingFixture.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -111,4 +108,4 @@
|
|||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
-->
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
|
<package id="FluentAssertions" version="4.18.0" targetFramework="net40" />
|
||||||
<package id="Moq" version="4.0.10827" />
|
<package id="Moq" version="4.0.10827" />
|
||||||
<package id="NBuilder" version="3.0.1.1" targetFramework="net40" />
|
<package id="NBuilder" version="4.0.0" targetFramework="net40" />
|
||||||
<package id="NUnit" version="2.6.3" targetFramework="net40" />
|
<package id="NUnit" version="3.5.0" targetFramework="net40" />
|
||||||
<package id="ValueInjecter" version="2.3.3" targetFramework="net40" />
|
|
||||||
</packages>
|
</packages>
|
||||||
@@ -43,7 +43,7 @@ namespace NzbDrone.Api.Authentication
|
|||||||
expiry = DateTime.UtcNow.AddDays(7);
|
expiry = DateTime.UtcNow.AddDays(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.LoginAndRedirect(user.Identifier, expiry);
|
return this.LoginAndRedirect(user.Identifier, expiry, _configFileProvider.UrlBase + "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response Logout()
|
private Response Logout()
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ namespace NzbDrone.Api.Authentication
|
|||||||
_configFileProvider = configFileProvider;
|
_configFileProvider = configFileProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Order => 10;
|
||||||
|
|
||||||
public void Register(IPipelines pipelines)
|
public void Register(IPipelines pipelines)
|
||||||
{
|
{
|
||||||
if (_configFileProvider.AuthenticationMethod == AuthenticationType.Forms)
|
if (_configFileProvider.AuthenticationMethod == AuthenticationType.Forms)
|
||||||
@@ -75,7 +77,7 @@ namespace NzbDrone.Api.Authentication
|
|||||||
if (context.Request.IsApiRequest())
|
if (context.Request.IsApiRequest())
|
||||||
{
|
{
|
||||||
if ((context.Response.StatusCode == HttpStatusCode.SeeOther &&
|
if ((context.Response.StatusCode == HttpStatusCode.SeeOther &&
|
||||||
context.Response.Headers["Location"].StartsWith("/login", StringComparison.InvariantCultureIgnoreCase)) ||
|
context.Response.Headers["Location"].StartsWith($"{_configFileProvider.UrlBase}/login", StringComparison.InvariantCultureIgnoreCase)) ||
|
||||||
context.Response.StatusCode == HttpStatusCode.Unauthorized)
|
context.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||||
{
|
{
|
||||||
context.Response = new { Error = "Unauthorized" }.AsResponse(HttpStatusCode.Unauthorized);
|
context.Response = new { Error = "Unauthorized" }.AsResponse(HttpStatusCode.Unauthorized);
|
||||||
|
|||||||
@@ -16,15 +16,9 @@ namespace NzbDrone.Api.Blacklist
|
|||||||
|
|
||||||
private PagingResource<BlacklistResource> GetBlacklist(PagingResource<BlacklistResource> pagingResource)
|
private PagingResource<BlacklistResource> GetBlacklist(PagingResource<BlacklistResource> pagingResource)
|
||||||
{
|
{
|
||||||
var pagingSpec = new PagingSpec<Core.Blacklisting.Blacklist>
|
var pagingSpec = pagingResource.MapToPagingSpec<BlacklistResource, Core.Blacklisting.Blacklist>("id", SortDirection.Ascending);
|
||||||
{
|
|
||||||
Page = pagingResource.Page,
|
|
||||||
PageSize = pagingResource.PageSize,
|
|
||||||
SortKey = pagingResource.SortKey,
|
|
||||||
SortDirection = pagingResource.SortDirection
|
|
||||||
};
|
|
||||||
|
|
||||||
return ApplyToPage(_blacklistService.Paged, pagingSpec);
|
return ApplyToPage(_blacklistService.Paged, pagingSpec, BlacklistResourceMapper.MapToResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeleteBlacklist(int id)
|
private void DeleteBlacklist(int id)
|
||||||
@@ -32,4 +26,4 @@ namespace NzbDrone.Api.Blacklist
|
|||||||
_blacklistService.Delete(id);
|
_blacklistService.Delete(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,28 @@ namespace NzbDrone.Api.Blacklist
|
|||||||
|
|
||||||
public SeriesResource Series { get; set; }
|
public SeriesResource Series { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class BlacklistResourceMapper
|
||||||
|
{
|
||||||
|
public static BlacklistResource MapToResource(this Core.Blacklisting.Blacklist model)
|
||||||
|
{
|
||||||
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new BlacklistResource
|
||||||
|
{
|
||||||
|
Id = model.Id,
|
||||||
|
|
||||||
|
SeriesId = model.SeriesId,
|
||||||
|
EpisodeIds = model.EpisodeIds,
|
||||||
|
SourceTitle = model.SourceTitle,
|
||||||
|
Quality = model.Quality,
|
||||||
|
Date = model.Date,
|
||||||
|
Protocol = model.Protocol,
|
||||||
|
Indexer = model.Indexer,
|
||||||
|
Message = model.Message,
|
||||||
|
|
||||||
|
Series = model.Series.ToResource()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,36 +2,51 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using DDay.iCal;
|
using Ical.Net;
|
||||||
|
using Ical.Net.DataTypes;
|
||||||
|
using Ical.Net.Interfaces.Serialization;
|
||||||
|
using Ical.Net.Serialization;
|
||||||
|
using Ical.Net.Serialization.iCalendar.Factory;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using Nancy.Responses;
|
using Nancy.Responses;
|
||||||
|
using NzbDrone.Core.Tags;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Calendar
|
namespace NzbDrone.Api.Calendar
|
||||||
{
|
{
|
||||||
public class CalendarFeedModule : NzbDroneFeedModule
|
public class CalendarFeedModule : NzbDroneFeedModule
|
||||||
{
|
{
|
||||||
private readonly IEpisodeService _episodeService;
|
private readonly IEpisodeService _episodeService;
|
||||||
|
private readonly ITagService _tagService;
|
||||||
|
|
||||||
public CalendarFeedModule(IEpisodeService episodeService)
|
public CalendarFeedModule(IEpisodeService episodeService, ITagService tagService)
|
||||||
: base("calendar")
|
: base("calendar")
|
||||||
{
|
{
|
||||||
_episodeService = episodeService;
|
_episodeService = episodeService;
|
||||||
|
_tagService = tagService;
|
||||||
|
|
||||||
Get["/NzbDrone.ics"] = options => GetCalendarFeed();
|
Get["/NzbDrone.ics"] = options => GetCalendarFeed();
|
||||||
|
Get["/Sonarr.ics"] = options => GetCalendarFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response GetCalendarFeed()
|
private Response GetCalendarFeed()
|
||||||
{
|
{
|
||||||
var pastDays = 7;
|
var pastDays = 7;
|
||||||
var futureDays = 28;
|
var futureDays = 28;
|
||||||
var start = DateTime.Today.AddDays(-pastDays);
|
var start = DateTime.Today.AddDays(-pastDays);
|
||||||
var end = DateTime.Today.AddDays(futureDays);
|
var end = DateTime.Today.AddDays(futureDays);
|
||||||
|
var unmonitored = false;
|
||||||
|
var premiersOnly = false;
|
||||||
|
var tags = new List<int>();
|
||||||
|
|
||||||
// TODO: Remove start/end parameters in v3, they don't work well for iCal
|
// TODO: Remove start/end parameters in v3, they don't work well for iCal
|
||||||
var queryStart = Request.Query.Start;
|
var queryStart = Request.Query.Start;
|
||||||
var queryEnd = Request.Query.End;
|
var queryEnd = Request.Query.End;
|
||||||
var queryPastDays = Request.Query.PastDays;
|
var queryPastDays = Request.Query.PastDays;
|
||||||
var queryFutureDays = Request.Query.FutureDays;
|
var queryFutureDays = Request.Query.FutureDays;
|
||||||
|
var queryUnmonitored = Request.Query.Unmonitored;
|
||||||
|
var queryPremiersOnly = Request.Query.PremiersOnly;
|
||||||
|
var queryTags = Request.Query.Tags;
|
||||||
|
|
||||||
if (queryStart.HasValue) start = DateTime.Parse(queryStart.Value);
|
if (queryStart.HasValue) start = DateTime.Parse(queryStart.Value);
|
||||||
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
|
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
|
||||||
@@ -48,33 +63,63 @@ namespace NzbDrone.Api.Calendar
|
|||||||
end = DateTime.Today.AddDays(futureDays);
|
end = DateTime.Today.AddDays(futureDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
var episodes = _episodeService.EpisodesBetweenDates(start, end, false);
|
if (queryUnmonitored.HasValue)
|
||||||
var icalCalendar = new iCalendar();
|
{
|
||||||
|
unmonitored = bool.Parse(queryUnmonitored.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryPremiersOnly.HasValue)
|
||||||
|
{
|
||||||
|
premiersOnly = bool.Parse(queryPremiersOnly.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryTags.HasValue)
|
||||||
|
{
|
||||||
|
var tagInput = (string)queryTags.Value.ToString();
|
||||||
|
tags.AddRange(tagInput.Split(',').Select(_tagService.GetTag).Select(t => t.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
var episodes = _episodeService.EpisodesBetweenDates(start, end, unmonitored);
|
||||||
|
var calendar = new Ical.Net.Calendar
|
||||||
|
{
|
||||||
|
ProductId = "-//sonarr.tv//Sonarr//EN"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var episode in episodes.OrderBy(v => v.AirDateUtc.Value))
|
foreach (var episode in episodes.OrderBy(v => v.AirDateUtc.Value))
|
||||||
{
|
{
|
||||||
var occurrence = icalCalendar.Create<Event>();
|
if (premiersOnly && (episode.SeasonNumber == 0 || episode.EpisodeNumber != 1))
|
||||||
occurrence.UID = "NzbDrone_episode_" + episode.Id.ToString();
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tags.Any() && tags.None(episode.Series.Tags.Contains))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var occurrence = calendar.Create<Event>();
|
||||||
|
occurrence.Uid = "NzbDrone_episode_" + episode.Id;
|
||||||
occurrence.Status = episode.HasFile ? EventStatus.Confirmed : EventStatus.Tentative;
|
occurrence.Status = episode.HasFile ? EventStatus.Confirmed : EventStatus.Tentative;
|
||||||
occurrence.Start = new iCalDateTime(episode.AirDateUtc.Value) { HasTime = true };
|
occurrence.Start = new CalDateTime(episode.AirDateUtc.Value) { HasTime = true };
|
||||||
occurrence.End = new iCalDateTime(episode.AirDateUtc.Value.AddMinutes(episode.Series.Runtime)) { HasTime = true };
|
occurrence.End = new CalDateTime(episode.AirDateUtc.Value.AddMinutes(episode.Series.Runtime)) { HasTime = true };
|
||||||
occurrence.Description = episode.Overview;
|
occurrence.Description = episode.Overview;
|
||||||
occurrence.Categories = new List<string>() { episode.Series.Network };
|
occurrence.Categories = new List<string>() { episode.Series.Network };
|
||||||
|
|
||||||
switch (episode.Series.SeriesType)
|
switch (episode.Series.SeriesType)
|
||||||
{
|
{
|
||||||
case SeriesTypes.Daily:
|
case SeriesTypes.Daily:
|
||||||
occurrence.Summary = string.Format("{0} - {1}", episode.Series.Title, episode.Title);
|
occurrence.Summary = $"{episode.Series.Title} - {episode.Title}";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
occurrence.Summary = string.Format("{0} - {1}x{2:00} - {3}", episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber, episode.Title);
|
occurrence.Summary =$"{episode.Series.Title} - {episode.SeasonNumber}x{episode.EpisodeNumber:00} - {episode.Title}";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var serializer = new DDay.iCal.Serialization.iCalendar.SerializerFactory().Build(icalCalendar.GetType(), new DDay.iCal.Serialization.SerializationContext()) as DDay.iCal.Serialization.IStringSerializer;
|
var serializer = (IStringSerializer) new SerializerFactory().Build(calendar.GetType(), new SerializationContext());
|
||||||
var icalendar = serializer.SerializeToString(icalCalendar);
|
var icalendar = serializer.SerializeToString(calendar);
|
||||||
|
|
||||||
return new TextResponse(icalendar, "text/calendar");
|
return new TextResponse(icalendar, "text/calendar");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace NzbDrone.Api.Calendar
|
|||||||
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
|
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
|
||||||
if (queryIncludeUnmonitored.HasValue) includeUnmonitored = Convert.ToBoolean(queryIncludeUnmonitored.Value);
|
if (queryIncludeUnmonitored.HasValue) includeUnmonitored = Convert.ToBoolean(queryIncludeUnmonitored.Value);
|
||||||
|
|
||||||
var resources = ToListResource(() => _episodeService.EpisodesBetweenDates(start, end, includeUnmonitored));
|
var resources = MapToResource(_episodeService.EpisodesBetweenDates(start, end, includeUnmonitored), true, true);
|
||||||
|
|
||||||
return resources.OrderBy(e => e.AirDateUtc).ToList();
|
return resources.OrderBy(e => e.AirDateUtc).ToList();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.ClientSchema
|
namespace NzbDrone.Api.ClientSchema
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using NzbDrone.Common.EnsureThat;
|
|||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Reflection;
|
using NzbDrone.Common.Reflection;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using Omu.ValueInjecter;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.ClientSchema
|
namespace NzbDrone.Api.ClientSchema
|
||||||
{
|
{
|
||||||
@@ -56,7 +55,7 @@ namespace NzbDrone.Api.ClientSchema
|
|||||||
return result.OrderBy(r => r.Order).ToList();
|
return result.OrderBy(r => r.Order).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object ReadFormSchema(List<Field> fields, Type targetType, object defaults = null)
|
public static object ReadFromSchema(List<Field> fields, Type targetType)
|
||||||
{
|
{
|
||||||
Ensure.That(targetType, () => targetType).IsNotNull();
|
Ensure.That(targetType, () => targetType).IsNotNull();
|
||||||
|
|
||||||
@@ -64,11 +63,6 @@ namespace NzbDrone.Api.ClientSchema
|
|||||||
|
|
||||||
var target = Activator.CreateInstance(targetType);
|
var target = Activator.CreateInstance(targetType);
|
||||||
|
|
||||||
if (defaults != null)
|
|
||||||
{
|
|
||||||
target.InjectFrom(defaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var propertyInfo in properties)
|
foreach (var propertyInfo in properties)
|
||||||
{
|
{
|
||||||
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);
|
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);
|
||||||
@@ -146,9 +140,9 @@ namespace NzbDrone.Api.ClientSchema
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T ReadFormSchema<T>(List<Field> fields)
|
public static T ReadFromSchema<T>(List<Field> fields)
|
||||||
{
|
{
|
||||||
return (T)ReadFormSchema(fields, typeof(T));
|
return (T)ReadFromSchema(fields, typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<SelectOption> GetSelectOptions(Type selectOptions)
|
private static List<SelectOption> GetSelectOptions(Type selectOptions)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Api.Extensions;
|
using NzbDrone.Api.Extensions;
|
||||||
using NzbDrone.Api.Mapping;
|
|
||||||
using NzbDrone.Api.Validation;
|
using NzbDrone.Api.Validation;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
using NzbDrone.Core.Datastore.Events;
|
using NzbDrone.Core.Datastore.Events;
|
||||||
@@ -36,15 +35,13 @@ namespace NzbDrone.Api.Commands
|
|||||||
|
|
||||||
private CommandResource GetCommand(int id)
|
private CommandResource GetCommand(int id)
|
||||||
{
|
{
|
||||||
return _commandQueueManager.Get(id).InjectTo<CommandResource>();
|
return _commandQueueManager.Get(id).ToResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int StartCommand(CommandResource commandResource)
|
private int StartCommand(CommandResource commandResource)
|
||||||
{
|
{
|
||||||
var commandType =
|
var commandType = _serviceFactory.GetImplementations(typeof(Command))
|
||||||
_serviceFactory.GetImplementations(typeof (Command))
|
.Single(c => c.Name.Replace("Command", "").Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
|
||||||
.Single(c => c.Name.Replace("Command", "")
|
|
||||||
.Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
|
|
||||||
|
|
||||||
dynamic command = Request.Body.FromJson(commandType);
|
dynamic command = Request.Body.FromJson(commandType);
|
||||||
command.Trigger = CommandTrigger.Manual;
|
command.Trigger = CommandTrigger.Manual;
|
||||||
@@ -55,14 +52,14 @@ namespace NzbDrone.Api.Commands
|
|||||||
|
|
||||||
private List<CommandResource> GetStartedCommands()
|
private List<CommandResource> GetStartedCommands()
|
||||||
{
|
{
|
||||||
return ToListResource(_commandQueueManager.GetStarted());
|
return _commandQueueManager.GetStarted().ToResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(CommandUpdatedEvent message)
|
public void Handle(CommandUpdatedEvent message)
|
||||||
{
|
{
|
||||||
if (message.Command.Body.SendUpdatesToClient)
|
if (message.Command.Body.SendUpdatesToClient)
|
||||||
{
|
{
|
||||||
BroadcastResourceChange(ModelAction.Updated, message.Command.InjectTo<CommandResource>());
|
BroadcastResourceChange(ModelAction.Updated, message.Command.ToResource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
@@ -9,7 +11,7 @@ namespace NzbDrone.Api.Commands
|
|||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
public Command Body { get; set; }
|
public object Body { get; set; }
|
||||||
public CommandPriority Priority { get; set; }
|
public CommandPriority Priority { get; set; }
|
||||||
public CommandStatus Status { get; set; }
|
public CommandStatus Status { get; set; }
|
||||||
public DateTime Queued { get; set; }
|
public DateTime Queued { get; set; }
|
||||||
@@ -70,7 +72,7 @@ namespace NzbDrone.Api.Commands
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (Body != null) return Body.SendUpdatesToClient;
|
if (Body != null) return (Body as Command).SendUpdatesToClient;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -82,7 +84,7 @@ namespace NzbDrone.Api.Commands
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (Body != null) return Body.UpdateScheduledTask;
|
if (Body != null) return (Body as Command).UpdateScheduledTask;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -92,4 +94,37 @@ namespace NzbDrone.Api.Commands
|
|||||||
|
|
||||||
public DateTime? LastExecutionTime { get; set; }
|
public DateTime? LastExecutionTime { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CommandResourceMapper
|
||||||
|
{
|
||||||
|
public static CommandResource ToResource(this CommandModel model)
|
||||||
|
{
|
||||||
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new CommandResource
|
||||||
|
{
|
||||||
|
Id = model.Id,
|
||||||
|
|
||||||
|
Name = model.Name,
|
||||||
|
Message = model.Message,
|
||||||
|
Body = model.Body,
|
||||||
|
Priority = model.Priority,
|
||||||
|
Status = model.Status,
|
||||||
|
Queued = model.QueuedAt,
|
||||||
|
Started = model.StartedAt,
|
||||||
|
Ended = model.EndedAt,
|
||||||
|
Duration = model.Duration,
|
||||||
|
Exception = model.Exception,
|
||||||
|
Trigger = model.Trigger,
|
||||||
|
|
||||||
|
CompletionMessage = model.Body.CompletionMessage,
|
||||||
|
LastExecutionTime = model.Body.LastExecutionTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<CommandResource> ToResource(this IEnumerable<CommandModel> models)
|
||||||
|
{
|
||||||
|
return models.Select(ToResource).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using FluentValidation;
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Validation.Paths;
|
using NzbDrone.Core.Validation.Paths;
|
||||||
|
|
||||||
@@ -21,5 +20,10 @@ namespace NzbDrone.Api.Config
|
|||||||
.SetValidator(pathExistsValidator)
|
.SetValidator(pathExistsValidator)
|
||||||
.When(c => !string.IsNullOrWhiteSpace(c.DownloadedEpisodesFolder));
|
.When(c => !string.IsNullOrWhiteSpace(c.DownloadedEpisodesFolder));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override DownloadClientConfigResource ToResource(IConfigService model)
|
||||||
|
{
|
||||||
|
return DownloadClientConfigResourceMapper.ToResource(model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using System;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
@@ -15,4 +15,23 @@ namespace NzbDrone.Api.Config
|
|||||||
public bool AutoRedownloadFailed { get; set; }
|
public bool AutoRedownloadFailed { get; set; }
|
||||||
public bool RemoveFailedDownloads { get; set; }
|
public bool RemoveFailedDownloads { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class DownloadClientConfigResourceMapper
|
||||||
|
{
|
||||||
|
public static DownloadClientConfigResource ToResource(IConfigService model)
|
||||||
|
{
|
||||||
|
return new DownloadClientConfigResource
|
||||||
|
{
|
||||||
|
DownloadedEpisodesFolder = model.DownloadedEpisodesFolder,
|
||||||
|
DownloadClientWorkingFolders = model.DownloadClientWorkingFolders,
|
||||||
|
DownloadedEpisodesScanInterval = model.DownloadedEpisodesScanInterval,
|
||||||
|
|
||||||
|
EnableCompletedDownloadHandling = model.EnableCompletedDownloadHandling,
|
||||||
|
RemoveCompletedDownloads = model.RemoveCompletedDownloads,
|
||||||
|
|
||||||
|
AutoRedownloadFailed = model.AutoRedownloadFailed,
|
||||||
|
RemoveFailedDownloads = model.RemoveFailedDownloads
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,19 +8,20 @@ using NzbDrone.Core.Configuration;
|
|||||||
using NzbDrone.Core.Update;
|
using NzbDrone.Core.Update;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Core.Validation.Paths;
|
using NzbDrone.Core.Validation.Paths;
|
||||||
using Omu.ValueInjecter;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
public class HostConfigModule : NzbDroneRestModule<HostConfigResource>
|
public class HostConfigModule : NzbDroneRestModule<HostConfigResource>
|
||||||
{
|
{
|
||||||
private readonly IConfigFileProvider _configFileProvider;
|
private readonly IConfigFileProvider _configFileProvider;
|
||||||
|
private readonly IConfigService _configService;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
public HostConfigModule(IConfigFileProvider configFileProvider, IUserService userService)
|
public HostConfigModule(IConfigFileProvider configFileProvider, IConfigService configService, IUserService userService)
|
||||||
: base("/config/host")
|
: base("/config/host")
|
||||||
{
|
{
|
||||||
_configFileProvider = configFileProvider;
|
_configFileProvider = configFileProvider;
|
||||||
|
_configService = configService;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
|
|
||||||
GetResourceSingle = GetHostConfig;
|
GetResourceSingle = GetHostConfig;
|
||||||
@@ -48,12 +49,10 @@ namespace NzbDrone.Api.Config
|
|||||||
|
|
||||||
private HostConfigResource GetHostConfig()
|
private HostConfigResource GetHostConfig()
|
||||||
{
|
{
|
||||||
var resource = new HostConfigResource();
|
var resource = _configFileProvider.ToResource(_configService);
|
||||||
resource.InjectFrom(_configFileProvider);
|
|
||||||
resource.Id = 1;
|
resource.Id = 1;
|
||||||
|
|
||||||
var user = _userService.FindUser();
|
var user = _userService.FindUser();
|
||||||
|
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
resource.Username = user.Username;
|
resource.Username = user.Username;
|
||||||
@@ -75,6 +74,7 @@ namespace NzbDrone.Api.Config
|
|||||||
.ToDictionary(prop => prop.Name, prop => prop.GetValue(resource, null));
|
.ToDictionary(prop => prop.Name, prop => prop.GetValue(resource, null));
|
||||||
|
|
||||||
_configFileProvider.SaveConfigDictionary(dictionary);
|
_configFileProvider.SaveConfigDictionary(dictionary);
|
||||||
|
_configService.SaveConfigDictionary(dictionary);
|
||||||
|
|
||||||
if (resource.Username.IsNotNullOrWhiteSpace() && resource.Password.IsNotNullOrWhiteSpace())
|
if (resource.Username.IsNotNullOrWhiteSpace() && resource.Password.IsNotNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
using System;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.REST;
|
|
||||||
using NzbDrone.Core.Authentication;
|
using NzbDrone.Core.Authentication;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Update;
|
using NzbDrone.Core.Update;
|
||||||
|
using NzbDrone.Common.Http.Proxy;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
@@ -19,11 +20,54 @@ namespace NzbDrone.Api.Config
|
|||||||
public string LogLevel { get; set; }
|
public string LogLevel { get; set; }
|
||||||
public string Branch { get; set; }
|
public string Branch { get; set; }
|
||||||
public string ApiKey { get; set; }
|
public string ApiKey { get; set; }
|
||||||
public bool Torrent { get; set; }
|
|
||||||
public string SslCertHash { get; set; }
|
public string SslCertHash { get; set; }
|
||||||
public string UrlBase { get; set; }
|
public string UrlBase { get; set; }
|
||||||
public bool UpdateAutomatically { get; set; }
|
public bool UpdateAutomatically { get; set; }
|
||||||
public UpdateMechanism UpdateMechanism { get; set; }
|
public UpdateMechanism UpdateMechanism { get; set; }
|
||||||
public string UpdateScriptPath { 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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HostConfigResourceMapper
|
||||||
|
{
|
||||||
|
public static HostConfigResource ToResource(this IConfigFileProvider model, IConfigService configService)
|
||||||
|
{
|
||||||
|
// TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead?
|
||||||
|
return new HostConfigResource
|
||||||
|
{
|
||||||
|
BindAddress = model.BindAddress,
|
||||||
|
Port = model.Port,
|
||||||
|
SslPort = model.SslPort,
|
||||||
|
EnableSsl = model.EnableSsl,
|
||||||
|
LaunchBrowser = model.LaunchBrowser,
|
||||||
|
AuthenticationMethod = model.AuthenticationMethod,
|
||||||
|
AnalyticsEnabled = model.AnalyticsEnabled,
|
||||||
|
//Username
|
||||||
|
//Password
|
||||||
|
LogLevel = model.LogLevel,
|
||||||
|
Branch = model.Branch,
|
||||||
|
ApiKey = model.ApiKey,
|
||||||
|
SslCertHash = model.SslCertHash,
|
||||||
|
UrlBase = model.UrlBase,
|
||||||
|
UpdateAutomatically = model.UpdateAutomatically,
|
||||||
|
UpdateMechanism = model.UpdateMechanism,
|
||||||
|
UpdateScriptPath = model.UpdateScriptPath,
|
||||||
|
ProxyEnabled = configService.ProxyEnabled,
|
||||||
|
ProxyType = configService.ProxyType,
|
||||||
|
ProxyHostname = configService.ProxyHostname,
|
||||||
|
ProxyPort = configService.ProxyPort,
|
||||||
|
ProxyUsername = configService.ProxyUsername,
|
||||||
|
ProxyPassword = configService.ProxyPassword,
|
||||||
|
ProxyBypassFilter = configService.ProxyBypassFilter,
|
||||||
|
ProxyBypassLocalAddresses = configService.ProxyBypassLocalAddresses
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,5 +19,10 @@ namespace NzbDrone.Api.Config
|
|||||||
SharedValidator.RuleFor(c => c.RssSyncInterval)
|
SharedValidator.RuleFor(c => c.RssSyncInterval)
|
||||||
.IsValidRssSyncInterval();
|
.IsValidRssSyncInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override IndexerConfigResource ToResource(IConfigService model)
|
||||||
|
{
|
||||||
|
return IndexerConfigResourceMapper.ToResource(model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using System;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
@@ -9,4 +9,17 @@ namespace NzbDrone.Api.Config
|
|||||||
public int Retention { get; set; }
|
public int Retention { get; set; }
|
||||||
public int RssSyncInterval { get; set; }
|
public int RssSyncInterval { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class IndexerConfigResourceMapper
|
||||||
|
{
|
||||||
|
public static IndexerConfigResource ToResource(IConfigService model)
|
||||||
|
{
|
||||||
|
return new IndexerConfigResource
|
||||||
|
{
|
||||||
|
MinimumAge = model.MinimumAge,
|
||||||
|
Retention = model.Retention,
|
||||||
|
RssSyncInterval = model.RssSyncInterval,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using FluentValidation;
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Validation.Paths;
|
using NzbDrone.Core.Validation.Paths;
|
||||||
|
|
||||||
@@ -14,5 +13,10 @@ namespace NzbDrone.Api.Config
|
|||||||
SharedValidator.RuleFor(c => c.FolderChmod).NotEmpty();
|
SharedValidator.RuleFor(c => c.FolderChmod).NotEmpty();
|
||||||
SharedValidator.RuleFor(c => c.RecycleBin).IsValidPath().SetValidator(pathExistsValidator).When(c => !string.IsNullOrWhiteSpace(c.RecycleBin));
|
SharedValidator.RuleFor(c => c.RecycleBin).IsValidPath().SetValidator(pathExistsValidator).When(c => !string.IsNullOrWhiteSpace(c.RecycleBin));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override MediaManagementConfigResource ToResource(IConfigService model)
|
||||||
|
{
|
||||||
|
return MediaManagementConfigResourceMapper.ToResource(model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using System;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
@@ -20,6 +20,33 @@ namespace NzbDrone.Api.Config
|
|||||||
|
|
||||||
public bool SkipFreeSpaceCheckWhenImporting { get; set; }
|
public bool SkipFreeSpaceCheckWhenImporting { get; set; }
|
||||||
public bool CopyUsingHardlinks { get; set; }
|
public bool CopyUsingHardlinks { get; set; }
|
||||||
|
public string ExtraFileExtensions { get; set; }
|
||||||
public bool EnableMediaInfo { get; set; }
|
public bool EnableMediaInfo { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class MediaManagementConfigResourceMapper
|
||||||
|
{
|
||||||
|
public static MediaManagementConfigResource ToResource(IConfigService model)
|
||||||
|
{
|
||||||
|
return new MediaManagementConfigResource
|
||||||
|
{
|
||||||
|
AutoUnmonitorPreviouslyDownloadedEpisodes = model.AutoUnmonitorPreviouslyDownloadedEpisodes,
|
||||||
|
RecycleBin = model.RecycleBin,
|
||||||
|
AutoDownloadPropers = model.AutoDownloadPropers,
|
||||||
|
CreateEmptySeriesFolders = model.CreateEmptySeriesFolders,
|
||||||
|
FileDate = model.FileDate,
|
||||||
|
|
||||||
|
SetPermissionsLinux = model.SetPermissionsLinux,
|
||||||
|
FileChmod = model.FileChmod,
|
||||||
|
FolderChmod = model.FolderChmod,
|
||||||
|
ChownUser = model.ChownUser,
|
||||||
|
ChownGroup = model.ChownGroup,
|
||||||
|
|
||||||
|
SkipFreeSpaceCheckWhenImporting = model.SkipFreeSpaceCheckWhenImporting,
|
||||||
|
CopyUsingHardlinks = model.CopyUsingHardlinks,
|
||||||
|
ExtraFileExtensions = model.ExtraFileExtensions,
|
||||||
|
EnableMediaInfo = model.EnableMediaInfo
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
@@ -7,9 +6,7 @@ using Nancy.Responses;
|
|||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
using Nancy.ModelBinding;
|
using Nancy.ModelBinding;
|
||||||
using NzbDrone.Api.Mapping;
|
|
||||||
using NzbDrone.Api.Extensions;
|
using NzbDrone.Api.Extensions;
|
||||||
using Omu.ValueInjecter;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
@@ -46,7 +43,7 @@ namespace NzbDrone.Api.Config
|
|||||||
|
|
||||||
private void UpdateNamingConfig(NamingConfigResource resource)
|
private void UpdateNamingConfig(NamingConfigResource resource)
|
||||||
{
|
{
|
||||||
var nameSpec = resource.InjectTo<NamingConfig>();
|
var nameSpec = resource.ToModel();
|
||||||
ValidateFormatResult(nameSpec);
|
ValidateFormatResult(nameSpec);
|
||||||
|
|
||||||
_namingConfigService.Save(nameSpec);
|
_namingConfigService.Save(nameSpec);
|
||||||
@@ -55,16 +52,14 @@ namespace NzbDrone.Api.Config
|
|||||||
private NamingConfigResource GetNamingConfig()
|
private NamingConfigResource GetNamingConfig()
|
||||||
{
|
{
|
||||||
var nameSpec = _namingConfigService.GetConfig();
|
var nameSpec = _namingConfigService.GetConfig();
|
||||||
var resource = nameSpec.InjectTo<NamingConfigResource>();
|
var resource = nameSpec.ToResource();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(resource.StandardEpisodeFormat))
|
if (resource.StandardEpisodeFormat.IsNotNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
return resource;
|
var basicConfig = _filenameBuilder.GetBasicNamingConfig(nameSpec);
|
||||||
|
basicConfig.AddToResource(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
var basicConfig = _filenameBuilder.GetBasicNamingConfig(nameSpec);
|
|
||||||
resource.InjectFrom(basicConfig);
|
|
||||||
|
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +70,7 @@ namespace NzbDrone.Api.Config
|
|||||||
|
|
||||||
private JsonResponse<NamingSampleResource> GetExamples(NamingConfigResource config)
|
private JsonResponse<NamingSampleResource> GetExamples(NamingConfigResource config)
|
||||||
{
|
{
|
||||||
var nameSpec = config.InjectTo<NamingConfig>();
|
var nameSpec = config.ToModel();
|
||||||
var sampleResource = new NamingSampleResource();
|
var sampleResource = new NamingSampleResource();
|
||||||
|
|
||||||
var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec);
|
var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Core.Organizer;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
@@ -20,4 +20,57 @@ namespace NzbDrone.Api.Config
|
|||||||
public string Separator { get; set; }
|
public string Separator { get; set; }
|
||||||
public string NumberStyle { get; set; }
|
public string NumberStyle { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class NamingConfigResourceMapper
|
||||||
|
{
|
||||||
|
public static NamingConfigResource ToResource(this NamingConfig model)
|
||||||
|
{
|
||||||
|
return new NamingConfigResource
|
||||||
|
{
|
||||||
|
Id = model.Id,
|
||||||
|
|
||||||
|
RenameEpisodes = model.RenameEpisodes,
|
||||||
|
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
|
||||||
|
MultiEpisodeStyle = model.MultiEpisodeStyle,
|
||||||
|
StandardEpisodeFormat = model.StandardEpisodeFormat,
|
||||||
|
DailyEpisodeFormat = model.DailyEpisodeFormat,
|
||||||
|
AnimeEpisodeFormat = model.AnimeEpisodeFormat,
|
||||||
|
SeriesFolderFormat = model.SeriesFolderFormat,
|
||||||
|
SeasonFolderFormat = model.SeasonFolderFormat
|
||||||
|
//IncludeSeriesTitle
|
||||||
|
//IncludeEpisodeTitle
|
||||||
|
//IncludeQuality
|
||||||
|
//ReplaceSpaces
|
||||||
|
//Separator
|
||||||
|
//NumberStyle
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddToResource(this BasicNamingConfig basicNamingConfig, NamingConfigResource resource)
|
||||||
|
{
|
||||||
|
resource.IncludeSeriesTitle = basicNamingConfig.IncludeSeriesTitle;
|
||||||
|
resource.IncludeEpisodeTitle = basicNamingConfig.IncludeEpisodeTitle;
|
||||||
|
resource.IncludeQuality = basicNamingConfig.IncludeQuality;
|
||||||
|
resource.ReplaceSpaces = basicNamingConfig.ReplaceSpaces;
|
||||||
|
resource.Separator = basicNamingConfig.Separator;
|
||||||
|
resource.NumberStyle = basicNamingConfig.NumberStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NamingConfig ToModel(this NamingConfigResource resource)
|
||||||
|
{
|
||||||
|
return new NamingConfig
|
||||||
|
{
|
||||||
|
Id = resource.Id,
|
||||||
|
|
||||||
|
RenameEpisodes = resource.RenameEpisodes,
|
||||||
|
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
|
||||||
|
MultiEpisodeStyle = resource.MultiEpisodeStyle,
|
||||||
|
StandardEpisodeFormat = resource.StandardEpisodeFormat,
|
||||||
|
DailyEpisodeFormat = resource.DailyEpisodeFormat,
|
||||||
|
AnimeEpisodeFormat = resource.AnimeEpisodeFormat,
|
||||||
|
SeriesFolderFormat = resource.SeriesFolderFormat,
|
||||||
|
SeasonFolderFormat = resource.SeasonFolderFormat
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using Omu.ValueInjecter;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
@@ -27,13 +26,14 @@ namespace NzbDrone.Api.Config
|
|||||||
|
|
||||||
private TResource GetConfig()
|
private TResource GetConfig()
|
||||||
{
|
{
|
||||||
var resource = new TResource();
|
var resource = ToResource(_configService);
|
||||||
resource.InjectFrom(_configService);
|
|
||||||
resource.Id = 1;
|
resource.Id = 1;
|
||||||
|
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract TResource ToResource(IConfigService model);
|
||||||
|
|
||||||
private TResource GetConfig(int id)
|
private TResource GetConfig(int id)
|
||||||
{
|
{
|
||||||
return GetConfig();
|
return GetConfig();
|
||||||
|
|||||||
@@ -1,45 +1,18 @@
|
|||||||
using System.Linq;
|
using NzbDrone.Core.Configuration;
|
||||||
using System.Reflection;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using Omu.ValueInjecter;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
public class UiConfigModule : NzbDroneRestModule<UiConfigResource>
|
public class UiConfigModule : NzbDroneConfigModule<UiConfigResource>
|
||||||
{
|
{
|
||||||
private readonly IConfigService _configService;
|
|
||||||
|
|
||||||
public UiConfigModule(IConfigService configService)
|
public UiConfigModule(IConfigService configService)
|
||||||
: base("/config/ui")
|
: base(configService)
|
||||||
{
|
{
|
||||||
_configService = configService;
|
|
||||||
|
|
||||||
GetResourceSingle = GetUiConfig;
|
|
||||||
GetResourceById = GetUiConfig;
|
|
||||||
UpdateResource = SaveUiConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private UiConfigResource GetUiConfig()
|
protected override UiConfigResource ToResource(IConfigService model)
|
||||||
{
|
{
|
||||||
var resource = new UiConfigResource();
|
return UiConfigResourceMapper.ToResource(model);
|
||||||
resource.InjectFrom(_configService);
|
|
||||||
resource.Id = 1;
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private UiConfigResource GetUiConfig(int id)
|
|
||||||
{
|
|
||||||
return GetUiConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveUiConfig(UiConfigResource resource)
|
|
||||||
{
|
|
||||||
var dictionary = resource.GetType()
|
|
||||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
|
||||||
.ToDictionary(prop => prop.Name, prop => prop.GetValue(resource, null));
|
|
||||||
|
|
||||||
_configService.SaveConfigDictionary(dictionary);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using System;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
@@ -17,4 +17,23 @@ namespace NzbDrone.Api.Config
|
|||||||
|
|
||||||
public bool EnableColorImpairedMode { get; set; }
|
public bool EnableColorImpairedMode { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class UiConfigResourceMapper
|
||||||
|
{
|
||||||
|
public static UiConfigResource ToResource(IConfigService model)
|
||||||
|
{
|
||||||
|
return new UiConfigResource
|
||||||
|
{
|
||||||
|
FirstDayOfWeek = model.FirstDayOfWeek,
|
||||||
|
CalendarWeekColumnHeader = model.CalendarWeekColumnHeader,
|
||||||
|
|
||||||
|
ShortDateFormat = model.ShortDateFormat,
|
||||||
|
LongDateFormat = model.LongDateFormat,
|
||||||
|
TimeFormat = model.TimeFormat,
|
||||||
|
ShowRelativeDates = model.ShowRelativeDates,
|
||||||
|
|
||||||
|
EnableColorImpairedMode = model.EnableColorImpairedMode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,16 @@ namespace NzbDrone.Api.DiskSpace
|
|||||||
private readonly IDiskSpaceService _diskSpaceService;
|
private readonly IDiskSpaceService _diskSpaceService;
|
||||||
|
|
||||||
public DiskSpaceModule(IDiskSpaceService diskSpaceService)
|
public DiskSpaceModule(IDiskSpaceService diskSpaceService)
|
||||||
:base("diskspace")
|
: base("diskspace")
|
||||||
{
|
{
|
||||||
_diskSpaceService = diskSpaceService;
|
_diskSpaceService = diskSpaceService;
|
||||||
GetResourceAll = GetFreeSpace;
|
GetResourceAll = GetFreeSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<DiskSpaceResource> GetFreeSpace()
|
public List<DiskSpaceResource> GetFreeSpace()
|
||||||
{
|
{
|
||||||
return ToListResource(_diskSpaceService.GetFreeSpace);
|
return _diskSpaceService.GetFreeSpace().ConvertAll(DiskSpaceResourceMapper.MapToResource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.DiskSpace
|
namespace NzbDrone.Api.DiskSpace
|
||||||
{
|
{
|
||||||
@@ -10,4 +9,20 @@ namespace NzbDrone.Api.DiskSpace
|
|||||||
public long FreeSpace { get; set; }
|
public long FreeSpace { get; set; }
|
||||||
public long TotalSpace { get; set; }
|
public long TotalSpace { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class DiskSpaceResourceMapper
|
||||||
|
{
|
||||||
|
public static DiskSpaceResource MapToResource(this Core.DiskSpace.DiskSpace model)
|
||||||
|
{
|
||||||
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new DiskSpaceResource
|
||||||
|
{
|
||||||
|
Path = model.Path,
|
||||||
|
Label = model.Label,
|
||||||
|
FreeSpace = model.FreeSpace,
|
||||||
|
TotalSpace = model.TotalSpace
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,22 @@ namespace NzbDrone.Api.DownloadClient
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void MapToResource(DownloadClientResource resource, DownloadClientDefinition definition)
|
||||||
|
{
|
||||||
|
base.MapToResource(resource, definition);
|
||||||
|
|
||||||
|
resource.Enable = definition.Enable;
|
||||||
|
resource.Protocol = definition.Protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void MapToModel(DownloadClientDefinition definition, DownloadClientResource resource)
|
||||||
|
{
|
||||||
|
base.MapToModel(definition, resource);
|
||||||
|
|
||||||
|
definition.Enable = resource.Enable;
|
||||||
|
definition.Protocol = resource.Protocol;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Validate(DownloadClientDefinition definition, bool includeWarnings)
|
protected override void Validate(DownloadClientDefinition definition, bool includeWarnings)
|
||||||
{
|
{
|
||||||
if (!definition.Enable) return;
|
if (!definition.Enable) return;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using NzbDrone.Core.Indexers;
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.DownloadClient
|
namespace NzbDrone.Api.DownloadClient
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Core.Datastore.Events;
|
using NzbDrone.Core.Datastore.Events;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Api.Mapping;
|
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
@@ -14,7 +12,7 @@ using NzbDrone.SignalR;
|
|||||||
|
|
||||||
namespace NzbDrone.Api.EpisodeFiles
|
namespace NzbDrone.Api.EpisodeFiles
|
||||||
{
|
{
|
||||||
public class EpisodeModule : NzbDroneRestModuleWithSignalR<EpisodeFileResource, EpisodeFile>,
|
public class EpisodeFileModule : NzbDroneRestModuleWithSignalR<EpisodeFileResource, EpisodeFile>,
|
||||||
IHandle<EpisodeFileAddedEvent>
|
IHandle<EpisodeFileAddedEvent>
|
||||||
{
|
{
|
||||||
private readonly IMediaFileService _mediaFileService;
|
private readonly IMediaFileService _mediaFileService;
|
||||||
@@ -23,7 +21,7 @@ namespace NzbDrone.Api.EpisodeFiles
|
|||||||
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
|
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public EpisodeModule(IBroadcastSignalRMessage signalRBroadcaster,
|
public EpisodeFileModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||||
IMediaFileService mediaFileService,
|
IMediaFileService mediaFileService,
|
||||||
IRecycleBinProvider recycleBinProvider,
|
IRecycleBinProvider recycleBinProvider,
|
||||||
ISeriesService seriesService,
|
ISeriesService seriesService,
|
||||||
@@ -47,7 +45,7 @@ namespace NzbDrone.Api.EpisodeFiles
|
|||||||
var episodeFile = _mediaFileService.Get(id);
|
var episodeFile = _mediaFileService.Get(id);
|
||||||
var series = _seriesService.GetSeries(episodeFile.SeriesId);
|
var series = _seriesService.GetSeries(episodeFile.SeriesId);
|
||||||
|
|
||||||
return MapToResource(series, episodeFile);
|
return episodeFile.ToResource(series, _qualityUpgradableSpecification);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<EpisodeFileResource> GetEpisodeFiles()
|
private List<EpisodeFileResource> GetEpisodeFiles()
|
||||||
@@ -61,8 +59,7 @@ namespace NzbDrone.Api.EpisodeFiles
|
|||||||
|
|
||||||
var series = _seriesService.GetSeries(seriesId);
|
var series = _seriesService.GetSeries(seriesId);
|
||||||
|
|
||||||
return _mediaFileService.GetFilesBySeries(seriesId)
|
return _mediaFileService.GetFilesBySeries(seriesId).ConvertAll(f => f.ToResource(series, _qualityUpgradableSpecification));
|
||||||
.Select(f => MapToResource(series, f)).ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetQuality(EpisodeFileResource episodeFileResource)
|
private void SetQuality(EpisodeFileResource episodeFileResource)
|
||||||
@@ -83,16 +80,6 @@ namespace NzbDrone.Api.EpisodeFiles
|
|||||||
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
|
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
private EpisodeFileResource MapToResource(Core.Tv.Series series, EpisodeFile episodeFile)
|
|
||||||
{
|
|
||||||
var resource = episodeFile.InjectTo<EpisodeFileResource>();
|
|
||||||
resource.Path = Path.Combine(series.Path, episodeFile.RelativePath);
|
|
||||||
|
|
||||||
resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(series.Profile.Value, episodeFile.Quality);
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(EpisodeFileAddedEvent message)
|
public void Handle(EpisodeFileAddedEvent message)
|
||||||
{
|
{
|
||||||
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id);
|
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
|
|
||||||
@@ -17,4 +18,47 @@ namespace NzbDrone.Api.EpisodeFiles
|
|||||||
|
|
||||||
public bool QualityCutoffNotMet { get; set; }
|
public bool QualityCutoffNotMet { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class EpisodeFileResourceMapper
|
||||||
|
{
|
||||||
|
private static EpisodeFileResource ToResource(this Core.MediaFiles.EpisodeFile model)
|
||||||
|
{
|
||||||
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new EpisodeFileResource
|
||||||
|
{
|
||||||
|
Id = model.Id,
|
||||||
|
|
||||||
|
SeriesId = model.SeriesId,
|
||||||
|
SeasonNumber = model.SeasonNumber,
|
||||||
|
RelativePath = model.RelativePath,
|
||||||
|
//Path
|
||||||
|
Size = model.Size,
|
||||||
|
DateAdded = model.DateAdded,
|
||||||
|
SceneName = model.SceneName,
|
||||||
|
Quality = model.Quality,
|
||||||
|
//QualityCutoffNotMet
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EpisodeFileResource ToResource(this Core.MediaFiles.EpisodeFile model, Core.Tv.Series series, Core.DecisionEngine.IQualityUpgradableSpecification qualityUpgradableSpecification)
|
||||||
|
{
|
||||||
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new EpisodeFileResource
|
||||||
|
{
|
||||||
|
Id = model.Id,
|
||||||
|
|
||||||
|
SeriesId = model.SeriesId,
|
||||||
|
SeasonNumber = model.SeasonNumber,
|
||||||
|
RelativePath = model.RelativePath,
|
||||||
|
Path = Path.Combine(series.Path, model.RelativePath),
|
||||||
|
Size = model.Size,
|
||||||
|
DateAdded = model.DateAdded,
|
||||||
|
SceneName = model.SceneName,
|
||||||
|
Quality = model.Quality,
|
||||||
|
QualityCutoffNotMet = qualityUpgradableSpecification.CutoffNotMet(series.Profile.Value, model.Quality)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using NzbDrone.Api.REST;
|
|||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.SignalR;
|
using NzbDrone.SignalR;
|
||||||
|
using Nancy;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Episodes
|
namespace NzbDrone.Api.Episodes
|
||||||
{
|
{
|
||||||
@@ -27,7 +28,7 @@ namespace NzbDrone.Api.Episodes
|
|||||||
|
|
||||||
var seriesId = (int)Request.Query.SeriesId;
|
var seriesId = (int)Request.Query.SeriesId;
|
||||||
|
|
||||||
var resources = ToListResource(_episodeService.GetEpisodeBySeries(seriesId));
|
var resources = MapToResource(_episodeService.GetEpisodeBySeries(seriesId), false, true);
|
||||||
|
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
@@ -36,10 +37,5 @@ namespace NzbDrone.Api.Episodes
|
|||||||
{
|
{
|
||||||
_episodeService.SetEpisodeMonitored(episodeResource.Id, episodeResource.Monitored);
|
_episodeService.SetEpisodeMonitored(episodeResource.Id, episodeResource.Monitored);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override List<EpisodeResource> LoadSeries(List<EpisodeResource> resources)
|
|
||||||
{
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
using NzbDrone.Common.Extensions;
|
||||||
using System.IO;
|
using NzbDrone.Api.EpisodeFiles;
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Api.Extensions;
|
|
||||||
using NzbDrone.Api.Mapping;
|
|
||||||
using NzbDrone.Api.Series;
|
using NzbDrone.Api.Series;
|
||||||
using NzbDrone.Core.Datastore.Events;
|
using NzbDrone.Core.Datastore.Events;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
@@ -53,41 +50,65 @@ namespace NzbDrone.Api.Episodes
|
|||||||
protected EpisodeResource GetEpisode(int id)
|
protected EpisodeResource GetEpisode(int id)
|
||||||
{
|
{
|
||||||
var episode = _episodeService.GetEpisode(id);
|
var episode = _episodeService.GetEpisode(id);
|
||||||
episode.EpisodeFile.LazyLoad();
|
var resource = MapToResource(episode, true, true);
|
||||||
episode.Series = _seriesService.GetSeries(episode.SeriesId);
|
return resource;
|
||||||
return ToResource(episode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override EpisodeResource ToResource<TModel>(TModel model)
|
protected EpisodeResource MapToResource(Episode episode, bool includeSeries, bool includeEpisodeFile)
|
||||||
{
|
{
|
||||||
var resource = base.ToResource(model);
|
var resource = episode.ToResource();
|
||||||
|
|
||||||
var episode = model as Episode;
|
if (includeSeries || includeEpisodeFile)
|
||||||
if (episode != null)
|
|
||||||
{
|
{
|
||||||
if (episode.EpisodeFile.IsLoaded && episode.EpisodeFile.Value != null)
|
var series = episode.Series ?? _seriesService.GetSeries(episode.SeriesId);
|
||||||
|
|
||||||
|
if (includeSeries)
|
||||||
{
|
{
|
||||||
resource.EpisodeFile.Path = Path.Combine(episode.Series.Path, episode.EpisodeFile.Value.RelativePath);
|
resource.Series = series.ToResource();
|
||||||
resource.EpisodeFile.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(episode.Series.Profile.Value, episode.EpisodeFile.Value.Quality);
|
}
|
||||||
|
if (includeEpisodeFile && episode.EpisodeFileId != 0)
|
||||||
|
{
|
||||||
|
resource.EpisodeFile = episode.EpisodeFile.Value.ToResource(series, _qualityUpgradableSpecification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override List<EpisodeResource> ToListResource<TModel>(IEnumerable<TModel> modelList)
|
protected List<EpisodeResource> MapToResource(List<Episode> episodes, bool includeSeries, bool includeEpisodeFile)
|
||||||
{
|
{
|
||||||
var resources = base.ToListResource(modelList);
|
var result = episodes.ToResource();
|
||||||
|
|
||||||
return LoadSeries(resources);
|
if (includeSeries || includeEpisodeFile)
|
||||||
|
{
|
||||||
|
var seriesDict = new Dictionary<int, Core.Tv.Series>();
|
||||||
|
for (var i = 0; i < episodes.Count; i++)
|
||||||
|
{
|
||||||
|
var episode = episodes[i];
|
||||||
|
var resource = result[i];
|
||||||
|
|
||||||
|
var series = episode.Series ?? seriesDict.GetValueOrDefault(episodes[i].SeriesId) ?? _seriesService.GetSeries(episodes[i].SeriesId);
|
||||||
|
seriesDict[series.Id] = series;
|
||||||
|
|
||||||
|
if (includeSeries)
|
||||||
|
{
|
||||||
|
resource.Series = series.ToResource();
|
||||||
|
}
|
||||||
|
if (includeEpisodeFile && episodes[i].EpisodeFileId != 0)
|
||||||
|
{
|
||||||
|
resource.EpisodeFile = episodes[i].EpisodeFile.Value.ToResource(series, _qualityUpgradableSpecification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(EpisodeGrabbedEvent message)
|
public void Handle(EpisodeGrabbedEvent message)
|
||||||
{
|
{
|
||||||
foreach (var episode in message.Episode.Episodes)
|
foreach (var episode in message.Episode.Episodes)
|
||||||
{
|
{
|
||||||
var resource = episode.InjectTo<EpisodeResource>();
|
var resource = episode.ToResource();
|
||||||
resource.Grabbed = true;
|
resource.Grabbed = true;
|
||||||
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, resource);
|
BroadcastResourceChange(ModelAction.Updated, resource);
|
||||||
@@ -101,10 +122,5 @@ namespace NzbDrone.Api.Episodes
|
|||||||
BroadcastResourceChange(ModelAction.Updated, episode.Id);
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NzbDrone.Api.EpisodeFiles;
|
using NzbDrone.Api.EpisodeFiles;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.Series;
|
using NzbDrone.Api.Series;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Episodes
|
namespace NzbDrone.Api.Episodes
|
||||||
{
|
{
|
||||||
@@ -25,8 +28,6 @@ namespace NzbDrone.Api.Episodes
|
|||||||
public int? SceneEpisodeNumber { get; set; }
|
public int? SceneEpisodeNumber { get; set; }
|
||||||
public int? SceneSeasonNumber { get; set; }
|
public int? SceneSeasonNumber { get; set; }
|
||||||
public bool UnverifiedSceneNumbering { get; set; }
|
public bool UnverifiedSceneNumbering { get; set; }
|
||||||
public DateTime? EndTime { get; set; }
|
|
||||||
public DateTime? GrabDate { get; set; }
|
|
||||||
public string SeriesTitle { get; set; }
|
public string SeriesTitle { get; set; }
|
||||||
public SeriesResource Series { get; set; }
|
public SeriesResource Series { get; set; }
|
||||||
|
|
||||||
@@ -34,4 +35,44 @@ namespace NzbDrone.Api.Episodes
|
|||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||||
public bool Grabbed { get; set; }
|
public bool Grabbed { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class EpisodeResourceMapper
|
||||||
|
{
|
||||||
|
public static EpisodeResource ToResource(this Episode model)
|
||||||
|
{
|
||||||
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new EpisodeResource
|
||||||
|
{
|
||||||
|
Id = model.Id,
|
||||||
|
|
||||||
|
SeriesId = model.SeriesId,
|
||||||
|
EpisodeFileId = model.EpisodeFileId,
|
||||||
|
SeasonNumber = model.SeasonNumber,
|
||||||
|
EpisodeNumber = model.EpisodeNumber,
|
||||||
|
Title = model.Title,
|
||||||
|
AirDate = model.AirDate,
|
||||||
|
AirDateUtc = model.AirDateUtc,
|
||||||
|
Overview = model.Overview,
|
||||||
|
//EpisodeFile
|
||||||
|
|
||||||
|
HasFile = model.HasFile,
|
||||||
|
Monitored = model.Monitored,
|
||||||
|
AbsoluteEpisodeNumber = model.AbsoluteEpisodeNumber,
|
||||||
|
SceneAbsoluteEpisodeNumber = model.SceneAbsoluteEpisodeNumber,
|
||||||
|
SceneEpisodeNumber = model.SceneEpisodeNumber,
|
||||||
|
SceneSeasonNumber = model.SceneSeasonNumber,
|
||||||
|
UnverifiedSceneNumbering = model.UnverifiedSceneNumbering,
|
||||||
|
SeriesTitle = model.SeriesTitle,
|
||||||
|
//Series = model.Series.MapToResource(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<EpisodeResource> ToResource(this IEnumerable<Episode> models)
|
||||||
|
{
|
||||||
|
if (models == null) return null;
|
||||||
|
|
||||||
|
return models.Select(ToResource).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ namespace NzbDrone.Api.Episodes
|
|||||||
if (Request.Query.SeasonNumber.HasValue)
|
if (Request.Query.SeasonNumber.HasValue)
|
||||||
{
|
{
|
||||||
var seasonNumber = (int)Request.Query.SeasonNumber;
|
var seasonNumber = (int)Request.Query.SeasonNumber;
|
||||||
return ToListResource(() => _renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber));
|
return _renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber).ToResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ToListResource(() => _renameEpisodeFileService.GetRenamePreviews(seriesId));
|
return _renameEpisodeFileService.GetRenamePreviews(seriesId).ToResource();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Episodes
|
namespace NzbDrone.Api.Episodes
|
||||||
@@ -13,4 +13,27 @@ namespace NzbDrone.Api.Episodes
|
|||||||
public string ExistingPath { get; set; }
|
public string ExistingPath { get; set; }
|
||||||
public string NewPath { get; set; }
|
public string NewPath { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class RenameEpisodeResourceMapper
|
||||||
|
{
|
||||||
|
public static RenameEpisodeResource ToResource(this Core.MediaFiles.RenameEpisodeFilePreview model)
|
||||||
|
{
|
||||||
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new RenameEpisodeResource
|
||||||
|
{
|
||||||
|
SeriesId = model.SeriesId,
|
||||||
|
SeasonNumber = model.SeasonNumber,
|
||||||
|
EpisodeNumbers = model.EpisodeNumbers.ToList(),
|
||||||
|
EpisodeFileId = model.EpisodeFileId,
|
||||||
|
ExistingPath = model.ExistingPath,
|
||||||
|
NewPath = model.NewPath
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<RenameEpisodeResource> ToResource(this IEnumerable<Core.MediaFiles.RenameEpisodeFilePreview> models)
|
||||||
|
{
|
||||||
|
return models.Select(ToResource).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using NzbDrone.Api.Mapping;
|
|
||||||
using NzbDrone.Api.REST;
|
|
||||||
using NzbDrone.Common.Cache;
|
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Extensions
|
|
||||||
{
|
|
||||||
public static class LazyExtensions
|
|
||||||
{
|
|
||||||
private static readonly ICached<MethodInfo> SetterCache = new Cached<MethodInfo>();
|
|
||||||
|
|
||||||
public static IEnumerable<TParent> LoadSubtype<TParent, TChild, TSourceChild>(this IEnumerable<TParent> parents, Func<TParent, int> foreignKeySelector, Func<IEnumerable<int>, IEnumerable<TSourceChild>> sourceChildSelector)
|
|
||||||
where TSourceChild : ModelBase, new()
|
|
||||||
where TChild : RestResource, new()
|
|
||||||
where TParent : RestResource
|
|
||||||
{
|
|
||||||
var parentList = parents.Where(p => foreignKeySelector(p) != 0).ToList();
|
|
||||||
|
|
||||||
if (!parentList.Any())
|
|
||||||
{
|
|
||||||
return parents;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ids = parentList.Select(foreignKeySelector).Distinct();
|
|
||||||
var childDictionary = sourceChildSelector(ids).ToDictionary(child => child.Id, child => child);
|
|
||||||
|
|
||||||
var childSetter = GetChildSetter<TParent, TChild>();
|
|
||||||
|
|
||||||
foreach (var episode in parentList)
|
|
||||||
{
|
|
||||||
childSetter.Invoke(episode, new object[] { childDictionary[foreignKeySelector(episode)].InjectTo<TChild>() });
|
|
||||||
}
|
|
||||||
|
|
||||||
return parents;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static MethodInfo GetChildSetter<TParent, TChild>()
|
|
||||||
where TChild : RestResource
|
|
||||||
where TParent : RestResource
|
|
||||||
{
|
|
||||||
var key = typeof(TChild).FullName + typeof(TParent).FullName;
|
|
||||||
|
|
||||||
return SetterCache.Get(key, () =>
|
|
||||||
{
|
|
||||||
var property = typeof(TParent).GetProperties().Single(c => c.PropertyType == typeof(TChild));
|
|
||||||
return property.GetSetMethod();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,8 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
|||||||
_cacheableSpecification = cacheableSpecification;
|
_cacheableSpecification = cacheableSpecification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Order => 0;
|
||||||
|
|
||||||
public void Register(IPipelines pipelines)
|
public void Register(IPipelines pipelines)
|
||||||
{
|
{
|
||||||
pipelines.AfterRequest.AddItemToStartOfPipeline((Action<NancyContext>) Handle);
|
pipelines.AfterRequest.AddItemToStartOfPipeline((Action<NancyContext>) Handle);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
|||||||
{
|
{
|
||||||
public class CorsPipeline : IRegisterNancyPipeline
|
public class CorsPipeline : IRegisterNancyPipeline
|
||||||
{
|
{
|
||||||
|
public int Order => 0;
|
||||||
|
|
||||||
public void Register(IPipelines pipelines)
|
public void Register(IPipelines pipelines)
|
||||||
{
|
{
|
||||||
pipelines.AfterRequest.AddItemToEndOfPipeline((Action<NancyContext>) Handle);
|
pipelines.AfterRequest.AddItemToEndOfPipeline((Action<NancyContext>) Handle);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using Nancy;
|
using Nancy;
|
||||||
using Nancy.Bootstrapper;
|
using Nancy.Bootstrapper;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Extensions.Pipelines
|
namespace NzbDrone.Api.Extensions.Pipelines
|
||||||
{
|
{
|
||||||
@@ -12,6 +13,8 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
|||||||
{
|
{
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public int Order => 0;
|
||||||
|
|
||||||
public GzipCompressionPipeline(Logger logger)
|
public GzipCompressionPipeline(Logger logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -19,43 +22,35 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
|||||||
|
|
||||||
public void Register(IPipelines pipelines)
|
public void Register(IPipelines pipelines)
|
||||||
{
|
{
|
||||||
pipelines.AfterRequest.AddItemToEndOfPipeline(c => CompressResponse(c.Request, c.Response));
|
pipelines.AfterRequest.AddItemToEndOfPipeline(CompressResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response CompressResponse(Request request, Response response)
|
private void CompressResponse(NancyContext context)
|
||||||
{
|
{
|
||||||
|
var request = context.Request;
|
||||||
|
var response = context.Response;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
!response.ContentType.Contains("image")
|
!response.ContentType.Contains("image")
|
||||||
&& !response.ContentType.Contains("font")
|
&& !response.ContentType.Contains("font")
|
||||||
&& request.Headers.AcceptEncoding.Any(x => x.Contains("gzip"))
|
&& request.Headers.AcceptEncoding.Any(x => x.Contains("gzip"))
|
||||||
&& (!response.Headers.ContainsKey("Content-Encoding") || response.Headers["Content-Encoding"] != "gzip"))
|
&& !AlreadyGzipEncoded(response)
|
||||||
|
&& !ContentLengthIsTooSmall(response))
|
||||||
{
|
{
|
||||||
var data = new MemoryStream();
|
var contents = response.Contents;
|
||||||
response.Contents.Invoke(data);
|
|
||||||
data.Position = 0;
|
|
||||||
if (data.Length < 1024)
|
|
||||||
{
|
|
||||||
response.Contents = stream =>
|
|
||||||
{
|
|
||||||
data.CopyTo(stream);
|
|
||||||
stream.Flush();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
response.Headers["Content-Encoding"] = "gzip";
|
|
||||||
response.Contents = s =>
|
|
||||||
{
|
|
||||||
var gzip = new GZipStream(s, CompressionMode.Compress, true);
|
|
||||||
data.CopyTo(gzip);
|
|
||||||
gzip.Close();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
response.Headers["Content-Encoding"] = "gzip";
|
||||||
|
response.Contents = responseStream =>
|
||||||
|
{
|
||||||
|
using (var gzip = new GZipStream(responseStream, CompressionMode.Compress, true))
|
||||||
|
using (var buffered = new BufferedStream(gzip, 8192))
|
||||||
|
{
|
||||||
|
contents.Invoke(buffered);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -64,5 +59,25 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool ContentLengthIsTooSmall(Response response)
|
||||||
|
{
|
||||||
|
var contentLength = response.Headers.GetValueOrDefault("Content-Length");
|
||||||
|
if (contentLength != null && long.Parse(contentLength) < 1024)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool AlreadyGzipEncoded(Response response)
|
||||||
|
{
|
||||||
|
var contentEncoding = response.Headers.GetValueOrDefault("Content-Encoding");
|
||||||
|
if (contentEncoding == "gzip")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
|||||||
{
|
{
|
||||||
public interface IRegisterNancyPipeline
|
public interface IRegisterNancyPipeline
|
||||||
{
|
{
|
||||||
|
int Order { get; }
|
||||||
|
|
||||||
void Register(IPipelines pipelines);
|
void Register(IPipelines pipelines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,8 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
|||||||
_cacheableSpecification = cacheableSpecification;
|
_cacheableSpecification = cacheableSpecification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Order => 0;
|
||||||
|
|
||||||
public void Register(IPipelines pipelines)
|
public void Register(IPipelines pipelines)
|
||||||
{
|
{
|
||||||
pipelines.BeforeRequest.AddItemToStartOfPipeline((Func<NancyContext, Response>) Handle);
|
pipelines.BeforeRequest.AddItemToStartOfPipeline((Func<NancyContext, Response>) Handle);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
|||||||
{
|
{
|
||||||
public class NzbDroneVersionPipeline : IRegisterNancyPipeline
|
public class NzbDroneVersionPipeline : IRegisterNancyPipeline
|
||||||
{
|
{
|
||||||
|
public int Order => 0;
|
||||||
|
|
||||||
public void Register(IPipelines pipelines)
|
public void Register(IPipelines pipelines)
|
||||||
{
|
{
|
||||||
pipelines.AfterRequest.AddItemToStartOfPipeline((Action<NancyContext>) Handle);
|
pipelines.AfterRequest.AddItemToStartOfPipeline((Action<NancyContext>) Handle);
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Nancy;
|
||||||
|
using Nancy.Bootstrapper;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Api.ErrorManagement;
|
||||||
|
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;
|
||||||
|
|
||||||
|
private readonly NzbDroneErrorPipeline _errorPipeline;
|
||||||
|
|
||||||
|
public RequestLoggingPipeline(NzbDroneErrorPipeline errorPipeline)
|
||||||
|
{
|
||||||
|
_errorPipeline = errorPipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Order => 100;
|
||||||
|
|
||||||
|
public void Register(IPipelines pipelines)
|
||||||
|
{
|
||||||
|
pipelines.BeforeRequest.AddItemToStartOfPipeline(LogStart);
|
||||||
|
pipelines.AfterRequest.AddItemToEndOfPipeline(LogEnd);
|
||||||
|
pipelines.OnError.AddItemToEndOfPipeline(LogError);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Response LogError(NancyContext context, Exception exception)
|
||||||
|
{
|
||||||
|
var response = _errorPipeline.HandleException(context, exception);
|
||||||
|
|
||||||
|
context.Response = response;
|
||||||
|
|
||||||
|
LogEnd(context);
|
||||||
|
|
||||||
|
context.Response = null;
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Nancy;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Api.Health
|
|||||||
|
|
||||||
private List<HealthResource> GetHealth()
|
private List<HealthResource> GetHealth()
|
||||||
{
|
{
|
||||||
return ToListResource(_healthCheckService.Results);
|
return _healthCheckService.Results().ToResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(HealthCheckCompleteEvent message)
|
public void Handle(HealthCheckCompleteEvent message)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.HealthCheck;
|
using NzbDrone.Core.HealthCheck;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Health
|
namespace NzbDrone.Api.Health
|
||||||
@@ -8,6 +10,28 @@ namespace NzbDrone.Api.Health
|
|||||||
{
|
{
|
||||||
public HealthCheckResult Type { get; set; }
|
public HealthCheckResult Type { get; set; }
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
public Uri WikiUrl { get; set; }
|
public HttpUri WikiUrl { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HealthResourceMapper
|
||||||
|
{
|
||||||
|
public static HealthResource ToResource(this HealthCheck model)
|
||||||
|
{
|
||||||
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new HealthResource
|
||||||
|
{
|
||||||
|
Id = model.Id,
|
||||||
|
|
||||||
|
Type = model.Type,
|
||||||
|
Message = model.Message,
|
||||||
|
WikiUrl = model.WikiUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<HealthResource> ToResource(this IEnumerable<HealthCheck> models)
|
||||||
|
{
|
||||||
|
return models.Select(ToResource).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using Nancy;
|
using Nancy;
|
||||||
|
using NzbDrone.Api.Episodes;
|
||||||
using NzbDrone.Api.Extensions;
|
using NzbDrone.Api.Extensions;
|
||||||
|
using NzbDrone.Api.Series;
|
||||||
|
using NzbDrone.Api.Movie;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.Download;
|
using NzbDrone.Core.Download;
|
||||||
@@ -26,15 +29,22 @@ namespace NzbDrone.Api.History
|
|||||||
Post["/failed"] = x => MarkAsFailed();
|
Post["/failed"] = x => MarkAsFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HistoryResource ToResource<TModel>(TModel model)
|
protected HistoryResource MapToResource(Core.History.History model)
|
||||||
{
|
{
|
||||||
var resource = base.ToResource(model);
|
var resource = model.ToResource();
|
||||||
|
|
||||||
var history = model as Core.History.History;
|
resource.Series = model.Series.ToResource();
|
||||||
|
resource.Episode = model.Episode.ToResource();
|
||||||
|
resource.Movie = model.Movie.ToResource();
|
||||||
|
|
||||||
if (history != null && history.Series != null)
|
if (model.Series != null)
|
||||||
{
|
{
|
||||||
resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(history.Series.Profile.Value, history.Quality);
|
resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Series.Profile.Value, model.Quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model.Movie != null)
|
||||||
|
{
|
||||||
|
resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Movie.Profile.Value, model.Quality);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resource;
|
return resource;
|
||||||
@@ -44,13 +54,9 @@ namespace NzbDrone.Api.History
|
|||||||
{
|
{
|
||||||
var episodeId = Request.Query.EpisodeId;
|
var episodeId = Request.Query.EpisodeId;
|
||||||
|
|
||||||
var pagingSpec = new PagingSpec<Core.History.History>
|
var movieId = Request.Query.MovieId;
|
||||||
{
|
|
||||||
Page = pagingResource.Page,
|
var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, Core.History.History>("date", SortDirection.Descending);
|
||||||
PageSize = pagingResource.PageSize,
|
|
||||||
SortKey = pagingResource.SortKey,
|
|
||||||
SortDirection = pagingResource.SortDirection
|
|
||||||
};
|
|
||||||
|
|
||||||
if (pagingResource.FilterKey == "eventType")
|
if (pagingResource.FilterKey == "eventType")
|
||||||
{
|
{
|
||||||
@@ -64,7 +70,13 @@ namespace NzbDrone.Api.History
|
|||||||
pagingSpec.FilterExpression = h => h.EpisodeId == i;
|
pagingSpec.FilterExpression = h => h.EpisodeId == i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ApplyToPage(_historyService.Paged, pagingSpec);
|
if (movieId.HasValue)
|
||||||
|
{
|
||||||
|
int i = (int)movieId;
|
||||||
|
pagingSpec.FilterExpression = h => h.MovieId == i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApplyToPage(_historyService.Paged, pagingSpec, MapToResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response MarkAsFailed()
|
private Response MarkAsFailed()
|
||||||
@@ -74,4 +86,4 @@ namespace NzbDrone.Api.History
|
|||||||
return new object().AsResponse();
|
return new object().AsResponse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using NzbDrone.Api.Episodes;
|
using NzbDrone.Api.Episodes;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.Series;
|
using NzbDrone.Api.Series;
|
||||||
|
using NzbDrone.Api.Movie;
|
||||||
using NzbDrone.Core.History;
|
using NzbDrone.Core.History;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
|
|
||||||
@@ -12,20 +13,47 @@ namespace NzbDrone.Api.History
|
|||||||
public class HistoryResource : RestResource
|
public class HistoryResource : RestResource
|
||||||
{
|
{
|
||||||
public int EpisodeId { get; set; }
|
public int EpisodeId { get; set; }
|
||||||
|
public int MovieId { get; set; }
|
||||||
public int SeriesId { get; set; }
|
public int SeriesId { get; set; }
|
||||||
public string SourceTitle { get; set; }
|
public string SourceTitle { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public bool QualityCutoffNotMet { get; set; }
|
public bool QualityCutoffNotMet { get; set; }
|
||||||
public DateTime Date { get; set; }
|
public DateTime Date { get; set; }
|
||||||
public string Indexer { get; set; }
|
|
||||||
public string ReleaseGroup { get; set; }
|
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
|
|
||||||
public HistoryEventType EventType { get; set; }
|
public HistoryEventType EventType { get; set; }
|
||||||
|
|
||||||
public Dictionary<string, string> Data { get; set; }
|
public Dictionary<string, string> Data { get; set; }
|
||||||
|
public MovieResource Movie { get; set; }
|
||||||
public EpisodeResource Episode { get; set; }
|
public EpisodeResource Episode { get; set; }
|
||||||
public SeriesResource Series { get; set; }
|
public SeriesResource Series { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class HistoryResourceMapper
|
||||||
|
{
|
||||||
|
public static HistoryResource ToResource(this Core.History.History model)
|
||||||
|
{
|
||||||
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new HistoryResource
|
||||||
|
{
|
||||||
|
Id = model.Id,
|
||||||
|
|
||||||
|
EpisodeId = model.EpisodeId,
|
||||||
|
SeriesId = model.SeriesId,
|
||||||
|
MovieId = model.MovieId,
|
||||||
|
SourceTitle = model.SourceTitle,
|
||||||
|
Quality = model.Quality,
|
||||||
|
//QualityCutoffNotMet
|
||||||
|
Date = model.Date,
|
||||||
|
DownloadId = model.DownloadId,
|
||||||
|
|
||||||
|
EventType = model.EventType,
|
||||||
|
|
||||||
|
Data = model.Data
|
||||||
|
//Episode
|
||||||
|
//Series
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,25 @@ namespace NzbDrone.Api.Indexers
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void MapToResource(IndexerResource resource, IndexerDefinition definition)
|
||||||
|
{
|
||||||
|
base.MapToResource(resource, definition);
|
||||||
|
|
||||||
|
resource.EnableRss = definition.EnableRss;
|
||||||
|
resource.EnableSearch = definition.EnableSearch;
|
||||||
|
resource.SupportsRss = definition.SupportsRss;
|
||||||
|
resource.SupportsSearch = definition.SupportsSearch;
|
||||||
|
resource.Protocol = definition.Protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void MapToModel(IndexerDefinition definition, IndexerResource resource)
|
||||||
|
{
|
||||||
|
base.MapToModel(definition, resource);
|
||||||
|
|
||||||
|
definition.EnableRss = resource.EnableRss;
|
||||||
|
definition.EnableSearch = resource.EnableSearch;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Validate(IndexerDefinition definition, bool includeWarnings)
|
protected override void Validate(IndexerDefinition definition, bool includeWarnings)
|
||||||
{
|
{
|
||||||
if (!definition.Enable) return;
|
if (!definition.Enable) return;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using NzbDrone.Core.Indexers;
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Indexers
|
namespace NzbDrone.Api.Indexers
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ namespace NzbDrone.Api.Indexers
|
|||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
private readonly ICached<RemoteEpisode> _remoteEpisodeCache;
|
private readonly ICached<RemoteEpisode> _remoteEpisodeCache;
|
||||||
|
private readonly ICached<RemoteMovie> _remoteMovieCache;
|
||||||
|
|
||||||
public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
|
public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
|
||||||
ISearchForNzb nzbSearchService,
|
ISearchForNzb nzbSearchService,
|
||||||
@@ -49,6 +50,7 @@ namespace NzbDrone.Api.Indexers
|
|||||||
PostValidator.RuleFor(s => s.Guid).NotEmpty();
|
PostValidator.RuleFor(s => s.Guid).NotEmpty();
|
||||||
|
|
||||||
_remoteEpisodeCache = cacheManager.GetCache<RemoteEpisode>(GetType(), "remoteEpisodes");
|
_remoteEpisodeCache = cacheManager.GetCache<RemoteEpisode>(GetType(), "remoteEpisodes");
|
||||||
|
_remoteMovieCache = cacheManager.GetCache<RemoteMovie>(GetType(), "remoteMovies");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response DownloadRelease(ReleaseResource release)
|
private Response DownloadRelease(ReleaseResource release)
|
||||||
@@ -59,7 +61,26 @@ namespace NzbDrone.Api.Indexers
|
|||||||
{
|
{
|
||||||
_logger.Debug("Couldn't find requested release in cache, cache timeout probably expired.");
|
_logger.Debug("Couldn't find requested release in cache, cache timeout probably expired.");
|
||||||
|
|
||||||
return new NotFoundResponse();
|
var remoteMovie = _remoteMovieCache.Find(release.Guid);
|
||||||
|
|
||||||
|
if (remoteMovie == null)
|
||||||
|
{
|
||||||
|
return new NotFoundResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_downloadService.DownloadReport(remoteMovie);
|
||||||
|
}
|
||||||
|
catch (ReleaseDownloadException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, ex.Message);
|
||||||
|
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return release.AsResponse();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -82,6 +103,11 @@ namespace NzbDrone.Api.Indexers
|
|||||||
return GetEpisodeReleases(Request.Query.episodeId);
|
return GetEpisodeReleases(Request.Query.episodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Request.Query.movieId != null)
|
||||||
|
{
|
||||||
|
return GetMovieReleases(Request.Query.movieId);
|
||||||
|
}
|
||||||
|
|
||||||
return GetRss();
|
return GetRss();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +115,7 @@ namespace NzbDrone.Api.Indexers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var decisions = _nzbSearchService.EpisodeSearch(episodeId);
|
var decisions = _nzbSearchService.EpisodeSearch(episodeId, true);
|
||||||
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
|
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
|
||||||
|
|
||||||
return MapDecisions(prioritizedDecisions);
|
return MapDecisions(prioritizedDecisions);
|
||||||
@@ -102,6 +128,27 @@ namespace NzbDrone.Api.Indexers
|
|||||||
return new List<ReleaseResource>();
|
return new List<ReleaseResource>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<ReleaseResource> GetMovieReleases(int movieId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var decisions = _nzbSearchService.MovieSearch(movieId, true);
|
||||||
|
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisionsForMovies(decisions);
|
||||||
|
|
||||||
|
return MapDecisions(prioritizedDecisions);
|
||||||
|
}
|
||||||
|
catch (NotImplementedException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "One or more indexer you selected does not support movie search yet: " + ex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Movie search failed: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<ReleaseResource>();
|
||||||
|
}
|
||||||
|
|
||||||
private List<ReleaseResource> GetRss()
|
private List<ReleaseResource> GetRss()
|
||||||
{
|
{
|
||||||
var reports = _rssFetcherAndParser.Fetch();
|
var reports = _rssFetcherAndParser.Fetch();
|
||||||
@@ -113,7 +160,15 @@ namespace NzbDrone.Api.Indexers
|
|||||||
|
|
||||||
protected override ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
|
protected override ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
|
||||||
{
|
{
|
||||||
_remoteEpisodeCache.Set(decision.RemoteEpisode.Release.Guid, decision.RemoteEpisode, TimeSpan.FromMinutes(30));
|
if (decision.IsForMovie)
|
||||||
|
{
|
||||||
|
_remoteMovieCache.Set(decision.RemoteMovie.Release.Guid, decision.RemoteMovie, TimeSpan.FromMinutes(30));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_remoteEpisodeCache.Set(decision.RemoteEpisode.Release.Guid, decision.RemoteEpisode, TimeSpan.FromMinutes(30));
|
||||||
|
}
|
||||||
|
|
||||||
return base.MapDecision(decision, initialWeight);
|
return base.MapDecision(decision, initialWeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
using NzbDrone.Core.Parser.Model;
|
|
||||||
using Omu.ValueInjecter;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Indexers
|
namespace NzbDrone.Api.Indexers
|
||||||
{
|
{
|
||||||
@@ -25,42 +21,20 @@ namespace NzbDrone.Api.Indexers
|
|||||||
|
|
||||||
protected virtual ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
|
protected virtual ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
|
||||||
{
|
{
|
||||||
var release = new ReleaseResource();
|
var release = decision.ToResource();
|
||||||
|
|
||||||
release.InjectFrom(decision.RemoteEpisode.Release);
|
|
||||||
release.InjectFrom(decision.RemoteEpisode.ParsedEpisodeInfo);
|
|
||||||
release.InjectFrom(decision);
|
|
||||||
release.Rejections = decision.Rejections.Select(r => r.Reason).ToList();
|
|
||||||
release.DownloadAllowed = decision.RemoteEpisode.DownloadAllowed;
|
|
||||||
release.ReleaseWeight = initialWeight;
|
release.ReleaseWeight = initialWeight;
|
||||||
|
|
||||||
if (decision.RemoteEpisode.Series != null)
|
if (decision.RemoteEpisode.Series != null)
|
||||||
{
|
{
|
||||||
release.QualityWeight = decision.RemoteEpisode
|
release.QualityWeight = decision.RemoteEpisode.Series
|
||||||
.Series
|
.Profile.Value
|
||||||
.Profile
|
.Items.FindIndex(v => v.Quality == release.Quality.Quality) * 100;
|
||||||
.Value
|
|
||||||
.Items
|
|
||||||
.FindIndex(v => v.Quality == release.Quality.Quality) * 100;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
release.QualityWeight += release.Quality.Revision.Real * 10;
|
release.QualityWeight += release.Quality.Revision.Real * 10;
|
||||||
release.QualityWeight += release.Quality.Revision.Version;
|
release.QualityWeight += release.Quality.Revision.Version;
|
||||||
|
|
||||||
var torrentRelease = decision.RemoteEpisode.Release as TorrentInfo;
|
|
||||||
|
|
||||||
if (torrentRelease != null)
|
|
||||||
{
|
|
||||||
release.Protocol = DownloadProtocol.Torrent;
|
|
||||||
release.Seeders = torrentRelease.Seeders;
|
|
||||||
//TODO: move this up the chains
|
|
||||||
release.Leechers = torrentRelease.Peers - torrentRelease.Seeders;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
release.Protocol = DownloadProtocol.Usenet;
|
|
||||||
}
|
|
||||||
|
|
||||||
return release;
|
return release;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ using NzbDrone.Core.Download;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Api.Mapping;
|
|
||||||
using NzbDrone.Api.Extensions;
|
using NzbDrone.Api.Extensions;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Indexers
|
namespace NzbDrone.Api.Indexers
|
||||||
{
|
{
|
||||||
@@ -39,9 +37,7 @@ namespace NzbDrone.Api.Indexers
|
|||||||
{
|
{
|
||||||
_logger.Info("Release pushed: {0} - {1}", release.Title, release.DownloadUrl);
|
_logger.Info("Release pushed: {0} - {1}", release.Title, release.DownloadUrl);
|
||||||
|
|
||||||
var info = release.Protocol == DownloadProtocol.Usenet ?
|
var info = release.ToModel();
|
||||||
release.InjectTo<ReleaseInfo>() :
|
|
||||||
release.InjectTo<TorrentInfo>();
|
|
||||||
|
|
||||||
info.Guid = "PUSH-" + info.DownloadUrl;
|
info.Guid = "PUSH-" + info.DownloadUrl;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using NzbDrone.Api.REST;
|
|||||||
using NzbDrone.Core.Parser;
|
using NzbDrone.Core.Parser;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Core.Indexers;
|
using NzbDrone.Core.Indexers;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using NzbDrone.Core.DecisionEngine;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Indexers
|
namespace NzbDrone.Api.Indexers
|
||||||
{
|
{
|
||||||
@@ -20,11 +23,9 @@ namespace NzbDrone.Api.Indexers
|
|||||||
public int IndexerId { get; set; }
|
public int IndexerId { get; set; }
|
||||||
public string Indexer { get; set; }
|
public string Indexer { get; set; }
|
||||||
public string ReleaseGroup { get; set; }
|
public string ReleaseGroup { get; set; }
|
||||||
public string SubGroup { get; set; }
|
|
||||||
public string ReleaseHash { get; set; }
|
public string ReleaseHash { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public bool FullSeason { get; set; }
|
public bool FullSeason { get; set; }
|
||||||
public bool SceneSource { get; set; }
|
|
||||||
public int SeasonNumber { get; set; }
|
public int SeasonNumber { get; set; }
|
||||||
public Language Language { get; set; }
|
public Language Language { get; set; }
|
||||||
public string AirDate { get; set; }
|
public string AirDate { get; set; }
|
||||||
@@ -45,6 +46,8 @@ namespace NzbDrone.Api.Indexers
|
|||||||
public int ReleaseWeight { get; set; }
|
public int ReleaseWeight { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public string MagnetUrl { get; set; }
|
||||||
|
public string InfoHash { get; set; }
|
||||||
public int? Seeders { get; set; }
|
public int? Seeders { get; set; }
|
||||||
public int? Leechers { get; set; }
|
public int? Leechers { get; set; }
|
||||||
public DownloadProtocol Protocol { get; set; }
|
public DownloadProtocol Protocol { get; set; }
|
||||||
@@ -74,4 +77,103 @@ namespace NzbDrone.Api.Indexers
|
|||||||
public bool IsPossibleSpecialEpisode { get; set; }
|
public bool IsPossibleSpecialEpisode { get; set; }
|
||||||
public bool Special { get; set; }
|
public bool Special { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ReleaseResourceMapper
|
||||||
|
{
|
||||||
|
public static ReleaseResource ToResource(this DownloadDecision model)
|
||||||
|
{
|
||||||
|
var releaseInfo = model.RemoteEpisode.Release;
|
||||||
|
var parsedEpisodeInfo = model.RemoteEpisode.ParsedEpisodeInfo;
|
||||||
|
var remoteEpisode = model.RemoteEpisode;
|
||||||
|
var torrentInfo = (model.RemoteEpisode.Release as TorrentInfo) ?? new TorrentInfo();
|
||||||
|
var downloadAllowed = model.RemoteEpisode.DownloadAllowed;
|
||||||
|
if (model.IsForMovie)
|
||||||
|
{
|
||||||
|
downloadAllowed = model.RemoteMovie.DownloadAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead? (Got a huge Deja Vu, didn't we talk about this already once?)
|
||||||
|
return new ReleaseResource
|
||||||
|
{
|
||||||
|
Guid = releaseInfo.Guid,
|
||||||
|
Quality = parsedEpisodeInfo.Quality,
|
||||||
|
//QualityWeight
|
||||||
|
Age = releaseInfo.Age,
|
||||||
|
AgeHours = releaseInfo.AgeHours,
|
||||||
|
AgeMinutes = releaseInfo.AgeMinutes,
|
||||||
|
Size = releaseInfo.Size,
|
||||||
|
IndexerId = releaseInfo.IndexerId,
|
||||||
|
Indexer = releaseInfo.Indexer,
|
||||||
|
ReleaseGroup = parsedEpisodeInfo.ReleaseGroup,
|
||||||
|
ReleaseHash = parsedEpisodeInfo.ReleaseHash,
|
||||||
|
Title = releaseInfo.Title,
|
||||||
|
FullSeason = parsedEpisodeInfo.FullSeason,
|
||||||
|
SeasonNumber = parsedEpisodeInfo.SeasonNumber,
|
||||||
|
Language = parsedEpisodeInfo.Language,
|
||||||
|
AirDate = parsedEpisodeInfo.AirDate,
|
||||||
|
SeriesTitle = parsedEpisodeInfo.SeriesTitle,
|
||||||
|
EpisodeNumbers = parsedEpisodeInfo.EpisodeNumbers,
|
||||||
|
AbsoluteEpisodeNumbers = parsedEpisodeInfo.AbsoluteEpisodeNumbers,
|
||||||
|
Approved = model.Approved,
|
||||||
|
TemporarilyRejected = model.TemporarilyRejected,
|
||||||
|
Rejected = model.Rejected,
|
||||||
|
TvdbId = releaseInfo.TvdbId,
|
||||||
|
TvRageId = releaseInfo.TvRageId,
|
||||||
|
Rejections = model.Rejections.Select(r => r.Reason).ToList(),
|
||||||
|
PublishDate = releaseInfo.PublishDate,
|
||||||
|
CommentUrl = releaseInfo.CommentUrl,
|
||||||
|
DownloadUrl = releaseInfo.DownloadUrl,
|
||||||
|
InfoUrl = releaseInfo.InfoUrl,
|
||||||
|
DownloadAllowed = downloadAllowed,
|
||||||
|
//ReleaseWeight
|
||||||
|
|
||||||
|
MagnetUrl = torrentInfo.MagnetUrl,
|
||||||
|
InfoHash = torrentInfo.InfoHash,
|
||||||
|
Seeders = torrentInfo.Seeders,
|
||||||
|
Leechers = (torrentInfo.Peers.HasValue && torrentInfo.Seeders.HasValue) ? (torrentInfo.Peers.Value - torrentInfo.Seeders.Value) : (int?)null,
|
||||||
|
Protocol = releaseInfo.DownloadProtocol,
|
||||||
|
|
||||||
|
IsDaily = parsedEpisodeInfo.IsDaily,
|
||||||
|
IsAbsoluteNumbering = parsedEpisodeInfo.IsAbsoluteNumbering,
|
||||||
|
IsPossibleSpecialEpisode = parsedEpisodeInfo.IsPossibleSpecialEpisode,
|
||||||
|
Special = parsedEpisodeInfo.Special,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReleaseInfo ToModel(this ReleaseResource resource)
|
||||||
|
{
|
||||||
|
ReleaseInfo model;
|
||||||
|
|
||||||
|
if (resource.Protocol == DownloadProtocol.Torrent)
|
||||||
|
{
|
||||||
|
model = new TorrentInfo
|
||||||
|
{
|
||||||
|
MagnetUrl = resource.MagnetUrl,
|
||||||
|
InfoHash = resource.InfoHash,
|
||||||
|
Seeders = resource.Seeders,
|
||||||
|
Peers = (resource.Seeders.HasValue && resource.Leechers.HasValue) ? (resource.Seeders + resource.Leechers) : null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
model = new ReleaseInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Guid = resource.Guid;
|
||||||
|
model.Title = resource.Title;
|
||||||
|
model.Size = resource.Size;
|
||||||
|
model.DownloadUrl = resource.DownloadUrl;
|
||||||
|
model.InfoUrl = resource.InfoUrl;
|
||||||
|
model.CommentUrl = resource.CommentUrl;
|
||||||
|
model.IndexerId = resource.IndexerId;
|
||||||
|
model.Indexer = resource.Indexer;
|
||||||
|
model.DownloadProtocol = resource.DownloadProtocol;
|
||||||
|
model.TvdbId = resource.TvdbId;
|
||||||
|
model.TvRageId = resource.TvRageId;
|
||||||
|
model.PublishDate = resource.PublishDate;
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -31,13 +31,6 @@ namespace NzbDrone.Api.Logs
|
|||||||
return Path.Combine(_appFolderInfo.GetLogFolder(), filename);
|
return Path.Combine(_appFolderInfo.GetLogFolder(), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string DownloadUrlRoot
|
protected override string DownloadUrlRoot => "logfile";
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return "logfile";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Instrumentation;
|
||||||
using NzbDrone.Core.Instrumentation;
|
|
||||||
using NzbDrone.Api.Mapping;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Logs
|
namespace NzbDrone.Api.Logs
|
||||||
{
|
{
|
||||||
@@ -16,7 +14,7 @@ namespace NzbDrone.Api.Logs
|
|||||||
|
|
||||||
private PagingResource<LogResource> GetLogs(PagingResource<LogResource> pagingResource)
|
private PagingResource<LogResource> GetLogs(PagingResource<LogResource> pagingResource)
|
||||||
{
|
{
|
||||||
var pageSpec = pagingResource.InjectTo<PagingSpec<Log>>();
|
var pageSpec = pagingResource.MapToPagingSpec<LogResource, Log>();
|
||||||
|
|
||||||
if (pageSpec.SortKey == "time")
|
if (pageSpec.SortKey == "time")
|
||||||
{
|
{
|
||||||
@@ -48,7 +46,7 @@ namespace NzbDrone.Api.Logs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ApplyToPage(_logService.Paged, pageSpec);
|
return ApplyToPage(_logService.Paged, pageSpec, LogResourceMapper.ToResource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,25 @@ namespace NzbDrone.Api.Logs
|
|||||||
public string Level { get; set; }
|
public string Level { get; set; }
|
||||||
public string Logger { get; set; }
|
public string Logger { get; set; }
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
public string Method { get; set; }
|
}
|
||||||
|
|
||||||
|
public static class LogResourceMapper
|
||||||
|
{
|
||||||
|
public static LogResource ToResource(this Core.Instrumentation.Log model)
|
||||||
|
{
|
||||||
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new LogResource
|
||||||
|
{
|
||||||
|
Id = model.Id,
|
||||||
|
|
||||||
|
Time = model.Time,
|
||||||
|
Exception = model.Exception,
|
||||||
|
ExceptionType = model.ExceptionType,
|
||||||
|
Level = model.Level,
|
||||||
|
Logger = model.Logger,
|
||||||
|
Message = model.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@@ -38,12 +37,6 @@ namespace NzbDrone.Api.Logs
|
|||||||
return Path.Combine(_appFolderInfo.GetUpdateLogFolder(), filename);
|
return Path.Combine(_appFolderInfo.GetUpdateLogFolder(), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string DownloadUrlRoot
|
protected override string DownloadUrlRoot => "updatelogfile";
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return "updatelogfile";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ namespace NzbDrone.Api.ManualImport
|
|||||||
var downloadIdQuery = Request.Query.downloadId;
|
var downloadIdQuery = Request.Query.downloadId;
|
||||||
var downloadId = (string)downloadIdQuery.Value;
|
var downloadId = (string)downloadIdQuery.Value;
|
||||||
|
|
||||||
return ToListResource(_manualImportService.GetMediaFiles(folder, downloadId)).Select(AddQualityWeight).ToList();
|
return _manualImportService.GetMediaFiles(folder, downloadId).ToResource().Select(AddQualityWeight).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ManualImportResource AddQualityWeight(ManualImportResource item)
|
private ManualImportResource AddQualityWeight(ManualImportResource item)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using NzbDrone.Api.Episodes;
|
using NzbDrone.Api.Episodes;
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.Series;
|
using NzbDrone.Api.Series;
|
||||||
|
using NzbDrone.Common.Crypto;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
|
|
||||||
@@ -20,13 +22,35 @@ namespace NzbDrone.Api.ManualImport
|
|||||||
public int QualityWeight { get; set; }
|
public int QualityWeight { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
public IEnumerable<Rejection> Rejections { get; set; }
|
public IEnumerable<Rejection> Rejections { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public int Id
|
public static class ManualImportResourceMapper
|
||||||
|
{
|
||||||
|
public static ManualImportResource ToResource(this Core.MediaFiles.EpisodeImport.Manual.ManualImportItem model)
|
||||||
{
|
{
|
||||||
get
|
if (model == null) return null;
|
||||||
|
|
||||||
|
return new ManualImportResource
|
||||||
{
|
{
|
||||||
return Path.GetHashCode();
|
Id = HashConverter.GetHashInt31(model.Path),
|
||||||
}
|
|
||||||
|
Path = model.Path,
|
||||||
|
RelativePath = model.RelativePath,
|
||||||
|
Name = model.Name,
|
||||||
|
Size = model.Size,
|
||||||
|
Series = model.Series.ToResource(),
|
||||||
|
SeasonNumber = model.SeasonNumber,
|
||||||
|
Episodes = model.Episodes.ToResource(),
|
||||||
|
Quality = model.Quality,
|
||||||
|
//QualityWeight
|
||||||
|
DownloadId = model.DownloadId,
|
||||||
|
Rejections = model.Rejections
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ManualImportResource> ToResource(this IEnumerable<Core.MediaFiles.EpisodeImport.Manual.ManualImportItem> models)
|
||||||
|
{
|
||||||
|
return models.Select(ToResource).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Marr.Data;
|
|
||||||
using Omu.ValueInjecter;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Mapping
|
|
||||||
{
|
|
||||||
public class CloneInjection : ConventionInjection
|
|
||||||
{
|
|
||||||
protected override bool Match(ConventionInfo conventionInfo)
|
|
||||||
{
|
|
||||||
return conventionInfo.SourceProp.Name == conventionInfo.TargetProp.Name &&
|
|
||||||
conventionInfo.SourceProp.Value != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override object SetValue(ConventionInfo conventionInfo)
|
|
||||||
{
|
|
||||||
if (conventionInfo.SourceProp.Type == conventionInfo.TargetProp.Type)
|
|
||||||
return conventionInfo.SourceProp.Value;
|
|
||||||
|
|
||||||
|
|
||||||
if (conventionInfo.SourceProp.Type.IsArray)
|
|
||||||
{
|
|
||||||
var array = (Array)conventionInfo.SourceProp.Value;
|
|
||||||
var clone = (Array)array.Clone();
|
|
||||||
|
|
||||||
for (var index = 0; index < array.Length; index++)
|
|
||||||
{
|
|
||||||
var item = array.GetValue(index);
|
|
||||||
if (!item.GetType().IsValueType && !(item is string))
|
|
||||||
{
|
|
||||||
clone.SetValue(Activator.CreateInstance(item.GetType()).InjectFrom<CloneInjection>(item), index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conventionInfo.SourceProp.Type.IsGenericType)
|
|
||||||
{
|
|
||||||
var genericInterfaces = conventionInfo.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces();
|
|
||||||
if (genericInterfaces.Any(d => d == typeof(IEnumerable)))
|
|
||||||
{
|
|
||||||
return MapLists(conventionInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (genericInterfaces.Any(i => i == typeof(ILazyLoaded)))
|
|
||||||
{
|
|
||||||
return MapLazy(conventionInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
//unhandled generic type, you could also return null or throw
|
|
||||||
return conventionInfo.SourceProp.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
//for simple object types create a new instace and apply the clone injection on it
|
|
||||||
return Activator.CreateInstance(conventionInfo.TargetProp.Type)
|
|
||||||
.InjectFrom<CloneInjection>(conventionInfo.SourceProp.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object MapLazy(ConventionInfo conventionInfo)
|
|
||||||
{
|
|
||||||
var sourceArgument = conventionInfo.SourceProp.Type.GetGenericArguments()[0];
|
|
||||||
|
|
||||||
dynamic lazy = conventionInfo.SourceProp.Value;
|
|
||||||
|
|
||||||
if (lazy.IsLoaded && lazy.Value != null)
|
|
||||||
{
|
|
||||||
if (conventionInfo.TargetProp.Type.IsAssignableFrom(sourceArgument))
|
|
||||||
{
|
|
||||||
return lazy.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var genericArgument = conventionInfo.TargetProp.Type;
|
|
||||||
|
|
||||||
if (genericArgument.IsValueType || genericArgument == typeof(string))
|
|
||||||
{
|
|
||||||
return lazy.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (genericArgument.IsGenericType)
|
|
||||||
{
|
|
||||||
if (conventionInfo.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces().Any(d => d == typeof(IEnumerable)))
|
|
||||||
{
|
|
||||||
return MapLists(genericArgument, lazy.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Activator.CreateInstance(genericArgument).InjectFrom((object)lazy.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object MapLists(ConventionInfo conventionInfo)
|
|
||||||
{
|
|
||||||
var genericArgument = conventionInfo.TargetProp.Type.GetGenericArguments()[0];
|
|
||||||
|
|
||||||
return MapLists(genericArgument, conventionInfo.SourceProp.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object MapLists(Type targetType, object sourceValue)
|
|
||||||
{
|
|
||||||
if (targetType.IsValueType || targetType == typeof(string))
|
|
||||||
{
|
|
||||||
return sourceValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var listType = typeof(List<>).MakeGenericType(targetType);
|
|
||||||
var addMethod = listType.GetMethod("Add");
|
|
||||||
|
|
||||||
var result = Activator.CreateInstance(listType);
|
|
||||||
|
|
||||||
foreach (var sourceItem in (IEnumerable)sourceValue)
|
|
||||||
{
|
|
||||||
var e = Activator.CreateInstance(targetType).InjectFrom<CloneInjection>(sourceItem);
|
|
||||||
addMethod.Invoke(result, new[] { e });
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using NzbDrone.Api.REST;
|
|
||||||
using NzbDrone.Common.Reflection;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Mapping
|
|
||||||
{
|
|
||||||
public static class MappingValidation
|
|
||||||
{
|
|
||||||
public static void ValidateMapping(Type modelType, Type resourceType)
|
|
||||||
{
|
|
||||||
var errors = modelType.GetSimpleProperties().Where(c=>!c.GetGetMethod().IsStatic).Select(p => GetError(resourceType, p)).Where(c => c != null).ToList();
|
|
||||||
|
|
||||||
if (errors.Any())
|
|
||||||
{
|
|
||||||
throw new ResourceMappingException(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintExtraProperties(modelType, resourceType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PrintExtraProperties(Type modelType, Type resourceType)
|
|
||||||
{
|
|
||||||
var resourceBaseProperties = typeof(RestResource).GetProperties().Select(c => c.Name);
|
|
||||||
var resourceProperties = resourceType.GetProperties().Select(c => c.Name).Except(resourceBaseProperties);
|
|
||||||
var modelProperties = modelType.GetProperties().Select(c => c.Name);
|
|
||||||
|
|
||||||
var extra = resourceProperties.Except(modelProperties);
|
|
||||||
|
|
||||||
foreach (var extraProp in extra)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Extra: [{0}]", extraProp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetError(Type resourceType, PropertyInfo modelProperty)
|
|
||||||
{
|
|
||||||
var resourceProperty = resourceType.GetProperties().FirstOrDefault(c => c.Name == modelProperty.Name);
|
|
||||||
|
|
||||||
if (resourceProperty == null)
|
|
||||||
{
|
|
||||||
return string.Format("public {0} {1} {{ get; set; }}", modelProperty.PropertyType.Name, modelProperty.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resourceProperty.PropertyType != modelProperty.PropertyType && !typeof(RestResource).IsAssignableFrom(resourceProperty.PropertyType))
|
|
||||||
{
|
|
||||||
return string.Format("Expected {0}.{1} to have type of {2} but found {3}", resourceType.Name, resourceProperty.Name, modelProperty.PropertyType, resourceProperty.PropertyType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Mapping
|
|
||||||
{
|
|
||||||
public class ResourceMappingException : ApplicationException
|
|
||||||
{
|
|
||||||
public ResourceMappingException(IEnumerable<string> error)
|
|
||||||
: base(Environment.NewLine + string.Join(Environment.NewLine, error.OrderBy(c => c)))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user