mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-22 22:43:31 -07:00
Merge remote-tracking branch 'refs/remotes/galli-leo/develop' into develop
This commit is contained in:
commit
49c7c033d9
191 changed files with 2456 additions and 304 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
4
.github/ISSUE_TEMPLATE.md
vendored
4
.github/ISSUE_TEMPLATE.md
vendored
|
@ -2,4 +2,6 @@
|
||||||
|
|
||||||
|
|
||||||
Provide a description of the feature request or bug, the more details the better.
|
Provide a description of the feature request or bug, the more details the better.
|
||||||
Please use https://forums.sonarr.tv/ for support or other questions. (When in doubt, use the forums)
|
When possible include a log!
|
||||||
|
|
||||||
|
Please use our [Discord server](https://discord.gg/NWYch8M) for support or longer discussions.
|
||||||
|
|
|
@ -10,3 +10,5 @@ install:
|
||||||
after_success:
|
after_success:
|
||||||
- chmod +x package.sh
|
- chmod +x package.sh
|
||||||
- ./package.sh
|
- ./package.sh
|
||||||
|
notifications:
|
||||||
|
- webhooks: https://discordapp.com/api/webhooks/266910310219251712/V-QvCcnYkg3O8PMevcAJOJyCgrYkZQoF2pupLDGbaISNUECmYPd6LRwl3avKHsPyfgWP
|
||||||
|
|
8
build.sh
8
build.sh
|
@ -154,8 +154,8 @@ PackageMono()
|
||||||
cp $sourceFolder/NzbDrone.Common/CurlSharp.dll.config $outputFolderMono
|
cp $sourceFolder/NzbDrone.Common/CurlSharp.dll.config $outputFolderMono
|
||||||
|
|
||||||
echo "Renaming NzbDrone.Console.exe to NzbDrone.exe"
|
echo "Renaming NzbDrone.Console.exe to NzbDrone.exe"
|
||||||
rm $outputFolderMono/NzbDrone.exe*
|
rm $outputFolderMono/Radarr.exe*
|
||||||
for file in $outputFolderMono/NzbDrone.Console.exe*; do
|
for file in $outputFolderMono/Radarr.Console.exe*; do
|
||||||
mv "$file" "${file//.Console/}"
|
mv "$file" "${file//.Console/}"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -192,8 +192,8 @@ PackageOsxApp()
|
||||||
rm -rf $outputFolderOsxApp
|
rm -rf $outputFolderOsxApp
|
||||||
mkdir $outputFolderOsxApp
|
mkdir $outputFolderOsxApp
|
||||||
|
|
||||||
cp -r ./osx/Sonarr.app $outputFolderOsxApp
|
cp -r ./osx/Radarr.app $outputFolderOsxApp
|
||||||
cp -r $outputFolderOsx $outputFolderOsxApp/Sonarr.app/Contents/MacOS
|
cp -r $outputFolderOsx $outputFolderOsxApp/Radarr.app/Contents/MacOS
|
||||||
|
|
||||||
echo "##teamcity[progressFinish 'Creating OS X App Package']"
|
echo "##teamcity[progressFinish 'Creating OS X App Package']"
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>Sonarr</string>
|
<string>Sonarr</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>sonarr.icns</string>
|
<string>radarr.icns</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.osx.sonarr.tv</string>
|
<string>com.osx.sonarr.tv</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
BIN
osx/Radarr.app/Contents/Resources/radarr.icns
Normal file
BIN
osx/Radarr.app/Contents/Resources/radarr.icns
Normal file
Binary file not shown.
|
@ -4,7 +4,7 @@
|
||||||
DIR=$(cd "$(dirname "$0")"; pwd)
|
DIR=$(cd "$(dirname "$0")"; pwd)
|
||||||
|
|
||||||
#change these values to match your app
|
#change these values to match your app
|
||||||
EXE_PATH="$DIR/NzbDrone.exe"
|
EXE_PATH="$DIR/Radarr.exe"
|
||||||
APPNAME="Sonarr"
|
APPNAME="Sonarr"
|
||||||
|
|
||||||
#set up environment
|
#set up environment
|
||||||
|
|
|
@ -17,10 +17,10 @@ outputFolderMono='./_output_mono'
|
||||||
outputFolderOsx='./_output_osx'
|
outputFolderOsx='./_output_osx'
|
||||||
outputFolderOsxApp='./_output_osx_app'
|
outputFolderOsxApp='./_output_osx_app'
|
||||||
|
|
||||||
tr -d "\r" < $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr > $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr2
|
tr -d "\r" < $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr > $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr2
|
||||||
rm $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr
|
rm $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr
|
||||||
chmod +x $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr2
|
chmod +x $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr2
|
||||||
mv $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr2 $outputFolderOsxApp/Sonarr.app/Contents/MacOS/Sonarr >& error.log
|
mv $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr2 $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr >& error.log
|
||||||
|
|
||||||
cp -r $outputFolder/ Radarr_Windows_$VERSION
|
cp -r $outputFolder/ Radarr_Windows_$VERSION
|
||||||
cp -r $outputFolderMono/ Radarr_Mono_$VERSION
|
cp -r $outputFolderMono/ Radarr_Mono_$VERSION
|
||||||
|
|
47
readme.md
47
readme.md
|
@ -3,40 +3,43 @@
|
||||||
This fork of Sonarr aims to turn it into something like Couchpotato.
|
This fork of Sonarr aims to turn it into something like Couchpotato.
|
||||||
|
|
||||||
## Currently working:
|
## Currently working:
|
||||||
* Adding new movies (Note: Movies are currently added as one series with one season and one episode. This will change in the future)
|
* Adding new movies
|
||||||
* Manually searching for releases of movies.
|
* Manually searching for releases of movies.
|
||||||
* Automatically searching for releases.
|
* Automatically searching for releases.
|
||||||
* Rarbg.to indexer (Other indexers are coming, I just need to find the right categories)
|
* Automatically importing downloaded movies.
|
||||||
* Everything that has nothing to do with series from Sonarr should be working as well.
|
* Recognizing Special Editions, Director's Cut, etc.
|
||||||
|
* Identifying releases with hardcoded subs.
|
||||||
|
* Rarbg.to, Torznab and Newznab Indexer.
|
||||||
|
* QBittorrent and Deluge download client (Other clients are coming)
|
||||||
|
* New TorrentPotato Indexer (Works well with [Jackett](https://github.com/Jackett/Jackett))
|
||||||
|
|
||||||
## Planned Features:
|
## Planned Features:
|
||||||
* Scanning PreDB to know when a new release is available.
|
* Scanning PreDB to know when a new release is available.
|
||||||
* Fixing the other Indexers.
|
* Fixing the other Indexers and download clients.
|
||||||
* Fixing how movies are stored and displayed.
|
|
||||||
* Importing of Sonarr config.
|
* Importing of Sonarr config.
|
||||||
* New TorrentPotato Indexer.
|
|
||||||
|
|
||||||
## Major Features Include: ##
|
|
||||||
|
|
||||||
* Support for major platforms: Windows, Linux, OSX, Raspberry Pi, etc.
|
|
||||||
* Automatically detects new episodes
|
|
||||||
* Can scan your existing library and download any missing episodes
|
|
||||||
* Can watch for better quality of the episodes you already have and do an automatic upgrade. *eg. from DVD to Blu-Ray*
|
|
||||||
* Automatic failed download handling will try another release if one fails
|
|
||||||
* Manual search so you can pick any release or to see why a release was not downloaded automatically
|
|
||||||
* Fully configurable episode renaming
|
|
||||||
* Full integration with SABNzbd and NzbGet
|
|
||||||
* Full integration with XBMC, Plex (notification, library update, metadata)
|
|
||||||
* Full support for specials and multi-episode releases
|
|
||||||
* And a beautiful UI
|
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
The latest precompiled binary versions can be found here: https://github.com/galli-leo/Radarr/releases.
|
The latest precompiled binary versions can be found here: https://github.com/galli-leo/Radarr/releases.
|
||||||
|
|
||||||
|
For more up to date versions (but also sometimes broken), daily builds can be found here:
|
||||||
|
* [OSX](https://leonardogalli.ch/radarr/builds/latest.php?os=osx)
|
||||||
|
* [Windows](https://leonardogalli.ch/radarr/builds/latest.php?os=windows)
|
||||||
|
* [Linux](https://leonardogalli.ch/radarr/builds/latest.php?os=mono)
|
||||||
|
|
||||||
|
## Major Features Include: ##
|
||||||
|
|
||||||
|
* Support for major platforms: Windows, Linux, OSX, Raspberry Pi, etc.
|
||||||
|
* Can watch for better quality of the movies you have and do an upgrade.
|
||||||
|
* Automatic failed download handling will try another release if one fails
|
||||||
|
* Manual search so you can pick any release or to see why a release was not downloaded automatically.
|
||||||
|
* Full integration with SABNzbd and NzbGet.
|
||||||
|
* Full integration with XBMC, Plex (notification, library update, metadata).
|
||||||
|
* And a beautiful UI
|
||||||
|
|
||||||
## Configuring Development Environment: ##
|
## Configuring Development Environment: ##
|
||||||
|
|
||||||
### Requirements ###
|
### Requirements ###
|
||||||
- Visual Studio 2015 [Free Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx)
|
- Visual Studio 2015 [Free Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) or Mono
|
||||||
- [Git](http://git-scm.com/downloads)
|
- [Git](http://git-scm.com/downloads)
|
||||||
- [NodeJS](http://nodejs.org/download/)
|
- [NodeJS](http://nodejs.org/download/)
|
||||||
|
|
||||||
|
@ -52,7 +55,7 @@ The latest precompiled binary versions can be found here: https://github.com/gal
|
||||||
|
|
||||||
|
|
||||||
### Development ###
|
### Development ###
|
||||||
- Open `NzbDrone.sln` in Visual Studio
|
- Open `NzbDrone.sln` in Visual Studio or run the build.sh script, if Mono is installed.
|
||||||
- Make sure `NzbDrone.Console` is set as the startup project
|
- Make sure `NzbDrone.Console` is set as the startup project
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
sonarr.icns
Normal file
BIN
sonarr.icns
Normal file
Binary file not shown.
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("10.0.0.*")]
|
[assembly: AssemblyVersion("0.1.0.*")]
|
||||||
|
|
|
@ -21,4 +21,4 @@ using System.Runtime.InteropServices;
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
[assembly: Guid("260b2ff9-d3b7-4d8a-b720-a12c93d045e5")]
|
[assembly: Guid("260b2ff9-d3b7-4d8a-b720-a12c93d045e5")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("10.0.0.*")]
|
[assembly: AssemblyVersion("0.1.0.*")]
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace NzbDrone.Api.Indexers
|
||||||
public string Indexer { get; set; }
|
public string Indexer { get; set; }
|
||||||
public string ReleaseGroup { get; set; }
|
public string ReleaseGroup { get; set; }
|
||||||
public string ReleaseHash { get; set; }
|
public string ReleaseHash { get; set; }
|
||||||
|
public string Edition { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public bool FullSeason { get; set; }
|
public bool FullSeason { get; set; }
|
||||||
public int SeasonNumber { get; set; }
|
public int SeasonNumber { get; set; }
|
||||||
|
@ -90,6 +91,55 @@ namespace NzbDrone.Api.Indexers
|
||||||
if (model.IsForMovie)
|
if (model.IsForMovie)
|
||||||
{
|
{
|
||||||
downloadAllowed = model.RemoteMovie.DownloadAllowed;
|
downloadAllowed = model.RemoteMovie.DownloadAllowed;
|
||||||
|
var parsedMovieInfo = model.RemoteMovie.ParsedMovieInfo;
|
||||||
|
|
||||||
|
return new ReleaseResource
|
||||||
|
{
|
||||||
|
Guid = releaseInfo.Guid,
|
||||||
|
Quality = parsedMovieInfo.Quality,
|
||||||
|
//QualityWeight
|
||||||
|
Age = releaseInfo.Age,
|
||||||
|
AgeHours = releaseInfo.AgeHours,
|
||||||
|
AgeMinutes = releaseInfo.AgeMinutes,
|
||||||
|
Size = releaseInfo.Size,
|
||||||
|
IndexerId = releaseInfo.IndexerId,
|
||||||
|
Indexer = releaseInfo.Indexer,
|
||||||
|
ReleaseGroup = parsedMovieInfo.ReleaseGroup,
|
||||||
|
ReleaseHash = parsedMovieInfo.ReleaseHash,
|
||||||
|
Title = releaseInfo.Title,
|
||||||
|
FullSeason = parsedMovieInfo.FullSeason,
|
||||||
|
SeasonNumber = parsedMovieInfo.SeasonNumber,
|
||||||
|
Language = parsedMovieInfo.Language,
|
||||||
|
AirDate = "",
|
||||||
|
SeriesTitle = parsedMovieInfo.MovieTitle,
|
||||||
|
EpisodeNumbers = new int[0],
|
||||||
|
AbsoluteEpisodeNumbers = new int[0],
|
||||||
|
Approved = model.Approved,
|
||||||
|
TemporarilyRejected = model.TemporarilyRejected,
|
||||||
|
Rejected = model.Rejected,
|
||||||
|
TvdbId = releaseInfo.TvdbId,
|
||||||
|
TvRageId = releaseInfo.TvRageId,
|
||||||
|
Rejections = model.Rejections.Select(r => r.Reason).ToList(),
|
||||||
|
PublishDate = releaseInfo.PublishDate,
|
||||||
|
CommentUrl = releaseInfo.CommentUrl,
|
||||||
|
DownloadUrl = releaseInfo.DownloadUrl,
|
||||||
|
InfoUrl = releaseInfo.InfoUrl,
|
||||||
|
DownloadAllowed = downloadAllowed,
|
||||||
|
//ReleaseWeight
|
||||||
|
|
||||||
|
MagnetUrl = torrentInfo.MagnetUrl,
|
||||||
|
InfoHash = torrentInfo.InfoHash,
|
||||||
|
Seeders = torrentInfo.Seeders,
|
||||||
|
Leechers = (torrentInfo.Peers.HasValue && torrentInfo.Seeders.HasValue) ? (torrentInfo.Peers.Value - torrentInfo.Seeders.Value) : (int?)null,
|
||||||
|
Protocol = releaseInfo.DownloadProtocol,
|
||||||
|
|
||||||
|
Edition = parsedMovieInfo.Edition,
|
||||||
|
|
||||||
|
IsDaily = false,
|
||||||
|
IsAbsoluteNumbering = false,
|
||||||
|
IsPossibleSpecialEpisode = false,
|
||||||
|
Special = parsedMovieInfo.Special,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead? (Got a huge Deja Vu, didn't we talk about this already once?)
|
// TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead? (Got a huge Deja Vu, didn't we talk about this already once?)
|
||||||
|
|
|
@ -6,6 +6,6 @@ using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: Guid("4c0922d7-979e-4ff7-b44b-b8ac2100eeb5")]
|
[assembly: Guid("4c0922d7-979e-4ff7-b44b-b8ac2100eeb5")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("10.0.0.*")]
|
[assembly: AssemblyVersion("0.1.0.*")]
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("NzbDrone.Core")]
|
[assembly: InternalsVisibleTo("NzbDrone.Core")]
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace NzbDrone.Api.Movie
|
||||||
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
|
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
|
||||||
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
|
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
|
||||||
PostValidator.RuleFor(s => s.Title).NotEmpty();
|
PostValidator.RuleFor(s => s.Title).NotEmpty();
|
||||||
PostValidator.RuleFor(s => s.ImdbId).NotNull().NotEmpty().SetValidator(moviesExistsValidator);
|
PostValidator.RuleFor(s => s.TmdbId).NotNull().NotEmpty().SetValidator(moviesExistsValidator);
|
||||||
|
|
||||||
PutValidator.RuleFor(s => s.Path).IsValidPath();
|
PutValidator.RuleFor(s => s.Path).IsValidPath();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ namespace NzbDrone.Api.Movie
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
public DateTime? InCinemas { get; set; }
|
public DateTime? InCinemas { get; set; }
|
||||||
public List<MediaCover> Images { get; set; }
|
public List<MediaCover> Images { get; set; }
|
||||||
|
public string Website { get; set; }
|
||||||
|
|
||||||
public string RemotePoster { get; set; }
|
public string RemotePoster { get; set; }
|
||||||
public int Year { get; set; }
|
public int Year { get; set; }
|
||||||
|
@ -42,6 +43,7 @@ namespace NzbDrone.Api.Movie
|
||||||
public DateTime? LastInfoSync { get; set; }
|
public DateTime? LastInfoSync { get; set; }
|
||||||
public string CleanTitle { get; set; }
|
public string CleanTitle { get; set; }
|
||||||
public string ImdbId { get; set; }
|
public string ImdbId { get; set; }
|
||||||
|
public int TmdbId { get; set; }
|
||||||
public string TitleSlug { get; set; }
|
public string TitleSlug { get; set; }
|
||||||
public string RootFolderPath { get; set; }
|
public string RootFolderPath { get; set; }
|
||||||
public string Certification { get; set; }
|
public string Certification { get; set; }
|
||||||
|
@ -50,6 +52,7 @@ namespace NzbDrone.Api.Movie
|
||||||
public DateTime Added { get; set; }
|
public DateTime Added { get; set; }
|
||||||
public AddMovieOptions AddOptions { get; set; }
|
public AddMovieOptions AddOptions { get; set; }
|
||||||
public Ratings Ratings { get; set; }
|
public Ratings Ratings { get; set; }
|
||||||
|
public List<string> AlternativeTitles { get; set; }
|
||||||
|
|
||||||
//TODO: Add series statistics as a property of the series (instead of individual properties)
|
//TODO: Add series statistics as a property of the series (instead of individual properties)
|
||||||
|
|
||||||
|
@ -79,7 +82,7 @@ namespace NzbDrone.Api.Movie
|
||||||
return new MovieResource
|
return new MovieResource
|
||||||
{
|
{
|
||||||
Id = model.Id,
|
Id = model.Id,
|
||||||
|
TmdbId = model.TmdbId,
|
||||||
Title = model.Title,
|
Title = model.Title,
|
||||||
//AlternateTitles
|
//AlternateTitles
|
||||||
SortTitle = model.SortTitle,
|
SortTitle = model.SortTitle,
|
||||||
|
@ -108,10 +111,12 @@ namespace NzbDrone.Api.Movie
|
||||||
TitleSlug = model.TitleSlug,
|
TitleSlug = model.TitleSlug,
|
||||||
RootFolderPath = model.RootFolderPath,
|
RootFolderPath = model.RootFolderPath,
|
||||||
Certification = model.Certification,
|
Certification = model.Certification,
|
||||||
|
Website = model.Website,
|
||||||
Genres = model.Genres,
|
Genres = model.Genres,
|
||||||
Tags = model.Tags,
|
Tags = model.Tags,
|
||||||
Added = model.Added,
|
Added = model.Added,
|
||||||
AddOptions = model.AddOptions,
|
AddOptions = model.AddOptions,
|
||||||
|
AlternativeTitles = model.AlternativeTitles,
|
||||||
Ratings = model.Ratings
|
Ratings = model.Ratings
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -123,6 +128,7 @@ namespace NzbDrone.Api.Movie
|
||||||
return new Core.Tv.Movie
|
return new Core.Tv.Movie
|
||||||
{
|
{
|
||||||
Id = resource.Id,
|
Id = resource.Id,
|
||||||
|
TmdbId = resource.TmdbId,
|
||||||
|
|
||||||
Title = resource.Title,
|
Title = resource.Title,
|
||||||
//AlternateTitles
|
//AlternateTitles
|
||||||
|
@ -151,10 +157,12 @@ namespace NzbDrone.Api.Movie
|
||||||
TitleSlug = resource.TitleSlug,
|
TitleSlug = resource.TitleSlug,
|
||||||
RootFolderPath = resource.RootFolderPath,
|
RootFolderPath = resource.RootFolderPath,
|
||||||
Certification = resource.Certification,
|
Certification = resource.Certification,
|
||||||
|
Website = resource.Website,
|
||||||
Genres = resource.Genres,
|
Genres = resource.Genres,
|
||||||
Tags = resource.Tags,
|
Tags = resource.Tags,
|
||||||
Added = resource.Added,
|
Added = resource.Added,
|
||||||
AddOptions = resource.AddOptions,
|
AddOptions = resource.AddOptions,
|
||||||
|
AlternativeTitles = resource.AlternativeTitles,
|
||||||
Ratings = resource.Ratings
|
Ratings = resource.Ratings
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -162,6 +170,7 @@ namespace NzbDrone.Api.Movie
|
||||||
public static Core.Tv.Movie ToModel(this MovieResource resource, Core.Tv.Movie movie)
|
public static Core.Tv.Movie ToModel(this MovieResource resource, Core.Tv.Movie movie)
|
||||||
{
|
{
|
||||||
movie.ImdbId = resource.ImdbId;
|
movie.ImdbId = resource.ImdbId;
|
||||||
|
movie.TmdbId = resource.TmdbId;
|
||||||
|
|
||||||
movie.Path = resource.Path;
|
movie.Path = resource.Path;
|
||||||
movie.ProfileId = resource.ProfileId;
|
movie.ProfileId = resource.ProfileId;
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace NzbDrone.Api.System.Tasks
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string TaskName { get; set; }
|
public string TaskName { get; set; }
|
||||||
public int Interval { get; set; }
|
public double Interval { get; set; }
|
||||||
public DateTime LastExecution { get; set; }
|
public DateTime LastExecution { get; set; }
|
||||||
public DateTime NextExecution { get; set; }
|
public DateTime NextExecution { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ using NzbDrone.Core.Jobs;
|
||||||
using NzbDrone.Core.Lifecycle;
|
using NzbDrone.Core.Lifecycle;
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Host;
|
using Radarr.Host;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
|
@ -3,8 +3,9 @@ using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Model;
|
using NzbDrone.Common.Model;
|
||||||
using NzbDrone.Common.Processes;
|
using NzbDrone.Common.Processes;
|
||||||
using NzbDrone.Host;
|
using Radarr.Host;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
|
using Radarr.Host;
|
||||||
|
|
||||||
namespace NzbDrone.App.Test
|
namespace NzbDrone.App.Test
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,4 +21,4 @@ using System.Runtime.InteropServices;
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
[assembly: Guid("b47d34ef-05e8-4826-8a57-9dd05106c964")]
|
[assembly: Guid("b47d34ef-05e8-4826-8a57-9dd05106c964")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("10.0.0.*")]
|
[assembly: AssemblyVersion("0.1.0.*")]
|
||||||
|
|
|
@ -3,7 +3,7 @@ using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Host;
|
using Radarr.Host;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
namespace NzbDrone.App.Test
|
namespace NzbDrone.App.Test
|
||||||
|
|
|
@ -21,4 +21,4 @@ using System.Runtime.InteropServices;
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
[assembly: Guid("6b8945f5-f5b5-4729-865d-f958fbd673d9")]
|
[assembly: Guid("6b8945f5-f5b5-4729-865d-f958fbd673d9")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("10.0.0.*")]
|
[assembly: AssemblyVersion("0.1.0.*")]
|
||||||
|
|
|
@ -263,7 +263,7 @@ namespace NzbDrone.Common.Test
|
||||||
[Test]
|
[Test]
|
||||||
public void GetUpdateClientExePath()
|
public void GetUpdateClientExePath()
|
||||||
{
|
{
|
||||||
GetIAppDirectoryInfo().GetUpdateClientExePath().Should().BeEquivalentTo(@"C:\Temp\nzbdrone_update\NzbDrone.Update.exe".AsOsAgnostic());
|
GetIAppDirectoryInfo().GetUpdateClientExePath().Should().BeEquivalentTo(@"C:\Temp\nzbdrone_update\Radarr.Update.exe".AsOsAgnostic());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|
|
@ -5,7 +5,7 @@ using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Lifecycle;
|
using NzbDrone.Core.Lifecycle;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Host;
|
using Radarr.Host;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
namespace NzbDrone.Common.Test
|
namespace NzbDrone.Common.Test
|
||||||
|
|
|
@ -6,6 +6,8 @@ namespace NzbDrone.Common.Cloud
|
||||||
{
|
{
|
||||||
IHttpRequestBuilderFactory Services { get; }
|
IHttpRequestBuilderFactory Services { get; }
|
||||||
IHttpRequestBuilderFactory SkyHookTvdb { get; }
|
IHttpRequestBuilderFactory SkyHookTvdb { get; }
|
||||||
|
IHttpRequestBuilderFactory TMDB { get; }
|
||||||
|
IHttpRequestBuilderFactory TMDBSingle { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SonarrCloudRequestBuilder : ISonarrCloudRequestBuilder
|
public class SonarrCloudRequestBuilder : ISonarrCloudRequestBuilder
|
||||||
|
@ -18,10 +20,20 @@ namespace NzbDrone.Common.Cloud
|
||||||
SkyHookTvdb = new HttpRequestBuilder("http://skyhook.sonarr.tv/v1/tvdb/{route}/{language}/")
|
SkyHookTvdb = new HttpRequestBuilder("http://skyhook.sonarr.tv/v1/tvdb/{route}/{language}/")
|
||||||
.SetSegment("language", "en")
|
.SetSegment("language", "en")
|
||||||
.CreateFactory();
|
.CreateFactory();
|
||||||
|
|
||||||
|
TMDB = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}/{id}{secondaryRoute}")
|
||||||
|
.AddQueryParam("api_key", "1a7373301961d03f97f853a876dd1212")
|
||||||
|
.CreateFactory();
|
||||||
|
|
||||||
|
TMDBSingle = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}")
|
||||||
|
.AddQueryParam("api_key", "1a7373301961d03f97f853a876dd1212")
|
||||||
|
.CreateFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHttpRequestBuilderFactory Services { get; private set; }
|
public IHttpRequestBuilderFactory Services { get; private set; }
|
||||||
|
|
||||||
public IHttpRequestBuilderFactory SkyHookTvdb { get; private set; }
|
public IHttpRequestBuilderFactory SkyHookTvdb { get; private set; }
|
||||||
|
public IHttpRequestBuilderFactory TMDB { get; private set; }
|
||||||
|
public IHttpRequestBuilderFactory TMDBSingle { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace NzbDrone.Common.Extensions
|
||||||
private const string NZBDRONE_DB = "nzbdrone.db";
|
private const string NZBDRONE_DB = "nzbdrone.db";
|
||||||
private const string NZBDRONE_LOG_DB = "logs.db";
|
private const string NZBDRONE_LOG_DB = "logs.db";
|
||||||
private const string NLOG_CONFIG_FILE = "nlog.config";
|
private const string NLOG_CONFIG_FILE = "nlog.config";
|
||||||
private const string UPDATE_CLIENT_EXE = "NzbDrone.Update.exe";
|
private const string UPDATE_CLIENT_EXE = "Radarr.Update.exe";
|
||||||
private const string BACKUP_FOLDER = "Backups";
|
private const string BACKUP_FOLDER = "Backups";
|
||||||
|
|
||||||
private static readonly string UPDATE_SANDBOX_FOLDER_NAME = "nzbdrone_update" + Path.DirectorySeparatorChar;
|
private static readonly string UPDATE_SANDBOX_FOLDER_NAME = "nzbdrone_update" + Path.DirectorySeparatorChar;
|
||||||
|
|
|
@ -35,8 +35,8 @@ namespace NzbDrone.Common.Processes
|
||||||
{
|
{
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public const string NZB_DRONE_PROCESS_NAME = "NzbDrone";
|
public const string NZB_DRONE_PROCESS_NAME = "Radarr";
|
||||||
public const string NZB_DRONE_CONSOLE_PROCESS_NAME = "NzbDrone.Console";
|
public const string NZB_DRONE_CONSOLE_PROCESS_NAME = "Radarr.Console";
|
||||||
|
|
||||||
public ProcessProvider(Logger logger)
|
public ProcessProvider(Logger logger)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,4 +9,4 @@ using System.Runtime.InteropServices;
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
[assembly: Guid("b6eaa144-e13b-42e5-a738-c60d89c0f728")]
|
[assembly: Guid("b6eaa144-e13b-42e5-a738-c60d89c0f728")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("10.0.0.*")]
|
[assembly: AssemblyVersion("0.1.0.*")]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using NzbDrone.Host;
|
using Radarr.Host;
|
||||||
|
|
||||||
namespace NzbDrone.Console
|
namespace NzbDrone.Console
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,7 @@ using System.Net.Sockets;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Instrumentation;
|
using NzbDrone.Common.Instrumentation;
|
||||||
using NzbDrone.Host;
|
using Radarr.Host;
|
||||||
|
|
||||||
namespace NzbDrone.Console
|
namespace NzbDrone.Console
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>NzbDrone.Console</RootNamespace>
|
<RootNamespace>NzbDrone.Console</RootNamespace>
|
||||||
<AssemblyName>NzbDrone.Console</AssemblyName>
|
<AssemblyName>Radarr.Console</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile>
|
<TargetFrameworkProfile>
|
||||||
|
|
|
@ -8,4 +8,4 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyTitle("NzbDrone.Host")]
|
[assembly: AssemblyTitle("NzbDrone.Host")]
|
||||||
[assembly: Guid("67AADCD9-89AA-4D95-8281-3193740E70E5")]
|
[assembly: Guid("67AADCD9-89AA-4D95-8281-3193740E70E5")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("10.0.0.*")]
|
[assembly: AssemblyVersion("0.1.0.*")]
|
|
@ -25,6 +25,6 @@ using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: Guid("699aed1b-015e-4f0d-9c81-d5557b05d260")]
|
[assembly: Guid("699aed1b-015e-4f0d-9c81-d5557b05d260")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("10.0.0.*")]
|
[assembly: AssemblyVersion("0.1.0.*")]
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("NzbDrone.Core")]
|
[assembly: InternalsVisibleTo("NzbDrone.Core")]
|
|
@ -59,7 +59,7 @@ namespace NzbDrone.Core.Test.UpdateTests
|
||||||
Mocker.GetMock<IVerifyUpdates>().Setup(c => c.Verify(It.IsAny<UpdatePackage>(), It.IsAny<string>())).Returns(true);
|
Mocker.GetMock<IVerifyUpdates>().Setup(c => c.Verify(It.IsAny<UpdatePackage>(), It.IsAny<string>())).Returns(true);
|
||||||
|
|
||||||
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetCurrentProcess()).Returns(new ProcessInfo { Id = 12 });
|
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetCurrentProcess()).Returns(new ProcessInfo { Id = 12 });
|
||||||
Mocker.GetMock<IRuntimeInfo>().Setup(c => c.ExecutingApplication).Returns(@"C:\Test\NzbDrone.exe");
|
Mocker.GetMock<IRuntimeInfo>().Setup(c => c.ExecutingApplication).Returns(@"C:\Test\Radarr.exe");
|
||||||
|
|
||||||
Mocker.GetMock<IConfigFileProvider>()
|
Mocker.GetMock<IConfigFileProvider>()
|
||||||
.SetupGet(s => s.UpdateAutomatically)
|
.SetupGet(s => s.UpdateAutomatically)
|
||||||
|
|
21
src/NzbDrone.Core/Datastore/Migration/106_add_tmdb_stuff.cs
Normal file
21
src/NzbDrone.Core/Datastore/Migration/106_add_tmdb_stuff.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using FluentMigrator;
|
||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
{
|
||||||
|
[Migration(106)]
|
||||||
|
public class add_tmdb_stuff : NzbDroneMigrationBase
|
||||||
|
{
|
||||||
|
protected override void MainDbUpgrade()
|
||||||
|
{
|
||||||
|
Alter.Table("Movies")
|
||||||
|
.AddColumn("TmdbId").AsInt32().WithDefaultValue(0);
|
||||||
|
Alter.Table("Movies")
|
||||||
|
.AddColumn("Website").AsString().Nullable();
|
||||||
|
Alter.Table("Movies")
|
||||||
|
.AlterColumn("ImdbId").AsString().Nullable();
|
||||||
|
Alter.Table("Movies")
|
||||||
|
.AddColumn("AlternativeTitles").AsString().Nullable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
src/NzbDrone.Core/Datastore/Migration/107_fix_movie_files.cs
Normal file
14
src/NzbDrone.Core/Datastore/Migration/107_fix_movie_files.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using FluentMigrator;
|
||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
{
|
||||||
|
[Migration(107)]
|
||||||
|
public class fix_movie_files : NzbDroneMigrationBase
|
||||||
|
{
|
||||||
|
protected override void MainDbUpgrade()
|
||||||
|
{
|
||||||
|
Alter.Table("MovieFiles").AlterColumn("Path").AsString().Nullable(); //Should be deleted, but to much work, ¯\_(ツ)_/¯
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using FluentMigrator;
|
||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
{
|
||||||
|
[Migration(108)]
|
||||||
|
public class update_schedule_intervale : NzbDroneMigrationBase
|
||||||
|
{
|
||||||
|
protected override void MainDbUpgrade()
|
||||||
|
{
|
||||||
|
Alter.Table("ScheduledTasks").AlterColumn("Interval").AsDouble();
|
||||||
|
Execute.Sql("UPDATE ScheduledTasks SET Interval=0.25 WHERE TypeName='NzbDrone.Core.Download.CheckForFinishedDownloadCommand'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -140,6 +140,7 @@ namespace NzbDrone.Core.Datastore
|
||||||
RegisterEmbeddedConverter();
|
RegisterEmbeddedConverter();
|
||||||
RegisterProviderSettingConverter();
|
RegisterProviderSettingConverter();
|
||||||
|
|
||||||
|
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(int), new Int32Converter());
|
MapRepository.Instance.RegisterTypeConverter(typeof(int), new Int32Converter());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(double), new DoubleConverter());
|
MapRepository.Instance.RegisterTypeConverter(typeof(double), new DoubleConverter());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(DateTime), new UtcConverter());
|
MapRepository.Instance.RegisterTypeConverter(typeof(DateTime), new UtcConverter());
|
||||||
|
|
|
@ -66,9 +66,9 @@ namespace NzbDrone.Core.DecisionEngine
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var parsedEpisodeInfo = Parser.Parser.ParseTitle(report.Title);
|
var parsedEpisodeInfo = Parser.Parser.ParseMovieTitle(report.Title);
|
||||||
|
|
||||||
if (parsedEpisodeInfo != null && !parsedEpisodeInfo.SeriesTitle.IsNullOrWhiteSpace())
|
if (parsedEpisodeInfo != null && !parsedEpisodeInfo.MovieTitle.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
RemoteMovie remoteEpisode = _parsingService.Map(parsedEpisodeInfo, "", searchCriteria);
|
RemoteMovie remoteEpisode = _parsingService.Map(parsedEpisodeInfo, "", searchCriteria);
|
||||||
remoteEpisode.Release = report;
|
remoteEpisode.Release = report;
|
||||||
|
@ -81,9 +81,18 @@ namespace NzbDrone.Core.DecisionEngine
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
remoteEpisode.DownloadAllowed = true;
|
if (parsedEpisodeInfo.Quality.HardcodedSubs.IsNotNullOrWhiteSpace())
|
||||||
//decision = GetDecisionForReport(remoteEpisode, searchCriteria); TODO: Rewrite this for movies!
|
{
|
||||||
decision = new DownloadDecision(remoteEpisode);
|
remoteEpisode.DownloadAllowed = true;
|
||||||
|
decision = new DownloadDecision(remoteEpisode, new Rejection("Hardcoded subs found: " + parsedEpisodeInfo.Quality.HardcodedSubs));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remoteEpisode.DownloadAllowed = true;
|
||||||
|
decision = GetDecisionForReport(remoteEpisode, searchCriteria);
|
||||||
|
//decision = new DownloadDecision(remoteEpisode);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,6 +205,14 @@ namespace NzbDrone.Core.DecisionEngine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DownloadDecision GetDecisionForReport(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria = null)
|
||||||
|
{
|
||||||
|
var reasons = _specifications.Select(c => EvaluateSpec(c, remoteEpisode, searchCriteria))
|
||||||
|
.Where(c => c != null);
|
||||||
|
|
||||||
|
return new DownloadDecision(remoteEpisode, reasons.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
private DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria = null)
|
private DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria = null)
|
||||||
{
|
{
|
||||||
var reasons = _specifications.Select(c => EvaluateSpec(c, remoteEpisode, searchCriteria))
|
var reasons = _specifications.Select(c => EvaluateSpec(c, remoteEpisode, searchCriteria))
|
||||||
|
@ -226,5 +243,32 @@ namespace NzbDrone.Core.DecisionEngine
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteriaBase = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = spec.IsSatisfiedBy(remoteEpisode, searchCriteriaBase);
|
||||||
|
|
||||||
|
if (!result.Accepted)
|
||||||
|
{
|
||||||
|
return new Rejection(result.Reason, spec.Type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NotImplementedException e)
|
||||||
|
{
|
||||||
|
_logger.Info("Spec " + spec.GetType().Name + " does not care about movies.");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.Data.Add("report", remoteEpisode.Release.ToJson());
|
||||||
|
e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson());
|
||||||
|
_logger.Error(e, "Couldn't evaluate decision on " + remoteEpisode.Release.Title + ", with spec: " + spec.GetType().Name);
|
||||||
|
return new Rejection(string.Format("{0}: {1}", spec.GetType().Name, e.Message));//TODO UPDATE SPECS!
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||||
RejectionType Type { get; }
|
RejectionType Type { get; }
|
||||||
|
|
||||||
Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria);
|
Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria);
|
||||||
|
|
||||||
|
Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,5 +107,58 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
_logger.Debug("Item: {0}, meets size constraints.", subject);
|
_logger.Debug("Item: {0}, meets size constraints.", subject);
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
_logger.Debug("Beginning size check for: {0}", subject);
|
||||||
|
|
||||||
|
var quality = subject.ParsedMovieInfo.Quality.Quality;
|
||||||
|
|
||||||
|
if (subject.Release.Size == 0)
|
||||||
|
{
|
||||||
|
_logger.Debug("Release has unknown size, skipping size check.");
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
var qualityDefinition = _qualityDefinitionService.Get(quality);
|
||||||
|
if (qualityDefinition.MinSize.HasValue)
|
||||||
|
{
|
||||||
|
var minSize = qualityDefinition.MinSize.Value.Megabytes();
|
||||||
|
|
||||||
|
//Multiply maxSize by Series.Runtime
|
||||||
|
minSize = minSize * subject.Movie.Runtime;
|
||||||
|
|
||||||
|
//If the parsed size is smaller than minSize we don't want it
|
||||||
|
if (subject.Release.Size < minSize)
|
||||||
|
{
|
||||||
|
var runtimeMessage = subject.Movie.Title;
|
||||||
|
|
||||||
|
_logger.Debug("Item: {0}, Size: {1} is smaller than minimum allowed size ({2} bytes for {3}), rejecting.", subject, subject.Release.Size, minSize, runtimeMessage);
|
||||||
|
return Decision.Reject("{0} is smaller than minimum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), minSize.SizeSuffix(), runtimeMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!qualityDefinition.MaxSize.HasValue || qualityDefinition.MaxSize.Value == 0)
|
||||||
|
{
|
||||||
|
_logger.Debug("Max size is unlimited - skipping check.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var maxSize = qualityDefinition.MaxSize.Value.Megabytes();
|
||||||
|
|
||||||
|
//Multiply maxSize by Series.Runtime
|
||||||
|
maxSize = maxSize * subject.Movie.Runtime;
|
||||||
|
|
||||||
|
//If the parsed size is greater than maxSize we don't want it
|
||||||
|
if (subject.Release.Size > maxSize)
|
||||||
|
{;
|
||||||
|
|
||||||
|
_logger.Debug("Item: {0}, Size: {1} is greater than maximum allowed size ({2} for {3}), rejecting.", subject, subject.Release.Size, maxSize, subject.Movie.Title);
|
||||||
|
return Decision.Reject("{0} is larger than maximum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), maxSize.SizeSuffix(), subject.Movie.Title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("Item: {0}, meets size constraints.", subject);
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
@ -55,5 +56,10 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ using NLog;
|
||||||
using NzbDrone.Core.Blacklisting;
|
using NzbDrone.Core.Blacklisting;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
|
@ -28,5 +29,13 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
|
@ -34,5 +35,18 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (subject.Movie.MovieFile.Value != null)
|
||||||
|
{
|
||||||
|
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Movie.Profile, subject.Movie.MovieFile.Value.Quality, subject.ParsedMovieInfo.Quality))
|
||||||
|
{
|
||||||
|
return Decision.Reject("Existing file meets cutoff: {0}", subject.Movie.Profile.Value.Cutoff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,5 +36,10 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,5 +29,20 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
var wantedLanguage = subject.Movie.Profile.Value.Language;
|
||||||
|
|
||||||
|
_logger.Debug("Checking if report meets language requirements. {0}", subject.ParsedMovieInfo.Language);
|
||||||
|
|
||||||
|
if (subject.ParsedMovieInfo.Language != wantedLanguage)
|
||||||
|
{
|
||||||
|
_logger.Debug("Report Language: {0} rejected because it is not wanted, wanted {1}", subject.ParsedMovieInfo.Language, wantedLanguage);
|
||||||
|
return Decision.Reject("{0} is wanted, but found {1}", wantedLanguage, subject.ParsedMovieInfo.Language);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,37 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_logger.Debug("Checking if report meets minimum age requirements. {0}", age);
|
||||||
|
|
||||||
|
if (age < minimumAge)
|
||||||
|
{
|
||||||
|
_logger.Debug("Only {0} minutes old, minimum age is {1} minutes", age, minimumAge);
|
||||||
|
return Decision.Reject("Only {0} minutes old, minimum age is {1} minutes", age, minimumAge);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("Release is {0} minutes old, greater than minimum age of {1} minutes", age, minimumAge);
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (subject.Release.DownloadProtocol != Indexers.DownloadProtocol.Usenet)
|
||||||
|
{
|
||||||
|
_logger.Debug("Not checking minimum age requirement for non-usenet report");
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
var age = subject.Release.AgeMinutes;
|
||||||
|
var minimumAge = _configService.MinimumAge;
|
||||||
|
|
||||||
|
if (minimumAge == 0)
|
||||||
|
{
|
||||||
|
_logger.Debug("Minimum age is not set.");
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
_logger.Debug("Checking if report meets minimum age requirements. {0}", age);
|
_logger.Debug("Checking if report meets minimum age requirements. {0}", age);
|
||||||
|
|
||||||
if (age < minimumAge)
|
if (age < minimumAge)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using NLog;
|
using System;
|
||||||
|
using NLog;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
||||||
|
@ -25,5 +26,16 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (subject.Release.Title.ToLower().Contains("sample") && subject.Release.Size < 70.Megabytes())
|
||||||
|
{
|
||||||
|
_logger.Debug("Sample release, rejecting.");
|
||||||
|
return Decision.Reject("Sample");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,5 +38,24 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
var delayProfile = _delayProfileService.BestForTags(subject.Movie.Tags);
|
||||||
|
|
||||||
|
if (subject.Release.DownloadProtocol == DownloadProtocol.Usenet && !delayProfile.EnableUsenet)
|
||||||
|
{
|
||||||
|
_logger.Debug("[{0}] Usenet is not enabled for this series", subject.Release.Title);
|
||||||
|
return Decision.Reject("Usenet is not enabled for this series");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subject.Release.DownloadProtocol == DownloadProtocol.Torrent && !delayProfile.EnableTorrent)
|
||||||
|
{
|
||||||
|
_logger.Debug("[{0}] Torrent is not enabled for this series", subject.Release.Title);
|
||||||
|
return Decision.Reject("Torrent is not enabled for this series");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,5 +26,17 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
_logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedMovieInfo.Quality);
|
||||||
|
if (!subject.Movie.Profile.Value.Items.Exists(v => v.Allowed && v.Quality == subject.ParsedMovieInfo.Quality.Quality))
|
||||||
|
{
|
||||||
|
_logger.Debug("Quality {0} rejected by Series' quality profile", subject.ParsedMovieInfo.Quality);
|
||||||
|
return Decision.Reject("{0} is not wanted in profile", subject.ParsedMovieInfo.Quality.Quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,5 +50,32 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
var queue = _queueService.GetQueue()
|
||||||
|
.Select(q => q.RemoteMovie).ToList();
|
||||||
|
|
||||||
|
var matchingSeries = queue.Where(q => q.Movie.Id == subject.Movie.Id);
|
||||||
|
|
||||||
|
foreach (var remoteEpisode in matchingSeries)
|
||||||
|
{
|
||||||
|
_logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteEpisode.ParsedEpisodeInfo.Quality);
|
||||||
|
|
||||||
|
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Movie.Profile, remoteEpisode.ParsedMovieInfo.Quality, subject.ParsedMovieInfo.Quality))
|
||||||
|
{
|
||||||
|
return Decision.Reject("Quality for release in queue already meets cutoff: {0}", remoteEpisode.ParsedEpisodeInfo.Quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0}", remoteEpisode.ParsedMovieInfo.Quality);
|
||||||
|
|
||||||
|
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Movie.Profile, remoteEpisode.ParsedMovieInfo.Quality, subject.ParsedMovieInfo.Quality))
|
||||||
|
{
|
||||||
|
return Decision.Reject("Quality for release in queue is of equal or higher preference: {0}", remoteEpisode.ParsedMovieInfo.Quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,5 +42,27 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (subject.Release == null || subject.Release.Container.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_dvdContainerTypes.Contains(subject.Release.Container.ToLower()))
|
||||||
|
{
|
||||||
|
_logger.Debug("Release contains raw DVD, rejecting.");
|
||||||
|
return Decision.Reject("Raw DVD release");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_blurayContainerTypes.Contains(subject.Release.Container.ToLower()))
|
||||||
|
{
|
||||||
|
_logger.Debug("Release contains raw Bluray, rejecting.");
|
||||||
|
return Decision.Reject("Raw Bluray release");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,46 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
_logger.Debug("Checking if release meets restrictions: {0}", subject);
|
||||||
|
|
||||||
|
var title = subject.Release.Title;
|
||||||
|
var restrictions = _restrictionService.AllForTags(subject.Movie.Tags);
|
||||||
|
|
||||||
|
var required = restrictions.Where(r => r.Required.IsNotNullOrWhiteSpace());
|
||||||
|
var ignored = restrictions.Where(r => r.Ignored.IsNotNullOrWhiteSpace());
|
||||||
|
|
||||||
|
foreach (var r in required)
|
||||||
|
{
|
||||||
|
var requiredTerms = r.Required.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
|
|
||||||
|
var foundTerms = ContainsAny(requiredTerms, title);
|
||||||
|
if (foundTerms.Empty())
|
||||||
|
{
|
||||||
|
var terms = string.Join(", ", requiredTerms);
|
||||||
|
_logger.Debug("[{0}] does not contain one of the required terms: {1}", title, terms);
|
||||||
|
return Decision.Reject("Does not contain one of the required terms: {0}", terms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var r in ignored)
|
||||||
|
{
|
||||||
|
var ignoredTerms = r.Ignored.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
|
|
||||||
|
var foundTerms = ContainsAny(ignoredTerms, title);
|
||||||
|
if (foundTerms.Any())
|
||||||
|
{
|
||||||
|
var terms = string.Join(", ", foundTerms);
|
||||||
|
_logger.Debug("[{0}] contains these ignored terms: {1}", title, terms);
|
||||||
|
return Decision.Reject("Contains these ignored terms: {0}", terms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("[{0}] No restrictions apply, allowing", subject);
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
private static List<string> ContainsAny(List<string> terms, string title)
|
private static List<string> ContainsAny(List<string> terms, string title)
|
||||||
{
|
{
|
||||||
return terms.Where(t => title.ToLowerInvariant().Contains(t.ToLowerInvariant())).ToList();
|
return terms.Where(t => title.ToLowerInvariant().Contains(t.ToLowerInvariant())).ToList();
|
||||||
|
|
|
@ -38,5 +38,26 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (subject.Release.DownloadProtocol != Indexers.DownloadProtocol.Usenet)
|
||||||
|
{
|
||||||
|
_logger.Debug("Not checking retention requirement for non-usenet report");
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
var age = subject.Release.Age;
|
||||||
|
var retention = _configService.Retention;
|
||||||
|
|
||||||
|
_logger.Debug("Checking if report meets retention requirements. {0}", age);
|
||||||
|
if (retention > 0 && age > retention)
|
||||||
|
{
|
||||||
|
_logger.Debug("Report age: {0} rejected by user's retention limit", age);
|
||||||
|
return Decision.Reject("Older than configured retention");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,71 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||||
|
|
||||||
public RejectionType Type => RejectionType.Temporary;
|
public RejectionType Type => RejectionType.Temporary;
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (searchCriteria != null && searchCriteria.UserInvokedSearch)
|
||||||
|
{
|
||||||
|
_logger.Debug("Ignoring delay for user invoked search");
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
var profile = subject.Movie.Profile.Value;
|
||||||
|
var delayProfile = _delayProfileService.BestForTags(subject.Movie.Tags);
|
||||||
|
var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol);
|
||||||
|
var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol;
|
||||||
|
|
||||||
|
if (delay == 0)
|
||||||
|
{
|
||||||
|
_logger.Debug("Profile does not require a waiting period before download for {0}.", subject.Release.DownloadProtocol);
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
var comparer = new QualityModelComparer(profile);
|
||||||
|
|
||||||
|
if (isPreferredProtocol)
|
||||||
|
{
|
||||||
|
var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, subject.Movie.MovieFile.Value.Quality, subject.ParsedMovieInfo.Quality);
|
||||||
|
|
||||||
|
if (upgradable)
|
||||||
|
{
|
||||||
|
var revisionUpgrade = _qualityUpgradableSpecification.IsRevisionUpgrade(subject.Movie.MovieFile.Value.Quality, subject.ParsedMovieInfo.Quality);
|
||||||
|
|
||||||
|
if (revisionUpgrade)
|
||||||
|
{
|
||||||
|
_logger.Debug("New quality is a better revision for existing quality, skipping delay");
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If quality meets or exceeds the best allowed quality in the profile accept it immediately
|
||||||
|
var bestQualityInProfile = new QualityModel(profile.LastAllowedQuality());
|
||||||
|
var isBestInProfile = comparer.Compare(subject.ParsedEpisodeInfo.Quality, bestQualityInProfile) >= 0;
|
||||||
|
|
||||||
|
if (isBestInProfile && isPreferredProtocol)
|
||||||
|
{
|
||||||
|
_logger.Debug("Quality is highest in profile for preferred protocol, will not delay");
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
var oldest = _pendingReleaseService.OldestPendingRelease(subject.Series.Id, episodeIds);
|
||||||
|
|
||||||
|
if (oldest != null && oldest.Release.AgeMinutes > delay)
|
||||||
|
{
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subject.Release.AgeMinutes < delay)
|
||||||
|
{
|
||||||
|
_logger.Debug("Waiting for better quality release, There is a {0} minute delay on {1}", delay, subject.Release.DownloadProtocol);
|
||||||
|
return Decision.Reject("Waiting for better quality release");
|
||||||
|
}*/ //TODO: Update for movies!
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||||
{
|
{
|
||||||
if (searchCriteria != null && searchCriteria.UserInvokedSearch)
|
if (searchCriteria != null && searchCriteria.UserInvokedSearch)
|
||||||
|
|
|
@ -28,6 +28,56 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||||
|
|
||||||
public RejectionType Type => RejectionType.Permanent;
|
public RejectionType Type => RejectionType.Permanent;
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (searchCriteria != null)
|
||||||
|
{
|
||||||
|
_logger.Debug("Skipping history check during search");
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
var cdhEnabled = _configService.EnableCompletedDownloadHandling;
|
||||||
|
|
||||||
|
_logger.Debug("Performing history status check on report");
|
||||||
|
_logger.Debug("Checking current status of episode [{0}] in history", subject.Movie.Id);
|
||||||
|
var mostRecent = _historyService.MostRecentForMovie(subject.Movie.Id);
|
||||||
|
|
||||||
|
if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed)
|
||||||
|
{
|
||||||
|
var recent = mostRecent.Date.After(DateTime.UtcNow.AddHours(-12));
|
||||||
|
var cutoffUnmet = _qualityUpgradableSpecification.CutoffNotMet(subject.Movie.Profile, mostRecent.Quality, subject.ParsedMovieInfo.Quality);
|
||||||
|
var upgradeable = _qualityUpgradableSpecification.IsUpgradable(subject.Movie.Profile, mostRecent.Quality, subject.ParsedMovieInfo.Quality);
|
||||||
|
|
||||||
|
if (!recent && cdhEnabled)
|
||||||
|
{
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cutoffUnmet)
|
||||||
|
{
|
||||||
|
if (recent)
|
||||||
|
{
|
||||||
|
return Decision.Reject("Recent grab event in history already meets cutoff: {0}", mostRecent.Quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Reject("CDH is disabled and grab event in history already meets cutoff: {0}", mostRecent.Quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!upgradeable)
|
||||||
|
{
|
||||||
|
if (recent)
|
||||||
|
{
|
||||||
|
return Decision.Reject("Recent grab event in history is of equal or higher quality: {0}", mostRecent.Quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Reject("CDH is disabled and grab event in history is of equal or higher quality: {0}", mostRecent.Quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||||
{
|
{
|
||||||
if (searchCriteria != null)
|
if (searchCriteria != null)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
|
@ -16,6 +17,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||||
|
|
||||||
public RejectionType Type => RejectionType.Permanent;
|
public RejectionType Type => RejectionType.Permanent;
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||||
{
|
{
|
||||||
if (searchCriteria != null)
|
if (searchCriteria != null)
|
||||||
|
|
|
@ -47,6 +47,39 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (searchCriteria != null)
|
||||||
|
{
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subject.Movie.MovieFile.Value == null)
|
||||||
|
{
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = subject.Movie.MovieFile.Value;
|
||||||
|
|
||||||
|
if (_qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedMovieInfo.Quality))
|
||||||
|
{
|
||||||
|
if (file.DateAdded < DateTime.Today.AddDays(-7))
|
||||||
|
{
|
||||||
|
_logger.Debug("Proper for old file, rejecting: {0}", subject);
|
||||||
|
return Decision.Reject("Proper for old file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_configService.AutoDownloadPropers)
|
||||||
|
{
|
||||||
|
_logger.Debug("Auto downloading of propers is disabled");
|
||||||
|
return Decision.Reject("Proper downloading is disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
@ -27,5 +28,10 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
_logger.Debug("Episode file on disk contains more episodes than this release contains");
|
_logger.Debug("Episode file on disk contains more episodes than this release contains");
|
||||||
return Decision.Reject("Episode file on disk contains more episodes than this release contains");
|
return Decision.Reject("Episode file on disk contains more episodes than this release contains");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
@ -39,5 +40,10 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Linq;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
@ -17,6 +18,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||||
|
|
||||||
public RejectionType Type => RejectionType.Permanent;
|
public RejectionType Type => RejectionType.Permanent;
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||||
{
|
{
|
||||||
if (searchCriteria == null)
|
if (searchCriteria == null)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
@ -15,6 +16,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||||
|
|
||||||
public RejectionType Type => RejectionType.Permanent;
|
public RejectionType Type => RejectionType.Permanent;
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||||
{
|
{
|
||||||
if (searchCriteria == null)
|
if (searchCriteria == null)
|
||||||
|
|
|
@ -32,5 +32,23 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (searchCriteria == null)
|
||||||
|
{
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("Checking if movie matches searched movie");
|
||||||
|
|
||||||
|
if (remoteEpisode.Movie.Id != searchCriteria.Movie.Id)
|
||||||
|
{
|
||||||
|
_logger.Debug("Series {0} does not match {1}", remoteEpisode.Movie, searchCriteria.Series);
|
||||||
|
return Decision.Reject("Wrong movie");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
|
@ -16,6 +17,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||||
|
|
||||||
public RejectionType Type => RejectionType.Permanent;
|
public RejectionType Type => RejectionType.Permanent;
|
||||||
|
|
||||||
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||||
{
|
{
|
||||||
if (searchCriteria == null)
|
if (searchCriteria == null)
|
||||||
|
|
|
@ -33,5 +33,23 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||||
|
|
||||||
return Decision.Accept();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,6 +30,25 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (subject.Movie.MovieFile.Value == null)
|
||||||
|
{
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = subject.Movie.MovieFile.Value;
|
||||||
|
_logger.Debug("Comparing file quality with report. Existing file is {0}", file.Quality);
|
||||||
|
|
||||||
|
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Movie.Profile, file.Quality, subject.ParsedMovieInfo.Quality))
|
||||||
|
{
|
||||||
|
return Decision.Reject("Quality for existing file on disk is of equal or higher preference: {0}", file.Quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
ScanGracePeriod = TimeSpan.FromSeconds(30);
|
ScanGracePeriod = TimeSpan.FromSeconds(30);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContent)
|
protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContents)
|
||||||
{
|
{
|
||||||
var title = remoteEpisode.Release.Title;
|
var title = remoteEpisode.Release.Title;
|
||||||
|
|
||||||
|
@ -42,7 +42,25 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
|
|
||||||
using (var stream = _diskProvider.OpenWriteStream(filepath))
|
using (var stream = _diskProvider.OpenWriteStream(filepath))
|
||||||
{
|
{
|
||||||
stream.Write(fileContent, 0, fileContent.Length);
|
stream.Write(fileContents, 0, fileContents.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("NZB Download succeeded, saved to: {0}", filepath);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string AddFromNzbFile(RemoteMovie remoteMovie, string filename, byte[] fileContents)
|
||||||
|
{
|
||||||
|
var title = remoteMovie.Release.Title;
|
||||||
|
|
||||||
|
title = FileNameBuilder.CleanFileName(title);
|
||||||
|
|
||||||
|
var filepath = Path.Combine(Settings.NzbFolder, title + ".nzb");
|
||||||
|
|
||||||
|
using (var stream = _diskProvider.OpenWriteStream(filepath))
|
||||||
|
{
|
||||||
|
stream.Write(fileContents, 0, fileContents.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Debug("NZB Download succeeded, saved to: {0}", filepath);
|
_logger.Debug("NZB Download succeeded, saved to: {0}", filepath);
|
||||||
|
|
|
@ -31,6 +31,50 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override string AddFromMagnetLink(RemoteMovie remoteEpisode, string hash, string magnetLink)
|
||||||
|
{
|
||||||
|
var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings);
|
||||||
|
|
||||||
|
if (!Settings.TvCategory.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
_proxy.SetTorrentConfiguration(actualHash, "remove_at_ratio", false, Settings);
|
||||||
|
|
||||||
|
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||||
|
|
||||||
|
if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First ||
|
||||||
|
!isRecentEpisode && Settings.OlderTvPriority == (int)DelugePriority.First)
|
||||||
|
{
|
||||||
|
_proxy.MoveTorrentToTopInQueue(actualHash, Settings);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return actualHash.ToUpper();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string AddFromTorrentFile(RemoteMovie remoteEpisode, string hash, string filename, byte[] fileContent)
|
||||||
|
{
|
||||||
|
var actualHash = _proxy.AddTorrentFromFile(filename, fileContent, Settings);
|
||||||
|
|
||||||
|
if (!Settings.TvCategory.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
_proxy.SetTorrentConfiguration(actualHash, "remove_at_ratio", false, Settings);
|
||||||
|
|
||||||
|
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||||
|
|
||||||
|
if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First ||
|
||||||
|
!isRecentEpisode && Settings.OlderTvPriority == (int)DelugePriority.First)
|
||||||
|
{
|
||||||
|
_proxy.MoveTorrentToTopInQueue(actualHash, Settings);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return actualHash.ToUpper();
|
||||||
|
}
|
||||||
|
|
||||||
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
||||||
{
|
{
|
||||||
var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings);
|
var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings);
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||||
Host = "localhost";
|
Host = "localhost";
|
||||||
Port = 8112;
|
Port = 8112;
|
||||||
Password = "deluge";
|
Password = "deluge";
|
||||||
TvCategory = "tv-sonarr";
|
TvCategory = "movie-radarr";
|
||||||
}
|
}
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
|
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
|
||||||
|
@ -40,7 +40,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||||
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
|
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
|
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||||
public string TvCategory { get; set; }
|
public string TvCategory { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
|
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
|
||||||
|
|
|
@ -29,11 +29,25 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContent)
|
protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContents)
|
||||||
{
|
{
|
||||||
var priority = remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority;
|
var priority = remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority;
|
||||||
|
|
||||||
var response = _proxy.DownloadNzb(fileContent, filename, priority, Settings);
|
var response = _proxy.DownloadNzb(fileContents, filename, priority, Settings);
|
||||||
|
|
||||||
|
if (response == null)
|
||||||
|
{
|
||||||
|
throw new DownloadClientException("Failed to add nzb {0}", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string AddFromNzbFile(RemoteMovie remoteMovie, string filename, byte[] fileContents)
|
||||||
|
{
|
||||||
|
var priority = Settings.RecentTvPriority;
|
||||||
|
|
||||||
|
var response = _proxy.DownloadNzb(fileContents, filename, priority, Settings);
|
||||||
|
|
||||||
if (response == null)
|
if (response == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,12 +29,12 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContent)
|
protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContents)
|
||||||
{
|
{
|
||||||
var category = Settings.TvCategory;
|
var category = Settings.TvCategory;
|
||||||
var priority = remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority;
|
var priority = remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority;
|
||||||
|
|
||||||
var response = _proxy.DownloadNzb(fileContent, filename, category, priority, Settings);
|
var response = _proxy.DownloadNzb(fileContents, filename, category, priority, Settings);
|
||||||
|
|
||||||
if (response == null)
|
if (response == null)
|
||||||
{
|
{
|
||||||
|
@ -46,7 +46,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||||
|
|
||||||
protected override string AddFromNzbFile(RemoteMovie remoteMovie, string filename, byte[] fileContents)
|
protected override string AddFromNzbFile(RemoteMovie remoteMovie, string filename, byte[] fileContents)
|
||||||
{
|
{
|
||||||
var category = Settings.TvCategory; //could update this to MovieCategory
|
var category = Settings.TvCategory; // TODO: Update this to MovieCategory?
|
||||||
var priority = Settings.RecentTvPriority;
|
var priority = Settings.RecentTvPriority;
|
||||||
|
|
||||||
var response = _proxy.DownloadNzb(fileContents, filename, category, priority, Settings);
|
var response = _proxy.DownloadNzb(fileContents, filename, category, priority, Settings);
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||||
{
|
{
|
||||||
Host = "localhost";
|
Host = "localhost";
|
||||||
Port = 9091;
|
Port = 9091;
|
||||||
TvCategory = "tv-sonarr";
|
TvCategory = "movie-radarr";
|
||||||
}
|
}
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
|
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
|
||||||
|
@ -37,9 +37,10 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||||
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
|
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
|
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||||
public string TvCategory { get; set; }
|
public string TvCategory { get; set; }
|
||||||
|
|
||||||
|
//Todo: update this shit.
|
||||||
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
|
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
|
||||||
public int RecentTvPriority { get; set; }
|
public int RecentTvPriority { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,27 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||||
// patch can be a number (releases) or 'x' (git)
|
// patch can be a number (releases) or 'x' (git)
|
||||||
private static readonly Regex VersionRegex = new Regex(@"(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+|x)(?<candidate>.*)", RegexOptions.Compiled);
|
private static readonly Regex VersionRegex = new Regex(@"(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+|x)(?<candidate>.*)", RegexOptions.Compiled);
|
||||||
|
|
||||||
protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContent)
|
protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContents)
|
||||||
{
|
{
|
||||||
var category = Settings.TvCategory;
|
var category = Settings.TvCategory;
|
||||||
var priority = remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority;
|
var priority = remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority;
|
||||||
|
|
||||||
var response = _proxy.DownloadNzb(fileContent, filename, category, priority, Settings);
|
var response = _proxy.DownloadNzb(fileContents, filename, category, priority, Settings);
|
||||||
|
|
||||||
|
if (response != null && response.Ids.Any())
|
||||||
|
{
|
||||||
|
return response.Ids.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string AddFromNzbFile(RemoteMovie remoteMovie, string filename, byte[] fileContents)
|
||||||
|
{
|
||||||
|
var category = Settings.TvCategory;
|
||||||
|
var priority = Settings.RecentTvPriority;
|
||||||
|
|
||||||
|
var response = _proxy.DownloadNzb(fileContents, filename, category, priority, Settings);
|
||||||
|
|
||||||
if (response != null && response.Ids.Any())
|
if (response != null && response.Ids.Any())
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.EnsureThat;
|
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Common.Instrumentation.Extensions;
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
|
|
|
@ -57,12 +57,17 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var parsedEpisodeInfo = Parser.Parser.ParseTitle(trackedDownload.DownloadItem.Title);
|
var parsedEpisodeInfo = Parser.Parser.ParseTitle(trackedDownload.DownloadItem.Title);
|
||||||
|
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(trackedDownload.DownloadItem.Title);
|
||||||
var historyItems = _historyService.FindByDownloadId(downloadItem.DownloadId);
|
var historyItems = _historyService.FindByDownloadId(downloadItem.DownloadId);
|
||||||
|
|
||||||
|
if (parsedMovieInfo != null)
|
||||||
|
{
|
||||||
|
trackedDownload.RemoteMovie = _parsingService.Map(parsedMovieInfo, "", null);
|
||||||
|
}
|
||||||
|
|
||||||
if (parsedEpisodeInfo != null)
|
if (parsedEpisodeInfo != null)
|
||||||
{
|
{
|
||||||
trackedDownload.RemoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
trackedDownload.RemoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
||||||
trackedDownload.RemoteMovie = _parsingService.Map(parsedEpisodeInfo, "", null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (historyItems.Any())
|
if (historyItems.Any())
|
||||||
|
|
|
@ -30,12 +30,9 @@ namespace NzbDrone.Core.Download
|
||||||
|
|
||||||
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
|
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
|
||||||
|
|
||||||
protected abstract string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContent);
|
protected abstract string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContents);
|
||||||
|
|
||||||
protected virtual string AddFromNzbFile(RemoteMovie remoteMovie, string filename, byte[] fileContents)
|
protected abstract string AddFromNzbFile(RemoteMovie remoteMovie, string filename, byte[] fileContents);
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string Download(RemoteEpisode remoteEpisode)
|
public override string Download(RemoteEpisode remoteEpisode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace NzbDrone.Core.History
|
||||||
public class HistoryService : IHistoryService,
|
public class HistoryService : IHistoryService,
|
||||||
IHandle<EpisodeGrabbedEvent>,
|
IHandle<EpisodeGrabbedEvent>,
|
||||||
IHandle<MovieGrabbedEvent>,
|
IHandle<MovieGrabbedEvent>,
|
||||||
|
IHandle<MovieImportedEvent>,
|
||||||
IHandle<EpisodeImportedEvent>,
|
IHandle<EpisodeImportedEvent>,
|
||||||
IHandle<DownloadFailedEvent>,
|
IHandle<DownloadFailedEvent>,
|
||||||
IHandle<EpisodeFileDeletedEvent>,
|
IHandle<EpisodeFileDeletedEvent>,
|
||||||
|
@ -186,7 +187,7 @@ namespace NzbDrone.Core.History
|
||||||
{
|
{
|
||||||
EventType = HistoryEventType.Grabbed,
|
EventType = HistoryEventType.Grabbed,
|
||||||
Date = DateTime.UtcNow,
|
Date = DateTime.UtcNow,
|
||||||
Quality = message.Movie.ParsedEpisodeInfo.Quality,
|
Quality = message.Movie.ParsedMovieInfo.Quality,
|
||||||
SourceTitle = message.Movie.Release.Title,
|
SourceTitle = message.Movie.Release.Title,
|
||||||
SeriesId = 0,
|
SeriesId = 0,
|
||||||
EpisodeId = 0,
|
EpisodeId = 0,
|
||||||
|
@ -196,7 +197,7 @@ namespace NzbDrone.Core.History
|
||||||
|
|
||||||
history.Data.Add("Indexer", message.Movie.Release.Indexer);
|
history.Data.Add("Indexer", message.Movie.Release.Indexer);
|
||||||
history.Data.Add("NzbInfoUrl", message.Movie.Release.InfoUrl);
|
history.Data.Add("NzbInfoUrl", message.Movie.Release.InfoUrl);
|
||||||
history.Data.Add("ReleaseGroup", message.Movie.ParsedEpisodeInfo.ReleaseGroup);
|
history.Data.Add("ReleaseGroup", message.Movie.ParsedMovieInfo.ReleaseGroup);
|
||||||
history.Data.Add("Age", message.Movie.Release.Age.ToString());
|
history.Data.Add("Age", message.Movie.Release.Age.ToString());
|
||||||
history.Data.Add("AgeHours", message.Movie.Release.AgeHours.ToString());
|
history.Data.Add("AgeHours", message.Movie.Release.AgeHours.ToString());
|
||||||
history.Data.Add("AgeMinutes", message.Movie.Release.AgeMinutes.ToString());
|
history.Data.Add("AgeMinutes", message.Movie.Release.AgeMinutes.ToString());
|
||||||
|
@ -209,9 +210,9 @@ namespace NzbDrone.Core.History
|
||||||
history.Data.Add("TvRageId", message.Movie.Release.TvRageId.ToString());
|
history.Data.Add("TvRageId", message.Movie.Release.TvRageId.ToString());
|
||||||
history.Data.Add("Protocol", ((int)message.Movie.Release.DownloadProtocol).ToString());
|
history.Data.Add("Protocol", ((int)message.Movie.Release.DownloadProtocol).ToString());
|
||||||
|
|
||||||
if (!message.Movie.ParsedEpisodeInfo.ReleaseHash.IsNullOrWhiteSpace())
|
if (!message.Movie.ParsedMovieInfo.ReleaseHash.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
history.Data.Add("ReleaseHash", message.Movie.ParsedEpisodeInfo.ReleaseHash);
|
history.Data.Add("ReleaseHash", message.Movie.ParsedMovieInfo.ReleaseHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
var torrentRelease = message.Movie.Release as TorrentInfo;
|
var torrentRelease = message.Movie.Release as TorrentInfo;
|
||||||
|
@ -264,6 +265,45 @@ namespace NzbDrone.Core.History
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Handle(MovieImportedEvent message)
|
||||||
|
{
|
||||||
|
if (!message.NewDownload)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var downloadId = message.DownloadId;
|
||||||
|
|
||||||
|
if (downloadId.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
//downloadId = FindDownloadId(message); For now fuck off.
|
||||||
|
}
|
||||||
|
|
||||||
|
var movie = message.MovieInfo.Movie;
|
||||||
|
var history = new History
|
||||||
|
{
|
||||||
|
EventType = HistoryEventType.DownloadFolderImported,
|
||||||
|
Date = DateTime.UtcNow,
|
||||||
|
Quality = message.MovieInfo.Quality,
|
||||||
|
SourceTitle = movie.Title,
|
||||||
|
SeriesId = 0,
|
||||||
|
EpisodeId = 0,
|
||||||
|
DownloadId = downloadId,
|
||||||
|
MovieId = movie.Id,
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//Won't have a value since we publish this event before saving to DB.
|
||||||
|
//history.Data.Add("FileId", message.ImportedEpisode.Id.ToString());
|
||||||
|
history.Data.Add("DroppedPath", message.MovieInfo.Path);
|
||||||
|
history.Data.Add("ImportedPath", Path.Combine(movie.Path, message.ImportedMovie.RelativePath));
|
||||||
|
history.Data.Add("DownloadClient", message.DownloadClient);
|
||||||
|
|
||||||
|
_historyRepository.Insert(history);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void Handle(DownloadFailedEvent message)
|
public void Handle(DownloadFailedEvent message)
|
||||||
{
|
{
|
||||||
foreach (var episodeId in message.EpisodeIds)
|
foreach (var episodeId in message.EpisodeIds)
|
||||||
|
|
|
@ -30,6 +30,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||||
public NewznabCapabilities GetCapabilities(NewznabSettings indexerSettings)
|
public NewznabCapabilities GetCapabilities(NewznabSettings indexerSettings)
|
||||||
{
|
{
|
||||||
var key = indexerSettings.ToJson();
|
var key = indexerSettings.ToJson();
|
||||||
|
_capabilitiesCache.Clear();
|
||||||
var capabilities = _capabilitiesCache.Get(key, () => FetchCapabilities(indexerSettings), TimeSpan.FromDays(7));
|
var capabilities = _capabilitiesCache.Get(key, () => FetchCapabilities(indexerSettings), TimeSpan.FromDays(7));
|
||||||
|
|
||||||
return capabilities;
|
return capabilities;
|
||||||
|
@ -98,6 +99,16 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||||
capabilities.SupportedTvSearchParameters = xmlTvSearch.Attribute("supportedParams").Value.Split(',');
|
capabilities.SupportedTvSearchParameters = xmlTvSearch.Attribute("supportedParams").Value.Split(',');
|
||||||
capabilities.SupportsAggregateIdSearch = true;
|
capabilities.SupportsAggregateIdSearch = true;
|
||||||
}
|
}
|
||||||
|
var xmlMovieSearch = xmlSearching.Element("movie-search");
|
||||||
|
if (xmlMovieSearch == null || xmlMovieSearch.Attribute("available").Value != "yes")
|
||||||
|
{
|
||||||
|
capabilities.SupportedMovieSearchParamters = null;
|
||||||
|
}
|
||||||
|
else if (xmlMovieSearch.Attribute("supportedParams") != null)
|
||||||
|
{
|
||||||
|
capabilities.SupportedMovieSearchParamters = xmlMovieSearch.Attribute("supportedParams").Value.Split(',');
|
||||||
|
capabilities.SupportsAggregateIdSearch = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var xmlCategories = xmlRoot.Element("categories");
|
var xmlCategories = xmlRoot.Element("categories");
|
||||||
|
|
|
@ -92,9 +92,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings);
|
var capabilities = _capabilitiesProvider.GetCapabilities(Settings);
|
||||||
|
|
||||||
return capabilities.SupportedMovieSearchParamters != null &&
|
return capabilities.SupportedMovieSearchParamters != null &&
|
||||||
capabilities.SupportedMovieSearchParamters.Contains("imdb") &&
|
capabilities.SupportedMovieSearchParamters.Contains("imdb");
|
||||||
capabilities.SupportedMovieSearchParamters.Contains("imdbtitle") &&
|
|
||||||
capabilities.SupportedMovieSearchParamters.Contains("imdbyear");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +129,12 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||||
pageableRequests.Add(GetPagedRequests(MaxPages, Settings.Categories, "movie",
|
pageableRequests.Add(GetPagedRequests(MaxPages, Settings.Categories, "movie",
|
||||||
string.Format("&imdbid={0}", searchCriteria.Movie.ImdbId.Substring(2)))); //strip off the "tt" - VERY HACKY
|
string.Format("&imdbid={0}", searchCriteria.Movie.ImdbId.Substring(2)))); //strip off the "tt" - VERY HACKY
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Let's try anyways with q parameter, worst case nothing found.
|
||||||
|
pageableRequests.Add(GetPagedRequests(MaxPages, Settings.Categories, "search",
|
||||||
|
string.Format("&q={0}", searchCriteria.Movie.Title)));
|
||||||
|
}
|
||||||
|
|
||||||
return pageableRequests;
|
return pageableRequests;
|
||||||
}
|
}
|
||||||
|
|
64
src/NzbDrone.Core/Indexers/TorrentPotato/TorrentPotato.cs
Normal file
64
src/NzbDrone.Core/Indexers/TorrentPotato/TorrentPotato.cs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Exceptions;
|
||||||
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Http.CloudFlare;
|
||||||
|
using NzbDrone.Core.Parser;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||||
|
{
|
||||||
|
public class TorrentPotato : HttpIndexerBase<TorrentPotatoSettings>
|
||||||
|
{
|
||||||
|
public override string Name => "TorrentPotato";
|
||||||
|
|
||||||
|
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||||
|
public override TimeSpan RateLimit => TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
|
public TorrentPotato(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
|
||||||
|
: base(httpClient, indexerStatusService, configService, parsingService, logger)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return GetDefinition("Jackett", new TorrentPotatoSettings { BaseUrl = "http://localhost:9117/potato/YOURINDEXER"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IndexerDefinition GetDefinition(string name, TorrentPotatoSettings settings)
|
||||||
|
{
|
||||||
|
return new IndexerDefinition
|
||||||
|
{
|
||||||
|
EnableRss = false,
|
||||||
|
EnableSearch = false,
|
||||||
|
Name = name,
|
||||||
|
Implementation = GetType().Name,
|
||||||
|
Settings = settings,
|
||||||
|
Protocol = DownloadProtocol.Torrent,
|
||||||
|
SupportsRss = SupportsRss,
|
||||||
|
SupportsSearch = SupportsSearch
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||||
|
{
|
||||||
|
return new TorrentPotatoRequestGenerator() { Settings = Settings };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IParseIndexerResponse GetParser()
|
||||||
|
{
|
||||||
|
return new TorrentPotatoParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Indexers.Exceptions;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||||
|
{
|
||||||
|
public class TorrentPotatoParser : IParseIndexerResponse
|
||||||
|
{
|
||||||
|
private static readonly Regex RegexGuid = new Regex(@"^magnet:\?xt=urn:btih:([a-f0-9]+)", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||||
|
{
|
||||||
|
var results = new List<ReleaseInfo>();
|
||||||
|
|
||||||
|
switch (indexerResponse.HttpResponse.StatusCode)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
throw new IndexerException(indexerResponse, "Indexer API call returned an unexpected StatusCode [{0}]", indexerResponse.HttpResponse.StatusCode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonResponse = new HttpResponse<TorrentPotatoResponse>(indexerResponse.HttpResponse);
|
||||||
|
|
||||||
|
foreach (var torrent in jsonResponse.Resource.results)
|
||||||
|
{
|
||||||
|
var torrentInfo = new TorrentInfo();
|
||||||
|
|
||||||
|
torrentInfo.Guid = GetGuid(torrent);
|
||||||
|
torrentInfo.Title = torrent.release_name;
|
||||||
|
torrentInfo.Size = (long)torrent.size*1000*1000;
|
||||||
|
torrentInfo.DownloadUrl = torrent.download_url;
|
||||||
|
torrentInfo.InfoUrl = torrent.details_url;
|
||||||
|
torrentInfo.PublishDate = new System.DateTime();
|
||||||
|
torrentInfo.Seeders = torrent.seeders;
|
||||||
|
torrentInfo.Peers = torrent.leechers + torrent.seeders;
|
||||||
|
torrentInfo.Freeleech = torrent.freeleech;
|
||||||
|
|
||||||
|
results.Add(torrentInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetGuid(Result torrent)
|
||||||
|
{
|
||||||
|
var match = RegexGuid.Match(torrent.download_url);
|
||||||
|
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
return string.Format("potato-{0}", match.Groups[1].Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string.Format("potato-{0}", torrent.download_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||||
|
{
|
||||||
|
public class TorrentPotatoRequestGenerator : IIndexerRequestGenerator
|
||||||
|
{
|
||||||
|
|
||||||
|
public TorrentPotatoSettings Settings { get; set; }
|
||||||
|
|
||||||
|
public TorrentPotatoRequestGenerator()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IndexerPageableRequestChain GetRecentRequests()
|
||||||
|
{
|
||||||
|
var pageableRequests = new IndexerPageableRequestChain();
|
||||||
|
|
||||||
|
pageableRequests.Add(GetPagedRequests("list", null, null));
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IndexerPageableRequestChain GetSearchRequests(SingleEpisodeSearchCriteria searchCriteria)
|
||||||
|
{
|
||||||
|
var pageableRequests = new IndexerPageableRequestChain();
|
||||||
|
|
||||||
|
pageableRequests.Add(GetPagedRequests("search", searchCriteria.Series.TvdbId, "S{0:00}E{1:00}", searchCriteria.SeasonNumber, searchCriteria.EpisodeNumber));
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IndexerPageableRequestChain GetSearchRequests(SeasonSearchCriteria searchCriteria)
|
||||||
|
{
|
||||||
|
var pageableRequests = new IndexerPageableRequestChain();
|
||||||
|
|
||||||
|
pageableRequests.Add(GetPagedRequests("search", searchCriteria.Series.TvdbId, "S{0:00}", searchCriteria.SeasonNumber));
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IndexerPageableRequestChain GetSearchRequests(DailyEpisodeSearchCriteria searchCriteria)
|
||||||
|
{
|
||||||
|
var pageableRequests = new IndexerPageableRequestChain();
|
||||||
|
|
||||||
|
pageableRequests.Add(GetPagedRequests("search", searchCriteria.Series.TvdbId, "\"{0:yyyy MM dd}\"", searchCriteria.AirDate));
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IndexerPageableRequestChain GetSearchRequests(AnimeEpisodeSearchCriteria searchCriteria)
|
||||||
|
{
|
||||||
|
return new IndexerPageableRequestChain();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IndexerPageableRequestChain GetSearchRequests(SpecialEpisodeSearchCriteria searchCriteria)
|
||||||
|
{
|
||||||
|
var pageableRequests = new IndexerPageableRequestChain();
|
||||||
|
|
||||||
|
foreach (var queryTitle in searchCriteria.EpisodeQueryTitles)
|
||||||
|
{
|
||||||
|
var query = queryTitle.Replace('+', ' ');
|
||||||
|
query = System.Web.HttpUtility.UrlEncode(query);
|
||||||
|
|
||||||
|
pageableRequests.Add(GetPagedRequests("search", searchCriteria.Series.TvdbId, query));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IndexerRequest> GetPagedRequests(string mode, int? tvdbId, string query, params object[] args)
|
||||||
|
{
|
||||||
|
var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl)
|
||||||
|
.Accept(HttpAccept.Json);
|
||||||
|
|
||||||
|
requestBuilder.AddQueryParam("passkey", Settings.Passkey);
|
||||||
|
requestBuilder.AddQueryParam("user", Settings.User);
|
||||||
|
requestBuilder.AddQueryParam("imdbid", "tt0076759"); //For now just search for Star Wars.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
yield return new IndexerRequest(requestBuilder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IndexerRequest> GetMovieRequest(MovieSearchCriteria searchCriteria)
|
||||||
|
{
|
||||||
|
var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl)
|
||||||
|
.Accept(HttpAccept.Json);
|
||||||
|
|
||||||
|
requestBuilder.AddQueryParam("passkey", Settings.Passkey);
|
||||||
|
requestBuilder.AddQueryParam("user", Settings.User);
|
||||||
|
|
||||||
|
if (searchCriteria.Movie.ImdbId.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
requestBuilder.AddQueryParam("imdbid", searchCriteria.Movie.ImdbId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestBuilder.AddQueryParam("search", searchCriteria.Movie.Title);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return new IndexerRequest(requestBuilder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
var pageableRequests = new IndexerPageableRequestChain();
|
||||||
|
|
||||||
|
pageableRequests.Add(GetMovieRequest(searchCriteria));
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||||
|
{
|
||||||
|
|
||||||
|
public class TorrentPotatoResponse
|
||||||
|
{
|
||||||
|
public Result[] results { get; set; }
|
||||||
|
public int total_results { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Result
|
||||||
|
{
|
||||||
|
public string release_name { get; set; }
|
||||||
|
public string torrent_id { get; set; }
|
||||||
|
public string details_url { get; set; }
|
||||||
|
public string download_url { get; set; }
|
||||||
|
public bool freeleech { get; set; }
|
||||||
|
public string type { get; set; }
|
||||||
|
public int size { get; set; }
|
||||||
|
public int leechers { get; set; }
|
||||||
|
public int seeders { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||||
|
{
|
||||||
|
public class TorrentPotatoSettingsValidator : AbstractValidator<TorrentPotatoSettings>
|
||||||
|
{
|
||||||
|
public TorrentPotatoSettingsValidator()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.BaseUrl).ValidRootUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TorrentPotatoSettings : IProviderConfig
|
||||||
|
{
|
||||||
|
private static readonly TorrentPotatoSettingsValidator Validator = new TorrentPotatoSettingsValidator();
|
||||||
|
|
||||||
|
public TorrentPotatoSettings()
|
||||||
|
{
|
||||||
|
BaseUrl = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "API URL", HelpText = "URL to TorrentPotato api.")]
|
||||||
|
public string BaseUrl { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Username", HelpText = "The username you use at your indexer.")]
|
||||||
|
public string User { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Label = "Passkey", HelpText = "The password you use at your Indexer,")]
|
||||||
|
public string Passkey { get; set; }
|
||||||
|
public NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ namespace NzbDrone.Core.Jobs
|
||||||
public class ScheduledTask : ModelBase
|
public class ScheduledTask : ModelBase
|
||||||
{
|
{
|
||||||
public string TypeName { get; set; }
|
public string TypeName { get; set; }
|
||||||
public int Interval { get; set; }
|
public double Interval { get; set; }
|
||||||
public DateTime LastExecution { get; set; }
|
public DateTime LastExecution { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -61,7 +61,7 @@ namespace NzbDrone.Core.Jobs
|
||||||
{
|
{
|
||||||
var defaultTasks = new[]
|
var defaultTasks = new[]
|
||||||
{
|
{
|
||||||
new ScheduledTask{ Interval = 1, TypeName = typeof(CheckForFinishedDownloadCommand).FullName},
|
new ScheduledTask{ Interval = 0.25f, TypeName = typeof(CheckForFinishedDownloadCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 5, TypeName = typeof(MessagingCleanupCommand).FullName},
|
new ScheduledTask{ Interval = 5, TypeName = typeof(MessagingCleanupCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 6*60, TypeName = typeof(ApplicationUpdateCommand).FullName},
|
new ScheduledTask{ Interval = 6*60, TypeName = typeof(ApplicationUpdateCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName},
|
new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName},
|
||||||
|
|
|
@ -7,5 +7,6 @@ namespace NzbDrone.Core.MetadataSource
|
||||||
public interface IProvideMovieInfo
|
public interface IProvideMovieInfo
|
||||||
{
|
{
|
||||||
Movie GetMovieInfo(string ImdbId);
|
Movie GetMovieInfo(string ImdbId);
|
||||||
|
Movie GetMovieInfo(int TmdbId);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||||
|
{
|
||||||
|
|
||||||
|
public class ConfigResource
|
||||||
|
{
|
||||||
|
public Images images { get; set; }
|
||||||
|
public string[] change_keys { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Images
|
||||||
|
{
|
||||||
|
public string base_url { get; set; }
|
||||||
|
public string secure_base_url { get; set; }
|
||||||
|
public string[] backdrop_sizes { get; set; }
|
||||||
|
public string[] logo_sizes { get; set; }
|
||||||
|
public string[] poster_sizes { get; set; }
|
||||||
|
public string[] profile_sizes { get; set; }
|
||||||
|
public string[] still_sizes { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||||
|
{
|
||||||
|
public class ImdbResource
|
||||||
|
{
|
||||||
|
public int v { get; set; }
|
||||||
|
public string q { get; set; }
|
||||||
|
public MovieResource[] d { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MovieResource
|
||||||
|
{
|
||||||
|
public string l { get; set; }
|
||||||
|
public string id { get; set; }
|
||||||
|
public string s { get; set; }
|
||||||
|
public int y { get; set; }
|
||||||
|
public string q { get; set; }
|
||||||
|
public object[] i { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||||
|
{
|
||||||
|
|
||||||
|
public class MovieSearchRoot
|
||||||
|
{
|
||||||
|
public int page { get; set; }
|
||||||
|
public MovieResult[] results { get; set; }
|
||||||
|
public int total_results { get; set; }
|
||||||
|
public int total_pages { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MovieResult
|
||||||
|
{
|
||||||
|
public string poster_path { get; set; }
|
||||||
|
public bool adult { get; set; }
|
||||||
|
public string overview { get; set; }
|
||||||
|
public string release_date { get; set; }
|
||||||
|
public int?[] genre_ids { get; set; }
|
||||||
|
public int id { get; set; }
|
||||||
|
public string original_title { get; set; }
|
||||||
|
public string original_language { get; set; }
|
||||||
|
public string title { get; set; }
|
||||||
|
public string backdrop_path { get; set; }
|
||||||
|
public float popularity { get; set; }
|
||||||
|
public int vote_count { get; set; }
|
||||||
|
public bool video { get; set; }
|
||||||
|
public float vote_average { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class MovieResourceRoot
|
||||||
|
{
|
||||||
|
public bool adult { get; set; }
|
||||||
|
public string backdrop_path { get; set; }
|
||||||
|
public Belongs_To_Collection belongs_to_collection { get; set; }
|
||||||
|
public int budget { get; set; }
|
||||||
|
public Genre[] genres { get; set; }
|
||||||
|
public string homepage { get; set; }
|
||||||
|
public int id { get; set; }
|
||||||
|
public string imdb_id { get; set; }
|
||||||
|
public string original_language { get; set; }
|
||||||
|
public string original_title { get; set; }
|
||||||
|
public string overview { get; set; }
|
||||||
|
public float popularity { get; set; }
|
||||||
|
public string poster_path { get; set; }
|
||||||
|
public Production_Companies[] production_companies { get; set; }
|
||||||
|
public Production_Countries[] production_countries { get; set; }
|
||||||
|
public string release_date { get; set; }
|
||||||
|
public int revenue { get; set; }
|
||||||
|
public int runtime { get; set; }
|
||||||
|
public Spoken_Languages[] spoken_languages { get; set; }
|
||||||
|
public string status { get; set; }
|
||||||
|
public string tagline { get; set; }
|
||||||
|
public string title { get; set; }
|
||||||
|
public bool video { get; set; }
|
||||||
|
public float vote_average { get; set; }
|
||||||
|
public int vote_count { get; set; }
|
||||||
|
public AlternativeTitles alternative_titles { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Belongs_To_Collection
|
||||||
|
{
|
||||||
|
public int id { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public string poster_path { get; set; }
|
||||||
|
public string backdrop_path { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Genre
|
||||||
|
{
|
||||||
|
public int id { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Production_Companies
|
||||||
|
{
|
||||||
|
public string name { get; set; }
|
||||||
|
public int id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Production_Countries
|
||||||
|
{
|
||||||
|
public string iso_3166_1 { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Spoken_Languages
|
||||||
|
{
|
||||||
|
public string iso_639_1 { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AlternativeTitles
|
||||||
|
{
|
||||||
|
public List<Title> titles { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Title
|
||||||
|
{
|
||||||
|
public string iso_3166_1 { get; set; }
|
||||||
|
public string title { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Exceptions;
|
using NzbDrone.Core.Exceptions;
|
||||||
using NzbDrone.Core.MediaCover;
|
using NzbDrone.Core.MediaCover;
|
||||||
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||||
|
using NzbDrone.Core.MetadataSource;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
@ -20,11 +21,15 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
private readonly IHttpRequestBuilderFactory _requestBuilder;
|
private readonly IHttpRequestBuilderFactory _requestBuilder;
|
||||||
|
private readonly IHttpRequestBuilderFactory _movieBuilder;
|
||||||
|
private readonly ITmdbConfigService _configService;
|
||||||
|
|
||||||
public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder, Logger logger)
|
public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder, ITmdbConfigService configService, Logger logger)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_requestBuilder = requestBuilder.SkyHookTvdb;
|
_requestBuilder = requestBuilder.SkyHookTvdb;
|
||||||
|
_movieBuilder = requestBuilder.TMDB;
|
||||||
|
_configService = configService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +63,65 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
return new Tuple<Series, List<Episode>>(series, episodes.ToList());
|
return new Tuple<Series, List<Episode>>(series, episodes.ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Movie GetMovieInfo(int TmdbId)
|
||||||
|
{
|
||||||
|
var request = _movieBuilder.Create()
|
||||||
|
.SetSegment("route", "movie")
|
||||||
|
.SetSegment("id", TmdbId.ToString())
|
||||||
|
.SetSegment("secondaryRoute", "")
|
||||||
|
.AddQueryParam("append_to_response", "alternative_titles")
|
||||||
|
.AddQueryParam("country", "US")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
request.AllowAutoRedirect = true;
|
||||||
|
request.SuppressHttpError = true;
|
||||||
|
|
||||||
|
var response = _httpClient.Get<MovieResourceRoot>(request);
|
||||||
|
|
||||||
|
var resource = response.Resource;
|
||||||
|
|
||||||
|
var movie = new Movie();
|
||||||
|
|
||||||
|
movie.TmdbId = TmdbId;
|
||||||
|
movie.ImdbId = resource.imdb_id;
|
||||||
|
movie.Title = resource.title;
|
||||||
|
movie.TitleSlug = movie.Title.ToLower().Replace(" ", "-");
|
||||||
|
movie.CleanTitle = Parser.Parser.CleanSeriesTitle(movie.Title);
|
||||||
|
movie.Overview = resource.overview;
|
||||||
|
movie.Website = resource.homepage;
|
||||||
|
movie.InCinemas = DateTime.Parse(resource.release_date);
|
||||||
|
movie.Year = movie.InCinemas.Value.Year;
|
||||||
|
|
||||||
|
movie.Images.Add(_configService.GetCoverForURL(resource.poster_path, MediaCoverTypes.Poster));//TODO: Update to load image specs from tmdb page!
|
||||||
|
movie.Images.Add(_configService.GetCoverForURL(resource.backdrop_path, MediaCoverTypes.Banner));
|
||||||
|
movie.Runtime = resource.runtime;
|
||||||
|
|
||||||
|
foreach(Title title in resource.alternative_titles.titles)
|
||||||
|
{
|
||||||
|
movie.AlternativeTitles.Add(title.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
movie.Ratings = new Ratings();
|
||||||
|
movie.Ratings.Votes = resource.vote_count;
|
||||||
|
movie.Ratings.Value = (decimal)resource.vote_average;
|
||||||
|
|
||||||
|
foreach(Genre genre in resource.genres)
|
||||||
|
{
|
||||||
|
movie.Genres.Add(genre.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.status == "Released")
|
||||||
|
{
|
||||||
|
movie.Status = MovieStatusType.Released;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
movie.Status = MovieStatusType.Announced;
|
||||||
|
}
|
||||||
|
|
||||||
|
return movie;
|
||||||
|
}
|
||||||
|
|
||||||
public Movie GetMovieInfo(string ImdbId)
|
public Movie GetMovieInfo(string ImdbId)
|
||||||
{
|
{
|
||||||
var imdbRequest = new HttpRequest("http://www.omdbapi.com/?i=" + ImdbId + "&plot=full&r=json");
|
var imdbRequest = new HttpRequest("http://www.omdbapi.com/?i=" + ImdbId + "&plot=full&r=json");
|
||||||
|
@ -136,11 +200,22 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var searchTerm = lowerTitle.Replace("+", "_").Replace(" ", "_");
|
var searchTerm = lowerTitle.Replace("_", "+").Replace(" ", "+");
|
||||||
|
|
||||||
var firstChar = searchTerm.First();
|
var firstChar = searchTerm.First();
|
||||||
|
|
||||||
var imdbRequest = new HttpRequest("https://v2.sg.media-imdb.com/suggests/" + firstChar + "/" + searchTerm + ".json");
|
var request = _movieBuilder.Create()
|
||||||
|
.SetSegment("route", "search")
|
||||||
|
.SetSegment("id", "movie")
|
||||||
|
.SetSegment("secondaryRoute", "")
|
||||||
|
.AddQueryParam("query", searchTerm)
|
||||||
|
.AddQueryParam("include_adult", false)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
request.AllowAutoRedirect = true;
|
||||||
|
request.SuppressHttpError = true;
|
||||||
|
|
||||||
|
/*var imdbRequest = new HttpRequest("https://v2.sg.media-imdb.com/suggests/" + firstChar + "/" + searchTerm + ".json");
|
||||||
|
|
||||||
var response = _httpClient.Get(imdbRequest);
|
var response = _httpClient.Get(imdbRequest);
|
||||||
|
|
||||||
|
@ -148,31 +223,42 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
|
|
||||||
var responseCleaned = response.Content.Replace(imdbCallback, "").TrimEnd(")");
|
var responseCleaned = response.Content.Replace(imdbCallback, "").TrimEnd(")");
|
||||||
|
|
||||||
dynamic json = JsonConvert.DeserializeObject(responseCleaned);
|
_logger.Warn("Cleaned response: " + responseCleaned);
|
||||||
|
|
||||||
|
ImdbResource json = JsonConvert.DeserializeObject<ImdbResource>(responseCleaned);
|
||||||
|
|
||||||
|
_logger.Warn("Json object: " + json);
|
||||||
|
|
||||||
|
_logger.Warn("Crash ahead.");*/
|
||||||
|
|
||||||
|
var response = _httpClient.Get<MovieSearchRoot>(request);
|
||||||
|
|
||||||
|
var movieResults = response.Resource.results;
|
||||||
|
|
||||||
var imdbMovies = new List<Movie>();
|
var imdbMovies = new List<Movie>();
|
||||||
|
|
||||||
foreach (dynamic entry in json.d)
|
foreach (MovieResult result in movieResults)
|
||||||
{
|
{
|
||||||
var imdbMovie = new Movie();
|
var imdbMovie = new Movie();
|
||||||
imdbMovie.ImdbId = entry.id;
|
imdbMovie.TmdbId = result.id;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
imdbMovie.SortTitle = entry.l;
|
imdbMovie.SortTitle = result.title;
|
||||||
imdbMovie.Title = entry.l;
|
imdbMovie.Title = result.title;
|
||||||
string titleSlug = entry.l;
|
string titleSlug = result.title;
|
||||||
imdbMovie.TitleSlug = titleSlug.ToLower().Replace(" ", "-");
|
imdbMovie.TitleSlug = titleSlug.ToLower().Replace(" ", "-");
|
||||||
imdbMovie.Year = entry.y;
|
imdbMovie.Year = DateTime.Parse(result.release_date).Year;
|
||||||
imdbMovie.Images = new List<MediaCover.MediaCover>();
|
imdbMovie.Images = new List<MediaCover.MediaCover>();
|
||||||
|
imdbMovie.Overview = result.overview;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string url = entry.i[0];
|
string url = result.poster_path;
|
||||||
var imdbPoster = new MediaCover.MediaCover(MediaCoverTypes.Poster, url);
|
var imdbPoster = _configService.GetCoverForURL(result.poster_path, MediaCoverTypes.Poster);
|
||||||
imdbMovie.Images.Add(imdbPoster);
|
imdbMovie.Images.Add(imdbPoster);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.Debug(entry);
|
_logger.Debug(result);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
77
src/NzbDrone.Core/MetadataSource/TmdbConfigurationService.cs
Normal file
77
src/NzbDrone.Core/MetadataSource/TmdbConfigurationService.cs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using NzbDrone.Core.MediaCover;
|
||||||
|
using NzbDrone.Common.Cache;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Common.Cloud;
|
||||||
|
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MetadataSource
|
||||||
|
{
|
||||||
|
public interface ITmdbConfigService
|
||||||
|
{
|
||||||
|
MediaCover.MediaCover GetCoverForURL(string url, MediaCover.MediaCoverTypes type);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TmdbConfigService : ITmdbConfigService
|
||||||
|
{
|
||||||
|
private readonly ICached<ConfigResource> _configurationCache;
|
||||||
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly IHttpRequestBuilderFactory _tmdbBuilder;
|
||||||
|
|
||||||
|
public TmdbConfigService(ICacheManager cacheManager, IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder)
|
||||||
|
{
|
||||||
|
_configurationCache = cacheManager.GetCache<ConfigResource>(GetType(), "configuration_cache");
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_tmdbBuilder = requestBuilder.TMDBSingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaCover.MediaCover GetCoverForURL(string url, MediaCover.MediaCoverTypes type)
|
||||||
|
{
|
||||||
|
if (_configurationCache.Count == 0)
|
||||||
|
{
|
||||||
|
RefreshCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
var images = _configurationCache.Find("configuration").images;
|
||||||
|
|
||||||
|
var cover = new MediaCover.MediaCover();
|
||||||
|
cover.CoverType = type;
|
||||||
|
|
||||||
|
var realUrl = images.base_url;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MediaCoverTypes.Banner:
|
||||||
|
realUrl += images.backdrop_sizes.Last();
|
||||||
|
break;
|
||||||
|
case MediaCoverTypes.Poster:
|
||||||
|
realUrl += images.poster_sizes.Last();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
realUrl += "original";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
realUrl += url;
|
||||||
|
|
||||||
|
cover.Url = realUrl;
|
||||||
|
|
||||||
|
return cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshCache()
|
||||||
|
{
|
||||||
|
var request = _tmdbBuilder.Create().SetSegment("route", "configuration").Build();
|
||||||
|
|
||||||
|
var response = _httpClient.Get<ConfigResource>(request);
|
||||||
|
|
||||||
|
if (response.Resource.images != null)
|
||||||
|
{
|
||||||
|
_configurationCache.Set("configuration", response.Resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -183,6 +183,10 @@
|
||||||
<Compile Include="Datastore\Migration\002_remove_tvrage_imdb_unique_constraint.cs" />
|
<Compile Include="Datastore\Migration\002_remove_tvrage_imdb_unique_constraint.cs" />
|
||||||
<Compile Include="Datastore\Migration\003_remove_clean_title_from_scene_mapping.cs" />
|
<Compile Include="Datastore\Migration\003_remove_clean_title_from_scene_mapping.cs" />
|
||||||
<Compile Include="Datastore\Migration\004_updated_history.cs" />
|
<Compile Include="Datastore\Migration\004_updated_history.cs" />
|
||||||
|
<Compile Include="Datastore\Migration\108_update_schedule_interval.cs" />
|
||||||
|
<Compile Include="Datastore\Migration\107_fix_movie_files.cs" />
|
||||||
|
<Compile Include="Datastore\Migration\106_add_tmdb_stuff.cs" />
|
||||||
|
<Compile Include="Datastore\Migration\105_fix_history_movieId.cs" />
|
||||||
<Compile Include="Datastore\Migration\005_added_eventtype_to_history.cs" />
|
<Compile Include="Datastore\Migration\005_added_eventtype_to_history.cs" />
|
||||||
<Compile Include="Datastore\Migration\006_add_index_to_log_time.cs" />
|
<Compile Include="Datastore\Migration\006_add_index_to_log_time.cs" />
|
||||||
<Compile Include="Datastore\Migration\007_add_renameEpisodes_to_naming.cs" />
|
<Compile Include="Datastore\Migration\007_add_renameEpisodes_to_naming.cs" />
|
||||||
|
@ -630,6 +634,11 @@
|
||||||
<Compile Include="Indexers\Omgwtfnzbs\OmgwtfnzbsRssParser.cs" />
|
<Compile Include="Indexers\Omgwtfnzbs\OmgwtfnzbsRssParser.cs" />
|
||||||
<Compile Include="Indexers\Omgwtfnzbs\OmgwtfnzbsSettings.cs" />
|
<Compile Include="Indexers\Omgwtfnzbs\OmgwtfnzbsSettings.cs" />
|
||||||
<Compile Include="Indexers\HttpIndexerBase.cs" />
|
<Compile Include="Indexers\HttpIndexerBase.cs" />
|
||||||
|
<Compile Include="Indexers\TorrentPotato\TorrentPotato.cs" />
|
||||||
|
<Compile Include="Indexers\TorrentPotato\TorrentPotatoParser.cs" />
|
||||||
|
<Compile Include="Indexers\TorrentPotato\TorrentPotatoRequestGenerator.cs" />
|
||||||
|
<Compile Include="Indexers\TorrentPotato\TorrentPotatoResponse.cs" />
|
||||||
|
<Compile Include="Indexers\TorrentPotato\TorrentPotatoSettings.cs" />
|
||||||
<Compile Include="Indexers\Rarbg\Rarbg.cs" />
|
<Compile Include="Indexers\Rarbg\Rarbg.cs" />
|
||||||
<Compile Include="Indexers\Rarbg\RarbgRequestGenerator.cs" />
|
<Compile Include="Indexers\Rarbg\RarbgRequestGenerator.cs" />
|
||||||
<Compile Include="Indexers\Rarbg\RarbgResponse.cs" />
|
<Compile Include="Indexers\Rarbg\RarbgResponse.cs" />
|
||||||
|
@ -803,12 +812,15 @@
|
||||||
<Compile Include="MetadataSource\IProvideMovieInfo.cs" />
|
<Compile Include="MetadataSource\IProvideMovieInfo.cs" />
|
||||||
<Compile Include="MetadataSource\ISearchForNewMovie.cs" />
|
<Compile Include="MetadataSource\ISearchForNewMovie.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\ActorResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\ActorResource.cs" />
|
||||||
|
<Compile Include="MetadataSource\SkyHook\Resource\ConfigurationResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\EpisodeResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\EpisodeResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\ImageResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\ImageResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\RatingResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\RatingResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\SeasonResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\SeasonResource.cs" />
|
||||||
|
<Compile Include="MetadataSource\SkyHook\Resource\MovieResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\ShowResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\ShowResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\TimeOfDayResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\TimeOfDayResource.cs" />
|
||||||
|
<Compile Include="MetadataSource\SkyHook\Resource\TMDBResources.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\SkyHookProxy.cs" />
|
<Compile Include="MetadataSource\SkyHook\SkyHookProxy.cs" />
|
||||||
<Compile Include="MetadataSource\SearchSeriesComparer.cs" />
|
<Compile Include="MetadataSource\SearchSeriesComparer.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\SkyHookException.cs" />
|
<Compile Include="MetadataSource\SkyHook\SkyHookException.cs" />
|
||||||
|
@ -833,6 +845,7 @@
|
||||||
<Compile Include="Extras\Metadata\MetadataType.cs" />
|
<Compile Include="Extras\Metadata\MetadataType.cs" />
|
||||||
<Compile Include="MetadataSource\IProvideSeriesInfo.cs" />
|
<Compile Include="MetadataSource\IProvideSeriesInfo.cs" />
|
||||||
<Compile Include="MetadataSource\ISearchForNewSeries.cs" />
|
<Compile Include="MetadataSource\ISearchForNewSeries.cs" />
|
||||||
|
<Compile Include="MetadataSource\TmdbConfigurationService.cs" />
|
||||||
<Compile Include="Notifications\Join\JoinAuthException.cs" />
|
<Compile Include="Notifications\Join\JoinAuthException.cs" />
|
||||||
<Compile Include="Notifications\Join\JoinInvalidDeviceException.cs" />
|
<Compile Include="Notifications\Join\JoinInvalidDeviceException.cs" />
|
||||||
<Compile Include="Notifications\Join\JoinResponseModel.cs" />
|
<Compile Include="Notifications\Join\JoinResponseModel.cs" />
|
||||||
|
@ -889,6 +902,7 @@
|
||||||
<Compile Include="Parser\IsoLanguages.cs" />
|
<Compile Include="Parser\IsoLanguages.cs" />
|
||||||
<Compile Include="Parser\LanguageParser.cs" />
|
<Compile Include="Parser\LanguageParser.cs" />
|
||||||
<Compile Include="Parser\Model\LocalMovie.cs" />
|
<Compile Include="Parser\Model\LocalMovie.cs" />
|
||||||
|
<Compile Include="Parser\Model\ParsedMovieInfo.cs" />
|
||||||
<Compile Include="Parser\Model\RemoteMovie.cs" />
|
<Compile Include="Parser\Model\RemoteMovie.cs" />
|
||||||
<Compile Include="Profiles\Delay\DelayProfile.cs" />
|
<Compile Include="Profiles\Delay\DelayProfile.cs" />
|
||||||
<Compile Include="Profiles\Delay\DelayProfileService.cs" />
|
<Compile Include="Profiles\Delay\DelayProfileService.cs" />
|
||||||
|
|
|
@ -346,7 +346,9 @@ namespace NzbDrone.Core.Organizer
|
||||||
public static string CleanFolderName(string name)
|
public static string CleanFolderName(string name)
|
||||||
{
|
{
|
||||||
name = FileNameCleanupRegex.Replace(name, match => match.Captures[0].Value[0].ToString());
|
name = FileNameCleanupRegex.Replace(name, match => match.Captures[0].Value[0].ToString());
|
||||||
return name.Trim(' ', '.');
|
name = name.Trim(' ', '.');
|
||||||
|
|
||||||
|
return CleanFileName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddSeriesTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Series series)
|
private void AddSeriesTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Series series)
|
||||||
|
|
31
src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs
Normal file
31
src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
using System.Linq;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Qualities;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Parser.Model
|
||||||
|
{
|
||||||
|
public class ParsedMovieInfo
|
||||||
|
{
|
||||||
|
public string MovieTitle { get; set; }
|
||||||
|
public SeriesTitleInfo MovieTitleInfo { get; set; }
|
||||||
|
public QualityModel Quality { get; set; }
|
||||||
|
public int SeasonNumber { get; set; }
|
||||||
|
public Language Language { get; set; }
|
||||||
|
public bool FullSeason { get; set; }
|
||||||
|
public bool Special { get; set; }
|
||||||
|
public string ReleaseGroup { get; set; }
|
||||||
|
public string ReleaseHash { get; set; }
|
||||||
|
public string Edition { get; set;}
|
||||||
|
public int Year { get; set; }
|
||||||
|
|
||||||
|
public ParsedMovieInfo()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("{0} - {1} {2}", MovieTitle, MovieTitleInfo.Year, Quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ namespace NzbDrone.Core.Parser.Model
|
||||||
{
|
{
|
||||||
public ReleaseInfo Release { get; set; }
|
public ReleaseInfo Release { get; set; }
|
||||||
public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; } //TODO: Change to ParsedMovieInfo, for now though ParsedEpisodeInfo will do.
|
public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; } //TODO: Change to ParsedMovieInfo, for now though ParsedEpisodeInfo will do.
|
||||||
|
public ParsedMovieInfo ParsedMovieInfo { get; set; }
|
||||||
public Movie Movie { get; set; }
|
public Movie Movie { get; set; }
|
||||||
public bool DownloadAllowed { get; set; }
|
public bool DownloadAllowed { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace NzbDrone.Core.Parser.Model
|
||||||
public string InfoHash { get; set; }
|
public string InfoHash { get; set; }
|
||||||
public int? Seeders { get; set; }
|
public int? Seeders { get; set; }
|
||||||
public int? Peers { get; set; }
|
public int? Peers { get; set; }
|
||||||
|
public bool Freeleech { get; set; }
|
||||||
|
|
||||||
public static int? GetSeeders(ReleaseInfo release)
|
public static int? GetSeeders(ReleaseInfo release)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,29 @@ namespace NzbDrone.Core.Parser
|
||||||
{
|
{
|
||||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(Parser));
|
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(Parser));
|
||||||
|
|
||||||
|
private static readonly Regex[] ReportMovieTitleRegex = new[]
|
||||||
|
{
|
||||||
|
//Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.Special.Edition.2011
|
||||||
|
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<edition>(\w+\.?edition))\.(?<year>(?<!e|x)\d{4}(?!p|i|\d+|\)|\]|\W\d+)))+(\W+|_|$)(?!\\)",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
|
//Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.2011.Special.Edition //TODO: Seems to slow down parsing heavily!
|
||||||
|
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<year>(?<!e|x)\d{4}(?!p|i|\d+|\)|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((\w+\.?){1,3}edition))",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
|
//Cut Movies, e.g: Mission.Impossible.3.Directors.Cut.2011
|
||||||
|
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<edition>(\w+\.?cut))\.(?<year>(?<!e|x)\d{4}(?!p|i|\d+|\)|\]|\W\d+)))+(\W+|_|$)(?!\\)",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
|
//Cut Movies, e.g: Mission.Impossible.3.2011.Directors.Cut
|
||||||
|
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<year>(?<!e|x)\d{4}(?!p|i|\d+|\)|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((\w+\.?){1,3}cut))",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
|
|
||||||
|
//Normal movie format, e.g: Mission.Impossible.3.2011
|
||||||
|
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<year>(?<!e|x)\d{4}(?!p|i|\d+|\)|\]|\W\d+)))+(\W+|_|$)(?!\\)",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
|
//PassThePopcorn Torrent names: Star.Wars[PassThePopcorn]
|
||||||
|
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<year>(\[\w *\])))+(\W+|_|$)(?!\\)",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
|
};
|
||||||
|
|
||||||
private static readonly Regex[] ReportTitleRegex = new[]
|
private static readonly Regex[] ReportTitleRegex = new[]
|
||||||
{
|
{
|
||||||
//Anime - Absolute Episode Number + Title + Season+Episode
|
//Anime - Absolute Episode Number + Title + Season+Episode
|
||||||
|
@ -298,6 +321,96 @@ namespace NzbDrone.Core.Parser
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ParsedMovieInfo ParseMovieTitle(string title)
|
||||||
|
{
|
||||||
|
|
||||||
|
ParsedMovieInfo realResult = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!ValidateBeforeParsing(title)) return null;
|
||||||
|
|
||||||
|
title = title.Replace(" ", "."); //TODO: Determine if this breaks something. However, it shouldn't.
|
||||||
|
|
||||||
|
Logger.Debug("Parsing string '{0}'", title);
|
||||||
|
|
||||||
|
if (ReversedTitleRegex.IsMatch(title))
|
||||||
|
{
|
||||||
|
var titleWithoutExtension = RemoveFileExtension(title).ToCharArray();
|
||||||
|
Array.Reverse(titleWithoutExtension);
|
||||||
|
|
||||||
|
title = new string(titleWithoutExtension) + title.Substring(titleWithoutExtension.Length);
|
||||||
|
|
||||||
|
Logger.Debug("Reversed name detected. Converted to '{0}'", title);
|
||||||
|
}
|
||||||
|
|
||||||
|
var simpleTitle = SimpleTitleRegex.Replace(title, string.Empty);
|
||||||
|
|
||||||
|
simpleTitle = RemoveFileExtension(simpleTitle);
|
||||||
|
|
||||||
|
// TODO: Quick fix stripping [url] - prefixes.
|
||||||
|
simpleTitle = WebsitePrefixRegex.Replace(simpleTitle, string.Empty);
|
||||||
|
|
||||||
|
simpleTitle = CleanTorrentSuffixRegex.Replace(simpleTitle, string.Empty);
|
||||||
|
|
||||||
|
foreach (var regex in ReportMovieTitleRegex)
|
||||||
|
{
|
||||||
|
var match = regex.Matches(simpleTitle);
|
||||||
|
|
||||||
|
if (match.Count != 0)
|
||||||
|
{
|
||||||
|
Logger.Trace(regex);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = ParseMovieMatchCollection(match);
|
||||||
|
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
result.Language = LanguageParser.ParseLanguage(title);
|
||||||
|
Logger.Debug("Language parsed: {0}", result.Language);
|
||||||
|
|
||||||
|
result.Quality = QualityParser.ParseQuality(title);
|
||||||
|
Logger.Debug("Quality parsed: {0}", result.Quality);
|
||||||
|
|
||||||
|
result.ReleaseGroup = ParseReleaseGroup(title);
|
||||||
|
|
||||||
|
var subGroup = GetSubGroup(match);
|
||||||
|
if (!subGroup.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
result.ReleaseGroup = subGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug("Release Group parsed: {0}", result.ReleaseGroup);
|
||||||
|
|
||||||
|
result.ReleaseHash = GetReleaseHash(match);
|
||||||
|
if (!result.ReleaseHash.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
Logger.Debug("Release Hash parsed: {0}", result.ReleaseHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
realResult = result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InvalidDateException ex)
|
||||||
|
{
|
||||||
|
Logger.Debug(ex, ex.Message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (!title.ToLower().Contains("password") && !title.ToLower().Contains("yenc"))
|
||||||
|
Logger.Error(e, "An error has occurred while trying to parse " + title);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug("Unable to parse {0}", title);
|
||||||
|
return realResult;
|
||||||
|
}
|
||||||
|
|
||||||
public static ParsedEpisodeInfo ParseTitle(string title)
|
public static ParsedEpisodeInfo ParseTitle(string title)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -528,6 +641,31 @@ namespace NzbDrone.Core.Parser
|
||||||
return seriesTitleInfo;
|
return seriesTitleInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ParsedMovieInfo ParseMovieMatchCollection(MatchCollection matchCollection)
|
||||||
|
{
|
||||||
|
var seriesName = matchCollection[0].Groups["title"].Value.Replace('.', ' ').Replace('_', ' ');
|
||||||
|
seriesName = RequestInfoRegex.Replace(seriesName, "").Trim(' ');
|
||||||
|
|
||||||
|
int airYear;
|
||||||
|
int.TryParse(matchCollection[0].Groups["year"].Value, out airYear);
|
||||||
|
|
||||||
|
ParsedMovieInfo result;
|
||||||
|
|
||||||
|
result = new ParsedMovieInfo { Year = airYear };
|
||||||
|
|
||||||
|
if (matchCollection[0].Groups["edition"].Success)
|
||||||
|
{
|
||||||
|
result.Edition = matchCollection[0].Groups["edition"].Value.Replace(".", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.MovieTitle = seriesName;
|
||||||
|
result.MovieTitleInfo = GetSeriesTitleInfo(result.MovieTitle);
|
||||||
|
|
||||||
|
Logger.Debug("Movie Parsed. {0}", result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static ParsedEpisodeInfo ParseMatchCollection(MatchCollection matchCollection)
|
private static ParsedEpisodeInfo ParseMatchCollection(MatchCollection matchCollection)
|
||||||
{
|
{
|
||||||
var seriesName = matchCollection[0].Groups["title"].Value.Replace('.', ' ').Replace('_', ' ');
|
var seriesName = matchCollection[0].Groups["title"].Value.Replace('.', ' ').Replace('_', ' ');
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace NzbDrone.Core.Parser
|
||||||
Movie GetMovie(string title);
|
Movie GetMovie(string title);
|
||||||
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
||||||
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int seriesId, IEnumerable<int> episodeIds);
|
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int seriesId, IEnumerable<int> episodeIds);
|
||||||
RemoteMovie Map(ParsedEpisodeInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria = null);
|
RemoteMovie Map(ParsedMovieInfo parsedMovieInfo, string imdbId, SearchCriteriaBase searchCriteria = null);
|
||||||
List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null);
|
List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null);
|
||||||
ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,20 @@ namespace NzbDrone.Core.Parser
|
||||||
private readonly ISceneMappingService _sceneMappingService;
|
private readonly ISceneMappingService _sceneMappingService;
|
||||||
private readonly IMovieService _movieService;
|
private readonly IMovieService _movieService;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
private readonly Dictionary<string, string> romanNumeralsMapper = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "1", "I"},
|
||||||
|
{ "2", "II"},
|
||||||
|
{ "3", "III"},
|
||||||
|
{ "4", "IV"},
|
||||||
|
{ "5", "V"},
|
||||||
|
{ "6", "VI"},
|
||||||
|
{ "7", "VII"},
|
||||||
|
{ "8", "VII"},
|
||||||
|
{ "9", "IX"},
|
||||||
|
{ "10", "X"},
|
||||||
|
|
||||||
|
}; //If a movie has more than 10 parts fuck 'em.
|
||||||
|
|
||||||
public ParsingService(IEpisodeService episodeService,
|
public ParsingService(IEpisodeService episodeService,
|
||||||
ISeriesService seriesService,
|
ISeriesService seriesService,
|
||||||
|
@ -163,19 +177,19 @@ namespace NzbDrone.Core.Parser
|
||||||
|
|
||||||
public Movie GetMovie(string title)
|
public Movie GetMovie(string title)
|
||||||
{
|
{
|
||||||
var parsedEpisodeInfo = Parser.ParseTitle(title);
|
var parsedEpisodeInfo = Parser.ParseMovieTitle(title);
|
||||||
|
|
||||||
if (parsedEpisodeInfo == null)
|
if (parsedEpisodeInfo == null)
|
||||||
{
|
{
|
||||||
return _movieService.FindByTitle(title);
|
return _movieService.FindByTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
var series = _movieService.FindByTitle(parsedEpisodeInfo.SeriesTitle);
|
var series = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitle);
|
||||||
|
|
||||||
if (series == null)
|
if (series == null)
|
||||||
{
|
{
|
||||||
series = _movieService.FindByTitle(parsedEpisodeInfo.SeriesTitleInfo.TitleWithoutYear,
|
series = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitleInfo.TitleWithoutYear,
|
||||||
parsedEpisodeInfo.SeriesTitleInfo.Year);
|
parsedEpisodeInfo.MovieTitleInfo.Year);
|
||||||
}
|
}
|
||||||
|
|
||||||
return series;
|
return series;
|
||||||
|
@ -201,11 +215,11 @@ namespace NzbDrone.Core.Parser
|
||||||
return remoteEpisode;
|
return remoteEpisode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RemoteMovie Map(ParsedEpisodeInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria = null)
|
public RemoteMovie Map(ParsedMovieInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria = null)
|
||||||
{
|
{
|
||||||
var remoteEpisode = new RemoteMovie
|
var remoteEpisode = new RemoteMovie
|
||||||
{
|
{
|
||||||
ParsedEpisodeInfo = parsedEpisodeInfo,
|
ParsedMovieInfo = parsedEpisodeInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
var movie = GetMovie(parsedEpisodeInfo, imdbId, searchCriteria);
|
var movie = GetMovie(parsedEpisodeInfo, imdbId, searchCriteria);
|
||||||
|
@ -334,23 +348,56 @@ namespace NzbDrone.Core.Parser
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Movie GetMovie(ParsedEpisodeInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria)
|
private Movie GetMovie(ParsedMovieInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria)
|
||||||
{
|
{
|
||||||
if (searchCriteria != null)
|
if (searchCriteria != null)
|
||||||
{
|
{
|
||||||
if (searchCriteria.Movie.CleanTitle == parsedEpisodeInfo.SeriesTitle.CleanSeriesTitle())
|
var possibleTitles = new List<string>();
|
||||||
|
|
||||||
|
possibleTitles.Add(searchCriteria.Movie.CleanTitle);
|
||||||
|
|
||||||
|
foreach (string altTitle in searchCriteria.Movie.AlternativeTitles)
|
||||||
{
|
{
|
||||||
return searchCriteria.Movie;
|
possibleTitles.Add(altTitle.CleanSeriesTitle());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imdbId.IsNotNullOrWhiteSpace() && imdbId == searchCriteria.Movie.ImdbId)
|
foreach (string title in possibleTitles)
|
||||||
{
|
{
|
||||||
//TODO: If series is found by TvdbId, we should report it as a scene naming exception, since it will fail to import
|
if (title == parsedEpisodeInfo.MovieTitle.CleanSeriesTitle())
|
||||||
return searchCriteria.Movie;
|
{
|
||||||
|
return searchCriteria.Movie;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, string> entry in romanNumeralsMapper)
|
||||||
|
{
|
||||||
|
string num = entry.Key;
|
||||||
|
string roman = entry.Value.ToLower();
|
||||||
|
|
||||||
|
if (title.Replace(num, roman) == parsedEpisodeInfo.MovieTitle.CleanSeriesTitle())
|
||||||
|
{
|
||||||
|
return searchCriteria.Movie;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (title.Replace(roman, num) == parsedEpisodeInfo.MovieTitle.CleanSeriesTitle())
|
||||||
|
{
|
||||||
|
return searchCriteria.Movie;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Movie movie = _movieService.FindByTitle(parsedEpisodeInfo.SeriesTitle);
|
Movie movie = null;
|
||||||
|
|
||||||
|
if (searchCriteria == null)
|
||||||
|
{
|
||||||
|
|
||||||
|
movie = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitle); //Todo: same as above!
|
||||||
|
|
||||||
|
return movie;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (movie == null && imdbId.IsNotNullOrWhiteSpace())
|
if (movie == null && imdbId.IsNotNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
|
@ -360,7 +407,7 @@ namespace NzbDrone.Core.Parser
|
||||||
|
|
||||||
if (movie == null)
|
if (movie == null)
|
||||||
{
|
{
|
||||||
_logger.Debug("No matching movie {0}", parsedEpisodeInfo.SeriesTitle);
|
_logger.Debug("No matching movie {0}", parsedEpisodeInfo.MovieTitle);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue