Merge branch 'develop' into feature/net-import

pull/497/head
Devin Buhl 8 years ago
commit 47824426c6

26
.gitignore vendored

@ -101,16 +101,21 @@ App_Data/*.ldf
_NCrunch_* _NCrunch_*
_TeamCity* _TeamCity*
# Sonarr # Radarr
config.xml Backups/
nzbdrone.log*txt logs/
MediaCover/
UpdateLogs/ UpdateLogs/
xdg/
config.xml
logs.db*
nzbdrone.db*
nzbdrone.pid
*workspace.xml *workspace.xml
*.test-cache *.test-cache
*.userprefs *.userprefs
*/test-results/* */test-results/*
src/UI/.idea/* src/UI/.idea/*
*log.txt
node_modules/ node_modules/
_output* _output*
_rawPackage/ _rawPackage/
@ -122,23 +127,26 @@ setup/Output/
UI.Phantom/ UI.Phantom/
#VS outout folders # VS outout folders
bin bin
obj obj
output/* output/*
#Packages # Packages
Radarr_*/ Radarr_*/
Radarr_*.zip Radarr_*.zip
Radarr_*.gz Radarr_*.gz
#OS X metadata files # macOS metadata files
._* ._*
.DS_Store .DS_Store
_start _start
_temp_*/**/* _temp_*/**/*
#AppVeyor # Windows thumbnail cache files
Thumbs.db
# AppVeyor
/tools-cake/ /tools-cake/
/_artifacts/ /_artifacts/

@ -1,12 +1,14 @@
language: csharp language: csharp
solution: src/NzbDrone.sln solution: src/NzbDrone.sln
script: # the following commands are just examples, use whatever your build process requires addons:
apt:
packages:
- nodejs
- npm
script:
- ./build.sh - ./build.sh
- chmod +x test.sh - chmod +x test.sh
# - ./test.sh Linux Unit Takes far too long, maybe even crashes travis :/ # - ./test.sh Linux Unit Takes far too long, maybe even crashes travis :/
install:
- sudo apt-get install nodejs
- sudo apt-get install npm
after_success: after_success:
- chmod +x package.sh - chmod +x package.sh
- ./package.sh - ./package.sh

@ -1,6 +1,6 @@
# Sonarr Individual Contributor License Agreement # # Radarr Individual Contributor License Agreement #
Thank you for your interest in contributing to Sonarr ("We" or "Us"). Thank you for your interest in contributing to Radarr ("We" or "Us").
This contributor agreement ("Agreement") documents the rights granted by contributors to Us. To make this document effective, please complete the form below. This is a legally binding document, so please read it carefully before agreeing to it. The Agreement may cover more than one software project managed by Us. This contributor agreement ("Agreement") documents the rights granted by contributors to Us. To make this document effective, please complete the form below. This is a legally binding document, so please read it carefully before agreeing to it. The Agreement may cover more than one software project managed by Us.
## 1. Definitions ## ## 1. Definitions ##

@ -1,6 +1,6 @@
# How to Contribute # # How to Contribute #
We're always looking for people to help make Sonarr even better, there are a number of ways to contribute. We're always looking for people to help make Radarr even better, there are a number of ways to contribute.
## Documentation ## ## Documentation ##
Setup guides, FAQ, the more information we have on the wiki the better. Setup guides, FAQ, the more information we have on the wiki the better.
@ -15,7 +15,7 @@ Setup guides, FAQ, the more information we have on the wiki the better.
### Getting started ### ### Getting started ###
1. Fork Sonarr 1. Fork Radarr
2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)* 2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)*
3. Run `npm install` 3. Run `npm install`
4. Run `npm start` - Used to compile the UI components and copy them. 4. Run `npm start` - Used to compile the UI components and copy them.
@ -24,8 +24,8 @@ Setup guides, FAQ, the more information we have on the wiki the better.
5. Compile in Visual Studio 5. Compile in Visual Studio
### Contributing Code ### ### Contributing Code ###
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Sonarr/Sonarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first) - If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Radarr/Radarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
- Rebase from Sonarr's develop branch, don't merge - Rebase from Radarr's develop branch, don't merge
- Make meaningful commits, or squash them - Make meaningful commits, or squash them
- Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements - Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements
- Reach out to us on the forums or on IRC if you have any questions - Reach out to us on the forums or on IRC if you have any questions

Binary file not shown.

