Contaienr size scan WIP
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
using FileTime.App.Core.Exceptions;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
using InitableService;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScanSnapshotProvider
|
||||
{
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly List<ISizeScanTask> _sizeScanTasks = new();
|
||||
internal const string ContentProviderName = "container-size-scan";
|
||||
|
||||
public ContainerScanSnapshotProvider(
|
||||
ITimelessContentProvider timelessContentProvider,
|
||||
IServiceProvider serviceProvider)
|
||||
: base(ContentProviderName, timelessContentProvider)
|
||||
{
|
||||
_timelessContentProvider = timelessContentProvider;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public override async Task<IItem> GetItemByFullNameAsync(
|
||||
FullName fullName,
|
||||
PointInTime pointInTime,
|
||||
bool forceResolve = false,
|
||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||
ItemInitializationSettings itemInitializationSettings = null
|
||||
)
|
||||
{
|
||||
if (fullName.Path == ContentProviderName)
|
||||
return this;
|
||||
|
||||
var pathParts = fullName.Path.Split(Constants.SeparatorChar);
|
||||
|
||||
var item = _sizeScanTasks.FirstOrDefault(t => t.SizeContainer.Name == pathParts[1])?.SizeContainer;
|
||||
|
||||
if (pathParts.Length == 2)
|
||||
return item ?? throw new ItemNotFoundException(fullName);
|
||||
|
||||
for (var i = 2; i < pathParts.Length - 1 && item != null; i++)
|
||||
{
|
||||
var childName = pathParts[i];
|
||||
item = item.ChildContainers.FirstOrDefault(c => c.Name == childName);
|
||||
}
|
||||
|
||||
if (item is not null)
|
||||
{
|
||||
var container = item.ChildContainers.FirstOrDefault(c => c.Name == pathParts[^1]);
|
||||
if (container is not null) return container;
|
||||
|
||||
var childName = item.RealContainer.FullName?.GetChild(pathParts[^1]);
|
||||
if (childName is null) throw new ItemNotFoundException(fullName);
|
||||
|
||||
return await _timelessContentProvider.GetItemByFullNameAsync(
|
||||
childName,
|
||||
pointInTime,
|
||||
forceResolve,
|
||||
forceResolvePathType,
|
||||
itemInitializationSettings
|
||||
);
|
||||
}
|
||||
|
||||
throw new ItemNotFoundException(fullName);
|
||||
}
|
||||
|
||||
public override async Task<IItem> GetItemByNativePathAsync(
|
||||
NativePath nativePath,
|
||||
PointInTime pointInTime,
|
||||
bool forceResolve = false,
|
||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||
ItemInitializationSettings itemInitializationSettings = default
|
||||
) =>
|
||||
await GetItemByFullNameAsync(
|
||||
new FullName(nativePath.Path),
|
||||
pointInTime,
|
||||
forceResolve,
|
||||
forceResolvePathType,
|
||||
itemInitializationSettings
|
||||
);
|
||||
|
||||
public override NativePath GetNativePath(FullName fullName)
|
||||
=> new(fullName.Path);
|
||||
|
||||
public override FullName GetFullName(NativePath nativePath)
|
||||
=> new(nativePath.Path);
|
||||
|
||||
public override Task<byte[]?> GetContentAsync(
|
||||
IElement element,
|
||||
int? maxLength = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
//TODO read from original source
|
||||
=> Task.FromResult((byte[]?) null);
|
||||
|
||||
public override bool CanHandlePath(NativePath path)
|
||||
=> path.Path.StartsWith(ContentProviderName);
|
||||
|
||||
public ISizeScanTask StartSizeScan(IContainer scanSizeOf)
|
||||
{
|
||||
var searchTask = _serviceProvider
|
||||
.GetInitableResolver(scanSizeOf)
|
||||
.GetRequiredService<ISizeScanTask>();
|
||||
|
||||
_sizeScanTasks.Add(searchTask);
|
||||
searchTask.Start();
|
||||
Items.Add(new AbsolutePath(_timelessContentProvider, searchTask.SizeContainer));
|
||||
|
||||
return searchTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
using ObservableComputations;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class ContainerSizeContainerPreview : ISizePreviewItem
|
||||
{
|
||||
public const string PreviewName = "SizePreviewContainer";
|
||||
public string Name => PreviewName;
|
||||
|
||||
public ObservableCollection<ISizePreviewItem> Items { get; }
|
||||
|
||||
public ContainerSizeContainerPreview(IContainerSizeScanContainer container)
|
||||
{
|
||||
Items = container
|
||||
.ChildContainers
|
||||
.Ordering(c => c.Size)
|
||||
.Taking(0, 10)
|
||||
.Selecting();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class ContainerSizePreviewProvider : IItemPreviewProvider
|
||||
{
|
||||
public bool CanHandle(IItem item) => item is ContainerSizeScanContainer;
|
||||
|
||||
public Task<IItemPreviewViewModel> CreatePreviewAsync(IItem item)
|
||||
{
|
||||
if(item is not ContainerSizeScanContainer container) throw new NotSupportedException();
|
||||
|
||||
return Task.FromResult((IItemPreviewViewModel)new ContainerSizeContainerPreview(container));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
//TODO: create readonly version
|
||||
public class ContainerSizeScanContainer : IContainerSizeScanContainer
|
||||
{
|
||||
private readonly ReadOnlyExtensionCollection _readOnlyExtensions;
|
||||
private readonly BehaviorSubject<bool> _isLoading = new(false);
|
||||
private readonly CombineProperty<long, long> _size;
|
||||
public required string Name { get; init; }
|
||||
public required string DisplayName { get; init; }
|
||||
public required FullName? FullName { get; init; }
|
||||
public required NativePath? NativePath { get; init; }
|
||||
public AbsolutePath? Parent { get; init; }
|
||||
public bool IsHidden => false;
|
||||
public bool IsExists => true;
|
||||
public DateTime? CreatedAt { get; }
|
||||
public SupportsDelete CanDelete => SupportsDelete.True;
|
||||
public bool CanRename => false;
|
||||
public IContentProvider Provider { get; }
|
||||
public string? Attributes => null;
|
||||
public AbsolutePathType Type => AbsolutePathType.Container;
|
||||
public PointInTime PointInTime => PointInTime.Present;
|
||||
public ObservableCollection<Exception> Exceptions { get; } = new();
|
||||
public ExtensionCollection Extensions { get; } = new();
|
||||
ReadOnlyExtensionCollection IItem.Extensions => _readOnlyExtensions;
|
||||
public IItem WithParent(AbsolutePath parent) => throw new NotImplementedException();
|
||||
|
||||
public ObservableCollection<AbsolutePath> Items { get; } = new();
|
||||
public IObservable<bool> IsLoading { get; }
|
||||
public bool? IsLoaded { get; private set; }
|
||||
public Task WaitForLoaded(CancellationToken token = default) => throw new NotImplementedException();
|
||||
|
||||
public bool AllowRecursiveDeletion => false;
|
||||
|
||||
public IDeclarativeProperty<long> Size => _size;
|
||||
public ObservableCollection<IContainerSizeScanContainer> ChildContainers { get; } = new();
|
||||
public required IContainer RealContainer { get; init; }
|
||||
|
||||
public ContainerSizeScanContainer(IContainerScanSnapshotProvider provider)
|
||||
{
|
||||
_readOnlyExtensions = new ReadOnlyExtensionCollection(Extensions);
|
||||
IsLoading = _isLoading.AsObservable();
|
||||
|
||||
_size = new(childContainerSizes => Task.FromResult(childContainerSizes.Sum()));
|
||||
CreatedAt = DateTime.Now;
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
public async Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement)
|
||||
=> await _size.AddSourceAsync(sizeElement);
|
||||
|
||||
public Task StartLoadingAsync()
|
||||
{
|
||||
_isLoading.OnNext(true);
|
||||
IsLoaded = false;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopLoadingAsync()
|
||||
{
|
||||
_isLoading.OnNext(false);
|
||||
IsLoaded = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public record ContainerSizeScanElement(
|
||||
string Name,
|
||||
string DisplayName,
|
||||
FullName FullName,
|
||||
NativePath NativePath,
|
||||
AbsolutePath? Parent,
|
||||
bool IsHidden,
|
||||
bool IsExists,
|
||||
DateTime? CreatedAt,
|
||||
SupportsDelete CanDelete,
|
||||
bool CanRename,
|
||||
string? Attributes,
|
||||
IContentProvider Provider,
|
||||
PointInTime PointInTime,
|
||||
ObservableCollection<Exception> Exceptions,
|
||||
ReadOnlyExtensionCollection Extensions) : IElement
|
||||
{
|
||||
public AbsolutePathType Type => AbsolutePathType.Element;
|
||||
|
||||
public IItem WithParent(AbsolutePath parent) => this with { Parent = parent };
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.ContentAccess\FileTime.Core.ContentAccess.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.ContainerSizeScanner.Abstractions\FileTime.App.ContainerSizeScanner.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
117
src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanTask.cs
Normal file
117
src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanTask.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using DeclarativeProperty;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Models.Extensions;
|
||||
using FileTime.Core.Timeline;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class SizeScanTask : ISizeScanTask
|
||||
{
|
||||
private IContainer _scanSizeOf = null!;
|
||||
private readonly IContainerScanSnapshotProvider _containerScanSnapshotProvider;
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
private readonly ILogger<SizeScanTask> _logger;
|
||||
private Thread? _sizeScanThread;
|
||||
private static int _searchId = 1;
|
||||
public IContainerSizeScanContainer SizeContainer { get; private set; } = null!;
|
||||
|
||||
public SizeScanTask(
|
||||
IContainerScanSnapshotProvider containerScanSnapshotProvider,
|
||||
ITimelessContentProvider timelessContentProvider,
|
||||
ILogger<SizeScanTask> logger)
|
||||
{
|
||||
_containerScanSnapshotProvider = containerScanSnapshotProvider;
|
||||
_timelessContentProvider = timelessContentProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Init(IContainer scanSizeOf)
|
||||
{
|
||||
_scanSizeOf = scanSizeOf;
|
||||
var name = $"{_searchId++}_{scanSizeOf.Name}";
|
||||
var randomId = ContainerScanSnapshotProvider.ContentProviderName + Constants.SeparatorChar + name;
|
||||
SizeContainer = new ContainerSizeScanContainer(_containerScanSnapshotProvider)
|
||||
{
|
||||
Name = name,
|
||||
DisplayName = scanSizeOf.DisplayName,
|
||||
FullName = new FullName(randomId),
|
||||
NativePath = new NativePath(randomId),
|
||||
Parent = new AbsolutePath(_timelessContentProvider, _containerScanSnapshotProvider),
|
||||
RealContainer = scanSizeOf
|
||||
};
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_sizeScanThread != null) return;
|
||||
|
||||
var sizeScanThread = new Thread(Run);
|
||||
sizeScanThread.Start();
|
||||
_sizeScanThread = sizeScanThread;
|
||||
}
|
||||
|
||||
private async void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
await TraverseTree(_scanSizeOf, SizeContainer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error while scanning container {ContainerName}", _scanSizeOf.Name);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: make static
|
||||
private async Task TraverseTree(
|
||||
IContainer realContainer,
|
||||
IContainerSizeScanContainer container)
|
||||
{
|
||||
var resolvedItems = new List<IItem>(realContainer.Items.Count);
|
||||
foreach (var item in realContainer.Items)
|
||||
{
|
||||
var resolvedItem = await item.ResolveAsync();
|
||||
resolvedItems.Add(resolvedItem);
|
||||
}
|
||||
|
||||
foreach (var element in resolvedItems.OfType<IElement>())
|
||||
{
|
||||
var fileExtension = element.GetExtension<FileExtension>();
|
||||
if (fileExtension?.Size is not { } size) continue;
|
||||
|
||||
var childName = container.FullName!.GetChild(element.Name).Path;
|
||||
await container.AddSizeSourceAsync(new DeclarativeProperty<long>(size));
|
||||
container.Items.Add(new AbsolutePath(
|
||||
_timelessContentProvider,
|
||||
PointInTime.Present,
|
||||
new FullName(childName),
|
||||
AbsolutePathType.Element));
|
||||
}
|
||||
|
||||
foreach (var childContainer in resolvedItems.OfType<IContainer>())
|
||||
{
|
||||
var childName = container.FullName!.GetChild(childContainer.Name).Path;
|
||||
var childSearchContainer = new ContainerSizeScanContainer(_containerScanSnapshotProvider)
|
||||
{
|
||||
Name = childContainer.Name,
|
||||
DisplayName = childContainer.DisplayName,
|
||||
FullName = new FullName(childName),
|
||||
NativePath = new NativePath(childName),
|
||||
Parent = new AbsolutePath(_timelessContentProvider, container),
|
||||
RealContainer = childContainer
|
||||
};
|
||||
|
||||
container.ChildContainers.Add(childSearchContainer);
|
||||
await container.AddSizeSourceAsync(childSearchContainer.Size);
|
||||
container.Items.Add(new AbsolutePath(
|
||||
_timelessContentProvider,
|
||||
PointInTime.Present,
|
||||
new FullName(childName),
|
||||
AbsolutePathType.Container));
|
||||
|
||||
await TraverseTree(childContainer, childSearchContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/AppCommon/FileTime.App.ContainerSizeScanner/Startup.cs
Normal file
18
src/AppCommon/FileTime.App.ContainerSizeScanner/Startup.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public static class Startup
|
||||
{
|
||||
public static IServiceCollection AddContainerSizeScanner(this IServiceCollection services)
|
||||
{
|
||||
services.TryAddSingleton<IContainerScanSnapshotProvider, ContainerScanSnapshotProvider>();
|
||||
services.AddSingleton<IContentProvider>(sp => sp.GetRequiredService<IContainerScanSnapshotProvider>());
|
||||
services.AddTransient<ISizeScanTask, SizeScanTask>();
|
||||
services.AddTransient<IItemPreviewProvider, ContainerSizePreviewProvider>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user