Decompress command
This commit is contained in:
@@ -63,6 +63,9 @@ public class LocalItemCreator : ItemCreatorBase<ILocalContentProvider>
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.LogTrace("Trying to create element with path {Path}", path);
|
_logger.LogTrace("Trying to create element with path {Path}", path);
|
||||||
|
var directory = Path.GetDirectoryName(path);
|
||||||
|
if (directory is { } && !Directory.Exists(directory)) Directory.CreateDirectory(directory);
|
||||||
|
|
||||||
await using (File.Create(path))
|
await using (File.Create(path))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,34 @@ public class CompressionUserCommandHandler : AggregatedUserCommandHandler
|
|||||||
_markedItems = appState.SelectedTab.Map(t => t?.MarkedItems).Switch();
|
_markedItems = appState.SelectedTab.Map(t => t?.MarkedItems).Switch();
|
||||||
_selectedItem = appState.SelectedTab.Map(t => t?.CurrentSelectedItem).Switch();
|
_selectedItem = appState.SelectedTab.Map(t => t?.CurrentSelectedItem).Switch();
|
||||||
|
|
||||||
AddCommandHandler(new[]
|
AddCommandHandler(new IUserCommandHandler[]
|
||||||
{
|
{
|
||||||
new TypeUserCommandHandler<CompressUserCommand>(Compress)
|
new TypeUserCommandHandler<CompressUserCommand>(Compress),
|
||||||
|
new TypeUserCommandHandler<DecompressUserCommand>(Decompress)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Task Decompress()
|
||||||
|
{
|
||||||
|
_clipboardService.Clear();
|
||||||
|
_clipboardService.SetCommand<DecompressCommandFactory>();
|
||||||
|
|
||||||
|
if (_markedItems.Value is {Count: > 0} markedItems)
|
||||||
|
{
|
||||||
|
foreach (var markedItem in markedItems)
|
||||||
|
{
|
||||||
|
_clipboardService.AddContent(markedItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_selectedItem.Value?.BaseItem?.FullName is { } fullname)
|
||||||
|
{
|
||||||
|
//TODO: check if file is decompressable
|
||||||
|
_clipboardService.AddContent(fullname);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
private Task Compress()
|
private Task Compress()
|
||||||
{
|
{
|
||||||
_clipboardService.Clear();
|
_clipboardService.Clear();
|
||||||
|
|||||||
131
src/Tools/FileTime.Tools.Compression/DecompressCommand.cs
Normal file
131
src/Tools/FileTime.Tools.Compression/DecompressCommand.cs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
using FileTime.Core.Command;
|
||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
using SharpCompress.Archives;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.Compression;
|
||||||
|
|
||||||
|
public class DecompressCommand : CommandBase, IExecutableCommand, ITransportationCommand, IDisposable
|
||||||
|
{
|
||||||
|
private record ArchiveContext(IArchive Archive, string TargetContainerName);
|
||||||
|
|
||||||
|
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||||
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
|
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||||
|
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
|
||||||
|
private readonly List<ArchiveContext> _archives = new();
|
||||||
|
private readonly List<IDisposable> _disposables = new();
|
||||||
|
|
||||||
|
public IReadOnlyList<FullName> Sources { get; }
|
||||||
|
public FullName Target { get; }
|
||||||
|
public TransportMode TransportMode { get; }
|
||||||
|
|
||||||
|
internal DecompressCommand(
|
||||||
|
ITimelessContentProvider timelessContentProvider,
|
||||||
|
IContentAccessorFactory contentAccessorFactory,
|
||||||
|
ICommandSchedulerNotifier commandSchedulerNotifier,
|
||||||
|
IReadOnlyCollection<FullName> sources,
|
||||||
|
TransportMode mode,
|
||||||
|
FullName targetFullName)
|
||||||
|
{
|
||||||
|
_timelessContentProvider = timelessContentProvider;
|
||||||
|
_contentAccessorFactory = contentAccessorFactory;
|
||||||
|
_commandSchedulerNotifier = commandSchedulerNotifier;
|
||||||
|
ArgumentNullException.ThrowIfNull(sources);
|
||||||
|
ArgumentNullException.ThrowIfNull(mode);
|
||||||
|
ArgumentNullException.ThrowIfNull(targetFullName);
|
||||||
|
|
||||||
|
Sources = new List<FullName>(sources).AsReadOnly();
|
||||||
|
TransportMode = mode;
|
||||||
|
Target = targetFullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override Task<CanCommandRun> CanRun(PointInTime currentTime)
|
||||||
|
{
|
||||||
|
//TODO:
|
||||||
|
return Task.FromResult(CanCommandRun.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<PointInTime> SimulateCommand(PointInTime currentTime)
|
||||||
|
{
|
||||||
|
//TODO:
|
||||||
|
return Task.FromResult(currentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void Cancel()
|
||||||
|
=> _cancellationTokenSource.Cancel();
|
||||||
|
|
||||||
|
public async Task Execute()
|
||||||
|
{
|
||||||
|
if (_archives.Count == 0) await OpenArchivesAsync();
|
||||||
|
|
||||||
|
var resolvedTarget = (IContainer) await _timelessContentProvider.GetItemByFullNameAsync(Target, PointInTime.Present);
|
||||||
|
var itemCreator = _contentAccessorFactory.GetItemCreator(resolvedTarget.Provider);
|
||||||
|
var contentWriterFactory = _contentAccessorFactory.GetContentWriterFactory(resolvedTarget.Provider);
|
||||||
|
|
||||||
|
foreach (var archiveContext in _archives)
|
||||||
|
{
|
||||||
|
await DecompressFile(archiveContext, resolvedTarget, contentWriterFactory, itemCreator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DecompressFile(
|
||||||
|
ArchiveContext archiveContext,
|
||||||
|
IContainer resolvedTarget,
|
||||||
|
IContentWriterFactory contentWriterFactory,
|
||||||
|
IItemCreator itemCreator)
|
||||||
|
{
|
||||||
|
var archive = archiveContext.Archive;
|
||||||
|
foreach (var archiveEntry in archive.Entries)
|
||||||
|
{
|
||||||
|
var subPath = string.Join(Constants.SeparatorChar, archiveEntry.Key.Split('\\'));
|
||||||
|
var entryPath = Target.GetChild(subPath);
|
||||||
|
|
||||||
|
if (archiveEntry.IsDirectory)
|
||||||
|
{
|
||||||
|
await itemCreator.CreateContainerAsync(resolvedTarget.Provider, entryPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await itemCreator.CreateElementAsync(resolvedTarget.Provider, entryPath);
|
||||||
|
var newItem = (IElement) await _timelessContentProvider.GetItemByFullNameAsync(entryPath, PointInTime.Present);
|
||||||
|
using var writer = await contentWriterFactory.CreateContentWriterAsync(newItem);
|
||||||
|
|
||||||
|
archiveEntry.WriteTo(writer.AsStream());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenArchivesAsync()
|
||||||
|
{
|
||||||
|
if (_archives.Count > 0) throw new InvalidOperationException("Archives are already open");
|
||||||
|
foreach (var source in Sources)
|
||||||
|
{
|
||||||
|
var targetElement = (IElement) await _timelessContentProvider.GetItemByFullNameAsync(source, PointInTime.Present);
|
||||||
|
var contentReader = await _contentAccessorFactory.GetContentReaderFactory(targetElement.Provider).CreateContentReaderAsync(targetElement);
|
||||||
|
var contentReaderStream = contentReader.AsStream();
|
||||||
|
_disposables.Add(contentReader);
|
||||||
|
using var archive = ArchiveFactory.Open(contentReaderStream);
|
||||||
|
|
||||||
|
_archives.Add(new ArchiveContext(archive, GetFileName(source.GetName())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFileName(string fullName)
|
||||||
|
{
|
||||||
|
var parts = fullName.Split('.');
|
||||||
|
var fileName = string.Join('.', parts[..^1]);
|
||||||
|
return string.IsNullOrEmpty(fileName) ? fullName : fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var archive in _archives)
|
||||||
|
{
|
||||||
|
archive.Archive.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using FileTime.Core.Command;
|
||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.Compression;
|
||||||
|
|
||||||
|
public class DecompressCommandFactory : ITransportationCommandFactory<DecompressCommand>
|
||||||
|
{
|
||||||
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
|
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||||
|
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
|
||||||
|
|
||||||
|
public DecompressCommandFactory(
|
||||||
|
ITimelessContentProvider timelessContentProvider,
|
||||||
|
IContentAccessorFactory contentAccessorFactory,
|
||||||
|
ICommandSchedulerNotifier commandSchedulerNotifier)
|
||||||
|
{
|
||||||
|
_timelessContentProvider = timelessContentProvider;
|
||||||
|
_contentAccessorFactory = contentAccessorFactory;
|
||||||
|
_commandSchedulerNotifier = commandSchedulerNotifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecompressCommand GenerateCommand(IReadOnlyCollection<FullName> sources, TransportMode mode, FullName targetFullName)
|
||||||
|
=> new(
|
||||||
|
_timelessContentProvider,
|
||||||
|
_contentAccessorFactory,
|
||||||
|
_commandSchedulerNotifier,
|
||||||
|
sources,
|
||||||
|
mode,
|
||||||
|
targetFullName);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using FileTime.App.Core.UserCommand;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.Compression;
|
||||||
|
|
||||||
|
public class DecompressUserCommand : IIdentifiableUserCommand
|
||||||
|
{
|
||||||
|
public const string CommandName = "decompress";
|
||||||
|
public static readonly DecompressUserCommand Instance = new();
|
||||||
|
private DecompressUserCommand()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public string UserCommandID => CommandName;
|
||||||
|
public string Title => "Select for decompression";
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ public class StartupHandler : IStartupHandler
|
|||||||
public StartupHandler(IIdentifiableUserCommandService identifiableUserCommandService)
|
public StartupHandler(IIdentifiableUserCommandService identifiableUserCommandService)
|
||||||
{
|
{
|
||||||
identifiableUserCommandService.AddIdentifiableUserCommand(CompressUserCommand.Instance);
|
identifiableUserCommandService.AddIdentifiableUserCommand(CompressUserCommand.Instance);
|
||||||
|
identifiableUserCommandService.AddIdentifiableUserCommand(DecompressUserCommand.Instance);
|
||||||
}
|
}
|
||||||
public Task InitAsync() => Task.CompletedTask;
|
public Task InitAsync() => Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -18,6 +19,7 @@ public static class Startup
|
|||||||
{
|
{
|
||||||
services.AddSingleton<IStartupHandler, StartupHandler>();
|
services.AddSingleton<IStartupHandler, StartupHandler>();
|
||||||
services.AddSingleton<CompressCommandFactory>();
|
services.AddSingleton<CompressCommandFactory>();
|
||||||
|
services.AddSingleton<DecompressCommandFactory>();
|
||||||
services.AddSingleton<IUserCommandHandler, CompressionUserCommandHandler>();
|
services.AddSingleton<IUserCommandHandler, CompressionUserCommandHandler>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user