1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-23 17:14:46 -04:00

Compare commits

...

87 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
Leonardo Galli
3c8162f1d6 Fix test. 2017-05-12 16:52:38 +02:00
Leonardo Galli
096d24ed91 Merge branch 'feature/better-mapping-info' into develop 2017-05-12 16:09:35 +02:00
Leonardo Galli
63e3361fb5 Movies with same name but different year being downloaded regardlessly is now fixed! 2017-05-12 16:09:23 +02:00
Leonardo Galli
51854ef73b Update TaskManager.cs 2017-05-11 23:05:12 +02:00
Levi Wilcox
a0486b54a6 Fixed design issue when deleting css bug (#1480) Fixes #1475 2017-05-11 22:05:34 +02:00
Leonardo Galli
7ed0db10cb 10 Movies are now shown on discover as well as search results.
Show more should also be more consistent now.
2017-05-09 22:28:15 +02:00
Leonardo Galli
e94591a290 Hotfix for when ignored movies would appear again after clicking on show more. 2017-05-09 21:28:22 +02:00
Leonardo Galli
fccd02a0ca Merge remote-tracking branch 'origin/develop' into develop 2017-05-09 20:44:43 +02:00
Leonardo Galli
b49f0e70ed Merge branch 'feature/better-import-exclusions' into develop 2017-05-09 20:44:17 +02:00
Leonardo Galli
fc1585e900 Completely overhauled how import exclusions work.
Currently new exclusions can only be added when adding new movies or deleting old ones. Not manually in the settings menu.
Movies can now be hidden in the new discover feature by using the new import exclusions!
2017-05-09 20:44:07 +02:00
Leonardo Galli
b36ac091fc Fix appveyor build 2017-05-09 19:49:30 +02:00
Leonardo Galli
ab28bfead2 Merge branch 'develop' into feature/better-import-exclusions 2017-05-09 16:55:19 +02:00
Leonardo Galli
3ab3fbfd57 Added ability to discover new movies based on upcoming blurays as well as popular movies (borrowed from steven lu :)) 2017-05-09 16:46:19 +02:00
Leonardo Galli
d133ee3143 Hopefully more logging to catch errors better. 2017-05-08 18:46:03 +02:00
Leonardo Galli
98b6932ffe Merge remote-tracking branch 'origin/develop' into develop 2017-05-08 18:07:38 +02:00
Leonardo Galli
58e81a916c Fixed error when language is present in title, but has dots instead of spaces. For example The.Danish.Girl.2015 2017-05-06 15:26:35 +02:00
PatrickGHanna
817f48448c Fix: A small bug fix for items loading as undefined in organize modal. Movie titles should now show up correctly. (#1424) 2017-05-06 12:31:41 +02:00
hotio
1eca179b4e Update Kodi icon, fixes #1464 (#1492) 2017-05-06 12:30:36 +02:00
Leonardo Galli
b05d505bce Fixed Final in titles parsing as an edition. 2017-05-02 22:15:03 +02:00
Leonardo Galli
ace426e69f Added initial migration. 2017-04-30 14:05:41 +02:00
Mitchell Cash
a57c9917cc Fixed: Radarr not importing torrents in Vuze if the torrent already finished seeding and was stopped (#1471) 2017-04-30 13:14:49 +02:00
Mitchell Cash
9e84b4a782 Fixed: Incorrect imports with Vuze when torrent contains a single file. (#1470) 2017-04-30 10:37:51 +02:00
Mitchell Cash
494ef16735 Fixed: Sonarr UI Authentication cookie should be placed on path (UrlBase) instead of domain alone. Fixes ##1451 2017-04-30 10:30:59 +02:00
Leonardo Galli
5a0f02007f Added trailer links to the discovery page. 2017-04-28 14:14:02 +02:00
Leonardo Galli
b1025e7229 Use Post for tmdbids request, to avoid too long URIs. 2017-04-28 13:14:00 +02:00
Leonardo Galli
446d661345 Added discovery tab based on tmdb recommendations based on your existing movies. (#1450)
Keep scroll position on more in search result view.
Added TMDB score to search results.
2017-04-28 11:04:30 +02:00
David Pooley
3eb351823e Tidy up layout of buttons on the Add Movies page for mobile/tablet (#1454) 2017-04-28 10:59:55 +02:00
morberg
835a7cffa1 Rename Sonarr to Radarr for OSX App 2017-04-26 18:44:57 +02:00
Leonardo Galli
e728330ce4 Minor text fixes. 2017-04-26 15:45:06 +02:00
Leonardo Galli
6f3118c142 Merge remote-tracking branch 'origin/develop' into develop 2017-04-26 15:17:49 +02:00
Leonardo Galli
b568072140 Change default page size to 250. Should help with safari timeouts. 2017-04-26 15:17:42 +02:00
Leonardo Galli
7db92c6bcf Enable automatic renaming, according to naming scheme, of movie folder after creation of the movie. (#1349)
Please test everything you can about this and report back if everything still works correctly.
2017-04-26 13:31:55 +02:00
Leonardo Galli
f1e8a9acfc Fix for error when clicking Rescan Drone Folder 2017-04-19 22:04:34 +02:00
Leonardo Galli
dae389ce64 Fix for error when trying to manually import. 2017-04-19 22:03:52 +02:00
Leonardo Galli
7c3d8c8ff9 Added multiple new editions such as FanEdit, Anniversary and 2in1. 2017-04-19 21:02:01 +02:00
346 changed files with 3689 additions and 974 deletions

1
.idea/Sonarr.iml generated
View File

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

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="ECMAScript 6" />
<excludedPredefinedLibrary name="Radarr/node_modules" />
</component>
</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
* New PassThePopcorn Indexer
* 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
* 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)
@@ -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
* 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:
* [ReSharper](http://www.jetbrains.com/resharper)
* [WebStorm](http://www.jetbrains.com/webstorm)
* [TeamCity](http://www.jetbrains.com/teamcity)
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
* Copyright 2010-2017

View File

@@ -104,11 +104,13 @@ Task("Compile").Does(() => {
});
Task("Gulp").Does(() => {
Npm
.WithLogLevel(NpmLogLevel.Silent)
.FromPath(".")
.Install()
.RunScript("build");
NpmInstall(new NpmInstallSettings {
LogLevel = NpmLogLevel.Silent,
WorkingDirectory = "./",
Production = true
});
NpmRunScript("build");
});
Task("PackageMono").Does(() => {
@@ -166,8 +168,14 @@ Task("PackageOsx").Does(() => {
// Adding MediaInfo dylib
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
CopyFile("./osx/Sonarr", outputFolderOsx + "/Sonarr");
CopyFile("./osx/Radarr", outputFolderOsx + "/Radarr");
});
Task("PackageOsxApp").Does(() => {
@@ -266,9 +274,9 @@ Task("ArtifactsWindows").Does(() => {
Task("ArtifactsWindowsInstaller").Does(() => {
InnoSetup("./setup/nzbdrone.iss", new InnoSetupSettings {
OutputDirectory = artifactsFolder,
OutputDirectory = artifactsFolder,
ToolPath = "./setup/inno/ISCC.exe"
});
});
});
Task("ArtifactsLinux").Does(() => {

View File

@@ -181,7 +181,7 @@ PackageOsx()
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderOsx
echo "Adding Startup script"
cp ./osx/Sonarr $outputFolderOsx
cp ./osx/Radarr $outputFolderOsx
echo "##teamcity[progressFinish 'Creating OS X Package']"
}

44
create_test_cases.py Normal file
View File

@@ -0,0 +1,44 @@
input1 = """Prometheus.Special.Edition.Fan Edit.2012..BRRip.x264.AAC-m2g
Star Wars Episode IV - A New Hope (Despecialized) 1999.mkv
Prometheus.(Special.Edition.Remastered).2012.[Bluray-1080p].mkv
Prometheus Extended 2012
Prometheus Extended Directors Cut Fan Edit 2012
Prometheus Director's Cut 2012
Prometheus Directors Cut 2012
Prometheus.(Extended.Theatrical.Version.IMAX).BluRay.1080p.2012.asdf
2001 A Space Odyssey Director's Cut (1968).mkv
2001: A Space Odyssey (Extended Directors Cut FanEdit) Bluray 1080p 1968
A Fake Movie 2035 Directors 2012.mkv
Blade Runner Director's Cut 2049.mkv
Prometheus 50th Anniversary Edition 2012.mkv
Movie 2in1 2012.mkv
Movie IMAX 2012.mkv"""
output1 = """Special.Edition.Fan Edit BRRip.x264.AAC-m2g
Despecialized mkv
Special.Edition.Remastered Bluray-1080p].mkv
Extended mkv
Extended Directors Cut Fan Edit mkv
Director's Cut mkv
Directors Cut mkv
Extended.Theatrical.Version.IMAX asdf
Director's Cut mkv
Extended Directors Cut FanEdit mkv
Directors mkv
Director's Cut mkv
50th Anniversary Edition mkv
2in1 mkv
IMAX mkv"""
inputs = input1.split("\n")
outputs = output1.split("\n")
real_o = []
for output in outputs:
real_o.append(output.split(" ")[0].replace(".", " ").strip())
count = 0
for inp in inputs:
o = real_o[count]
print "[TestCase(\"{0}\", \"{1}\")]".format(inp, o)
count += 1

View File

@@ -25,7 +25,7 @@ gulp.task('copyHtml', 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(livereload());
});

View File

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

View File

@@ -5,7 +5,7 @@ DIR=$(cd "$(dirname "$0")"; pwd)
#change these values to match your app
EXE_PATH="$DIR/Radarr.exe"
APPNAME="Sonarr"
APPNAME="Radarr"
#set up environment
if [[ -x '/opt/local/bin/mono' ]]; then

View File

@@ -11,7 +11,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>Sonarr</string>
<string>Radarr</string>
<key>CFBundleIconFile</key>
<string>radarr.icns</string>
<key>CFBundleIdentifier</key>

View File

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

View File

@@ -32,10 +32,10 @@ outputFolderMono='./_output_mono'
outputFolderOsx='./_output_osx'
outputFolderOsxApp='./_output_osx_app'
tr -d "\r" < $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr > $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr2
rm $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr
chmod +x $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr2
mv $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr2 $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr >& error.log
tr -d "\r" < $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr > $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr2
rm $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr
chmod +x $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr2
mv $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr2 $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr >& error.log
if [ $runtime = "dotnet" ] ; then
./7za.exe a Radarr_Windows_$VERSION.zip ./Radarr_Windows_$VERSION/*

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

@@ -70,6 +70,7 @@ namespace NzbDrone.Api.Authentication
{
RedirectUrl = _configFileProvider.UrlBase + "/login",
UserMapper = _authenticationService,
Path = _configFileProvider.UrlBase,
CryptographyConfiguration = cryptographyConfiguration
});
}

View File

@@ -46,7 +46,7 @@ namespace NzbDrone.Api.ClientSchema
field.Value = value;
}
if (fieldAttribute.Type == FieldType.Select)
if (fieldAttribute.Type == FieldType.Select || fieldAttribute.Type == FieldType.Tag)
{
field.SelectOptions = GetSelectOptions(fieldAttribute.SelectOptions);
}
@@ -150,7 +150,7 @@ namespace NzbDrone.Api.ClientSchema
private static List<SelectOption> GetSelectOptions(Type selectOptions)
{
if (selectOptions == typeof(Profile))
if (selectOptions == null || selectOptions == typeof(Profile))
{
return new List<SelectOption>();
}
@@ -165,7 +165,7 @@ namespace NzbDrone.Api.ClientSchema
var options = from Enum e in Enum.GetValues(selectOptions)
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,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Api.Extensions;
using NzbDrone.Api.Validation;
using NzbDrone.Common;
@@ -17,14 +18,17 @@ namespace NzbDrone.Api.Commands
{
private readonly IManageCommandQueue _commandQueueManager;
private readonly IServiceFactory _serviceFactory;
private readonly Logger _logger;
public CommandModule(IManageCommandQueue commandQueueManager,
IBroadcastSignalRMessage signalRBroadcaster,
IServiceFactory serviceFactory)
IServiceFactory serviceFactory,
Logger logger)
: base(signalRBroadcaster)
{
_commandQueueManager = commandQueueManager;
_serviceFactory = serviceFactory;
_logger = logger;
GetResourceById = GetCommand;
CreateResource = StartCommand;
@@ -41,7 +45,13 @@ namespace NzbDrone.Api.Commands
private int StartCommand(CommandResource commandResource)
{
var commandType = _serviceFactory.GetImplementations(typeof(Command))
.Single(c => c.Name.Replace("Command", "").Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
.SingleOrDefault(c => c.Name.Replace("Command", "").Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
if (commandType == null)
{
_logger.Error("Found no matching command for {0}", commandResource.Name);
return 0;
}
dynamic command = Request.Body.FromJson(commandType);
command.Trigger = CommandTrigger.Manual;

View File

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

View File

@@ -11,6 +11,8 @@ namespace NzbDrone.Api.Config
public bool AutoDownloadPropers { get; set; }
public bool CreateEmptySeriesFolders { get; set; }
public FileDateType FileDate { get; set; }
public bool AutoRenameFolders { get; set; }
public bool PathsDefaultStatic { get; set; }
public bool SetPermissionsLinux { get; set; }
public string FileChmod { get; set; }
@@ -35,6 +37,8 @@ namespace NzbDrone.Api.Config
AutoDownloadPropers = model.AutoDownloadPropers,
CreateEmptySeriesFolders = model.CreateEmptySeriesFolders,
FileDate = model.FileDate,
AutoRenameFolders = model.AutoRenameFolders,
PathsDefaultStatic = model.PathsDefaultStatic,
SetPermissionsLinux = model.SetPermissionsLinux,
FileChmod = model.FileChmod,

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

View File

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

View File

@@ -46,11 +46,14 @@ namespace NzbDrone.Api.EpisodeFiles
return movie.ToResource();
}
private void SetQuality(MovieFileResource movieFileResource)
{
{
var movieFile = _mediaFileService.GetMovie(movieFileResource.Id);
movieFile.Quality = movieFileResource.Quality;
_mediaFileService.Update(movieFile);
BroadcastResourceChange(ModelAction.Updated, movieFile.Id);
}
private void DeleteMovieFile(int id)

View File

@@ -0,0 +1,45 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Api.ClientSchema;
using NzbDrone.Core.NetImport;
using NzbDrone.Core.NetImport.ImportExclusions;
using NzbDrone.Core.Validation.Paths;
namespace NzbDrone.Api.NetImport
{
public class ImportExclusionsModule : NzbDroneRestModule<ImportExclusionsResource>
{
private readonly IImportExclusionsService _exclusionService;
public ImportExclusionsModule(NetImportFactory netImportFactory, IImportExclusionsService exclusionService) : base("exclusions")
{
_exclusionService = exclusionService;
GetResourceAll = GetAll;
CreateResource = AddExclusion;
DeleteResource = RemoveExclusion;
GetResourceById = GetById;
}
public List<ImportExclusionsResource> GetAll()
{
return _exclusionService.GetAllExclusions().ToResource();
}
public ImportExclusionsResource GetById(int id)
{
return _exclusionService.GetById(id).ToResource();
}
public int AddExclusion(ImportExclusionsResource exclusionResource)
{
var model = exclusionResource.ToModel();
return _exclusionService.AddExclusion(model).Id;
}
public void RemoveExclusion (int id)
{
_exclusionService.RemoveExclusion(new ImportExclusion { Id = id });
}
}
}

View File

@@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.NetImport;
using NzbDrone.Core.Tv;
namespace NzbDrone.Api.NetImport
{
public class ImportExclusionsResource : ProviderResource
{
//public int Id { get; set; }
public int TmdbId { get; set; }
public string MovieTitle { get; set; }
public int MovieYear { get; set; }
}
public static class ImportExclusionsResourceMapper
{
public static ImportExclusionsResource ToResource(this Core.NetImport.ImportExclusions.ImportExclusion model)
{
if (model == null) return null;
return new ImportExclusionsResource
{
Id = model.Id,
TmdbId = model.TmdbId,
MovieTitle = model.MovieTitle,
MovieYear = model.MovieYear
};
}
public static List<ImportExclusionsResource> ToResource(this IEnumerable<Core.NetImport.ImportExclusions.ImportExclusion> exclusions)
{
return exclusions.Select(ToResource).ToList();
}
public static Core.NetImport.ImportExclusions.ImportExclusion ToModel(this ImportExclusionsResource resource)
{
return new Core.NetImport.ImportExclusions.ImportExclusion
{
TmdbId = resource.TmdbId,
MovieTitle = resource.MovieTitle,
MovieYear = resource.MovieYear
};
}
}
}

View File

@@ -113,6 +113,7 @@
<Compile Include="Config\NetImportConfigResource.cs" />
<Compile Include="Extensions\AccessControlHeaders.cs" />
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
<Compile Include="Extensions\Pipelines\UrlBasePipeline.cs" />
<Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" />
<Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" />
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
@@ -270,6 +271,9 @@
<Compile Include="Wanted\MissingModule.cs" />
<Compile Include="Wanted\MovieCutoffModule.cs" />
<Compile Include="Wanted\MovieMissingModule.cs" />
<Compile Include="Series\MovieDiscoverModule.cs" />
<Compile Include="NetImport\ImportExclusionsModule.cs" />
<Compile Include="NetImport\ImportExclusionsResource.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />

View File

@@ -0,0 +1,63 @@
using System.Collections.Generic;
using Nancy;
using NzbDrone.Api.Extensions;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using System.Linq;
using System;
using NzbDrone.Api.REST;
using NzbDrone.Core.NetImport;
using NzbDrone.Api.NetImport;
namespace NzbDrone.Api.Movie
{
public class MovieDiscoverModule : NzbDroneRestModule<MovieResource>
{
private readonly IDiscoverNewMovies _searchProxy;
private readonly INetImportFactory _netImportFactory;
public MovieDiscoverModule(IDiscoverNewMovies searchProxy, INetImportFactory netImportFactory)
: base("/movies/discover")
{
_searchProxy = searchProxy;
_netImportFactory = netImportFactory;
Get["/lists"] = x => GetLists();
Get["/{action?recommendations}"] = x => Search(x.action);
}
private Response Search(string action)
{
var imdbResults = _searchProxy.DiscoverNewMovies(action);
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)
{
foreach (var currentSeries in movies)
{
var resource = currentSeries.ToResource();
var poster = currentSeries.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
if (poster != null)
{
resource.RemotePoster = poster.Url;
}
yield return resource;
}
}
}
}

View File

@@ -17,6 +17,8 @@ using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Validation;
using NzbDrone.SignalR;
using NzbDrone.Core.Datastore;
using Microsoft.CSharp.RuntimeBinder;
using Nancy;
namespace NzbDrone.Api.Movie
{
@@ -58,9 +60,12 @@ namespace NzbDrone.Api.Movie
GetResourceAll = AllMovie;
GetResourcePaged = GetMoviePaged;
GetResourceById = GetMovie;
Get[TITLE_SLUG_ROUTE] = (options) => {
return ReqResExtensions.AsResponse(GetByTitleSlug(options.slug));
};
Get[TITLE_SLUG_ROUTE] = GetByTitleSlug; /*(options) => {
return ReqResExtensions.AsResponse(GetByTitleSlug(options.slug), Nancy.HttpStatusCode.OK);
};*/
CreateResource = AddMovie;
UpdateResource = UpdateMovie;
DeleteResource = DeleteMovie;
@@ -145,9 +150,27 @@ namespace NzbDrone.Api.Movie
return moviesResources;
}
private MovieResource GetByTitleSlug(string slug)
private Response GetByTitleSlug(dynamic options)
{
return MapToResource(_moviesService.FindByTitleSlug(slug));
var slug = "";
try
{
slug = options.slug;
// do stuff with x
}
catch (RuntimeBinderException)
{
return new NotFoundResponse();
}
try
{
return MapToResource(_moviesService.FindByTitleSlug(slug)).AsResponse(Nancy.HttpStatusCode.OK);
}
catch (ModelNotFoundException)
{
return new NotFoundResponse();
}
}
private int AddMovie(MovieResource moviesResource)

View File

@@ -28,6 +28,7 @@ namespace NzbDrone.Api.Movie
public string Overview { get; set; }
public DateTime? InCinemas { get; set; }
public DateTime? PhysicalRelease { get; set; }
public string PhysicalReleaseNote { get; set; }
public List<MediaCover> Images { get; set; }
public string Website { get; set; }
public bool Downloaded { get; set; }
@@ -40,6 +41,7 @@ namespace NzbDrone.Api.Movie
//View & Edit
public string Path { get; set; }
public int ProfileId { get; set; }
public MoviePathState PathState { get; set; }
//Editing Only
public bool Monitored { get; set; }
@@ -115,6 +117,7 @@ namespace NzbDrone.Api.Movie
SortTitle = model.SortTitle,
InCinemas = model.InCinemas,
PhysicalRelease = model.PhysicalRelease,
PhysicalReleaseNote = model.PhysicalReleaseNote,
HasFile = model.HasFile,
Downloaded = downloaded,
//TotalEpisodeCount
@@ -131,6 +134,7 @@ namespace NzbDrone.Api.Movie
Path = model.Path,
ProfileId = model.ProfileId,
PathState = model.PathState,
Monitored = model.Monitored,
MinimumAvailability = model.MinimumAvailability,
@@ -174,6 +178,7 @@ namespace NzbDrone.Api.Movie
SortTitle = resource.SortTitle,
InCinemas = resource.InCinemas,
PhysicalRelease = resource.PhysicalRelease,
PhysicalReleaseNote = resource.PhysicalReleaseNote,
//TotalEpisodeCount
//EpisodeCount
//EpisodeFileCount
@@ -187,6 +192,7 @@ namespace NzbDrone.Api.Movie
Path = resource.Path,
ProfileId = resource.ProfileId,
PathState = resource.PathState,
Monitored = resource.Monitored,
MinimumAvailability = resource.MinimumAvailability,
@@ -217,6 +223,7 @@ namespace NzbDrone.Api.Movie
movie.Path = resource.Path;
movie.ProfileId = resource.ProfileId;
movie.PathState = resource.PathState;
movie.Monitored = resource.Monitored;
movie.MinimumAvailability = resource.MinimumAvailability;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -80,5 +80,30 @@ namespace NzbDrone.Common.Extensions
{
return source.Select(predicate).ToList();
}
public static IEnumerable<T> DropLast<T>(this IEnumerable<T> source, int n)
{
if (source == null)
throw new ArgumentNullException("source");
if (n < 0)
throw new ArgumentOutOfRangeException("n",
"Argument n should be non-negative.");
return InternalDropLast(source, n);
}
private static IEnumerable<T> InternalDropLast<T>(IEnumerable<T> source, int n)
{
Queue<T> buffer = new Queue<T>(n + 1);
foreach (T x in source)
{
buffer.Enqueue(x);
if (buffer.Count == n + 1)
yield return buffer.Dequeue();
}
}
}
}

View File

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

View File

@@ -19,6 +19,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
private List<ReleaseInfo> _reports;
private RemoteMovie _remoteEpisode;
private MappingResult _mappingResult;
private Mock<IDecisionEngineSpecification> _pass1;
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" } };
_remoteEpisode = new RemoteMovie {
Movie = new Movie(),
ParsedMovieInfo = new ParsedMovieInfo()
};
_mappingResult = new MappingResult {Movie = new Movie(), MappingResultType = MappingResultType.Success};
_mappingResult.RemoteMovie = _remoteEpisode;
Mocker.GetMock<IParsingService>()
.Setup(c => c.Map(It.IsAny<ParsedMovieInfo>(), It.IsAny<string>(), It.IsAny<SearchCriteriaBase>()))
.Returns(_remoteEpisode);
.Setup(c => c.Map(It.IsAny<ParsedMovieInfo>(), It.IsAny<string>(), It.IsAny<SearchCriteriaBase>())).Returns(_mappingResult);
}
private void GivenSpecifications(params Mock<IDecisionEngineSpecification>[] mocks)
@@ -121,6 +126,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
GivenSpecifications(_pass1, _pass2, _pass3);
_reports[0].Title = "Not parsable";
_mappingResult.MappingResultType = MappingResultType.NotParsable;
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());
_pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteMovie>(), null), Times.Never());
results.Should().BeEmpty();
results.Should().NotBeEmpty();
}
[Test]
@@ -138,6 +144,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
GivenSpecifications(_pass1, _pass2, _pass3);
_reports[0].Title = "1937 - Snow White and the Seven Dwarves";
_mappingResult.MappingResultType = MappingResultType.NotParsable;
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());
_pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteMovie>(), null), Times.Never());
results.Should().BeEmpty();
results.Should().NotBeEmpty();
}
[Test]
@@ -156,6 +163,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
GivenSpecifications(_pass1, _pass2, _pass3);
_remoteEpisode.Movie = null;
_mappingResult.MappingResultType = MappingResultType.TitleNotFound;
Subject.GetRssDecision(_reports);
@@ -249,6 +257,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
GivenSpecifications(_pass1, _pass2, _pass3);
_remoteEpisode.Movie = null;
_mappingResult.MappingResultType = MappingResultType.TitleNotFound;
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>()
.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>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))

