1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-10 15:10:57 -04:00

Compare commits

..

80 Commits

Author SHA1 Message Date
Leonardo Galli
a63587bb19 Fix alternative titles, if there is only one. 2017-01-05 14:54:48 +01:00
Leonardo Galli
eb46343ce8 Fix History not picking up imported movie. 2017-01-05 14:50:12 +01:00
Leonardo Galli
8fa43fb9f7 Fix for Path column in MovieFiles table. 2017-01-05 14:36:58 +01:00
Leonardo Galli
6d5e9ad4a1 Fixed matching wrong movies from database when searching. 2017-01-05 13:23:22 +01:00
Leonardo Galli
70c8228605 Updated Newznab to correctly identify movie search capabilities 2017-01-05 13:16:23 +01:00
Leonardo Galli
82470bd995 Fixed an issue where tracked downloads would not be found in the database due to alternative titles. 2017-01-05 12:39:48 +01:00
Leonardo Galli
a263558383 Fix parsing of special editions without . in title. 2017-01-05 11:36:26 +01:00
Leonardo Galli
7d82e35650 Fixes some strings still saying episode. 2017-01-05 11:28:22 +01:00
Leonardo Galli
8616bcedd4 Fixes Deluge for movies. 2017-01-05 11:04:29 +01:00
Leonardo Galli
2c66322121 Fixes default Category of QBittorent client. 2017-01-05 10:59:37 +01:00
Leonardo Galli
1402bc883e Merge branch 'develop' of https://github.com/galli-leo/Radarr into develop 2017-01-05 10:50:35 +01:00
Leonardo Galli
1325822798 Added the TMDB Configuration service. This allows Image urls to be dynamically generated! 2017-01-05 10:49:55 +01:00
Mike
7a786d4c0e Fixes all usenet download clients. (#25)
* Remove confusing abstract method from UsenetClientBase.

* Fix Sabnzbd.

* Put back abstract method.

* Fix usenet download clients.
2017-01-05 01:06:18 +01:00
Leonardo Galli
87c7afac16 Merge branch 'develop' of https://github.com/galli-leo/Radarr into develop 2017-01-04 23:20:52 +01:00
Leonardo Galli
06d39579a5 Fixed some things regarding director's cut / special edition. 2017-01-04 23:20:48 +01:00
Leonardo Galli
c8ea0a73e2 Merge pull request #24 from AeonLucid/develop
Better folder name.
2017-01-04 23:01:44 +01:00
Leonardo Galli
eeb3c88131 Merge branch 'develop' of https://github.com/galli-leo/Radarr into develop 2017-01-04 22:59:39 +01:00
Leonardo Galli
fd718b61ac Updated Parser to parse movie titles. Should also parse things, such as: Director's Cut, Special Edition, etc. This is then displayed in the manual search UI. Importing is not yet updated for the new parser! 2017-01-04 22:59:34 +01:00
AeonLucid
4a3b2a0014 Better folder name. 2017-01-04 22:54:45 +01:00
Tim Turner
cbd87dcc38 Merge pull request #23 from fedoranimus/develop
Fix newznab indexers and nzbget
2017-01-04 15:55:19 -05:00
Tim Turner
956de03a62 Fixed up Newznab indexers
Hacky way to remove the "tt" from an imdbid - need to come up with a
better way.
2017-01-04 15:48:30 -05:00
Tim Turner
2b74098040 Merge remote-tracking branch 'refs/remotes/origin/develop' into galli-leo/develop 2017-01-04 14:55:25 -05:00
Leonardo Galli
402a9e1ee0 Fixes some issues when adding movies caused by TMDB. 2017-01-04 20:52:59 +01:00
Leonardo Galli
e68653463d Merge branch 'develop' of https://github.com/galli-leo/Radarr into develop 2017-01-04 20:34:37 +01:00
Leonardo Galli
0715962ec5 TheMovieDB.org is now used as metadata source. 2017-01-04 20:27:14 +01:00
Leonardo Galli
5668a3bcfd Merge pull request #22 from AeonLucid/develop
Replace colon in movie path.
2017-01-04 18:42:01 +01:00
AeonLucid
bd241dcbe0 Replace colon in movie path. 2017-01-04 18:23:54 +01:00
Leonardo Galli
69786b3968 Fixes OSX Application name. 2017-01-04 15:05:14 +01:00
Leonardo Galli
6e2ded5d33 Fixed an issue where sometimes the json returned from IMDb just was not parsed correctly for some misterious reason. 2017-01-04 15:01:38 +01:00
Leonardo Galli
b47d5f7fa1 Update readme.md 2017-01-04 12:15:56 +01:00
Tim Turner
90ff73d45f Update NzbGet & NewznabSettings to support movies 2017-01-03 18:33:16 -05:00
Tim Turner
c2c7015f39 Merge remote-tracking branch 'refs/remotes/galli-leo/develop' into develop 2017-01-03 18:32:31 -05:00
Tim Turner
579602419e Merge remote-tracking branch 'refs/remotes/galli-leo/develop' into develop 2017-01-03 18:01:17 -05:00
Leonardo Galli
28857e7fac Fixes issue with History table. 2017-01-03 23:59:41 +01:00
Leonardo Galli
6d23fb1b61 Fixes issue with History table not having a movie id field. 2017-01-03 23:57:02 +01:00
Tim Turner
96332978a0 Merge remote-tracking branch 'refs/remotes/galli-leo/develop' into develop 2017-01-03 17:42:54 -05:00
Leonardo Galli
ad95fbfd4a Implemented importing movies. This is still in early stages, however it should work pretty well. 2017-01-03 23:18:51 +01:00
Tim Turner
1488c0a0fc Merge remote-tracking branch 'refs/remotes/galli-leo/develop' into develop 2017-01-03 16:54:20 -05:00
Leonardo Galli
d9d8cbacec Fixes package.sh for OSX builds 2017-01-03 17:35:47 +01:00
Leonardo Galli
80f2adad50 Changes name to Radarr in system tray icon. 2017-01-03 17:35:31 +01:00
Leonardo Galli
7a72f4a05b Fix package.sh 2017-01-03 16:29:49 +01:00
Leonardo Galli
6d7ff628d8 Updated package.sh for Travis 2017-01-03 16:15:13 +01:00
Leonardo Galli
e9f9f66b2f Allow Sonarr and Radarr to run together.
Also changes default port of Radarr to 7878.
However, now multiple instances of Radarr can also be run. This should
be fixed in the future.
2017-01-03 16:06:06 +01:00
Leonardo Galli
6ca88f98af Fix package.sh permissions for travis 2017-01-03 16:04:41 +01:00
Leonardo Galli
631cf776f6 Travis now automatically pushes a build to a server. 2017-01-03 15:54:15 +01:00
Leonardo Galli
6ea9b4b94a Added Script for easier packaging. 2017-01-03 14:18:13 +01:00
Leonardo Galli
d835c168d3 Searching for movies directly when adding them is now working. 2017-01-03 13:26:09 +01:00
Leonardo Galli
329786365d Fixed adding multiple movies. 2017-01-03 12:52:09 +01:00
Leonardo Galli
4f6380a73c Automatically downloading the best movie release works now. 2017-01-03 11:59:03 +01:00
Leonardo Galli
cde1217356 Movies now show up in the Queue. 2017-01-02 22:01:11 +01:00
Leonardo Galli
2eedfca78a Movie history now fully implemented. 2017-01-02 21:02:54 +01:00
Leonardo Galli
0d65984991 Fix History for Movie Details page. 2017-01-02 20:41:44 +01:00
Leonardo Galli
2a932fe7e8 First implementation of History for Movies. However, nothing is returned from the Database query misteriously. 2017-01-02 20:15:13 +01:00
Leonardo Galli
0e02171938 Fixes downloading a movie. However, now all downloaders except QBittorrent are non functional until they get fixed. See #14 2017-01-02 19:20:32 +01:00
Tim Turner
a3dfa05f25 Merge remote-tracking branch 'refs/remotes/galli-leo/develop' into develop 2017-01-02 12:24:56 -05:00
Leonardo Galli
2a3b0304cb Fixed a few things with displaying the Movie Details Page. Implemented the first round of Searching for and Downloading movie Releases. ATM this is still a bit hacky and alot of things need to be cleaned up. However, one can now manually search for and download (only in qBittorrent) a movie torrent. 2017-01-02 18:05:55 +01:00
Leonardo Galli
16e35f68bb Merge branch 'develop' of https://github.com/galli-leo/Radarr into develop 2017-01-02 13:21:11 +01:00
Leonardo Galli
74b1c846a5 Fixed css for movies. 2017-01-02 13:20:50 +01:00
Leonardo Galli
1f930c18e4 Update readme.md 2017-01-02 11:45:11 +01:00
Leonardo Galli
d9e60eff6b Update .travis.yml
Removed tests since they take far too long
2017-01-02 11:25:28 +01:00
Leonardo Galli
097982334c Update .travis.yml
Fixed syntax for test.sh
2017-01-02 10:40:09 +01:00
Leonardo Galli
be6e6b910e Update .travis.yml
symlinking node was already done in apt-get and therefore resulted in an error.
2017-01-02 10:32:23 +01:00
Leonardo Galli
31b9ec1116 Create .travis.yml
Should enable automatically testing.
2017-01-02 10:27:59 +01:00
Leonardo Galli
ff6c3b70d3 Merge pull request #12 from fedoranimus/develop
Fix template references and 'movie' strings
2017-01-02 10:12:49 +01:00
Tim Turner
0fd0b31a60 Fix template references and 'movie' strings 2016-12-31 14:42:32 -05:00
Leonardo Galli
76e6ebc63c Merge branch 'develop' of https://github.com/galli-leo/Radarr into develop 2016-12-30 11:58:43 +01:00
Leonardo Galli
2ea35adb98 Fixed issue with Homepage movies not loading correctly. 2016-12-30 11:58:39 +01:00
Leonardo Galli
782f63f510 Update readme.md 2016-12-30 11:34:11 +01:00
Leonardo Galli
c874122fc0 Use different folder to store sqlite database. Fixes #10. 2016-12-30 11:25:25 +01:00
Leonardo Galli
2efda4933d Changed the name in the UI to Radarr. 2016-12-29 17:44:51 +01:00
Leonardo Galli
b7c70d750a Movies should now show on the main page. However, a lot has to be done to the detail controller before it is really going to work. 2016-12-29 17:38:54 +01:00
Leonardo Galli
5ebfac6cc8 First implementation of custom database table for movies.Some things are not yet working quite well (e.g. search clears when movies are added.). Also movies cannot yet be looked up! 2016-12-29 16:04:01 +01:00
Leonardo Galli
74ca6149e3 Merge branch 'develop' of https://github.com/galli-leo/Radarr into develop 2016-12-29 14:07:43 +01:00
Leonardo Galli
40d7590f80 First implementation of completely rewriting the way Radarr handles movies. Searching for new movies is now mostly feature complete. 2016-12-29 14:06:51 +01:00
Leonardo Galli
2cb27240dc Update readme.md 2016-12-28 22:37:06 +01:00
Leonardo Galli
837c2683df Update readme.md 2016-12-28 18:43:31 +01:00
Leonardo Galli
0b278c7db8 Searching for movie now works with downloading. They also get imported fine.
Additionally, a whole series (or movie in this case) can now be
downloaded manually.
Note: It probably won't start downloading missed releases. Only manually
clicking search for is working ATM.
2016-12-28 17:13:18 +01:00
Leonardo Galli
0b765d10fe Updated some text to say movies instead of series 2016-12-27 18:48:59 +01:00
Leonardo Galli
20dbdfb344 Added first iteration of adding movies.
Currently working:
- Searching for new Movies on IMDb (very hacky)
- Adding movie as a series with one season and episode (very hacky)
- Rarbg.to indexer. (somewhat hacky)

TODO:
- Tweak release specifications so that they do not cause exceptions.
- Add Movie struct so that searching for ones is not so hacky.
- rework movies UI.
2016-12-27 18:31:38 +01:00
Leonardo Galli
426448ed98 Update readme.md 2016-12-25 12:43:05 +01:00
938 changed files with 17771 additions and 15269 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

6
.gitignore vendored
View File

@@ -127,11 +127,13 @@ bin
obj
output/*
#Packages
Radarr_*/
Radarr_*.zip
#OS X metadata files
._*
.DS_Store
_start
_temp_*/**/*
src/.idea/

12
.travis.yml Normal file
View File

@@ -0,0 +1,12 @@
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
after_success:
- chmod +x package.sh
- ./package.sh

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,52 +0,0 @@
# Sonarr
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.
## Major Features Include:
* Support for major platforms: Windows, Linux, macOS, Raspberry Pi, etc.
* Automatically detects new episodes
* Can scan your existing library and download any missing episodes
* Can watch for better quality of the episodes you already have and do an automatic upgrade. *eg. from DVD to Blu-Ray*
* Automatic failed download handling will try another release if one fails
* Manual search so you can pick any release or to see why a release was not downloaded automatically
* Fully configurable episode renaming
* Full integration with SABnzbd and NZBGet
* Full integration with Kodi, Plex (notification, library update, metadata)
* Full support for specials and multi-episode releases
* And a beautiful UI
## Configuring Development Environment:
### Requirements
* Visual Studio 2015 (https://www.visualstudio.com/vs/)
* [Git](https://git-scm.com/downloads)
* [NodeJS](https://nodejs.org/en/download/)
### Setup
* Make sure all the required software mentioned above are installed.
* Clone the repository into your development machine. [*info*](https://help.github.com/articles/working-with-repositories)
* Grab the submodules `git submodule init && git submodule update`
* Install the required Node Packages `npm install`
* Start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
*Please note gulp must be running at all times while you are working with Sonarr client source files.*
### Development
* Open `NzbDrone.sln` in Visual Studio
* Make sure `NzbDrone.Console` is set as the startup project
### License
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
* Copyright 2010-2017
### Sponsors
* [JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools
* [ReSharper](http://www.jetbrains.com/resharper/)
* [WebStorm](http://www.jetbrains.com/webstorm/)
* [TeamCity](http://www.jetbrains.com/teamcity/)

View File

@@ -192,8 +192,8 @@ PackageOsxApp()
rm -rf $outputFolderOsxApp
mkdir $outputFolderOsxApp
cp -r ./osx/Sonarr.app $outputFolderOsxApp
cp -r $outputFolderOsx $outputFolderOsxApp/Sonarr.app/Contents/MacOS
cp -r ./osx/Radarr.app $outputFolderOsxApp
cp -r $outputFolderOsx $outputFolderOsxApp/Radarr.app/Contents/MacOS
echo "##teamcity[progressFinish 'Creating OS X App Package']"
}

View File

@@ -25,7 +25,7 @@ gulp.task('copyHtml', function () {
});
gulp.task('copyContent', function () {
return gulp.src([paths.src.content + '**/*.*', '!**/*.less', '!**/*.css'])
return gulp.src([paths.src.content + '**/*.*', '!**/*.less'])
.pipe(gulp.dest(paths.dest.content))
.pipe(livereload());
});

View File

@@ -5,7 +5,7 @@ var postcss = require('gulp-postcss');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('autoprefixer-core');
var livereload = require('gulp-livereload');
var cleancss = require('gulp-clean-css');
var print = require('gulp-print');
var paths = require('./paths');
var errorHandler = require('./errorHandler');
@@ -16,20 +16,19 @@ gulp.task('less', function() {
paths.src.content + 'bootstrap.less',
paths.src.content + 'theme.less',
paths.src.content + 'overrides.less',
paths.src.content + 'bootstrap.toggle-switch.css',
paths.src.content + 'fullcalendar.css',
paths.src.content + 'Messenger/messenger.css',
paths.src.content + 'Messenger/messenger.flat.css',
paths.src.root + 'Series/series.less',
paths.src.root + 'Activity/activity.less',
paths.src.root + 'AddSeries/addSeries.less',
paths.src.root + 'AddMovies/addMovies.less',
paths.src.root + 'Calendar/calendar.less',
paths.src.root + 'Cells/cells.less',
paths.src.root + 'ManualImport/manualimport.less',
paths.src.root + 'Settings/settings.less',
paths.src.root + 'System/Logs/logs.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)
@@ -37,13 +36,12 @@ gulp.task('less', function() {
.pipe(sourcemaps.init())
.pipe(less({
dumpLineNumbers : 'false',
compress : false,
yuicompress : false,
compress : true,
yuicompress : true,
ieCompat : true,
strictImports : true
}))
.pipe(postcss([ autoprefixer({ browsers: ['last 2 versions'] }) ]))
.pipe(cleancss())
.on('error', errorHandler.onError)
.pipe(sourcemaps.write(paths.dest.content))
.pipe(gulp.dest(paths.dest.content))

View File

@@ -20,7 +20,6 @@
"del": "1.2.0",
"gulp": "3.9.0",
"gulp-cached": "1.1.0",
"gulp-clean-css": "^3.0.4",
"gulp-concat": "2.6.0",
"gulp-declare": "0.3.0",
"gulp-handlebars": "3.0.1",

50
package.sh Normal file
View File

@@ -0,0 +1,50 @@
if [ $# -eq 0 ]; then
if [ "$TRAVIS_PULL_REQUEST" != false ]; then
echo "Need to supply version argument" && exit;
fi
fi
if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
VERSION="`date +%H:%M:%S`"
YEAR="`date +%Y`"
MONTH="`date +%m`"
DAY="`date +%d`"
else
VERSION=$1
fi
outputFolder='./_output'
outputFolderMono='./_output_mono'
outputFolderOsx='./_output_osx'
outputFolderOsxApp='./_output_osx_app'
tr -d "\r" < $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr > $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr2
rm $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr
chmod +x $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr2
mv $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr2 $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr >& error.log
cp -r $outputFolder/ Radarr_Windows_$VERSION
cp -r $outputFolderMono/ Radarr_Mono_$VERSION
cp -r $outputFolderOsxApp/ Radarr_OSX_$VERSION
zip -r Radarr_Windows_$VERSION.zip Radarr_Windows_$VERSION >& /dev/null
zip -r Radarr_Mono_$VERSION.zip Radarr_Mono_$VERSION >& /dev/null
zip -r Radarr_OSX_$VERSION.zip Radarr_OSX_$VERSION >& /dev/null
ftp -n ftp.leonardogalli.ch << END_SCRIPT
passive
quote USER $FTP_USER
quote PASS $FTP_PASS
mkdir builds
cd builds
mkdir $YEAR
cd $YEAR
mkdir $MONTH
cd $MONTH
mkdir $DAY
cd $DAY
binary
put Radarr_Windows_$VERSION.zip
put Radarr_Mono_$VERSION.zip
put Radarr_OSX_$VERSION.zip
quit
END_SCRIPT

71
readme.md Normal file
View File

@@ -0,0 +1,71 @@
# Radarr [![Build Status](https://travis-ci.org/galli-leo/Radarr.svg?branch=develop)](https://travis-ci.org/galli-leo/Radarr)#
This fork of Sonarr aims to turn it into something like Couchpotato.
## Currently working:
* Adding new movies
* 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)
* QBittorrent download client (Other clients are coming)
## Planned Features:
* Scanning PreDB to know when a new release is available.
* Fixing the other Indexers and download clients.
* Fixing how movies are parsed.
* Fixing movie import.
* Importing of Sonarr config.
* New TorrentPotato Indexer.
## Download
The latest precompiled binary versions can be found here: https://github.com/galli-leo/Radarr/releases.
For more up to date versions (but also sometimes broken), daily builds can be found here: https://leonardogalli.ch/radarr/builds/.
## Major Features Include: ##
* Support for major platforms: Windows, Linux, OSX, Raspberry Pi, etc.
* Automatically detects new episodes
* Can scan your existing library and download any missing episodes
* Can watch for better quality of the episodes you already have and do an automatic upgrade. *eg. from DVD to Blu-Ray*
* Automatic failed download handling will try another release if one fails
* Manual search so you can pick any release or to see why a release was not downloaded automatically
* Fully configurable episode renaming
* Full integration with SABNzbd and NzbGet
* Full integration with XBMC, Plex (notification, library update, metadata)
* Full support for specials and multi-episode releases
* And a beautiful UI
## Configuring Development Environment: ##
### Requirements ###
- Visual Studio 2015 [Free Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) or Mono
- [Git](http://git-scm.com/downloads)
- [NodeJS](http://nodejs.org/download/)
### Setup ###
- Make sure all the required software mentioned above are installed.
- Clone the repository into your development machine. [*info*](https://help.github.com/articles/working-with-repositories)
- Grab the submodules `git submodule init && git submodule update`
- install the required Node Packages `npm install`
- start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
*Please note gulp must be running at all times while you are working with Sonarr client source files.*
### Development ###
- Open `NzbDrone.sln` in Visual Studio or run the build.sh script, if Mono is installed.
- Make sure `NzbDrone.Console` is set as the startup project
### License ###
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
Copyright 2010-2016
### Sponsors ###
- [JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools
- [ReSharper](http://www.jetbrains.com/resharper/)
- [WebStorm](http://www.jetbrains.com/webstorm/)
- [TeamCity](http://www.jetbrains.com/teamcity/)

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -52,7 +52,8 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.4.3\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.3.11\lib\net40\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.4.3" targetFramework="net40" />
<package id="NLog" version="4.3.11" targetFramework="net40" />
</packages>

View File

@@ -43,9 +43,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@@ -281,4 +281,4 @@
<Target Name="BeforeBuild">
</Target>
-->
</Project>
</Project>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
</packages>

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Owin.Infrastructure;
namespace Microsoft.AspNet.SignalR.Owin

View File

@@ -42,17 +42,17 @@
<HintPath>..\packages\NBuilder.4.0.0\lib\net40\FizzWare.NBuilder.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FluentAssertions, Version=4.19.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.dll</HintPath>
<Reference Include="FluentAssertions, Version=4.18.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.18.0\lib\net40\FluentAssertions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FluentAssertions.Core, Version=4.19.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.Core.dll</HintPath>
<Reference Include="FluentAssertions.Core, Version=4.18.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.18.0\lib\net40\FluentAssertions.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Moq, Version=4.2.1510.2205, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
<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 Include="System" />
<Reference Include="System.Core" />

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="4.19.0" targetFramework="net40" />
<package id="FluentAssertions" version="4.18.0" targetFramework="net40" />
<package id="Moq" version="4.0.10827" />
<package id="NBuilder" version="4.0.0" targetFramework="net40" />
<package id="NUnit" version="3.6.0" targetFramework="net40" />
<package id="NUnit" version="3.5.0" targetFramework="net40" />
</packages>

View File

@@ -33,12 +33,12 @@ namespace NzbDrone.Api.Authentication
{
if (_configFileProvider.AuthenticationMethod == AuthenticationType.Forms)
{
RegisterFormsAuth(pipelines);
RegisterFormsAuth(pipelines);
}
else if (_configFileProvider.AuthenticationMethod == AuthenticationType.Basic)
{
pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration(_authenticationService, "Sonarr"));
pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration(_authenticationService, "Sonarr"));
}
pipelines.BeforeRequest.AddItemToEndOfPipeline((Func<NancyContext, Response>) RequiresAuthentication);
@@ -64,13 +64,10 @@ namespace NzbDrone.Api.Authentication
new DefaultHmacProvider(new PassphraseKeyGenerator(_configService.HmacPassphrase, Encoding.ASCII.GetBytes(_configService.HmacSalt)))
);
FormsAuthentication.FormsAuthenticationCookieName = "_ncfa_sonarr";
FormsAuthentication.Enable(pipelines, new FormsAuthenticationConfiguration
{
RedirectUrl = _configFileProvider.UrlBase + "/login",
UserMapper = _authenticationService,
Path = _configFileProvider.UrlBase,
CryptographyConfiguration = cryptographyConfiguration
});
}

View File

@@ -37,7 +37,6 @@ namespace NzbDrone.Api.Calendar
var end = DateTime.Today.AddDays(futureDays);
var unmonitored = false;
var premiersOnly = false;
var asAllDay = false;
var tags = new List<int>();
// TODO: Remove start/end parameters in v3, they don't work well for iCal
@@ -47,7 +46,6 @@ namespace NzbDrone.Api.Calendar
var queryFutureDays = Request.Query.FutureDays;
var queryUnmonitored = Request.Query.Unmonitored;
var queryPremiersOnly = Request.Query.PremiersOnly;
var queryAsAllDay = Request.Query.AsAllDay;
var queryTags = Request.Query.Tags;
if (queryStart.HasValue) start = DateTime.Parse(queryStart.Value);
@@ -75,11 +73,6 @@ namespace NzbDrone.Api.Calendar
premiersOnly = bool.Parse(queryPremiersOnly.Value);
}
if (queryAsAllDay.HasValue)
{
asAllDay = bool.Parse(queryAsAllDay.Value);
}
if (queryTags.HasValue)
{
var tagInput = (string)queryTags.Value.ToString();
@@ -109,19 +102,11 @@ namespace NzbDrone.Api.Calendar
var occurrence = calendar.Create<Event>();
occurrence.Uid = "NzbDrone_episode_" + episode.Id;
occurrence.Status = episode.HasFile ? EventStatus.Confirmed : EventStatus.Tentative;
occurrence.Start = new CalDateTime(episode.AirDateUtc.Value) { HasTime = true };
occurrence.End = new CalDateTime(episode.AirDateUtc.Value.AddMinutes(episode.Series.Runtime)) { HasTime = true };
occurrence.Description = episode.Overview;
occurrence.Categories = new List<string>() { episode.Series.Network };
if (asAllDay)
{
occurrence.Start = new CalDateTime(episode.AirDateUtc.Value) { HasTime = false };
}
else
{
occurrence.Start = new CalDateTime(episode.AirDateUtc.Value) { HasTime = true };
occurrence.End = new CalDateTime(episode.AirDateUtc.Value.AddMinutes(episode.Series.Runtime)) { HasTime = true };
}
switch (episode.Series.SeriesType)
{
case SeriesTypes.Daily:

View File

@@ -73,14 +73,14 @@ namespace NzbDrone.Api.ClientSchema
if (propertyInfo.PropertyType == typeof(int))
{
var value = field.Value.ToString().ParseInt32();
propertyInfo.SetValue(target, value ?? 0, null);
var value = Convert.ToInt32(field.Value);
propertyInfo.SetValue(target, value, null);
}
else if (propertyInfo.PropertyType == typeof(long))
{
var value = field.Value.ToString().ParseInt64();
propertyInfo.SetValue(target, value ?? 0, null);
var value = Convert.ToInt64(field.Value);
propertyInfo.SetValue(target, value, null);
}
else if (propertyInfo.PropertyType == typeof(int?))

View File

@@ -20,7 +20,6 @@ namespace NzbDrone.Api.Config
public bool SkipFreeSpaceCheckWhenImporting { get; set; }
public bool CopyUsingHardlinks { get; set; }
public bool ImportExtraFiles { get; set; }
public string ExtraFileExtensions { get; set; }
public bool EnableMediaInfo { get; set; }
}
@@ -45,7 +44,6 @@ namespace NzbDrone.Api.Config
SkipFreeSpaceCheckWhenImporting = model.SkipFreeSpaceCheckWhenImporting,
CopyUsingHardlinks = model.CopyUsingHardlinks,
ImportExtraFiles = model.ImportExtraFiles,
ExtraFileExtensions = model.ExtraFileExtensions,
EnableMediaInfo = model.EnableMediaInfo
};

View File

@@ -1,13 +1,14 @@
using System.Collections.Generic;
using System.IO;
using NLog;
using NzbDrone.Api.REST;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Exceptions;
using NzbDrone.SignalR;
using HttpStatusCode = System.Net.HttpStatusCode;
namespace NzbDrone.Api.EpisodeFiles
{
@@ -15,21 +16,24 @@ namespace NzbDrone.Api.EpisodeFiles
IHandle<EpisodeFileAddedEvent>
{
private readonly IMediaFileService _mediaFileService;
private readonly IDeleteMediaFiles _mediaFileDeletionService;
private readonly IRecycleBinProvider _recycleBinProvider;
private readonly ISeriesService _seriesService;
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
private readonly Logger _logger;
public EpisodeFileModule(IBroadcastSignalRMessage signalRBroadcaster,
IMediaFileService mediaFileService,
IDeleteMediaFiles mediaFileDeletionService,
IRecycleBinProvider recycleBinProvider,
ISeriesService seriesService,
IQualityUpgradableSpecification qualityUpgradableSpecification)
IQualityUpgradableSpecification qualityUpgradableSpecification,
Logger logger)
: base(signalRBroadcaster)
{
_mediaFileService = mediaFileService;
_mediaFileDeletionService = mediaFileDeletionService;
_recycleBinProvider = recycleBinProvider;
_seriesService = seriesService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
_logger = logger;
GetResourceById = GetEpisodeFile;
GetResourceAll = GetEpisodeFiles;
UpdateResource = SetQuality;
@@ -68,15 +72,12 @@ namespace NzbDrone.Api.EpisodeFiles
private void DeleteEpisodeFile(int id)
{
var episodeFile = _mediaFileService.Get(id);
if (episodeFile == null)
{
throw new NzbDroneClientException(HttpStatusCode.NotFound, "Episode file not found");
}
var series = _seriesService.GetSeries(episodeFile.SeriesId);
var fullPath = Path.Combine(series.Path, episodeFile.RelativePath);
_mediaFileDeletionService.DeleteEpisodeFile(series, episodeFile);
_logger.Info("Deleting episode file: {0}", fullPath);
_recycleBinProvider.DeleteFile(fullPath);
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
}
public void Handle(EpisodeFileAddedEvent message)

View File

@@ -3,6 +3,7 @@ using NzbDrone.Api.REST;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.SignalR;
using Nancy;
namespace NzbDrone.Api.Episodes
{

View File

@@ -14,7 +14,7 @@ namespace NzbDrone.Api.Episodes
{
public abstract class EpisodeModuleWithSignalR : NzbDroneRestModuleWithSignalR<EpisodeResource, Episode>,
IHandle<EpisodeGrabbedEvent>,
IHandle<EpisodeImportedEvent>
IHandle<EpisodeDownloadedEvent>
{
protected readonly IEpisodeService _episodeService;
protected readonly ISeriesService _seriesService;
@@ -115,14 +115,9 @@ namespace NzbDrone.Api.Episodes
}
}
public void Handle(EpisodeImportedEvent message)
public void Handle(EpisodeDownloadedEvent message)
{
if (!message.NewDownload)
{
return;
}
foreach (var episode in message.EpisodeInfo.Episodes)
foreach (var episode in message.Episode.Episodes)
{
BroadcastResourceChange(ModelAction.Updated, episode.Id);
}

View File

@@ -63,16 +63,18 @@ namespace NzbDrone.Api.ErrorManagement
}.AsResponse(HttpStatusCode.Conflict);
}
_logger.Error(sqLiteException, "[{0} {1}]", context.Request.Method, context.Request.Path);
}
var sqlErrorMessage = string.Format("[{0} {1}]", context.Request.Method, context.Request.Path);
_logger.Fatal(exception, "Request Failed. {0} {1}", context.Request.Method, context.Request.Path);
_logger.Error(sqLiteException, sqlErrorMessage);
}
_logger.Fatal(exception, "Request Failed");
return new ErrorModel
{
Message = exception.Message,
Description = exception.ToString()
}.AsResponse(HttpStatusCode.InternalServerError);
{
Message = exception.Message,
Description = exception.ToString()
}.AsResponse(HttpStatusCode.InternalServerError);
}
}
}

View File

@@ -39,7 +39,7 @@ namespace NzbDrone.Api.Extensions.Pipelines
context.Items["ApiRequestStartTime"] = DateTime.UtcNow;
var reqPath = GetRequestPathAndQuery(context.Request);
_loggerHttp.Trace("Req: {0} [{1}] {2}", id, context.Request.Method, reqPath);
return null;
@@ -80,7 +80,7 @@ namespace NzbDrone.Api.Extensions.Pipelines
{
if (request.Url.Query.IsNotNullOrWhiteSpace())
{
return string.Concat(request.Url.Path, request.Url.Query);
return string.Concat(request.Url.Path, "?", request.Url.Query);
}
else
{
@@ -88,4 +88,4 @@ namespace NzbDrone.Api.Extensions.Pipelines
}
}
}
}
}

View File

@@ -1,46 +0,0 @@
using System;
using Nancy;
using Nancy.Bootstrapper;
using Nancy.Responses;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Extensions.Pipelines
{
public class UrlBasePipeline : IRegisterNancyPipeline
{
private readonly string _urlBase;
public UrlBasePipeline(IConfigFileProvider configFileProvider)
{
_urlBase = configFileProvider.UrlBase;
}
public int Order => 99;
public void Register(IPipelines pipelines)
{
if (_urlBase.IsNotNullOrWhiteSpace())
{
pipelines.BeforeRequest.AddItemToStartOfPipeline((Func<NancyContext, Response>) Handle);
}
}
private Response Handle(NancyContext context)
{
var basePath = context.Request.Url.BasePath;
if (basePath.IsNullOrWhiteSpace())
{
return new RedirectResponse($"{_urlBase}{context.Request.Path}{context.Request.Url.Query}");
}
if (_urlBase != basePath)
{
return new NotFoundResponse();
}
return null;
}
}
}

View File

@@ -14,7 +14,7 @@ namespace NzbDrone.Api.Frontend
{
public bool IsCacheable(NancyContext context)
{
if (!RuntimeInfo.IsProduction)
if (!RuntimeInfoBase.IsProduction)
{
return false;
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Text.RegularExpressions;
using Nancy;
@@ -17,7 +17,7 @@ namespace NzbDrone.Api.Frontend.Mappers
private readonly IAnalyticsService _analyticsService;
private readonly Func<ICacheBreakerProvider> _cacheBreakProviderFactory;
private readonly string _indexPath;
private static readonly Regex ReplaceRegex = new Regex(@"(?:(?<attribute>href|src)=\"")(?<path>.*?(?<extension>css|js|png|ico|ics|svg))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex ReplaceRegex = new Regex(@"(?:(?<attribute>href|src)=\"")(?<path>.*?(?<extension>css|js|png|ico|ics))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static string API_KEY;
private static string URL_BASE;
@@ -74,7 +74,7 @@ namespace NzbDrone.Api.Frontend.Mappers
private string GetIndexText()
{
if (RuntimeInfo.IsProduction && _generatedContent != null)
if (RuntimeInfoBase.IsProduction && _generatedContent != null)
{
return _generatedContent;
}
@@ -106,7 +106,7 @@ namespace NzbDrone.Api.Frontend.Mappers
text = text.Replace("APP_BRANCH", _configFileProvider.Branch.ToLower());
text = text.Replace("APP_ANALYTICS", _analyticsService.IsEnabled.ToString().ToLowerInvariant());
text = text.Replace("URL_BASE", URL_BASE);
text = text.Replace("PRODUCTION", RuntimeInfo.IsProduction.ToString().ToLowerInvariant());
text = text.Replace("PRODUCTION", RuntimeInfoBase.IsProduction.ToString().ToLowerInvariant());
_generatedContent = text;

View File

@@ -67,7 +67,7 @@ namespace NzbDrone.Api.Frontend.Mappers
private string GetLoginText()
{
if (RuntimeInfo.IsProduction && _generatedContent != null)
if (RuntimeInfoBase.IsProduction && _generatedContent != null)
{
return _generatedContent;
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using NLog;
using Nancy;
@@ -21,7 +21,7 @@ namespace NzbDrone.Api.Frontend.Mappers
_diskProvider = diskProvider;
_logger = logger;
if (!RuntimeInfo.IsProduction)
if (!RuntimeInfoBase.IsProduction)
{
_caseSensitive = StringComparison.OrdinalIgnoreCase;
}
@@ -38,7 +38,7 @@ namespace NzbDrone.Api.Frontend.Mappers
if (_diskProvider.FileExists(filePath, _caseSensitive))
{
var response = new StreamResponse(() => GetContentStream(filePath), MimeTypes.GetMimeType(filePath));
return new MaterialisingResponse(response);
return response;
}
_logger.Warn("File {0} not found", filePath);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Nancy.Responses;
@@ -38,6 +38,20 @@ namespace NzbDrone.Api.Frontend
return new NotFoundResponse();
}
//Redirect to the subfolder if the request went to the base URL
if (path.Equals("/"))
{
var urlBase = _configFileProvider.UrlBase;
if (!string.IsNullOrEmpty(urlBase))
{
if (Request.Url.BasePath != urlBase)
{
return new RedirectResponse(urlBase + "/");
}
}
}
var mapper = _requestMappers.SingleOrDefault(m => m.CanHandle(path));
if (mapper != null)

View File

@@ -3,6 +3,7 @@ using Nancy;
using NzbDrone.Api.Episodes;
using NzbDrone.Api.Extensions;
using NzbDrone.Api.Series;
using NzbDrone.Api.Movie;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
@@ -34,12 +35,18 @@ namespace NzbDrone.Api.History
resource.Series = model.Series.ToResource();
resource.Episode = model.Episode.ToResource();
resource.Movie = model.Movie.ToResource();
if (model.Series != null)
{
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;
}
@@ -47,6 +54,8 @@ namespace NzbDrone.Api.History
{
var episodeId = Request.Query.EpisodeId;
var movieId = Request.Query.MovieId;
var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, Core.History.History>("date", SortDirection.Descending);
if (pagingResource.FilterKey == "eventType")
@@ -61,6 +70,12 @@ namespace NzbDrone.Api.History
pagingSpec.FilterExpression = h => h.EpisodeId == i;
}
if (movieId.HasValue)
{
int i = (int)movieId;
pagingSpec.FilterExpression = h => h.MovieId == i;
}
return ApplyToPage(_historyService.Paged, pagingSpec, MapToResource);
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using NzbDrone.Api.Episodes;
using NzbDrone.Api.REST;
using NzbDrone.Api.Series;
using NzbDrone.Api.Movie;
using NzbDrone.Core.History;
using NzbDrone.Core.Qualities;
@@ -12,6 +13,7 @@ namespace NzbDrone.Api.History
public class HistoryResource : RestResource
{
public int EpisodeId { get; set; }
public int MovieId { get; set; }
public int SeriesId { get; set; }
public string SourceTitle { get; set; }
public QualityModel Quality { get; set; }
@@ -22,7 +24,7 @@ namespace NzbDrone.Api.History
public HistoryEventType EventType { get; set; }
public Dictionary<string, string> Data { get; set; }
public MovieResource Movie { get; set; }
public EpisodeResource Episode { get; set; }
public SeriesResource Series { get; set; }
}
@@ -39,6 +41,7 @@ namespace NzbDrone.Api.History
EpisodeId = model.EpisodeId,
SeriesId = model.SeriesId,
MovieId = model.MovieId,
SourceTitle = model.SourceTitle,
Quality = model.Quality,
//QualityCutoffNotMet

View File

@@ -26,6 +26,7 @@ namespace NzbDrone.Api.Indexers
private readonly Logger _logger;
private readonly ICached<RemoteEpisode> _remoteEpisodeCache;
private readonly ICached<RemoteMovie> _remoteMovieCache;
public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
ISearchForNzb nzbSearchService,
@@ -49,17 +50,37 @@ namespace NzbDrone.Api.Indexers
PostValidator.RuleFor(s => s.Guid).NotEmpty();
_remoteEpisodeCache = cacheManager.GetCache<RemoteEpisode>(GetType(), "remoteEpisodes");
_remoteMovieCache = cacheManager.GetCache<RemoteMovie>(GetType(), "remoteMovies");
}
private Response DownloadRelease(ReleaseResource release)
{
var remoteEpisode = _remoteEpisodeCache.Find(GetCacheKey(release));
var remoteEpisode = _remoteEpisodeCache.Find(release.Guid);
if (remoteEpisode == null)
{
_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
@@ -82,6 +103,11 @@ namespace NzbDrone.Api.Indexers
return GetEpisodeReleases(Request.Query.episodeId);
}
if (Request.Query.movieId != null)
{
return GetMovieReleases(Request.Query.movieId);
}
return GetRss();
}
@@ -96,7 +122,28 @@ namespace NzbDrone.Api.Indexers
}
catch (Exception ex)
{
_logger.Error(ex, "Episode search failed");
_logger.Error(ex, "Episode search failed: " + ex.Message);
}
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>();
@@ -113,14 +160,16 @@ namespace NzbDrone.Api.Indexers
protected override ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
{
var resource = base.MapDecision(decision, initialWeight);
_remoteEpisodeCache.Set(GetCacheKey(resource), decision.RemoteEpisode, TimeSpan.FromMinutes(30));
return resource;
}
private string GetCacheKey(ReleaseResource resource)
{
return string.Concat(resource.IndexerId, "_", resource.Guid);
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);
}
}
}

View File

@@ -24,6 +24,7 @@ namespace NzbDrone.Api.Indexers
public string Indexer { get; set; }
public string ReleaseGroup { get; set; }
public string ReleaseHash { get; set; }
public string Edition { get; set; }
public string Title { get; set; }
public bool FullSeason { get; set; }
public int SeasonNumber { get; set; }
@@ -86,6 +87,60 @@ namespace NzbDrone.Api.Indexers
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;
var parsedMovieInfo = model.RemoteMovie.ParsedMovieInfo;
return new ReleaseResource
{
Guid = releaseInfo.Guid,
Quality = parsedMovieInfo.Quality,
//QualityWeight
Age = releaseInfo.Age,
AgeHours = releaseInfo.AgeHours,
AgeMinutes = releaseInfo.AgeMinutes,
Size = releaseInfo.Size,
IndexerId = releaseInfo.IndexerId,
Indexer = releaseInfo.Indexer,
ReleaseGroup = parsedMovieInfo.ReleaseGroup,
ReleaseHash = parsedMovieInfo.ReleaseHash,
Title = releaseInfo.Title,
FullSeason = parsedMovieInfo.FullSeason,
SeasonNumber = parsedMovieInfo.SeasonNumber,
Language = parsedMovieInfo.Language,
AirDate = "",
SeriesTitle = parsedMovieInfo.MovieTitle,
EpisodeNumbers = new int[0],
AbsoluteEpisodeNumbers = new int[0],
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,
Edition = parsedMovieInfo.Edition,
IsDaily = false,
IsAbsoluteNumbering = false,
IsPossibleSpecialEpisode = false,
Special = parsedMovieInfo.Special,
};
}
// 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
@@ -119,7 +174,7 @@ namespace NzbDrone.Api.Indexers
CommentUrl = releaseInfo.CommentUrl,
DownloadUrl = releaseInfo.DownloadUrl,
InfoUrl = releaseInfo.InfoUrl,
DownloadAllowed = remoteEpisode.DownloadAllowed,
DownloadAllowed = downloadAllowed,
//ReleaseWeight
MagnetUrl = torrentInfo.MagnetUrl,

View File

@@ -24,9 +24,9 @@ namespace NzbDrone.Api
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
Logger.Info("Starting Web Server");
Logger.Info("Starting NzbDrone API");
if (RuntimeInfo.IsProduction)
if (RuntimeInfoBase.IsProduction)
{
DiagnosticsHook.Disable(pipelines);
}

View File

@@ -41,17 +41,20 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="antlr.runtime, Version=2.7.6.2, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.32\lib\net40\antlr.runtime.dll</HintPath>
<HintPath>..\packages\Ical.Net.2.2.25\lib\net40\antlr.runtime.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FluentValidation, Version=6.2.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\FluentValidation.6.2.1.0\lib\portable-net40+sl50+wp80+win8+wpa81\FluentValidation.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Ical.Net, Version=2.1.0.18776, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.32\lib\net40\Ical.Net.dll</HintPath>
<Reference Include="Ical.Net, Version=2.1.0.30332, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.25\lib\net40\Ical.Net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Ical.Net.Collections, Version=2.1.0.18775, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.32\lib\net40\Ical.Net.Collections.dll</HintPath>
<Reference Include="Ical.Net.Collections, Version=2.1.0.30331, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.25\lib\net40\Ical.Net.Collections.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nancy, Version=1.4.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.1.4.3\lib\net40\Nancy.dll</HintPath>
@@ -65,15 +68,17 @@
<HintPath>..\packages\Nancy.Authentication.Forms.1.4.1\lib\net40\Nancy.Authentication.Forms.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.4.3\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.3.11\lib\net40\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NodaTime, Version=1.3.0.0, Culture=neutral, PublicKeyToken=4226afe0d9b296d1, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.32\lib\net40\NodaTime.dll</HintPath>
<HintPath>..\packages\Ical.Net.2.2.25\lib\net40\NodaTime.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@@ -106,7 +111,6 @@
<Compile Include="Commands\CommandResource.cs" />
<Compile Include="Extensions\AccessControlHeaders.cs" />
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
<Compile Include="Extensions\Pipelines\UrlBasePipeline.cs" />
<Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" />
<Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" />
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
@@ -227,8 +231,11 @@
<Compile Include="Series\SeasonResource.cs" />
<Compile Include="SeasonPass\SeasonPassModule.cs" />
<Compile Include="Series\SeriesEditorModule.cs" />
<Compile Include="Series\MovieLookupModule.cs" />
<Compile Include="Series\SeriesLookupModule.cs" />
<Compile Include="Series\MovieModule.cs" />
<Compile Include="Series\SeriesModule.cs" />
<Compile Include="Series\MovieResource.cs" />
<Compile Include="Series\SeriesResource.cs" />
<Compile Include="Series\SeasonStatisticsResource.cs" />
<Compile Include="System\Backup\BackupModule.cs" />

View File

@@ -4,6 +4,7 @@ using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
using NzbDrone.Api.Series;
using NzbDrone.Api.Episodes;
using NzbDrone.Api.Movie;
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Indexers;
using System.Linq;
@@ -14,6 +15,7 @@ namespace NzbDrone.Api.Queue
{
public SeriesResource Series { get; set; }
public EpisodeResource Episode { get; set; }
public MovieResource Movie { get; set; }
public QualityModel Quality { get; set; }
public decimal Size { get; set; }
public string Title { get; set; }
@@ -49,7 +51,8 @@ namespace NzbDrone.Api.Queue
TrackedDownloadStatus = model.TrackedDownloadStatus,
StatusMessages = model.StatusMessages,
DownloadId = model.DownloadId,
Protocol = model.Protocol
Protocol = model.Protocol,
Movie = model.Movie.ToResource()
};
}

View File

@@ -0,0 +1,44 @@
using System.Collections.Generic;
using Nancy;
using NzbDrone.Api.Extensions;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using System.Linq;
namespace NzbDrone.Api.Movie
{
public class MovieLookupModule : NzbDroneRestModule<MovieResource>
{
private readonly ISearchForNewMovie _searchProxy;
public MovieLookupModule(ISearchForNewMovie searchProxy)
: base("/movies/lookup")
{
_searchProxy = searchProxy;
Get["/"] = x => Search();
}
private Response Search()
{
var imdbResults = _searchProxy.SearchForNewMovie((string)Request.Query.term);
return MapToResource(imdbResults).AsResponse();
}
private static IEnumerable<MovieResource> MapToResource(IEnumerable<Core.Tv.Movie> movies)
{
foreach (var currentSeries in movies)
{
var resource = currentSeries.ToResource();
var poster = currentSeries.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
if (poster != null)
{
resource.RemotePoster = poster.Url;
}
yield return resource;
}
}
}
}

View File

@@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.MovieStats;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events;
using NzbDrone.Core.Validation.Paths;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Validation;
using NzbDrone.SignalR;
namespace NzbDrone.Api.Movie
{
public class MovieModule : NzbDroneRestModuleWithSignalR<MovieResource, Core.Tv.Movie>,
IHandle<EpisodeImportedEvent>,
IHandle<EpisodeFileDeletedEvent>,
IHandle<MovieUpdatedEvent>,
IHandle<MovieEditedEvent>,
IHandle<MovieDeletedEvent>,
IHandle<MovieRenamedEvent>,
IHandle<MediaCoversUpdatedEvent>
{
private readonly IMovieService _moviesService;
private readonly IMovieStatisticsService _moviesStatisticsService;
private readonly IMapCoversToLocal _coverMapper;
public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
IMovieService moviesService,
IMovieStatisticsService moviesStatisticsService,
ISceneMappingService sceneMappingService,
IMapCoversToLocal coverMapper,
RootFolderValidator rootFolderValidator,
MoviePathValidator moviesPathValidator,
MovieExistsValidator moviesExistsValidator,
DroneFactoryValidator droneFactoryValidator,
MovieAncestorValidator moviesAncestorValidator,
ProfileExistsValidator profileExistsValidator
)
: base(signalRBroadcaster)
{
_moviesService = moviesService;
_moviesStatisticsService = moviesStatisticsService;
_coverMapper = coverMapper;
GetResourceAll = AllMovie;
GetResourceById = GetMovie;
CreateResource = AddMovie;
UpdateResource = UpdateMovie;
DeleteResource = DeleteMovie;
Validation.RuleBuilderExtensions.ValidId(SharedValidator.RuleFor(s => s.ProfileId));
SharedValidator.RuleFor(s => s.Path)
.Cascade(CascadeMode.StopOnFirstFailure)
.IsValidPath()
.SetValidator(rootFolderValidator)
.SetValidator(moviesPathValidator)
.SetValidator(droneFactoryValidator)
.SetValidator(moviesAncestorValidator)
.When(s => !s.Path.IsNullOrWhiteSpace());
SharedValidator.RuleFor(s => s.ProfileId).SetValidator(profileExistsValidator);
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.Title).NotEmpty();
PostValidator.RuleFor(s => s.TmdbId).NotNull().NotEmpty().SetValidator(moviesExistsValidator);
PutValidator.RuleFor(s => s.Path).IsValidPath();
}
private MovieResource GetMovie(int id)
{
var movies = _moviesService.GetMovie(id);
return MapToResource(movies);
}
private MovieResource MapToResource(Core.Tv.Movie movies)
{
if (movies == null) return null;
var resource = movies.ToResource();
MapCoversToLocal(resource);
FetchAndLinkMovieStatistics(resource);
PopulateAlternateTitles(resource);
return resource;
}
private List<MovieResource> AllMovie()
{
var moviesStats = _moviesStatisticsService.MovieStatistics();
var moviesResources = _moviesService.GetAllMovies().ToResource();
MapCoversToLocal(moviesResources.ToArray());
LinkMovieStatistics(moviesResources, moviesStats);
PopulateAlternateTitles(moviesResources);
return moviesResources;
}
private int AddMovie(MovieResource moviesResource)
{
var model = moviesResource.ToModel();
return _moviesService.AddMovie(model).Id;
}
private void UpdateMovie(MovieResource moviesResource)
{
var model = moviesResource.ToModel(_moviesService.GetMovie(moviesResource.Id));
_moviesService.UpdateMovie(model);
BroadcastResourceChange(ModelAction.Updated, moviesResource);
}
private void DeleteMovie(int id)
{
var deleteFiles = false;
var deleteFilesQuery = Request.Query.deleteFiles;
if (deleteFilesQuery.HasValue)
{
deleteFiles = Convert.ToBoolean(deleteFilesQuery.Value);
}
_moviesService.DeleteMovie(id, deleteFiles);
}
private void MapCoversToLocal(params MovieResource[] movies)
{
foreach (var moviesResource in movies)
{
_coverMapper.ConvertToLocalUrls(moviesResource.Id, moviesResource.Images);
}
}
private void FetchAndLinkMovieStatistics(MovieResource resource)
{
LinkMovieStatistics(resource, _moviesStatisticsService.MovieStatistics(resource.Id));
}
private void LinkMovieStatistics(List<MovieResource> resources, List<MovieStatistics> moviesStatistics)
{
var dictMovieStats = moviesStatistics.ToDictionary(v => v.MovieId);
foreach (var movies in resources)
{
var stats = dictMovieStats.GetValueOrDefault(movies.Id);
if (stats == null) continue;
LinkMovieStatistics(movies, stats);
}
}
private void LinkMovieStatistics(MovieResource resource, MovieStatistics moviesStatistics)
{
resource.SizeOnDisk = moviesStatistics.SizeOnDisk;
}
private void PopulateAlternateTitles(List<MovieResource> resources)
{
foreach (var resource in resources)
{
PopulateAlternateTitles(resource);
}
}
private void PopulateAlternateTitles(MovieResource resource)
{
//var mappings = null;//_sceneMappingService.FindByTvdbId(resource.TvdbId);
//if (mappings == null) return;
//resource.AlternateTitles = mappings.Select(v => new AlternateTitleResource { Title = v.Title, SeasonNumber = v.SeasonNumber, SceneSeasonNumber = v.SceneSeasonNumber }).ToList();
}
public void Handle(EpisodeImportedEvent message)
{
//BroadcastResourceChange(ModelAction.Updated, message.ImportedEpisode.MovieId);
}
public void Handle(EpisodeFileDeletedEvent message)
{
if (message.Reason == DeleteMediaFileReason.Upgrade) return;
//BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.MovieId);
}
public void Handle(MovieUpdatedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
}
public void Handle(MovieEditedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
}
public void Handle(MovieDeletedEvent message)
{
BroadcastResourceChange(ModelAction.Deleted, message.Movie.ToResource());
}
public void Handle(MovieRenamedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
}
public void Handle(MediaCoversUpdatedEvent message)
{
//BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
}
}
}

View File

@@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Api.REST;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.Tv;
using NzbDrone.Api.Series;
namespace NzbDrone.Api.Movie
{
public class MovieResource : RestResource
{
public MovieResource()
{
Monitored = true;
}
//Todo: Sorters should be done completely on the client
//Todo: Is there an easy way to keep IgnoreArticlesWhenSorting in sync between, Series, History, Missing?
//Todo: We should get the entire Profile instead of ID and Name separately
//View Only
public string Title { get; set; }
public List<AlternateTitleResource> AlternateTitles { get; set; }
public string SortTitle { get; set; }
public long? SizeOnDisk { get; set; }
public MovieStatusType Status { get; set; }
public string Overview { get; set; }
public DateTime? InCinemas { get; set; }
public List<MediaCover> Images { get; set; }
public string Website { get; set; }
public string RemotePoster { get; set; }
public int Year { get; set; }
//View & Edit
public string Path { get; set; }
public int ProfileId { get; set; }
//Editing Only
public bool Monitored { get; set; }
public int Runtime { get; set; }
public DateTime? LastInfoSync { get; set; }
public string CleanTitle { get; set; }
public string ImdbId { get; set; }
public int TmdbId { get; set; }
public string TitleSlug { get; set; }
public string RootFolderPath { get; set; }
public string Certification { get; set; }
public List<string> Genres { get; set; }
public HashSet<int> Tags { get; set; }
public DateTime Added { get; set; }
public AddMovieOptions AddOptions { get; set; }
public Ratings Ratings { get; set; }
public List<string> AlternativeTitles { get; set; }
//TODO: Add series statistics as a property of the series (instead of individual properties)
//Used to support legacy consumers
public int QualityProfileId
{
get
{
return ProfileId;
}
set
{
if (value > 0 && ProfileId == 0)
{
ProfileId = value;
}
}
}
}
public static class MovieResourceMapper
{
public static MovieResource ToResource(this Core.Tv.Movie model)
{
if (model == null) return null;
return new MovieResource
{
Id = model.Id,
TmdbId = model.TmdbId,
Title = model.Title,
//AlternateTitles
SortTitle = model.SortTitle,
InCinemas = model.InCinemas,
//TotalEpisodeCount
//EpisodeCount
//EpisodeFileCount
//SizeOnDisk
Status = model.Status,
Overview = model.Overview,
//NextAiring
//PreviousAiring
Images = model.Images,
Year = model.Year,
Path = model.Path,
ProfileId = model.ProfileId,
Monitored = model.Monitored,
Runtime = model.Runtime,
LastInfoSync = model.LastInfoSync,
CleanTitle = model.CleanTitle,
ImdbId = model.ImdbId,
TitleSlug = model.TitleSlug,
RootFolderPath = model.RootFolderPath,
Certification = model.Certification,
Website = model.Website,
Genres = model.Genres,
Tags = model.Tags,
Added = model.Added,
AddOptions = model.AddOptions,
AlternativeTitles = model.AlternativeTitles,
Ratings = model.Ratings
};
}
public static Core.Tv.Movie ToModel(this MovieResource resource)
{
if (resource == null) return null;
return new Core.Tv.Movie
{
Id = resource.Id,
TmdbId = resource.TmdbId,
Title = resource.Title,
//AlternateTitles
SortTitle = resource.SortTitle,
InCinemas = resource.InCinemas,
//TotalEpisodeCount
//EpisodeCount
//EpisodeFileCount
//SizeOnDisk
Overview = resource.Overview,
//NextAiring
//PreviousAiring
Images = resource.Images,
Year = resource.Year,
Path = resource.Path,
ProfileId = resource.ProfileId,
Monitored = resource.Monitored,
Runtime = resource.Runtime,
LastInfoSync = resource.LastInfoSync,
CleanTitle = resource.CleanTitle,
ImdbId = resource.ImdbId,
TitleSlug = resource.TitleSlug,
RootFolderPath = resource.RootFolderPath,
Certification = resource.Certification,
Website = resource.Website,
Genres = resource.Genres,
Tags = resource.Tags,
Added = resource.Added,
AddOptions = resource.AddOptions,
AlternativeTitles = resource.AlternativeTitles,
Ratings = resource.Ratings
};
}
public static Core.Tv.Movie ToModel(this MovieResource resource, Core.Tv.Movie movie)
{
movie.ImdbId = resource.ImdbId;
movie.TmdbId = resource.TmdbId;
movie.Path = resource.Path;
movie.ProfileId = resource.ProfileId;
movie.Monitored = resource.Monitored;
movie.RootFolderPath = resource.RootFolderPath;
movie.Tags = resource.Tags;
movie.AddOptions = resource.AddOptions;
return movie;
}
public static List<MovieResource> ToResource(this IEnumerable<Core.Tv.Movie> movies)
{
return movies.Select(ToResource).ToList();
}
}
}

View File

@@ -41,7 +41,7 @@ namespace NzbDrone.Api.Series
public static List<Season> ToModel(this IEnumerable<SeasonResource> resources)
{
return resources?.Select(ToModel).ToList() ?? new List<Season>();
return resources.Select(ToModel).ToList();
}
}
}

View File

@@ -29,14 +29,12 @@ namespace NzbDrone.Api.Series
{
private readonly ISeriesService _seriesService;
private readonly IAddSeriesService _addSeriesService;
private readonly ISeriesStatisticsService _seriesStatisticsService;
private readonly ISceneMappingService _sceneMappingService;
private readonly IMapCoversToLocal _coverMapper;
public SeriesModule(IBroadcastSignalRMessage signalRBroadcaster,
ISeriesService seriesService,
IAddSeriesService addSeriesService,
ISeriesStatisticsService seriesStatisticsService,
ISceneMappingService sceneMappingService,
IMapCoversToLocal coverMapper,
@@ -50,7 +48,6 @@ namespace NzbDrone.Api.Series
: base(signalRBroadcaster)
{
_seriesService = seriesService;
_addSeriesService = addSeriesService;
_seriesStatisticsService = seriesStatisticsService;
_sceneMappingService = sceneMappingService;
@@ -77,6 +74,7 @@ namespace NzbDrone.Api.Series
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.Title).NotEmpty();
PostValidator.RuleFor(s => s.TvdbId).GreaterThan(0).SetValidator(seriesExistsValidator);
PutValidator.RuleFor(s => s.Path).IsValidPath();
@@ -116,7 +114,7 @@ namespace NzbDrone.Api.Series
{
var model = seriesResource.ToModel();
return _addSeriesService.AddSeries(model).Id;
return _seriesService.AddSeries(model).Id;
}
private void UpdateSeries(SeriesResource seriesResource)
@@ -182,7 +180,7 @@ namespace NzbDrone.Api.Series
foreach (var season in resource.Seasons)
{
season.Statistics = dictSeasonStats.GetValueOrDefault(season.SeasonNumber).ToResource();
season.Statistics = SeasonStatisticsResourceMapper.ToResource(dictSeasonStats.GetValueOrDefault(season.SeasonNumber));
}
}
}

View File

@@ -207,9 +207,19 @@ namespace NzbDrone.Api.Series
public static Core.Tv.Series ToModel(this SeriesResource resource, Core.Tv.Series series)
{
var updatedSeries = resource.ToModel();
series.TvdbId = resource.TvdbId;
series.ApplyChanges(updatedSeries);
series.Seasons = resource.Seasons.ToModel();
series.Path = resource.Path;
series.ProfileId = resource.ProfileId;
series.SeasonFolder = resource.SeasonFolder;
series.Monitored = resource.Monitored;
series.SeriesType = resource.SeriesType;
series.RootFolderPath = resource.RootFolderPath;
series.Tags = resource.Tags;
series.AddOptions = resource.AddOptions;
return series;
}

View File

@@ -13,8 +13,6 @@ namespace NzbDrone.Api.System
{
private readonly IAppFolderInfo _appFolderInfo;
private readonly IRuntimeInfo _runtimeInfo;
private readonly IPlatformInfo _platformInfo;
private readonly IOsInfo _osInfo;
private readonly IRouteCacheProvider _routeCacheProvider;
private readonly IConfigFileProvider _configFileProvider;
private readonly IMainDatabase _database;
@@ -22,17 +20,14 @@ namespace NzbDrone.Api.System
public SystemModule(IAppFolderInfo appFolderInfo,
IRuntimeInfo runtimeInfo,
IPlatformInfo platformInfo,
IOsInfo osInfo,
IRouteCacheProvider routeCacheProvider,
IConfigFileProvider configFileProvider,
IMainDatabase database,
ILifecycleService lifecycleService) : base("system")
ILifecycleService lifecycleService)
: base("system")
{
_appFolderInfo = appFolderInfo;
_runtimeInfo = runtimeInfo;
_platformInfo = platformInfo;
_osInfo = osInfo;
_routeCacheProvider = routeCacheProvider;
_configFileProvider = configFileProvider;
_database = database;
@@ -46,29 +41,27 @@ namespace NzbDrone.Api.System
private Response GetStatus()
{
return new
{
Version = BuildInfo.Version.ToString(),
BuildTime = BuildInfo.BuildDateTime,
IsDebug = BuildInfo.IsDebug,
IsProduction = RuntimeInfo.IsProduction,
IsAdmin = _runtimeInfo.IsAdmin,
IsUserInteractive = RuntimeInfo.IsUserInteractive,
StartupPath = _appFolderInfo.StartUpFolder,
AppData = _appFolderInfo.GetAppDataPath(),
OsName = _osInfo.Name,
OsVersion = _osInfo.Version,
IsMonoRuntime = PlatformInfo.IsMono,
IsMono = PlatformInfo.IsMono,
IsLinux = OsInfo.IsLinux,
IsOsx = OsInfo.IsOsx,
IsWindows = OsInfo.IsWindows,
Branch = _configFileProvider.Branch,
Authentication = _configFileProvider.AuthenticationMethod,
SqliteVersion = _database.Version,
UrlBase = _configFileProvider.UrlBase,
RuntimeVersion = _platformInfo.Version,
RuntimeName = PlatformInfo.Platform
}.AsResponse();
{
Version = BuildInfo.Version.ToString(),
BuildTime = BuildInfo.BuildDateTime,
IsDebug = BuildInfo.IsDebug,
IsProduction = RuntimeInfoBase.IsProduction,
IsAdmin = _runtimeInfo.IsAdmin,
IsUserInteractive = RuntimeInfoBase.IsUserInteractive,
StartupPath = _appFolderInfo.StartUpFolder,
AppData = _appFolderInfo.GetAppDataPath(),
OsVersion = OsInfo.Version.ToString(),
IsMonoRuntime = OsInfo.IsMonoRuntime,
IsMono = OsInfo.IsNotWindows,
IsLinux = OsInfo.IsLinux,
IsOsx = OsInfo.IsOsx,
IsWindows = OsInfo.IsWindows,
Branch = _configFileProvider.Branch,
Authentication = _configFileProvider.AuthenticationMethod,
SqliteVersion = _database.Version,
UrlBase = _configFileProvider.UrlBase,
RuntimeVersion = _runtimeInfo.RuntimeVersion
}.AsResponse();
}
private Response GetRoutes()

View File

@@ -22,7 +22,7 @@ namespace NzbDrone.Api
/// </summary>
public static IEnumerable<Func<Assembly, bool>> DefaultAutoRegisterIgnoredAssemblies = new Func<Assembly, bool>[]
{
asm => !asm.FullName.StartsWith("Nancy.", StringComparison.InvariantCulture)
asm => !asm.FullName.StartsWith("Nancy.", StringComparison.InvariantCulture),
};
/// <summary>

View File

@@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" />

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentValidation" version="6.2.1.0" targetFramework="net40" />
<package id="Ical.Net" version="2.2.32" targetFramework="net40" />
<package id="Ical.Net" version="2.2.25" targetFramework="net40" />
<package id="Nancy" version="1.4.3" targetFramework="net40" />
<package id="Nancy.Authentication.Basic" version="1.4.1" targetFramework="net40" />
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net40" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
<package id="NLog" version="4.4.3" targetFramework="net40" />
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
<package id="NLog" version="4.3.11" targetFramework="net40" />
</packages>

View File

@@ -41,17 +41,21 @@
<HintPath>..\packages\NBuilder.4.0.0\lib\net40\FizzWare.NBuilder.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FluentAssertions, Version=4.19.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.dll</HintPath>
<Reference Include="FluentAssertions, Version=4.18.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.18.0\lib\net40\FluentAssertions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FluentAssertions.Core, Version=4.19.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.Core.dll</HintPath>
<Reference Include="FluentAssertions.Core, Version=4.18.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.18.0\lib\net40\FluentAssertions.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.4.3\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.3.11\lib\net40\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
<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 Include="System" />
<Reference Include="System.Core" />
@@ -108,8 +112,8 @@
xcopy /s /y "$(SolutionDir)\..\_output\NzbDrone.Windows.*" "$(TargetDir)"
</PostBuildEvent>
<PostBuildEvent Condition="('$(OS)' != 'Windows_NT')">
cp -rv $(SolutionDir)\..\_output\NzbDrone.Mono.* $(TargetDir) || true
cp -rv $(SolutionDir)\..\_output\NzbDrone.Windows.* $(TargetDir) || true
cp -rv $(SolutionDir)\..\_output\NzbDrone.Mono.* $(TargetDir)
cp -rv $(SolutionDir)\..\_output\NzbDrone.Windows.* $(TargetDir)
</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="4.19.0" targetFramework="net40" />
<package id="FluentAssertions" version="4.18.0" targetFramework="net40" />
<package id="Moq" version="4.0.10827" />
<package id="NBuilder" version="4.0.0" targetFramework="net40" />
<package id="NLog" version="4.4.3" targetFramework="net40" />
<package id="NUnit" version="3.6.0" targetFramework="net40" />
<package id="NLog" version="4.3.11" targetFramework="net40" />
<package id="NUnit" version="3.5.0" targetFramework="net40" />
</packages>

View File

@@ -40,7 +40,7 @@ namespace NzbDrone.Automation.Test
_runner.KillAll();
_runner.Start();
driver.Url = "http://localhost:8989";
driver.Url = "http://localhost:7878";
var page = new PageBase(driver);
page.WaitForNoSpinner();

View File

@@ -38,17 +38,21 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="FluentAssertions, Version=4.19.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.dll</HintPath>
<Reference Include="FluentAssertions, Version=4.18.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.18.0\lib\net40\FluentAssertions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FluentAssertions.Core, Version=4.19.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.Core.dll</HintPath>
<Reference Include="FluentAssertions.Core, Version=4.18.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.18.0\lib\net40\FluentAssertions.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.4.3\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.3.11\lib\net40\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
<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 Include="System" />
<Reference Include="System.Core" />
@@ -58,11 +62,13 @@
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="WebDriver, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Selenium.WebDriver.3.2.0\lib\net40\WebDriver.dll</HintPath>
<Reference Include="WebDriver, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Selenium.WebDriver.3.0.1\lib\net40\WebDriver.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="WebDriver.Support, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Selenium.Support.3.2.0\lib\net40\WebDriver.Support.dll</HintPath>
<Reference Include="WebDriver.Support, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Selenium.Support.3.0.1\lib\net40\WebDriver.Support.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>

View File

@@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" />

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="4.19.0" targetFramework="net40" />
<package id="NLog" version="4.4.3" targetFramework="net40" />
<package id="NUnit" version="3.6.0" targetFramework="net40" />
<package id="Selenium.Support" version="3.2.0" targetFramework="net40" />
<package id="Selenium.WebDriver" version="3.2.0" targetFramework="net40" />
<package id="FluentAssertions" version="4.18.0" targetFramework="net40" />
<package id="NLog" version="4.3.11" targetFramework="net40" />
<package id="NUnit" version="3.5.0" targetFramework="net40" />
<package id="Selenium.Support" version="3.0.1" targetFramework="net40" />
<package id="Selenium.WebDriver" version="3.0.1" targetFramework="net40" />
</packages>

View File

@@ -49,7 +49,7 @@ namespace NzbDrone.Common.Test
public void GetValue_Success()
{
const string key = "Port";
const string value = "8989";
const string value = "7878";
var result = Subject.GetValue(key, value);
@@ -60,7 +60,7 @@ namespace NzbDrone.Common.Test
public void GetInt_Success()
{
const string key = "Port";
const int value = 8989;
const int value = 7878;
var result = Subject.GetValueInt(key, value);
@@ -95,7 +95,7 @@ namespace NzbDrone.Common.Test
[Test]
public void GetPort_Success()
{
const int value = 8989;
const int value = 7878;
var result = Subject.Port;

View File

@@ -16,7 +16,6 @@ namespace NzbDrone.Common.Test.DiskTests
private readonly string _targetPath = @"C:\target\my.video.mkv".AsOsAgnostic();
private readonly string _backupPath = @"C:\source\my.video.mkv.backup~".AsOsAgnostic();
private readonly string _tempTargetPath = @"C:\target\my.video.mkv.partial~".AsOsAgnostic();
private readonly string _nfsFile = ".nfs01231232";
[SetUp]
public void SetUp()
@@ -643,21 +642,6 @@ namespace NzbDrone.Common.Test.DiskTests
VerifyCopyFolder(source.FullName, destination.FullName);
}
[Test]
public void CopyFolder_should_ignore_nfs_temp_file()
{
WithRealDiskProvider();
var source = GetFilledTempFolder();
File.WriteAllText(Path.Combine(source.FullName, _nfsFile), "SubFile1");
var destination = new DirectoryInfo(GetTempFilePath());
Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Copy);
File.Exists(Path.Combine(destination.FullName, _nfsFile)).Should().BeFalse();
}
[Test]
public void MoveFolder_should_move_folder()
@@ -720,26 +704,6 @@ namespace NzbDrone.Common.Test.DiskTests
destination.GetFileSystemInfos().Should().BeEmpty();
}
[Test]
public void MirrorFolder_should_not_remove_nfs_files()
{
WithRealDiskProvider();
var original = GetFilledTempFolder();
var source = new DirectoryInfo(GetTempFilePath());
var destination = new DirectoryInfo(GetTempFilePath());
source.Create();
Subject.TransferFolder(original.FullName, destination.FullName, TransferMode.Copy);
File.WriteAllText(Path.Combine(destination.FullName, _nfsFile), "SubFile1");
var count = Subject.MirrorFolder(source.FullName, destination.FullName);
count.Should().Equals(0);
destination.GetFileSystemInfos().Should().HaveCount(1);
}
[Test]
public void MirrorFolder_should_add_new_files()
{
@@ -757,24 +721,6 @@ namespace NzbDrone.Common.Test.DiskTests
VerifyCopyFolder(original.FullName, destination.FullName);
}
[Test]
public void MirrorFolder_should_ignore_nfs_temp_file()
{
WithRealDiskProvider();
var source = GetFilledTempFolder();
File.WriteAllText(Path.Combine(source.FullName, _nfsFile), "SubFile1");
var destination = new DirectoryInfo(GetTempFilePath());
var count = Subject.MirrorFolder(source.FullName, destination.FullName);
count.Should().Equals(3);
File.Exists(Path.Combine(destination.FullName, _nfsFile)).Should().BeFalse();
}
[Test]
public void MirrorFolder_should_not_touch_equivalent_files()
{

View File

@@ -1,23 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Common.Test.EnvironmentInfo
{
[TestFixture]
public class BuildInfoFixture
{
[Test]
public void should_return_version()
{
BuildInfo.Version.Major.Should().BeOneOf(2, 10);
}
[Test]
public void should_get_branch()
{
BuildInfo.Branch.Should().NotBe("unknow");
BuildInfo.Branch.Should().NotBeNullOrWhiteSpace();
}
}
}

View File

@@ -29,7 +29,7 @@ namespace NzbDrone.Common.Test
[Test]
public void IsProduction_should_return_false_when_run_within_nunit()
{
RuntimeInfo.IsProduction.Should().BeFalse("Process name is " + Process.GetCurrentProcess().ProcessName + " Folder is " + Directory.GetCurrentDirectory());
RuntimeInfoBase.IsProduction.Should().BeFalse("Process name is " + Process.GetCurrentProcess().ProcessName + " Folder is " + Directory.GetCurrentDirectory());
}
[Test]

View File

@@ -1,21 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
namespace NzbDrone.Common.Test
{
[TestFixture]
public class HashUtilFixture
{
[Test]
public void should_create_anon_id()
{
HashUtil.AnonymousToken().Should().NotBeNullOrEmpty();
}
[Test]
public void should_create_the_same_id()
{
HashUtil.AnonymousToken().Should().Be(HashUtil.AnonymousToken());
}
}
}

View File

@@ -9,7 +9,6 @@ using Moq;
using NLog;
using NUnit.Framework;
using NzbDrone.Common.Cache;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
using NzbDrone.Common.Http.Dispatchers;
using NzbDrone.Common.Http.Proxy;
@@ -31,12 +30,6 @@ namespace NzbDrone.Common.Test.Http
[SetUp]
public void SetUp()
{
Mocker.GetMock<IPlatformInfo>().Setup(c => c.Version).Returns(new Version("1.0.0"));
Mocker.GetMock<IOsInfo>().Setup(c => c.Name).Returns("TestOS");
Mocker.GetMock<IOsInfo>().Setup(c => c.Version).Returns("9.0.0");
Mocker.SetConstant<IUserAgentBuilder>(Mocker.Resolve<UserAgentBuilder>());
Mocker.SetConstant<ICacheManager>(Mocker.Resolve<CacheManager>());
Mocker.SetConstant<ICreateManagedWebProxy>(Mocker.Resolve<ManagedWebProxyFactory>());
Mocker.SetConstant<IRateLimitService>(Mocker.Resolve<RateLimitService>());
@@ -55,7 +48,7 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_execute_simple_get()
{
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
var response = Subject.Execute(request);
@@ -65,7 +58,7 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_execute_https_get()
{
var request = new HttpRequest($"https://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("https://{0}/get", _httpBinHost));
var response = Subject.Execute(request);
@@ -75,7 +68,7 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_execute_typed_get()
{
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
var response = Subject.Get<HttpBinResource>(request);
@@ -87,7 +80,7 @@ namespace NzbDrone.Common.Test.Http
{
var message = "{ my: 1 }";
var request = new HttpRequest($"http://{_httpBinHost}/post");
var request = new HttpRequest(string.Format("http://{0}/post", _httpBinHost));
request.SetContent(message);
var response = Subject.Post<HttpBinResource>(request);
@@ -98,7 +91,7 @@ namespace NzbDrone.Common.Test.Http
[TestCase("gzip")]
public void should_execute_get_using_gzip(string compression)
{
var request = new HttpRequest($"http://{_httpBinHost}/{compression}");
var request = new HttpRequest(string.Format("http://{0}/{1}", _httpBinHost, compression));
var response = Subject.Get<HttpBinResource>(request);
@@ -114,7 +107,7 @@ namespace NzbDrone.Common.Test.Http
[TestCase(HttpStatusCode.BadGateway)]
public void should_throw_on_unsuccessful_status_codes(int statusCode)
{
var request = new HttpRequest($"http://{_httpBinHost}/status/{statusCode}");
var request = new HttpRequest(string.Format("http://{0}/status/{1}", _httpBinHost, statusCode));
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
@@ -126,7 +119,7 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_not_follow_redirects_when_not_in_production()
{
var request = new HttpRequest($"http://{_httpBinHost}/redirect/1");
var request = new HttpRequest(string.Format("http://{0}/redirect/1", _httpBinHost));
Subject.Get(request);
@@ -136,7 +129,7 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_follow_redirects()
{
var request = new HttpRequest($"http://{_httpBinHost}/redirect/1");
var request = new HttpRequest(string.Format("http://{0}/redirect/1", _httpBinHost));
request.AllowAutoRedirect = true;
Subject.Get(request);
@@ -147,7 +140,7 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_send_user_agent()
{
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
var response = Subject.Get<HttpBinResource>(request);
@@ -161,7 +154,7 @@ namespace NzbDrone.Common.Test.Http
[TestCase("Accept", "text/xml, text/rss+xml, application/rss+xml")]
public void should_send_headers(string header, string value)
{
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
request.Headers.Add(header, value);
var response = Subject.Get<HttpBinResource>(request);
@@ -184,7 +177,7 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_send_cookie()
{
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
request.Cookies["my"] = "cookie";
var response = Subject.Get<HttpBinResource>(request);
@@ -201,7 +194,7 @@ namespace NzbDrone.Common.Test.Http
var oldRequest = new HttpRequest("http://eu.httpbin.org/get");
oldRequest.Cookies["my"] = "cookie";
var oldClient = new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<ICacheManager>(), Mocker.Resolve<IRateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.GetMock<IUserAgentBuilder>().Object, Mocker.Resolve<Logger>());
var oldClient = new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<ICacheManager>(), Mocker.Resolve<IRateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.Resolve<Logger>());
oldClient.Should().NotBeSameAs(Subject);
@@ -241,12 +234,12 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_not_store_response_cookie()
{
var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies/set?my=cookie");
var requestSet = new HttpRequest(string.Format("http://{0}/cookies/set?my=cookie", _httpBinHost));
requestSet.AllowAutoRedirect = false;
var responseSet = Subject.Get(requestSet);
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
var response = Subject.Get<HttpBinResource>(request);
@@ -258,13 +251,13 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_store_response_cookie()
{
var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies/set?my=cookie");
var requestSet = new HttpRequest(string.Format("http://{0}/cookies/set?my=cookie", _httpBinHost));
requestSet.AllowAutoRedirect = false;
requestSet.StoreResponseCookie = true;
var responseSet = Subject.Get(requestSet);
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
var response = Subject.Get<HttpBinResource>(request);
@@ -280,14 +273,14 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_overwrite_response_cookie()
{
var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies/set?my=cookie");
var requestSet = new HttpRequest(string.Format("http://{0}/cookies/set?my=cookie", _httpBinHost));
requestSet.AllowAutoRedirect = false;
requestSet.StoreResponseCookie = true;
requestSet.Cookies["my"] = "oldcookie";
var responseSet = Subject.Get(requestSet);
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
var response = Subject.Get<HttpBinResource>(request);
@@ -303,7 +296,7 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_throw_on_http429_too_many_requests()
{
var request = new HttpRequest($"http://{_httpBinHost}/status/429");
var request = new HttpRequest(string.Format("http://{0}/status/429", _httpBinHost));
Assert.Throws<TooManyRequestsException>(() => Subject.Get(request));
@@ -323,7 +316,7 @@ namespace NzbDrone.Common.Test.Http
.Setup(v => v.PostResponse(It.IsAny<HttpResponse>()))
.Returns<HttpResponse>(r => r);
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
Subject.Get(request);
@@ -345,7 +338,7 @@ namespace NzbDrone.Common.Test.Http
{
// the date is bad in the below - should be 13-Jul-2026
string malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Mon, 13-Jul-26 16:19:50 GMT; path=/; HttpOnly";
var requestSet = new HttpRequestBuilder($"http://{_httpBinHost}/response-headers")
var requestSet = new HttpRequestBuilder(string.Format("http://{0}/response-headers", _httpBinHost))
.AddQueryParam("Set-Cookie", malformedCookie)
.Build();
@@ -354,7 +347,7 @@ namespace NzbDrone.Common.Test.Http
var responseSet = Subject.Get(requestSet);
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
var response = Subject.Get<HttpBinResource>(request);
@@ -378,7 +371,7 @@ namespace NzbDrone.Common.Test.Http
{
try
{
string url = $"http://{_httpBinHost}/response-headers?Set-Cookie={Uri.EscapeUriString(malformedCookie)}";
string url = string.Format("http://{0}/response-headers?Set-Cookie={1}", _httpBinHost, Uri.EscapeUriString(malformedCookie));
var requestSet = new HttpRequest(url);
requestSet.AllowAutoRedirect = false;
@@ -386,7 +379,7 @@ namespace NzbDrone.Common.Test.Http
var responseSet = Subject.Get(requestSet);
var request = new HttpRequest($"http://{_httpBinHost}/get");
var request = new HttpRequest(string.Format("http://{0}/get", _httpBinHost));
var response = Subject.Get<HttpBinResource>(request);

View File

@@ -5,7 +5,6 @@ using System;
using System.Text;
using NzbDrone.Common.Http;
using System.Collections.Specialized;
using System.Linq;
namespace NzbDrone.Common.Test.Http
{
@@ -37,17 +36,5 @@ namespace NzbDrone.Common.Test.Http
Action action = () => httpheader.GetEncodingFromContentType();
action.ShouldThrow<ArgumentException>();
}
[Test]
public void should_parse_cookie_with_trailing_semi_colon()
{
var cookies = HttpHeader.ParseCookies("uid=123456; pass=123456b2f3abcde42ac3a123f3f1fc9f;");
cookies.Count.Should().Be(2);
cookies.First().Key.Should().Be("uid");
cookies.First().Value.Should().Be("123456");
cookies.Last().Key.Should().Be("pass");
cookies.Last().Value.Should().Be("123456b2f3abcde42ac3a123f3f1fc9f");
}
}
}

View File

@@ -1,30 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
using NzbDrone.Test.Common;
namespace NzbDrone.Common.Test.Http
{
[TestFixture]
public class UserAgentBuilderFixture : TestBase<UserAgentBuilder>
{
[Test]
public void should_get_user_agent_if_os_version_is_null()
{
Mocker.GetMock<IOsInfo>().SetupGet(c => c.Version).Returns((string)null);
Mocker.GetMock<IOsInfo>().SetupGet(c => c.Name).Returns("TestOS");
Subject.GetUserAgent(false).Should().NotBeNullOrWhiteSpace();
}
[Test]
public void should_get_use_os_family_if_name_is_null()
{
Mocker.GetMock<IOsInfo>().SetupGet(c => c.Version).Returns((string)null);
Mocker.GetMock<IOsInfo>().SetupGet(c => c.Name).Returns((string)null);
Subject.GetUserAgent(false).Should().NotBeNullOrWhiteSpace();
}
}
}

View File

@@ -37,17 +37,21 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="FluentAssertions, Version=4.19.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.dll</HintPath>
<Reference Include="FluentAssertions, Version=4.18.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.18.0\lib\net40\FluentAssertions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FluentAssertions.Core, Version=4.19.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.Core.dll</HintPath>
<Reference Include="FluentAssertions.Core, Version=4.18.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.4.18.0\lib\net40\FluentAssertions.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.4.3\lib\net40\NLog.dll</HintPath>
<HintPath>..\packages\NLog.4.3.11\lib\net40\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
<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 Include="System" />
<Reference Include="System.Core" />
@@ -72,7 +76,6 @@
<Compile Include="DiskTests\IsParentFixtureBase.cs" />
<Compile Include="DiskTests\DiskTransferServiceFixture.cs" />
<Compile Include="EnsureTest\PathExtensionFixture.cs" />
<Compile Include="EnvironmentInfo\BuildInfoFixture.cs" />
<Compile Include="EnvironmentProviderTest.cs" />
<Compile Include="EnvironmentTests\EnvironmentProviderTest.cs" />
<Compile Include="EnvironmentTests\StartupArgumentsFixture.cs" />
@@ -80,13 +83,11 @@
<Compile Include="ExtensionTests\IEnumerableExtensionTests\ExceptByFixture.cs" />
<Compile Include="ExtensionTests\IEnumerableExtensionTests\IntersectByFixture.cs" />
<Compile Include="ExtensionTests\Int64ExtensionFixture.cs" />
<Compile Include="HashUtilFixture.cs" />
<Compile Include="Http\HttpClientFixture.cs" />
<Compile Include="Http\HttpHeaderFixture.cs" />
<Compile Include="Http\HttpRequestBuilderFixture.cs" />
<Compile Include="Http\HttpRequestFixture.cs" />
<Compile Include="Http\HttpUriFixture.cs" />
<Compile Include="Http\UserAgentBuilderFixture.cs" />
<Compile Include="InstrumentationTests\CleanseLogMessageFixture.cs" />
<Compile Include="LevenshteinDistanceFixture.cs" />
<Compile Include="OsPathFixture.cs" />

View File

@@ -86,7 +86,7 @@ namespace NzbDrone.Common.Test
{
first.AsOsAgnostic().PathEquals(second.AsOsAgnostic()).Should().BeFalse();
}
[Test]
public void should_return_false_when_not_a_child()
{
@@ -113,7 +113,6 @@ namespace NzbDrone.Common.Test
[TestCase(@"C:\Test\", @"C:\Test\mydir")]
[TestCase(@"C:\Test\", @"C:\Test\mydir\")]
[TestCase(@"C:\Test", @"C:\Test\30.Rock.S01E01.Pilot.avi")]
[TestCase(@"C:\", @"C:\Test\30.Rock.S01E01.Pilot.avi")]
public void path_should_be_parent(string parentPath, string childPath)
{
parentPath.AsOsAgnostic().IsParentPath(childPath.AsOsAgnostic()).Should().BeTrue();

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentAssertions" version="4.19.0" targetFramework="net40" />
<package id="FluentAssertions" version="4.18.0" targetFramework="net40" />
<package id="Moq" version="4.0.10827" />
<package id="NLog" version="4.4.3" targetFramework="net40" />
<package id="NUnit" version="3.6.0" targetFramework="net40" />
<package id="NLog" version="4.3.11" targetFramework="net40" />
<package id="NUnit" version="3.5.0" targetFramework="net40" />
</packages>

View File

@@ -6,6 +6,8 @@ namespace NzbDrone.Common.Cloud
{
IHttpRequestBuilderFactory Services { get; }
IHttpRequestBuilderFactory SkyHookTvdb { get; }
IHttpRequestBuilderFactory TMDB { get; }
IHttpRequestBuilderFactory TMDBSingle { get; }
}
public class SonarrCloudRequestBuilder : ISonarrCloudRequestBuilder
@@ -18,10 +20,20 @@ namespace NzbDrone.Common.Cloud
SkyHookTvdb = new HttpRequestBuilder("http://skyhook.sonarr.tv/v1/tvdb/{route}/{language}/")
.SetSegment("language", "en")
.CreateFactory();
TMDB = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}/{id}{secondaryRoute}")
.AddQueryParam("api_key", "1a7373301961d03f97f853a876dd1212")
.CreateFactory();
TMDBSingle = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}")
.AddQueryParam("api_key", "1a7373301961d03f97f853a876dd1212")
.CreateFactory();
}
public IHttpRequestBuilderFactory Services { get; }
public IHttpRequestBuilderFactory Services { get; private set; }
public IHttpRequestBuilderFactory SkyHookTvdb { get; }
public IHttpRequestBuilderFactory SkyHookTvdb { get; private set; }
public IHttpRequestBuilderFactory TMDB { get; private set; }
public IHttpRequestBuilderFactory TMDBSingle { get; private set; }
}
}

View File

@@ -6,21 +6,19 @@ using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Messaging;
using TinyIoC;
namespace NzbDrone.Common.Composition
{
public abstract class ContainerBuilderBase
{
private readonly List<Type> _loadedTypes;
protected IContainer Container { get; }
public IContainer Container { get; private set; }
protected ContainerBuilderBase(IStartupContext args, List<string> assemblies)
protected ContainerBuilderBase(IStartupContext args, params string[] assemblies)
{
_loadedTypes = new List<Type>();
assemblies.Add(OsInfo.IsWindows ? "NzbDrone.Windows" : "NzbDrone.Mono");
assemblies.Add("NzbDrone.Common");
foreach (var assembly in assemblies)
{
_loadedTypes.AddRange(Assembly.Load(assembly).GetTypes());

View File

@@ -16,19 +16,6 @@ namespace NzbDrone.Common.Disk
{
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(DiskProviderBase));
public static StringComparison PathStringComparison
{
get
{
if (OsInfo.IsWindows)
{
return StringComparison.OrdinalIgnoreCase;
}
return StringComparison.Ordinal;
}
}
public abstract long? GetAvailableSpace(string path);
public abstract void InheritFolderPermissions(string filename);
public abstract void SetPermissions(string path, string mask, string user, string group);
@@ -99,7 +86,7 @@ namespace NzbDrone.Common.Disk
public bool FileExists(string path)
{
Ensure.That(path, () => path).IsValidPath();
return FileExists(path, PathStringComparison);
return FileExists(path, OsInfo.PathStringComparison);
}
public bool FileExists(string path, StringComparison stringComparison)
@@ -108,16 +95,16 @@ namespace NzbDrone.Common.Disk
switch (stringComparison)
{
case StringComparison.CurrentCulture:
case StringComparison.InvariantCulture:
case StringComparison.Ordinal:
{
return File.Exists(path) && path == path.GetActualCasing();
}
case StringComparison.CurrentCulture:
case StringComparison.InvariantCulture:
case StringComparison.Ordinal:
{
return File.Exists(path) && path == path.GetActualCasing();
}
default:
{
return File.Exists(path);
}
{
return File.Exists(path);
}
}
}
@@ -128,7 +115,7 @@ namespace NzbDrone.Common.Disk
try
{
var testPath = Path.Combine(path, "sonarr_write_test.txt");
var testContent = $"This file was created to verify if '{path}' is writable. It should've been automatically deleted. Feel free to delete it.";
var testContent = string.Format("This file was created to verify if '{0}' is writable. It should've been automatically deleted. Feel free to delete it.", path);
File.WriteAllText(testPath, testContent);
File.Delete(testPath);
return true;

View File

@@ -64,15 +64,11 @@ namespace NzbDrone.Common.Disk
foreach (var subDir in _diskProvider.GetDirectoryInfos(sourcePath))
{
if (ShouldIgnore(subDir)) continue;
result &= TransferFolder(subDir.FullName, Path.Combine(targetPath, subDir.Name), mode, verificationMode);
}
foreach (var sourceFile in _diskProvider.GetFileInfos(sourcePath))
{
if (ShouldIgnore(sourceFile)) continue;
var destFile = Path.Combine(targetPath, sourceFile.Name);
result &= TransferFile(sourceFile.FullName, destFile, mode, true, verificationMode);
@@ -105,15 +101,11 @@ namespace NzbDrone.Common.Disk
foreach (var subDir in targetFolders.Where(v => !sourceFolders.Any(d => d.Name == v.Name)))
{
if (ShouldIgnore(subDir)) continue;
_diskProvider.DeleteFolder(subDir.FullName, true);
}
foreach (var subDir in sourceFolders)
{
if (ShouldIgnore(subDir)) continue;
filesCopied += MirrorFolder(subDir.FullName, Path.Combine(targetPath, subDir.Name));
}
@@ -122,15 +114,11 @@ namespace NzbDrone.Common.Disk
foreach (var targetFile in targetFiles.Where(v => !sourceFiles.Any(d => d.Name == v.Name)))
{
if (ShouldIgnore(targetFile)) continue;
_diskProvider.DeleteFile(targetFile.FullName);
}
foreach (var sourceFile in sourceFiles)
{
if (ShouldIgnore(sourceFile)) continue;
var targetFile = Path.Combine(targetPath, sourceFile.Name);
if (CompareFiles(sourceFile.FullName, targetFile))
@@ -364,7 +352,7 @@ namespace NzbDrone.Common.Disk
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to properly rollback the file move [{0}] to [{1}], incomplete file may be left in target path.", sourcePath, targetPath);
_logger.Error(ex, string.Format("Failed to properly rollback the file move [{0}] to [{1}], incomplete file may be left in target path.", sourcePath, targetPath));
}
}
@@ -380,7 +368,7 @@ namespace NzbDrone.Common.Disk
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to properly rollback the file move [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath);
_logger.Error(ex, string.Format("Failed to properly rollback the file move [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath));
}
}
@@ -399,7 +387,7 @@ namespace NzbDrone.Common.Disk
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to properly rollback the file copy [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath);
_logger.Error(ex, string.Format("Failed to properly rollback the file copy [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath));
}
}
@@ -441,7 +429,7 @@ namespace NzbDrone.Common.Disk
if (i == RetryCount)
{
_logger.Error("Failed to completely transfer [{0}] to [{1}], aborting.", sourcePath, targetPath);
_logger.Error("Failed to completely transfer [{0}] to [{1}], aborting.", sourcePath, targetPath, i + 1, RetryCount);
}
else
{
@@ -576,27 +564,5 @@ namespace NzbDrone.Common.Disk
throw;
}
}
private bool ShouldIgnore(DirectoryInfo folder)
{
if (folder.Name.StartsWith(".nfs"))
{
_logger.Trace("Ignoring folder {0}", folder.FullName);
return true;
}
return false;
}
private bool ShouldIgnore(FileInfo file)
{
if (file.Name.StartsWith(".nfs") || file.Name == "debug.log" || file.Name.EndsWith(".socket"))
{
_logger.Trace("Ignoring file {0}", file.FullName);
return true;
}
return false;
}
}
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.IO;
using System.IO;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Disk
@@ -9,11 +8,10 @@ namespace NzbDrone.Common.Disk
private readonly DriveInfo _driveInfo;
private readonly DriveType _driveType;
public DriveInfoMount(DriveInfo driveInfo, DriveType driveType = DriveType.Unknown, MountOptions mountOptions = null)
public DriveInfoMount(DriveInfo driveInfo, DriveType driveType = DriveType.Unknown)
{
_driveInfo = driveInfo;
_driveType = driveType;
MountOptions = mountOptions;
}
public long AvailableFreeSpace => _driveInfo.AvailableFreeSpace;
@@ -35,8 +33,6 @@ namespace NzbDrone.Common.Disk
public bool IsReady => _driveInfo.IsReady;
public MountOptions MountOptions { get; private set; }
public string Name => _driveInfo.Name;
public string RootDirectory => _driveInfo.RootDirectory.FullName;

View File

@@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.IO;
namespace NzbDrone.Common.Disk
@@ -9,7 +8,6 @@ namespace NzbDrone.Common.Disk
string DriveFormat { get; }
DriveType DriveType { get; }
bool IsReady { get; }
MountOptions MountOptions { get; }
string Name { get; }
string RootDirectory { get; }
long TotalFreeSpace { get; }

View File

@@ -1,16 +0,0 @@
using System.Collections.Generic;
namespace NzbDrone.Common.Disk
{
public class MountOptions
{
private readonly Dictionary<string, string> _options;
public MountOptions(Dictionary<string, string> options)
{
_options = options;
}
public bool IsReadOnly => _options.ContainsKey("ro");
}
}

View File

@@ -101,12 +101,12 @@ namespace NzbDrone.Common.EnsureThat
if (param.Value.IsPathValid()) return param;
if (OsInfo.IsWindows)
if (OsInfo.IsNotWindows)
{
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid Windows path. paths must be a full path eg. C:\\Windows", param.Value));
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid *nix path. paths must start with /", param.Value));
}
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid *nix path. paths must start with /", param.Value));
throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid Windows path. paths must be a full path eg. C:\\Windows", param.Value));
}
}
}

View File

@@ -34,17 +34,17 @@ namespace NzbDrone.Common.EnvironmentInfo
}
else
{
AppDataFolder = Path.Combine(Environment.GetFolderPath(DATA_SPECIAL_FOLDER, Environment.SpecialFolderOption.None), "NzbDrone");
AppDataFolder = Path.Combine(Environment.GetFolderPath(DATA_SPECIAL_FOLDER, Environment.SpecialFolderOption.None), "Radarr");
}
StartUpFolder = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.FullName;
TempFolder = Path.GetTempPath();
}
public string AppDataFolder { get; }
public string AppDataFolder { get; private set; }
public string StartUpFolder { get; }
public string StartUpFolder { get; private set; }
public string TempFolder { get; }
public string TempFolder { get; private set; }
}
}

View File

@@ -1,35 +1,12 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
namespace NzbDrone.Common.EnvironmentInfo
{
public static class BuildInfo
{
static BuildInfo()
{
var assembly = Assembly.GetExecutingAssembly();
Version = assembly.GetName().Version;
var attributes = assembly.GetCustomAttributes(true);
Branch = "unknow";
var config = attributes.OfType<AssemblyConfigurationAttribute>().FirstOrDefault();
if (config != null)
{
Branch = config.Configuration;
}
Release = $"{Version}-{Branch}";
}
public static Version Version { get; }
public static String Branch { get; }
public static string Release { get; }
public static Version Version => Assembly.GetExecutingAssembly().GetName().Version;
public static DateTime BuildDateTime
{

View File

@@ -1,9 +0,0 @@
namespace NzbDrone.Common.EnvironmentInfo
{
public interface IOperatingSystemVersionInfo
{
string Version { get; }
string Name { get; }
string FullName { get; }
}
}

View File

@@ -1,9 +0,0 @@
namespace NzbDrone.Common.EnvironmentInfo
{
public interface IOsVersionAdapter
{
bool Enabled { get; }
OsVersionModel Read();
}
}

View File

@@ -1,50 +0,0 @@
using System;
namespace NzbDrone.Common.EnvironmentInfo
{
public enum PlatformType
{
DotNet = 0,
Mono = 1
}
public interface IPlatformInfo
{
Version Version { get; }
}
public abstract class PlatformInfo : IPlatformInfo
{
static PlatformInfo()
{
if (Type.GetType("Mono.Runtime") != null)
{
Platform = PlatformType.Mono;
}
else
{
Platform = PlatformType.DotNet;
}
}
public static PlatformType Platform { get; }
public static bool IsMono => Platform == PlatformType.Mono;
public static bool IsDotNet => Platform == PlatformType.DotNet;
public static string PlatformName
{
get
{
if (IsDotNet)
{
return ".NET";
}
return "Mono";
}
}
public abstract Version Version { get; }
}
}

View File

@@ -1,14 +1,14 @@
using System;
namespace NzbDrone.Common.EnvironmentInfo
namespace NzbDrone.Common.EnvironmentInfo
{
public interface IRuntimeInfo
{
bool IsUserInteractive { get; }
bool IsAdmin { get; }
bool IsWindowsService { get; }
bool IsExiting { get; set; }
bool IsConsole { get; }
bool IsRunning { get; set; }
bool RestartPending { get; set; }
string ExecutingApplication { get; }
string RuntimeVersion { get; }
}
}

View File

@@ -1,100 +1,89 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
namespace NzbDrone.Common.EnvironmentInfo
{
public class OsInfo : IOsInfo
public static class OsInfo
{
public static Os Os { get; }
public static bool IsNotWindows => !IsWindows;
public static bool IsLinux => Os == Os.Linux;
public static bool IsOsx => Os == Os.Osx;
public static bool IsWindows => Os == Os.Windows;
public string Version { get; }
public string Name { get; }
public string FullName { get; }
static OsInfo()
{
var platform = Environment.OSVersion.Platform;
var platform = (int)Environment.OSVersion.Platform;
switch (platform)
Version = Environment.OSVersion.Version;
IsMonoRuntime = Type.GetType("Mono.Runtime") != null;
IsNotWindows = (platform == 4) || (platform == 6) || (platform == 128);
IsOsx = IsRunningOnMac();
IsLinux = IsNotWindows && !IsOsx;
IsWindows = !IsNotWindows;
FirstDayOfWeek = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
if (IsWindows)
{
case PlatformID.Win32NT:
{
Os = Os.Windows;
break;
}
case PlatformID.MacOSX:
case PlatformID.Unix:
{
// Sometimes Mac OS reports itself as Unix
if (Directory.Exists("/System/Library/CoreServices/") &&
(File.Exists("/System/Library/CoreServices/SystemVersion.plist") ||
File.Exists("/System/Library/CoreServices/ServerVersion.plist"))
)
{
Os = Os.Osx;
}
else
{
Os = Os.Linux;
}
break;
}
}
}
public OsInfo(IEnumerable<IOsVersionAdapter> versionAdapters, Logger logger)
{
OsVersionModel osInfo = null;
foreach (var osVersionAdapter in versionAdapters.Where(c => c.Enabled))
{
try
{
osInfo = osVersionAdapter.Read();
}
catch (Exception e)
{
logger.Error(e, "Couldn't get OS Version info");
}
if (osInfo != null)
{
break;
}
}
if (osInfo != null)
{
Name = osInfo.Name;
Version = osInfo.Version;
FullName = osInfo.FullName;
Os = Os.Windows;
PathStringComparison = StringComparison.OrdinalIgnoreCase;
}
else
{
Name = Os.ToString();
FullName = Name;
Os = IsOsx ? Os.Osx : Os.Linux;
PathStringComparison = StringComparison.Ordinal;
}
}
public static Version Version { get; private set; }
public static bool IsMonoRuntime { get; private set; }
public static bool IsNotWindows { get; private set; }
public static bool IsLinux { get; private set; }
public static bool IsOsx { get; private set; }
public static bool IsWindows { get; private set; }
public static Os Os { get; private set; }
public static DayOfWeek FirstDayOfWeek { get; private set; }
public static StringComparison PathStringComparison { get; private set; }
//Borrowed from: https://github.com/jpobst/Pinta/blob/master/Pinta.Core/Managers/SystemManager.cs
//From Managed.Windows.Forms/XplatUI
[DllImport("libc")]
static extern int uname(IntPtr buf);
[DebuggerStepThrough]
static bool IsRunningOnMac()
{
var buf = IntPtr.Zero;
try
{
buf = Marshal.AllocHGlobal(8192);
// This is a hacktastic way of getting sysname from uname ()
if (uname(buf) == 0)
{
var os = Marshal.PtrToStringAnsi(buf);
if (os == "Darwin")
{
return true;
}
}
}
catch
{
}
finally
{
if (buf != IntPtr.Zero)
{
Marshal.FreeHGlobal(buf);
}
}
Environment.SetEnvironmentVariable("OS_NAME", Name);
Environment.SetEnvironmentVariable("OS_VERSION", Version);
return false;
}
}
public interface IOsInfo
{
string Version { get; }
string Name { get; }
string FullName { get; }
}
public enum Os
{
Windows,

View File

@@ -1,29 +0,0 @@
namespace NzbDrone.Common.EnvironmentInfo
{
public class OsVersionModel
{
public OsVersionModel(string name, string version, string fullName = null)
{
Name = Trim(name);
Version = Trim(version);
if (string.IsNullOrWhiteSpace(fullName))
{
fullName = $"{Name} {Version}";
}
FullName = Trim(fullName);
}
private static string Trim(string source)
{
return source.Trim().Trim('"', '\'');
}
public string Name { get; }
public string FullName { get; }
public string Version { get; }
}
}

View File

@@ -5,14 +5,15 @@ using System.Reflection;
using System.Security.Principal;
using System.ServiceProcess;
using NLog;
using NzbDrone.Common.Processes;
namespace NzbDrone.Common.EnvironmentInfo
{
public class RuntimeInfo : IRuntimeInfo
public abstract class RuntimeInfoBase : IRuntimeInfo
{
private readonly Logger _logger;
public RuntimeInfo(IServiceProvider serviceProvider, Logger logger)
public RuntimeInfoBase(IServiceProvider serviceProvider, Logger logger)
{
_logger = logger;
@@ -30,7 +31,7 @@ namespace NzbDrone.Common.EnvironmentInfo
}
}
static RuntimeInfo()
static RuntimeInfoBase()
{
IsProduction = InternalIsProduction();
}
@@ -58,18 +59,31 @@ namespace NzbDrone.Common.EnvironmentInfo
public bool IsWindowsService { get; private set; }
public bool IsExiting { get; set; }
public bool RestartPending { get; set; }
public string ExecutingApplication { get; }
public bool IsConsole
{
get
{
if (OsInfo.IsWindows)
{
return IsUserInteractive && Process.GetCurrentProcess().ProcessName.Equals(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME, StringComparison.InvariantCultureIgnoreCase);
}
public static bool IsProduction { get; }
return true;
}
}
public bool IsRunning { get; set; }
public bool RestartPending { get; set; }
public string ExecutingApplication { get; private set; }
public abstract string RuntimeVersion { get; }
public static bool IsProduction { get; private set; }
private static bool InternalIsProduction()
{
if (BuildInfo.IsDebug || Debugger.IsAttached) return false;
//Official builds will never have such a high revision
if (BuildInfo.Version.Revision > 10000) return false;
if (BuildInfo.Version.Revision > 10000) return false; //Official builds will never have such a high revision
try
{
@@ -85,19 +99,18 @@ namespace NzbDrone.Common.EnvironmentInfo
}
try
{
var currentAssemblyLocation = typeof(RuntimeInfo).Assembly.Location;
if (currentAssemblyLocation.ToLower().Contains("_output")) return false;
}
catch
{
try
{
var currentAssmeblyLocation = typeof(RuntimeInfoBase).Assembly.Location;
if(currentAssmeblyLocation.ToLower().Contains("_output"))return false;
}
catch
{
}
}
var lowerCurrentDir = Directory.GetCurrentDirectory().ToLower();
string lowerCurrentDir = Directory.GetCurrentDirectory().ToLower();
if (lowerCurrentDir.Contains("teamcity")) return false;
if (lowerCurrentDir.Contains("buildagent")) return false;
if (lowerCurrentDir.Contains("_output")) return false;
return true;

View File

@@ -0,0 +1,73 @@
using System.ComponentModel;
using System.Configuration;
namespace NzbDrone.Common.Exceptron.Configuration
{
public class ExceptronConfiguration : ConfigurationSection
{
public ExceptronConfiguration()
{
Host = "http://exceptron.azurewebsites.net/api/v1/";
IncludeMachineName = true;
}
public static ExceptronConfiguration ReadConfig(string sectionName = "exceptron")
{
var configSection = ConfigurationManager.GetSection(sectionName);
if (configSection == null)
{
throw new ConfigurationErrorsException("ExceptronConfiguration section missing.");
}
return (ExceptronConfiguration)configSection;
}
/// <summary>
/// exceptron api address. Do not modify this property.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public string Host { get; set; }
/// <summary>
/// If ExceptronClinet should throw exceptions in case of an error. Default: <see cref="bool.False"/>
/// </summary>
/// <remarks>
/// Its recommended that this flag is set to True during development and <see cref="bool.False"/> in production systems.
/// If an exception is thrown while this flag is set to <see cref="bool.False"/> the thrown exception will be returned in <see cref="ExceptionResponse.Exception"/>
/// </remarks>
[ConfigurationProperty("throwExceptions", DefaultValue = false)]
public bool ThrowExceptions
{
get { return (bool)this["throwExceptions"]; }
set { this["throwExceptions"] = value; }
}
/// <summary>
/// The API of this application. Can find your API key in application settings page.
/// </summary>
[ConfigurationProperty("apiKey")]
public string ApiKey
{
get { return (string)this["apiKey"]; }
set { this["apiKey"] = value; }
}
/// <summary>
/// If the machine name should be attached to the exception report
/// </summary>
/// <remarks>Machine name can be usefull in webfarm enviroments when multiple
/// servers are running the same app and the issue could be machine specific.
/// Hoewever, You might want to disable this feature for privacy reasons.</remarks>
[ConfigurationProperty("includeMachineName", DefaultValue = true)]
public bool IncludeMachineName
{
get { return (bool)this["includeMachineName"]; }
set { this["includeMachineName"] = value; }
}
}
}

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