Compare commits
7 Commits
develop
...
transfer-p
Author | SHA1 | Date |
---|---|---|
Taloth Saldono | 942dddb5e4 | |
Taloth Saldono | 2f02f0a92d | |
Taloth Saldono | 5c1491ca06 | |
Taloth Saldono | e089bfb4d2 | |
Taloth Saldono | 2c8489de43 | |
Taloth Saldono | 74fbc97835 | |
Taloth Saldono | 2a0a4cfb6d |
|
@ -215,9 +215,11 @@
|
||||||
<Compile Include="ServiceFactory.cs" />
|
<Compile Include="ServiceFactory.cs" />
|
||||||
<Compile Include="ServiceProvider.cs" />
|
<Compile Include="ServiceProvider.cs" />
|
||||||
<Compile Include="Extensions\StringExtensions.cs" />
|
<Compile Include="Extensions\StringExtensions.cs" />
|
||||||
|
<Compile Include="Timeline\TimelineContext.cs" />
|
||||||
<Compile Include="TinyIoC.cs" />
|
<Compile Include="TinyIoC.cs" />
|
||||||
<Compile Include="TPL\Debouncer.cs" />
|
<Compile Include="TPL\Debouncer.cs" />
|
||||||
<Compile Include="TPL\LimitedConcurrencyLevelTaskScheduler.cs" />
|
<Compile Include="TPL\LimitedConcurrencyLevelTaskScheduler.cs" />
|
||||||
|
<Compile Include="Timeline\ProgressReporter.cs" />
|
||||||
<Compile Include="TPL\RateLimitService.cs" />
|
<Compile Include="TPL\RateLimitService.cs" />
|
||||||
<Compile Include="TPL\TaskExtensions.cs" />
|
<Compile Include="TPL\TaskExtensions.cs" />
|
||||||
<Compile Include="Extensions\TryParseExtensions.cs" />
|
<Compile Include="Extensions\TryParseExtensions.cs" />
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Timeline
|
||||||
|
{
|
||||||
|
public interface IProgressReporter
|
||||||
|
{
|
||||||
|
long Raw { get; }
|
||||||
|
long Total { get; }
|
||||||
|
double Progress { get; }
|
||||||
|
|
||||||
|
void UpdateProgress(long currentProgress, long maxProgress);
|
||||||
|
void FinishProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProgressReporter : IProgressReporter
|
||||||
|
{
|
||||||
|
private readonly int _maxSteps;
|
||||||
|
|
||||||
|
public long Raw { get; protected set; }
|
||||||
|
public long Total { get; private set; }
|
||||||
|
|
||||||
|
public double Progress => Total == 0 ? 1.0 : Math.Min(Raw, Total) / Total;
|
||||||
|
|
||||||
|
//public TimeSpan? EstimatedDurationRemaining { get; private set; }
|
||||||
|
|
||||||
|
public ProgressReporter(long initialProgress, long maxProgress, int maxSteps = 100)
|
||||||
|
{
|
||||||
|
_maxSteps = maxSteps;
|
||||||
|
|
||||||
|
Raw = initialProgress;
|
||||||
|
Total = maxProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateProgress(long currentProgress, long maxProgress)
|
||||||
|
{
|
||||||
|
bool shouldRaiseEvent;
|
||||||
|
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
var oldRaw = Raw;
|
||||||
|
var oldTotal = Total;
|
||||||
|
|
||||||
|
Raw = currentProgress;
|
||||||
|
Total = Total;
|
||||||
|
|
||||||
|
var oldStep = oldTotal <= 0 ? _maxSteps : oldRaw * _maxSteps / oldTotal;
|
||||||
|
var newStep = Total <= 0 ? _maxSteps : Raw * _maxSteps / Total;
|
||||||
|
|
||||||
|
shouldRaiseEvent = (oldStep != newStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldRaiseEvent)
|
||||||
|
{
|
||||||
|
RaiseEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FinishProgress()
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
Raw = Total;
|
||||||
|
}
|
||||||
|
|
||||||
|
RaiseEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void RaiseEvent()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Timeline
|
||||||
|
{
|
||||||
|
public enum TimelineState
|
||||||
|
{
|
||||||
|
Pending,
|
||||||
|
Started,
|
||||||
|
Completed,
|
||||||
|
Failed
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ITimelineContext : IProgressReporter
|
||||||
|
{
|
||||||
|
string Name { get; }
|
||||||
|
TimelineState State { get; }
|
||||||
|
|
||||||
|
void UpdateState(TimelineState state);
|
||||||
|
ITimelineContext AppendTimeline(string name, long initialProgress = 0, long maxProgress = 1);
|
||||||
|
void AppendTimeline(ITimelineContext timeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TimelineContext : ProgressReporter, ITimelineContext
|
||||||
|
{
|
||||||
|
private List<ITimelineContext> _timelines = new List<ITimelineContext>();
|
||||||
|
|
||||||
|
public string Name { get; private set; }
|
||||||
|
public TimelineState State { get; private set; }
|
||||||
|
|
||||||
|
public IEnumerable<ITimelineContext> Timelines
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
return _timelines.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimelineContext(string name, long initialProgress, long maxProgress)
|
||||||
|
: base(initialProgress, maxProgress)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(TimelineState state)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
State = state;
|
||||||
|
|
||||||
|
if (State == TimelineState.Completed || State == TimelineState.Failed)
|
||||||
|
{
|
||||||
|
Raw = Total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RaiseEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITimelineContext AppendTimeline(string name, long initialProgress = 0, long maxProgress = 1)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
var timeline = new TimelineContext(name, initialProgress, maxProgress);
|
||||||
|
_timelines.Add(timeline);
|
||||||
|
return timeline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AppendTimeline(ITimelineContext timeline)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
_timelines.Add(timeline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RaiseEvent()
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
if (Raw == Total)
|
||||||
|
{
|
||||||
|
State = TimelineState.Completed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
State = TimelineState.Started;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base.RaiseEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,6 @@ using NzbDrone.Core.Instrumentation;
|
||||||
using NzbDrone.Core.Jobs;
|
using NzbDrone.Core.Jobs;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Profiles.Delay;
|
using NzbDrone.Core.Profiles.Delay;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.Notifications;
|
using NzbDrone.Core.Notifications;
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
@ -105,8 +104,7 @@ namespace NzbDrone.Core.Datastore
|
||||||
|
|
||||||
Mapper.Entity<PendingRelease>().RegisterModel("PendingReleases")
|
Mapper.Entity<PendingRelease>().RegisterModel("PendingReleases")
|
||||||
.Ignore(e => e.RemoteEpisode);
|
.Ignore(e => e.RemoteEpisode);
|
||||||
|
|
||||||
Mapper.Entity<RemotePathMapping>().RegisterModel("RemotePathMappings");
|
|
||||||
Mapper.Entity<Tag>().RegisterModel("Tags");
|
Mapper.Entity<Tag>().RegisterModel("Tags");
|
||||||
Mapper.Entity<Restriction>().RegisterModel("Restrictions");
|
Mapper.Entity<Restriction>().RegisterModel("Restrictions");
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Blackhole
|
namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
|
@ -29,9 +28,8 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
: base(torrentFileInfoReader, httpClient, configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_scanWatchFolder = scanWatchFolder;
|
_scanWatchFolder = scanWatchFolder;
|
||||||
|
|
||||||
|
@ -99,7 +97,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
TotalSize = item.TotalSize,
|
TotalSize = item.TotalSize,
|
||||||
RemainingTime = item.RemainingTime,
|
RemainingTime = item.RemainingTime,
|
||||||
|
|
||||||
OutputPath = item.OutputPath,
|
OutputPath = new DownloadClientPath(Definition.Id, item.OutputPath),
|
||||||
|
|
||||||
Status = item.Status,
|
Status = item.Status,
|
||||||
|
|
||||||
|
@ -124,7 +122,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
return new DownloadClientInfo
|
return new DownloadClientInfo
|
||||||
{
|
{
|
||||||
IsLocalhost = true,
|
IsLocalhost = true,
|
||||||
OutputRootFolders = new List<OsPath> { new OsPath(Settings.WatchFolder) }
|
OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, new OsPath(Settings.WatchFolder)) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Blackhole
|
namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
{
|
{
|
||||||
|
@ -23,10 +22,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
IValidateNzbs nzbValidationService,
|
IValidateNzbs nzbValidationService,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger)
|
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
|
||||||
{
|
{
|
||||||
_scanWatchFolder = scanWatchFolder;
|
_scanWatchFolder = scanWatchFolder;
|
||||||
|
|
||||||
|
@ -67,7 +65,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
TotalSize = item.TotalSize,
|
TotalSize = item.TotalSize,
|
||||||
RemainingTime = item.RemainingTime,
|
RemainingTime = item.RemainingTime,
|
||||||
|
|
||||||
OutputPath = item.OutputPath,
|
OutputPath = new DownloadClientPath(Definition.Id, item.OutputPath),
|
||||||
|
|
||||||
Status = item.Status,
|
Status = item.Status,
|
||||||
|
|
||||||
|
@ -92,7 +90,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
return new DownloadClientInfo
|
return new DownloadClientInfo
|
||||||
{
|
{
|
||||||
IsLocalhost = true,
|
IsLocalhost = true,
|
||||||
OutputRootFolders = new List<OsPath> { new OsPath(Settings.WatchFolder) }
|
OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, new OsPath(Settings.WatchFolder)) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ using NzbDrone.Core.Validation;
|
||||||
using NLog;
|
using NLog;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Deluge
|
namespace NzbDrone.Core.Download.Clients.Deluge
|
||||||
{
|
{
|
||||||
|
@ -24,9 +23,8 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
: base(torrentFileInfoReader, httpClient, configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
}
|
}
|
||||||
|
@ -106,8 +104,8 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||||
|
|
||||||
item.DownloadClient = Definition.Name;
|
item.DownloadClient = Definition.Name;
|
||||||
|
|
||||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.DownloadPath));
|
var outputPath = new OsPath(torrent.DownloadPath) + torrent.Name;
|
||||||
item.OutputPath = outputPath + torrent.Name;
|
item.OutputPath = new DownloadClientPath(Definition.Id, outputPath);
|
||||||
item.RemainingSize = torrent.Size - torrent.BytesDownloaded;
|
item.RemainingSize = torrent.Size - torrent.BytesDownloaded;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -176,7 +174,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||||
|
|
||||||
if (!destDir.IsEmpty)
|
if (!destDir.IsEmpty)
|
||||||
{
|
{
|
||||||
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) };
|
status.OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, destDir) };
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -12,7 +12,6 @@ using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Download.Clients.DownloadStation.Proxies;
|
using NzbDrone.Core.Download.Clients.DownloadStation.Proxies;
|
||||||
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.DownloadStation
|
namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
|
@ -34,9 +33,8 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
: base(torrentFileInfoReader, httpClient, configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_dsInfoProxy = dsInfoProxy;
|
_dsInfoProxy = dsInfoProxy;
|
||||||
_dsTaskProxy = dsTaskProxy;
|
_dsTaskProxy = dsTaskProxy;
|
||||||
|
@ -96,7 +94,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
|
|
||||||
if (item.Status == DownloadItemStatus.Completed || item.Status == DownloadItemStatus.Failed)
|
if (item.Status == DownloadItemStatus.Completed || item.Status == DownloadItemStatus.Failed)
|
||||||
{
|
{
|
||||||
item.OutputPath = GetOutputPath(outputPath, torrent, serialNumber);
|
item.OutputPath = new DownloadClientPath(Definition.Id, GetOutputPath(outputPath, torrent, serialNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
items.Add(item);
|
items.Add(item);
|
||||||
|
@ -114,7 +112,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
return new DownloadClientInfo
|
return new DownloadClientInfo
|
||||||
{
|
{
|
||||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(path)) }
|
OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, new OsPath(path)) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (DownloadClientException e)
|
catch (DownloadClientException e)
|
||||||
|
@ -140,9 +138,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
{
|
{
|
||||||
var fullPath = _sharedFolderResolver.RemapToFullPath(outputPath, Settings, serialNumber);
|
var fullPath = _sharedFolderResolver.RemapToFullPath(outputPath, Settings, serialNumber);
|
||||||
|
|
||||||
var remotePath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, fullPath);
|
var finalPath = fullPath + torrent.Title;
|
||||||
|
|
||||||
var finalPath = remotePath + torrent.Title;
|
|
||||||
|
|
||||||
return finalPath;
|
return finalPath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Download.Clients.DownloadStation.Proxies;
|
using NzbDrone.Core.Download.Clients.DownloadStation.Proxies;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.DownloadStation
|
namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
|
@ -31,11 +30,10 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
IValidateNzbs nzbValidationService,
|
IValidateNzbs nzbValidationService,
|
||||||
Logger logger
|
Logger logger
|
||||||
)
|
)
|
||||||
: base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger)
|
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
|
||||||
{
|
{
|
||||||
_dsInfoProxy = dsInfoProxy;
|
_dsInfoProxy = dsInfoProxy;
|
||||||
_dsTaskProxy = dsTaskProxy;
|
_dsTaskProxy = dsTaskProxy;
|
||||||
|
@ -111,7 +109,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
|
|
||||||
if (item.Status == DownloadItemStatus.Completed || item.Status == DownloadItemStatus.Failed)
|
if (item.Status == DownloadItemStatus.Completed || item.Status == DownloadItemStatus.Failed)
|
||||||
{
|
{
|
||||||
item.OutputPath = GetOutputPath(outputPath, nzb, serialNumber);
|
item.OutputPath = new DownloadClientPath(Definition.Id, GetOutputPath(outputPath, nzb, serialNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
items.Add(item);
|
items.Add(item);
|
||||||
|
@ -124,7 +122,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
{
|
{
|
||||||
var fullPath = _sharedFolderResolver.RemapToFullPath(outputPath, Settings, serialNumber);
|
var fullPath = _sharedFolderResolver.RemapToFullPath(outputPath, Settings, serialNumber);
|
||||||
|
|
||||||
var remotePath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, fullPath);
|
var remotePath = fullPath;
|
||||||
|
|
||||||
var finalPath = remotePath + task.Title;
|
var finalPath = remotePath + task.Title;
|
||||||
|
|
||||||
|
@ -140,7 +138,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
return new DownloadClientInfo
|
return new DownloadClientInfo
|
||||||
{
|
{
|
||||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(path)) }
|
OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, new OsPath(path)) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (DownloadClientException e)
|
catch (DownloadClientException e)
|
||||||
|
|
|
@ -10,7 +10,6 @@ using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Download.Clients.Hadouken.Models;
|
using NzbDrone.Core.Download.Clients.Hadouken.Models;
|
||||||
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Hadouken
|
namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||||
|
@ -24,9 +23,8 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
: base(torrentFileInfoReader, httpClient, configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
}
|
}
|
||||||
|
@ -46,7 +44,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.SavePath));
|
var outputPath = new OsPath(torrent.SavePath);
|
||||||
var eta = TimeSpan.FromSeconds(0);
|
var eta = TimeSpan.FromSeconds(0);
|
||||||
|
|
||||||
if (torrent.DownloadRate > 0 && torrent.TotalSize > 0)
|
if (torrent.DownloadRate > 0 && torrent.TotalSize > 0)
|
||||||
|
@ -58,7 +56,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||||
{
|
{
|
||||||
DownloadClient = Definition.Name,
|
DownloadClient = Definition.Name,
|
||||||
DownloadId = torrent.InfoHash.ToUpper(),
|
DownloadId = torrent.InfoHash.ToUpper(),
|
||||||
OutputPath = outputPath + torrent.Name,
|
OutputPath = new DownloadClientPath(Definition.Id, outputPath + torrent.Name),
|
||||||
RemainingSize = torrent.TotalSize - torrent.DownloadedBytes,
|
RemainingSize = torrent.TotalSize - torrent.DownloadedBytes,
|
||||||
RemainingTime = eta,
|
RemainingTime = eta,
|
||||||
Title = torrent.Name,
|
Title = torrent.Name,
|
||||||
|
@ -119,7 +117,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||||
|
|
||||||
if (!destDir.IsEmpty)
|
if (!destDir.IsEmpty)
|
||||||
{
|
{
|
||||||
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) };
|
status.OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, destDir) };
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -10,7 +10,6 @@ using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.NzbVortex
|
namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||||
{
|
{
|
||||||
|
@ -22,10 +21,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
IValidateNzbs nzbValidationService,
|
IValidateNzbs nzbValidationService,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger)
|
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +86,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
queueItem.OutputPath = GetOutputPath(vortexQueueItem, queueItem);
|
queueItem.OutputPath = new DownloadClientPath(Definition.Id, GetOutputPath(vortexQueueItem, queueItem));
|
||||||
|
|
||||||
if (vortexQueueItem.State == NzbVortexStateType.PasswordRequest)
|
if (vortexQueueItem.State == NzbVortexStateType.PasswordRequest)
|
||||||
{
|
{
|
||||||
|
@ -221,7 +219,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||||
|
|
||||||
private OsPath GetOutputPath(NzbVortexQueueItem vortexQueueItem, DownloadClientItem queueItem)
|
private OsPath GetOutputPath(NzbVortexQueueItem vortexQueueItem, DownloadClientItem queueItem)
|
||||||
{
|
{
|
||||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(vortexQueueItem.DestinationPath));
|
var outputPath = new OsPath(vortexQueueItem.DestinationPath);
|
||||||
|
|
||||||
if (outputPath.FileName == vortexQueueItem.UiTitle)
|
if (outputPath.FileName == vortexQueueItem.UiTitle)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,7 +10,6 @@ using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Nzbget
|
namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||||
|
@ -25,10 +24,9 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
IValidateNzbs nzbValidationService,
|
IValidateNzbs nzbValidationService,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger)
|
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +121,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||||
historyItem.DownloadId = droneParameter == null ? item.Id.ToString() : droneParameter.Value.ToString();
|
historyItem.DownloadId = droneParameter == null ? item.Id.ToString() : droneParameter.Value.ToString();
|
||||||
historyItem.Title = item.Name;
|
historyItem.Title = item.Name;
|
||||||
historyItem.TotalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo);
|
historyItem.TotalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo);
|
||||||
historyItem.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(item.DestDir));
|
historyItem.OutputPath = new DownloadClientPath(Definition.Id, new OsPath(item.DestDir));
|
||||||
historyItem.Category = item.Category;
|
historyItem.Category = item.Category;
|
||||||
historyItem.Message = $"PAR Status: {item.ParStatus} - Unpack Status: {item.UnpackStatus} - Move Status: {item.MoveStatus} - Script Status: {item.ScriptStatus} - Delete Status: {item.DeleteStatus} - Mark Status: {item.MarkStatus}";
|
historyItem.Message = $"PAR Status: {item.ParStatus} - Unpack Status: {item.UnpackStatus} - Move Status: {item.MoveStatus} - Script Status: {item.ScriptStatus} - Delete Status: {item.DeleteStatus} - Mark Status: {item.MarkStatus}";
|
||||||
historyItem.Status = DownloadItemStatus.Completed;
|
historyItem.Status = DownloadItemStatus.Completed;
|
||||||
|
@ -208,7 +206,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||||
|
|
||||||
if (category != null)
|
if (category != null)
|
||||||
{
|
{
|
||||||
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(category.DestDir)) };
|
status.OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, new OsPath(category.DestDir)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -8,7 +8,6 @@ using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Indexers;
|
using NzbDrone.Core.Indexers;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
||||||
|
@ -21,9 +20,8 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
||||||
public Pneumatic(IHttpClient httpClient,
|
public Pneumatic(IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(configService, diskProvider, remotePathMappingService, logger)
|
: base(configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
@ -82,7 +80,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
||||||
|
|
||||||
TotalSize = _diskProvider.GetFileSize(file),
|
TotalSize = _diskProvider.GetFileSize(file),
|
||||||
|
|
||||||
OutputPath = new OsPath(file)
|
OutputPath = new DownloadClientPath(Definition.Id, new OsPath(file))
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_diskProvider.IsFileLocked(file))
|
if (_diskProvider.IsFileLocked(file))
|
||||||
|
|
|
@ -11,7 +11,6 @@ using NzbDrone.Core.Validation;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.QBittorrent
|
namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||||
{
|
{
|
||||||
|
@ -24,9 +23,8 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
: base(torrentFileInfoReader, httpClient, configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
}
|
}
|
||||||
|
@ -109,16 +107,17 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||||
item.RemainingSize = (long)(torrent.Size * (1.0 - torrent.Progress));
|
item.RemainingSize = (long)(torrent.Size * (1.0 - torrent.Progress));
|
||||||
item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta);
|
item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta);
|
||||||
|
|
||||||
item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.SavePath));
|
var outputPath = new OsPath(torrent.SavePath);
|
||||||
|
if (!outputPath.IsEmpty && outputPath.FileName != torrent.Name)
|
||||||
|
{
|
||||||
|
outputPath += torrent.Name;
|
||||||
|
}
|
||||||
|
item.OutputPath = new DownloadClientPath(Definition.Id, outputPath);
|
||||||
|
|
||||||
// Avoid removing torrents that haven't reached the global max ratio.
|
// Avoid removing torrents that haven't reached the global max ratio.
|
||||||
// Removal also requires the torrent to be paused, in case a higher max ratio was set on the torrent itself (which is not exposed by the api).
|
// Removal also requires the torrent to be paused, in case a higher max ratio was set on the torrent itself (which is not exposed by the api).
|
||||||
item.CanMoveFiles = item.CanBeRemoved = (!config.MaxRatioEnabled || config.MaxRatio <= torrent.Ratio) && torrent.State == "pausedUP";
|
item.CanMoveFiles = item.CanBeRemoved = (!config.MaxRatioEnabled || config.MaxRatio <= torrent.Ratio) && torrent.State == "pausedUP";
|
||||||
|
|
||||||
if (!item.OutputPath.IsEmpty && item.OutputPath.FileName != torrent.Name)
|
|
||||||
{
|
|
||||||
item.OutputPath += torrent.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (torrent.State)
|
switch (torrent.State)
|
||||||
{
|
{
|
||||||
|
@ -176,7 +175,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||||
return new DownloadClientInfo
|
return new DownloadClientInfo
|
||||||
{
|
{
|
||||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) }
|
OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, destDir) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||||
{
|
{
|
||||||
|
@ -22,10 +21,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
IValidateNzbs nzbValidationService,
|
IValidateNzbs nzbValidationService,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger)
|
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
}
|
}
|
||||||
|
@ -163,23 +161,22 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||||
historyItem.Status = DownloadItemStatus.Downloading;
|
historyItem.Status = DownloadItemStatus.Downloading;
|
||||||
}
|
}
|
||||||
|
|
||||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(sabHistoryItem.Storage));
|
var outputPath = new OsPath(sabHistoryItem.Storage);
|
||||||
|
|
||||||
if (!outputPath.IsEmpty)
|
if (!outputPath.IsEmpty)
|
||||||
{
|
{
|
||||||
historyItem.OutputPath = outputPath;
|
|
||||||
|
|
||||||
var parent = outputPath.Directory;
|
var parent = outputPath.Directory;
|
||||||
while (!parent.IsEmpty)
|
while (!parent.IsEmpty)
|
||||||
{
|
{
|
||||||
if (parent.FileName == sabHistoryItem.Title)
|
if (parent.FileName == sabHistoryItem.Title)
|
||||||
{
|
{
|
||||||
historyItem.OutputPath = parent;
|
outputPath = parent;
|
||||||
}
|
}
|
||||||
parent = parent.Directory;
|
parent = parent.Directory;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
historyItem.OutputPath = new DownloadClientPath(Definition.Id, outputPath);
|
||||||
|
}
|
||||||
|
|
||||||
historyItems.Add(historyItem);
|
historyItems.Add(historyItem);
|
||||||
}
|
}
|
||||||
|
@ -261,7 +258,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||||
|
|
||||||
if (category != null)
|
if (category != null)
|
||||||
{
|
{
|
||||||
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, category.FullPath) };
|
status.OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, category.FullPath) };
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -6,7 +6,6 @@ using NzbDrone.Core.Configuration;
|
||||||
using NLog;
|
using NLog;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Transmission
|
namespace NzbDrone.Core.Download.Clients.Transmission
|
||||||
{
|
{
|
||||||
|
@ -17,9 +16,8 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
: base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Transmission
|
namespace NzbDrone.Core.Download.Clients.Transmission
|
||||||
|
@ -24,9 +23,8 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
: base(torrentFileInfoReader, httpClient, configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
}
|
}
|
||||||
|
@ -54,8 +52,6 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||||
if (!directories.Contains(Settings.TvCategory)) continue;
|
if (!directories.Contains(Settings.TvCategory)) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
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.TvCategory;
|
||||||
|
@ -63,7 +59,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||||
|
|
||||||
item.DownloadClient = Definition.Name;
|
item.DownloadClient = Definition.Name;
|
||||||
|
|
||||||
item.OutputPath = GetOutputPath(outputPath, torrent);
|
item.OutputPath = new DownloadClientPath(Definition.Id, GetOutputPath(outputPath, torrent));
|
||||||
item.TotalSize = torrent.TotalSize;
|
item.TotalSize = torrent.TotalSize;
|
||||||
item.RemainingSize = torrent.LeftUntilDone;
|
item.RemainingSize = torrent.LeftUntilDone;
|
||||||
if (torrent.Eta >= 0)
|
if (torrent.Eta >= 0)
|
||||||
|
@ -122,7 +118,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||||
return new DownloadClientInfo
|
return new DownloadClientInfo
|
||||||
{
|
{
|
||||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(destDir)) }
|
OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, new OsPath(destDir)) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Download.Clients.Transmission;
|
using NzbDrone.Core.Download.Clients.Transmission;
|
||||||
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Vuze
|
namespace NzbDrone.Core.Download.Clients.Vuze
|
||||||
{
|
{
|
||||||
|
@ -18,9 +17,8 @@ namespace NzbDrone.Core.Download.Clients.Vuze
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
: base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ using FluentValidation.Results;
|
||||||
using NzbDrone.Core.Download.Clients.rTorrent;
|
using NzbDrone.Core.Download.Clients.rTorrent;
|
||||||
using NzbDrone.Core.Exceptions;
|
using NzbDrone.Core.Exceptions;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.RTorrent
|
namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
|
@ -28,10 +27,9 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
IRTorrentDirectoryValidator rTorrentDirectoryValidator,
|
IRTorrentDirectoryValidator rTorrentDirectoryValidator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
: base(torrentFileInfoReader, httpClient, configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
_rTorrentDirectoryValidator = rTorrentDirectoryValidator;
|
_rTorrentDirectoryValidator = rTorrentDirectoryValidator;
|
||||||
|
@ -100,7 +98,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
item.DownloadClient = Definition.Name;
|
item.DownloadClient = Definition.Name;
|
||||||
item.Title = torrent.Name;
|
item.Title = torrent.Name;
|
||||||
item.DownloadId = torrent.Hash;
|
item.DownloadId = torrent.Hash;
|
||||||
item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.Path));
|
item.OutputPath = new DownloadClientPath(Definition.Id, new OsPath(torrent.Path));
|
||||||
item.TotalSize = torrent.TotalSize;
|
item.TotalSize = torrent.TotalSize;
|
||||||
item.RemainingSize = torrent.RemainingSize;
|
item.RemainingSize = torrent.RemainingSize;
|
||||||
item.Category = torrent.Category;
|
item.Category = torrent.Category;
|
||||||
|
|
|
@ -11,7 +11,6 @@ using NzbDrone.Core.Validation;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Common.Cache;
|
using NzbDrone.Common.Cache;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.UTorrent
|
namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||||
|
@ -27,9 +26,8 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
: base(torrentFileInfoReader, httpClient, configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
|
|
||||||
|
@ -99,16 +97,13 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||||
item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta);
|
item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta);
|
||||||
}
|
}
|
||||||
|
|
||||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.RootDownloadPath));
|
var outputPath = new OsPath(torrent.RootDownloadPath);
|
||||||
|
|
||||||
if (outputPath == null || outputPath.FileName == torrent.Name)
|
if (outputPath != null && outputPath.FileName != torrent.Name)
|
||||||
{
|
{
|
||||||
item.OutputPath = outputPath;
|
outputPath += torrent.Name;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
item.OutputPath = outputPath + torrent.Name;
|
|
||||||
}
|
}
|
||||||
|
item.OutputPath = new DownloadClientPath(Definition.Id, outputPath);
|
||||||
|
|
||||||
if (torrent.Status.HasFlag(UTorrentTorrentStatus.Error))
|
if (torrent.Status.HasFlag(UTorrentTorrentStatus.Error))
|
||||||
{
|
{
|
||||||
|
@ -209,7 +204,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||||
|
|
||||||
if (!destDir.IsEmpty)
|
if (!destDir.IsEmpty)
|
||||||
{
|
{
|
||||||
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) };
|
status.OutputRootFolders = new List<DownloadClientPath> { new DownloadClientPath(Definition.Id, destDir) };
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -8,7 +8,6 @@ using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Indexers;
|
using NzbDrone.Core.Indexers;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
@ -19,7 +18,6 @@ namespace NzbDrone.Core.Download
|
||||||
{
|
{
|
||||||
protected readonly IConfigService _configService;
|
protected readonly IConfigService _configService;
|
||||||
protected readonly IDiskProvider _diskProvider;
|
protected readonly IDiskProvider _diskProvider;
|
||||||
protected readonly IRemotePathMappingService _remotePathMappingService;
|
|
||||||
protected readonly Logger _logger;
|
protected readonly Logger _logger;
|
||||||
|
|
||||||
public abstract string Name { get; }
|
public abstract string Name { get; }
|
||||||
|
@ -36,14 +34,12 @@ namespace NzbDrone.Core.Download
|
||||||
|
|
||||||
protected TSettings Settings => (TSettings)Definition.Settings;
|
protected TSettings Settings => (TSettings)Definition.Settings;
|
||||||
|
|
||||||
protected DownloadClientBase(IConfigService configService,
|
protected DownloadClientBase(IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
_remotePathMappingService = remotePathMappingService;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +72,9 @@ namespace NzbDrone.Core.Download
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Doesn't work if remote.
|
||||||
|
throw new NotSupportedException();
|
||||||
|
/*
|
||||||
if (item.OutputPath.IsEmpty)
|
if (item.OutputPath.IsEmpty)
|
||||||
{
|
{
|
||||||
_logger.Trace("[{0}] Doesn't have an outputPath, skipping delete data.", item.Title);
|
_logger.Trace("[{0}] Doesn't have an outputPath, skipping delete data.", item.Title);
|
||||||
|
@ -104,7 +103,7 @@ namespace NzbDrone.Core.Download
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Warn(ex, string.Format("[{0}] Error occurred while trying to delete data from '{1}'.", item.Title, item.OutputPath));
|
_logger.Warn(ex, string.Format("[{0}] Error occurred while trying to delete data from '{1}'.", item.Title, item.OutputPath));
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidationResult Test()
|
public ValidationResult Test()
|
||||||
|
|
|
@ -6,6 +6,6 @@ namespace NzbDrone.Core.Download
|
||||||
public class DownloadClientInfo
|
public class DownloadClientInfo
|
||||||
{
|
{
|
||||||
public bool IsLocalhost { get; set; }
|
public bool IsLocalhost { get; set; }
|
||||||
public List<OsPath> OutputRootFolders { get; set; }
|
public List<DownloadClientPath> OutputRootFolders { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace NzbDrone.Core.Download
|
||||||
public long RemainingSize { get; set; }
|
public long RemainingSize { get; set; }
|
||||||
public TimeSpan? RemainingTime { get; set; }
|
public TimeSpan? RemainingTime { get; set; }
|
||||||
|
|
||||||
public OsPath OutputPath { get; set; }
|
public DownloadClientPath OutputPath { get; set; }
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
|
|
||||||
public DownloadItemStatus Status { get; set; }
|
public DownloadItemStatus Status { get; set; }
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download
|
||||||
|
{
|
||||||
|
public class DownloadClientPath
|
||||||
|
{
|
||||||
|
public int DownloadClientId { get; set; }
|
||||||
|
public OsPath Path { get; set; }
|
||||||
|
|
||||||
|
public DownloadClientPath(int downloadClientId, OsPath path)
|
||||||
|
{
|
||||||
|
DownloadClientId = downloadClientId;
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download
|
namespace NzbDrone.Core.Download
|
||||||
{
|
{
|
||||||
|
@ -26,9 +25,8 @@ namespace NzbDrone.Core.Download
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(configService, diskProvider, remotePathMappingService, logger)
|
: base(configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_torrentFileInfoReader = torrentFileInfoReader;
|
_torrentFileInfoReader = torrentFileInfoReader;
|
||||||
|
|
|
@ -8,7 +8,6 @@ using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download
|
namespace NzbDrone.Core.Download
|
||||||
{
|
{
|
||||||
|
@ -21,10 +20,9 @@ namespace NzbDrone.Core.Download
|
||||||
protected UsenetClientBase(IHttpClient httpClient,
|
protected UsenetClientBase(IHttpClient httpClient,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IRemotePathMappingService remotePathMappingService,
|
|
||||||
IValidateNzbs nzbValidationService,
|
IValidateNzbs nzbValidationService,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(configService, diskProvider, remotePathMappingService, logger)
|
: base(configService, diskProvider, logger)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_nzbValidationService = nzbValidationService;
|
_nzbValidationService = nzbValidationService;
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.Configuration.Events;
|
|
||||||
using NzbDrone.Core.Download;
|
|
||||||
using NzbDrone.Core.Download.Clients;
|
|
||||||
using NzbDrone.Core.Download.Clients.Nzbget;
|
|
||||||
using NzbDrone.Core.Download.Clients.Sabnzbd;
|
|
||||||
using NzbDrone.Core.ThingiProvider.Events;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.HealthCheck.Checks
|
|
||||||
{
|
|
||||||
[CheckOn(typeof(ProviderUpdatedEvent<IDownloadClient>))]
|
|
||||||
[CheckOn(typeof(ProviderDeletedEvent<IDownloadClient>))]
|
|
||||||
[CheckOn(typeof(ConfigSavedEvent))]
|
|
||||||
public class ImportMechanismCheck : HealthCheckBase
|
|
||||||
{
|
|
||||||
private readonly IConfigService _configService;
|
|
||||||
private readonly IProvideDownloadClient _provideDownloadClient;
|
|
||||||
|
|
||||||
|
|
||||||
public ImportMechanismCheck(IConfigService configService, IProvideDownloadClient provideDownloadClient)
|
|
||||||
{
|
|
||||||
_configService = configService;
|
|
||||||
_provideDownloadClient = provideDownloadClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override HealthCheck Check()
|
|
||||||
{
|
|
||||||
var droneFactoryFolder = new OsPath(_configService.DownloadedEpisodesFolder);
|
|
||||||
List<ImportMechanismCheckStatus> downloadClients;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
downloadClients = _provideDownloadClient.GetDownloadClients().Select(v => new ImportMechanismCheckStatus
|
|
||||||
{
|
|
||||||
DownloadClient = v,
|
|
||||||
Status = v.GetStatus()
|
|
||||||
}).ToList();
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// One or more download clients failed, assume the health is okay and verify later
|
|
||||||
return new HealthCheck(GetType());
|
|
||||||
}
|
|
||||||
|
|
||||||
var downloadClientIsLocalHost = downloadClients.All(v => v.Status.IsLocalhost);
|
|
||||||
var downloadClientOutputInDroneFactory = !droneFactoryFolder.IsEmpty &&
|
|
||||||
downloadClients.Any(v => v.Status.OutputRootFolders != null &&
|
|
||||||
v.Status.OutputRootFolders.Any(droneFactoryFolder.Contains));
|
|
||||||
|
|
||||||
if (!_configService.IsDefined("EnableCompletedDownloadHandling"))
|
|
||||||
{
|
|
||||||
// Migration helper logic
|
|
||||||
if (!downloadClientIsLocalHost)
|
|
||||||
{
|
|
||||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Multi-Computer unsupported)", "Migrating-to-Completed-Download-Handling#Unsupported-download-client-on-different-computer");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloadClients.All(v => v.DownloadClient is Sabnzbd))
|
|
||||||
{
|
|
||||||
// With Sabnzbd we can check if the category should be changed.
|
|
||||||
if (downloadClientOutputInDroneFactory)
|
|
||||||
{
|
|
||||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Sabnzbd - Conflicting Category)", "Migrating-to-Completed-Download-Handling#sabnzbd-conflicting-download-client-category");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Sabnzbd)", "Migrating-to-Completed-Download-Handling#sabnzbd-enable-completed-download-handling");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloadClients.All(v => v.DownloadClient is Nzbget))
|
|
||||||
{
|
|
||||||
// With Nzbget we can check if the category should be changed.
|
|
||||||
if (downloadClientOutputInDroneFactory)
|
|
||||||
{
|
|
||||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Nzbget - Conflicting Category)", "Migrating-to-Completed-Download-Handling#nzbget-conflicting-download-client-category");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Nzbget)", "Migrating-to-Completed-Download-Handling#nzbget-enable-completed-download-handling");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible", "Migrating-to-Completed-Download-Handling");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_configService.EnableCompletedDownloadHandling && droneFactoryFolder.IsEmpty)
|
|
||||||
{
|
|
||||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling or configure Drone factory");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new HealthCheck(GetType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ImportMechanismCheckStatus
|
|
||||||
{
|
|
||||||
public IDownloadClient DownloadClient { get; set; }
|
|
||||||
public DownloadClientInfo Status { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -503,6 +503,7 @@
|
||||||
<Compile Include="Download\Clients\uTorrent\UTorrentTorrentStatus.cs" />
|
<Compile Include="Download\Clients\uTorrent\UTorrentTorrentStatus.cs" />
|
||||||
<Compile Include="Download\Clients\Vuze\Vuze.cs" />
|
<Compile Include="Download\Clients\Vuze\Vuze.cs" />
|
||||||
<Compile Include="Download\CompletedDownloadService.cs" />
|
<Compile Include="Download\CompletedDownloadService.cs" />
|
||||||
|
<Compile Include="Download\DownloadClientPath.cs" />
|
||||||
<Compile Include="Download\DownloadClientStatus.cs" />
|
<Compile Include="Download\DownloadClientStatus.cs" />
|
||||||
<Compile Include="Download\DownloadEventHub.cs" />
|
<Compile Include="Download\DownloadEventHub.cs" />
|
||||||
<Compile Include="Download\DownloadClientStatusRepository.cs" />
|
<Compile Include="Download\DownloadClientStatusRepository.cs" />
|
||||||
|
@ -578,7 +579,6 @@
|
||||||
<Compile Include="HealthCheck\Checks\MonoTlsCheck.cs" />
|
<Compile Include="HealthCheck\Checks\MonoTlsCheck.cs" />
|
||||||
<Compile Include="HealthCheck\Checks\MountCheck.cs" />
|
<Compile Include="HealthCheck\Checks\MountCheck.cs" />
|
||||||
<Compile Include="HealthCheck\Checks\DroneFactoryCheck.cs" />
|
<Compile Include="HealthCheck\Checks\DroneFactoryCheck.cs" />
|
||||||
<Compile Include="HealthCheck\Checks\ImportMechanismCheck.cs" />
|
|
||||||
<Compile Include="HealthCheck\Checks\IndexerRssCheck.cs" />
|
<Compile Include="HealthCheck\Checks\IndexerRssCheck.cs" />
|
||||||
<Compile Include="HealthCheck\Checks\IndexerStatusCheck.cs" />
|
<Compile Include="HealthCheck\Checks\IndexerStatusCheck.cs" />
|
||||||
<Compile Include="HealthCheck\Checks\IndexerSearchCheck.cs" />
|
<Compile Include="HealthCheck\Checks\IndexerSearchCheck.cs" />
|
||||||
|
@ -946,9 +946,16 @@
|
||||||
<Compile Include="ProgressMessaging\ProgressMessageContext.cs" />
|
<Compile Include="ProgressMessaging\ProgressMessageContext.cs" />
|
||||||
<Compile Include="Qualities\QualitySource.cs" />
|
<Compile Include="Qualities\QualitySource.cs" />
|
||||||
<Compile Include="Qualities\Revision.cs" />
|
<Compile Include="Qualities\Revision.cs" />
|
||||||
<Compile Include="RemotePathMappings\RemotePathMapping.cs" />
|
<Compile Include="TransferProviders\Providers\DefaultTransfer.cs" />
|
||||||
<Compile Include="RemotePathMappings\RemotePathMappingRepository.cs" />
|
<Compile Include="TransferProviders\Providers\DirectVirtualDiskProvider.cs" />
|
||||||
<Compile Include="RemotePathMappings\RemotePathMappingService.cs" />
|
<Compile Include="TransferProviders\Providers\EmptyVirtualDiskProvider.cs" />
|
||||||
|
<Compile Include="TransferProviders\Providers\MountVirtualDiskProvider.cs" />
|
||||||
|
<Compile Include="TransferProviders\TransferProviderDefinition.cs" />
|
||||||
|
<Compile Include="TransferProviders\TransferProviderRepository.cs" />
|
||||||
|
<Compile Include="TransferProviders\Providers\CustomTransfer.cs" />
|
||||||
|
<Compile Include="TransferProviders\ITransferProvider.cs" />
|
||||||
|
<Compile Include="TransferProviders\IVirtualDiskProvider.cs" />
|
||||||
|
<Compile Include="TransferProviders\Providers\MountTransfer.cs" />
|
||||||
<Compile Include="MediaFiles\TorrentInfo\TorrentFileInfoReader.cs" />
|
<Compile Include="MediaFiles\TorrentInfo\TorrentFileInfoReader.cs" />
|
||||||
<Compile Include="Notifications\DownloadMessage.cs" />
|
<Compile Include="Notifications\DownloadMessage.cs" />
|
||||||
<Compile Include="Notifications\Email\Email.cs">
|
<Compile Include="Notifications\Email\Email.cs">
|
||||||
|
@ -1122,6 +1129,10 @@
|
||||||
<Compile Include="ThingiProvider\Status\ProviderStatusRepository.cs" />
|
<Compile Include="ThingiProvider\Status\ProviderStatusRepository.cs" />
|
||||||
<Compile Include="ThingiProvider\Status\ProviderStatusServiceBase.cs" />
|
<Compile Include="ThingiProvider\Status\ProviderStatusServiceBase.cs" />
|
||||||
<Compile Include="TinyTwitter.cs" />
|
<Compile Include="TinyTwitter.cs" />
|
||||||
|
<Compile Include="TransferProviders\TransferProviderBase.cs" />
|
||||||
|
<Compile Include="TransferProviders\TransferProviderFactory.cs" />
|
||||||
|
<Compile Include="TransferProviders\TransferProviderService.cs" />
|
||||||
|
<Compile Include="TransferProviders\TransferTask.cs" />
|
||||||
<Compile Include="Tv\Actor.cs" />
|
<Compile Include="Tv\Actor.cs" />
|
||||||
<Compile Include="Tv\AddSeriesOptions.cs" />
|
<Compile Include="Tv\AddSeriesOptions.cs" />
|
||||||
<Compile Include="Tv\AddSeriesService.cs" />
|
<Compile Include="Tv\AddSeriesService.cs" />
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.RemotePathMappings
|
|
||||||
{
|
|
||||||
public class RemotePathMapping : ModelBase
|
|
||||||
{
|
|
||||||
public string Host { get; set; }
|
|
||||||
public string RemotePath { get; set; }
|
|
||||||
public string LocalPath { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.RemotePathMappings
|
|
||||||
{
|
|
||||||
public interface IRemotePathMappingRepository : IBasicRepository<RemotePathMapping>
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RemotePathMappingRepository : BasicRepository<RemotePathMapping>, IRemotePathMappingRepository
|
|
||||||
{
|
|
||||||
|
|
||||||
public RemotePathMappingRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
|
||||||
: base(database, eventAggregator)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool PublishModelEvents => true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
using System.Linq;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Common.Cache;
|
|
||||||
using NzbDrone.Core.Download;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.RemotePathMappings
|
|
||||||
{
|
|
||||||
public interface IRemotePathMappingService
|
|
||||||
{
|
|
||||||
List<RemotePathMapping> All();
|
|
||||||
RemotePathMapping Add(RemotePathMapping mapping);
|
|
||||||
void Remove(int id);
|
|
||||||
RemotePathMapping Get(int id);
|
|
||||||
RemotePathMapping Update(RemotePathMapping mapping);
|
|
||||||
|
|
||||||
OsPath RemapRemoteToLocal(string host, OsPath remotePath);
|
|
||||||
OsPath RemapLocalToRemote(string host, OsPath localPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RemotePathMappingService : IRemotePathMappingService
|
|
||||||
{
|
|
||||||
private readonly IRemotePathMappingRepository _remotePathMappingRepository;
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
private readonly ICached<List<RemotePathMapping>> _cache;
|
|
||||||
|
|
||||||
public RemotePathMappingService(IDownloadClientRepository downloadClientRepository,
|
|
||||||
IRemotePathMappingRepository remotePathMappingRepository,
|
|
||||||
IDiskProvider diskProvider,
|
|
||||||
ICacheManager cacheManager,
|
|
||||||
Logger logger)
|
|
||||||
{
|
|
||||||
_remotePathMappingRepository = remotePathMappingRepository;
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
_logger = logger;
|
|
||||||
|
|
||||||
_cache = cacheManager.GetCache<List<RemotePathMapping>>(GetType());
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<RemotePathMapping> All()
|
|
||||||
{
|
|
||||||
return _cache.Get("all", () => _remotePathMappingRepository.All().ToList(), TimeSpan.FromSeconds(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
public RemotePathMapping Add(RemotePathMapping mapping)
|
|
||||||
{
|
|
||||||
mapping.LocalPath = new OsPath(mapping.LocalPath).AsDirectory().FullPath;
|
|
||||||
mapping.RemotePath = new OsPath(mapping.RemotePath).AsDirectory().FullPath;
|
|
||||||
|
|
||||||
var all = All();
|
|
||||||
|
|
||||||
ValidateMapping(all, mapping);
|
|
||||||
|
|
||||||
var result = _remotePathMappingRepository.Insert(mapping);
|
|
||||||
|
|
||||||
_cache.Clear();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Remove(int id)
|
|
||||||
{
|
|
||||||
_remotePathMappingRepository.Delete(id);
|
|
||||||
|
|
||||||
_cache.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public RemotePathMapping Get(int id)
|
|
||||||
{
|
|
||||||
return _remotePathMappingRepository.Get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RemotePathMapping Update(RemotePathMapping mapping)
|
|
||||||
{
|
|
||||||
var existing = All().Where(v => v.Id != mapping.Id).ToList();
|
|
||||||
|
|
||||||
ValidateMapping(existing, mapping);
|
|
||||||
|
|
||||||
var result = _remotePathMappingRepository.Update(mapping);
|
|
||||||
|
|
||||||
_cache.Clear();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ValidateMapping(List<RemotePathMapping> existing, RemotePathMapping mapping)
|
|
||||||
{
|
|
||||||
if (mapping.Host.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid Host");
|
|
||||||
}
|
|
||||||
|
|
||||||
var remotePath = new OsPath(mapping.RemotePath);
|
|
||||||
var localPath = new OsPath(mapping.LocalPath);
|
|
||||||
|
|
||||||
if (remotePath.IsEmpty)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid RemotePath");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localPath.IsEmpty || !localPath.IsRooted)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid LocalPath");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(localPath.FullPath))
|
|
||||||
{
|
|
||||||
throw new DirectoryNotFoundException("Can't add mount point directory that doesn't exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existing.Exists(r => r.Host == mapping.Host && r.RemotePath == mapping.RemotePath))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("RemotePath already mounted.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsPath RemapRemoteToLocal(string host, OsPath remotePath)
|
|
||||||
{
|
|
||||||
if (remotePath.IsEmpty)
|
|
||||||
{
|
|
||||||
return remotePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var mapping in All())
|
|
||||||
{
|
|
||||||
if (host == mapping.Host && new OsPath(mapping.RemotePath).Contains(remotePath))
|
|
||||||
{
|
|
||||||
var localPath = new OsPath(mapping.LocalPath) + (remotePath - new OsPath(mapping.RemotePath));
|
|
||||||
|
|
||||||
return localPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return remotePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsPath RemapLocalToRemote(string host, OsPath localPath)
|
|
||||||
{
|
|
||||||
if (localPath.IsEmpty)
|
|
||||||
{
|
|
||||||
return localPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var mapping in All())
|
|
||||||
{
|
|
||||||
if (host == mapping.Host && new OsPath(mapping.LocalPath).Contains(localPath))
|
|
||||||
{
|
|
||||||
var remotePath = new OsPath(mapping.RemotePath) + (localPath - new OsPath(mapping.LocalPath));
|
|
||||||
|
|
||||||
return remotePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return localPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NzbDrone.Core.Download;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders
|
||||||
|
{
|
||||||
|
public interface ITransferProvider : IProvider
|
||||||
|
{
|
||||||
|
// Whether the TransferProvider is ready to be accessed. (Useful for external transfers that may not have finished yet)
|
||||||
|
bool IsAvailable(DownloadClientPath item);
|
||||||
|
|
||||||
|
// Returns a wrapper for the specific download. Optionally we might want to supply a 'tempPath' that's close to the series path, in case the TransferProvider needs an intermediate location.
|
||||||
|
IVirtualDiskProvider GetFileSystemWrapper(DownloadClientPath item, string tempPath = null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders
|
||||||
|
{
|
||||||
|
// Represents the remote filesystem, or contents of rar, or ... etc.
|
||||||
|
// Any Move/Copy action should return an asynchroneous context representing the transfer in progress. So it can be shown in CDH / Activity->Queue.
|
||||||
|
public interface IVirtualDiskProvider // : IDiskProvider
|
||||||
|
{
|
||||||
|
// Whether the VirtualFileSystem supports direct streaming of the file content.
|
||||||
|
bool SupportStreaming { get; }
|
||||||
|
|
||||||
|
// Returns recursive list of all files in the 'volume'/'filesystem'/'dataset' (whatever we want to call it).
|
||||||
|
string[] GetFiles();
|
||||||
|
|
||||||
|
// Opens a readable stream.
|
||||||
|
Stream OpenFile(string vfsFilePath);
|
||||||
|
|
||||||
|
// Copies file from the virtual filesystem to the actual one.
|
||||||
|
TransferTask CopyFile(string vfsSourcePath, string destinationPath);
|
||||||
|
|
||||||
|
// Move file from the virtual filesystem to the actual one.
|
||||||
|
TransferTask MoveFile(string vfsSourcePath, string destinationPath);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Core.Download;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders.Providers
|
||||||
|
{
|
||||||
|
// Indicates that the files use some custom external transfer method. It's not guaranteed that the files are already available.
|
||||||
|
// The IsCopy flag indicates that the files are copied, not mounted. And thus can be safely moved during import, overriding the DownloadItem IsReadOnly flag.
|
||||||
|
// This TransferProvider should also have a mechanism for detecting whether the external transfer is in progress. But it should be 'deferred'. (see IsAvailable())
|
||||||
|
|
||||||
|
public class CustomTransferSettings : IProviderConfig
|
||||||
|
{
|
||||||
|
public string DownloadClientPath { get; set; }
|
||||||
|
public string LocalPath { get; set; }
|
||||||
|
|
||||||
|
public bool IsCopy { get; set; }
|
||||||
|
|
||||||
|
public NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomTransfer : TransferProviderBase<CustomTransferSettings>
|
||||||
|
{
|
||||||
|
private readonly Logger _logger;
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
|
private readonly IDiskTransferService _transferService;
|
||||||
|
|
||||||
|
public override string Name => "Mount";
|
||||||
|
|
||||||
|
public CustomTransfer(IDiskTransferService transferService, IDiskProvider diskProvider, Logger logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_diskProvider = diskProvider;
|
||||||
|
_transferService = transferService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResult Test()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsAvailable(DownloadClientPath item)
|
||||||
|
{
|
||||||
|
if (item == null) return false;
|
||||||
|
|
||||||
|
var path = ResolvePath(item);
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _diskProvider.FolderExists(path) || _diskProvider.FileExists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Give MountVirtualDiskProvider the tempPath.
|
||||||
|
public override IVirtualDiskProvider GetFileSystemWrapper(DownloadClientPath item, string tempPath = null)
|
||||||
|
{
|
||||||
|
var path = ResolvePath(item);
|
||||||
|
|
||||||
|
if (_diskProvider.FolderExists(path) || _diskProvider.FileExists(path))
|
||||||
|
{
|
||||||
|
// Expose a virtual filesystem with only that directory/file in it.
|
||||||
|
// This allows the caller to delete the directory if desired, but not it's siblings.
|
||||||
|
return new MountVirtualDiskProvider(_diskProvider, _transferService, Path.GetDirectoryName(path), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new EmptyVirtualDiskProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string ResolvePath(DownloadClientPath path)
|
||||||
|
{
|
||||||
|
return ResolvePath(path.Path, Settings.DownloadClientPath, Settings.LocalPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Common.EnsureThat;
|
||||||
|
using NzbDrone.Core.Download;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders.Providers
|
||||||
|
{
|
||||||
|
// Represents a local filesystem transfer.
|
||||||
|
class DefaultTransfer : TransferProviderBase<NullConfig>
|
||||||
|
{
|
||||||
|
private readonly Logger _logger;
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
|
private readonly IDiskTransferService _transferService;
|
||||||
|
|
||||||
|
public override string Name => "Default";
|
||||||
|
|
||||||
|
public DefaultTransfer(IDiskTransferService transferService, IDiskProvider diskProvider, Logger logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_diskProvider = diskProvider;
|
||||||
|
_transferService = transferService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return new TransferProviderDefinition
|
||||||
|
{
|
||||||
|
Enable = true,
|
||||||
|
Name = "Default",
|
||||||
|
ImplementationName = nameof(DefaultTransfer),
|
||||||
|
Implementation = nameof(DefaultTransfer),
|
||||||
|
Settings = NullConfig.Instance
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResult Test()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsAvailable(DownloadClientPath item)
|
||||||
|
{
|
||||||
|
if (item == null) return false;
|
||||||
|
|
||||||
|
var path = ResolvePath(item);
|
||||||
|
|
||||||
|
return _diskProvider.FolderExists(path) || _diskProvider.FileExists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Give DirectVirtualDiskProvider the tempPath.
|
||||||
|
public override IVirtualDiskProvider GetFileSystemWrapper(DownloadClientPath item, string tempPath = null)
|
||||||
|
{
|
||||||
|
var path = ResolvePath(item);
|
||||||
|
|
||||||
|
if (_diskProvider.FolderExists(path) || _diskProvider.FileExists(path))
|
||||||
|
{
|
||||||
|
// Expose a virtual filesystem with only that directory/file in it.
|
||||||
|
// This allows the caller to delete the directory if desired, but not it's siblings.
|
||||||
|
return new DirectVirtualDiskProvider(_diskProvider, _transferService, Path.GetDirectoryName(path), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new EmptyVirtualDiskProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string ResolvePath(DownloadClientPath path)
|
||||||
|
{
|
||||||
|
return path.Path.FullPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Timeline;
|
||||||
|
using NzbDrone.Common.TPL;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders.Providers
|
||||||
|
{
|
||||||
|
public class DirectVirtualDiskProvider : IVirtualDiskProvider
|
||||||
|
{
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
|
private readonly IDiskTransferService _transferService;
|
||||||
|
private readonly string _rootFolder;
|
||||||
|
private readonly List<string> _items;
|
||||||
|
|
||||||
|
public bool SupportStreaming => true;
|
||||||
|
|
||||||
|
public DirectVirtualDiskProvider(IDiskProvider diskProvider, IDiskTransferService transferService, string rootFolder, params string[] items)
|
||||||
|
{
|
||||||
|
_diskProvider = diskProvider;
|
||||||
|
_transferService = transferService;
|
||||||
|
_rootFolder = rootFolder;
|
||||||
|
_items = items.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetFiles()
|
||||||
|
{
|
||||||
|
return _items.SelectMany(GetFiles).Select(_rootFolder.GetRelativePath).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] GetFiles(string sourcePath)
|
||||||
|
{
|
||||||
|
if (_diskProvider.FileExists(sourcePath))
|
||||||
|
{
|
||||||
|
return new [] { sourcePath };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _diskProvider.GetFiles(sourcePath, SearchOption.AllDirectories);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransferTask MoveFile(string vfsSourcePath, string destinationPath)
|
||||||
|
{
|
||||||
|
return TransferFile(vfsSourcePath, destinationPath, TransferMode.Move);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransferTask CopyFile(string vfsSourcePath, string destinationPath)
|
||||||
|
{
|
||||||
|
return TransferFile(vfsSourcePath, destinationPath, TransferMode.Copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TransferTask TransferFile(string vfsSourcePath, string destinationPath, TransferMode mode)
|
||||||
|
{
|
||||||
|
var sourcePath = ResolveVirtualPath(vfsSourcePath);
|
||||||
|
|
||||||
|
var fileSize = _diskProvider.GetFileSize(sourcePath);
|
||||||
|
var progress = new TimelineContext($"{mode} {Path.GetFileName(sourcePath)}", 0, fileSize);
|
||||||
|
var task = Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
progress.UpdateState(TimelineState.Started);
|
||||||
|
_transferService.TransferFile(sourcePath, destinationPath, mode);
|
||||||
|
if (mode == TransferMode.Move && _items.Contains(vfsSourcePath))
|
||||||
|
{
|
||||||
|
// If it was moved, then remove it from the list.
|
||||||
|
_items.Remove(vfsSourcePath);
|
||||||
|
}
|
||||||
|
progress.FinishProgress();
|
||||||
|
});
|
||||||
|
|
||||||
|
return new TransferTask(progress, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream OpenFile(string vfsFilePath)
|
||||||
|
{
|
||||||
|
var sourcePath = ResolveVirtualPath(vfsFilePath);
|
||||||
|
|
||||||
|
return _diskProvider.OpenReadStream(sourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ResolveVirtualPath(string virtualPath)
|
||||||
|
{
|
||||||
|
if (Path.IsPathRooted(virtualPath))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Path not valid in the virtual filesystem");
|
||||||
|
}
|
||||||
|
|
||||||
|
var basePath = virtualPath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)[0];
|
||||||
|
|
||||||
|
if (!_items.Contains(basePath))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Path not valid in the virtual filesystem");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Path.Combine(_rootFolder, virtualPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders.Providers
|
||||||
|
{
|
||||||
|
public class EmptyVirtualDiskProvider : IVirtualDiskProvider
|
||||||
|
{
|
||||||
|
public bool SupportStreaming => true;
|
||||||
|
|
||||||
|
public string[] GetFiles()
|
||||||
|
{
|
||||||
|
return new string[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransferTask MoveFile(string vfsSourcePath, string destinationPath)
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("File not found in virtual filesystem", vfsSourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransferTask CopyFile(string vfsSourcePath, string destinationPath)
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("File not found in virtual filesystem", vfsSourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream OpenFile(string vfsFilePath)
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("File not found in virtual filesystem", vfsFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Core.Download;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders.Providers
|
||||||
|
{
|
||||||
|
// Indicates that the remote path is mounted locally, and thus should honor the DownloadItem isReadonly flag and may transfer slowly.
|
||||||
|
public class MountSettings : IProviderConfig
|
||||||
|
{
|
||||||
|
public string DownloadClientPath { get; set; }
|
||||||
|
public string MountPath { get; set; }
|
||||||
|
|
||||||
|
public NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MountTransfer : TransferProviderBase<MountSettings>
|
||||||
|
{
|
||||||
|
private readonly Logger _logger;
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
|
private readonly IDiskTransferService _transferService;
|
||||||
|
|
||||||
|
public override string Name => "Mount";
|
||||||
|
|
||||||
|
public MountTransfer(IDiskTransferService transferService, IDiskProvider diskProvider, Logger logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_diskProvider = diskProvider;
|
||||||
|
_transferService = transferService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResult Test()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsAvailable(DownloadClientPath item)
|
||||||
|
{
|
||||||
|
if (item == null) return false;
|
||||||
|
|
||||||
|
var path = ResolvePath(item);
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _diskProvider.FolderExists(path) || _diskProvider.FileExists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Give MountVirtualDiskProvider the tempPath.
|
||||||
|
public override IVirtualDiskProvider GetFileSystemWrapper(DownloadClientPath item, string tempPath = null)
|
||||||
|
{
|
||||||
|
var path = ResolvePath(item);
|
||||||
|
|
||||||
|
if (_diskProvider.FolderExists(path) || _diskProvider.FileExists(path))
|
||||||
|
{
|
||||||
|
// Expose a virtual filesystem with only that directory/file in it.
|
||||||
|
// This allows the caller to delete the directory if desired, but not it's siblings.
|
||||||
|
return new MountVirtualDiskProvider(_diskProvider, _transferService, Path.GetDirectoryName(path), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new EmptyVirtualDiskProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string ResolvePath(DownloadClientPath path)
|
||||||
|
{
|
||||||
|
return ResolvePath(path.Path, Settings.DownloadClientPath, Settings.MountPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders.Providers
|
||||||
|
{
|
||||||
|
// Empty wrapper, it would server in dealing with stuff being slower and remote mounts potentially being unavailable temporarily.
|
||||||
|
// Ideally it should wrap a DirectVirtualDiskProvider instance, rather than inheriting from it.
|
||||||
|
public class MountVirtualDiskProvider : DirectVirtualDiskProvider
|
||||||
|
{
|
||||||
|
public MountVirtualDiskProvider(IDiskProvider diskProvider, IDiskTransferService transferService, string rootFolder, params string[] items)
|
||||||
|
: base(diskProvider, transferService, rootFolder, items)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Core.Download;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders
|
||||||
|
{
|
||||||
|
public abstract class TransferProviderBase<TSettings> : ITransferProvider where TSettings : IProviderConfig, new()
|
||||||
|
{
|
||||||
|
public abstract string Name { get; }
|
||||||
|
public Type ConfigContract => typeof(TSettings);
|
||||||
|
public virtual ProviderMessage Message => null;
|
||||||
|
public virtual IEnumerable<ProviderDefinition> DefaultDefinitions => new List<ProviderDefinition>();
|
||||||
|
public ProviderDefinition Definition { get; set; }
|
||||||
|
public abstract ValidationResult Test();
|
||||||
|
public virtual object RequestAction(string action, IDictionary<string, string> query) { return null; }
|
||||||
|
|
||||||
|
protected TSettings Settings => (TSettings)Definition.Settings;
|
||||||
|
|
||||||
|
public abstract bool IsAvailable(DownloadClientPath item);
|
||||||
|
public abstract IVirtualDiskProvider GetFileSystemWrapper(DownloadClientPath item, string tempPath = null);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected static string ResolvePath(OsPath path, string currentParent, string newParent)
|
||||||
|
{
|
||||||
|
var currentParentPath = new OsPath(currentParent);
|
||||||
|
var newParentPath = new OsPath(newParent);
|
||||||
|
if (!currentParentPath.Contains(path))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPath = newParentPath + (path - currentParentPath);
|
||||||
|
|
||||||
|
return newPath.FullPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders
|
||||||
|
{
|
||||||
|
public class TransferProviderDefinition : ProviderDefinition
|
||||||
|
{
|
||||||
|
public int DownloadClientId { get; set; }
|
||||||
|
// OR
|
||||||
|
// Path could be extracted from download client.
|
||||||
|
//public string DownloadClientRootPath { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Composition;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders
|
||||||
|
{
|
||||||
|
public interface ITransferProviderFactory : IProviderFactory<ITransferProvider, TransferProviderDefinition>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TransferProviderFactory : ProviderFactory<ITransferProvider, TransferProviderDefinition>, ITransferProviderFactory
|
||||||
|
{
|
||||||
|
public TransferProviderFactory(ITransferProviderRepository providerRepository, IEnumerable<ITransferProvider> providers, IContainer container, IEventAggregator eventAggregator, Logger logger)
|
||||||
|
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders
|
||||||
|
{
|
||||||
|
public interface ITransferProviderRepository : IProviderRepository<TransferProviderDefinition>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Core.Download;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.TransferProviders.Providers;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders
|
||||||
|
{
|
||||||
|
public interface ITransferProviderService
|
||||||
|
{
|
||||||
|
ITransferProvider GetProvider(int downloadClientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TransferProviderService : ITransferProviderService
|
||||||
|
{
|
||||||
|
private readonly ITransferProviderFactory _transferProviderFactory;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public TransferProviderService(ITransferProviderFactory transferProviderFactory, Logger logger)
|
||||||
|
{
|
||||||
|
_transferProviderFactory = transferProviderFactory;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITransferProvider GetProvider(int downloadClientId)
|
||||||
|
{
|
||||||
|
var definition = _transferProviderFactory.All().FirstOrDefault(v => v.DownloadClientId == downloadClientId);
|
||||||
|
|
||||||
|
if (definition == null)
|
||||||
|
{
|
||||||
|
definition = _transferProviderFactory.GetDefaultDefinitions().First(v => v.ImplementationName == nameof(DefaultTransfer));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _transferProviderFactory.GetInstance(definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NzbDrone.Common.Timeline;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.TransferProviders
|
||||||
|
{
|
||||||
|
public class TransferTask
|
||||||
|
{
|
||||||
|
public ITimelineContext Timeline { get; private set; }
|
||||||
|
|
||||||
|
// Async task that is completed once the Transfer has finished or ended in failure. (Do not rely on ProgressReporter for finished detection)
|
||||||
|
public Task CompletionTask { get; private set; }
|
||||||
|
|
||||||
|
public TransferTask(ITimelineContext timeline, Task completionTask)
|
||||||
|
{
|
||||||
|
Timeline = timeline;
|
||||||
|
CompletionTask = completionTask.ContinueWith(t => Timeline.FinishProgress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue