|
|
|
@ -0,0 +1,222 @@
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using NLog;
|
|
|
|
|
using NzbDrone.Common;
|
|
|
|
|
using NzbDrone.Common.Disk;
|
|
|
|
|
using NzbDrone.Common.Instrumentation;
|
|
|
|
|
using NzbDrone.Core.Configuration;
|
|
|
|
|
using NzbDrone.Core.Tv;
|
|
|
|
|
using NzbDrone.Common.Cache;
|
|
|
|
|
using NzbDrone.Core.Download;
|
|
|
|
|
using NzbDrone.Core.ThingiProvider;
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
String RemapRemoteToLocal(String host, String remotePath);
|
|
|
|
|
String RemapLocalToRemote(String host, String localPath);
|
|
|
|
|
|
|
|
|
|
// TODO: Remove around January 2015. Used to migrate legacy Local Category Path settings.
|
|
|
|
|
void MigrateLocalCategoryPath(Int32 downloadClientId, IProviderConfig newSettings, String host, String remotePath, String localPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class RemotePathMappingService : IRemotePathMappingService
|
|
|
|
|
{
|
|
|
|
|
// TODO: Remove DownloadClientRepository reference around January 2015. Used to migrate legacy Local Category Path settings.
|
|
|
|
|
private readonly IDownloadClientRepository _downloadClientRepository;
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_downloadClientRepository = downloadClientRepository;
|
|
|
|
|
_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 = CleanPath(mapping.LocalPath);
|
|
|
|
|
mapping.RemotePath = CleanPath(mapping.RemotePath);
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.RemotePath.IsNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Invalid RemotePath");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.LocalPath.IsNullOrWhiteSpace() || !Path.IsPathRooted(mapping.LocalPath))
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Invalid LocalPath");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!_diskProvider.FolderExists(mapping.LocalPath))
|
|
|
|
|
{
|
|
|
|
|
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 String RemapRemoteToLocal(String host, String remotePath)
|
|
|
|
|
{
|
|
|
|
|
if (remotePath.IsNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
return remotePath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cleanRemotePath = CleanPath(remotePath);
|
|
|
|
|
|
|
|
|
|
foreach (var mapping in All())
|
|
|
|
|
{
|
|
|
|
|
if (host == mapping.Host && cleanRemotePath.StartsWith(mapping.RemotePath))
|
|
|
|
|
{
|
|
|
|
|
var localPath = mapping.LocalPath + cleanRemotePath.Substring(mapping.RemotePath.Length);
|
|
|
|
|
|
|
|
|
|
localPath = localPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
|
|
|
|
|
|
|
|
|
if (!remotePath.EndsWith("/") && !remotePath.EndsWith("\\"))
|
|
|
|
|
{
|
|
|
|
|
localPath = localPath.TrimEnd('/', '\\');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return localPath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return remotePath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String RemapLocalToRemote(String host, String localPath)
|
|
|
|
|
{
|
|
|
|
|
if (localPath.IsNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
return localPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cleanLocalPath = CleanPath(localPath);
|
|
|
|
|
|
|
|
|
|
foreach (var mapping in All())
|
|
|
|
|
{
|
|
|
|
|
if (host != mapping.Host) continue;
|
|
|
|
|
|
|
|
|
|
if (cleanLocalPath.StartsWith(mapping.LocalPath))
|
|
|
|
|
{
|
|
|
|
|
var remotePath = mapping.RemotePath + cleanLocalPath.Substring(mapping.LocalPath.Length);
|
|
|
|
|
|
|
|
|
|
remotePath = remotePath.Replace(Path.DirectorySeparatorChar, mapping.RemotePath.Contains('\\') ? '\\' : '/');
|
|
|
|
|
|
|
|
|
|
if (!localPath.EndsWith("/") && !localPath.EndsWith("\\"))
|
|
|
|
|
{
|
|
|
|
|
remotePath = remotePath.TrimEnd('/', '\\');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return remotePath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return localPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Remove around January 2015. Used to migrate legacy Local Category Path settings.
|
|
|
|
|
public void MigrateLocalCategoryPath(Int32 downloadClientId, IProviderConfig newSettings, String host, String remotePath, String localPath)
|
|
|
|
|
{
|
|
|
|
|
_logger.Debug("Migrating local category path for Host {0}/{1} to {2}", host, remotePath, localPath);
|
|
|
|
|
|
|
|
|
|
var existingMappings = All().Where(v => v.Host == host).ToList();
|
|
|
|
|
|
|
|
|
|
remotePath = CleanPath(remotePath);
|
|
|
|
|
localPath = CleanPath(localPath);
|
|
|
|
|
|
|
|
|
|
if (!existingMappings.Any(v => v.LocalPath == localPath && v.RemotePath == remotePath))
|
|
|
|
|
{
|
|
|
|
|
Add(new RemotePathMapping { Host = host, RemotePath = remotePath, LocalPath = localPath });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var downloadClient = _downloadClientRepository.Get(downloadClientId);
|
|
|
|
|
downloadClient.Settings = newSettings;
|
|
|
|
|
_downloadClientRepository.Update(downloadClient);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static String CleanPath(String path)
|
|
|
|
|
{
|
|
|
|
|
if (path.Contains('\\'))
|
|
|
|
|
{
|
|
|
|
|
return path.TrimEnd('\\', '/') + "\\";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return path.TrimEnd('\\', '/') + "/";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|