1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-18 21:35:51 -04:00

Compare commits

...

52 Commits

Author SHA1 Message Date
Leonardo Galli
f2e5d9e996 Updated: Radarr API url now points to new v2 version. 2017-06-20 10:23:26 +02:00
Leonardo Galli
bae3a48aa1 Changed Name of Radarr Lists 2017-06-19 15:22:03 +02:00
Leonardo Galli
6d033c57f4 Added: More detailed descriptions why a movie was not able to be mapped. (#1696)
Added: Option to make mapping more lenient. This should practically allow all movies to be correctly mapped. Though it also opens the path for movies being wrongly mapped! (So it is a toggable option)

Added: Improved edition parsing. Now almost all releases should have the correct edition, even ones with no year, etc.
2017-06-18 23:12:14 +02:00
Leonardo Galli
d6cf53e12c Added: Options to make parsing more lenient. (Adds support for some german and french releasegroups) (#1692)
Fixes #1676. Fixes #1632.
2017-06-17 15:02:58 +02:00
Leonardo Galli
5238b78813 Merge remote-tracking branch 'origin/develop' into develop 2017-06-13 16:10:17 +02:00
Leonardo Galli
705b233819 Fix migration.
Fixes #1678
2017-06-13 16:09:12 +02:00
Mitchell Cash
295536ae30 Updated: Bootstrap Tags Input (#1674) 2017-06-13 13:15:43 +02:00
Leonardo Galli
42c980c905 Merge remote-tracking branch 'origin/develop' into develop 2017-06-13 13:09:09 +02:00
Leonardo Galli
0a71aac56c Fixed: Urls missing from multiple indexers after latest nightly update. 2017-06-13 13:09:02 +02:00
Mitchell Cash
c312ffd7db Fixed: Redirect calls missing URL Base (#1668) 2017-06-13 08:42:49 +02:00
Mitchell Cash
5c5f06a92c Fixed: Twitter oAuth callback URL (#1669) 2017-06-13 08:42:07 +02:00
Mitchell Cash
3eab8248e6 Fixed: Error when processing manual import decisions (#1670) 2017-06-13 08:40:51 +02:00
Mitchell Cash
c33d9727a0 Added: Include css files in minification (#1672) 2017-06-13 08:39:42 +02:00
Mitchell Cash
ca6784f7b5 Added: Upgrade to Bootstrap 3.3.7 (#1673) 2017-06-13 08:38:39 +02:00
Leonardo Galli
ca00d0d6cc Create README.md 2017-06-12 19:17:56 +02:00
Leonardo Galli
756fc7556c Add license 2017-06-12 19:11:11 +02:00
Leonardo Galli
9f4ce50dd6 Added: Allow minimum seeders to be set on a per indexer basis. Pulled from Sonarr Upstream (#1624) 2017-06-12 14:47:05 +02:00
Leonardo Galli
a460e89a8d Merge remote-tracking branch 'origin/develop' into develop 2017-06-12 14:10:18 +02:00
Leonardo Galli
edda8786ad Minimum availability is now working similarely to profile when adding a movie. 2017-06-12 14:10:07 +02:00
Mitchell Cash
b1c5dd8167 Changed: Remove redundant IE meta tag as we use http header instead (#1655) 2017-06-11 09:32:12 +02:00
Mitchell Cash
a620c4b5d8 Changed: Use cleancss for minification (#1654) 2017-06-11 09:31:27 +02:00
Mitchell Cash
28b523b504 Fixed: Follow 301 redirects when fetching torrents (#1653)
Closes #1564
2017-06-11 09:30:51 +02:00
Mitchell Cash
7af88037bb Fixed: Ensure an API Key is set when starting Radarr (#1652) 2017-06-11 09:30:00 +02:00
Leonardo Galli
43446ee3d0 Fixed: Forgot to include some js files in the last commit 2017-06-07 13:37:20 +02:00
Leonardo Galli
accf8a9efa Added: Ability to see TMDB and lists going through the Radarr API on the discovery page.
Added: More lists (specifically presets for IMDB Top 250 and IMDB Popular)
Added: Ability to set Radarr API endpoint as list.
2017-06-06 22:40:44 +02:00
Leonardo Galli
b70ed720c5 Fix error when we get invalid datetime from our api. 2017-06-06 20:03:52 +02:00
Leonardo Galli
cff4578c66 Merge remote-tracking branch 'origin/develop' into develop 2017-06-06 19:51:00 +02:00
Leonardo Galli
7bbebe53a3 Added: Custom Class for Radarr API requests. Also implements new error handling present on staging server. 2017-06-06 19:50:53 +02:00
Leonardo Galli
d81e3a79cf Added: Search 5 alternative titles as well. This should help with french as well as movies with very different titles. 2017-06-05 17:48:04 +02:00
Jason Costomiris
09c86013b0 Added: [Radarr] tag for Twitter Notifications (#1558) 2017-06-05 12:06:20 +02:00
Fish2
f1873a9684 lossless compression of images saved 92KB (#1620) 2017-06-04 13:11:52 +02:00
randellhodges
3d48da2111 Added HDBits Category, Codec, and Medium Filtering Capability (#1458)
* Added advanced configuration options to support filtering Categories, Codecs, and Medium to the HDBits indexer.

* Changes to use the existing tags with a controlled vocabulary.

* 1) Sorting select options by name
2) Moved the autocomplete tag code into taginput as requested

* removed commented out line

* require cleanups
2017-05-29 17:56:16 +02:00
Leonardo Galli
86634006e5 update radarr api url 2017-05-29 15:49:42 +02:00
Leonardo Galli
234827d5fd Mostly fixes UI glitches for list settings.
Fixes #679
2017-05-26 18:31:11 +02:00
Leonardo Galli
10bceaf370 Merge remote-tracking branch 'origin/develop' into develop 2017-05-26 11:12:14 +02:00
Leonardo Galli
b7f6ff7b2e Fixed PTP indexer being disabled if no results are found for a movie.
Fixes #1573
2017-05-26 11:12:07 +02:00
Sentir101
6767f602fb Refresh IsDuplicate in bulk import when the tmdbId changes (#1570) 2017-05-25 15:44:43 +02:00
flightlevel
e43e7d2b8d Encourage Torznab use with Jackett (#1559) 2017-05-23 17:30:59 +02:00
Leonardo Galli
811a4bf117 Fix basic naming settings.
Fixes #376
2017-05-21 18:10:46 +02:00
Leonardo Galli
9dee1d6fad Discovery of upcoming movies points to our server now.
Discovery results now show physical release date (if present).
All physical release dates now should show a note (if available) detailing what kind of release date it is (e.g. Netflix, DVD, Streaming, etc.)
2017-05-20 14:31:38 +02:00
Mike
390e4c3014 Most likely fixed #745 now 2017-05-18 01:08:13 +02:00
Mike
aace29e1e1 Chmod osx file as executable. (#1539) 2017-05-17 11:20:32 +02:00
tsubus
8fb257d5b7 Add IMDB URL to notifications (#1531) 2017-05-15 14:47:22 +02:00
Levi Wilcox
28e0f85e88 Fixed design calendar css bug (#1527)
Fixes #983
2017-05-15 09:40:00 +02:00
Luke Anderson
41d3f6d7ea Correct Program Name (#1524) 2017-05-14 12:19:20 +02:00
Luke Anderson
b19fbcbb71 Correct Program Name (#1523) 2017-05-14 12:19:10 +02:00
Leonardo Galli
5200758200 Osx updater now updates plist file to point to the correct executable binary. 2017-05-14 11:08:33 +02:00
Leonardo Galli
6210c5b8f8 Merge remote-tracking branch 'origin/develop' into develop 2017-05-14 11:03:28 +02:00
Leonardo Galli
6e31f7220c Fix osx updater failing. 2017-05-14 11:03:20 +02:00
Leonardo Galli
797a9acff5 Update TaskManager.cs 2017-05-13 21:22:55 +02:00
Leonardo Galli
b36053e1f4 Update LogEntries token again :) 2017-05-13 20:32:55 +02:00
Leonardo Galli
212bcebdaf Fixed: Using our own logentries token now.
Fixes #1517
2017-05-13 13:11:54 +02:00
275 changed files with 2278 additions and 826 deletions

1
.idea/Sonarr.iml generated
View File

@@ -20,6 +20,5 @@
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Sonarr node_modules" level="project" />
</component> </component>
</module> </module>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="JavaScriptLibraryMappings"> <component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="ECMAScript 6" /> <excludedPredefinedLibrary name="Radarr/node_modules" />
</component> </component>
</project> </project>

View File

@@ -1,14 +0,0 @@
<component name="libraryTable">
<library name="Sonarr node_modules" type="javaScript">
<properties>
<option name="frameworkName" value="node_modules" />
<sourceFilesUrls>
<item url="file://$PROJECT_DIR$/node_modules" />
</sourceFilesUrls>
</properties>
<CLASSES>
<root url="file://$PROJECT_DIR$/node_modules" />
</CLASSES>
<SOURCES />
</library>
</component>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 701 B

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -71,7 +71,8 @@ Radarr is currently undergoing rapid development and pull requests are actively
* All indexers supported by Sonarr also supported * All indexers supported by Sonarr also supported
* New PassThePopcorn Indexer * New PassThePopcorn Indexer
* QBittorrent, Deluge, rTorrent, Transmission and uTorrent download client (Other clients are coming) * QBittorrent, Deluge, rTorrent, Transmission and uTorrent download client (Other clients are coming)
* New TorrentPotato Indexer (Works well with [Jackett](https://github.com/Jackett/Jackett)) * New TorrentPotato Indexer
* Torznab Indexer now supports Movies (Works well with [Jackett](https://github.com/Jackett/Jackett))
* Scanning PreDB to know when a new release is available * Scanning PreDB to know when a new release is available
* Importing movies from various online sources, such as IMDb Watchlists (A complete list can be found [here](https://github.com/Radarr/Radarr/issues/114)) * Importing movies from various online sources, such as IMDb Watchlists (A complete list can be found [here](https://github.com/Radarr/Radarr/issues/114))
* Full integration with Kodi, Plex (notification, library update) * Full integration with Kodi, Plex (notification, library update)
@@ -114,9 +115,7 @@ Radarr is currently undergoing rapid development and pull requests are actively
* Open `NzbDrone.sln` in Visual Studio or run the build.sh script, if Mono is installed * 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 * Make sure `NzbDrone.Console` is set as the startup project
## Sponsors ### License
Thanks to [JetBrains](http://www.jetbrains.com) for providing us with free licenses to their great tools: * [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
* [ReSharper](http://www.jetbrains.com/resharper) * Copyright 2010-2017
* [WebStorm](http://www.jetbrains.com/webstorm)
* [TeamCity](http://www.jetbrains.com/teamcity)

View File

@@ -168,6 +168,12 @@ Task("PackageOsx").Does(() => {
// Adding MediaInfo dylib // Adding MediaInfo dylib
CopyFiles(sourceFolder + "/Libraries/MediaInfo/*.dylib", outputFolderOsx); CopyFiles(sourceFolder + "/Libraries/MediaInfo/*.dylib", outputFolderOsx);
// Chmod as executable
StartProcess(@"C:\cygwin64\bin\chmod.exe", new ProcessSettings()
.WithArguments(args => args
.Append("+x")
.Append(outputFolderOsx + "/Radarr")));
// Adding Startup script // Adding Startup script
CopyFile("./osx/Radarr", outputFolderOsx + "/Radarr"); CopyFile("./osx/Radarr", outputFolderOsx + "/Radarr");
}); });
@@ -268,9 +274,9 @@ Task("ArtifactsWindows").Does(() => {
Task("ArtifactsWindowsInstaller").Does(() => { Task("ArtifactsWindowsInstaller").Does(() => {
InnoSetup("./setup/nzbdrone.iss", new InnoSetupSettings { InnoSetup("./setup/nzbdrone.iss", new InnoSetupSettings {
OutputDirectory = artifactsFolder, OutputDirectory = artifactsFolder,
ToolPath = "./setup/inno/ISCC.exe" ToolPath = "./setup/inno/ISCC.exe"
}); });
}); });
Task("ArtifactsLinux").Does(() => { Task("ArtifactsLinux").Does(() => {

View File

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

View File

@@ -5,7 +5,7 @@ var postcss = require('gulp-postcss');
var sourcemaps = require('gulp-sourcemaps'); var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('autoprefixer-core'); var autoprefixer = require('autoprefixer-core');
var livereload = require('gulp-livereload'); var livereload = require('gulp-livereload');
var cleancss = require('gulp-clean-css');
var print = require('gulp-print'); var print = require('gulp-print');
var paths = require('./paths'); var paths = require('./paths');
var errorHandler = require('./errorHandler'); var errorHandler = require('./errorHandler');
@@ -16,6 +16,10 @@ gulp.task('less', function() {
paths.src.content + 'bootstrap.less', paths.src.content + 'bootstrap.less',
paths.src.content + 'theme.less', paths.src.content + 'theme.less',
paths.src.content + 'overrides.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 + 'Series/series.less',
paths.src.root + 'Activity/activity.less', paths.src.root + 'Activity/activity.less',
paths.src.root + 'AddSeries/addSeries.less', paths.src.root + 'AddSeries/addSeries.less',
@@ -36,12 +40,13 @@ gulp.task('less', function() {
.pipe(sourcemaps.init()) .pipe(sourcemaps.init())
.pipe(less({ .pipe(less({
dumpLineNumbers : 'false', dumpLineNumbers : 'false',
compress : true, compress : false,
yuicompress : true, yuicompress : false,
ieCompat : true, ieCompat : true,
strictImports : true strictImports : true
})) }))
.pipe(postcss([ autoprefixer({ browsers: ['last 2 versions'] }) ])) .pipe(postcss([ autoprefixer({ browsers: ['last 2 versions'] }) ]))
.pipe(cleancss())
.on('error', errorHandler.onError) .on('error', errorHandler.onError)
.pipe(sourcemaps.write(paths.dest.content)) .pipe(sourcemaps.write(paths.dest.content))
.pipe(gulp.dest(paths.dest.content)) .pipe(gulp.dest(paths.dest.content))

View File

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

1
src/.idea/.idea.NzbDrone/.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
NzbDrone

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RiderRiderContentModelStore">
<excludedPaths />
</component>
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.NzbDrone/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.NzbDrone/riderModule.iml" />
</modules>
</component>
</project>

103
src/.idea/.idea.NzbDrone/riderModule.iml generated Normal file
View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="RIDER_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$/../../../Logo/1024.png">
<sourceFolder url="file://$MODULE_DIR$/../../../Logo/1024.png" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$/../../../Logo/64.png">
<sourceFolder url="file://$MODULE_DIR$/../../../Logo/64.png" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$/../..">
<sourceFolder url="file://$MODULE_DIR$/../../.nuget/NuGet.exe" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Common/CommonAssemblyInfo.cs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Common/CommonVersionInfo.cs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Common/GlobalSuppressions.cs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../ExternalModules/CurlSharp/CurlSharp" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Libraries/MediaInfo/libmediainfo.0.dylib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Libraries/MediaInfo/MediaInfo.dll" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Libraries/Sqlite/libsqlite3.0.dylib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Libraries/Sqlite/sqlite3.dll" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../LogentriesCore" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../LogentriesNLog" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Marr.Data" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Core" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Owin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../MonoTorrent" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Api" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Api.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.App.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Automation.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Common" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Common.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Console" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Core" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Core.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Host" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Integration.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Libraries.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Mono" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Mono.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.SignalR" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Common" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Dummy" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Update" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Update.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Windows" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Windows.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../ServiceHelpers/ServiceInstall" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../ServiceHelpers/ServiceUninstall" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/../../ExternalModules/CurlSharp/CurlSharp/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../ExternalModules/CurlSharp/CurlSharp/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../LogentriesCore/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../LogentriesCore/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../LogentriesNLog/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../LogentriesNLog/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../Marr.Data/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Core/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Core/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Owin/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Owin/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../MonoTorrent/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Api.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Api.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Api/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.App.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.App.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Automation.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Automation.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Common.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Common.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Common/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Console/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Core.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Core.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Core/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Host/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Integration.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Integration.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Libraries.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Libraries.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Mono.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Mono.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Mono/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.SignalR/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Common/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Common/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Dummy/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Dummy/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Update.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Update.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Update/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Windows.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Windows.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Windows/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../ServiceHelpers/ServiceInstall/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../ServiceHelpers/ServiceUninstall/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../_ReSharper.Caches/ReSharperHost8.NzbDrone.00" />
<excludeFolder url="file://$MODULE_DIR$/../../packages" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -46,7 +46,7 @@ namespace NzbDrone.Api.ClientSchema
field.Value = value; field.Value = value;
} }
if (fieldAttribute.Type == FieldType.Select) if (fieldAttribute.Type == FieldType.Select || fieldAttribute.Type == FieldType.Tag)
{ {
field.SelectOptions = GetSelectOptions(fieldAttribute.SelectOptions); field.SelectOptions = GetSelectOptions(fieldAttribute.SelectOptions);
} }
@@ -150,7 +150,7 @@ namespace NzbDrone.Api.ClientSchema
private static List<SelectOption> GetSelectOptions(Type selectOptions) private static List<SelectOption> GetSelectOptions(Type selectOptions)
{ {
if (selectOptions == typeof(Profile)) if (selectOptions == null || selectOptions == typeof(Profile))
{ {
return new List<SelectOption>(); return new List<SelectOption>();
} }
@@ -165,7 +165,7 @@ namespace NzbDrone.Api.ClientSchema
var options = from Enum e in Enum.GetValues(selectOptions) var options = from Enum e in Enum.GetValues(selectOptions)
select new SelectOption { Value = Convert.ToInt32(e), Name = e.ToString() }; select new SelectOption { Value = Convert.ToInt32(e), Name = e.ToString() };
return options.OrderBy(o => o.Value).ToList(); return options.OrderBy(o => o.Name).ToList();
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser;
namespace NzbDrone.Api.Config namespace NzbDrone.Api.Config
{ {
@@ -12,6 +13,7 @@ namespace NzbDrone.Api.Config
public int AvailabilityDelay { get; set; } public int AvailabilityDelay { get; set; }
public bool AllowHardcodedSubs { get; set; } public bool AllowHardcodedSubs { get; set; }
public string WhitelistedHardcodedSubs { get; set; } public string WhitelistedHardcodedSubs { get; set; }
public ParsingLeniencyType ParsingLeniency { get; set; }
} }
public static class IndexerConfigResourceMapper public static class IndexerConfigResourceMapper
@@ -27,7 +29,7 @@ namespace NzbDrone.Api.Config
AvailabilityDelay = model.AvailabilityDelay, AvailabilityDelay = model.AvailabilityDelay,
AllowHardcodedSubs = model.AllowHardcodedSubs, AllowHardcodedSubs = model.AllowHardcodedSubs,
WhitelistedHardcodedSubs = model.WhitelistedHardcodedSubs, WhitelistedHardcodedSubs = model.WhitelistedHardcodedSubs,
ParsingLeniency = model.ParsingLeniency,
}; };
} }
} }

View File

@@ -0,0 +1,46 @@
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

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Nancy.Responses; using Nancy.Responses;
@@ -38,20 +38,6 @@ namespace NzbDrone.Api.Frontend
return new NotFoundResponse(); 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)); var mapper = _requestMappers.SingleOrDefault(m => m.CanHandle(path));
if (mapper != null) if (mapper != null)

View File

@@ -90,7 +90,7 @@ namespace NzbDrone.Api.Movie
return mappedMovie; return mappedMovie;
} }
var parsedTitle = Parser.ParseMoviePath(f.Name); var parsedTitle = Parser.ParseMoviePath(f.Name, false);
if (parsedTitle == null) if (parsedTitle == null)
{ {
m = new Core.Tv.Movie m = new Core.Tv.Movie

View File

@@ -113,6 +113,7 @@
<Compile Include="Config\NetImportConfigResource.cs" /> <Compile Include="Config\NetImportConfigResource.cs" />
<Compile Include="Extensions\AccessControlHeaders.cs" /> <Compile Include="Extensions\AccessControlHeaders.cs" />
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" /> <Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
<Compile Include="Extensions\Pipelines\UrlBasePipeline.cs" />
<Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" /> <Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" />
<Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" /> <Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" />
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" /> <Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />

View File

@@ -6,17 +6,22 @@ using NzbDrone.Core.MetadataSource;
using System.Linq; using System.Linq;
using System; using System;
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Core.NetImport;
using NzbDrone.Api.NetImport;
namespace NzbDrone.Api.Movie namespace NzbDrone.Api.Movie
{ {
public class MovieDiscoverModule : NzbDroneRestModule<MovieResource> public class MovieDiscoverModule : NzbDroneRestModule<MovieResource>
{ {
private readonly IDiscoverNewMovies _searchProxy; private readonly IDiscoverNewMovies _searchProxy;
private readonly INetImportFactory _netImportFactory;
public MovieDiscoverModule(IDiscoverNewMovies searchProxy) public MovieDiscoverModule(IDiscoverNewMovies searchProxy, INetImportFactory netImportFactory)
: base("/movies/discover") : base("/movies/discover")
{ {
_searchProxy = searchProxy; _searchProxy = searchProxy;
_netImportFactory = netImportFactory;
Get["/lists"] = x => GetLists();
Get["/{action?recommendations}"] = x => Search(x.action); Get["/{action?recommendations}"] = x => Search(x.action);
} }
@@ -26,6 +31,20 @@ namespace NzbDrone.Api.Movie
return MapToResource(imdbResults).AsResponse(); return MapToResource(imdbResults).AsResponse();
} }
private Response GetLists()
{
var lists = _netImportFactory.Discoverable();
return lists.Select(definition => {
var resource = new NetImportResource();
resource.Id = definition.Definition.Id;
resource.Name = definition.Definition.Name;
return resource;
}).AsResponse();
}
private static IEnumerable<MovieResource> MapToResource(IEnumerable<Core.Tv.Movie> movies) private static IEnumerable<MovieResource> MapToResource(IEnumerable<Core.Tv.Movie> movies)
{ {
foreach (var currentSeries in movies) foreach (var currentSeries in movies)

View File

@@ -28,6 +28,7 @@ namespace NzbDrone.Api.Movie
public string Overview { get; set; } public string Overview { get; set; }
public DateTime? InCinemas { get; set; } public DateTime? InCinemas { get; set; }
public DateTime? PhysicalRelease { get; set; } public DateTime? PhysicalRelease { get; set; }
public string PhysicalReleaseNote { get; set; }
public List<MediaCover> Images { get; set; } public List<MediaCover> Images { get; set; }
public string Website { get; set; } public string Website { get; set; }
public bool Downloaded { get; set; } public bool Downloaded { get; set; }
@@ -116,6 +117,7 @@ namespace NzbDrone.Api.Movie
SortTitle = model.SortTitle, SortTitle = model.SortTitle,
InCinemas = model.InCinemas, InCinemas = model.InCinemas,
PhysicalRelease = model.PhysicalRelease, PhysicalRelease = model.PhysicalRelease,
PhysicalReleaseNote = model.PhysicalReleaseNote,
HasFile = model.HasFile, HasFile = model.HasFile,
Downloaded = downloaded, Downloaded = downloaded,
//TotalEpisodeCount //TotalEpisodeCount
@@ -176,6 +178,7 @@ namespace NzbDrone.Api.Movie
SortTitle = resource.SortTitle, SortTitle = resource.SortTitle,
InCinemas = resource.InCinemas, InCinemas = resource.InCinemas,
PhysicalRelease = resource.PhysicalRelease, PhysicalRelease = resource.PhysicalRelease,
PhysicalReleaseNote = resource.PhysicalReleaseNote,
//TotalEpisodeCount //TotalEpisodeCount
//EpisodeCount //EpisodeCount
//EpisodeFileCount //EpisodeFileCount

View File

@@ -63,7 +63,7 @@ namespace NzbDrone.Common.Instrumentation
{ {
var target = new LogentriesTarget(); var target = new LogentriesTarget();
target.Name = "logentriesTarget"; target.Name = "logentriesTarget";
target.Token = "d3a83ee9-74fb-4045-ad25-a84c1d4d7c81"; target.Token = "7688c9ac-015f-45c7-bfee-73f370f5f380";
target.LogHostname = true; target.LogHostname = true;
target.Debug = false; target.Debug = false;

View File

@@ -19,6 +19,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
private List<ReleaseInfo> _reports; private List<ReleaseInfo> _reports;
private RemoteMovie _remoteEpisode; private RemoteMovie _remoteEpisode;
private MappingResult _mappingResult;
private Mock<IDecisionEngineSpecification> _pass1; private Mock<IDecisionEngineSpecification> _pass1;
private Mock<IDecisionEngineSpecification> _pass2; private Mock<IDecisionEngineSpecification> _pass2;
@@ -50,11 +51,15 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_reports = new List<ReleaseInfo> { new ReleaseInfo { Title = "Trolls.2016.720p.WEB-DL.DD5.1.H264-FGT" } }; _reports = new List<ReleaseInfo> { new ReleaseInfo { Title = "Trolls.2016.720p.WEB-DL.DD5.1.H264-FGT" } };
_remoteEpisode = new RemoteMovie { _remoteEpisode = new RemoteMovie {
Movie = new Movie(), Movie = new Movie(),
ParsedMovieInfo = new ParsedMovieInfo()
}; };
_mappingResult = new MappingResult {Movie = new Movie(), MappingResultType = MappingResultType.Success};
_mappingResult.RemoteMovie = _remoteEpisode;
Mocker.GetMock<IParsingService>() Mocker.GetMock<IParsingService>()
.Setup(c => c.Map(It.IsAny<ParsedMovieInfo>(), It.IsAny<string>(), It.IsAny<SearchCriteriaBase>())) .Setup(c => c.Map(It.IsAny<ParsedMovieInfo>(), It.IsAny<string>(), It.IsAny<SearchCriteriaBase>())).Returns(_mappingResult);
.Returns(_remoteEpisode);
} }
private void GivenSpecifications(params Mock<IDecisionEngineSpecification>[] mocks) private void GivenSpecifications(params Mock<IDecisionEngineSpecification>[] mocks)
@@ -121,6 +126,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
GivenSpecifications(_pass1, _pass2, _pass3); GivenSpecifications(_pass1, _pass2, _pass3);
_reports[0].Title = "Not parsable"; _reports[0].Title = "Not parsable";
_mappingResult.MappingResultType = MappingResultType.NotParsable;
var results = Subject.GetRssDecision(_reports).ToList(); var results = Subject.GetRssDecision(_reports).ToList();
@@ -130,7 +136,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteMovie>(), null), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteMovie>(), null), Times.Never());
_pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteMovie>(), null), Times.Never()); _pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteMovie>(), null), Times.Never());
results.Should().BeEmpty(); results.Should().NotBeEmpty();
} }
[Test] [Test]
@@ -138,6 +144,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
GivenSpecifications(_pass1, _pass2, _pass3); GivenSpecifications(_pass1, _pass2, _pass3);
_reports[0].Title = "1937 - Snow White and the Seven Dwarves"; _reports[0].Title = "1937 - Snow White and the Seven Dwarves";
_mappingResult.MappingResultType = MappingResultType.NotParsable;
var results = Subject.GetRssDecision(_reports).ToList(); var results = Subject.GetRssDecision(_reports).ToList();
@@ -147,7 +154,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteMovie>(), null), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteMovie>(), null), Times.Never());
_pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteMovie>(), null), Times.Never()); _pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteMovie>(), null), Times.Never());
results.Should().BeEmpty(); results.Should().NotBeEmpty();
} }
[Test] [Test]
@@ -156,6 +163,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
GivenSpecifications(_pass1, _pass2, _pass3); GivenSpecifications(_pass1, _pass2, _pass3);
_remoteEpisode.Movie = null; _remoteEpisode.Movie = null;
_mappingResult.MappingResultType = MappingResultType.TitleNotFound;
Subject.GetRssDecision(_reports); Subject.GetRssDecision(_reports);
@@ -249,6 +257,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
GivenSpecifications(_pass1, _pass2, _pass3); GivenSpecifications(_pass1, _pass2, _pass3);
_remoteEpisode.Movie = null; _remoteEpisode.Movie = null;
_mappingResult.MappingResultType = MappingResultType.TitleNotFound;
var result = Subject.GetRssDecision(_reports); var result = Subject.GetRssDecision(_reports);

View File

@@ -0,0 +1,111 @@
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine.Specifications.Search;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.TorrentRss;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.DecisionEngineTests.Search
{
[TestFixture]
public class TorrentSeedingSpecificationFixture : TestBase<TorrentSeedingSpecification>
{
private Series _series;
private RemoteEpisode _remoteEpisode;
private IndexerDefinition _indexerDefinition;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateNew().With(s => s.Id = 1).Build();
_remoteEpisode = new RemoteEpisode
{
Series = _series,
Release = new TorrentInfo
{
IndexerId = 1,
Title = "Series.Title.S01.720p.BluRay.X264-RlsGrp",
Seeders = 0
}
};
_indexerDefinition = new IndexerDefinition
{
Settings = new TorrentRssIndexerSettings { MinimumSeeders = 5 }
};
Mocker.GetMock<IIndexerFactory>()
.Setup(v => v.Get(1))
.Returns(_indexerDefinition);
}
private void GivenReleaseSeeders(int? seeders)
{
(_remoteEpisode.Release as TorrentInfo).Seeders = seeders;
}
[Test]
public void should_return_true_if_not_torrent()
{
_remoteEpisode.Release = new ReleaseInfo
{
IndexerId = 1,
Title = "Series.Title.S01.720p.BluRay.X264-RlsGrp"
};
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_indexer_not_specified()
{
_remoteEpisode.Release.IndexerId = 0;
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_indexer_no_longer_exists()
{
Mocker.GetMock<IIndexerFactory>()
.Setup(v => v.Get(It.IsAny<int>()))
.Callback<int>(i => { throw new ModelNotFoundException(typeof(IndexerDefinition), i); });
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_seeds_unknown()
{
GivenReleaseSeeders(null);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[TestCase(5)]
[TestCase(6)]
public void should_return_true_if_seeds_above_or_equal_to_limit(int seeders)
{
GivenReleaseSeeders(seeders);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[TestCase(0)]
[TestCase(4)]
public void should_return_false_if_seeds_belove_limit(int seeders)
{
GivenReleaseSeeders(seeders);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
}
}
}

View File

@@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
Mocker.GetMock<IParsingService>() Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.IsAny<ParsedMovieInfo>(), It.IsAny<string>(), (SearchCriteriaBase)null)) .Setup(s => s.Map(It.IsAny<ParsedMovieInfo>(), It.IsAny<string>(), (SearchCriteriaBase)null))
.Returns(() => CreateRemoteMovie()); .Returns(() => new MappingResult{RemoteMovie = CreateRemoteMovie(), MappingResultType = MappingResultType.Success});
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>())) .Setup(s => s.Get(It.IsAny<HttpRequest>()))

View File

@@ -50,7 +50,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
Mocker.GetMock<IParsingService>() Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.Is<ParsedMovieInfo>(i => i.MovieTitle == "A Movie"), It.IsAny<string>(), null)) .Setup(s => s.Map(It.Is<ParsedMovieInfo>(i => i.MovieTitle == "A Movie"), It.IsAny<string>(), null))
.Returns(remoteEpisode); .Returns(new MappingResult{RemoteMovie = remoteEpisode});
var client = new DownloadClientDefinition() var client = new DownloadClientDefinition()
{ {

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.IndexerTests.IPTorrentsTests
Subject.Definition = new IndexerDefinition() Subject.Definition = new IndexerDefinition()
{ {
Name = "IPTorrents", Name = "IPTorrents",
Settings = new IPTorrentsSettings() { Url = "http://fake.com/" } Settings = new IPTorrentsSettings() { BaseUrl = "http://fake.com/" }
}; };
} }

View File

@@ -14,6 +14,7 @@ using NzbDrone.Test.Common.Categories;
namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests
{ {
[IntegrationTest] [IntegrationTest]
[Ignore("Nyaa is down!")]
public class IndexerIntegrationTests : CoreTest public class IndexerIntegrationTests : CoreTest
{ {
private SingleEpisodeSearchCriteria _singleSearchCriteria; private SingleEpisodeSearchCriteria _singleSearchCriteria;

View File

@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
{ {
_settings = new NewznabSettings() _settings = new NewznabSettings()
{ {
Url = "http://indxer.local" BaseUrl = "http://indxer.local"
}; };
_caps = ReadAllText("Files/Indexers/Newznab/newznab_caps.xml"); _caps = ReadAllText("Files/Indexers/Newznab/newznab_caps.xml");

View File

@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
Name = "Newznab", Name = "Newznab",
Settings = new NewznabSettings() Settings = new NewznabSettings()
{ {
Url = "http://indexer.local/", BaseUrl = "http://indexer.local/",
Categories = new int[] { 1 } Categories = new int[] { 1 }
} }
}; };

View File

@@ -19,7 +19,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
{ {
Subject.Settings = new NewznabSettings() Subject.Settings = new NewznabSettings()
{ {
Url = "http://127.0.0.1:1234/", BaseUrl = "http://127.0.0.1:1234/",
Categories = new [] { 1, 2 }, Categories = new [] { 1, 2 },
AnimeCategories = new [] { 3, 4 }, AnimeCategories = new [] { 3, 4 },
ApiKey = "abcd", ApiKey = "abcd",

View File

@@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
var setting = new NewznabSettings() var setting = new NewznabSettings()
{ {
ApiKey = "", ApiKey = "",
Url = url BaseUrl = url
}; };
@@ -32,13 +32,13 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
var setting = new NewznabSettings var setting = new NewznabSettings
{ {
ApiKey = "", ApiKey = "",
Url = url BaseUrl = url
}; };
setting.Validate().IsValid.Should().BeFalse(); setting.Validate().IsValid.Should().BeFalse();
setting.Validate().Errors.Should().NotContain(c => c.PropertyName == "ApiKey"); setting.Validate().Errors.Should().NotContain(c => c.PropertyName == "ApiKey");
setting.Validate().Errors.Should().Contain(c => c.PropertyName == "Url"); setting.Validate().Errors.Should().Contain(c => c.PropertyName == "BaseUrl");
} }
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
var setting = new NewznabSettings() var setting = new NewznabSettings()
{ {
ApiKey = "", ApiKey = "",
Url = url BaseUrl = url
}; };

View File

@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
Name = "Torznab", Name = "Torznab",
Settings = new TorznabSettings() Settings = new TorznabSettings()
{ {
Url = "http://indexer.local/", BaseUrl = "http://indexer.local/",
Categories = new int[] { 1 } Categories = new int[] { 1 }
} }
}; };

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.NetImport
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
Subject.Definition = Subject.DefaultDefinitions.First(); Subject.Definition = Subject.GetDefaultDefinitions().First();
} }
private void GivenRecentFeedResponse(string rssXmlFile) private void GivenRecentFeedResponse(string rssXmlFile)
{ {

View File

@@ -159,6 +159,7 @@
<Compile Include="DecisionEngineTests\RetentionSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\RetentionSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\DelaySpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\RssSync\DelaySpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\Search\TorrentSeedingSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\Search\SeriesSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\Search\SeriesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\SameEpisodesSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\SameEpisodesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RawDiskSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\RawDiskSpecificationFixture.cs" />

View File

@@ -51,9 +51,12 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Castle.2009.S01E14.HDTV.XviD.HUN-LOL", Language.Hungarian)] [TestCase("Castle.2009.S01E14.HDTV.XviD.HUN-LOL", Language.Hungarian)]
[TestCase("The Danish Girl 2015", Language.English)] [TestCase("The Danish Girl 2015", Language.English)]
[TestCase("Passengers.2016.German.DL.AC3.Dubbed.1080p.WebHD.h264.iNTERNAL-PsO", Language.German)] [TestCase("Passengers.2016.German.DL.AC3.Dubbed.1080p.WebHD.h264.iNTERNAL-PsO", Language.German)]
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", Language.German)]
[TestCase("Passengers.German.DL.AC3.Dubbed..BluRay.x264-PsO", Language.German)]
[TestCase("Valana la Legende FRENCH BluRay 720p 2016 kjhlj", Language.French)]
public void should_parse_language(string postTitle, Language language) public void should_parse_language(string postTitle, Language language)
{ {
var result = Parser.Parser.ParseMovieTitle(postTitle); var result = Parser.Parser.ParseMovieTitle(postTitle, true);
if (result == null) if (result == null)
{ {
Parser.Parser.ParseTitle(postTitle).Language.Should().Be(language); Parser.Parser.ParseTitle(postTitle).Language.Should().Be(language);

View File

@@ -63,6 +63,7 @@ namespace NzbDrone.Core.Test.ParserTests
Parser.Parser.ParseTitle(postTitle).SeriesTitle.Should().Be(title); Parser.Parser.ParseTitle(postTitle).SeriesTitle.Should().Be(title);
} }
//Note: This assumes extended language parser is activated
[TestCase("The.Man.from.U.N.C.L.E.2015.1080p.BluRay.x264-SPARKS", "The Man from U.N.C.L.E.")] [TestCase("The.Man.from.U.N.C.L.E.2015.1080p.BluRay.x264-SPARKS", "The Man from U.N.C.L.E.")]
[TestCase("1941.1979.EXTENDED.720p.BluRay.X264-AMIABLE", "1941")] [TestCase("1941.1979.EXTENDED.720p.BluRay.X264-AMIABLE", "1941")]
[TestCase("MY MOVIE (2016) [R][Action, Horror][720p.WEB-DL.AVC.8Bit.6ch.AC3].mkv", "MY MOVIE")] [TestCase("MY MOVIE (2016) [R][Action, Horror][720p.WEB-DL.AVC.8Bit.6ch.AC3].mkv", "MY MOVIE")]
@@ -76,22 +77,31 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("A.Movie.Name.(1998)", "A Movie Name")] [TestCase("A.Movie.Name.(1998)", "A Movie Name")]
[TestCase("Thor: The Dark World 2013", "Thor The Dark World")] [TestCase("Thor: The Dark World 2013", "Thor The Dark World")]
[TestCase("Resident.Evil.The.Final.Chapter.2016", "Resident Evil The Final Chapter")] [TestCase("Resident.Evil.The.Final.Chapter.2016", "Resident Evil The Final Chapter")]
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", "Der Soldat James")]
[TestCase("Passengers.German.DL.AC3.Dubbed..BluRay.x264-PsO", "Passengers")]
[TestCase("Valana la Legende FRENCH BluRay 720p 2016 kjhlj", "Valana la Legende")]
[TestCase("Valana la Legende TRUEFRENCH BluRay 720p 2016 kjhlj", "Valana la Legende")]
[TestCase("Mission Impossible: Rogue Nation (2015)<29>[XviD - Ita Ac3 - SoftSub Ita]azione, spionaggio, thriller *Prima Visione* Team mulnic Tom Cruise", "Mission Impossible Rogue Nation")]
[TestCase("Scary.Movie.2000.FRENCH..BluRay.-AiRLiNE", "Scary Movie")]
[TestCase("My Movie 1999 German Bluray", "My Movie")]
public void should_parse_movie_title(string postTitle, string title) public void should_parse_movie_title(string postTitle, string title)
{ {
Parser.Parser.ParseMovieTitle(postTitle).MovieTitle.Should().Be(title); Parser.Parser.ParseMovieTitle(postTitle, true).MovieTitle.Should().Be(title);
} }
[TestCase("1941.1979.EXTENDED.720p.BluRay.X264-AMIABLE", 1979)] [TestCase("1941.1979.EXTENDED.720p.BluRay.X264-AMIABLE", 1979)]
[TestCase("Valana la Legende FRENCH BluRay 720p 2016 kjhlj", 2016)]
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", 1998)]
public void should_parse_movie_year(string postTitle, int year) public void should_parse_movie_year(string postTitle, int year)
{ {
Parser.Parser.ParseMovieTitle(postTitle).Year.Should().Be(year); Parser.Parser.ParseMovieTitle(postTitle, false).Year.Should().Be(year);
} }
[TestCase("The Danish Girl 2015")] [TestCase("The Danish Girl 2015")]
[TestCase("The.Danish.Girl.2015.1080p.BluRay.x264.DTS-HD.MA.5.1-RARBG")] [TestCase("The.Danish.Girl.2015.1080p.BluRay.x264.DTS-HD.MA.5.1-RARBG")]
public void should_not_parse_language_in_movie_title(string postTitle) public void should_not_parse_language_in_movie_title(string postTitle)
{ {
Parser.Parser.ParseMovieTitle(postTitle).Language.Should().Be(Language.English); Parser.Parser.ParseMovieTitle(postTitle, false).Language.Should().Be(Language.English);
} }
[TestCase("Prometheus 2012 Directors Cut", "Directors Cut")] [TestCase("Prometheus 2012 Directors Cut", "Directors Cut")]
@@ -117,9 +127,9 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Prometheus Extended Directors Cut Fan Edit 2012", "Extended Directors Cut Fan Edit")] [TestCase("Prometheus Extended Directors Cut Fan Edit 2012", "Extended Directors Cut Fan Edit")]
[TestCase("Prometheus Director's Cut 2012", "Director's Cut")] [TestCase("Prometheus Director's Cut 2012", "Director's Cut")]
[TestCase("Prometheus Directors Cut 2012", "Directors Cut")] [TestCase("Prometheus Directors Cut 2012", "Directors Cut")]
[TestCase("Prometheus.(Extended.Theatrical.Version.IMAX).BluRay.1080p.2012.asdf", "Extended Theatrical Version IMAX")] [TestCase("Prometheus.(Extended.Theatrical.Version.IMAX).2012.BluRay.1080p.asdf", "Extended Theatrical Version IMAX")]
[TestCase("2001 A Space Odyssey Director's Cut (1968).mkv", "Director's Cut")] [TestCase("2001 A Space Odyssey Director's Cut (1968).mkv", "Director's Cut")]
[TestCase("2001: A Space Odyssey (Extended Directors Cut FanEdit) Bluray 1080p 1968", "Extended Directors Cut FanEdit")] [TestCase("2001: A Space Odyssey (Extended Directors Cut FanEdit) 1968 Bluray 1080p", "Extended Directors Cut FanEdit")]
[TestCase("A Fake Movie 2035 Directors 2012.mkv", "Directors")] [TestCase("A Fake Movie 2035 Directors 2012.mkv", "Directors")]
[TestCase("Blade Runner Director's Cut 2049.mkv", "Director's Cut")] [TestCase("Blade Runner Director's Cut 2049.mkv", "Director's Cut")]
[TestCase("Prometheus 50th Anniversary Edition 2012.mkv", "50th Anniversary Edition")] [TestCase("Prometheus 50th Anniversary Edition 2012.mkv", "50th Anniversary Edition")]
@@ -127,9 +137,20 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie IMAX 2012.mkv", "IMAX")] [TestCase("Movie IMAX 2012.mkv", "IMAX")]
[TestCase("Fake Movie Final Cut 2016", "Final Cut")] [TestCase("Fake Movie Final Cut 2016", "Final Cut")]
[TestCase("Fake Movie 2016 Final Cut ", "Final Cut")] [TestCase("Fake Movie 2016 Final Cut ", "Final Cut")]
[TestCase("My Movie GERMAN Extended Cut 2016", "Extended Cut")]
[TestCase("My.Movie.GERMAN.Extended.Cut.2016", "Extended Cut")]
[TestCase("My.Movie.GERMAN.Extended.Cut", "Extended Cut")]
[TestCase("Mission Impossible: Rogue Nation 2012 Bluray", "")]
public void should_parse_edition(string postTitle, string edition) public void should_parse_edition(string postTitle, string edition)
{ {
Parser.Parser.ParseMovieTitle(postTitle).Edition.Should().Be(edition); Parser.Parser.ParseMovieTitle(postTitle, true).Edition.Should().Be(edition);
}
[TestCase("The Lord of the Rings The Fellowship of the Ring (Extended Edition) 1080p BD25", "The Lord Of The Rings The Fellowship Of The Ring", "Extended Edition")]
[TestCase("The.Lord.of.the.Rings.The.Fellowship.of.the.Ring.(Extended.Edition).1080p.BD25", "The Lord Of The Rings The Fellowship Of The Ring", "Extended Edition")]
public void should_parse_edition_lenient_mapping(string postTitle, string foundTitle, string edition)
{
Parser.Parser.ParseMinimalMovieTitle(postTitle, foundTitle, 1290).Edition.Should().Be(edition);
} }
} }
} }

View File

@@ -21,6 +21,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
private Movie _movie; private Movie _movie;
private ParsedMovieInfo _parsedMovieInfo; private ParsedMovieInfo _parsedMovieInfo;
private ParsedMovieInfo _wrongYearInfo; private ParsedMovieInfo _wrongYearInfo;
private ParsedMovieInfo _wrongTitleInfo;
private ParsedMovieInfo _romanTitleInfo; private ParsedMovieInfo _romanTitleInfo;
private ParsedMovieInfo _alternativeTitleInfo; private ParsedMovieInfo _alternativeTitleInfo;
private ParsedMovieInfo _umlautInfo; private ParsedMovieInfo _umlautInfo;
@@ -71,6 +72,12 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Year = 1900, Year = 1900,
}; };
_wrongTitleInfo = new ParsedMovieInfo
{
MovieTitle = "Other Title",
Year = 2015
};
_alternativeTitleInfo = new ParsedMovieInfo _alternativeTitleInfo = new ParsedMovieInfo
{ {
MovieTitle = _movie.AlternativeTitles.First(), MovieTitle = _movie.AlternativeTitles.First(),
@@ -139,7 +146,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedMovieInfo, "", _movieSearchCriteria); Subject.Map(_parsedMovieInfo, "", _movieSearchCriteria);
Mocker.GetMock<ISeriesService>() Mocker.GetMock<IMovieService>()
.Verify(v => v.FindByTitle(It.IsAny<string>()), Times.Never()); .Verify(v => v.FindByTitle(It.IsAny<string>()), Times.Never());
} }
@@ -147,7 +154,24 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
public void should_not_match_with_wrong_year() public void should_not_match_with_wrong_year()
{ {
GivenMatchByMovieTitle(); GivenMatchByMovieTitle();
Subject.Map(_wrongYearInfo, "", _movieSearchCriteria).Movie.Should().BeNull(); Subject.Map(_wrongYearInfo, "", _movieSearchCriteria).MappingResultType.Should().Be(MappingResultType.WrongYear);
}
[Test]
public void should_not_match_wrong_title()
{
GivenMatchByMovieTitle();
Subject.Map(_wrongTitleInfo, "", _movieSearchCriteria).MappingResultType.Should().Be(MappingResultType.WrongTitle);
}
[Test]
public void should_return_title_not_found_when_all_is_null()
{
Mocker.GetMock<IMovieService>()
.Setup(s => s.FindByTitle(It.IsAny<string>()))
.Returns((Movie)null);
Subject.Map(_parsedMovieInfo, "", null).MappingResultType.Should()
.Be(MappingResultType.TitleNotFound);
} }
[Test] [Test]

View File

@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Test.ParserTests.RomanNumeralTests
[Test(Description = "Converts the supported range [1-3999] of Arabic to Roman numerals.")] [Test(Description = "Converts the supported range [1-3999] of Arabic to Roman numerals.")]
[Order(0)] [Order(0)]
public void should_convert_arabic_numeral_to_roman_numeral([Range(1,3999)] int arabicNumeral) public void should_convert_arabic_numeral_to_roman_numeral([Range(1,20)] int arabicNumeral)
{ {
RomanNumeral romanNumeral = new RomanNumeral(arabicNumeral); RomanNumeral romanNumeral = new RomanNumeral(arabicNumeral);
@@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.ParserTests.RomanNumeralTests
[Test] [Test]
[Order(1)] [Order(1)]
public void should_convert_roman_numeral_to_arabic_numeral([Range(1, 3999)] int arabicNumeral) public void should_convert_roman_numeral_to_arabic_numeral([Range(1, 20)] int arabicNumeral)
{ {
RomanNumeral romanNumeral = new RomanNumeral(_arabicToRomanNumeralsMapping[arabicNumeral]); RomanNumeral romanNumeral = new RomanNumeral(_arabicToRomanNumeralsMapping[arabicNumeral]);

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -141,7 +141,21 @@ namespace NzbDrone.Core.Configuration
public bool LaunchBrowser => GetValueBoolean("LaunchBrowser", true); public bool LaunchBrowser => GetValueBoolean("LaunchBrowser", true);
public string ApiKey => GetValue("ApiKey", GenerateApiKey()); public string ApiKey
{
get
{
var apiKey = GetValue("ApiKey", GenerateApiKey());
if (apiKey.IsNullOrWhiteSpace())
{
apiKey = GenerateApiKey();
SetValue("ApiKey", apiKey);
}
return apiKey;
}
}
public AuthenticationType AuthenticationMethod public AuthenticationType AuthenticationMethod
{ {

View File

@@ -8,6 +8,7 @@ using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Common.Http.Proxy; using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Configuration namespace NzbDrone.Core.Configuration
{ {
@@ -211,6 +212,12 @@ namespace NzbDrone.Core.Configuration
set { SetValue("WhitelistedHardcodedSubs", value); } set { SetValue("WhitelistedHardcodedSubs", value); }
} }
public ParsingLeniencyType ParsingLeniency
{
get { return GetValueEnum<ParsingLeniencyType>("ParsingLeniency", ParsingLeniencyType.Strict); }
set { SetValue("ParsingLeniency", value); }
}
public bool RemoveCompletedDownloads public bool RemoveCompletedDownloads
{ {
get { return GetValueBoolean("RemoveCompletedDownloads", false); } get { return GetValueBoolean("RemoveCompletedDownloads", false); }

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Common.Http.Proxy; using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Configuration namespace NzbDrone.Core.Configuration
{ {
@@ -54,6 +55,7 @@ namespace NzbDrone.Core.Configuration
bool AllowHardcodedSubs { get; set; } bool AllowHardcodedSubs { get; set; }
string WhitelistedHardcodedSubs { get; set; } string WhitelistedHardcodedSubs { get; set; }
ParsingLeniencyType ParsingLeniency { get; set; }
int NetImportSyncInterval { get; set; } int NetImportSyncInterval { get; set; }
string ListSyncLevel { get; set; } string ListSyncLevel { get; set; }

View File

@@ -26,13 +26,13 @@ namespace NzbDrone.Core.Datastore.Migration
var id = seriesReader.GetInt32(0); var id = seriesReader.GetInt32(0);
var relativePath = seriesReader.GetString(1); var relativePath = seriesReader.GetString(1);
var result = Parser.Parser.ParseMovieTitle(relativePath); var result = Parser.Parser.ParseMovieTitle(relativePath, false);
var edition = ""; var edition = "";
if (result != null) if (result != null)
{ {
edition = Parser.Parser.ParseMovieTitle(relativePath).Edition; edition = Parser.Parser.ParseMovieTitle(relativePath, false).Edition;
} }
using (IDbCommand updateCmd = conn.CreateCommand()) using (IDbCommand updateCmd = conn.CreateCommand())

View File

@@ -0,0 +1,16 @@
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(138)]
public class add_physical_release_note : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Movies").AddColumn("PhysicalReleaseNote").AsString().Nullable();
}
}
}

View File

@@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Data;
using FluentMigrator;
using Newtonsoft.Json.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(139)]
public class consolidate_indexer_baseurl : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(RenameUrlToBaseUrl);
}
private void RenameUrlToBaseUrl(IDbConnection conn, IDbTransaction tran)
{
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = tran;
cmd.CommandText = "SELECT Id, Settings FROM Indexers WHERE ConfigContract IN ('NewznabSettings', 'TorznabSettings', 'IPTorrentsSettings', 'OmgwtfnzbsSettings')";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var id = reader.GetInt32(0);
var settings = reader.GetString(1);
if (settings.IsNotNullOrWhiteSpace())
{
var jsonObject = Json.Deserialize<JObject>(settings);
if (jsonObject.Property("url") != null)
{
jsonObject.AddFirst(new JProperty("baseUrl", jsonObject["url"]));
jsonObject.Remove("url");
settings = jsonObject.ToJson();
using (var updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE Indexers SET Settings = ? WHERE Id = ?";
updateCmd.AddParameter(settings);
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}
}
}

View File

@@ -9,6 +9,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine namespace NzbDrone.Core.DecisionEngine
{ {
@@ -69,52 +70,87 @@ namespace NzbDrone.Core.DecisionEngine
try try
{ {
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(report.Title); var parsedMovieInfo = Parser.Parser.ParseMovieTitle(report.Title, _configService.ParsingLeniency > 0);
if (parsedMovieInfo != null && !parsedMovieInfo.MovieTitle.IsNullOrWhiteSpace()) MappingResult result = null;
{
RemoteMovie remoteMovie = _parsingService.Map(parsedMovieInfo, report.ImdbId.ToString(), searchCriteria);
remoteMovie.Release = report;
if (remoteMovie.Movie == null) if (parsedMovieInfo == null || parsedMovieInfo.MovieTitle.IsNullOrWhiteSpace())
{ {
decision = new DownloadDecision(remoteMovie, new Rejection("Unknown movie. Movie found does not match wanted movie.")); _logger.Debug("{0} could not be parsed :(.", report.Title);
} parsedMovieInfo = new ParsedMovieInfo
else {
{ MovieTitle = report.Title,
if (parsedMovieInfo.Quality.HardcodedSubs.IsNotNullOrWhiteSpace()) Year = 1290,
{ Language = Language.Unknown,
remoteMovie.DownloadAllowed = true; Quality = new QualityModel(),
if (_configService.AllowHardcodedSubs) };
{
decision = GetDecisionForReport(remoteMovie, searchCriteria);
}
else
{
var whitelisted = _configService.WhitelistedHardcodedSubs.Split(',');
_logger.Debug("Testing: {0}", whitelisted);
if (whitelisted != null && whitelisted.Any(t => (parsedMovieInfo.Quality.HardcodedSubs.ToLower().Contains(t.ToLower()) && t.IsNotNullOrWhiteSpace())))
{
decision = GetDecisionForReport(remoteMovie, searchCriteria);
}
else
{
decision = new DownloadDecision(remoteMovie, new Rejection("Hardcoded subs found: " + parsedMovieInfo.Quality.HardcodedSubs));
}
}
}
else
{
remoteMovie.DownloadAllowed = true;
decision = GetDecisionForReport(remoteMovie, searchCriteria);
}
} if (_configService.ParsingLeniency == ParsingLeniencyType.MappingLenient)
} {
else result = _parsingService.Map(parsedMovieInfo, report.ImdbId.ToString(), searchCriteria);
{ }
_logger.Trace("{0} could not be parsed :(.", report.Title);
} if (result == null || result.MappingResultType != MappingResultType.SuccessLenientMapping)
{
result = new MappingResult {MappingResultType = MappingResultType.NotParsable};
result.Movie = null; //To ensure we have a remote movie, else null exception on next line!
result.RemoteMovie.ParsedMovieInfo = parsedMovieInfo;
}
else
{
//Enhance Parsed Movie Info!
result.RemoteMovie.ParsedMovieInfo = Parser.Parser.ParseMinimalMovieTitle(parsedMovieInfo.MovieTitle,
result.RemoteMovie.Movie.Title, parsedMovieInfo.Year);
}
}
else
{
result = _parsingService.Map(parsedMovieInfo, report.ImdbId.ToString(), searchCriteria);
}
result.ReleaseName = report.Title;
var remoteMovie = result.RemoteMovie;
remoteMovie.Release = report;
if (result.MappingResultType != MappingResultType.Success && result.MappingResultType != MappingResultType.SuccessLenientMapping)
{
var rejection = result.ToRejection();
remoteMovie.Movie = null; // HACK: For now!
decision = new DownloadDecision(remoteMovie, rejection);
}
else
{
if (parsedMovieInfo.Quality.HardcodedSubs.IsNotNullOrWhiteSpace())
{
remoteMovie.DownloadAllowed = true;
if (_configService.AllowHardcodedSubs)
{
decision = GetDecisionForReport(remoteMovie, searchCriteria);
}
else
{
var whitelisted = _configService.WhitelistedHardcodedSubs.Split(',');
_logger.Debug("Testing: {0}", whitelisted);
if (whitelisted != null && whitelisted.Any(t => (parsedMovieInfo.Quality.HardcodedSubs.ToLower().Contains(t.ToLower()) && t.IsNotNullOrWhiteSpace())))
{
decision = GetDecisionForReport(remoteMovie, searchCriteria);
}
else
{
decision = new DownloadDecision(remoteMovie, new Rejection("Hardcoded subs found: " + parsedMovieInfo.Quality.HardcodedSubs));
}
}
}
else
{
remoteMovie.DownloadAllowed = true;
decision = GetDecisionForReport(remoteMovie, searchCriteria);
}
}
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -1,55 +0,0 @@
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class TorrentSeedingSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public TorrentSeedingSpecification(Logger logger)
{
_logger = logger;
}
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release as TorrentInfo;
if (torrentInfo == null)
{
return Decision.Accept();
}
if (torrentInfo.Seeders != null && torrentInfo.Seeders < 1)
{
_logger.Debug("Not enough seeders. ({0})", torrentInfo.Seeders);
return Decision.Reject("Not enough seeders. ({0})", torrentInfo.Seeders);
}
return Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release as TorrentInfo;
if (torrentInfo == null)
{
return Decision.Accept();
}
if (torrentInfo.Seeders != null && torrentInfo.Seeders < 1)
{
_logger.Debug("Not enough seeders. ({0})", torrentInfo.Seeders);
return Decision.Reject("Not enough seeders. ({0})", torrentInfo.Seeders);
}
return Decision.Accept();
}
}
}

View File

@@ -0,0 +1,96 @@
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class TorrentSeedingSpecification : IDecisionEngineSpecification
{
private readonly IIndexerFactory _indexerFactory;
private readonly Logger _logger;
public TorrentSeedingSpecification(IIndexerFactory indexerFactory, Logger logger)
{
_indexerFactory = indexerFactory;
_logger = logger;
}
//public SpecificationPriority Priority => SpecificationPriority.Default;
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release as TorrentInfo;
if (torrentInfo == null || torrentInfo.IndexerId == 0)
{
return Decision.Accept();
}
IndexerDefinition indexer;
try
{
indexer = _indexerFactory.Get(torrentInfo.IndexerId);
}
catch (ModelNotFoundException)
{
_logger.Debug("Indexer with id {0} does not exist, skipping seeders check", torrentInfo.IndexerId);
return Decision.Accept();
}
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
if (torrentIndexerSettings != null)
{
var minimumSeeders = torrentIndexerSettings.MinimumSeeders;
if (torrentInfo.Seeders.HasValue && torrentInfo.Seeders.Value < minimumSeeders)
{
_logger.Debug("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
return Decision.Reject("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
}
}
return Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release as TorrentInfo;
if (torrentInfo == null || torrentInfo.IndexerId == 0)
{
return Decision.Accept();
}
IndexerDefinition indexer;
try
{
indexer = _indexerFactory.Get(torrentInfo.IndexerId);
}
catch (ModelNotFoundException)
{
_logger.Debug("Indexer with id {0} does not exist, skipping seeders check", torrentInfo.IndexerId);
return Decision.Accept();
}
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
if (torrentIndexerSettings != null)
{
var minimumSeeders = torrentIndexerSettings.MinimumSeeders;
if (torrentInfo.Seeders.HasValue && torrentInfo.Seeders.Value < minimumSeeders)
{
_logger.Debug("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
return Decision.Reject("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
}
}
return Decision.Accept();
}
}
}

View File

@@ -28,7 +28,10 @@ namespace NzbDrone.Core.Download
public virtual ProviderMessage Message => null; public virtual ProviderMessage Message => null;
public IEnumerable<ProviderDefinition> DefaultDefinitions => new List<ProviderDefinition>(); public IEnumerable<ProviderDefinition> GetDefaultDefinitions()
{
return new List<ProviderDefinition>();
}
public ProviderDefinition Definition { get; set; } public ProviderDefinition Definition { get; set; }

View File

@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Download
_httpClient = httpClient; _httpClient = httpClient;
_torrentFileInfoReader = torrentFileInfoReader; _torrentFileInfoReader = torrentFileInfoReader;
} }
public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public virtual bool PreferTorrentFile => false; public virtual bool PreferTorrentFile => false;
@@ -149,7 +149,7 @@ namespace NzbDrone.Core.Download
{ {
magnetUrl = torrentInfo.MagnetUrl; magnetUrl = torrentInfo.MagnetUrl;
} }
if (PreferTorrentFile) if (PreferTorrentFile)
{ {
if (torrentUrl.IsNotNullOrWhiteSpace()) if (torrentUrl.IsNotNullOrWhiteSpace())
@@ -221,7 +221,9 @@ namespace NzbDrone.Core.Download
var response = _httpClient.Get(request); var response = _httpClient.Get(request);
if (response.StatusCode == HttpStatusCode.SeeOther || response.StatusCode == HttpStatusCode.Found) if (response.StatusCode == HttpStatusCode.MovedPermanently ||
response.StatusCode == HttpStatusCode.Found ||
response.StatusCode == HttpStatusCode.SeeOther)
{ {
var locationHeader = response.Headers.GetSingleValue("Location"); var locationHeader = response.Headers.GetSingleValue("Location");
@@ -321,7 +323,9 @@ namespace NzbDrone.Core.Download
var response = _httpClient.Get(request); var response = _httpClient.Get(request);
if (response.StatusCode == HttpStatusCode.SeeOther || response.StatusCode == HttpStatusCode.Found) if (response.StatusCode == HttpStatusCode.MovedPermanently ||
response.StatusCode == HttpStatusCode.Found ||
response.StatusCode == HttpStatusCode.SeeOther)
{ {
var locationHeader = response.Headers.GetSingleValue("Location"); var locationHeader = response.Headers.GetSingleValue("Location");

View File

@@ -3,6 +3,7 @@ using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
@@ -18,17 +19,20 @@ namespace NzbDrone.Core.Download.TrackedDownloads
{ {
private readonly IParsingService _parsingService; private readonly IParsingService _parsingService;
private readonly IHistoryService _historyService; private readonly IHistoryService _historyService;
private readonly IConfigService _config;
private readonly Logger _logger; private readonly Logger _logger;
private readonly ICached<TrackedDownload> _cache; private readonly ICached<TrackedDownload> _cache;
public TrackedDownloadService(IParsingService parsingService, public TrackedDownloadService(IParsingService parsingService,
ICacheManager cacheManager, ICacheManager cacheManager,
IHistoryService historyService, IHistoryService historyService,
IConfigService config,
Logger logger) Logger logger)
{ {
_parsingService = parsingService; _parsingService = parsingService;
_historyService = historyService; _historyService = historyService;
_cache = cacheManager.GetCache<TrackedDownload>(GetType()); _cache = cacheManager.GetCache<TrackedDownload>(GetType());
_config = config;
_logger = logger; _logger = logger;
} }
@@ -56,12 +60,12 @@ namespace NzbDrone.Core.Download.TrackedDownloads
try try
{ {
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(trackedDownload.DownloadItem.Title); var parsedMovieInfo = Parser.Parser.ParseMovieTitle(trackedDownload.DownloadItem.Title, _config.ParsingLeniency > 0);
var historyItems = _historyService.FindByDownloadId(downloadItem.DownloadId); var historyItems = _historyService.FindByDownloadId(downloadItem.DownloadId);
if (parsedMovieInfo != null) if (parsedMovieInfo != null)
{ {
trackedDownload.RemoteMovie = _parsingService.Map(parsedMovieInfo, "", null); trackedDownload.RemoteMovie = _parsingService.Map(parsedMovieInfo, "", null).RemoteMovie;
} }
if (historyItems.Any()) if (historyItems.Any())
@@ -73,11 +77,11 @@ namespace NzbDrone.Core.Download.TrackedDownloads
trackedDownload.RemoteMovie == null || trackedDownload.RemoteMovie == null ||
trackedDownload.RemoteMovie.Movie == null) trackedDownload.RemoteMovie.Movie == null)
{ {
parsedMovieInfo = Parser.Parser.ParseMovieTitle(firstHistoryItem.SourceTitle); parsedMovieInfo = Parser.Parser.ParseMovieTitle(firstHistoryItem.SourceTitle, _config.ParsingLeniency > 0);
if (parsedMovieInfo != null) if (parsedMovieInfo != null)
{ {
trackedDownload.RemoteMovie = _parsingService.Map(parsedMovieInfo, "", null); trackedDownload.RemoteMovie = _parsingService.Map(parsedMovieInfo, "", null).RemoteMovie;
} }
} }
} }

View File

@@ -17,7 +17,10 @@ namespace NzbDrone.Core.Extras.Metadata
public virtual ProviderMessage Message => null; public virtual ProviderMessage Message => null;
public IEnumerable<ProviderDefinition> DefaultDefinitions => new List<ProviderDefinition>(); public IEnumerable<ProviderDefinition> GetDefaultDefinitions()
{
return new List<ProviderDefinition>();
}
public ProviderDefinition Definition { get; set; } public ProviderDefinition Definition { get; set; }

View File

@@ -19,7 +19,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (enabled.Empty()) if (enabled.Empty())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, "No indexers available with Search enabled, Sonarr will not provide any search results"); return new HealthCheck(GetType(), HealthCheckResult.Warning, "No indexers available with Search enabled, Radarr will not provide any search results");
} }
var active = _indexerFactory.SearchEnabled(true); var active = _indexerFactory.SearchEnabled(true);

View File

@@ -14,13 +14,14 @@ namespace NzbDrone.Core.Indexers.AwesomeHD
} }
} }
public class AwesomeHDSettings : IProviderConfig public class AwesomeHDSettings : ITorrentIndexerSettings
{ {
private static readonly AwesomeHDSettingsValidator Validator = new AwesomeHDSettingsValidator(); private static readonly AwesomeHDSettingsValidator Validator = new AwesomeHDSettingsValidator();
public AwesomeHDSettings() public AwesomeHDSettings()
{ {
BaseUrl = "https://awesome-hd.me"; BaseUrl = "https://awesome-hd.me";
MinimumSeeders = 0;
} }
[FieldDefinition(0, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since you Passkey will be sent to that host.")] [FieldDefinition(0, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since you Passkey will be sent to that host.")]
@@ -32,6 +33,9 @@ namespace NzbDrone.Core.Indexers.AwesomeHD
[FieldDefinition(2, Type = FieldType.Checkbox, Label = "Require Internal", HelpText = "Will only include internal releases for RSS Sync.")] [FieldDefinition(2, Type = FieldType.Checkbox, Label = "Require Internal", HelpText = "Will only include internal releases for RSS Sync.")]
public bool Internal { get; set; } public bool Internal { get; set; }
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -59,6 +59,10 @@ namespace NzbDrone.Core.Indexers.HDBits
query.Username = Settings.Username; query.Username = Settings.Username;
query.Passkey = Settings.ApiKey; query.Passkey = Settings.ApiKey;
query.Category = Settings.Categories.ToArray();
query.Codec = Settings.Codecs.ToArray();
query.Medium = Settings.Mediums.ToArray();
// Require Internal only if came from RSS sync // Require Internal only if came from RSS sync
if (Settings.RequireInternal && query.ImdbInfo == null) if (Settings.RequireInternal && query.ImdbInfo == null)
{ {

View File

@@ -1,7 +1,11 @@
using FluentValidation; using System;
using System.Linq;
using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
using System.Linq.Expressions;
using FluentValidation.Results;
using System.Collections.Generic;
namespace NzbDrone.Core.Indexers.HDBits namespace NzbDrone.Core.Indexers.HDBits
{ {
@@ -14,13 +18,18 @@ namespace NzbDrone.Core.Indexers.HDBits
} }
} }
public class HDBitsSettings : IProviderConfig public class HDBitsSettings : ITorrentIndexerSettings
{ {
private static readonly HDBitsSettingsValidator Validator = new HDBitsSettingsValidator(); private static readonly HDBitsSettingsValidator Validator = new HDBitsSettingsValidator();
public HDBitsSettings() public HDBitsSettings()
{ {
BaseUrl = "https://hdbits.org"; BaseUrl = "https://hdbits.org";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
Categories = new int[] { (int)HdBitsCategory.Movie };
Codecs = new int[0];
Mediums = new int[0];
} }
[FieldDefinition(0, Label = "Username")] [FieldDefinition(0, Label = "Username")]
@@ -38,6 +47,18 @@ namespace NzbDrone.Core.Indexers.HDBits
[FieldDefinition(4, Label = "Require Internal", Type = FieldType.Checkbox, HelpText = "Require Internal releases for release to be accepted.")] [FieldDefinition(4, Label = "Require Internal", Type = FieldType.Checkbox, HelpText = "Require Internal releases for release to be accepted.")]
public bool RequireInternal { get; set; } public bool RequireInternal { get; set; }
[FieldDefinition(5, Label = "Categories", Type = FieldType.Tag, SelectOptions = typeof(HdBitsCategory), Advanced = true, HelpText = "Options: Movie, TV, Documentary, Music, Sport, Audio, XXX, MiscDemo. If unspecified, all options are used.")]
public IEnumerable<int> Categories { get; set; }
[FieldDefinition(6, Label = "Codecs", Type = FieldType.Tag, SelectOptions = typeof(HdBitsCodec), Advanced = true, HelpText = "Options: h264, Mpeg2, VC1, Xvid. If unspecified, all options are used.")]
public IEnumerable<int> Codecs { get; set; }
[FieldDefinition(7, Label = "Mediums", Type = FieldType.Tag, SelectOptions = typeof(HdBitsMedium), Advanced = true, HelpText = "Options: BluRay, Encode, Capture, Remux, WebDL. If unspecified, all options are used.")]
public IEnumerable<int> Mediums { get; set; }
[FieldDefinition(8, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -0,0 +1,9 @@
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers
{
public interface IIndexerSettings : IProviderConfig
{
string BaseUrl { get; set; }
}
}

View File

@@ -50,7 +50,7 @@ namespace NzbDrone.Core.Indexers.IPTorrents
private IEnumerable<IndexerRequest> GetRssRequests() private IEnumerable<IndexerRequest> GetRssRequests()
{ {
yield return new IndexerRequest(Settings.Url, HttpAccept.Rss); yield return new IndexerRequest(Settings.BaseUrl, HttpAccept.Rss);
} }
} }
} }

View File

@@ -1,4 +1,4 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using FluentValidation; using FluentValidation;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
@@ -11,26 +11,31 @@ namespace NzbDrone.Core.Indexers.IPTorrents
{ {
public IPTorrentsSettingsValidator() public IPTorrentsSettingsValidator()
{ {
RuleFor(c => c.Url).ValidRootUrl(); RuleFor(c => c.BaseUrl).ValidRootUrl();
RuleFor(c => c.Url).Matches(@"/rss\?.+$"); RuleFor(c => c.BaseUrl).Matches(@"/rss\?.+$");
RuleFor(c => c.Url).Matches(@"/rss\?.+;download(?:;|$)") RuleFor(c => c.BaseUrl).Matches(@"/rss\?.+;download(?:;|$)")
.WithMessage("Use Direct Download Url (;download)") .WithMessage("Use Direct Download Url (;download)")
.When(v => v.Url.IsNotNullOrWhiteSpace() && Regex.IsMatch(v.Url, @"/rss\?.+$")); .When(v => v.BaseUrl.IsNotNullOrWhiteSpace() && Regex.IsMatch(v.BaseUrl, @"/rss\?.+$"));
} }
} }
public class IPTorrentsSettings : IProviderConfig public class IPTorrentsSettings : ITorrentIndexerSettings
{ {
private static readonly IPTorrentsSettingsValidator Validator = new IPTorrentsSettingsValidator(); private static readonly IPTorrentsSettingsValidator Validator = new IPTorrentsSettingsValidator();
public IPTorrentsSettings() public IPTorrentsSettings()
{ {
BaseUrl = string.Empty;
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
} }
[FieldDefinition(0, Label = "Feed URL", HelpText = "The full RSS feed url generated by IPTorrents, using only the categories you selected (HD, SD, x264, etc ...)")] [FieldDefinition(0, Label = "Feed URL", HelpText = "The full RSS feed url generated by IPTorrents, using only the categories you selected (HD, SD, x264, etc ...)")]
public string Url { get; set; } public string BaseUrl { get; set; }
[FieldDefinition(1, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {

View File

@@ -0,0 +1,7 @@
namespace NzbDrone.Core.Indexers
{
public interface ITorrentIndexerSettings : IIndexerSettings
{
int MinimumSeeders { get; set; }
}
}

View File

@@ -38,21 +38,18 @@ namespace NzbDrone.Core.Indexers
public virtual ProviderMessage Message => null; public virtual ProviderMessage Message => null;
public virtual IEnumerable<ProviderDefinition> DefaultDefinitions public virtual IEnumerable<ProviderDefinition> GetDefaultDefinitions()
{ {
get var config = (IProviderConfig)new TSettings();
{
var config = (IProviderConfig)new TSettings();
yield return new IndexerDefinition yield return new IndexerDefinition
{ {
Name = GetType().Name, Name = GetType().Name,
EnableRss = config.Validate().IsValid && SupportsRss, EnableRss = config.Validate().IsValid && SupportsRss,
EnableSearch = config.Validate().IsValid && SupportsSearch, EnableSearch = config.Validate().IsValid && SupportsSearch,
Implementation = GetType().Name, Implementation = GetType().Name,
Settings = config Settings = config
}; };
}
} }
public virtual ProviderDefinition Definition { get; set; } public virtual ProviderDefinition Definition { get; set; }

View File

@@ -0,0 +1,7 @@
namespace NzbDrone.Core.Indexers
{
public static class IndexerDefaults
{
public const int MINIMUM_SEEDERS = 1;
}
}

View File

@@ -35,25 +35,22 @@ namespace NzbDrone.Core.Indexers.Newznab
return new NewznabRssParser(Settings); return new NewznabRssParser(Settings);
} }
public override IEnumerable<ProviderDefinition> DefaultDefinitions public override IEnumerable<ProviderDefinition> GetDefaultDefinitions()
{ {
get yield return GetDefinition("DOGnzb", GetSettings("https://api.dognzb.cr"));
{ yield return GetDefinition("DrunkenSlug", GetSettings("https://api.drunkenslug.com"));
yield return GetDefinition("DOGnzb", GetSettings("https://api.dognzb.cr")); yield return GetDefinition("Nzb-Tortuga", GetSettings("https://www.nzb-tortuga.com"));
yield return GetDefinition("DrunkenSlug", GetSettings("https://api.drunkenslug.com")); yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su"));
yield return GetDefinition("Nzb-Tortuga", GetSettings("https://www.nzb-tortuga.com")); yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat"));
yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su")); yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws"));
yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat")); yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"));
yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws")); yield return GetDefinition("nzbplanet.net", GetSettings("https://api.nzbplanet.net"));
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info")); yield return GetDefinition("Nzbs.org", GetSettings("http://nzbs.org"));
yield return GetDefinition("nzbplanet.net", GetSettings("https://api.nzbplanet.net")); yield return GetDefinition("omgwtfnzbs", GetSettings("https://api.omgwtfnzbs.me"));
yield return GetDefinition("Nzbs.org", GetSettings("http://nzbs.org")); yield return GetDefinition("OZnzb.com", GetSettings("https://api.oznzb.com"));
yield return GetDefinition("omgwtfnzbs", GetSettings("https://api.omgwtfnzbs.me")); yield return GetDefinition("PFmonkey", GetSettings("https://www.pfmonkey.com"));
yield return GetDefinition("OZnzb.com", GetSettings("https://api.oznzb.com")); yield return GetDefinition("SimplyNZBs", GetSettings("https://simplynzbs.com"));
yield return GetDefinition("PFmonkey", GetSettings("https://www.pfmonkey.com")); yield return GetDefinition("Usenet Crawler", GetSettings("https://www.usenet-crawler.com"));
yield return GetDefinition("SimplyNZBs", GetSettings("https://simplynzbs.com"));
yield return GetDefinition("Usenet Crawler", GetSettings("https://www.usenet-crawler.com"));
}
} }
public Newznab(INewznabCapabilitiesProvider capabilitiesProvider, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger) public Newznab(INewznabCapabilitiesProvider capabilitiesProvider, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
@@ -79,7 +76,7 @@ namespace NzbDrone.Core.Indexers.Newznab
private NewznabSettings GetSettings(string url, params int[] categories) private NewznabSettings GetSettings(string url, params int[] categories)
{ {
var settings = new NewznabSettings { Url = url }; var settings = new NewznabSettings { BaseUrl = url };
if (categories.Any()) if (categories.Any())
{ {

View File

@@ -42,7 +42,7 @@ namespace NzbDrone.Core.Indexers.Newznab
{ {
var capabilities = new NewznabCapabilities(); var capabilities = new NewznabCapabilities();
var url = string.Format("{0}/api?t=caps", indexerSettings.Url.TrimEnd('/')); var url = string.Format("{0}/api?t=caps", indexerSettings.BaseUrl.TrimEnd('/'));
if (indexerSettings.ApiKey.IsNotNullOrWhiteSpace()) if (indexerSettings.ApiKey.IsNotNullOrWhiteSpace())
{ {
@@ -59,7 +59,7 @@ namespace NzbDrone.Core.Indexers.Newznab
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Debug(ex, "Failed to get newznab api capabilities from {0}", indexerSettings.Url); _logger.Debug(ex, "Failed to get newznab api capabilities from {0}", indexerSettings.BaseUrl);
throw; throw;
} }
@@ -69,12 +69,12 @@ namespace NzbDrone.Core.Indexers.Newznab
} }
catch (XmlException ex) catch (XmlException ex)
{ {
_logger.Debug(ex, "Failed to parse newznab api capabilities for {0}.", indexerSettings.Url); _logger.Debug(ex, "Failed to parse newznab api capabilities for {0}.", indexerSettings.BaseUrl);
throw; throw;
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to determine newznab api capabilities for {0}, using the defaults instead till Sonarr restarts.", indexerSettings.Url); _logger.Error(ex, "Failed to determine newznab api capabilities for {0}, using the defaults instead till Sonarr restarts.", indexerSettings.BaseUrl);
} }
return capabilities; return capabilities;

View File

@@ -59,7 +59,18 @@ namespace NzbDrone.Core.Indexers.Newznab
else else
{ {
var searchTitle = System.Web.HttpUtility.UrlPathEncode(Parser.Parser.ReplaceGermanUmlauts(Parser.Parser.NormalizeTitle(searchCriteria.Movie.Title))); var searchTitle = System.Web.HttpUtility.UrlPathEncode(Parser.Parser.ReplaceGermanUmlauts(Parser.Parser.NormalizeTitle(searchCriteria.Movie.Title)));
pageableRequests.Add(GetPagedRequests(MaxPages, Settings.Categories, "search", $"&q={searchTitle}%20{searchCriteria.Movie.Year}")); var altTitles = searchCriteria.Movie.AlternativeTitles.DistinctBy(t => Parser.Parser.CleanSeriesTitle(t)).Take(5).ToList();
var realMaxPages = (int)MaxPages / (altTitles.Count() + 1);
pageableRequests.Add(GetPagedRequests(MaxPages - (altTitles.Count() * realMaxPages), Settings.Categories, "search", $"&q={searchTitle}%20{searchCriteria.Movie.Year}"));
//Also use alt titles for searching.
foreach (String altTitle in altTitles)
{
var searchAltTitle = System.Web.HttpUtility.UrlPathEncode(Parser.Parser.ReplaceGermanUmlauts(Parser.Parser.NormalizeTitle(altTitle)));
pageableRequests.Add(GetPagedRequests(realMaxPages, Settings.Categories, "search", $"&q={searchAltTitle}%20{searchCriteria.Movie.Year}"));
}
} }
return pageableRequests; return pageableRequests;
@@ -74,7 +85,7 @@ namespace NzbDrone.Core.Indexers.Newznab
var categoriesQuery = string.Join(",", categories.Distinct()); var categoriesQuery = string.Join(",", categories.Distinct());
var baseUrl = string.Format("{0}/api?t={1}&cat={2}&extended=1{3}", Settings.Url.TrimEnd('/'), searchType, categoriesQuery, Settings.AdditionalParameters); var baseUrl = string.Format("{0}/api?t={1}&cat={2}&extended=1{3}", Settings.BaseUrl.TrimEnd('/'), searchType, categoriesQuery, Settings.AdditionalParameters);
if (Settings.ApiKey.IsNotNullOrWhiteSpace()) if (Settings.ApiKey.IsNotNullOrWhiteSpace())
{ {

View File

@@ -25,12 +25,12 @@ namespace NzbDrone.Core.Indexers.Newznab
private static bool ShouldHaveApiKey(NewznabSettings settings) private static bool ShouldHaveApiKey(NewznabSettings settings)
{ {
if (settings.Url == null) if (settings.BaseUrl == null)
{ {
return false; return false;
} }
return ApiKeyWhiteList.Any(c => settings.Url.ToLowerInvariant().Contains(c)); return ApiKeyWhiteList.Any(c => settings.BaseUrl.ToLowerInvariant().Contains(c));
} }
private static readonly Regex AdditionalParametersRegex = new Regex(@"(&.+?\=.+?)+", RegexOptions.Compiled); private static readonly Regex AdditionalParametersRegex = new Regex(@"(&.+?\=.+?)+", RegexOptions.Compiled);
@@ -47,14 +47,14 @@ namespace NzbDrone.Core.Indexers.Newznab
return null; return null;
}); });
RuleFor(c => c.Url).ValidRootUrl(); RuleFor(c => c.BaseUrl).ValidRootUrl();
RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey); RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey);
RuleFor(c => c.AdditionalParameters).Matches(AdditionalParametersRegex) RuleFor(c => c.AdditionalParameters).Matches(AdditionalParametersRegex)
.When(c => !c.AdditionalParameters.IsNullOrWhiteSpace()); .When(c => !c.AdditionalParameters.IsNullOrWhiteSpace());
} }
} }
public class NewznabSettings : IProviderConfig public class NewznabSettings : IIndexerSettings
{ {
private static readonly NewznabSettingsValidator Validator = new NewznabSettingsValidator(); private static readonly NewznabSettingsValidator Validator = new NewznabSettingsValidator();
@@ -65,7 +65,7 @@ namespace NzbDrone.Core.Indexers.Newznab
} }
[FieldDefinition(0, Label = "URL")] [FieldDefinition(0, Label = "URL")]
public string Url { get; set; } public string BaseUrl { get; set; }
[FieldDefinition(1, Label = "API Key")] [FieldDefinition(1, Label = "API Key")]
public string ApiKey { get; set; } public string ApiKey { get; set; }
@@ -79,6 +79,9 @@ namespace NzbDrone.Core.Indexers.Newznab
[FieldDefinition(4, Label = "Additional Parameters", HelpText = "Additional Newznab parameters", Advanced = true)] [FieldDefinition(4, Label = "Additional Parameters", HelpText = "Additional Newznab parameters", Advanced = true)]
public string AdditionalParameters { get; set; } public string AdditionalParameters { get; set; }
// Field 5 is used by TorznabSettings MinimumSeeders
// If you need to add another field here, update TorznabSettings as well and this comment
public virtual NzbDroneValidationResult Validate() public virtual NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace NzbDrone.Core.Indexers.Nyaa namespace NzbDrone.Core.Indexers.Nyaa
@@ -14,7 +13,7 @@ namespace NzbDrone.Core.Indexers.Nyaa
} }
} }
public class NyaaSettings : IProviderConfig public class NyaaSettings : ITorrentIndexerSettings
{ {
private static readonly NyaaSettingsValidator Validator = new NyaaSettingsValidator(); private static readonly NyaaSettingsValidator Validator = new NyaaSettingsValidator();
@@ -22,6 +21,7 @@ namespace NzbDrone.Core.Indexers.Nyaa
{ {
BaseUrl = "http://www.nyaa.se"; BaseUrl = "http://www.nyaa.se";
AdditionalParameters = "&cats=1_37&filter=1"; AdditionalParameters = "&cats=1_37&filter=1";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
} }
[FieldDefinition(0, Label = "Website URL")] [FieldDefinition(0, Label = "Website URL")]
@@ -30,6 +30,9 @@ namespace NzbDrone.Core.Indexers.Nyaa
[FieldDefinition(1, Label = "Additional Parameters", Advanced = true, HelpText = "Please note if you change the category you will have to add required/restricted rules about the subgroups to avoid foreign language releases.")] [FieldDefinition(1, Label = "Additional Parameters", Advanced = true, HelpText = "Please note if you change the category you will have to add required/restricted rules about the subgroups to avoid foreign language releases.")]
public string AdditionalParameters { get; set; } public string AdditionalParameters { get; set; }
[FieldDefinition(2, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -15,7 +15,7 @@ namespace NzbDrone.Core.Indexers.Omgwtfnzbs
} }
} }
public class OmgwtfnzbsSettings : IProviderConfig public class OmgwtfnzbsSettings : IIndexerSettings
{ {
private static readonly OmgwtfnzbsSettingsValidator Validator = new OmgwtfnzbsSettingsValidator(); private static readonly OmgwtfnzbsSettingsValidator Validator = new OmgwtfnzbsSettingsValidator();
@@ -24,6 +24,9 @@ namespace NzbDrone.Core.Indexers.Omgwtfnzbs
Delay = 30; Delay = 30;
} }
// Unused since Omg has a hardcoded url.
public string BaseUrl { get; set; }
[FieldDefinition(0, Label = "Username")] [FieldDefinition(0, Label = "Username")]
public string Username { get; set; } public string Username { get; set; }

View File

@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
jsonResponse.TotalResults.IsNullOrWhiteSpace() || jsonResponse.TotalResults.IsNullOrWhiteSpace() ||
jsonResponse.Movies == null) jsonResponse.Movies == null)
{ {
throw new IndexerException(indexerResponse, "No results were found"); return torrentInfos;
} }

View File

@@ -17,13 +17,14 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
} }
} }
public class PassThePopcornSettings : IProviderConfig public class PassThePopcornSettings : ITorrentIndexerSettings
{ {
private static readonly PassThePopcornSettingsValidator Validator = new PassThePopcornSettingsValidator(); private static readonly PassThePopcornSettingsValidator Validator = new PassThePopcornSettingsValidator();
public PassThePopcornSettings() public PassThePopcornSettings()
{ {
BaseUrl = "https://passthepopcorn.me"; BaseUrl = "https://passthepopcorn.me";
MinimumSeeders = 0;
} }
[FieldDefinition(0, Label = "URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your cookie will be sent to that host.")] [FieldDefinition(0, Label = "URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your cookie will be sent to that host.")]
@@ -50,6 +51,9 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
[FieldDefinition(7, Label = "Require Golden", Type = FieldType.Checkbox, HelpText = "Require Golden Popcorn-releases for releases to be accepted.")] [FieldDefinition(7, Label = "Require Golden", Type = FieldType.Checkbox, HelpText = "Require Golden Popcorn-releases for releases to be accepted.")]
public bool RequireGolden { get; set; } public bool RequireGolden { get; set; }
[FieldDefinition(8, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Rarbg namespace NzbDrone.Core.Indexers.Rarbg
@@ -13,7 +12,7 @@ namespace NzbDrone.Core.Indexers.Rarbg
} }
} }
public class RarbgSettings : IProviderConfig public class RarbgSettings : ITorrentIndexerSettings
{ {
private static readonly RarbgSettingsValidator Validator = new RarbgSettingsValidator(); private static readonly RarbgSettingsValidator Validator = new RarbgSettingsValidator();
@@ -21,6 +20,7 @@ namespace NzbDrone.Core.Indexers.Rarbg
{ {
BaseUrl = "https://torrentapi.org"; BaseUrl = "https://torrentapi.org";
RankedOnly = false; RankedOnly = false;
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
} }
[FieldDefinition(0, Label = "API URL", HelpText = "URL to Rarbg api, not the website.")] [FieldDefinition(0, Label = "API URL", HelpText = "URL to Rarbg api, not the website.")]
@@ -32,6 +32,9 @@ namespace NzbDrone.Core.Indexers.Rarbg
[FieldDefinition(2, Type = FieldType.Captcha, Label = "CAPTCHA Token", HelpText = "CAPTCHA Clearance token used to handle CloudFlare Anti-DDOS measures on shared-ip VPNs.")] [FieldDefinition(2, Type = FieldType.Captcha, Label = "CAPTCHA Token", HelpText = "CAPTCHA Clearance token used to handle CloudFlare Anti-DDOS measures on shared-ip VPNs.")]
public string CaptchaToken { get; set; } public string CaptchaToken { get; set; }
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@@ -26,14 +26,6 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
} }
public override IEnumerable<ProviderDefinition> DefaultDefinitions
{
get
{
yield return GetDefinition("Jackett", new TorrentPotatoSettings { BaseUrl = "http://localhost:9117/potato/YOURINDEXER"});
}
}
private IndexerDefinition GetDefinition(string name, TorrentPotatoSettings settings) private IndexerDefinition GetDefinition(string name, TorrentPotatoSettings settings)
{ {
return new IndexerDefinition return new IndexerDefinition

View File

@@ -13,13 +13,14 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
} }
} }
public class TorrentPotatoSettings : IProviderConfig public class TorrentPotatoSettings : ITorrentIndexerSettings
{ {
private static readonly TorrentPotatoSettingsValidator Validator = new TorrentPotatoSettingsValidator(); private static readonly TorrentPotatoSettingsValidator Validator = new TorrentPotatoSettingsValidator();
public TorrentPotatoSettings() public TorrentPotatoSettings()
{ {
BaseUrl = "http://127.0.0.1"; BaseUrl = "http://127.0.0.1";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
} }
[FieldDefinition(0, Label = "API URL", HelpText = "URL to TorrentPotato api.")] [FieldDefinition(0, Label = "API URL", HelpText = "URL to TorrentPotato api.")]
@@ -28,8 +29,12 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
[FieldDefinition(1, Label = "Username", HelpText = "The username you use at your indexer.")] [FieldDefinition(1, Label = "Username", HelpText = "The username you use at your indexer.")]
public string User { get; set; } public string User { get; set; }
[FieldDefinition(2, Label = "Passkey", HelpText = "The password you use at your Indexer (or your API key if you're using Jackett).")] [FieldDefinition(2, Label = "Passkey", HelpText = "The password you use at your Indexer.")]
public string Passkey { get; set; } public string Passkey { get; set; }
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.TorrentRss namespace NzbDrone.Core.Indexers.TorrentRss
@@ -13,7 +12,7 @@ namespace NzbDrone.Core.Indexers.TorrentRss
} }
} }
public class TorrentRssIndexerSettings : IProviderConfig public class TorrentRssIndexerSettings : ITorrentIndexerSettings
{ {
private static readonly TorrentRssIndexerSettingsValidator validator = new TorrentRssIndexerSettingsValidator(); private static readonly TorrentRssIndexerSettingsValidator validator = new TorrentRssIndexerSettingsValidator();
@@ -21,6 +20,7 @@ namespace NzbDrone.Core.Indexers.TorrentRss
{ {
BaseUrl = string.Empty; BaseUrl = string.Empty;
AllowZeroSize = false; AllowZeroSize = false;
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
} }
[FieldDefinition(0, Label = "Full RSS Feed URL")] [FieldDefinition(0, Label = "Full RSS Feed URL")]
@@ -32,6 +32,9 @@ namespace NzbDrone.Core.Indexers.TorrentRss
[FieldDefinition(2, Type = FieldType.Checkbox, Label = "Allow Zero Size", HelpText="Enabling this will allow you to use feeds that don't specify release size, but be careful, size related checks will not be performed.")] [FieldDefinition(2, Type = FieldType.Checkbox, Label = "Allow Zero Size", HelpText="Enabling this will allow you to use feeds that don't specify release size, but be careful, size related checks will not be performed.")]
public bool AllowZeroSize { get; set; } public bool AllowZeroSize { get; set; }
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(validator.Validate(this)); return new NzbDroneValidationResult(validator.Validate(this));

View File

@@ -37,12 +37,10 @@ namespace NzbDrone.Core.Indexers.Torznab
return new TorznabRssParser(); return new TorznabRssParser();
} }
public override IEnumerable<ProviderDefinition> DefaultDefinitions public override IEnumerable<ProviderDefinition> GetDefaultDefinitions()
{ {
get yield return GetDefinition("Jackett", GetSettings("http://localhost:9117/torznab/YOURINDEXER"));
{ yield return GetDefinition("HD4Free.xyz", GetSettings("http://hd4free.xyz"));
yield return GetDefinition("HD4Free.xyz", GetSettings("http://hd4free.xyz"));
}
} }
public Torznab(INewznabCapabilitiesProvider capabilitiesProvider, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger) public Torznab(INewznabCapabilitiesProvider capabilitiesProvider, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
@@ -68,7 +66,7 @@ namespace NzbDrone.Core.Indexers.Torznab
private TorznabSettings GetSettings(string url, params int[] categories) private TorznabSettings GetSettings(string url, params int[] categories)
{ {
var settings = new TorznabSettings { Url = url }; var settings = new TorznabSettings { BaseUrl = url };
if (categories.Any()) if (categories.Any())
{ {

View File

@@ -1,8 +1,9 @@
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using FluentValidation; using FluentValidation;
using FluentValidation.Results; using FluentValidation.Results;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
@@ -17,12 +18,12 @@ namespace NzbDrone.Core.Indexers.Torznab
private static bool ShouldHaveApiKey(TorznabSettings settings) private static bool ShouldHaveApiKey(TorznabSettings settings)
{ {
if (settings.Url == null) if (settings.BaseUrl == null)
{ {
return false; return false;
} }
return ApiKeyWhiteList.Any(c => settings.Url.ToLowerInvariant().Contains(c)); return ApiKeyWhiteList.Any(c => settings.BaseUrl.ToLowerInvariant().Contains(c));
} }
private static readonly Regex AdditionalParametersRegex = new Regex(@"(&.+?\=.+?)+", RegexOptions.Compiled); private static readonly Regex AdditionalParametersRegex = new Regex(@"(&.+?\=.+?)+", RegexOptions.Compiled);
@@ -39,17 +40,25 @@ namespace NzbDrone.Core.Indexers.Torznab
return null; return null;
}); });
RuleFor(c => c.Url).ValidRootUrl(); RuleFor(c => c.BaseUrl).ValidRootUrl();
RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey); RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey);
RuleFor(c => c.AdditionalParameters).Matches(AdditionalParametersRegex) RuleFor(c => c.AdditionalParameters).Matches(AdditionalParametersRegex)
.When(c => !c.AdditionalParameters.IsNullOrWhiteSpace()); .When(c => !c.AdditionalParameters.IsNullOrWhiteSpace());
} }
} }
public class TorznabSettings : NewznabSettings public class TorznabSettings : NewznabSettings, ITorrentIndexerSettings
{ {
private static readonly TorznabSettingsValidator Validator = new TorznabSettingsValidator(); private static readonly TorznabSettingsValidator Validator = new TorznabSettingsValidator();
public TorznabSettings()
{
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
}
[FieldDefinition(6, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
public override NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -134,9 +134,9 @@ namespace NzbDrone.Core.Jobs
{ {
var interval = _configService.RssSyncInterval; var interval = _configService.RssSyncInterval;
if (interval > 0 && interval < 5) if (interval > 0 && interval < 10)
{ {
return 5; return 10;
} }
if (interval < 0) if (interval < 0)

View File

@@ -94,7 +94,7 @@ namespace NzbDrone.Core.MediaFiles
return ProcessFile(fileInfo, importMode, series, downloadClientItem); return ProcessFile(fileInfo, importMode, series, downloadClientItem);
} }
_logger.Error("Import failed, path does not exist or is not accessible by Sonarr: {0}", path); _logger.Error("Import failed, path does not exist or is not accessible by Radarr: {0}", path);
return new List<ImportResult>(); return new List<ImportResult>();
} }

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
@@ -29,6 +30,7 @@ namespace NzbDrone.Core.MediaFiles
private readonly IMakeImportDecision _importDecisionMaker; private readonly IMakeImportDecision _importDecisionMaker;
private readonly IImportApprovedMovie _importApprovedMovie; private readonly IImportApprovedMovie _importApprovedMovie;
private readonly IDetectSample _detectSample; private readonly IDetectSample _detectSample;
private readonly IConfigService _config;
private readonly Logger _logger; private readonly Logger _logger;
public DownloadedMovieImportService(IDiskProvider diskProvider, public DownloadedMovieImportService(IDiskProvider diskProvider,
@@ -38,6 +40,7 @@ namespace NzbDrone.Core.MediaFiles
IMakeImportDecision importDecisionMaker, IMakeImportDecision importDecisionMaker,
IImportApprovedMovie importApprovedMovie, IImportApprovedMovie importApprovedMovie,
IDetectSample detectSample, IDetectSample detectSample,
IConfigService config,
Logger logger) Logger logger)
{ {
_diskProvider = diskProvider; _diskProvider = diskProvider;
@@ -47,6 +50,7 @@ namespace NzbDrone.Core.MediaFiles
_importDecisionMaker = importDecisionMaker; _importDecisionMaker = importDecisionMaker;
_importApprovedMovie = importApprovedMovie; _importApprovedMovie = importApprovedMovie;
_detectSample = detectSample; _detectSample = detectSample;
_config = config;
_logger = logger; _logger = logger;
} }
@@ -160,7 +164,7 @@ namespace NzbDrone.Core.MediaFiles
} }
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name); var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name, _config.ParsingLeniency > 0);
if (folderInfo != null) if (folderInfo != null)
{ {

View File

@@ -154,7 +154,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
{ {
var title = Parser.Parser.RemoveFileExtension(downloadClientItem.Title); var title = Parser.Parser.RemoveFileExtension(downloadClientItem.Title);
var parsedTitle = Parser.Parser.ParseMovieTitle(title); var parsedTitle = Parser.Parser.ParseMovieTitle(title, false);
if (parsedTitle != null) if (parsedTitle != null)
{ {

View File

@@ -368,6 +368,11 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
decision = new ImportDecision(localEpisode, new Rejection("Unexpected error processing file")); decision = new ImportDecision(localEpisode, new Rejection("Unexpected error processing file"));
} }
if (decision == null)
{
_logger.Error("Unable to make a decision on {0}", file);
}
return decision; return decision;
} }

View File

@@ -6,6 +6,7 @@ using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.Download.TrackedDownloads;
@@ -38,6 +39,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
private readonly ITrackedDownloadService _trackedDownloadService; private readonly ITrackedDownloadService _trackedDownloadService;
private readonly IDownloadedMovieImportService _downloadedMovieImportService; private readonly IDownloadedMovieImportService _downloadedMovieImportService;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly IConfigService _config;
private readonly Logger _logger; private readonly Logger _logger;
public ManualImportService(IDiskProvider diskProvider, public ManualImportService(IDiskProvider diskProvider,
@@ -53,6 +55,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
ITrackedDownloadService trackedDownloadService, ITrackedDownloadService trackedDownloadService,
IDownloadedMovieImportService downloadedMovieImportService, IDownloadedMovieImportService downloadedMovieImportService,
IEventAggregator eventAggregator, IEventAggregator eventAggregator,
IConfigService config,
Logger logger) Logger logger)
{ {
_diskProvider = diskProvider; _diskProvider = diskProvider;
@@ -68,6 +71,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
_trackedDownloadService = trackedDownloadService; _trackedDownloadService = trackedDownloadService;
_downloadedMovieImportService = downloadedMovieImportService; _downloadedMovieImportService = downloadedMovieImportService;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_config = config;
_logger = logger; _logger = logger;
} }
@@ -116,7 +120,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
return files.Select(file => ProcessFile(file, downloadId, folder)).Where(i => i != null).ToList(); return files.Select(file => ProcessFile(file, downloadId, folder)).Where(i => i != null).ToList();
} }
var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name); var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name, _config.ParsingLeniency > 0);
var seriesFiles = _diskScanService.GetVideoFiles(folder).ToList(); var seriesFiles = _diskScanService.GetVideoFiles(folder).ToList();
var decisions = _importDecisionMaker.GetImportDecisions(seriesFiles, series, folderInfo, SceneSource(series, folder), false); var decisions = _importDecisionMaker.GetImportDecisions(seriesFiles, series, folderInfo, SceneSource(series, folder), false);
@@ -160,7 +164,17 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file }, var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file },
movie, null, SceneSource(movie, folder), true); movie, null, SceneSource(movie, folder), true);
return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null; return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : new ManualImportItem
{
DownloadId = downloadId,
Path = file,
RelativePath = folder.GetRelativePath(file),
Name = Path.GetFileNameWithoutExtension(file),
Rejections = new List<Rejection>
{
new Rejection("Unable to process file")
}
};
} }
//private ManualImportItem ProcessFile(string file, string downloadId, string folder = null) //private ManualImportItem ProcessFile(string file, string downloadId, string folder = null)
@@ -272,7 +286,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
var file = message.Files[i]; var file = message.Files[i];
var movie = _movieService.GetMovie(file.MovieId); var movie = _movieService.GetMovie(file.MovieId);
var parsedMovieInfo = Parser.Parser.ParseMoviePath(file.Path) ?? new ParsedMovieInfo(); var parsedMovieInfo = Parser.Parser.ParseMoviePath(file.Path, _config.ParsingLeniency > 0) ?? new ParsedMovieInfo();
var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path); var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path);
var existingFile = movie.Path.IsParentPath(file.Path); var existingFile = movie.Path.IsParentPath(file.Path);