View File

@@ -145,8 +145,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
[TestCase(TransmissionTorrentStatus.Check, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
public void GetItems_should_return_queued_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_queued.Status = apiStatus;
@@ -160,7 +160,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
public void GetItems_should_return_downloading_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_downloading.Status = apiStatus;

View File

@@ -13,6 +13,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
[TestFixture]
public class VuzeFixture : TransmissionFixtureBase<Vuze>
{
[SetUp]
public void Setup_Vuze()
{
// Vuze never sets isFinished.
_completed.IsFinished = false;
}
[Test]
public void queued_item_should_have_required_properties()
{
@@ -147,8 +154,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
[TestCase(TransmissionTorrentStatus.Check, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
public void GetItems_should_return_queued_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_queued.Status = apiStatus;
@@ -162,7 +169,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
public void GetItems_should_return_downloading_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_downloading.Status = apiStatus;
@@ -177,7 +184,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
[TestCase(TransmissionTorrentStatus.Stopped, DownloadItemStatus.Completed, false)]
[TestCase(TransmissionTorrentStatus.CheckWait, DownloadItemStatus.Downloading, true)]
[TestCase(TransmissionTorrentStatus.Check, DownloadItemStatus.Downloading, true)]
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Completed, true)]
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued, true)]
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Completed, true)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed, true)]
public void GetItems_should_return_completed_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus, bool expectedReadOnly)
@@ -294,7 +301,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
}
[Test]
public void should_have_correct_output_directory()
public void should_have_correct_output_directory_for_multifile_torrents()
{
WindowsOnly();
@@ -311,5 +318,25 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
items.First().OutputPath.Should().Be(@"C:\Downloads\" + _title);
}
[Test]
public void should_have_correct_output_directory_for_singlefile_torrents()
{
WindowsOnly();
var fileName = _title + ".mkv";
_downloading.Name = fileName;
_downloading.DownloadDir = @"C:/Downloads";
GivenTorrents(new List<TransmissionTorrent>
{
_downloading
});
var items = Subject.GetItems().ToList();
items.Should().HaveCount(1);
items.First().OutputPath.Should().Be(@"C:\Downloads\" + fileName);
}
}
}
}