@ -0,0 +1,85 @@
## Status
[![GitHub issues](https://img.shields.io/github/issues/radarr/radarr.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/issues)
[![GitHub pull requests](https://img.shields.io/github/issues-pr/radarr/radarr.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/pulls)
[![GNU GPL v3](https://img.shields.io/badge/license-GNU%20GPL%20v3-blue.svg?maxAge=60&style=flat-square)](http://www.gnu.org/licenses/gpl.html)
[![Copyright 2010-2017](https://img.shields.io/badge/copyright-2017-blue.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr)
| Service | Master | Develop |
|----------|:---------------------------:|:----------------------------:|
| AppVeyor | [![AppVeyor](https://img.shields.io/appveyor/ci/galli-leo/Radarr/master.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/Radarr) | [![AppVeyor](https://img.shields.io/appveyor/ci/galli-leo/Radarr-usby1/develop.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/Radarr-usby1) |
| Travis | [![Travis](https://img.shields.io/travis/Radarr/Radarr/master.svg?maxAge=60&style=flat-square)](https://travis-ci.org/Radarr/Radarr) | [![Travis](https://img.shields.io/travis/Radarr/Radarr/develop.svg?maxAge=60&style=flat-square)](https://travis-ci.org/Radarr/Radarr) |
This fork of Sonarr aims to turn it into something like CouchPotato.
## Downloads
[![GitHub Releases](https://img.shields.io/badge/downloads-releases-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/releases)
[![AppVeyor Builds](https://img.shields.io/badge/downloads-continuous-green.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/radarr-usby1/build/artifacts)
[![Docker x64](https://img.shields.io/badge/docker-x64-blue.svg?maxAge=60&style=flat-square)](https://store.docker.com/community/images/linuxserver/radarr)
[![Docker armhf](https://img.shields.io/badge/docker-armhf-blue.svg?maxAge=60&style=flat-square)](https://store.docker.com/community/images/lsioarmhf/radarr)
[![Docker aarch64](https://img.shields.io/badge/docker-aarch64-blue.svg?maxAge=60&style=flat-square)](https://store.docker.com/community/images/lsioarmhf/radarr-aarch64)
To connect to the UI, fire up your browser and open <http://localhost:7878> or <http://your-ip:7878>.
## Features
### Currently Working
* Adding new movies
* Manually searching for releases of movies
* Automatically searching for releases
* Automatically importing downloaded movies
* 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
* Scanning PreDB to know when a new release is available
* Fixing the other Indexers and download clients
* Importing of Sonarr config
### Major Features
* Support for major platforms: Windows, Linux, macOS, Raspberry Pi, etc.
* Can watch for better quality of the movies you 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
* Full integration with SABnzbd and NZBGet
* Full integration with Kodi, Plex (notification, library update, metadata)
* And a beautiful UI
## Configuring Development Environment
### Requirements
* [Visual Studio Community](https://www.visualstudio.com/vs/community/) or [MonoDevelop](http://www.monodevelop.com)
* [Git](https://git-scm.com/downloads)
* [Node.js](https://nodejs.org/en/download/)
### Setup
* Make sure all the required software mentioned above are installed
* Clone the repository into your development machine ([*info*](https://help.github.com/desktop/guides/contributing/working-with-your-remote-repository-on-github-or-github-enterprise))
* Grab the submodules `git submodule init && git submodule update`
* Install the required Node Packages `npm install`
* Start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
*Please note: gulp must be running at all times while you are working with Radarr client source files.*
### Development
* Open `NzbDrone.sln` in Visual Studio or run the build.sh script, if Mono is installed
* Make sure `NzbDrone.Console` is set as the startup project
## Sponsors
[JetBrains](http://www.jetbrains.com) for providing us with free licenses to their great tools:
* [ReSharper](http://www.jetbrains.com/resharper)
* [WebStorm](http://www.jetbrains.com/webstorm)
* [TeamCity](http://www.jetbrains.com/teamcity)

@ -36,6 +36,9 @@ artifacts:
cache: cache:
- '%USERPROFILE%\.nuget\packages' - '%USERPROFILE%\.nuget\packages'
- node_modules - node_modules
pull_requests:
do_not_increment_build_number: true
only_commits: only_commits:
files: files:

@ -1,81 +0,0 @@
# Radarr
| Service | Master | Develop |
|----------|:---------------------------:|:----------------------------:|
| AppVeyor | [![AppVeyor](https://img.shields.io/appveyor/ci/galli-leo/Radarr/master.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/Radarr) | [![AppVeyor](https://img.shields.io/appveyor/ci/galli-leo/Radarr-usby1/develop.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/Radarr-usby1) |
| Travis | [![Travis](https://img.shields.io/travis/galli-leo/Radarr/master.svg?maxAge=60&style=flat-square)](https://travis-ci.org/galli-leo/Radarr) | [![Travis](https://img.shields.io/travis/galli-leo/Radarr/develop.svg?maxAge=60&style=flat-square)](https://travis-ci.org/galli-leo/Radarr) |
This fork of Sonarr aims to turn it into something like Couchpotato.
## Currently working:
* Adding new movies
* Manually searching for releases of movies.
* Automatically searching for releases.
* Automatically importing downloaded movies.
* 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:
* Scanning PreDB to know when a new release is available.
* Fixing the other Indexers and download clients.
* Importing of Sonarr config.
## Download
The latest precompiled binary versions can be found here: https://github.com/galli-leo/Radarr/releases.
Docker containers from [linuxserver.io](https://linuxserver.io) can be found here.
* [Radarr (x64)](https://hub.docker.com/r/linuxserver/radarr/)
* [Radarr (armhf)](https://hub.docker.com/r/lsioarmhf/radarr/)
* [Radarr (aarch64)](https://hub.docker.com/r/lsioarmhf/radarr-aarch64/)
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: ##
### Requirements ###
- Visual Studio 2015 [Free Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) or Mono
- [Git](http://git-scm.com/downloads)
- [NodeJS](http://nodejs.org/download/)
### Setup ###
- Make sure all the required software mentioned above are installed.
- Clone the repository into your development machine. [*info*](https://help.github.com/articles/working-with-repositories)
- Grab the submodules `git submodule init && git submodule update`
- install the required Node Packages `npm install`
- start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
*Please note gulp must be running at all times while you are working with Sonarr client source files.*
### Development ###
- Open `NzbDrone.sln` in Visual Studio or run the build.sh script, if Mono is installed.
- Make sure `NzbDrone.Console` is set as the startup project
### License ###
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
Copyright 2010-2016
### Sponsors ###
- [JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools
- [ReSharper](http://www.jetbrains.com/resharper/)
- [WebStorm](http://www.jetbrains.com/webstorm/)
- [TeamCity](http://www.jetbrains.com/teamcity/)

@ -27,6 +27,7 @@ namespace NzbDrone.Api.Calendar
Get["/NzbDrone.ics"] = options => GetCalendarFeed(); Get["/NzbDrone.ics"] = options => GetCalendarFeed();
Get["/Sonarr.ics"] = options => GetCalendarFeed(); Get["/Sonarr.ics"] = options => GetCalendarFeed();
Get["/Radarr.ics"] = options => GetCalendarFeed();
} }
private Response GetCalendarFeed() private Response GetCalendarFeed()

@ -97,7 +97,7 @@ namespace NzbDrone.Api.Indexers
{ {
Guid = releaseInfo.Guid, Guid = releaseInfo.Guid,
Quality = parsedMovieInfo.Quality, Quality = parsedMovieInfo.Quality,
//QualityWeight QualityWeight = parsedMovieInfo.Quality.Quality.Id, //Id kinda hacky for wheight, but what you gonna do? TODO: Fix this shit!
Age = releaseInfo.Age, Age = releaseInfo.Age,
AgeHours = releaseInfo.AgeHours, AgeHours = releaseInfo.AgeHours,
AgeMinutes = releaseInfo.AgeMinutes, AgeMinutes = releaseInfo.AgeMinutes,

@ -0,0 +1,89 @@
using System.Collections.Generic;
using System.IO;
using NLog;
using NzbDrone.Api.REST;
using NzbDrone.Api.Movie;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.SignalR;
namespace NzbDrone.Api.EpisodeFiles
{
public class MovieFileModule : NzbDroneRestModuleWithSignalR<MovieFileResource, MovieFile>
//IHandle<EpisodeFileAddedEvent>
{
private readonly IMediaFileService _mediaFileService;
private readonly IRecycleBinProvider _recycleBinProvider;
private readonly IMovieService _seriesService;
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
private readonly Logger _logger;
public MovieFileModule(IBroadcastSignalRMessage signalRBroadcaster,
IMediaFileService mediaFileService,
IRecycleBinProvider recycleBinProvider,
IMovieService seriesService,
IQualityUpgradableSpecification qualityUpgradableSpecification,
Logger logger)
: base(signalRBroadcaster)
{
_mediaFileService = mediaFileService;
_recycleBinProvider = recycleBinProvider;
_seriesService = seriesService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
_logger = logger;
/*GetResourceById = GetEpisodeFile;
GetResourceAll = GetEpisodeFiles;
UpdateResource = SetQuality;*/
DeleteResource = DeleteEpisodeFile;
}
/*private EpisodeFileResource GetEpisodeFile(int id)
{
var episodeFile = _mediaFileService.Get(id);
var series = _seriesService.GetSeries(episodeFile.SeriesId);
return episodeFile.ToResource(series, _qualityUpgradableSpecification);
}
private List<EpisodeFileResource> GetEpisodeFiles()
{
if (!Request.Query.SeriesId.HasValue)
{
throw new BadRequestException("seriesId is missing");
}
var seriesId = (int)Request.Query.SeriesId;
var series = _seriesService.GetSeries(seriesId);
return _mediaFileService.GetFilesBySeries(seriesId).ConvertAll(f => f.ToResource(series, _qualityUpgradableSpecification));
}
private void SetQuality(EpisodeFileResource episodeFileResource)
{
var episodeFile = _mediaFileService.Get(episodeFileResource.Id);
episodeFile.Quality = episodeFileResource.Quality;
_mediaFileService.Update(episodeFile);
}*/
private void DeleteEpisodeFile(int id)
{
var episodeFile = _mediaFileService.GetMovie(id);
var series = _seriesService.GetMovie(episodeFile.MovieId);
var fullPath = Path.Combine(series.Path, episodeFile.RelativePath);
_logger.Info("Deleting episode file: {0}", fullPath);
_recycleBinProvider.DeleteFile(fullPath);
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
}
public void Handle(EpisodeFileAddedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id);
}
}
}

@ -116,6 +116,7 @@
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" /> <Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
<Compile Include="Indexers\ReleaseModuleBase.cs" /> <Compile Include="Indexers\ReleaseModuleBase.cs" />
<Compile Include="Indexers\ReleasePushModule.cs" /> <Compile Include="Indexers\ReleasePushModule.cs" />
<Compile Include="Movies\MovieFileModule.cs" />
<Compile Include="Movies\MovieModule.cs" /> <Compile Include="Movies\MovieModule.cs" />
<Compile Include="Movies\RenameMovieModule.cs" /> <Compile Include="Movies\RenameMovieModule.cs" />
<Compile Include="Movies\RenameMovieResource.cs" /> <Compile Include="Movies\RenameMovieResource.cs" />

@ -30,8 +30,8 @@ namespace NzbDrone.Api.Movie
public string ReleaseGroup { get; set; } public string ReleaseGroup { get; set; }
public QualityModel Quality { get; set; } public QualityModel Quality { get; set; }
public MovieResource Movie { get; set; } public MovieResource Movie { get; set; }
public string Edition { get; set; }
public Core.MediaFiles.MediaInfo.MediaInfoModel MediaInfo { 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)
} }
@ -63,7 +63,8 @@ namespace NzbDrone.Api.Movie
ReleaseGroup = model.ReleaseGroup, ReleaseGroup = model.ReleaseGroup,
Quality = model.Quality, Quality = model.Quality,
Movie = movie, Movie = movie,
MediaInfo = model.MediaInfo,
Edition = model.Edition
}; };
} }

@ -23,7 +23,7 @@ namespace NzbDrone.Common
Console.WriteLine(" Commands:"); Console.WriteLine(" Commands:");
Console.WriteLine(" /{0} Install the application as a Windows Service ({1}).", StartupContext.INSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME); Console.WriteLine(" /{0} Install the application as a Windows Service ({1}).", StartupContext.INSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME);
Console.WriteLine(" /{0} Uninstall already installed Windows Service ({1}).", StartupContext.UNINSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME); Console.WriteLine(" /{0} Uninstall already installed Windows Service ({1}).", StartupContext.UNINSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME);
Console.WriteLine(" /{0} Don't open Sonarr in a browser", StartupContext.NO_BROWSER); Console.WriteLine(" /{0} Don't open Radarr in a browser", StartupContext.NO_BROWSER);
Console.WriteLine(" <No Arguments> Run application in console mode."); Console.WriteLine(" <No Arguments> Run application in console mode.");
} }

@ -9,12 +9,12 @@ namespace NzbDrone.Common.Http
static UserAgentBuilder() static UserAgentBuilder()
{ {
UserAgent = string.Format("Sonarr/{0} ({1} {2})", UserAgent = string.Format("Radarr/{0} ({1} {2})",
BuildInfo.Version, BuildInfo.Version,
OsInfo.Os, OsInfo.Version.ToString(2)); OsInfo.Os, OsInfo.Version.ToString(2));
UserAgentSimplified = string.Format("Sonarr/{0}", UserAgentSimplified = string.Format("Radarr/{0}",
BuildInfo.Version.ToString(2)); BuildInfo.Version.ToString(2));
} }
} }
} }

@ -103,9 +103,9 @@ namespace NzbDrone.Common.Instrumentation
private static void RegisterAppFile(IAppFolderInfo appFolderInfo) private static void RegisterAppFile(IAppFolderInfo appFolderInfo)
{ {
RegisterAppFile(appFolderInfo, "appFileInfo", "sonarr.txt", 5, LogLevel.Info); RegisterAppFile(appFolderInfo, "appFileInfo", "radarr.txt", 5, LogLevel.Info);
RegisterAppFile(appFolderInfo, "appFileDebug", "sonarr.debug.txt", 50, LogLevel.Off); RegisterAppFile(appFolderInfo, "appFileDebug", "radarr.debug.txt", 50, LogLevel.Off);
RegisterAppFile(appFolderInfo, "appFileTrace", "sonarr.trace.txt", 50, LogLevel.Off); RegisterAppFile(appFolderInfo, "appFileTrace", "radarr.trace.txt", 50, LogLevel.Off);
} }
private static LoggingRule RegisterAppFile(IAppFolderInfo appFolderInfo, string name, string fileName, int maxArchiveFiles, LogLevel minLogLevel) private static LoggingRule RegisterAppFile(IAppFolderInfo appFolderInfo, string name, string fileName, int maxArchiveFiles, LogLevel minLogLevel)

@ -26,7 +26,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DelugeTests
Subject.Definition = new DownloadClientDefinition(); Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new DelugeSettings() Subject.Definition.Settings = new DelugeSettings()
{ {
TvCategory = null MovieCategory = null
}; };
_queued = new DelugeTorrent _queued = new DelugeTorrent

@ -25,7 +25,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
Port = 2222, Port = 2222,
Username = "admin", Username = "admin",
Password = "pass", Password = "pass",
TvCategory = "tv" MovieCategory = "movies-radarr"
}; };
Mocker.GetMock<ITorrentFileInfoReader>() Mocker.GetMock<ITorrentFileInfoReader>()

@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.RTorrentTests
Subject.Definition = new DownloadClientDefinition(); Subject.Definition = new DownloadClientDefinition();
Subject.Definition.Settings = new RTorrentSettings() Subject.Definition.Settings = new RTorrentSettings()
{ {
TvCategory = null MovieCategory = null
}; };
_downloading = new RTorrentTorrent _downloading = new RTorrentTorrent

@ -112,12 +112,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
protected void GivenTvCategory() protected void GivenTvCategory()
{ {
_settings.TvCategory = "sonarr"; _settings.MovieCategory = "radarr";
} }
protected void GivenTvDirectory() protected void GivenTvDirectory()
{ {
_settings.TvDirectory = @"C:/Downloads/Finished/sonarr"; _settings.MovieDirectory = @"C:/Downloads/Finished/radarr";
} }
protected void GivenFailedDownload() protected void GivenFailedDownload()

@ -42,14 +42,14 @@ namespace NzbDrone.Core.Test.IndexerTests.OmgwtfnzbsTests
var releaseInfo = releases.First(); var releaseInfo = releases.First();
releaseInfo.Title.Should().Be("Stephen.Fry.Gadget.Man.S01E05.HDTV.x264-C4TV"); releaseInfo.Title.Should().Be("Un.Petit.Boulot.2016.FRENCH.720p.BluRay.DTS.x264-LOST");
releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Usenet); releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Usenet);
releaseInfo.DownloadUrl.Should().Be("http://api.omgwtfnzbs.org/sn.php?id=OAl4g&user=nzbdrone&api=nzbdrone"); releaseInfo.DownloadUrl.Should().Be("https://api.omgwtfnzbs.me/nzb/?id=8a2Bw&user=nzbdrone&api=nzbdrone");
releaseInfo.InfoUrl.Should().Be("http://omgwtfnzbs.org/details.php?id=OAl4g"); releaseInfo.InfoUrl.Should().Be("https://omgwtfnzbs.me/details.php?id=8a2Bw");
releaseInfo.CommentUrl.Should().BeNullOrEmpty(); releaseInfo.CommentUrl.Should().BeNullOrEmpty();
releaseInfo.Indexer.Should().Be(Subject.Definition.Name); releaseInfo.Indexer.Should().Be(Subject.Definition.Name);
releaseInfo.PublishDate.Should().Be(DateTime.Parse("2012/12/17 23:30:13")); releaseInfo.PublishDate.Should().Be(DateTime.Parse("2017/01/09 00:16:54"));
releaseInfo.Size.Should().Be(236822906); releaseInfo.Size.Should().Be(5354909355);
} }
} }
} }

@ -0,0 +1,45 @@
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(115)]
public class update_movie_sorttitle : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
// Create.Column("SortTitle").OnTable("Series").AsString().Nullable();
Execute.WithConnection(SetSortTitles);
}
private void SetSortTitles(IDbConnection conn, IDbTransaction tran)
{
using (IDbCommand getSeriesCmd = conn.CreateCommand())
{
getSeriesCmd.Transaction = tran;
getSeriesCmd.CommandText = @"SELECT Id, Title FROM Movies";
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
{
while (seriesReader.Read())
{
var id = seriesReader.GetInt32(0);
var title = seriesReader.GetString(1);
var sortTitle = Parser.Parser.NormalizeTitle(title).ToLower();
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE Movies SET SortTitle = ? WHERE Id = ?";
updateCmd.AddParameter(sortTitle);
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}

@ -0,0 +1,44 @@
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(116)]
public class update_movie_sorttitle_again : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(SetSortTitles);
}
private void SetSortTitles(IDbConnection conn, IDbTransaction tran)
{
using (IDbCommand getSeriesCmd = conn.CreateCommand())
{
getSeriesCmd.Transaction = tran;
getSeriesCmd.CommandText = @"SELECT Id, Title FROM Movies";
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
{
while (seriesReader.Read())
{
var id = seriesReader.GetInt32(0);
var title = seriesReader.GetString(1);
var sortTitle = Parser.Parser.NormalizeTitle(title).ToLower();
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE Movies SET SortTitle = ? WHERE Id = ?";
updateCmd.AddParameter(sortTitle);
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}

@ -0,0 +1,52 @@
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(117)]
public class update_movie_file : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Create.Column("Edition").OnTable("MovieFiles").AsString().Nullable();
//Execute.WithConnection(SetSortTitles);
}
private void SetSortTitles(IDbConnection conn, IDbTransaction tran)
{
using (IDbCommand getSeriesCmd = conn.CreateCommand())
{
getSeriesCmd.Transaction = tran;
getSeriesCmd.CommandText = @"SELECT Id, RelativePath FROM MovieFiles";
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
{
while (seriesReader.Read())
{
var id = seriesReader.GetInt32(0);
var relativePath = seriesReader.GetString(1);
var result = Parser.Parser.ParseMovieTitle(relativePath);
var edition = "";
if (result != null)
{
edition = Parser.Parser.ParseMovieTitle(relativePath).Edition;
}
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE MovieFiles SET Edition = ? WHERE Id = ?";
updateCmd.AddParameter(edition);
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}

@ -0,0 +1,71 @@
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
using System.Text;
using System.Text.RegularExpressions;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(118)]
public class update_movie_slug : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(SetTitleSlug);
}
private void SetTitleSlug(IDbConnection conn, IDbTransaction tran)
{
using (IDbCommand getSeriesCmd = conn.CreateCommand())
{
getSeriesCmd.Transaction = tran;
getSeriesCmd.CommandText = @"SELECT Id, Title, Year FROM Movies";
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
{
while (seriesReader.Read())
{
var id = seriesReader.GetInt32(0);
var title = seriesReader.GetString(1);
var year = seriesReader.GetInt32(2);
var titleSlug = ToUrlSlug(title + "-" + year);
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE Movies SET TitleSlug = ? WHERE Id = ?";
updateCmd.AddParameter(titleSlug);
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
public static string ToUrlSlug(string value)
{
//First to lower case
value = value.ToLowerInvariant();
//Remove all accents
var bytes = Encoding.GetEncoding("Cyrillic").GetBytes(value);
value = Encoding.ASCII.GetString(bytes);
//Replace spaces
value = Regex.Replace(value, @"\s", "-", RegexOptions.Compiled);
//Remove invalid chars
value = Regex.Replace(value, @"[^a-z0-9\s-_]", "", RegexOptions.Compiled);
//Trim dashes from end
value = value.Trim('-', '_');
//Replace double occurences of - or _
value = Regex.Replace(value, @"([-_]){2,}", "$1", RegexOptions.Compiled);
return value;
}
}
}

@ -24,8 +24,6 @@ namespace NzbDrone.Core.DecisionEngine
{ {
CompareQuality, CompareQuality,
CompareProtocol, CompareProtocol,
CompareEpisodeCount,
CompareEpisodeNumber,
ComparePeersIfTorrent, ComparePeersIfTorrent,
CompareAgeIfUsenet, CompareAgeIfUsenet,
CompareSize CompareSize
@ -56,6 +54,12 @@ namespace NzbDrone.Core.DecisionEngine
private int CompareQuality(DownloadDecision x, DownloadDecision y) private int CompareQuality(DownloadDecision x, DownloadDecision y)
{ {
if (x.IsForMovie && y.IsForMovie)
{
return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.Movie.Profile.Value.Items.FindIndex(v => v.Quality == remoteEpisode.ParsedMovieInfo.Quality.Quality)),
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.ParsedMovieInfo.Quality.Revision.Real),
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.ParsedMovieInfo.Quality.Revision.Version));
}
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.Profile.Value.Items.FindIndex(v => v.Quality == remoteEpisode.ParsedEpisodeInfo.Quality.Quality)), return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.Profile.Value.Items.FindIndex(v => v.Quality == remoteEpisode.ParsedEpisodeInfo.Quality.Quality)),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Real), CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Real),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version)); CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version));
@ -63,6 +67,7 @@ namespace NzbDrone.Core.DecisionEngine
private int CompareProtocol(DownloadDecision x, DownloadDecision y) private int CompareProtocol(DownloadDecision x, DownloadDecision y)
{ {
var result = CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => var result = CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
{ {
var delayProfile = _delayProfileService.BestForTags(remoteEpisode.Series.Tags); var delayProfile = _delayProfileService.BestForTags(remoteEpisode.Series.Tags);
@ -70,13 +75,22 @@ namespace NzbDrone.Core.DecisionEngine
return downloadProtocol == delayProfile.PreferredProtocol; return downloadProtocol == delayProfile.PreferredProtocol;
}); });
if (x.IsForMovie)
{
result = CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{
var delayProfile = _delayProfileService.BestForTags(remoteEpisode.Movie.Tags);
var downloadProtocol = remoteEpisode.Release.DownloadProtocol;
return downloadProtocol == delayProfile.PreferredProtocol;
});
}
return result; return result;
} }
private int CompareEpisodeCount(DownloadDecision x, DownloadDecision y) private int CompareEpisodeCount(DownloadDecision x, DownloadDecision y)
{ {
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.FullSeason), return 0;
CompareByReverse(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Episodes.Count));
} }
private int CompareEpisodeNumber(DownloadDecision x, DownloadDecision y) private int CompareEpisodeNumber(DownloadDecision x, DownloadDecision y)
@ -88,20 +102,20 @@ namespace NzbDrone.Core.DecisionEngine
{ {
// Different protocols should get caught when checking the preferred protocol, // Different protocols should get caught when checking the preferred protocol,
// since we're dealing with the same series in our comparisions // since we're dealing with the same series in our comparisions
if (x.RemoteEpisode.Release.DownloadProtocol != DownloadProtocol.Torrent || if (x.RemoteMovie.Release.DownloadProtocol != DownloadProtocol.Torrent ||
y.RemoteEpisode.Release.DownloadProtocol != DownloadProtocol.Torrent) y.RemoteMovie.Release.DownloadProtocol != DownloadProtocol.Torrent)
{ {
return 0; return 0;
} }
return CompareAll( return CompareAll(
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{ {
var seeders = TorrentInfo.GetSeeders(remoteEpisode.Release); var seeders = TorrentInfo.GetSeeders(remoteEpisode.Release);
return seeders.HasValue && seeders.Value > 0 ? Math.Round(Math.Log10(seeders.Value)) : 0; return seeders.HasValue && seeders.Value > 0 ? Math.Round(Math.Log10(seeders.Value)) : 0;
}), }),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{ {
var peers = TorrentInfo.GetPeers(remoteEpisode.Release); var peers = TorrentInfo.GetPeers(remoteEpisode.Release);
@ -117,7 +131,7 @@ namespace NzbDrone.Core.DecisionEngine
return 0; return 0;
} }
return CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{ {
var ageHours = remoteEpisode.Release.AgeHours; var ageHours = remoteEpisode.Release.AgeHours;
var age = remoteEpisode.Release.Age; var age = remoteEpisode.Release.Age;
@ -145,7 +159,7 @@ namespace NzbDrone.Core.DecisionEngine
{ {
// TODO: Is smaller better? Smaller for usenet could mean no par2 files. // TODO: Is smaller better? Smaller for usenet could mean no par2 files.
return CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Release.Size.Round(200.Megabytes())); return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.Release.Size.Round(200.Megabytes()));
} }
} }
} }

@ -265,7 +265,6 @@ namespace NzbDrone.Core.DecisionEngine
e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson()); e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson());
_logger.Error(e, "Couldn't evaluate decision on " + remoteEpisode.Release.Title + ", with spec: " + spec.GetType().Name); _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 new Rejection(string.Format("{0}: {1}", spec.GetType().Name, e.Message));//TODO UPDATE SPECS!
return null;
} }
return null; return null;

@ -64,7 +64,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Movie.Profile, remoteEpisode.ParsedMovieInfo.Quality, subject.ParsedMovieInfo.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); return Decision.Reject("Quality for release in queue already meets cutoff: {0}", remoteEpisode.ParsedMovieInfo.Quality);
} }
_logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0}", remoteEpisode.ParsedMovieInfo.Quality); _logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0}", remoteEpisode.ParsedMovieInfo.Quality);

@ -26,10 +26,10 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
private static readonly TorrentBlackholeSettingsValidator Validator = new TorrentBlackholeSettingsValidator(); private static readonly TorrentBlackholeSettingsValidator Validator = new TorrentBlackholeSettingsValidator();
[FieldDefinition(0, Label = "Torrent Folder", Type = FieldType.Path, HelpText = "Folder in which Sonarr will store the .torrent file")] [FieldDefinition(0, Label = "Torrent Folder", Type = FieldType.Path, HelpText = "Folder in which Radarr will store the .torrent file")]
public string TorrentFolder { get; set; } public string TorrentFolder { get; set; }
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Sonarr should import completed downloads")] [FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Radarr should import completed downloads")]
public string WatchFolder { get; set; } public string WatchFolder { get; set; }
[DefaultValue(false)] [DefaultValue(false)]
@ -39,7 +39,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
[DefaultValue(false)] [DefaultValue(false)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[FieldDefinition(3, Label = "Read Only", Type = FieldType.Checkbox, HelpText = "Instead of moving files this will instruct Sonarr to Copy or Hardlink (depending on settings/system configuration)")] [FieldDefinition(3, Label = "Read Only", Type = FieldType.Checkbox, HelpText = "Instead of moving files this will instruct Radarr to Copy or Hardlink (depending on settings/system configuration)")]
public bool ReadOnly { get; set; } public bool ReadOnly { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

@ -19,10 +19,10 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
{ {
private static readonly UsenetBlackholeSettingsValidator Validator = new UsenetBlackholeSettingsValidator(); private static readonly UsenetBlackholeSettingsValidator Validator = new UsenetBlackholeSettingsValidator();
[FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "Folder in which Sonarr will store the .nzb file")] [FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "Folder in which Radarr will store the .nzb file")]
public string NzbFolder { get; set; } public string NzbFolder { get; set; }
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Sonarr should import completed downloads")] [FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Radarr should import completed downloads")]
public string WatchFolder { get; set; } public string WatchFolder { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

@ -31,25 +31,17 @@ namespace NzbDrone.Core.Download.Clients.Deluge
_proxy = proxy; _proxy = proxy;
} }
protected override string AddFromMagnetLink(RemoteMovie remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
{ {
var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings); var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings);
if (!Settings.TvCategory.IsNullOrWhiteSpace()) if (!Settings.MovieCategory.IsNullOrWhiteSpace())
{ {
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings); _proxy.SetLabel(actualHash, Settings.MovieCategory, Settings);
} }
_proxy.SetTorrentConfiguration(actualHash, "remove_at_ratio", false, 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(); return actualHash.ToUpper();
} }
@ -57,66 +49,24 @@ namespace NzbDrone.Core.Download.Clients.Deluge
{ {
var actualHash = _proxy.AddTorrentFromFile(filename, fileContent, Settings); var actualHash = _proxy.AddTorrentFromFile(filename, fileContent, Settings);
if (!Settings.TvCategory.IsNullOrWhiteSpace()) if (!Settings.MovieCategory.IsNullOrWhiteSpace())
{ {
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings); _proxy.SetLabel(actualHash, Settings.MovieCategory, Settings);
} }
_proxy.SetTorrentConfiguration(actualHash, "remove_at_ratio", false, 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(); 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); throw new NotImplementedException("Episodes are not working with Radarr");
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(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent) protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
{ {
var actualHash = _proxy.AddTorrentFromFile(filename, fileContent, Settings); throw new NotImplementedException("Episodes are not working with Radarr");
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();
} }
public override string Name => "Deluge"; public override string Name => "Deluge";
@ -127,9 +77,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
try try
{ {
if (!Settings.TvCategory.IsNullOrWhiteSpace()) if (!Settings.MovieCategory.IsNullOrWhiteSpace())
{ {
torrents = _proxy.GetTorrentsByLabel(Settings.TvCategory, Settings); torrents = _proxy.GetTorrentsByLabel(Settings.MovieCategory, Settings);
} }
else else
{ {
@ -149,7 +99,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
var item = new DownloadClientItem(); var item = new DownloadClientItem();
item.DownloadId = torrent.Hash.ToUpper(); item.DownloadId = torrent.Hash.ToUpper();
item.Title = torrent.Name; item.Title = torrent.Name;
item.Category = Settings.TvCategory; item.Category = Settings.MovieCategory;
item.DownloadClient = Definition.Name; item.DownloadClient = Definition.Name;
@ -280,7 +230,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
private ValidationFailure TestCategory() private ValidationFailure TestCategory()
{ {
if (Settings.TvCategory.IsNullOrWhiteSpace()) if (Settings.MovieCategory.IsNullOrWhiteSpace())
{ {
return null; return null;
} }
@ -297,16 +247,16 @@ namespace NzbDrone.Core.Download.Clients.Deluge
var labels = _proxy.GetAvailableLabels(Settings); var labels = _proxy.GetAvailableLabels(Settings);
if (!labels.Contains(Settings.TvCategory)) if (!labels.Contains(Settings.MovieCategory))
{ {
_proxy.AddLabel(Settings.TvCategory, Settings); _proxy.AddLabel(Settings.MovieCategory, Settings);
labels = _proxy.GetAvailableLabels(Settings); labels = _proxy.GetAvailableLabels(Settings);
if (!labels.Contains(Settings.TvCategory)) if (!labels.Contains(Settings.MovieCategory))
{ {
return new NzbDroneValidationFailure("TvCategory", "Configuration of label failed") return new NzbDroneValidationFailure("MovieCategory", "Configuration of label failed")
{ {
DetailedDescription = "Sonarr as unable to add the label to Deluge." DetailedDescription = "Radarr as unable to add the label to Deluge."
}; };
} }
} }

@ -12,7 +12,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
RuleFor(c => c.Host).ValidHost(); RuleFor(c => c.Host).ValidHost();
RuleFor(c => c.Port).GreaterThan(0); RuleFor(c => c.Port).GreaterThan(0);
RuleFor(c => c.TvCategory).Matches("^[-a-z]*$").WithMessage("Allowed characters a-z and -"); RuleFor(c => c.MovieCategory).Matches("^[-a-z]*$").WithMessage("Allowed characters a-z and -");
} }
} }
@ -25,7 +25,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
Host = "localhost"; Host = "localhost";
Port = 8112; Port = 8112;
Password = "deluge"; Password = "deluge";
TvCategory = "movie-radarr"; MovieCategory = "movie-radarr";
} }
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)] [FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
@ -41,15 +41,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr 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 MovieCategory { 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 = "Use SSL", Type = FieldType.Checkbox)]
public int RecentTvPriority { get; set; }
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
public int OlderTvPriority { get; set; }
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox)]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