View File

@@ -8,5 +8,7 @@ namespace NzbDrone.Core.MetadataSource
List<Movie> SearchForNewMovie(string title); List<Movie> SearchForNewMovie(string title);
Movie MapMovieToTmdbMovie(Movie movie); Movie MapMovieToTmdbMovie(Movie movie);
Movie MapMovie(SkyHook.Resource.MovieResult result);
} }
} }

View File

@@ -119,7 +119,7 @@ namespace NzbDrone.Core.MetadataSource.PreDB
foreach (PreDBResult result in results) foreach (PreDBResult result in results)
{ {
var parsedInfo = Parser.Parser.ParseMovieTitle(result.Title); var parsedInfo = Parser.Parser.ParseMovieTitle(result.Title, true);
if (parsedInfo != null) if (parsedInfo != null)
{ {
@@ -178,14 +178,14 @@ namespace NzbDrone.Core.MetadataSource.PreDB
foreach (PreDBResult result in results) foreach (PreDBResult result in results)
{ {
var parsed = Parser.Parser.ParseMovieTitle(result.Title); var parsed = Parser.Parser.ParseMovieTitle(result.Title, true);
if (parsed == null) if (parsed == null)
{ {
parsed = new Parser.Model.ParsedMovieInfo { MovieTitle = result.Title, Year = 0 }; parsed = new Parser.Model.ParsedMovieInfo { MovieTitle = result.Title, Year = 0 };
} }
var match = _parsingService.Map(parsed, "", new MovieSearchCriteria { Movie = movie }); var match = _parsingService.Map(parsed, "", new MovieSearchCriteria { Movie = movie });
if (match != null && match.Movie != null && match.Movie.Id == movie.Id) if (match != null && match.RemoteMovie.Movie != null && match.RemoteMovie.Movie.Id == movie.Id)
{ {
return true; return true;
} }

View File

@@ -0,0 +1,101 @@
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
namespace NzbDrone.Core.MetadataSource.RadarrAPI
{
public interface IRadarrAPIClient
{
IHttpRequestBuilderFactory RadarrAPI { get; }
List<MovieResult> DiscoverMovies(string action, Func<HttpRequest, HttpRequest> enhanceRequest);
string APIURL { get; }
}
public class RadarrAPIClient : IRadarrAPIClient
{
private readonly IHttpClient _httpClient;
public string APIURL { get; private set; }
public RadarrAPIClient(IConfigFileProvider configFile, IHttpClient httpClient)
{
_httpClient = httpClient;
if (configFile.Branch == "nightly")
{
APIURL = "https://staging.api.radarr.video";
}
else
{
APIURL = "https://api.radarr.video/v2";
}
RadarrAPI = new HttpRequestBuilder(APIURL+"/{route}/{action}")
.CreateFactory();
}
private HttpResponse Execute(HttpRequest request)
{
if (request.Method == HttpMethod.GET)
{
return _httpClient.Get(request);
}
else if (request.Method == HttpMethod.POST)
{
return _httpClient.Post(request);
}
else
{
throw new NotImplementedException($"Method {request.Method} not implemented");
}
}
private T Execute<T>(HttpRequest request)
{
request.AllowAutoRedirect = true;
request.Headers.Accept = HttpAccept.Json.Value;
request.SuppressHttpError = true;
var response = Execute(request);
try
{
var error = JsonConvert.DeserializeObject<RadarrError>(response.Content);
if (error != null && error.Errors.Count != 0)
{
throw new RadarrAPIException(error);
}
}
catch (JsonSerializationException)
{
//No error!
}
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new HttpException(request, response);
}
return JsonConvert.DeserializeObject<T>(response.Content);
}
public List<MovieResult> DiscoverMovies(string action, Func<HttpRequest, HttpRequest> enhanceRequest = null )
{
var request = RadarrAPI.Create().SetSegment("route", "discovery").SetSegment("action", action).Build();
if (enhanceRequest != null)
{
request = enhanceRequest(request);
}
return Execute<List<MovieResult>>(request);
}
public IHttpRequestBuilderFactory RadarrAPI { get; private set; }
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace NzbDrone.Core.MetadataSource.RadarrAPI
{
public class Error
{
[JsonProperty("id")]
public string RayId { get; set; }
[JsonProperty("status")]
public int Status { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("detail")]
public string Detail { get; set; }
}
public class RadarrError
{
[JsonProperty("errors")]
public IList<Error> Errors { get; set; }
}
public class RadarrAPIException : Exception
{
RadarrError APIErrors;
public RadarrAPIException(RadarrError apiError) : base(HumanReadable(apiError))
{
}
private static string HumanReadable(RadarrError APIErrors)
{
var firstError = APIErrors.Errors.First();
var details = string.Join("\n", APIErrors.Errors.Select(error =>
{
return $"{error.Title} ({error.Status}, RayId: {error.RayId}), Details: {error.Detail}";
}));
return $"Error while calling api: {firstError.Title}\nFull error(s): {details}";
}
}
}

View File

@@ -36,6 +36,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
public float vote_average { get; set; } public float vote_average { get; set; }
public string trailer_key { get; set; } public string trailer_key { get; set; }
public string trailer_site { get; set; } public string trailer_site { get; set; }
public string physical_release { get; set; }
public string physical_release_note { get; set; }
} }
@@ -166,23 +168,9 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
public object poster_path { get; set; } public object poster_path { get; set; }
} }
public class Item public class Item : MovieResult
{ {
public string poster_path { get; set; }
public bool adult { get; set; }
public string overview { get; set; }
public string release_date { get; set; }
public string original_title { get; set; }
public int[] genre_ids { get; set; }
public int id { get; set; }
public string media_type { get; set; } public string media_type { get; set; }
public string original_language { get; set; }
public string title { get; set; }
public string backdrop_path { get; set; }
public float popularity { get; set; }
public int vote_count { get; set; }
public bool video { get; set; }
public float vote_average { get; set; }
public string first_air_date { get; set; } public string first_air_date { get; set; }
public string[] origin_country { get; set; } public string[] origin_country { get; set; }
public string name { get; set; } public string name { get; set; }

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@@ -17,6 +17,8 @@ using NzbDrone.Core.Parser;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.NetImport.ImportExclusions; using NzbDrone.Core.NetImport.ImportExclusions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.MetadataSource.RadarrAPI;
namespace NzbDrone.Core.MetadataSource.SkyHook namespace NzbDrone.Core.MetadataSource.SkyHook
{ {
@@ -31,9 +33,12 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
private readonly IMovieService _movieService; private readonly IMovieService _movieService;
private readonly IPreDBService _predbService; private readonly IPreDBService _predbService;
private readonly IImportExclusionsService _exclusionService; private readonly IImportExclusionsService _exclusionService;
private readonly IRadarrAPIClient _radarrAPI;
private readonly IHttpRequestBuilderFactory _apiBuilder;
public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder, ITmdbConfigService configService, IMovieService movieService, public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder, ITmdbConfigService configService, IMovieService movieService,
IPreDBService predbService, IImportExclusionsService exclusionService, Logger logger) IPreDBService predbService, IImportExclusionsService exclusionService, IRadarrAPIClient radarrAPI, Logger logger)
{ {
_httpClient = httpClient; _httpClient = httpClient;
_requestBuilder = requestBuilder.SkyHookTvdb; _requestBuilder = requestBuilder.SkyHookTvdb;
@@ -42,6 +47,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
_movieService = movieService; _movieService = movieService;
_predbService = predbService; _predbService = predbService;
_exclusionService = exclusionService; _exclusionService = exclusionService;
_radarrAPI = radarrAPI;
_logger = logger; _logger = logger;
} }
@@ -191,11 +198,13 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
if (movie.PhysicalRelease.Value.After(DateTime.Parse(releaseDate.release_date))) if (movie.PhysicalRelease.Value.After(DateTime.Parse(releaseDate.release_date)))
{ {
movie.PhysicalRelease = DateTime.Parse(releaseDate.release_date); //Use oldest release date available. movie.PhysicalRelease = DateTime.Parse(releaseDate.release_date); //Use oldest release date available.
movie.PhysicalReleaseNote = releaseDate.note;
} }
} }
else else
{ {
movie.PhysicalRelease = DateTime.Parse(releaseDate.release_date); movie.PhysicalRelease = DateTime.Parse(releaseDate.release_date);
movie.PhysicalReleaseNote = releaseDate.note;
} }
} }
} }
@@ -360,55 +369,31 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
string allIds = string.Join(",", allMovies.Select(m => m.TmdbId)); string allIds = string.Join(",", allMovies.Select(m => m.TmdbId));
string ignoredIds = string.Join(",", allExclusions.Select(ex => ex.TmdbId)); string ignoredIds = string.Join(",", allExclusions.Select(ex => ex.TmdbId));
HttpRequest request; List<MovieResult> results = new List<MovieResult>();
List<MovieResult> results;
if (action == "upcoming") try
{ {
var lastWeek = DateTime.Now.AddDays(-7); results = _radarrAPI.DiscoverMovies(action, (request) =>
var threeWeeks = DateTime.Now.AddDays(7 * 3);
request = _movieBuilder.Create().SetSegment("route", "discover")
.SetSegment("id", "movie")
.SetSegment("secondaryRoute", "")
.AddQueryParam("region", "us")
.AddQueryParam("with_release_type", "5|4|6")
.AddQueryParam("release_date.gte", lastWeek.ToString("yyyy-MM-dd"))
.AddQueryParam("sort_by", "popularity.desc")
.AddQueryParam("release_date.lte", threeWeeks.ToString("yyyy-MM-dd")).Build();
var response = _httpClient.Get<MovieSearchRoot>(request);
if (response.StatusCode != HttpStatusCode.OK)
{ {
throw new HttpException(request, response); request.AllowAutoRedirect = true;
} request.Method = HttpMethod.POST;
request.Headers.ContentType = "application/x-www-form-urlencoded";
request.SetContent($"tmdbIds={allIds}&ignoredIds={ignoredIds}");
return request;
});
results = response.Resource.results.ToList(); results = results.Where(m => allMovies.None(mo => mo.TmdbId == m.id) && allExclusions.None(ex => ex.TmdbId == m.id)).ToList();
} }
else catch (RadarrAPIException exception)
{ {
request = new HttpRequestBuilder("https://radarr.video/api/{action}/").SetSegment("action", action).Build(); _logger.Error(exception, "Failed to discover movies for action {0}!", action);
}
request.AllowAutoRedirect = true; catch (Exception exception)
request.Method = HttpMethod.POST; {
request.Headers.ContentType = "application/x-www-form-urlencoded"; _logger.Error(exception, "Failed to discover movies for action {0}!", action);
request.SetContent($"tmdbids={allIds}&ignoredIds={ignoredIds}");
var response = _httpClient.Post<List<MovieResult>>(request);
if (response.StatusCode != HttpStatusCode.OK)
{
throw new HttpException(request, response);
}
results = response.Resource;
} }
results = results.Where(m => allMovies.None(mo => mo.TmdbId == m.id) && allExclusions.None(ex => ex.TmdbId == m.id)).ToList(); return results.SelectList(MapMovie);
return results.SelectList(MapMovie);
} }
private string StripTrailingTheFromTitle(string title) private string StripTrailingTheFromTitle(string title)
@@ -429,7 +414,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
lowerTitle = lowerTitle.Replace(".", ""); lowerTitle = lowerTitle.Replace(".", "");
var parserResult = Parser.Parser.ParseMovieTitle(title, true); var parserResult = Parser.Parser.ParseMovieTitle(title, true, true);
var yearTerm = ""; var yearTerm = "";
@@ -497,7 +482,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
_logger.Warn("Cleaned response: " + responseCleaned); _logger.Warn("Cleaned response: " + responseCleaned);
ImdbResource json = JsonConvert.DeserializeObject<ImdbResource>(responseCleaned); ImdbResource json =Json Convert.DeserializeObject<ImdbResource>(responseCleaned);
_logger.Warn("Json object: " + json); _logger.Warn("Json object: " + json);
@@ -561,7 +546,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
} }
} }
private Movie MapMovie(MovieResult result) public Movie MapMovie(MovieResult result)
{ {
var imdbMovie = new Movie(); var imdbMovie = new Movie();
imdbMovie.TmdbId = result.id; imdbMovie.TmdbId = result.id;
@@ -571,13 +556,31 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
imdbMovie.Title = result.title; imdbMovie.Title = result.title;
imdbMovie.TitleSlug = Parser.Parser.ToUrlSlug(result.title); imdbMovie.TitleSlug = Parser.Parser.ToUrlSlug(result.title);
if (result.release_date.IsNotNullOrWhiteSpace()) try
{ {
imdbMovie.InCinemas = DateTime.Parse(result.release_date); if (result.release_date.IsNotNullOrWhiteSpace())
imdbMovie.Year = imdbMovie.InCinemas.Value.Year; {
imdbMovie.InCinemas = DateTime.Parse(result.release_date);
imdbMovie.Year = imdbMovie.InCinemas.Value.Year;
}
if (result.physical_release.IsNotNullOrWhiteSpace())
{
imdbMovie.PhysicalRelease = DateTime.Parse(result.physical_release);
if (result.physical_release_note.IsNotNullOrWhiteSpace())
{
imdbMovie.PhysicalReleaseNote = result.physical_release_note;
}
}
}
catch (Exception ex)
{
_logger.Debug("Not a valid date time.");
} }
var now = DateTime.Now;
var now = DateTime.Now;
//handle the case when we have both theatrical and physical release dates //handle the case when we have both theatrical and physical release dates
if (imdbMovie.InCinemas.HasValue && imdbMovie.PhysicalRelease.HasValue) if (imdbMovie.InCinemas.HasValue && imdbMovie.PhysicalRelease.HasValue)
{ {

View File

@@ -32,23 +32,21 @@ namespace NzbDrone.Core.NetImport
public Type ConfigContract => typeof(TSettings); public Type ConfigContract => typeof(TSettings);
public virtual ProviderMessage Message => null; public virtual ProviderMessage Message => null;
public virtual IEnumerable<ProviderDefinition> DefaultDefinitions
{
get
{
var config = (IProviderConfig)new TSettings();
yield return new NetImportDefinition public virtual IEnumerable<ProviderDefinition> GetDefaultDefinitions()
{ {
Name = this.Name, var config = (IProviderConfig)new TSettings();
Enabled = config.Validate().IsValid && Enabled,
EnableAuto = true, yield return new NetImportDefinition
ProfileId = 1, {
MinimumAvailability = MovieStatusType.Announced, Name = this.Name,
Implementation = GetType().Name, Enabled = config.Validate().IsValid && Enabled,
Settings = config EnableAuto = true,
}; ProfileId = 1,
} MinimumAvailability = MovieStatusType.Announced,
Implementation = GetType().Name,
Settings = config
};
} }
public virtual ProviderDefinition Definition { get; set; } public virtual ProviderDefinition Definition { get; set; }

View File

@@ -10,6 +10,8 @@ namespace NzbDrone.Core.NetImport
public interface INetImportFactory : IProviderFactory<INetImport, NetImportDefinition> public interface INetImportFactory : IProviderFactory<INetImport, NetImportDefinition>
{ {
List<INetImport> Enabled(); List<INetImport> Enabled();
List<INetImport> Discoverable();
} }
public class NetImportFactory : ProviderFactory<INetImport, NetImportDefinition>, INetImportFactory public class NetImportFactory : ProviderFactory<INetImport, NetImportDefinition>, INetImportFactory
@@ -46,6 +48,13 @@ namespace NzbDrone.Core.NetImport
return indexers.ToList(); return indexers.ToList();
} }
public List<INetImport> Discoverable()
{
var enabledImporters = GetAvailableProviders().Where(n => (n.GetType() == typeof(Radarr.RadarrLists) || n.GetType() == typeof(TMDb.TMDbImport)));
var indexers = FilterBlockedIndexers(enabledImporters);
return indexers.ToList();
}
private IEnumerable<INetImport> FilterBlockedIndexers(IEnumerable<INetImport> importers) private IEnumerable<INetImport> FilterBlockedIndexers(IEnumerable<INetImport> importers)
{ {
foreach (var importer in importers) foreach (var importer in importers)

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