2014-02-14 05:31:49 +00:00
using System ;
2014-06-06 05:55:38 +00:00
using System.IO ;
2014-03-07 06:28:06 +00:00
using System.Linq ;
2014-06-06 05:55:38 +00:00
using System.Collections.Generic ;
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 22:11:57 +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.Nzbget
{
2014-09-07 11:39:00 +00:00
public class Nzbget : UsenetClientBase < NzbgetSettings >
2014-02-14 05:31:49 +00:00
{
private readonly INzbgetProxy _proxy ;
public Nzbget ( INzbgetProxy 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
{
_proxy = proxy ;
}
2017-01-05 00:06:18 +00:00
protected override string AddFromNzbFile ( RemoteEpisode remoteEpisode , string filename , byte [ ] fileContents )
2014-04-19 15:09:22 +00:00
{
2014-06-25 06:32:26 +00:00
var category = Settings . TvCategory ;
var priority = remoteEpisode . IsRecentEpisode ( ) ? Settings . RecentTvPriority : Settings . OlderTvPriority ;
2014-02-14 05:31:49 +00:00
2017-01-05 00:06:18 +00:00
var response = _proxy . DownloadNzb ( fileContents , filename , category , priority , Settings ) ;
2014-02-14 05:31:49 +00:00
2015-04-11 07:03:35 +00:00
if ( response = = null )
{
throw new DownloadClientException ( "Failed to add nzb {0}" , filename ) ;
}
2014-09-07 11:39:00 +00:00
return response ;
2014-02-14 05:31:49 +00:00
}
2017-01-03 23:33:16 +00:00
protected override string AddFromNzbFile ( RemoteMovie remoteMovie , string filename , byte [ ] fileContents )
{
2017-01-05 00:06:18 +00:00
var category = Settings . TvCategory ; // TODO: Update this to MovieCategory?
2017-01-03 23:33:16 +00:00
var priority = Settings . RecentTvPriority ;
var response = _proxy . DownloadNzb ( fileContents , filename , category , priority , Settings ) ;
if ( response = = null )
{
throw new DownloadClientException ( "Failed to add nzb {0}" , filename ) ;
}
return response ;
}
2014-04-19 15:09:22 +00:00
private IEnumerable < DownloadClientItem > GetQueue ( )
2014-02-14 05:31:49 +00:00
{
2014-05-24 10:48:55 +00:00
NzbgetGlobalStatus globalStatus ;
2014-03-07 06:28:06 +00:00
List < NzbgetQueueItem > queue ;
2014-02-14 05:31:49 +00:00
2014-03-07 06:28:06 +00:00
try
{
2014-05-24 10:48:55 +00:00
globalStatus = _proxy . GetGlobalStatus ( Settings ) ;
2014-03-07 06:28:06 +00:00
queue = _proxy . GetQueue ( Settings ) ;
}
catch ( DownloadClientException ex )
{
2016-02-11 21:13:42 +00:00
_logger . Error ( ex , ex . Message ) ;
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 queueItems = new List < DownloadClientItem > ( ) ;
2014-03-07 06:28:06 +00:00
2015-10-03 17:45:26 +00:00
long totalRemainingSize = 0 ;
2014-05-24 10:48:55 +00:00
2014-03-20 07:08:15 +00:00
foreach ( var item in queue )
2014-02-14 05:31:49 +00:00
{
2014-05-26 06:31:00 +00:00
var totalSize = MakeInt64 ( item . FileSizeHi , item . FileSizeLo ) ;
var pausedSize = MakeInt64 ( item . PausedSizeHi , item . PausedSizeLo ) ;
var remainingSize = MakeInt64 ( item . RemainingSizeHi , item . RemainingSizeLo ) ;
2014-09-11 23:49:41 +00:00
2014-03-20 07:08:15 +00:00
var droneParameter = item . Parameters . SingleOrDefault ( p = > p . Name = = "drone" ) ;
2017-01-03 23:33:16 +00:00
var queueItem = new DownloadClientItem ( )
{
DownloadId = droneParameter = = null ? item . NzbId . ToString ( ) : droneParameter . Value . ToString ( ) ,
Title = item . NzbName ,
TotalSize = totalSize ,
Category = item . Category ,
DownloadClient = Definition . Name
} ;
2015-05-05 15:42:41 +00:00
if ( globalStatus . DownloadPaused | | remainingSize = = pausedSize & & remainingSize ! = 0 )
2014-04-19 15:09:22 +00:00
{
2014-05-24 10:48:55 +00:00
queueItem . Status = DownloadItemStatus . Paused ;
queueItem . RemainingSize = remainingSize ;
2014-04-19 15:09:22 +00:00
}
else
{
2014-05-24 10:48:55 +00:00
if ( item . ActiveDownloads = = 0 & & remainingSize ! = 0 )
{
queueItem . Status = DownloadItemStatus . Queued ;
}
else
{
queueItem . Status = DownloadItemStatus . Downloading ;
}
queueItem . RemainingSize = remainingSize - pausedSize ;
if ( globalStatus . DownloadRate ! = 0 )
{
queueItem . RemainingTime = TimeSpan . FromSeconds ( ( totalRemainingSize + queueItem . RemainingSize ) / globalStatus . DownloadRate ) ;
totalRemainingSize + = queueItem . RemainingSize ;
}
2014-04-19 15:09:22 +00:00
}
2014-02-14 05:31:49 +00:00
2014-03-07 06:28:06 +00:00
queueItems . Add ( queueItem ) ;
2014-02-14 05:31:49 +00:00
}
2014-03-07 06:28:06 +00:00
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-20 07:08:15 +00:00
List < NzbgetHistoryItem > history ;
try
{
2014-05-27 21:04:13 +00:00
history = _proxy . GetHistory ( Settings ) . Take ( _configService . DownloadClientHistoryLimit ) . ToList ( ) ;
2014-03-20 07:08:15 +00:00
}
catch ( DownloadClientException ex )
{
2016-02-11 21:13:42 +00:00
_logger . Error ( ex , ex . Message ) ;
2014-04-19 15:09:22 +00:00
return Enumerable . Empty < DownloadClientItem > ( ) ;
2014-03-20 07:08:15 +00:00
}
2014-04-19 15:09:22 +00:00
var historyItems = new List < DownloadClientItem > ( ) ;
2014-09-11 23:49:41 +00:00
var successStatus = new [ ] { "SUCCESS" , "NONE" } ;
2014-03-20 07:08:15 +00:00
foreach ( var item in history )
{
var droneParameter = item . Parameters . SingleOrDefault ( p = > p . Name = = "drone" ) ;
2017-01-03 23:33:16 +00:00
var historyItem = new DownloadClientItem ( )
{
DownloadClient = Definition . Name ,
DownloadId = droneParameter = = null ? item . Id . ToString ( ) : droneParameter . Value . ToString ( ) ,
Title = item . Name ,
TotalSize = MakeInt64 ( item . FileSizeHi , item . FileSizeLo ) ,
OutputPath = _remotePathMappingService . RemapRemoteToLocal ( Settings . Host , new OsPath ( item . DestDir ) ) ,
Category = item . Category ,
Message = string . Format ( "PAR Status: {0} - Unpack Status: {1} - Move Status: {2} - Script Status: {3} - Delete Status: {4} - Mark Status: {5}" , item . ParStatus , item . UnpackStatus , item . MoveStatus , item . ScriptStatus , item . DeleteStatus , item . MarkStatus ) ,
Status = DownloadItemStatus . Completed ,
RemainingTime = TimeSpan . Zero
} ;
2014-05-24 10:48:55 +00:00
if ( item . DeleteStatus = = "MANUAL" )
{
continue ;
}
2014-05-23 19:18:49 +00:00
2014-09-25 18:48:41 +00:00
if ( ! successStatus . Contains ( item . ParStatus ) )
2014-09-18 21:00:52 +00:00
{
historyItem . Status = DownloadItemStatus . Failed ;
}
2014-09-25 18:48:41 +00:00
if ( item . UnpackStatus = = "SPACE" )
2014-09-18 21:00:52 +00:00
{
historyItem . Status = DownloadItemStatus . Warning ;
}
2014-09-25 18:48:41 +00:00
else if ( ! successStatus . Contains ( item . UnpackStatus ) )
{
historyItem . Status = DownloadItemStatus . Failed ;
}
if ( ! successStatus . Contains ( item . MoveStatus ) )
{
historyItem . Status = DownloadItemStatus . Warning ;
}
if ( ! successStatus . Contains ( item . ScriptStatus ) )
{
historyItem . Status = DownloadItemStatus . Failed ;
}
2014-09-18 21:00:52 +00:00
2015-03-22 20:42:55 +00:00
if ( ! successStatus . Contains ( item . DeleteStatus ) & & item . DeleteStatus . IsNotNullOrWhiteSpace ( ) )
{
2016-04-09 18:17:15 +00:00
if ( item . DeleteStatus = = "COPY" | | item . DeleteStatus = = "DUPE" )
{
historyItem . Status = DownloadItemStatus . Failed ;
}
else
{
historyItem . Status = DownloadItemStatus . Warning ;
}
2015-03-22 20:42:55 +00:00
}
if ( item . DeleteStatus = = "HEALTH" )
2014-05-23 19:18:49 +00:00
{
historyItem . Status = DownloadItemStatus . Failed ;
}
2014-03-20 07:08:15 +00:00
historyItems . Add ( historyItem ) ;
}
return historyItems ;
2014-02-14 05:31:49 +00:00
}
2016-12-09 06:54:15 +00:00
public override string Name = > "NZBGet" ;
2015-04-25 16:22:53 +00:00
2014-04-19 15:09:22 +00:00
public override IEnumerable < DownloadClientItem > GetItems ( )
2014-02-14 05:31:49 +00:00
{
2014-12-19 00:26:42 +00:00
return GetQueue ( ) . Concat ( GetHistory ( ) ) . Where ( downloadClientItem = > downloadClientItem . Category = = Settings . TvCategory ) ;
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
{
2015-05-01 22:19:15 +00:00
if ( deleteData )
{
DeleteItemData ( downloadId ) ;
}
2014-12-23 01:17:48 +00:00
_proxy . RemoveItem ( downloadId , Settings ) ;
2014-04-01 20:07:41 +00:00
}
2014-06-06 05:55:38 +00:00
public override DownloadClientStatus GetStatus ( )
2014-02-26 05:40:47 +00:00
{
2014-06-06 05:55:38 +00:00
var config = _proxy . GetConfig ( Settings ) ;
var category = GetCategories ( config ) . FirstOrDefault ( v = > v . Name = = Settings . TvCategory ) ;
var status = new DownloadClientStatus
{
IsLocalhost = Settings . Host = = "127.0.0.1" | | Settings . Host = = "localhost"
} ;
if ( category ! = null )
{
2014-10-12 22:00:03 +00:00
status . OutputRootFolders = new List < OsPath > { _remotePathMappingService . RemapRemoteToLocal ( Settings . Host , new OsPath ( category . DestDir ) ) } ;
2014-06-06 05:55:38 +00:00
}
return status ;
}
2015-10-03 17:45:26 +00:00
protected IEnumerable < NzbgetCategory > GetCategories ( Dictionary < string , string > config )
2014-06-06 05:55:38 +00:00
{
for ( int i = 1 ; i < 100 ; i + + )
{
var name = config . GetValueOrDefault ( "Category" + i + ".Name" ) ;
if ( name = = null ) yield break ;
var destDir = config . GetValueOrDefault ( "Category" + i + ".DestDir" ) ;
2014-09-11 23:49:41 +00:00
2014-06-06 05:55:38 +00:00
if ( destDir . IsNullOrWhiteSpace ( ) )
{
var mainDir = config . GetValueOrDefault ( "MainDir" ) ;
2015-10-03 17:45:26 +00:00
destDir = config . GetValueOrDefault ( "DestDir" , string . Empty ) . Replace ( "${MainDir}" , mainDir ) ;
2014-06-06 05:55:38 +00:00
if ( config . GetValueOrDefault ( "AppendCategoryDir" , "yes" ) = = "yes" )
{
destDir = Path . Combine ( destDir , name ) ;
}
}
yield return new NzbgetCategory
{
Name = name ,
DestDir = destDir ,
Unpack = config . GetValueOrDefault ( "Category" + i + ".Unpack" ) = = "yes" ,
DefScript = config . GetValueOrDefault ( "Category" + i + ".DefScript" ) ,
Aliases = config . GetValueOrDefault ( "Category" + i + ".Aliases" ) ,
} ;
}
2014-02-26 05:40:47 +00:00
}
2014-07-09 22:11:57 +00:00
protected override void Test ( List < ValidationFailure > failures )
2014-06-06 05:55:38 +00:00
{
2014-07-04 08:09:48 +00:00
failures . AddIfNotNull ( TestConnection ( ) ) ;
failures . AddIfNotNull ( TestCategory ( ) ) ;
2016-09-13 20:58:14 +00:00
failures . AddIfNotNull ( TestSettings ( ) ) ;
2014-07-04 08:09:48 +00:00
}
2014-06-12 21:46:08 +00:00
2014-07-04 08:09:48 +00:00
private ValidationFailure TestConnection ( )
{
try
{
2015-04-11 07:07:35 +00:00
var version = _proxy . GetVersion ( Settings ) . Split ( '-' ) [ 0 ] ;
2015-03-21 20:28:23 +00:00
2015-04-11 07:07:35 +00:00
if ( Version . Parse ( version ) < Version . Parse ( "12.0" ) )
2015-03-21 20:28:23 +00:00
{
return new ValidationFailure ( string . Empty , "Nzbget version too low, need 12.0 or higher" ) ;
}
2014-07-04 08:09:48 +00:00
}
catch ( Exception ex )
2014-06-12 21:46:08 +00:00
{
2014-07-09 22:11:57 +00:00
if ( ex . Message . ContainsIgnoreCase ( "Authentication failed" ) )
{
return new ValidationFailure ( "Username" , "Authentication failed" ) ;
}
2016-02-11 21:13:42 +00:00
_logger . Error ( ex , ex . Message ) ;
2014-07-04 08:09:48 +00:00
return new ValidationFailure ( "Host" , "Unable to connect to NZBGet" ) ;
2014-06-12 21:46:08 +00:00
}
2014-07-04 08:09:48 +00:00
return null ;
2014-06-06 05:55:38 +00:00
}
2014-07-04 08:09:48 +00:00
private ValidationFailure TestCategory ( )
2014-02-14 05:31:49 +00:00
{
2014-07-04 08:09:48 +00:00
var config = _proxy . GetConfig ( Settings ) ;
var categories = GetCategories ( config ) ;
if ( ! Settings . TvCategory . IsNullOrWhiteSpace ( ) & & ! categories . Any ( v = > v . Name = = Settings . TvCategory ) )
{
2014-07-09 22:11:57 +00:00
return new NzbDroneValidationFailure ( "TvCategory" , "Category does not exist" )
{
2015-10-03 17:45:26 +00:00
InfoLink = string . Format ( "http://{0}:{1}/" , Settings . Host , Settings . Port ) ,
2014-07-09 22:11:57 +00:00
DetailedDescription = "The Category your entered doesn't exist in NzbGet. Go to NzbGet to create it."
} ;
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
}
2014-04-19 15:09:22 +00:00
2016-09-13 20:58:14 +00:00
private ValidationFailure TestSettings ( )
{
var config = _proxy . GetConfig ( Settings ) ;
var keepHistory = config . GetValueOrDefault ( "KeepHistory" ) ;
if ( keepHistory = = "0" )
{
return new NzbDroneValidationFailure ( string . Empty , "NzbGet setting KeepHistory should be greater than 0" )
{
InfoLink = string . Format ( "http://{0}:{1}/" , Settings . Host , Settings . Port ) ,
2017-01-12 02:59:13 +00:00
DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Radarr from seeing completed downloads."
2016-09-13 20:58:14 +00:00
} ;
}
return null ;
}
// Javascript doesn't support 64 bit integers natively so json officially doesn't either.
2014-04-19 15:09:22 +00:00
// NzbGet api thus sends it in two 32 bit chunks. Here we join the two chunks back together.
// Simplified decimal example: "42" splits into "4" and "2". To join them I shift (<<) the "4" 1 digit to the left = "40". combine it with "2". which becomes "42" again.
2015-10-03 17:45:26 +00:00
private long MakeInt64 ( uint high , uint low )
2014-04-19 15:09:22 +00:00
{
2015-10-03 17:45:26 +00:00
long result = high ;
2014-04-19 15:09:22 +00:00
2015-10-03 17:45:26 +00:00
result = ( result < < 32 ) | ( long ) low ;
2014-04-19 15:09:22 +00:00
return result ;
}
2014-02-14 05:31:49 +00:00
}
2016-09-13 20:58:14 +00:00
}