@ -43,7 +43,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
[FieldDefinition(2, Label = "API Key", Type = FieldType.Textbox)] [FieldDefinition(2, Label = "API Key", Type = FieldType.Textbox)]
public string ApiKey { get; set; } public string ApiKey { get; set; }
[FieldDefinition(3, Label = "Group", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")] [FieldDefinition(3, Label = "Group", 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(4, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] [FieldDefinition(4, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]

@ -337,7 +337,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0") return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0")
{ {
InfoLink = string.Format("http://{0}:{1}/", Settings.Host, Settings.Port), InfoLink = string.Format("http://{0}:{1}/", Settings.Host, Settings.Port),
DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Sonarr from seeing completed downloads." DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Radarr from seeing completed downloads."
}; };
} }

@ -45,7 +45,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
[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(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] [FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]

@ -33,81 +33,35 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
{ {
_proxy.AddTorrentFromUrl(magnetLink, Settings); throw new NotImplementedException("Episodes are not working with Radarr");
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{
_proxy.SetTorrentLabel(hash.ToLower(), Settings.TvCategory, Settings);
}
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)QBittorrentPriority.First ||
!isRecentEpisode && Settings.OlderTvPriority == (int)QBittorrentPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash.ToLower(), Settings);
}
return hash;
} }
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, Byte[] fileContent) protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, Byte[] fileContent)
{ {
_proxy.AddTorrentFromFile(filename, fileContent, Settings); throw new NotImplementedException("Episodes are not working with Radarr");
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{
_proxy.SetTorrentLabel(hash.ToLower(), Settings.TvCategory, Settings);
}
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)QBittorrentPriority.First ||
!isRecentEpisode && Settings.OlderTvPriority == (int)QBittorrentPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash.ToLower(), Settings);
}
return hash;
} }
protected override string AddFromMagnetLink(RemoteMovie remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
{ {
_proxy.AddTorrentFromUrl(magnetLink, Settings); _proxy.AddTorrentFromUrl(magnetLink, Settings);
if (Settings.TvCategory.IsNotNullOrWhiteSpace()) if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
{ {
_proxy.SetTorrentLabel(hash.ToLower(), Settings.TvCategory, Settings); _proxy.SetTorrentLabel(hash.ToLower(), Settings.MovieCategory, Settings);
} }
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)QBittorrentPriority.First ||
!isRecentEpisode && Settings.OlderTvPriority == (int)QBittorrentPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash.ToLower(), Settings);
}*/ //TODO: Maybe reimplement for movies
return hash; return hash;
} }
protected override string AddFromTorrentFile(RemoteMovie remoteEpisode, string hash, string filename, Byte[] fileContent) protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, Byte[] fileContent)
{ {
_proxy.AddTorrentFromFile(filename, fileContent, Settings); _proxy.AddTorrentFromFile(filename, fileContent, Settings);
if (Settings.TvCategory.IsNotNullOrWhiteSpace()) if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
{ {
_proxy.SetTorrentLabel(hash.ToLower(), Settings.TvCategory, Settings); _proxy.SetTorrentLabel(hash.ToLower(), Settings.MovieCategory, Settings);
} }
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)QBittorrentPriority.First ||
!isRecentEpisode && Settings.OlderTvPriority == (int)QBittorrentPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash.ToLower(), Settings);
}*/
return hash; return hash;
} }
@ -236,7 +190,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
else if (version < 6) else if (version < 6)
{ {
// API version 6 introduced support for labels // API version 6 introduced support for labels
if (Settings.TvCategory.IsNotNullOrWhiteSpace()) if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
{ {
return new NzbDroneValidationFailure("Category", "Category is not supported") return new NzbDroneValidationFailure("Category", "Category is not supported")
{ {
@ -244,7 +198,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
}; };
} }
} }
else if (Settings.TvCategory.IsNullOrWhiteSpace()) else if (Settings.MovieCategory.IsNullOrWhiteSpace())
{ {
// warn if labels are supported, but category is not provided // warn if labels are supported, but category is not provided
return new NzbDroneValidationFailure("TvCategory", "Category is recommended") return new NzbDroneValidationFailure("TvCategory", "Category is recommended")

@ -58,8 +58,8 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
public List<QBittorrentTorrent> GetTorrents(QBittorrentSettings settings) public List<QBittorrentTorrent> GetTorrents(QBittorrentSettings settings)
{ {
var request = BuildRequest(settings).Resource("/query/torrents") var request = BuildRequest(settings).Resource("/query/torrents")
.AddQueryParam("label", settings.TvCategory) .AddQueryParam("label", settings.MovieCategory)
.AddQueryParam("category", settings.TvCategory); .AddQueryParam("category", settings.MovieCategory);
var response = ProcessRequest<List<QBittorrentTorrent>>(request, settings); var response = ProcessRequest<List<QBittorrentTorrent>>(request, settings);

@ -22,7 +22,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
{ {
Host = "localhost"; Host = "localhost";
Port = 9091; Port = 9091;
TvCategory = "movie-radarr"; MovieCategory = "movie-radarr";
} }
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)] [FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
@ -38,16 +38,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr 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 MovieCategory { get; set; }
//Todo: update this shit. [FieldDefinition(5, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")]
[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; }
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
public int OlderTvPriority { get; set; }
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

@ -30,13 +30,13 @@ 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)", RegexOptions.Compiled);
protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContents) 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(fileContents, filename, category, priority, Settings); var response = _proxy.DownloadNzb(fileContents, filename, category, priority, Settings);
if (response != null && response.Ids.Any()) if (response != null && response.Ids.Any())
@ -284,110 +284,103 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
failures.AddIfNotNull(TestCategory()); failures.AddIfNotNull(TestCategory());
} }
private bool HasVersion(int major, int minor, int patch = 0, string candidate = null) private bool HasVersion(int major, int minor, int patch = 0)
{ {
candidate = candidate ?? string.Empty; var rawVersion = _proxy.GetVersion(Settings);
var version = ParseVersion(rawVersion);
var version = _proxy.GetVersion(Settings);
var parsed = VersionRegex.Match(version);
int actualMajor; if (version == null)
int actualMinor;
int actualPatch;
string actualCandidate;
if (!parsed.Success)
{ {
if (!version.Equals("develop", StringComparison.InvariantCultureIgnoreCase)) return false;
{
return false;
}
actualMajor = 1;
actualMinor = 1;
actualPatch = 0;
actualCandidate = null;
}
else
{
actualMajor = Convert.ToInt32(parsed.Groups["major"].Value);
actualMinor = Convert.ToInt32(parsed.Groups["minor"].Value);
actualPatch = Convert.ToInt32(parsed.Groups["patch"].Value.Replace("x", ""));
actualCandidate = parsed.Groups["candidate"].Value.ToUpper();
} }
if (actualMajor > major) if (version.Major > major)
{ {
return true; return true;
} }
else if (actualMajor < major) else if (version.Major < major)
{ {
return false; return false;
} }
if (actualMinor > minor) if (version.Minor > minor)
{ {
return true; return true;
} }
else if (actualMinor < minor) else if (version.Minor < minor)
{ {
return false; return false;
} }
if (actualPatch > patch) if (version.Build > patch)
{ {
return true; return true;
} }
else if (actualPatch < patch) else if (version.Build < patch)
{ {
return false; return false;
} }
if (actualCandidate.IsNullOrWhiteSpace()) return true;
{ }
return true;
} private Version ParseVersion(string version)
else if (candidate.IsNullOrWhiteSpace()) {
var parsed = VersionRegex.Match(version);
int major;
int minor;
int patch;
if (parsed.Success)
{ {
return false; major = Convert.ToInt32(parsed.Groups["major"].Value);
minor = Convert.ToInt32(parsed.Groups["minor"].Value);
patch = Convert.ToInt32(parsed.Groups["patch"].Value.Replace("x", "0"));
} }
else else
{ {
return actualCandidate.CompareTo(candidate) > 0; if (!version.Equals("develop", StringComparison.InvariantCultureIgnoreCase))
{
return null;
}
major = 1;
minor = 1;
patch = 0;
} }
return new Version(major, minor, patch);
} }
private ValidationFailure TestConnectionAndVersion() private ValidationFailure TestConnectionAndVersion()
{ {
try try
{ {
var version = _proxy.GetVersion(Settings); var rawVersion = _proxy.GetVersion(Settings);
var parsed = VersionRegex.Match(version); var version = ParseVersion(rawVersion);
if (!parsed.Success) if (version == null)
{ {
if (version.Equals("develop", StringComparison.InvariantCultureIgnoreCase))
{
return new NzbDroneValidationFailure("Version", "Sabnzbd develop version, assuming version 1.1.0 or higher.")
{
IsWarning = true,
DetailedDescription = "Sonarr may not be able to support new features added to SABnzbd when running develop versions."
};
}
return new ValidationFailure("Version", "Unknown Version: " + version); return new ValidationFailure("Version", "Unknown Version: " + version);
} }
var major = Convert.ToInt32(parsed.Groups["major"].Value); if (rawVersion.Equals("develop", StringComparison.InvariantCultureIgnoreCase))
var minor = Convert.ToInt32(parsed.Groups["minor"].Value); {
return new NzbDroneValidationFailure("Version", "Sabnzbd develop version, assuming version 1.1.0 or higher.")
{
IsWarning = true,
DetailedDescription = "Radarr may not be able to support new features added to SABnzbd when running develop versions."
};
}
if (major >= 1) if (version.Major >= 1)
{ {
return null; return null;
} }
if (minor >= 7) if (version.Minor >= 7)
{ {
return null; return null;
} }
@ -431,7 +424,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("", "Disable 'Check before download' option in Sabnbzd") return new NzbDroneValidationFailure("", "Disable 'Check before download' option in Sabnbzd")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/switches/", Settings.Host, Settings.Port), InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/switches/", Settings.Host, Settings.Port),
DetailedDescription = "Using Check before download affects Sonarr ability to track new downloads. Also Sabnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective." DetailedDescription = "Using Check before download affects Radarr ability to track new downloads. Also Sabnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective."
}; };
} }
@ -450,7 +443,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Enable Job folders") return new NzbDroneValidationFailure("TvCategory", "Enable Job folders")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/categories/", Settings.Host, Settings.Port), InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/categories/", Settings.Host, Settings.Port),
DetailedDescription = "Sonarr prefers each download to have a separate folder. With * appended to the Folder/Path Sabnzbd will not create these job folders. Go to Sabnzbd to fix it." DetailedDescription = "Radarr prefers each download to have a separate folder. With * appended to the Folder/Path Sabnzbd will not create these job folders. Go to Sabnzbd to fix it."
}; };
} }
} }
@ -475,7 +468,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Disable TV Sorting") return new NzbDroneValidationFailure("TvCategory", "Disable TV Sorting")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port), InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
DetailedDescription = "You must disable Sabnzbd TV Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it." DetailedDescription = "You must disable Sabnzbd TV Sorting for the category Radarr uses to prevent import issues. Go to Sabnzbd to fix it."
}; };
} }
} }
@ -489,7 +482,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Disable Movie Sorting") return new NzbDroneValidationFailure("TvCategory", "Disable Movie Sorting")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port), InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
DetailedDescription = "You must disable Sabnzbd Movie Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it." DetailedDescription = "You must disable Sabnzbd Movie Sorting for the category Radarr uses to prevent import issues. Go to Sabnzbd to fix it."
}; };
} }
} }
@ -503,7 +496,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Disable Date Sorting") return new NzbDroneValidationFailure("TvCategory", "Disable Date Sorting")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port), InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
DetailedDescription = "You must disable Sabnzbd Date Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it." DetailedDescription = "You must disable Sabnzbd Date Sorting for the category Radarr uses to prevent import issues. Go to Sabnzbd to fix it."
}; };
} }
} }