View File

@@ -50,7 +50,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
Mocker.GetMock<IParsingService>()
.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()
{

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.IndexerTests.IPTorrentsTests
Subject.Definition = new IndexerDefinition()
{
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
{
[IntegrationTest]
[Ignore("Nyaa is down!")]
public class IndexerIntegrationTests : CoreTest
{
private SingleEpisodeSearchCriteria _singleSearchCriteria;

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
var setting = new NewznabSettings()
{
ApiKey = "",
Url = url
BaseUrl = url
};
@@ -32,13 +32,13 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
var setting = new NewznabSettings
{
ApiKey = "",
Url = url
BaseUrl = url
};
setting.Validate().IsValid.Should().BeFalse();
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()
{
ApiKey = "",
Url = url
BaseUrl = url
};

View File

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

View File

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

View File

@@ -159,6 +159,7 @@
<Compile Include="DecisionEngineTests\RetentionSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\DelaySpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\Search\TorrentSeedingSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\Search\SeriesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\SameEpisodesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RawDiskSpecificationFixture.cs" />
@@ -296,6 +297,7 @@
<Compile Include="OrganizerTests\FileNameBuilderTests\CleanTitleFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\EpisodeTitleCollapseFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\MultiEpisodeFixture.cs" />
<Compile Include="OrganizerTests\GetMovieFolderFixture.cs" />
<Compile Include="ParserTests\MiniSeriesEpisodeParserFixture.cs" />
<Compile Include="ParserTests\RomanNumeralTests\RomanNumeralConversionFixture.cs" />
<Compile Include="Qualities\RevisionComparableFixture.cs" />

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
@@ -734,5 +734,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
.Should().Be(releaseGroup);
}
}
}

View File

@@ -0,0 +1,40 @@
using NUnit.Framework;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Test.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Tv;
using FluentAssertions;
namespace NzbDrone.Core.Test.OrganizerTests
{
[TestFixture]
public class GetMovieFolderFixture : CoreTest<FileNameBuilder>
{
private NamingConfig namingConfig;
[SetUp]
public void Setup()
{
namingConfig = NamingConfig.Default;
Mocker.GetMock<INamingConfigService>()
.Setup(c => c.GetConfig()).Returns(namingConfig);
}
[TestCase("Arrival", 2016, "{Movie Title} ({Release Year})", "Arrival (2016)")]
[TestCase("The Big Short", 2015, "{Movie TitleThe} ({Release Year})", "Big Short, The (2015)")]
[TestCase("The Big Short", 2015, "{Movie Title} ({Release Year})", "The Big Short (2015)")]
public void should_use_movieFolderFormat_to_build_folder_name(string movieTitle, int year, string format, string expected)
{
namingConfig.MovieFolderFormat = format;
var movie = new Movie { Title = movieTitle, Year = year };
Subject.GetMovieFolder(movie).Should().Be(expected);
}
}
}

View File

@@ -51,9 +51,12 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Castle.2009.S01E14.HDTV.XviD.HUN-LOL", Language.Hungarian)]
[TestCase("The Danish Girl 2015", Language.English)]
[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)
{
var result = Parser.Parser.ParseMovieTitle(postTitle);
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
if (result == null)
{
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);
}
//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("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")]
@@ -75,21 +76,81 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("A.I.Artificial.Intelligence.(2001)", "A.I. Artificial Intelligence")]
[TestCase("A.Movie.Name.(1998)", "A Movie Name")]
[TestCase("Thor: The Dark World 2013", "Thor The Dark World")]
[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)
{
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("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)
{
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.1080p.BluRay.x264.DTS-HD.MA.5.1-RARBG")]
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("Star Wars Episode IV - A New Hope 1999 (Despecialized).mkv", "Despecialized")]
[TestCase("Prometheus.2012.(Special.Edition.Remastered).[Bluray-1080p].mkv", "Special Edition Remastered")]
[TestCase("Prometheus 2012 Extended", "Extended")]
[TestCase("Prometheus 2012 Extended Directors Cut Fan Edit", "Extended Directors Cut Fan Edit")]
[TestCase("Prometheus 2012 Director's Cut", "Director's Cut")]
[TestCase("Prometheus 2012 Directors Cut", "Directors Cut")]
[TestCase("Prometheus.2012.(Extended.Theatrical.Version.IMAX).BluRay.1080p.2012.asdf", "Extended Theatrical Version IMAX")]
[TestCase("2001 A Space Odyssey (1968) Director's Cut .mkv", "Director's Cut")]
[TestCase("2001: A Space Odyssey 1968 (Extended Directors Cut FanEdit)", "Extended Directors Cut FanEdit")]
[TestCase("A Fake Movie 2035 2012 Directors.mkv", "Directors")]
[TestCase("Blade Runner 2049 Director's Cut.mkv", "Director's Cut")]
[TestCase("Prometheus 2012 50th Anniversary Edition.mkv", "50th Anniversary Edition")]
[TestCase("Movie 2012 2in1.mkv", "2in1")]
[TestCase("Movie 2012 IMAX.mkv", "IMAX")]
[TestCase("Movie 2012 Restored.mkv", "Restored")]
[TestCase("Prometheus.Special.Edition.Fan Edit.2012..BRRip.x264.AAC-m2g", "Special Edition Fan Edit")]
[TestCase("Star Wars Episode IV - A New Hope (Despecialized) 1999.mkv", "Despecialized")]
[TestCase("Prometheus.(Special.Edition.Remastered).2012.[Bluray-1080p].mkv", "Special Edition Remastered")]
[TestCase("Prometheus Extended 2012", "Extended")]
[TestCase("Prometheus Extended Directors Cut Fan Edit 2012", "Extended Directors Cut Fan Edit")]
[TestCase("Prometheus Director's Cut 2012", "Director's Cut")]
[TestCase("Prometheus Directors Cut 2012", "Directors Cut")]
[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 (Extended Directors Cut FanEdit) 1968 Bluray 1080p", "Extended Directors Cut FanEdit")]
[TestCase("A Fake Movie 2035 Directors 2012.mkv", "Directors")]
[TestCase("Blade Runner Director's Cut 2049.mkv", "Director's Cut")]
[TestCase("Prometheus 50th Anniversary Edition 2012.mkv", "50th Anniversary Edition")]
[TestCase("Movie 2in1 2012.mkv", "2in1")]
[TestCase("Movie IMAX 2012.mkv", "IMAX")]
[TestCase("Fake Movie Final Cut 2016", "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)
{
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 ParsedMovieInfo _parsedMovieInfo;
private ParsedMovieInfo _wrongYearInfo;
private ParsedMovieInfo _wrongTitleInfo;
private ParsedMovieInfo _romanTitleInfo;
private ParsedMovieInfo _alternativeTitleInfo;
private ParsedMovieInfo _umlautInfo;
@@ -71,6 +72,12 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Year = 1900,
};
_wrongTitleInfo = new ParsedMovieInfo
{
MovieTitle = "Other Title",
Year = 2015
};
_alternativeTitleInfo = new ParsedMovieInfo
{
MovieTitle = _movie.AlternativeTitles.First(),
@@ -129,7 +136,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedMovieInfo, "", null);
Mocker.GetMock<IMovieService>()
.Verify(v => v.FindByTitle(It.IsAny<string>()), Times.Once());
.Verify(v => v.FindByTitle(It.IsAny<string>(), It.IsAny<int>()), Times.Once());
}
[Test]
@@ -139,7 +146,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedMovieInfo, "", _movieSearchCriteria);
Mocker.GetMock<ISeriesService>()
Mocker.GetMock<IMovieService>()
.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()
{
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]

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.")]
[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);
@@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.ParserTests.RomanNumeralTests
[Test]
[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]);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -141,7 +141,21 @@ namespace NzbDrone.Core.Configuration
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
{

View File

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

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Configuration
{
@@ -33,6 +34,8 @@ namespace NzbDrone.Core.Configuration
bool CopyUsingHardlinks { get; set; }
bool EnableMediaInfo { get; set; }
string ExtraFileExtensions { get; set; }
bool AutoRenameFolders { get; set; }
bool PathsDefaultStatic { get; set; }
//Permissions (Media Management)
bool SetPermissionsLinux { get; set; }
@@ -52,6 +55,7 @@ namespace NzbDrone.Core.Configuration
bool AllowHardcodedSubs { get; set; }
string WhitelistedHardcodedSubs { get; set; }
ParsingLeniencyType ParsingLeniency { get; set; }
int NetImportSyncInterval { get; set; }
string ListSyncLevel { get; set; }

View File

@@ -26,13 +26,13 @@ namespace NzbDrone.Core.Datastore.Migration
var id = seriesReader.GetInt32(0);
var relativePath = seriesReader.GetString(1);
var result = Parser.Parser.ParseMovieTitle(relativePath);
var result = Parser.Parser.ParseMovieTitle(relativePath, false);
var edition = "";
if (result != null)
{
edition = Parser.Parser.ParseMovieTitle(relativePath).Edition;
edition = Parser.Parser.ParseMovieTitle(relativePath, false).Edition;
}
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(136)]
public class add_pathstate_to_movies : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Movies").AddColumn("PathState").AsInt32().WithDefaultValue(2);
}
}
}

