2014-02-14 05:31:49 +00:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
2014-07-04 08:09:48 +00:00
using FluentValidation.Results ;
2014-02-14 05:31:49 +00:00
using NLog ;
2014-07-04 20:27:21 +00:00
using NzbDrone.Common.Disk ;
2014-12-02 06:26:25 +00:00
using NzbDrone.Common.Extensions ;
2014-05-11 04:37:07 +00:00
using NzbDrone.Common.Http ;
2014-05-27 21:04:13 +00:00
using NzbDrone.Core.Configuration ;
2014-02-14 05:31:49 +00:00
using NzbDrone.Core.Parser.Model ;
2014-07-09 19:35:05 +00:00
using NzbDrone.Core.Validation ;
2014-09-11 20:24:00 +00:00
using NzbDrone.Core.RemotePathMappings ;
2014-02-14 05:31:49 +00:00
namespace NzbDrone.Core.Download.Clients.Sabnzbd
{
2014-09-07 11:39:00 +00:00
public class Sabnzbd : UsenetClientBase < SabnzbdSettings >
2014-02-14 05:31:49 +00:00
{
2014-04-01 20:07:41 +00:00
private readonly ISabnzbdProxy _proxy ;
2014-02-14 05:31:49 +00:00
2014-07-04 20:27:21 +00:00
public Sabnzbd ( ISabnzbdProxy proxy ,
2014-09-11 23:49:41 +00:00
IHttpClient httpClient ,
2014-05-27 21:04:13 +00:00
IConfigService configService ,
2014-07-04 20:27:21 +00:00
IDiskProvider diskProvider ,
2014-09-11 20:24:00 +00:00
IRemotePathMappingService remotePathMappingService ,
2014-02-14 05:31:49 +00:00
Logger logger )
2014-10-13 21:11:35 +00:00
: base ( httpClient , configService , diskProvider , remotePathMappingService , logger )
2014-02-14 05:31:49 +00:00
{
2014-04-01 20:07:41 +00:00
_proxy = proxy ;
2014-02-14 05:31:49 +00:00
}
2014-09-07 11:39:00 +00:00
protected override string AddFromNzbFile ( RemoteEpisode remoteEpisode , string filename , byte [ ] fileContent )
2014-04-19 15:09:22 +00:00
{
2014-02-14 05:31:49 +00:00
var category = Settings . TvCategory ;
var priority = remoteEpisode . IsRecentEpisode ( ) ? Settings . RecentTvPriority : Settings . OlderTvPriority ;
2015-03-20 23:55:15 +00:00
var response = _proxy . DownloadNzb ( fileContent , filename , category , priority , Settings ) ;
2014-02-14 05:31:49 +00:00
2014-09-07 11:39:00 +00:00
if ( response ! = null & & response . Ids . Any ( ) )
{
return response . Ids . First ( ) ;
2014-02-14 05:31:49 +00:00
}
2014-09-07 11:39:00 +00:00
return null ;
2014-02-14 05:31:49 +00:00
}
2014-04-19 15:09:22 +00:00
private IEnumerable < DownloadClientItem > GetQueue ( )
2014-02-14 05:31:49 +00:00
{
2014-04-19 15:09:22 +00:00
SabnzbdQueue sabQueue ;
try
2014-02-14 05:31:49 +00:00
{
2014-04-19 15:09:22 +00:00
sabQueue = _proxy . GetQueue ( 0 , 0 , Settings ) ;
}
catch ( DownloadClientException ex )
{
2014-10-03 21:38:57 +00:00
_logger . Warn ( "Couldn't get download queue. {0}" , ex . Message ) ;
2014-04-19 15:09:22 +00:00
return Enumerable . Empty < DownloadClientItem > ( ) ;
}
var queueItems = new List < DownloadClientItem > ( ) ;
2014-03-07 06:28:06 +00:00
2014-04-19 15:09:22 +00:00
foreach ( var sabQueueItem in sabQueue . Items )
{
var queueItem = new DownloadClientItem ( ) ;
queueItem . DownloadClient = Definition . Name ;
2014-12-19 00:26:42 +00:00
queueItem . DownloadId = sabQueueItem . Id ;
2014-04-19 15:09:22 +00:00
queueItem . Category = sabQueueItem . Category ;
queueItem . Title = sabQueueItem . Title ;
queueItem . TotalSize = ( long ) ( sabQueueItem . Size * 1024 * 1024 ) ;
queueItem . RemainingSize = ( long ) ( sabQueueItem . Sizeleft * 1024 * 1024 ) ;
queueItem . RemainingTime = sabQueueItem . Timeleft ;
if ( sabQueue . Paused | | sabQueueItem . Status = = SabnzbdDownloadStatus . Paused )
2014-03-07 06:28:06 +00:00
{
2014-04-19 15:09:22 +00:00
queueItem . Status = DownloadItemStatus . Paused ;
2014-05-24 19:02:25 +00:00
queueItem . RemainingTime = null ;
2014-03-07 06:28:06 +00:00
}
2014-04-19 15:09:22 +00:00
else if ( sabQueueItem . Status = = SabnzbdDownloadStatus . Queued | | sabQueueItem . Status = = SabnzbdDownloadStatus . Grabbing )
2014-03-07 06:28:06 +00:00
{
2014-04-19 15:09:22 +00:00
queueItem . Status = DownloadItemStatus . Queued ;
2014-03-07 06:28:06 +00:00
}
2014-04-19 15:09:22 +00:00
else
2014-02-14 05:31:49 +00:00
{
2014-04-19 15:09:22 +00:00
queueItem . Status = DownloadItemStatus . Downloading ;
}
2014-02-14 05:31:49 +00:00
2014-04-19 15:09:22 +00:00
if ( queueItem . Title . StartsWith ( "ENCRYPTED /" ) )
{
queueItem . Title = queueItem . Title . Substring ( 11 ) ;
queueItem . IsEncrypted = true ;
2014-02-14 05:31:49 +00:00
}
2014-04-19 15:09:22 +00:00
queueItems . Add ( queueItem ) ;
}
return queueItems ;
2014-02-14 05:31:49 +00:00
}
2014-04-19 15:09:22 +00:00
private IEnumerable < DownloadClientItem > GetHistory ( )
2014-02-14 05:31:49 +00:00
{
2014-03-07 06:28:06 +00:00
SabnzbdHistory sabHistory ;
try
{
2014-05-27 21:04:13 +00:00
sabHistory = _proxy . GetHistory ( 0 , _configService . DownloadClientHistoryLimit , Settings ) ;
2014-03-07 06:28:06 +00:00
}
catch ( DownloadClientException ex )
{
_logger . ErrorException ( ex . Message , ex ) ;
2014-04-19 15:09:22 +00:00
return Enumerable . Empty < DownloadClientItem > ( ) ;
2014-03-07 06:28:06 +00:00
}
2014-04-19 15:09:22 +00:00
var historyItems = new List < DownloadClientItem > ( ) ;
2014-02-14 05:31:49 +00:00
2014-03-07 06:28:06 +00:00
foreach ( var sabHistoryItem in sabHistory . Items )
2014-02-14 05:31:49 +00:00
{
2014-04-19 15:09:22 +00:00
var historyItem = new DownloadClientItem
{
DownloadClient = Definition . Name ,
2014-12-19 00:26:42 +00:00
DownloadId = sabHistoryItem . Id ,
2014-04-19 15:09:22 +00:00
Category = sabHistoryItem . Category ,
Title = sabHistoryItem . Title ,
TotalSize = sabHistoryItem . Size ,
RemainingSize = 0 ,
RemainingTime = TimeSpan . Zero ,
Message = sabHistoryItem . FailMessage
} ;
if ( sabHistoryItem . Status = = SabnzbdDownloadStatus . Failed )
{
2014-10-03 21:38:57 +00:00
if ( sabHistoryItem . FailMessage . IsNotNullOrWhiteSpace ( ) & &
2014-09-18 21:00:52 +00:00
sabHistoryItem . FailMessage . Equals ( "Unpacking failed, write error or disk is full?" , StringComparison . InvariantCultureIgnoreCase ) )
{
historyItem . Status = DownloadItemStatus . Warning ;
}
else
{
historyItem . Status = DownloadItemStatus . Failed ;
}
2014-04-19 15:09:22 +00:00
}
else if ( sabHistoryItem . Status = = SabnzbdDownloadStatus . Completed )
{
historyItem . Status = DownloadItemStatus . Completed ;
}
else // Verifying/Moving etc
{
historyItem . Status = DownloadItemStatus . Downloading ;
}
2014-02-14 05:31:49 +00:00
2014-10-12 22:00:03 +00:00
var outputPath = _remotePathMappingService . RemapRemoteToLocal ( Settings . Host , new OsPath ( sabHistoryItem . Storage ) ) ;
2014-09-11 20:24:00 +00:00
2014-10-12 22:00:03 +00:00
if ( ! outputPath . IsEmpty )
2014-06-04 20:08:36 +00:00
{
2014-09-11 20:24:00 +00:00
historyItem . OutputPath = outputPath ;
2014-07-19 22:53:22 +00:00
2014-10-12 22:00:03 +00:00
var parent = outputPath . Directory ;
while ( ! parent . IsEmpty )
2014-06-04 20:08:36 +00:00
{
2014-10-12 22:00:03 +00:00
if ( parent . FileName = = sabHistoryItem . Title )
2014-07-19 22:53:22 +00:00
{
historyItem . OutputPath = parent ;
}
2014-10-12 22:00:03 +00:00
parent = parent . Directory ;
2014-06-04 20:08:36 +00:00
}
}
2014-02-14 05:31:49 +00:00
historyItems . Add ( historyItem ) ;
}
return historyItems ;
}
2015-04-25 16:22:53 +00:00
public override string Name
{
get
{
return "SABnzb" ;
}
}
2014-04-19 15:09:22 +00:00
public override IEnumerable < DownloadClientItem > GetItems ( )
2014-02-14 05:31:49 +00:00
{
2014-04-19 15:09:22 +00:00
foreach ( var downloadClientItem in GetQueue ( ) . Concat ( GetHistory ( ) ) )
{
2015-02-17 23:43:33 +00:00
if ( downloadClientItem . Category = = Settings . TvCategory | | downloadClientItem . Category = = "*" & & Settings . TvCategory . IsNullOrWhiteSpace ( ) )
2014-07-05 14:21:44 +00:00
{
yield return downloadClientItem ;
}
2014-04-19 15:09:22 +00:00
}
2014-02-14 05:31:49 +00:00
}
2014-12-23 01:17:48 +00:00
public override void RemoveItem ( string downloadId , bool deleteData )
2014-02-14 05:31:49 +00:00
{
2014-12-23 01:17:48 +00:00
if ( GetQueue ( ) . Any ( v = > v . DownloadId = = downloadId ) )
2014-04-19 15:09:22 +00:00
{
2014-12-23 01:17:48 +00:00
_proxy . RemoveFrom ( "queue" , downloadId , deleteData , Settings ) ;
2014-04-19 15:09:22 +00:00
}
else
{
2014-12-23 01:17:48 +00:00
_proxy . RemoveFrom ( "history" , downloadId , deleteData , Settings ) ;
2014-04-19 15:09:22 +00:00
}
2014-04-01 20:07:41 +00:00
}
2014-07-04 20:27:21 +00:00
protected IEnumerable < SabnzbdCategory > GetCategories ( SabnzbdConfig config )
{
2014-10-12 22:00:03 +00:00
var completeDir = new OsPath ( config . Misc . complete_dir ) ;
2014-07-04 20:27:21 +00:00
2014-10-12 22:00:03 +00:00
if ( ! completeDir . IsRooted )
2014-07-11 22:16:49 +00:00
{
var queue = _proxy . GetQueue ( 0 , 1 , Settings ) ;
2014-10-12 22:00:03 +00:00
var defaultRootFolder = new OsPath ( queue . DefaultRootFolder ) ;
2014-07-11 22:16:49 +00:00
2014-10-12 22:00:03 +00:00
completeDir = defaultRootFolder + completeDir ;
2014-07-11 22:16:49 +00:00
}
2014-07-04 20:27:21 +00:00
foreach ( var category in config . Categories )
{
2014-10-12 22:00:03 +00:00
var relativeDir = new OsPath ( category . Dir . TrimEnd ( '*' ) ) ;
2014-07-04 20:27:21 +00:00
2014-10-12 22:00:03 +00:00
category . FullPath = completeDir + relativeDir ;
2014-07-04 20:27:21 +00:00
yield return category ;
}
}
2014-06-06 05:55:38 +00:00
public override DownloadClientStatus GetStatus ( )
{
2014-07-04 20:27:21 +00:00
var config = _proxy . GetConfig ( Settings ) ;
var categories = GetCategories ( config ) . ToArray ( ) ;
var category = categories . FirstOrDefault ( v = > v . Name = = Settings . TvCategory ) ;
if ( category = = null )
{
category = categories . FirstOrDefault ( v = > v . Name = = "*" ) ;
}
2014-06-06 05:55:38 +00:00
var status = new DownloadClientStatus
{
IsLocalhost = Settings . Host = = "127.0.0.1" | | Settings . Host = = "localhost"
} ;
2014-07-04 20:27:21 +00:00
if ( category ! = null )
{
2014-10-12 22:00:03 +00:00
status . OutputRootFolders = new List < OsPath > { _remotePathMappingService . RemapRemoteToLocal ( Settings . Host , category . FullPath ) } ;
2014-07-04 20:27:21 +00:00
}
2014-06-06 05:55:38 +00:00
return status ;
}
2014-07-09 22:11:57 +00:00
protected override void Test ( List < ValidationFailure > failures )
2014-02-26 05:40:47 +00:00
{
2014-07-04 08:09:48 +00:00
failures . AddIfNotNull ( TestConnection ( ) ) ;
2014-07-09 22:11:57 +00:00
failures . AddIfNotNull ( TestAuthentication ( ) ) ;
2014-08-02 22:51:20 +00:00
failures . AddIfNotNull ( TestGlobalConfig ( ) ) ;
2014-07-04 08:09:48 +00:00
failures . AddIfNotNull ( TestCategory ( ) ) ;
}
private ValidationFailure TestConnection ( )
{
try
{
2014-07-04 20:27:21 +00:00
_proxy . GetVersion ( Settings ) ;
2014-07-04 08:09:48 +00:00
}
catch ( Exception ex )
2014-06-12 21:46:08 +00:00
{
2014-07-04 08:09:48 +00:00
_logger . ErrorException ( ex . Message , ex ) ;
return new ValidationFailure ( "Host" , "Unable to connect to SABnzbd" ) ;
2014-06-12 21:46:08 +00:00
}
2014-07-04 08:09:48 +00:00
return null ;
2014-02-26 05:40:47 +00:00
}
2014-07-09 22:11:57 +00:00
private ValidationFailure TestAuthentication ( )
{
try
{
_proxy . GetConfig ( Settings ) ;
}
catch ( Exception ex )
{
if ( ex . Message . ContainsIgnoreCase ( "API Key Incorrect" ) )
{
return new ValidationFailure ( "APIKey" , "API Key Incorrect" ) ;
}
if ( ex . Message . ContainsIgnoreCase ( "API Key Required" ) )
{
return new ValidationFailure ( "APIKey" , "API Key Required" ) ;
}
throw ;
}
return null ;
}
2014-09-11 23:49:41 +00:00
2014-08-02 22:51:20 +00:00
private ValidationFailure TestGlobalConfig ( )
{
var config = _proxy . GetConfig ( Settings ) ;
if ( config . Misc . pre_check )
{
return new NzbDroneValidationFailure ( "" , "Disable 'Check before download' option in Sabnbzd" )
{
InfoLink = String . Format ( "http://{0}:{1}/sabnzbd/config/switches/" , Settings . Host , Settings . Port ) ,
2015-03-21 11:05:06 +00:00
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."
2014-08-02 22:51:20 +00:00
} ;
}
return null ;
}
2014-07-09 22:11:57 +00:00
2014-07-04 08:09:48 +00:00
private ValidationFailure TestCategory ( )
2014-02-14 05:31:49 +00:00
{
2014-08-02 22:51:20 +00:00
var config = _proxy . GetConfig ( Settings ) ;
2014-07-04 20:27:21 +00:00
var category = GetCategories ( config ) . FirstOrDefault ( ( SabnzbdCategory v ) = > v . Name = = Settings . TvCategory ) ;
2014-07-04 08:09:48 +00:00
2014-07-04 20:27:21 +00:00
if ( category ! = null )
2014-07-04 08:09:48 +00:00
{
2014-07-04 20:27:21 +00:00
if ( category . Dir . EndsWith ( "*" ) )
{
2014-07-09 19:35:05 +00:00
return new NzbDroneValidationFailure ( "TvCategory" , "Enable Job folders" )
{
InfoLink = String . Format ( "http://{0}:{1}/sabnzbd/config/categories/" , Settings . Host , Settings . Port ) ,
2015-03-21 11:05:06 +00:00
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."
2014-07-09 19:35:05 +00:00
} ;
2014-07-04 20:27:21 +00:00
}
}
else
{
if ( ! Settings . TvCategory . IsNullOrWhiteSpace ( ) )
{
2014-07-09 19:35:05 +00:00
return new NzbDroneValidationFailure ( "TvCategory" , "Category does not exist" )
{
InfoLink = String . Format ( "http://{0}:{1}/sabnzbd/config/categories/" , Settings . Host , Settings . Port ) ,
DetailedDescription = "The Category your entered doesn't exist in Sabnzbd. Go to Sabnzbd to create it."
} ;
2014-07-04 20:27:21 +00:00
}
}
if ( config . Misc . enable_tv_sorting )
{
if ( ! config . Misc . tv_categories . Any < string > ( ) | |
config . Misc . tv_categories . Contains ( Settings . TvCategory ) | |
( Settings . TvCategory . IsNullOrWhiteSpace ( ) & & config . Misc . tv_categories . Contains ( "Default" ) ) )
{
2014-07-09 19:35:05 +00:00
return new NzbDroneValidationFailure ( "TvCategory" , "Disable TV Sorting" )
{
InfoLink = String . Format ( "http://{0}:{1}/sabnzbd/config/sorting/" , Settings . Host , Settings . Port ) ,
2015-03-21 11:05:06 +00:00
DetailedDescription = "You must disable Sabnzbd TV Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it."
2014-07-09 19:35:05 +00:00
} ;
2014-07-04 20:27:21 +00:00
}
2014-07-04 08:09:48 +00:00
}
2014-02-14 05:31:49 +00:00
2014-07-04 08:09:48 +00:00
return null ;
2014-02-14 05:31:49 +00:00
}
}
}