@ -58,7 +58,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)] [FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")] [FieldDefinition(5, 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(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] [FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]

@ -54,21 +54,21 @@ namespace NzbDrone.Core.Download.Clients.Transmission
var outputPath = new OsPath(torrent.DownloadDir); var outputPath = new OsPath(torrent.DownloadDir);
if (Settings.TvDirectory.IsNotNullOrWhiteSpace()) if (Settings.MovieDirectory.IsNotNullOrWhiteSpace())
{ {
if (!new OsPath(Settings.TvDirectory).Contains(outputPath)) continue; if (!new OsPath(Settings.MovieDirectory).Contains(outputPath)) continue;
} }
else if (Settings.TvCategory.IsNotNullOrWhiteSpace()) else if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
{ {
var directories = outputPath.FullPath.Split('\\', '/'); var directories = outputPath.FullPath.Split('\\', '/');
if (!directories.Contains(Settings.TvCategory)) continue; if (!directories.Contains(Settings.MovieCategory)) continue;
} }
outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, outputPath); outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, outputPath);
var item = new DownloadClientItem(); var item = new DownloadClientItem();
item.DownloadId = torrent.HashString.ToUpper(); item.DownloadId = torrent.HashString.ToUpper();
item.Category = Settings.TvCategory; item.Category = Settings.MovieCategory;
item.Title = torrent.Name; item.Title = torrent.Name;
item.DownloadClient = Definition.Name; item.DownloadClient = Definition.Name;
@ -123,9 +123,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission
var config = _proxy.GetConfig(Settings); var config = _proxy.GetConfig(Settings);
var destDir = config.GetValueOrDefault("download-dir") as string; var destDir = config.GetValueOrDefault("download-dir") as string;
if (Settings.TvCategory.IsNotNullOrWhiteSpace()) if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
{ {
destDir = string.Format("{0}/.{1}", destDir, Settings.TvCategory); destDir = string.Format("{0}/.{1}", destDir, Settings.MovieCategory);
} }
return new DownloadClientStatus return new DownloadClientStatus
@ -137,56 +137,23 @@ namespace NzbDrone.Core.Download.Clients.Transmission
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
{ {
_proxy.AddTorrentFromUrl(magnetLink, GetDownloadDirectory(), Settings); throw new NotImplementedException("Episodes are not working with Radarr");
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)TransmissionPriority.First ||
!isRecentEpisode && Settings.OlderTvPriority == (int)TransmissionPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash, Settings);
}
return hash;
} }
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent) protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
{ {
_proxy.AddTorrentFromData(fileContent, GetDownloadDirectory(), Settings); throw new NotImplementedException("Episodes are not working with Radarr");
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)TransmissionPriority.First ||
!isRecentEpisode && Settings.OlderTvPriority == (int)TransmissionPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash, Settings);
}
return hash;
} }
protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
{ {
_proxy.AddTorrentFromUrl(magnetLink, GetDownloadDirectory(), Settings); _proxy.AddTorrentFromUrl(magnetLink, GetDownloadDirectory(), Settings);
if (remoteMovie.Release.Age < 14 && Settings.RecentTvPriority == (int)TransmissionPriority.First ||
remoteMovie.Release.Age > 14 && Settings.OlderTvPriority == (int)TransmissionPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash, Settings);
}
return hash; return hash;
} }
protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, byte[] fileContent) protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, byte[] fileContent)
{ {
_proxy.AddTorrentFromData(fileContent, GetDownloadDirectory(), Settings); _proxy.AddTorrentFromData(fileContent, GetDownloadDirectory(), Settings);
if (remoteMovie.Release.Age < 14 && Settings.RecentTvPriority == (int)TransmissionPriority.First ||
remoteMovie.Release.Age > 14 && Settings.OlderTvPriority == (int)TransmissionPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash, Settings);
}
return hash; return hash;
} }
@ -204,16 +171,16 @@ namespace NzbDrone.Core.Download.Clients.Transmission
protected string GetDownloadDirectory() protected string GetDownloadDirectory()
{ {
if (Settings.TvDirectory.IsNotNullOrWhiteSpace()) if (Settings.MovieDirectory.IsNotNullOrWhiteSpace())
{ {
return Settings.TvDirectory; return Settings.MovieDirectory;
} }
else if (Settings.TvCategory.IsNotNullOrWhiteSpace()) else if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
{ {
var config = _proxy.GetConfig(Settings); var config = _proxy.GetConfig(Settings);
var destDir = (string)config.GetValueOrDefault("download-dir"); var destDir = (string)config.GetValueOrDefault("download-dir");
return string.Format("{0}/{1}", destDir.TrimEnd('/'), Settings.TvCategory); return string.Format("{0}/{1}", destDir.TrimEnd('/'), Settings.MovieCategory);
} }
else else
{ {
@ -232,7 +199,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
_logger.Error(ex, ex.Message); _logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Username", "Authentication failure") return new NzbDroneValidationFailure("Username", "Authentication failure")
{ {
DetailedDescription = string.Format("Please verify your username and password. Also verify if the host running Sonarr isn't blocked from accessing {0} by WhiteList limitations in the {0} configuration.", Name) DetailedDescription = string.Format("Please verify your username and password. Also verify if the host running Radarr isn't blocked from accessing {0} by WhiteList limitations in the {0} configuration.", Name)
}; };
} }
catch (WebException ex) catch (WebException ex)

@ -16,10 +16,10 @@ namespace NzbDrone.Core.Download.Clients.Transmission
RuleFor(c => c.UrlBase).ValidUrlBase(); RuleFor(c => c.UrlBase).ValidUrlBase();
RuleFor(c => c.TvCategory).Matches(@"^\.?[-a-z]*$", RegexOptions.IgnoreCase).WithMessage("Allowed characters a-z and -"); RuleFor(c => c.MovieCategory).Matches(@"^\.?[-a-z]*$", RegexOptions.IgnoreCase).WithMessage("Allowed characters a-z and -");
RuleFor(c => c.TvCategory).Empty() RuleFor(c => c.MovieCategory).Empty()
.When(c => c.TvDirectory.IsNotNullOrWhiteSpace()) .When(c => c.MovieDirectory.IsNotNullOrWhiteSpace())
.WithMessage("Cannot use Category and Directory"); .WithMessage("Cannot use Category and Directory");
} }
} }
@ -50,19 +50,13 @@ namespace NzbDrone.Core.Download.Clients.Transmission
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)] [FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional. Creates a [category] subdirectory in the output directory.")] [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional. Creates a [category] subdirectory in the output directory.")]
public string TvCategory { get; set; } public string MovieCategory { get; set; }
[FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Transmission location")] [FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Transmission location")]
public string TvDirectory { get; set; } public string MovieDirectory { get; set; }
[FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing movies that we're released within the last 14 days")] [FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox)]
public int RecentTvPriority { get; set; }
[FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing movies that we're released over 14 days ago")]
public int OlderTvPriority { get; set; }
[FieldDefinition(9, Label = "Use SSL", Type = FieldType.Checkbox)]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

@ -37,51 +37,23 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
_rTorrentDirectoryValidator = rTorrentDirectoryValidator; _rTorrentDirectoryValidator = rTorrentDirectoryValidator;
} }
protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
{ {
_proxy.AddTorrentFromUrl(magnetLink, Settings); throw new NotImplementedException("Episodes are not working with Radarr");
}
// Download the magnet to the appropriate directory.
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
//SetPriority(remoteEpisode, hash);
SetDownloadDirectory(hash);
// Once the magnet meta download finishes, rTorrent replaces it with the actual torrent download with default settings.
// Schedule an event to apply the appropriate settings when that happens.
// var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);
//_proxy.SetDeferredMagnetProperties(hash, Settings.TvCategory, Settings.TvDirectory, priority, Settings);
_proxy.StartTorrent(hash, Settings);
// Wait for the magnet to be resolved.
var tries = 10;
var retryDelay = 500;
if (WaitForTorrent(hash, tries, retryDelay))
{
return hash;
}
else
{
_logger.Warn("rTorrent could not resolve magnet within {0} seconds, download may remain stuck: {1}.", tries * retryDelay / 1000, magnetLink);
return hash; protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
} {
throw new NotImplementedException("Episodes are not working with Radarr");
} }
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
{ {
_proxy.AddTorrentFromUrl(magnetLink, Settings); _proxy.AddTorrentFromUrl(magnetLink, Settings);
// Download the magnet to the appropriate directory. // Download the magnet to the appropriate directory.
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings); _proxy.SetTorrentLabel(hash, Settings.MovieCategory, Settings);
SetPriority(remoteEpisode, hash);
SetDownloadDirectory(hash); SetDownloadDirectory(hash);
// Once the magnet meta download finishes, rTorrent replaces it with the actual torrent download with default settings.
// Schedule an event to apply the appropriate settings when that happens.
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);
_proxy.SetDeferredMagnetProperties(hash, Settings.TvCategory, Settings.TvDirectory, priority, Settings);
_proxy.StartTorrent(hash, Settings); _proxy.StartTorrent(hash, Settings);
// Wait for the magnet to be resolved. // Wait for the magnet to be resolved.
@ -107,13 +79,9 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
var retryDelay = 100; var retryDelay = 100;
if (WaitForTorrent(hash, tries, retryDelay)) if (WaitForTorrent(hash, tries, retryDelay))
{ {
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings); _proxy.SetTorrentLabel(hash, Settings.MovieCategory, Settings);
//SetPriority(remoteEpisode, hash);
SetDownloadDirectory(hash); SetDownloadDirectory(hash);
_proxy.StartTorrent(hash, Settings); _proxy.StartTorrent(hash, Settings);
return hash; return hash;
} }
else else
@ -125,32 +93,6 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
} }
} }
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
{
_proxy.AddTorrentFromFile(filename, fileContent, Settings);
var tries = 2;
var retryDelay = 100;
if (WaitForTorrent(hash, tries, retryDelay))
{
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
SetPriority(remoteEpisode, hash);
SetDownloadDirectory(hash);
_proxy.StartTorrent(hash, Settings);
return hash;
}
else
{
_logger.Debug("rTorrent could not add file");
RemoveItem(hash, true);
throw new ReleaseDownloadException(remoteEpisode.Release, "Downloading torrent failed");
}
}
public override string Name => "rTorrent"; public override string Name => "rTorrent";
public override ProviderMessage Message => new ProviderMessage("Radarr is unable to remove torrents that have finished seeding when using rTorrent", ProviderMessageType.Warning); public override ProviderMessage Message => new ProviderMessage("Radarr is unable to remove torrents that have finished seeding when using rTorrent", ProviderMessageType.Warning);
@ -167,7 +109,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
foreach (RTorrentTorrent torrent in torrents) foreach (RTorrentTorrent torrent in torrents)
{ {
// Don't concern ourselves with categories other than specified // Don't concern ourselves with categories other than specified
if (torrent.Category != Settings.TvCategory) continue; if (torrent.Category != Settings.MovieCategory) continue;
if (torrent.Path.StartsWith(".")) if (torrent.Path.StartsWith("."))
{ {
@ -287,17 +229,11 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
return result.Errors.First(); return result.Errors.First();
} }
private void SetPriority(RemoteEpisode remoteEpisode, string hash)
{
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);
_proxy.SetTorrentPriority(hash, priority, Settings);
}
private void SetDownloadDirectory(string hash) private void SetDownloadDirectory(string hash)
{ {
if (Settings.TvDirectory.IsNotNullOrWhiteSpace()) if (Settings.MovieDirectory.IsNotNullOrWhiteSpace())
{ {
_proxy.SetTorrentDownloadDirectory(hash, Settings.TvDirectory, Settings); _proxy.SetTorrentDownloadDirectory(hash, Settings.MovieDirectory, Settings);
} }
} }

@ -18,13 +18,13 @@ namespace NzbDrone.Core.Download.Clients.rTorrent
DroneFactoryValidator droneFactoryValidator, DroneFactoryValidator droneFactoryValidator,
MappedNetworkDriveValidator mappedNetworkDriveValidator) MappedNetworkDriveValidator mappedNetworkDriveValidator)
{ {
RuleFor(c => c.TvDirectory).Cascade(CascadeMode.StopOnFirstFailure) RuleFor(c => c.MovieDirectory).Cascade(CascadeMode.StopOnFirstFailure)
.IsValidPath() .IsValidPath()
.SetValidator(rootFolderValidator) .SetValidator(rootFolderValidator)
.SetValidator(droneFactoryValidator) .SetValidator(droneFactoryValidator)
.SetValidator(mappedNetworkDriveValidator) .SetValidator(mappedNetworkDriveValidator)
.SetValidator(pathExistsValidator) .SetValidator(pathExistsValidator)
.When(c => c.TvDirectory.IsNotNullOrWhiteSpace()) .When(c => c.MovieDirectory.IsNotNullOrWhiteSpace())
.When(c => c.Host == "localhost" || c.Host == "127.0.0.1"); .When(c => c.Host == "localhost" || c.Host == "127.0.0.1");
} }
} }