View File

@@ -0,0 +1,60 @@
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
using System.Text;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text.RegularExpressions;
using System.Globalization;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(137)]
public class add_import_exclusions_table : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
if (!this.Schema.Schema("dbo").Table("ImportExclusions").Exists())
{
Create.TableForModel("ImportExclusions")
.WithColumn("TmdbId").AsInt64().NotNullable().Unique().PrimaryKey()
.WithColumn("MovieTitle").AsString().Nullable()
.WithColumn("MovieYear").AsInt64().Nullable().WithDefault(0);
}
Execute.WithConnection(AddExisting);
}
private void AddExisting(IDbConnection conn, IDbTransaction tran)
{
using (IDbCommand getSeriesCmd = conn.CreateCommand())
{
getSeriesCmd.Transaction = tran;
getSeriesCmd.CommandText = @"SELECT Key, Value FROM Config WHERE Key = 'importexclusions'";
TextInfo textInfo = new CultureInfo("en-US", false).TextInfo;
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
{
while (seriesReader.Read())
{
var Key = seriesReader.GetString(0);
var Value = seriesReader.GetString(1);
var importExclusions = Value.Split(',').Select(x => {
return string.Format("(\"{0}\", \"{1}\")", Regex.Replace(x, @"^.*\-(.*)$", "$1"),
textInfo.ToTitleCase(string.Join(" ", x.Split('-').DropLast(1))));
}).ToList();
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "INSERT INTO ImportExclusions (tmdbid, MovieTitle) VALUES " + string.Join(", ", importExclusions);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}

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

@@ -35,6 +35,7 @@ using NzbDrone.Core.Extras.Others;
using NzbDrone.Core.Extras.Subtitles;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.NetImport;
using NzbDrone.Core.NetImport.ImportExclusions;
namespace NzbDrone.Core.Datastore
{
@@ -105,6 +106,8 @@ namespace NzbDrone.Core.Datastore
.Relationship()
.HasOne(s => s.Profile, s => s.ProfileId)
.HasOne(m => m.MovieFile, m => m.MovieFileId);
Mapper.Entity<ImportExclusion>().RegisterModel("ImportExclusions");
Mapper.Entity<Episode>().RegisterModel("Episodes")

View File

@@ -9,6 +9,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine
{
@@ -69,52 +70,87 @@ namespace NzbDrone.Core.DecisionEngine
try
{
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(report.Title);
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(report.Title, _configService.ParsingLeniency > 0);
if (parsedMovieInfo != null && !parsedMovieInfo.MovieTitle.IsNullOrWhiteSpace())
{
RemoteMovie remoteMovie = _parsingService.Map(parsedMovieInfo, report.ImdbId.ToString(), searchCriteria);
remoteMovie.Release = report;
MappingResult result = null;
if (remoteMovie.Movie == null)
{
decision = new DownloadDecision(remoteMovie, new Rejection("Unknown movie. Movie found does not match wanted movie."));
}
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);
}
if (parsedMovieInfo == null || parsedMovieInfo.MovieTitle.IsNullOrWhiteSpace())
{
_logger.Debug("{0} could not be parsed :(.", report.Title);
parsedMovieInfo = new ParsedMovieInfo
{
MovieTitle = report.Title,
Year = 1290,
Language = Language.Unknown,
Quality = new QualityModel(),
};
}
}
else
{
_logger.Trace("{0} could not be parsed :(.", report.Title);
}
if (_configService.ParsingLeniency == ParsingLeniencyType.MappingLenient)
{
result = _parsingService.Map(parsedMovieInfo, report.ImdbId.ToString(), searchCriteria);
}
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)
{

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

@@ -86,8 +86,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission
item.Status = DownloadItemStatus.Warning;
item.Message = torrent.ErrorString;
}
else if (torrent.Status == TransmissionTorrentStatus.Seeding ||
torrent.Status == TransmissionTorrentStatus.SeedingWait)
else if (torrent.LeftUntilDone == 0 && (torrent.Status == TransmissionTorrentStatus.Stopped ||
torrent.Status == TransmissionTorrentStatus.Seeding ||
torrent.Status == TransmissionTorrentStatus.SeedingWait))
{
item.Status = DownloadItemStatus.Completed;
}

View File

@@ -26,7 +26,19 @@ namespace NzbDrone.Core.Download.Clients.Vuze
protected override OsPath GetOutputPath(OsPath outputPath, TransmissionTorrent torrent)
{
_logger.Debug("Vuze output directory: {0}", outputPath);
// Vuze has similar behavior as uTorrent:
// - A multi-file torrent is downloaded in a job folder and 'outputPath' points to that directory directly.
// - A single-file torrent is downloaded in the root folder and 'outputPath' poinst to that root folder.
// We have to make sure the return value points to the job folder OR file.
if (outputPath == null || outputPath.FileName == torrent.Name)
{
_logger.Trace("Vuze output directory: {0}", outputPath);
}
else
{
outputPath = outputPath + torrent.Name;
_logger.Trace("Vuze output file: {0}", outputPath);
}
return outputPath;
}
@@ -50,4 +62,4 @@ namespace NzbDrone.Core.Download.Clients.Vuze
public override string Name => "Vuze";
}
}
}

