Added a bit more implementation for the Default TransferProvider.

This commit is contained in:
Taloth Saldono 2017-12-14 20:27:25 +01:00
parent 2c8489de43
commit e089bfb4d2
12 changed files with 279 additions and 54 deletions

View File

@ -0,0 +1,8 @@
namespace NzbDrone.Core.Download
{
public class DownloadClientPath
{
public int DownloadClientId { get; set; }
public string Path { get; set; }
}
}

View File

@ -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" />
@ -947,7 +948,10 @@
<Compile Include="Qualities\QualitySource.cs" /> <Compile Include="Qualities\QualitySource.cs" />
<Compile Include="Qualities\Revision.cs" /> <Compile Include="Qualities\Revision.cs" />
<Compile Include="TransferProviders\Providers\DefaultTransfer.cs" /> <Compile Include="TransferProviders\Providers\DefaultTransfer.cs" />
<Compile Include="TransferProviders\Providers\DirectVirtualDiskProvider.cs" />
<Compile Include="TransferProviders\Providers\Dummy.cs" /> <Compile Include="TransferProviders\Providers\Dummy.cs" />
<Compile Include="TransferProviders\Providers\EmptyVirtualDiskProvider.cs" />
<Compile Include="TransferProviders\Providers\MountVirtualDiskProvider.cs" />
<Compile Include="TransferProviders\TransferProviderDefinition.cs" /> <Compile Include="TransferProviders\TransferProviderDefinition.cs" />
<Compile Include="TransferProviders\TransferProviderRepository.cs" /> <Compile Include="TransferProviders\TransferProviderRepository.cs" />
<Compile Include="TransferProviders\Providers\CustomTransfer.cs" /> <Compile Include="TransferProviders\Providers\CustomTransfer.cs" />

View File

@ -7,14 +7,10 @@ namespace NzbDrone.Core.TransferProviders
{ {
public interface ITransferProvider : IProvider public interface ITransferProvider : IProvider
{ {
// TODO: Perhaps change 'string' to 'DownloadClientPath' struct/class so we're more typesafe.
// Whether the TransferProvider is ready to be accessed. (Useful for external transfers that may not have finished yet) // Whether the TransferProvider is ready to be accessed. (Useful for external transfers that may not have finished yet)
bool IsAvailable(string downloadClientPath); bool IsAvailable(DownloadClientPath item);
bool IsAvailable(DownloadClientItem item);
// Returns a wrapper for the specific download. Optionally we might want to supply a 'tempDir' that's close to the series path, in case the TransferProvider needs an intermediate location. // 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(string downloadClientPath); IVirtualDiskProvider GetFileSystemWrapper(DownloadClientPath item, string tempPath = null);
IVirtualDiskProvider GetFileSystemWrapper(DownloadClientItem item);
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
@ -16,6 +17,9 @@ namespace NzbDrone.Core.TransferProviders
// Returns recursive list of all files in the 'volume'/'filesystem'/'dataset' (whatever we want to call it). // Returns recursive list of all files in the 'volume'/'filesystem'/'dataset' (whatever we want to call it).
string[] GetFiles(); string[] GetFiles();
// Opens a readable stream.
Stream OpenFile(string vfsFilePath);
// Copies file from the virtual filesystem to the actual one. // Copies file from the virtual filesystem to the actual one.
TransferTask CopyFile(string vfsSourcePath, string destinationPath); TransferTask CopyFile(string vfsSourcePath, string destinationPath);

View File

@ -1,7 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using FluentValidation.Results; using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Core.Download;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.TransferProviders.Providers namespace NzbDrone.Core.TransferProviders.Providers
@ -9,6 +14,19 @@ namespace NzbDrone.Core.TransferProviders.Providers
// Represents a local filesystem transfer. // Represents a local filesystem transfer.
class DefaultTransfer : TransferProviderBase<NullConfig> 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 public override IEnumerable<ProviderDefinition> DefaultDefinitions
{ {
get get
@ -23,19 +41,39 @@ namespace NzbDrone.Core.TransferProviders.Providers
}; };
} }
} }
public override string Link
{
get { throw new NotImplementedException(); }
}
public override string Name
{
get { throw new NotImplementedException(); }
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
throw new NotImplementedException(); 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;
}
} }
} }

View File

@ -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);
}
}
}

View File

@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentValidation.Results;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.TransferProviders.Providers
{
// Marks the files are permanently unavailable. Perhaps useful in fire-and-forget.
class Dummy : TransferProviderBase<NullConfig>
{
public override string Link
{
get { throw new NotImplementedException(); }
}
public override string Name
{
get { throw new NotImplementedException(); }
}
public override ValidationResult Test()
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,32 @@
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);
}
}
}

View File

@ -1,6 +1,10 @@
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using FluentValidation.Results; using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Download;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
@ -21,19 +25,52 @@ namespace NzbDrone.Core.TransferProviders.Providers
public class MountTransfer : TransferProviderBase<MountSettings> public class MountTransfer : TransferProviderBase<MountSettings>
{ {
public override string Link private readonly Logger _logger;
{ private readonly IDiskProvider _diskProvider;
get { throw new NotImplementedException(); } private readonly IDiskTransferService _transferService;
}
public override string Name public override string Name => "Mount";
public MountTransfer(IDiskTransferService transferService, IDiskProvider diskProvider, Logger logger)
{ {
get { throw new NotImplementedException(); } _logger = logger;
_diskProvider = diskProvider;
_transferService = transferService;
} }
public override ValidationResult Test() public override ValidationResult Test()
{ {
throw new NotImplementedException(); 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 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)
{
// Same logic as RemotePathMapping service.
throw new NotImplementedException();
}
} }
} }

View File

@ -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)
{
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation.Results; using FluentValidation.Results;
using NzbDrone.Core.Download;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.TransferProviders namespace NzbDrone.Core.TransferProviders
@ -19,8 +20,10 @@ namespace NzbDrone.Core.TransferProviders
public ProviderDefinition Definition { get; set; } public ProviderDefinition Definition { get; set; }
public abstract ValidationResult Test(); public abstract ValidationResult Test();
public abstract string Link { get; }
public virtual object RequestAction(string action, IDictionary<string, string> query) { return null; } public virtual object RequestAction(string action, IDictionary<string, string> query) { return null; }
public abstract bool IsAvailable(DownloadClientPath item);
public abstract IVirtualDiskProvider GetFileSystemWrapper(DownloadClientPath item, string tempPath = null);
} }
} }

View File

@ -2,13 +2,22 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using NzbDrone.Common.Timeline;
namespace NzbDrone.Core.TransferProviders namespace NzbDrone.Core.TransferProviders
{ {
public class TransferTask public class TransferTask
{ {
// TODO: Progress reporting public ITimelineContext Timeline { get; private set; }
// TODO: Async task or waitable object so Importing can handle. // 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());
}
} }
} }