@ -11,7 +11,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
{ {
RuleFor(c => c.Host).ValidHost(); RuleFor(c => c.Host).ValidHost();
RuleFor(c => c.Port).InclusiveBetween(0, 65535); RuleFor(c => c.Port).InclusiveBetween(0, 65535);
RuleFor(c => c.TvCategory).NotEmpty() RuleFor(c => c.MovieCategory).NotEmpty()
.WithMessage("A category is recommended") .WithMessage("A category is recommended")
.AsWarning(); .AsWarning();
} }
@ -26,9 +26,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
Host = "localhost"; Host = "localhost";
Port = 8080; Port = 8080;
UrlBase = "RPC2"; UrlBase = "RPC2";
TvCategory = "movies-radarr"; MovieCategory = "movies-radarr";
OlderTvPriority = (int)RTorrentPriority.Normal;
RecentTvPriority = (int)RTorrentPriority.Normal;
} }
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)] [FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
@ -49,17 +47,11 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[FieldDefinition(5, Label = "Password", Type = FieldType.Password)] [FieldDefinition(5, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional.")] [FieldDefinition(6, 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 MovieCategory { get; set; }
[FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default rTorrent location")] [FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default rTorrent location")]
public string TvDirectory { get; set; } public string MovieDirectory { get; set; }
[FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
public int RecentTvPriority { get; set; }
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
public int OlderTvPriority { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {

@ -68,6 +68,38 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
return hash; return hash;
} }
protected override string AddFromMagnetLink(RemoteMovie remoteEpisode, string hash, string magnetLink)
{
_proxy.AddTorrentFromUrl(magnetLink, Settings);
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)UTorrentPriority.First ||
!isRecentEpisode && Settings.OlderTvPriority == (int)UTorrentPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash, Settings);
}*/
return hash;
}
protected override string AddFromTorrentFile(RemoteMovie remoteEpisode, string hash, string filename, byte[] fileContent)
{
_proxy.AddTorrentFromFile(filename, fileContent, Settings);
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)UTorrentPriority.First ||
!isRecentEpisode && Settings.OlderTvPriority == (int)UTorrentPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash, Settings);
}*/
return hash;
}
public override string Name => "uTorrent"; public override string Name => "uTorrent";
public override IEnumerable<DownloadClientItem> GetItems() public override IEnumerable<DownloadClientItem> GetItems()

@ -38,7 +38,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
[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(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] [FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]

@ -94,30 +94,18 @@ namespace NzbDrone.Core.Download
return; return;
} }
var series = _parsingService.GetSeries(trackedDownload.DownloadItem.Title); var movie = _parsingService.GetMovie(trackedDownload.DownloadItem.Title);
if (movie == null)
if (series == null)
{ {
if (historyItem != null) if (historyItem != null)
{ {
//series = _seriesService.GetSeries(historyItem.SeriesId); movie = _movieService.GetMovie(historyItem.MovieId);
} }
if (series == null) if (movie == null)
{ {
var movie = _parsingService.GetMovie(trackedDownload.DownloadItem.Title); trackedDownload.Warn("Movie title mismatch, automatic import is not possible.");
return;
if (movie == null)
{
movie = _movieService.GetMovie(historyItem.MovieId);
if (movie == null)
{
trackedDownload.Warn("Movie title mismatch, automatic import is not possible.");
}
}
//trackedDownload.Warn("Series title mismatch, automatic import is not possible.");
//return;
} }
} }
} }
@ -128,61 +116,30 @@ namespace NzbDrone.Core.Download
private void Import(TrackedDownload trackedDownload) private void Import(TrackedDownload trackedDownload)
{ {
var outputPath = trackedDownload.DownloadItem.OutputPath.FullPath; var outputPath = trackedDownload.DownloadItem.OutputPath.FullPath;
if (trackedDownload.RemoteMovie.Movie != null) var importResults = _downloadedMovieImportService.ProcessPath(outputPath, ImportMode.Auto, trackedDownload.RemoteMovie.Movie, trackedDownload.DownloadItem);
{
var importResults = _downloadedMovieImportService.ProcessPath(outputPath, ImportMode.Auto, trackedDownload.RemoteMovie.Movie, trackedDownload.DownloadItem);
if (importResults.Empty())
{
trackedDownload.Warn("No files found are eligible for import in {0}", outputPath);
return;
}
if (importResults.Count(c => c.Result == ImportResultType.Imported) >= 1) if (importResults.Empty())
{
trackedDownload.State = TrackedDownloadStage.Imported;
_eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
return;
}
if (importResults.Any(c => c.Result != ImportResultType.Imported))
{
var statusMessages = importResults
.Where(v => v.Result != ImportResultType.Imported)
.Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalEpisode.Path), v.Errors))
.ToArray();
trackedDownload.Warn(statusMessages);
}
}
else if (trackedDownload.RemoteEpisode.Series != null)
{ {
var importResults = _downloadedEpisodesImportService.ProcessPath(outputPath, ImportMode.Auto, trackedDownload.RemoteEpisode.Series, trackedDownload.DownloadItem); trackedDownload.Warn("No files found are eligible for import in {0}", outputPath);
return;
if (importResults.Empty()) }
{
trackedDownload.Warn("No files found are eligible for import in {0}", outputPath);
return;
}
if (importResults.Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, trackedDownload.RemoteEpisode.Episodes.Count)) if (importResults.Count(c => c.Result == ImportResultType.Imported) >= 1)
{ {
trackedDownload.State = TrackedDownloadStage.Imported; trackedDownload.State = TrackedDownloadStage.Imported;
_eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload)); _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
return; return;
} }
if (importResults.Any(c => c.Result != ImportResultType.Imported)) if (importResults.Any(c => c.Result != ImportResultType.Imported))
{ {
var statusMessages = importResults var statusMessages = importResults
.Where(v => v.Result != ImportResultType.Imported) .Where(v => v.Result != ImportResultType.Imported)
.Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalEpisode.Path), v.Errors)) .Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalMovie.Path), v.Errors))
.ToArray(); .ToArray();
trackedDownload.Warn(statusMessages); trackedDownload.Warn(statusMessages);
}
} }
} }
} }
} }

@ -72,7 +72,7 @@ namespace NzbDrone.Core.Download
if (grabbedItems.Empty()) if (grabbedItems.Empty())
{ {
trackedDownload.Warn("Download wasn't grabbed by sonarr, skipping"); trackedDownload.Warn("Download wasn't grabbed by Radarr, skipping");
return; return;
} }

@ -13,8 +13,9 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
CleanupOrphanedBySeries(); //CleanupOrphanedBySeries();
CleanupOrphanedByEpisode(); //CleanupOrphanedByEpisode();
CleanupOrphanedByMovie();
} }
private void CleanupOrphanedBySeries() private void CleanupOrphanedBySeries()
@ -29,6 +30,18 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
WHERE Series.Id IS NULL)"); WHERE Series.Id IS NULL)");
} }
private void CleanupOrphanedByMovie()
{
var mapper = _database.GetDataMapper();
mapper.ExecuteNonQuery(@"DELETE FROM History
WHERE Id IN (
SELECT History.Id FROM History
LEFT OUTER JOIN Movies
ON History.MovieId = Movies.Id
WHERE Movies.Id IS NULL)");
}
private void CleanupOrphanedByEpisode() private void CleanupOrphanedByEpisode()
{ {
var mapper = _database.GetDataMapper(); var mapper = _database.GetDataMapper();

@ -0,0 +1,30 @@
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Indexers.AwesomeHD
{
public class AwesomeHD : HttpIndexerBase<AwesomeHDSettings>
{
public override string Name => "AwesomeHD";
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override bool SupportsRss => true;
public override bool SupportsSearch => true;
public override int PageSize => 50;
public AwesomeHD(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
: base(httpClient, indexerStatusService, configService, parsingService, logger)
{ }
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new AwesomeHDRequestGenerator() { Settings = Settings };
}
public override IParseIndexerResponse GetParser()
{
return new AwesomeHDRssParser(Settings);
}
}
}

@ -0,0 +1,80 @@
using System;
using Newtonsoft.Json;
using System.Xml.Serialization;
using System.Collections.Generic;
namespace NzbDrone.Core.Indexers.AwesomeHD
{
public class Torrent
{
[XmlElement(ElementName = "id")]
public string Id { get; set; }
[XmlElement(ElementName = "groupid")]
public string GroupId { get; set; }
[XmlElement(ElementName = "time")]
public DateTime Time { get; set; }
[XmlElement(ElementName = "userid")]
public string Userid { get; set; }
[XmlElement(ElementName = "size")]
public long Size { get; set; }
[XmlElement(ElementName = "snatched")]
public string Snatched { get; set; }
[XmlElement(ElementName = "seeders")]
public string Seeders { get; set; }
[XmlElement(ElementName = "leechers")]
public string Leechers { get; set; }
[XmlElement(ElementName = "releasegroup")]
public string Releasegroup { get; set; }
[XmlElement(ElementName = "resolution")]
public string Resolution { get; set; }
[XmlElement(ElementName = "media")]
public string Media { get; set; }
[XmlElement(ElementName = "format")]
public string Format { get; set; }
[XmlElement(ElementName = "encoding")]
public string Encoding { get; set; }
[XmlElement(ElementName = "audioformat")]
public string Audioformat { get; set; }
[XmlElement(ElementName = "audiobitrate")]
public string Audiobitrate { get; set; }
[XmlElement(ElementName = "audiochannels")]
public string Audiochannels { get; set; }
[XmlElement(ElementName = "subtitles")]
public string Subtitles { get; set; }
[XmlElement(ElementName = "encodestatus")]
public string Encodestatus { get; set; }
[XmlElement(ElementName = "freeleech")]
public string Freeleech { get; set; }
[XmlElement(ElementName = "cover")]
public string Cover { get; set; }
[XmlElement(ElementName = "smallcover")]
public string Smallcover { get; set; }
[XmlElement(ElementName = "year")]
public string Year { get; set; }
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "imdb")]
public string Imdb { get; set; }
[XmlElement(ElementName = "type")]
public string Type { get; set; }
[XmlElement(ElementName = "plotoutline")]
public string Plotoutline { get; set; }
}
public class SearchResults
{
[XmlElement(ElementName = "authkey")]
public string AuthKey { get; set; }
[XmlElement(ElementName = "torrent")]
public List<Torrent> Torrent { get; set; }
}
public class AwesomeHDSearchResponse
{
[XmlElement(ElementName = "?xml")]
public string Xml { get; set; }
[XmlElement(ElementName = "searchresults")]
public SearchResults SearchResults { get; set; }
}
}

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.IndexerSearch.Definitions;
namespace NzbDrone.Core.Indexers.AwesomeHD
{
public class AwesomeHDRequestGenerator : IIndexerRequestGenerator
{
public AwesomeHDSettings Settings { get; set; }
public virtual IndexerPageableRequestChain GetRecentRequests()
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetRequest(null));
return pageableRequests;
}
public virtual IndexerPageableRequestChain GetSearchRequests(SingleEpisodeSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public virtual IndexerPageableRequestChain GetSearchRequests(AnimeEpisodeSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public virtual IndexerPageableRequestChain GetSearchRequests(SpecialEpisodeSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public virtual IndexerPageableRequestChain GetSearchRequests(DailyEpisodeSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public virtual IndexerPageableRequestChain GetSearchRequests(SeasonSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetRequest(searchCriteria.Movie.ImdbId));
return pageableRequests;
}
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
{
if (searchParameters != null)
{
yield return new IndexerRequest(string.Format("{0}/searchapi.php?action=imdbsearch&passkey={1}&imdb={2}", Settings.BaseUrl.Trim().TrimEnd('/'), Settings.Passkey.Trim(), searchParameters), HttpAccept.Rss);
}
else
{
yield return new IndexerRequest(string.Format("{0}/searchapi.php?action=latestmovies&passkey={1}", Settings.BaseUrl.Trim().TrimEnd('/'), Settings.Passkey.Trim()), HttpAccept.Rss);
}
}
}
}

@ -0,0 +1,92 @@
using System.Collections.Generic;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Parser.Model;
using System;
using System.Linq;
using System.Xml;
namespace NzbDrone.Core.Indexers.AwesomeHD
{
public class AwesomeHDRssParser : IParseIndexerResponse
{
private readonly AwesomeHDSettings _settings;
public AwesomeHDRssParser(AwesomeHDSettings settings)
{
_settings = settings;
}
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
throw new IndexerException(indexerResponse,
"Unexpected response status {0} code from API request",
indexerResponse.HttpResponse.StatusCode);
}
// Hacky ¯\_(ツ)_/¯
XmlDocument doc = new XmlDocument();
doc.LoadXml(indexerResponse.Content);
var json = JsonConvert.SerializeXmlNode(doc);
Console.WriteLine(json);
var jsonResponse = JsonConvert.DeserializeObject<AwesomeHDSearchResponse>(json);
if (jsonResponse == null)
{
throw new IndexerException(indexerResponse, "Unexpected response from request");
}
foreach (var torrent in jsonResponse.SearchResults.Torrent)
{
var id = torrent.Id;
var title = $"{torrent.Name}.{torrent.Year}.{torrent.Resolution}.{torrent.Media}.{torrent.Encoding}.{torrent.Audioformat}-{torrent.Releasegroup}";
torrentInfos.Add(new TorrentInfo()
{
Guid = string.Format("AwesomeHD-{0}", id),
Title = title,
Size = torrent.Size,
DownloadUrl = GetDownloadUrl(id, jsonResponse.SearchResults.AuthKey, _settings.Passkey),
InfoUrl = GetInfoUrl(torrent.GroupId, id),
Seeders = int.Parse(torrent.Seeders),
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
PublishDate = torrent.Time.ToUniversalTime()
});
}
return torrentInfos.OrderByDescending(o => ((dynamic)o).Seeders).ToArray();
}
private string GetDownloadUrl(string torrentId, string authKey, string passKey)
{
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("action", "download")
.AddQueryParam("id", torrentId)
.AddQueryParam("authkey", authKey)
.AddQueryParam("torrent_pass", passKey);
return url.FullUri;
}
private string GetInfoUrl(string groupId, string torrentId)
{
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("id", groupId)
.AddQueryParam("torrentid", torrentId);
return url.FullUri;
}
}
}