View File

@@ -28,7 +28,10 @@ namespace NzbDrone.Core.Download
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; }

View File

@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Download
_httpClient = httpClient;
_torrentFileInfoReader = torrentFileInfoReader;
}
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public virtual bool PreferTorrentFile => false;
@@ -149,7 +149,7 @@ namespace NzbDrone.Core.Download
{
magnetUrl = torrentInfo.MagnetUrl;
}
if (PreferTorrentFile)
{
if (torrentUrl.IsNotNullOrWhiteSpace())
@@ -221,7 +221,9 @@ namespace NzbDrone.Core.Download
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");
@@ -321,7 +323,9 @@ namespace NzbDrone.Core.Download
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");

View File

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

View File

@@ -19,7 +19,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
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);

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();
public AwesomeHDSettings()
{
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.")]
@@ -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.")]
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()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -59,6 +59,10 @@ namespace NzbDrone.Core.Indexers.HDBits
query.Username = Settings.Username;
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
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.ThingiProvider;
using NzbDrone.Core.Validation;
using System.Linq.Expressions;
using FluentValidation.Results;
using System.Collections.Generic;
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();
public HDBitsSettings()
{
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")]
@@ -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.")]
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()
{
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()
{
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 NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
@@ -11,26 +11,31 @@ namespace NzbDrone.Core.Indexers.IPTorrents
{
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)")
.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();
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 ...)")]
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()
{

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 IEnumerable<ProviderDefinition> DefaultDefinitions
public virtual IEnumerable<ProviderDefinition> GetDefaultDefinitions()
{
get
{
var config = (IProviderConfig)new TSettings();
var config = (IProviderConfig)new TSettings();
yield return new IndexerDefinition
{
Name = GetType().Name,
EnableRss = config.Validate().IsValid && SupportsRss,
EnableSearch = config.Validate().IsValid && SupportsSearch,
Implementation = GetType().Name,
Settings = config
};
}
yield return new IndexerDefinition
{
Name = GetType().Name,
EnableRss = config.Validate().IsValid && SupportsRss,
EnableSearch = config.Validate().IsValid && SupportsSearch,
Implementation = GetType().Name,
Settings = config
};
}
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);
}
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("Nzb-Tortuga", GetSettings("https://www.nzb-tortuga.com"));
yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su"));
yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat"));
yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws"));
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"));
yield return GetDefinition("nzbplanet.net", GetSettings("https://api.nzbplanet.net"));
yield return GetDefinition("Nzbs.org", GetSettings("http://nzbs.org"));
yield return GetDefinition("omgwtfnzbs", GetSettings("https://api.omgwtfnzbs.me"));
yield return GetDefinition("OZnzb.com", GetSettings("https://api.oznzb.com"));
yield return GetDefinition("PFmonkey", GetSettings("https://www.pfmonkey.com"));
yield return GetDefinition("SimplyNZBs", GetSettings("https://simplynzbs.com"));
yield return GetDefinition("Usenet Crawler", GetSettings("https://www.usenet-crawler.com"));
}
yield return GetDefinition("DOGnzb", GetSettings("https://api.dognzb.cr"));
yield return GetDefinition("DrunkenSlug", GetSettings("https://api.drunkenslug.com"));
yield return GetDefinition("Nzb-Tortuga", GetSettings("https://www.nzb-tortuga.com"));
yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su"));
yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat"));
yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws"));
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"));
yield return GetDefinition("nzbplanet.net", GetSettings("https://api.nzbplanet.net"));
yield return GetDefinition("Nzbs.org", GetSettings("http://nzbs.org"));
yield return GetDefinition("omgwtfnzbs", GetSettings("https://api.omgwtfnzbs.me"));
yield return GetDefinition("OZnzb.com", GetSettings("https://api.oznzb.com"));
yield return GetDefinition("PFmonkey", GetSettings("https://www.pfmonkey.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)
@@ -79,7 +76,7 @@ namespace NzbDrone.Core.Indexers.Newznab
private NewznabSettings GetSettings(string url, params int[] categories)
{
var settings = new NewznabSettings { Url = url };
var settings = new NewznabSettings { BaseUrl = url };
if (categories.Any())
{

View File

@@ -42,7 +42,7 @@ namespace NzbDrone.Core.Indexers.Newznab
{
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())
{
@@ -59,7 +59,7 @@ namespace NzbDrone.Core.Indexers.Newznab
}
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;
}
@@ -69,12 +69,12 @@ namespace NzbDrone.Core.Indexers.Newznab
}
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;
}
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;

View File

@@ -59,7 +59,18 @@ namespace NzbDrone.Core.Indexers.Newznab
else
{
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;
@@ -74,7 +85,7 @@ namespace NzbDrone.Core.Indexers.Newznab
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())
{

View File

@@ -25,12 +25,12 @@ namespace NzbDrone.Core.Indexers.Newznab
private static bool ShouldHaveApiKey(NewznabSettings settings)
{
if (settings.Url == null)
if (settings.BaseUrl == null)
{
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);
@@ -47,14 +47,14 @@ namespace NzbDrone.Core.Indexers.Newznab
return null;
});
RuleFor(c => c.Url).ValidRootUrl();
RuleFor(c => c.BaseUrl).ValidRootUrl();
RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey);
RuleFor(c => c.AdditionalParameters).Matches(AdditionalParametersRegex)
.When(c => !c.AdditionalParameters.IsNullOrWhiteSpace());
}
}
public class NewznabSettings : IProviderConfig
public class NewznabSettings : IIndexerSettings
{
private static readonly NewznabSettingsValidator Validator = new NewznabSettingsValidator();
@@ -65,7 +65,7 @@ namespace NzbDrone.Core.Indexers.Newznab
}
[FieldDefinition(0, Label = "URL")]
public string Url { get; set; }
public string BaseUrl { get; set; }
[FieldDefinition(1, Label = "API Key")]
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)]
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()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -1,6 +1,5 @@
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
using System.Text.RegularExpressions;
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();
@@ -22,6 +21,7 @@ namespace NzbDrone.Core.Indexers.Nyaa
{
BaseUrl = "http://www.nyaa.se";
AdditionalParameters = "&cats=1_37&filter=1";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
}
[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.")]
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()
{
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();
@@ -24,6 +24,9 @@ namespace NzbDrone.Core.Indexers.Omgwtfnzbs
Delay = 30;
}
// Unused since Omg has a hardcoded url.
public string BaseUrl { get; set; }
[FieldDefinition(0, Label = "Username")]
public string Username { get; set; }

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