@ -0,0 +1,37 @@
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.AwesomeHD
{
public class AwesomeHDSettingsValidator : AbstractValidator<AwesomeHDSettings>
{
public AwesomeHDSettingsValidator()
{
RuleFor(c => c.BaseUrl).ValidRootUrl();
RuleFor(c => c.Passkey).NotEmpty();
}
}
public class AwesomeHDSettings : IProviderConfig
{
private static readonly AwesomeHDSettingsValidator Validator = new AwesomeHDSettingsValidator();
public AwesomeHDSettings()
{
BaseUrl = "https://awesome-hd.me";
}
[FieldDefinition(0, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since you Passkey will be sent to that host.")]
public string BaseUrl { get; set; }
[FieldDefinition(1, Label = "Passkey")]
public string Passkey { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
}

@ -39,14 +39,15 @@ namespace NzbDrone.Core.Indexers.Newznab
{ {
get get
{ {
yield return GetDefinition("Dognzb.cr", GetSettings("https://api.dognzb.cr")); yield return GetDefinition("DOGnzb", GetSettings("https://api.dognzb.cr"));
yield return GetDefinition("DrunkenSlug", GetSettings("https://api.drunkenslug.com")); yield return GetDefinition("DrunkenSlug", GetSettings("https://api.drunkenslug.com"));
yield return GetDefinition("Nzb-Tortuga", GetSettings("https://www.nzb-tortuga.com"));
yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su")); yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su"));
yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat")); yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat"));
yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws", 5010, 5030, 5040, 5045)); yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws"));
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info")); yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"));
yield return GetDefinition("nzbplanet.net", GetSettings("https://api.nzbplanet.net")); yield return GetDefinition("nzbplanet.net", GetSettings("https://api.nzbplanet.net"));
yield return GetDefinition("Nzbs.org", GetSettings("http://nzbs.org", 2000)); yield return GetDefinition("Nzbs.org", GetSettings("http://nzbs.org"));
yield return GetDefinition("OZnzb.com", GetSettings("https://api.oznzb.com")); yield return GetDefinition("OZnzb.com", GetSettings("https://api.oznzb.com"));
yield return GetDefinition("PFmonkey", GetSettings("https://www.pfmonkey.com")); yield return GetDefinition("PFmonkey", GetSettings("https://www.pfmonkey.com"));
yield return GetDefinition("SimplyNZBs", GetSettings("https://simplynzbs.com")); yield return GetDefinition("SimplyNZBs", GetSettings("https://simplynzbs.com"));

@ -124,7 +124,7 @@ namespace NzbDrone.Core.Indexers.Newznab
{ {
var pageableRequests = new IndexerPageableRequestChain(); var pageableRequests = new IndexerPageableRequestChain();
if (SupportsMovieSearch) if (false)
{ {
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
@ -133,7 +133,7 @@ namespace NzbDrone.Core.Indexers.Newznab
{ {
//Let's try anyways with q parameter, worst case nothing found. //Let's try anyways with q parameter, worst case nothing found.
pageableRequests.Add(GetPagedRequests(MaxPages, Settings.Categories, "search", pageableRequests.Add(GetPagedRequests(MaxPages, Settings.Categories, "search",
string.Format("&q={0}", searchCriteria.Movie.Title))); string.Format("&q={0}", Parser.Parser.NormalizeTitle(searchCriteria.Movie.Title))));
} }
return pageableRequests; return pageableRequests;

@ -60,7 +60,7 @@ namespace NzbDrone.Core.Indexers.Newznab
public NewznabSettings() public NewznabSettings()
{ {
Categories = new[] { 2030, 2035, 2040, 2050 }; Categories = new[] { 2000, 2010, 2020, 2030, 2035, 2040, 2045, 2050, 2060 };
AnimeCategories = Enumerable.Empty<int>(); AnimeCategories = Enumerable.Empty<int>();
} }

@ -29,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Omgwtfnzbs
protected override string GetInfoUrl(XElement item) protected override string GetInfoUrl(XElement item)
{ {
//Todo: Me thinks I need to parse details to get this... //Todo: Me thinks I need to parse details to get this...
var match = Regex.Match(item.Description(), @"(?:\<b\>View NZB\:\<\/b\>\s\<a\shref\=\"")(?<URL>.+)(?:\""\starget)", var match = Regex.Match(item.Description(), @"(?:\<b\>View NZB\:\<\/b\>\s\<a\shref\=\"")(?<URL>.+)(?:\"")",
RegexOptions.IgnoreCase | RegexOptions.Compiled); RegexOptions.IgnoreCase | RegexOptions.Compiled);
if (match.Success) if (match.Success)

@ -40,6 +40,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
torrentInfo.Seeders = torrent.seeders; torrentInfo.Seeders = torrent.seeders;
torrentInfo.Peers = torrent.leechers + torrent.seeders; torrentInfo.Peers = torrent.leechers + torrent.seeders;
torrentInfo.Freeleech = torrent.freeleech; torrentInfo.Freeleech = torrent.freeleech;
torrentInfo.PublishDate = torrent.publishdate.ToUniversalTime();
results.Add(torrentInfo); results.Add(torrentInfo);
} }

@ -78,9 +78,16 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
.Accept(HttpAccept.Json); .Accept(HttpAccept.Json);
requestBuilder.AddQueryParam("passkey", Settings.Passkey); requestBuilder.AddQueryParam("passkey", Settings.Passkey);
requestBuilder.AddQueryParam("user", Settings.User); if (!string.IsNullOrWhiteSpace(Settings.User))
// requestBuilder.AddQueryParam("imdbid", "tt0076759"); //For now just search for Star Wars. {
requestBuilder.AddQueryParam("search", "the"); // there has to be movies with 'the' in the title on any indexer requestBuilder.AddQueryParam("user", Settings.User);
}
else
{
requestBuilder.AddQueryParam("user", "");
}
requestBuilder.AddQueryParam("search", "the");
yield return new IndexerRequest(requestBuilder.Build()); yield return new IndexerRequest(requestBuilder.Build());
} }
@ -91,7 +98,15 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
.Accept(HttpAccept.Json); .Accept(HttpAccept.Json);
requestBuilder.AddQueryParam("passkey", Settings.Passkey); requestBuilder.AddQueryParam("passkey", Settings.Passkey);
requestBuilder.AddQueryParam("user", Settings.User);
if (!string.IsNullOrWhiteSpace(Settings.User))
{
requestBuilder.AddQueryParam("user", Settings.User);
}
else
{
requestBuilder.AddQueryParam("user", "");
}
if (searchCriteria.Movie.ImdbId.IsNotNullOrWhiteSpace()) if (searchCriteria.Movie.ImdbId.IsNotNullOrWhiteSpace())
{ {

@ -21,6 +21,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
public int size { get; set; } public int size { get; set; }
public int leechers { get; set; } public int leechers { get; set; }
public int seeders { get; set; } public int seeders { get; set; }
public DateTime publishdate { get; set; }
} }
} }

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
public TorrentPotatoSettings() public TorrentPotatoSettings()
{ {
BaseUrl = ""; BaseUrl = "http://127.0.0.1";
} }
[FieldDefinition(0, Label = "API URL", HelpText = "URL to TorrentPotato api.")] [FieldDefinition(0, Label = "API URL", HelpText = "URL to TorrentPotato api.")]

@ -97,8 +97,7 @@ namespace NzbDrone.Core.Indexers.Torznab
} }
if (capabilities.SupportedTvSearchParameters != null && if (capabilities.SupportedTvSearchParameters != null &&
new[] { "q", "tvdbid", "rid" }.Any(v => capabilities.SupportedTvSearchParameters.Contains(v)) && new[] { "q", "imdbid" }.Any(v => capabilities.SupportedTvSearchParameters.Contains(v)))
new[] { "season", "ep" }.All(v => capabilities.SupportedTvSearchParameters.Contains(v)))
{ {
return null; return null;
} }

@ -39,10 +39,7 @@ namespace NzbDrone.Core.Indexers.Torznab
protected override ReleaseInfo ProcessItem(XElement item, ReleaseInfo releaseInfo) protected override ReleaseInfo ProcessItem(XElement item, ReleaseInfo releaseInfo)
{ {
var torrentInfo = base.ProcessItem(item, releaseInfo) as TorrentInfo; var torrentInfo = base.ProcessItem(item, releaseInfo) as TorrentInfo;
torrentInfo.ImdbId = int.Parse(GetImdbId(item).Substring(2));
torrentInfo.TvdbId = GetTvdbId(item);
torrentInfo.TvRageId = GetTvRageId(item);
return torrentInfo; return torrentInfo;
} }
@ -100,31 +97,12 @@ namespace NzbDrone.Core.Indexers.Torznab
return url; return url;
} }
protected virtual int GetTvdbId(XElement item) protected virtual string GetImdbId(XElement item)
{ {
var tvdbIdString = TryGetTorznabAttribute(item, "tvdbid"); var imdbIdString = TryGetTorznabAttribute(item, "imdbid");
int tvdbId; return (!imdbIdString.IsNullOrWhiteSpace() ? imdbIdString.Substring(2) : null);
if (!tvdbIdString.IsNullOrWhiteSpace() && int.TryParse(tvdbIdString, out tvdbId))
{
return tvdbId;
}
return 0;
} }
protected virtual int GetTvRageId(XElement item)
{
var tvRageIdString = TryGetTorznabAttribute(item, "rageid");
int tvRageId;
if (!tvRageIdString.IsNullOrWhiteSpace() && int.TryParse(tvRageIdString, out tvRageId))
{
return tvRageId;
}
return 0;
}
protected override string GetInfoHash(XElement item) protected override string GetInfoHash(XElement item)
{ {
return TryGetTorznabAttribute(item, "infohash"); return TryGetTorznabAttribute(item, "infohash");

@ -178,7 +178,7 @@ namespace NzbDrone.Core.MediaFiles
_mediaFileTableCleanupService.Clean(movie, mediaFileList); _mediaFileTableCleanupService.Clean(movie, mediaFileList);
var decisionsStopwatch = Stopwatch.StartNew(); var decisionsStopwatch = Stopwatch.StartNew();
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, movie); var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, movie, true);
decisionsStopwatch.Stop(); decisionsStopwatch.Stop();
_logger.Trace("Import decisions complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed); _logger.Trace("Import decisions complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed);

@ -183,7 +183,7 @@ namespace NzbDrone.Core.MediaFiles
} }
} }
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), movie, folderInfo, true); var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), movie, folderInfo, true, false);
var importResults = _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode); var importResults = _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode);
if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) && if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) &&
@ -237,7 +237,7 @@ namespace NzbDrone.Core.MediaFiles
} }
} }
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, movie, null, true); var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, movie, null, true, false);
return _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode); return _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode);
} }

@ -83,6 +83,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
episodeFile.MediaInfo = localMovie.MediaInfo; episodeFile.MediaInfo = localMovie.MediaInfo;
episodeFile.Movie = localMovie.Movie; episodeFile.Movie = localMovie.Movie;
episodeFile.ReleaseGroup = localMovie.ParsedMovieInfo.ReleaseGroup; episodeFile.ReleaseGroup = localMovie.ParsedMovieInfo.ReleaseGroup;
episodeFile.Edition = localMovie.ParsedMovieInfo.Edition;
bool copyOnly; bool copyOnly;
switch (importMode) switch (importMode)

@ -19,7 +19,8 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
{ {
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series); List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series);
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie); List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie);
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource); //TODO: Needs changing to ParsedMovieInfo!! List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool shouldCheckQuality);
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldCheckQuality); //TODO: Needs changing to ParsedMovieInfo!!
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource); List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource);
} }
@ -31,6 +32,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly IVideoFileInfoReader _videoFileInfoReader; private readonly IVideoFileInfoReader _videoFileInfoReader;
private readonly IDetectSample _detectSample; private readonly IDetectSample _detectSample;
private readonly IQualityDefinitionService _qualitiesService;
private readonly Logger _logger; private readonly Logger _logger;
public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications, public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications,
@ -39,6 +41,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
IDiskProvider diskProvider, IDiskProvider diskProvider,
IVideoFileInfoReader videoFileInfoReader, IVideoFileInfoReader videoFileInfoReader,
IDetectSample detectSample, IDetectSample detectSample,
IQualityDefinitionService qualitiesService,
Logger logger) Logger logger)
{ {
_specifications = specifications; _specifications = specifications;
@ -47,6 +50,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
_diskProvider = diskProvider; _diskProvider = diskProvider;
_videoFileInfoReader = videoFileInfoReader; _videoFileInfoReader = videoFileInfoReader;
_detectSample = detectSample; _detectSample = detectSample;
_qualitiesService = qualitiesService;
_logger = logger; _logger = logger;
} }
@ -57,7 +61,12 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie) public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie)
{ {
return GetImportDecisions(videoFiles, movie, null, false); return GetImportDecisions(videoFiles, movie, null, true, false);
}
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool shouldCheckQuality = false)
{
return GetImportDecisions(videoFiles, movie, null, true, shouldCheckQuality);
} }
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource) public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource)
@ -77,7 +86,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
return decisions; return decisions;
} }
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource) public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldCheckQuality = false)
{ {
var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), movie); var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), movie);
@ -88,13 +97,13 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
foreach (var file in newFiles) foreach (var file in newFiles)
{ {
decisions.AddIfNotNull(GetDecision(file, movie, folderInfo, sceneSource, shouldUseFolderName)); decisions.AddIfNotNull(GetDecision(file, movie, folderInfo, sceneSource, shouldUseFolderName, shouldCheckQuality));
} }
return decisions; return decisions;
} }
private ImportDecision GetDecision(string file, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldUseFolderName) private ImportDecision GetDecision(string file, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldUseFolderName, bool shouldCheckQuality = false)
{ {
ImportDecision decision = null; ImportDecision decision = null;
@ -113,6 +122,106 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
if (sceneSource) if (sceneSource)
{ {
localMovie.MediaInfo = _videoFileInfoReader.GetMediaInfo(file); localMovie.MediaInfo = _videoFileInfoReader.GetMediaInfo(file);
if (shouldCheckQuality)
{
var width = localMovie.MediaInfo.Width;
var current = localMovie.Quality;
var qualityName = current.Quality.Name.ToLower();
QualityModel updated = null;
if (width > 1400)
{
if (qualityName.Contains("bluray"))
{
updated = new QualityModel(Quality.Bluray1080p);
}
else if (qualityName.Contains("webdl"))
{
updated = new QualityModel(Quality.WEBDL1080p);
}
else if (qualityName.Contains("hdtv"))
{
updated = new QualityModel(Quality.HDTV1080p);
}
else
{
var def = _qualitiesService.Get(Quality.HDTV1080p);
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
{
updated = new QualityModel(Quality.HDTV1080p);
}
def = _qualitiesService.Get(Quality.WEBDL1080p);
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
{
updated = new QualityModel(Quality.WEBDL1080p);
}
def = _qualitiesService.Get(Quality.Bluray1080p);
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
{
updated = new QualityModel(Quality.Bluray1080p);
}
if (updated == null)
{
updated = new QualityModel(Quality.Bluray1080p);
}
}
}
else
if (width > 900)
{
if (qualityName.Contains("bluray"))
{
updated = new QualityModel(Quality.Bluray720p);
}
else if (qualityName.Contains("webdl"))
{
updated = new QualityModel(Quality.WEBDL720p);
}
else if (qualityName.Contains("hdtv"))
{
updated = new QualityModel(Quality.HDTV720p);
}
else
{
var def = _qualitiesService.Get(Quality.HDTV720p);
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
{
updated = new QualityModel(Quality.HDTV720p);
}
def = _qualitiesService.Get(Quality.WEBDL720p);
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
{
updated = new QualityModel(Quality.WEBDL720p);
}
def = _qualitiesService.Get(Quality.Bluray720p);
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
{
updated = new QualityModel(Quality.Bluray720p);
}
if (updated == null)
{
updated = new QualityModel(Quality.Bluray720p);
}
}
}
if (updated != null && updated != current)
{
updated.QualitySource = QualitySource.MediaInfo;
localMovie.Quality = updated;
}
}
decision = GetDecision(localMovie); decision = GetDecision(localMovie);
} }
else else

@ -161,7 +161,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
} }
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file }, var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file },
movie, null, SceneSource(movie, folder)); movie, null, SceneSource(movie, folder), true);
return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null; return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null;
} }

@ -26,6 +26,7 @@ namespace NzbDrone.Core.MediaFiles
List<string> FilterExistingFiles(List<string> files, Series series); List<string> FilterExistingFiles(List<string> files, Series series);
List<string> FilterExistingFiles(List<string> files, Movie movie); List<string> FilterExistingFiles(List<string> files, Movie movie);
EpisodeFile Get(int id); EpisodeFile Get(int id);
MovieFile GetMovie(int id);
List<EpisodeFile> Get(IEnumerable<int> ids); List<EpisodeFile> Get(IEnumerable<int> ids);
List<MovieFile> GetMovies(IEnumerable<int> ids); List<MovieFile> GetMovies(IEnumerable<int> ids);
@ -150,5 +151,9 @@ namespace NzbDrone.Core.MediaFiles
_movieFileRepository.Update(episodeFile); _movieFileRepository.Update(episodeFile);
} }
public MovieFile GetMovie(int id)
{
return _movieFileRepository.Get(id);
}
} }
} }

@ -19,6 +19,7 @@ namespace NzbDrone.Core.MediaFiles
public string ReleaseGroup { get; set; } public string ReleaseGroup { get; set; }
public QualityModel Quality { get; set; } public QualityModel Quality { get; set; }
public MediaInfoModel MediaInfo { get; set; } public MediaInfoModel MediaInfo { get; set; }
public string Edition { get; set; }
public LazyLoaded<Movie> Movie { get; set; } public LazyLoaded<Movie> Movie { get; set; }
public override string ToString() public override string ToString()

@ -55,7 +55,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
public Production_Companies[] production_companies { get; set; } public Production_Companies[] production_companies { get; set; }
public Production_Countries[] production_countries { get; set; } public Production_Countries[] production_countries { get; set; }
public string release_date { get; set; } public string release_date { get; set; }
public int revenue { get; set; } public long revenue { get; set; }
public int runtime { get; set; } public int runtime { get; set; }
public Spoken_Languages[] spoken_languages { get; set; } public Spoken_Languages[] spoken_languages { get; set; }
public string status { get; set; } public string status { get; set; }

@ -13,6 +13,7 @@ using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Text;
namespace NzbDrone.Core.MetadataSource.SkyHook namespace NzbDrone.Core.MetadataSource.SkyHook
{ {
@ -88,8 +89,9 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
movie.TmdbId = TmdbId; movie.TmdbId = TmdbId;
movie.ImdbId = resource.imdb_id; movie.ImdbId = resource.imdb_id;
movie.Title = resource.title; movie.Title = resource.title;
movie.TitleSlug = movie.Title.ToLower().Replace(" ", "-"); movie.TitleSlug = ToUrlSlug(movie.Title);
movie.CleanTitle = Parser.Parser.CleanSeriesTitle(movie.Title); movie.CleanTitle = Parser.Parser.CleanSeriesTitle(movie.Title);
movie.SortTitle = Parser.Parser.NormalizeTitle(movie.Title);
movie.Overview = resource.overview; movie.Overview = resource.overview;
movie.Website = resource.homepage; movie.Website = resource.homepage;
if (resource.release_date.IsNotNullOrWhiteSpace()) if (resource.release_date.IsNotNullOrWhiteSpace())
@ -98,11 +100,6 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
movie.Year = movie.InCinemas.Value.Year; movie.Year = movie.InCinemas.Value.Year;
} }
var slugResult = _movieService.FindByTitleSlug(movie.TitleSlug);
if (slugResult != null)
{
_logger.Debug("Movie with this title slug already exists. Adding year...");
}
movie.TitleSlug += "-" + movie.Year.ToString(); movie.TitleSlug += "-" + movie.Year.ToString();
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.poster_path, MediaCoverTypes.Poster));//TODO: Update to load image specs from tmdb page!
@ -189,7 +186,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
{ {
var lowerTitle = title.ToLower(); var lowerTitle = title.ToLower();
var parserResult = Parser.Parser.ParseMovieTitle(title); var parserResult = Parser.Parser.ParseMovieTitle(title, true);
var yearTerm = ""; var yearTerm = "";
@ -327,23 +324,20 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
imdbMovie.TmdbId = result.id; imdbMovie.TmdbId = result.id;
try try
{ {
imdbMovie.SortTitle = result.title; imdbMovie.SortTitle = Parser.Parser.NormalizeTitle(result.title);
imdbMovie.Title = result.title; imdbMovie.Title = result.title;
string titleSlug = result.title; string titleSlug = ToUrlSlug(result.title);
imdbMovie.TitleSlug = titleSlug.ToLower().Replace(" ", "-"); imdbMovie.TitleSlug = titleSlug.ToLower().Replace(" ", "-");
if (result.release_date.IsNotNullOrWhiteSpace()) if (result.release_date.IsNotNullOrWhiteSpace())
{ {
imdbMovie.Year = DateTime.Parse(result.release_date).Year; imdbMovie.Year = DateTime.Parse(result.release_date).Year;
} }
//var slugResult = _movieService.FindByTitleSlug(imdbMovie.TitleSlug);
//if (slugResult != null)
//{
var slugResult = _movieService.FindByTitleSlug(imdbMovie.TitleSlug); // _logger.Debug("Movie with this title slug already exists. Adding year...");
if (slugResult != null) //}
{
_logger.Debug("Movie with this title slug already exists. Adding year...");
}
imdbMovie.TitleSlug += "-" + imdbMovie.Year.ToString(); imdbMovie.TitleSlug += "-" + imdbMovie.Year.ToString();
imdbMovie.Images = new List<MediaCover.MediaCover>(); imdbMovie.Images = new List<MediaCover.MediaCover>();
@ -524,5 +518,29 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
return MediaCoverTypes.Unknown; return MediaCoverTypes.Unknown;
} }
} }
public static string ToUrlSlug(string value)
{
//First to lower case
value = value.ToLowerInvariant();
//Remove all accents
var bytes = Encoding.GetEncoding("Cyrillic").GetBytes(value);
value = Encoding.ASCII.GetString(bytes);
//Replace spaces
value = Regex.Replace(value, @"\s", "-", RegexOptions.Compiled);
//Remove invalid chars
value = Regex.Replace(value, @"[^a-z0-9\s-_]", "", RegexOptions.Compiled);
//Trim dashes from end
value = value.Trim('-', '_');
//Replace double occurences of - or _
value = Regex.Replace(value, @"([-_]){2,}", "$1", RegexOptions.Compiled);
return value;
}
} }
} }

@ -18,7 +18,7 @@ namespace NzbDrone.Core.Notifications.Email
public override void OnGrab(GrabMessage grabMessage) public override void OnGrab(GrabMessage grabMessage)
{ {
const string subject = "Sonarr [TV] - Grabbed"; const string subject = "Radarr [TV] - Grabbed";
var body = string.Format("{0} sent to queue.", grabMessage.Message); var body = string.Format("{0} sent to queue.", grabMessage.Message);
_emailService.SendEmail(Settings, subject, body); _emailService.SendEmail(Settings, subject, body);
@ -26,7 +26,7 @@ namespace NzbDrone.Core.Notifications.Email
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string subject = "Sonarr [TV] - Downloaded"; const string subject = "Radarr [TV] - Downloaded";
var body = string.Format("{0} Downloaded and sorted.", message.Message); var body = string.Format("{0} Downloaded and sorted.", message.Message);
_emailService.SendEmail(Settings, subject, body); _emailService.SendEmail(Settings, subject, body);

@ -64,7 +64,7 @@ namespace NzbDrone.Core.Notifications.Email
try try
{ {
SendEmail(settings, "Sonarr - Test Notification", body); SendEmail(settings, "Radarr - Test Notification", body);
} }
catch (Exception ex) catch (Exception ex)
{ {

@ -18,14 +18,14 @@ namespace NzbDrone.Core.Notifications.Join
public override void OnGrab(GrabMessage grabMessage) public override void OnGrab(GrabMessage grabMessage)
{ {
const string title = "Sonarr - Episode Grabbed"; const string title = "Radarr - Episode Grabbed";
_proxy.SendNotification(title, grabMessage.Message, Settings); _proxy.SendNotification(title, grabMessage.Message, Settings);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string title = "Sonarr - Episode Downloaded"; const string title = "Radarr - Episode Downloaded";
_proxy.SendNotification(title, message.Message, Settings); _proxy.SendNotification(title, message.Message, Settings);
} }

@ -18,7 +18,7 @@ namespace NzbDrone.Core.Notifications.MediaBrowser
public override void OnGrab(GrabMessage grabMessage) public override void OnGrab(GrabMessage grabMessage)
{ {
const string title = "Sonarr - Grabbed"; const string title = "Radarr - Grabbed";
if (Settings.Notify) if (Settings.Notify)
{ {
@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications.MediaBrowser
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string title = "Sonarr - Downloaded"; const string title = "Radarr - Downloaded";
if (Settings.Notify) if (Settings.Notify)
{ {

@ -18,13 +18,13 @@ namespace NzbDrone.Core.Notifications.Plex
public override void OnGrab(GrabMessage grabMessage) public override void OnGrab(GrabMessage grabMessage)
{ {
const string header = "Sonarr [TV] - Grabbed"; const string header = "Radarr [TV] - Grabbed";
_plexClientService.Notify(Settings, header, grabMessage.Message); _plexClientService.Notify(Settings, header, grabMessage.Message);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string header = "Sonarr [TV] - Downloaded"; const string header = "Radarr [TV] - Downloaded";
_plexClientService.Notify(Settings, header, message.Message); _plexClientService.Notify(Settings, header, message.Message);
} }

@ -23,14 +23,14 @@ namespace NzbDrone.Core.Notifications.Plex
public override void OnGrab(GrabMessage grabMessage) public override void OnGrab(GrabMessage grabMessage)
{ {
const string header = "Sonarr - Grabbed"; const string header = "Radarr - Grabbed";
Notify(Settings, header, grabMessage.Message); Notify(Settings, header, grabMessage.Message);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string header = "Sonarr - Downloaded"; const string header = "Radarr - Downloaded";
Notify(Settings, header, message.Message); Notify(Settings, header, message.Message);
} }

@ -98,7 +98,7 @@ namespace NzbDrone.Core.Notifications.Plex
{ {
if (version >= new Version(1, 3, 0) && version < new Version(1, 3, 1)) if (version >= new Version(1, 3, 0) && version < new Version(1, 3, 1))
{ {
throw new PlexVersionException("Found version {0}, upgrade to PMS 1.3.1 to fix library updating and then restart Sonarr", version); throw new PlexVersionException("Found version {0}, upgrade to PMS 1.3.1 to fix library updating and then restart Radarr", version);
} }
} }

@ -18,14 +18,14 @@ namespace NzbDrone.Core.Notifications.PushBullet
public override void OnGrab(GrabMessage grabMessage) public override void OnGrab(GrabMessage grabMessage)
{ {
const string title = "Sonarr - Episode Grabbed"; const string title = "Radarr - Episode Grabbed";
_proxy.SendNotification(title, grabMessage.Message, Settings); _proxy.SendNotification(title, grabMessage.Message, Settings);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string title = "Sonarr - Episode Downloaded"; const string title = "Radarr - Episode Downloaded";
_proxy.SendNotification(title, message.Message, Settings); _proxy.SendNotification(title, message.Message, Settings);
} }

@ -92,7 +92,7 @@ namespace NzbDrone.Core.Notifications.PushBullet
{ {
try try
{ {
const string title = "Sonarr - Test Notification"; const string title = "Radarr - Test Notification";
const string body = "This is a test message from Radarr"; const string body = "This is a test message from Radarr";
SendNotification(title, body, settings); SendNotification(title, body, settings);

@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications.Pushalot
[FieldDefinition(1, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(PushalotPriority))] [FieldDefinition(1, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(PushalotPriority))]
public int Priority { get; set; } public int Priority { get; set; }
[FieldDefinition(2, Label = "Image", Type = FieldType.Checkbox, HelpText = "Include Sonarr logo with notifications")] [FieldDefinition(2, Label = "Image", Type = FieldType.Checkbox, HelpText = "Include Radarr logo with notifications")]
public bool Image { get; set; } public bool Image { get; set; }
public bool IsValid => !string.IsNullOrWhiteSpace(AuthToken); public bool IsValid => !string.IsNullOrWhiteSpace(AuthToken);

@ -101,7 +101,7 @@ namespace NzbDrone.Core.Notifications.Slack
{ {
try try
{ {
var message = $"Test message from Sonarr posted at {DateTime.Now}"; var message = $"Test message from Radarr posted at {DateTime.Now}";
var payload = new SlackPayload var payload = new SlackPayload
{ {
IconEmoji = Settings.Icon, IconEmoji = Settings.Icon,

@ -125,7 +125,7 @@ namespace NzbDrone.Core.Notifications.Twitter
{ {
try try
{ {
var body = "Sonarr: Test Message @ " + DateTime.Now; var body = "Radarr: Test Message @ " + DateTime.Now;
SendNotification(body, settings); SendNotification(body, settings);
} }

@ -23,14 +23,14 @@ namespace NzbDrone.Core.Notifications.Xbmc
public override void OnGrab(GrabMessage grabMessage) public override void OnGrab(GrabMessage grabMessage)
{ {
const string header = "Sonarr - Grabbed"; const string header = "Radarr - Grabbed";
Notify(Settings, header, grabMessage.Message); Notify(Settings, header, grabMessage.Message);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string header = "Sonarr - Downloaded"; const string header = "Radarr - Downloaded";
Notify(Settings, header, message.Message); Notify(Settings, header, message.Message);
UpdateAndClean(message.Series, message.OldFiles.Any()); UpdateAndClean(message.Series, message.OldFiles.Any());

@ -193,6 +193,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\118_update_movie_slug.cs" />
<Compile Include="Datastore\Migration\117_update_movie_file.cs" />
<Compile Include="Datastore\Migration\116_update_movie_sorttitle_again.cs" />
<Compile Include="Datastore\Migration\115_update_movie_sorttitle.cs" />
<Compile Include="Datastore\Migration\111_remove_bitmetv.cs" /> <Compile Include="Datastore\Migration\111_remove_bitmetv.cs" />
<Compile Include="Datastore\Migration\112_remove_torrentleech.cs" /> <Compile Include="Datastore\Migration\112_remove_torrentleech.cs" />
<Compile Include="Datastore\Migration\114_remove_fanzub.cs" /> <Compile Include="Datastore\Migration\114_remove_fanzub.cs" />
@ -587,6 +591,7 @@
<Compile Include="IndexerSearch\Definitions\MovieSearchCriteria.cs" /> <Compile Include="IndexerSearch\Definitions\MovieSearchCriteria.cs" />
<Compile Include="IndexerSearch\MoviesSearchCommand.cs" /> <Compile Include="IndexerSearch\MoviesSearchCommand.cs" />
<Compile Include="IndexerSearch\MoviesSearchService.cs" /> <Compile Include="IndexerSearch\MoviesSearchService.cs" />
<Compile Include="Indexers\AwesomeHD\AwesomeHDRssParser.cs" />
<Compile Include="Indexers\DownloadProtocol.cs" /> <Compile Include="Indexers\DownloadProtocol.cs" />
<Compile Include="Indexers\Exceptions\ApiKeyException.cs" /> <Compile Include="Indexers\Exceptions\ApiKeyException.cs" />
<Compile Include="Indexers\Exceptions\IndexerException.cs" /> <Compile Include="Indexers\Exceptions\IndexerException.cs" />
@ -594,6 +599,10 @@
<Compile Include="Indexers\Exceptions\UnsupportedFeedException.cs" /> <Compile Include="Indexers\Exceptions\UnsupportedFeedException.cs" />
<Compile Include="Indexers\EzrssTorrentRssParser.cs" /> <Compile Include="Indexers\EzrssTorrentRssParser.cs" />
<Compile Include="Indexers\FetchAndParseRssService.cs" /> <Compile Include="Indexers\FetchAndParseRssService.cs" />
<Compile Include="Indexers\AwesomeHD\AwesomeHD.cs" />
<Compile Include="Indexers\AwesomeHD\AwesomeHDApi.cs" />
<Compile Include="Indexers\AwesomeHD\AwesomeHDRequestGenerator.cs" />
<Compile Include="Indexers\AwesomeHD\AwesomeHDSettings.cs" />
<Compile Include="Indexers\PassThePopcorn\PassThePopcorn.cs" /> <Compile Include="Indexers\PassThePopcorn\PassThePopcorn.cs" />
<Compile Include="Indexers\PassThePopcorn\PassThePopcornApi.cs" /> <Compile Include="Indexers\PassThePopcorn\PassThePopcornApi.cs" />
<Compile Include="Indexers\PassThePopcorn\PassThePopcornInfo.cs" /> <Compile Include="Indexers\PassThePopcorn\PassThePopcornInfo.cs" />

@ -58,7 +58,7 @@ namespace NzbDrone.Core.Organizer
public static readonly Regex SeriesTitleRegex = new Regex(@"(?<token>\{(?:Series)(?<separator>[- ._])(Clean)?Title\})", public static readonly Regex SeriesTitleRegex = new Regex(@"(?<token>\{(?:Series)(?<separator>[- ._])(Clean)?Title\})",
RegexOptions.Compiled | RegexOptions.IgnoreCase); RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static readonly Regex MovieTitleRegex = new Regex(@"(?<token>\{(?:Movie)(?<separator>[- ._])(Clean)?Title\})", public static readonly Regex MovieTitleRegex = new Regex(@"(?<token>\{((?:(Movie|Original))(?<separator>[- ._])(Clean)?Title)\})",
RegexOptions.Compiled | RegexOptions.IgnoreCase); RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled); private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled);
@ -163,6 +163,7 @@ namespace NzbDrone.Core.Organizer
AddReleaseDateTokens(tokenHandlers, movie.Year); //In case we want to separate the year AddReleaseDateTokens(tokenHandlers, movie.Year); //In case we want to separate the year
AddQualityTokens(tokenHandlers, movie, movieFile); AddQualityTokens(tokenHandlers, movie, movieFile);
AddMediaInfoTokens(tokenHandlers, movieFile); AddMediaInfoTokens(tokenHandlers, movieFile);
AddMovieFileTokens(tokenHandlers, movieFile);
var fileName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim(); var fileName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim();
fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString()); fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString());
@ -503,6 +504,13 @@ namespace NzbDrone.Core.Organizer
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Sonarr"); tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Sonarr");
} }
private void AddMovieFileTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, MovieFile episodeFile)
{
tokenHandlers["{Original Title}"] = m => GetOriginalTitle(episodeFile);
tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(episodeFile);
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Sonarr");
}
private void AddQualityTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Series series, EpisodeFile episodeFile) private void AddQualityTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Series series, EpisodeFile episodeFile)
{ {
var qualityTitle = _qualityDefinitionService.Get(episodeFile.Quality.Quality).Title; var qualityTitle = _qualityDefinitionService.Get(episodeFile.Quality.Quality).Title;

@ -18,16 +18,10 @@ namespace NzbDrone.Core.Parser
private static readonly Regex[] ReportMovieTitleRegex = new[] private static readonly Regex[] ReportMovieTitleRegex = new[]
{ {
//Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.Special.Edition.2011 //Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.Special.Edition.2011
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\w+\.?edition))\.(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)", new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\.?((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX)))\.(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled), RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.2011.Special.Edition //TODO: Seems to slow down parsing heavily! //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>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((\w+\.?){1,3}edition))", new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX))",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Cut Movies, e.g: Mission.Impossible.3.Directors.Cut.2011
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\w+\.?cut))\.(?<year>(19|20)\d{2}(?!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>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((\w+\.?){1,3}cut))",
RegexOptions.IgnoreCase | RegexOptions.Compiled), RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Normal movie format, e.g: Mission.Impossible.3.2011 //Normal movie format, e.g: Mission.Impossible.3.2011
@ -36,6 +30,15 @@ namespace NzbDrone.Core.Parser
//PassThePopcorn Torrent names: Star.Wars[PassThePopcorn] //PassThePopcorn Torrent names: Star.Wars[PassThePopcorn]
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<year>(\[\w *\])))+(\W+|_|$)(?!\\)", new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<year>(\[\w *\])))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled), RegexOptions.IgnoreCase | RegexOptions.Compiled),
//That did not work? Maybe some tool uses [] for years. Who would do that?
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\W\d+)))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
};
private static readonly Regex[] ReportMovieTitleFolderRegex = new[]
{
//When year comes first.
new Regex(@"^(?:(?:[-_\W](?<![)!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\W\d+)))+(\W+|_|$)(?<title>.+?)?$")
}; };
private static readonly Regex[] ReportTitleRegex = new[] private static readonly Regex[] ReportTitleRegex = new[]
@ -327,7 +330,7 @@ namespace NzbDrone.Core.Parser
{ {
var fileInfo = new FileInfo(path); var fileInfo = new FileInfo(path);
var result = ParseMovieTitle(fileInfo.Name); var result = ParseMovieTitle(fileInfo.Name, true);
if (result == null) if (result == null)
{ {
@ -345,7 +348,7 @@ namespace NzbDrone.Core.Parser
} }
public static ParsedMovieInfo ParseMovieTitle(string title) public static ParsedMovieInfo ParseMovieTitle(string title, bool isDir = false)
{ {
ParsedMovieInfo realResult = null; ParsedMovieInfo realResult = null;
@ -376,7 +379,14 @@ namespace NzbDrone.Core.Parser
simpleTitle = CleanTorrentSuffixRegex.Replace(simpleTitle, string.Empty); simpleTitle = CleanTorrentSuffixRegex.Replace(simpleTitle, string.Empty);
foreach (var regex in ReportMovieTitleRegex) var allRegexes = ReportMovieTitleRegex.ToList();
if (isDir)
{
allRegexes.AddRange(ReportMovieTitleFolderRegex);
}
foreach (var regex in allRegexes)
{ {
var match = regex.Matches(simpleTitle); var match = regex.Matches(simpleTitle);

@ -44,14 +44,14 @@ namespace NzbDrone.Core.Queue
private IEnumerable<Queue> MapQueue(TrackedDownload trackedDownload) private IEnumerable<Queue> MapQueue(TrackedDownload trackedDownload)
{ {
if (trackedDownload.RemoteEpisode.Episodes != null && trackedDownload.RemoteEpisode.Episodes.Any()) if (trackedDownload.RemoteEpisode != null && trackedDownload.RemoteEpisode.Episodes != null && trackedDownload.RemoteEpisode.Episodes.Any())
{ {
foreach (var episode in trackedDownload.RemoteEpisode.Episodes) foreach (var episode in trackedDownload.RemoteEpisode.Episodes)
{ {
yield return MapEpisode(trackedDownload, episode); yield return MapEpisode(trackedDownload, episode);
} }
} }
else if (trackedDownload.RemoteMovie.Movie != null) else if (trackedDownload.RemoteMovie != null && trackedDownload.RemoteMovie.Movie != null)
{ {
yield return MapMovie(trackedDownload, trackedDownload.RemoteMovie.Movie); yield return MapMovie(trackedDownload, trackedDownload.RemoteMovie.Movie);
} }

@ -149,7 +149,11 @@ namespace NzbDrone.Core.RootFolders
foreach (string unmappedFolder in unmappedFolders) foreach (string unmappedFolder in unmappedFolders)
{ {
var di = new DirectoryInfo(unmappedFolder.Normalize()); var di = new DirectoryInfo(unmappedFolder.Normalize());
results.Add(new UnmappedFolder { Name = di.Name, Path = di.FullName }); if (!di.Attributes.HasFlag(FileAttributes.System) && !di.Attributes.HasFlag(FileAttributes.Hidden))
{
results.Add(new UnmappedFolder { Name = di.Name, Path = di.FullName });
}
} }
var setToRemove = SpecialFolders; var setToRemove = SpecialFolders;

@ -24,7 +24,7 @@ namespace Radarr.Host
SecurityProtocolPolicy.Register(); SecurityProtocolPolicy.Register();
X509CertificateValidationPolicy.Register(); X509CertificateValidationPolicy.Register();
Logger.Info("Starting Sonarr - {0} - Version {1}", Assembly.GetCallingAssembly().Location, Assembly.GetExecutingAssembly().GetName().Version); Logger.Info("Starting Radarr - {0} - Version {1}", Assembly.GetCallingAssembly().Location, Assembly.GetExecutingAssembly().GetName().Version);
if (!PlatformValidation.IsValidate(userAlert)) if (!PlatformValidation.IsValidate(userAlert))
{ {

Binary file not shown.

Binary file not shown.

@ -91,7 +91,7 @@
{{/if_eq}} {{/if_eq}}
{{#if_eq reason compare="MissingFromDisk"}} {{#if_eq reason compare="MissingFromDisk"}}
Sonarr was unable to find the file on disk so it was removed Radarr was unable to find the file on disk so it was removed
{{/if_eq}} {{/if_eq}}
{{#if_eq reason compare="Upgrade"}} {{#if_eq reason compare="Upgrade"}}

@ -1,18 +1,6 @@
<dl class="monitor-tooltip-contents"> <dl class="monitor-tooltip-contents">
<dt>All</dt> <dt>Yes</dt>
<dd>Monitor all episodes except specials</dd> <dd>Monitor for new releases</dd>
<dt>Future</dt> <dt>No</dt>
<dd>Monitor episodes that have not aired yet</dd> <dd>Do not monitor for new releases</dd>
<dt>Missing</dt>
<dd>Monitor episodes that do not have files or have not aired yet</dd>
<dt>Existing</dt>
<dd>Monitor episodes that have files or have not aired yet</dd>
<dt>First Season</dt>
<dd>Monitor all episodes of the first season. All other seasons will be ignored</dd>
<dt>Latest Season</dt>
<dd>Monitor all episodes of the latest season and future seasons</dd>
<dt>None</dt>
<dd>No episodes will be monitored.</dd>
<!--<dt>Latest Season</dt>-->
<!--<dd>Monitor all episodes the latest season only, previous seasons will be ignored</dd>-->
</dl> </dl>

@ -84,7 +84,7 @@ var view = Marionette.ItemView.extend({
content : content, content : content,
html : true, html : true,
trigger : 'hover', trigger : 'hover',
title : 'Episode Monitoring Options', title : 'Movie Monitoring Options',
placement : 'right', placement : 'right',
container : this.$el container : this.$el
}); });

@ -1,7 +1,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Sonarr Calendar feed</h3> <h3>Radarr Calendar feed</h3>
</div> </div>
<div class="modal-body edit-series-modal"> <div class="modal-body edit-series-modal">
<div class="form-horizontal"> <div class="form-horizontal">

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

Loading…
Cancel
Save