Browse ISOs
This commit is contained in:
@@ -28,7 +28,7 @@ public class ContainerSizeSizeScanProvider : ContentProviderBase, IContainerSize
|
|||||||
PointInTime pointInTime,
|
PointInTime pointInTime,
|
||||||
bool forceResolve = false,
|
bool forceResolve = false,
|
||||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||||
ItemInitializationSettings itemInitializationSettings = null
|
ItemInitializationSettings itemInitializationSettings = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (fullName.Path == ContentProviderName)
|
if (fullName.Path == ContentProviderName)
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace FileTime.App.Core.Services;
|
||||||
|
|
||||||
|
public interface IPreStartupHandler
|
||||||
|
{
|
||||||
|
Task InitAsync();
|
||||||
|
}
|
||||||
@@ -5,15 +5,18 @@ namespace FileTime.App.Core.Services;
|
|||||||
|
|
||||||
public class LifecycleService : ILifecycleService
|
public class LifecycleService : ILifecycleService
|
||||||
{
|
{
|
||||||
|
private readonly IEnumerable<IPreStartupHandler> _preStartupHandlers;
|
||||||
private readonly IEnumerable<IExitHandler> _exitHandlers;
|
private readonly IEnumerable<IExitHandler> _exitHandlers;
|
||||||
private readonly IEnumerable<IStartupHandler> _startupHandlers;
|
private readonly IEnumerable<IStartupHandler> _startupHandlers;
|
||||||
private readonly ILogger<LifecycleService> _logger;
|
private readonly ILogger<LifecycleService> _logger;
|
||||||
|
|
||||||
public LifecycleService(
|
public LifecycleService(
|
||||||
|
IEnumerable<IPreStartupHandler> preStartupHandlers,
|
||||||
IEnumerable<IStartupHandler> startupHandlers,
|
IEnumerable<IStartupHandler> startupHandlers,
|
||||||
IEnumerable<IExitHandler> exitHandlers,
|
IEnumerable<IExitHandler> exitHandlers,
|
||||||
ILogger<LifecycleService> logger)
|
ILogger<LifecycleService> logger)
|
||||||
{
|
{
|
||||||
|
_preStartupHandlers = preStartupHandlers;
|
||||||
_exitHandlers = exitHandlers;
|
_exitHandlers = exitHandlers;
|
||||||
_startupHandlers = startupHandlers;
|
_startupHandlers = startupHandlers;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -21,6 +24,18 @@ public class LifecycleService : ILifecycleService
|
|||||||
|
|
||||||
public async Task InitStartupHandlersAsync()
|
public async Task InitStartupHandlersAsync()
|
||||||
{
|
{
|
||||||
|
foreach (var preStartupHandler in _preStartupHandlers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await preStartupHandler.InitAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error while running pre-startup handler {Handler}", preStartupHandler.GetType().FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var startupHandler in _startupHandlers)
|
foreach (var startupHandler in _startupHandlers)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -29,7 +44,7 @@ public class LifecycleService : ILifecycleService
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error while running startup handler {Handler}", startupHandler?.GetType().FullName);
|
_logger.LogError(ex, "Error while running startup handler {Handler}", startupHandler.GetType().FullName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using FileTime.App.Core.Models.Enums;
|
|||||||
using FileTime.App.Core.UserCommand;
|
using FileTime.App.Core.UserCommand;
|
||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
using FileTime.App.FrequencyNavigation.Services;
|
using FileTime.App.FrequencyNavigation.Services;
|
||||||
|
using FileTime.Core.ContentAccess;
|
||||||
using FileTime.Core.Interactions;
|
using FileTime.Core.Interactions;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Services;
|
using FileTime.Core.Services;
|
||||||
@@ -29,6 +30,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
private readonly IUserCommunicationService _userCommunicationService;
|
private readonly IUserCommunicationService _userCommunicationService;
|
||||||
private readonly IFrequencyNavigationService _frequencyNavigationService;
|
private readonly IFrequencyNavigationService _frequencyNavigationService;
|
||||||
private readonly ICommandPaletteService _commandPaletteService;
|
private readonly ICommandPaletteService _commandPaletteService;
|
||||||
|
private readonly IContentProviderRegistry _contentProviderRegistry;
|
||||||
private readonly ILogger<NavigationUserCommandHandlerService> _logger;
|
private readonly ILogger<NavigationUserCommandHandlerService> _logger;
|
||||||
private readonly ApplicationConfiguration _applicationConfiguration;
|
private readonly ApplicationConfiguration _applicationConfiguration;
|
||||||
private ITabViewModel? _selectedTab;
|
private ITabViewModel? _selectedTab;
|
||||||
@@ -46,6 +48,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
IUserCommunicationService userCommunicationService,
|
IUserCommunicationService userCommunicationService,
|
||||||
IFrequencyNavigationService frequencyNavigationService,
|
IFrequencyNavigationService frequencyNavigationService,
|
||||||
ICommandPaletteService commandPaletteService,
|
ICommandPaletteService commandPaletteService,
|
||||||
|
IContentProviderRegistry contentProviderRegistry,
|
||||||
ILogger<NavigationUserCommandHandlerService> logger,
|
ILogger<NavigationUserCommandHandlerService> logger,
|
||||||
ApplicationConfiguration applicationConfiguration) : base(appState)
|
ApplicationConfiguration applicationConfiguration) : base(appState)
|
||||||
{
|
{
|
||||||
@@ -57,6 +60,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
_userCommunicationService = userCommunicationService;
|
_userCommunicationService = userCommunicationService;
|
||||||
_frequencyNavigationService = frequencyNavigationService;
|
_frequencyNavigationService = frequencyNavigationService;
|
||||||
_commandPaletteService = commandPaletteService;
|
_commandPaletteService = commandPaletteService;
|
||||||
|
_contentProviderRegistry = contentProviderRegistry;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_applicationConfiguration = applicationConfiguration;
|
_applicationConfiguration = applicationConfiguration;
|
||||||
|
|
||||||
@@ -249,13 +253,40 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
|
|
||||||
private async Task OpenSelected()
|
private async Task OpenSelected()
|
||||||
{
|
{
|
||||||
if (_currentSelectedItem?.Value is not IContainerViewModel containerViewModel || containerViewModel.Container is null)
|
var targetContainer = await GetSubContainer();
|
||||||
|
|
||||||
|
if (targetContainer is null
|
||||||
|
&& _currentSelectedItem?.Value is IContainerViewModel {Container: { } container})
|
||||||
|
{
|
||||||
|
targetContainer = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetContainer is null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await _appState.SetRapidTravelTextAsync("");
|
await _appState.SetRapidTravelTextAsync("");
|
||||||
if (_selectedTab?.Tab is { } tab)
|
if (_selectedTab?.Tab is { } tab)
|
||||||
{
|
{
|
||||||
await tab.SetCurrentLocation(containerViewModel.Container);
|
await tab.SetCurrentLocation(targetContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<IContainer?> GetSubContainer()
|
||||||
|
{
|
||||||
|
if (_currentSelectedItem?.Value is not {BaseItem: IElement element})
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var subContentProvider = await _contentProviderRegistry.GetSubContentProviderForElement(element);
|
||||||
|
if (subContentProvider is null) return null;
|
||||||
|
|
||||||
|
var resolvedItem = await subContentProvider.GetItemByFullNameAsync(
|
||||||
|
element,
|
||||||
|
new FullName(""),
|
||||||
|
_timelessContentProvider.CurrentPointInTime.Value!);
|
||||||
|
return resolvedItem as IContainer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,9 @@ public partial class TabViewModel : ITabViewModel
|
|||||||
|
|
||||||
private static IItem MapItem(AbsolutePath item)
|
private static IItem MapItem(AbsolutePath item)
|
||||||
{
|
{
|
||||||
var t = Task.Run(async () => await MapItemAsync(item));
|
var t = Task.Run(async () =>
|
||||||
|
await MapItemAsync(item)
|
||||||
|
?? throw new Exception("Could not resolve path " + item.Path.Path));
|
||||||
t.Wait();
|
t.Wait();
|
||||||
return t.Result;
|
return t.Result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using FileTime.Core;
|
|||||||
using FileTime.Providers.Local;
|
using FileTime.Providers.Local;
|
||||||
using FileTime.Providers.LocalAdmin;
|
using FileTime.Providers.LocalAdmin;
|
||||||
using FileTime.Providers.Remote;
|
using FileTime.Providers.Remote;
|
||||||
|
using FileTime.Tools.VirtualDiskSources;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
@@ -28,6 +29,7 @@ public static class DependencyInjection
|
|||||||
.AddAppCoreDependencies(configuration)
|
.AddAppCoreDependencies(configuration)
|
||||||
.AddLocalProviderServices()
|
.AddLocalProviderServices()
|
||||||
.AddLocalAdminProviderServices(configuration)
|
.AddLocalAdminProviderServices(configuration)
|
||||||
.AddRemoteProviderServices();
|
.AddRemoteProviderServices()
|
||||||
|
.AddVirtualDisk();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<ProjectReference Include="..\..\Core\FileTime.Core.Timeline\FileTime.Core.Timeline.csproj" />
|
<ProjectReference Include="..\..\Core\FileTime.Core.Timeline\FileTime.Core.Timeline.csproj" />
|
||||||
<ProjectReference Include="..\..\Providers\FileTime.Providers.LocalAdmin\FileTime.Providers.LocalAdmin.csproj" />
|
<ProjectReference Include="..\..\Providers\FileTime.Providers.LocalAdmin\FileTime.Providers.LocalAdmin.csproj" />
|
||||||
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" />
|
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Tools\FileTime.Tools.VirtualDiskSources\FileTime.Tools.VirtualDiskSources.csproj" />
|
||||||
<ProjectReference Include="..\FileTime.App.Core\FileTime.App.Core.csproj" />
|
<ProjectReference Include="..\FileTime.App.Core\FileTime.App.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class SearchContentProvider : ContentProviderBase, ISearchContentProvider
|
|||||||
PointInTime pointInTime,
|
PointInTime pointInTime,
|
||||||
bool forceResolve = false,
|
bool forceResolve = false,
|
||||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||||
ItemInitializationSettings itemInitializationSettings = null
|
ItemInitializationSettings itemInitializationSettings = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (fullName.Path == ContentProviderName)
|
if (fullName.Path == ContentProviderName)
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
namespace FileTime.Core.ContentAccess;
|
namespace FileTime.Core.ContentAccess;
|
||||||
|
|
||||||
public interface IContentProviderRegistry
|
public interface IContentProviderRegistry
|
||||||
{
|
{
|
||||||
ReadOnlyObservableCollection<IContentProvider> ContentProviders { get; }
|
ReadOnlyObservableCollection<IContentProvider> ContentProviders { get; }
|
||||||
|
ReadOnlyObservableCollection<ISubContentProvider> SubContentProviders { get; }
|
||||||
void AddContentProvider(IContentProvider contentProvider);
|
void AddContentProvider(IContentProvider contentProvider);
|
||||||
void RemoveContentProvider(IContentProvider contentProvider);
|
void RemoveContentProvider(IContentProvider contentProvider);
|
||||||
|
Task<ISubContentProvider?> GetSubContentProviderForElement(IElement parentElement);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using FileTime.Core.Enums;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Core.ContentAccess;
|
||||||
|
|
||||||
|
public interface ISubContentProvider
|
||||||
|
{
|
||||||
|
Task<IItem?> GetItemByFullNameAsync(
|
||||||
|
IElement parentElement,
|
||||||
|
FullName itemPath,
|
||||||
|
PointInTime pointInTime,
|
||||||
|
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||||
|
ItemInitializationSettings itemInitializationSettings = default);
|
||||||
|
|
||||||
|
Task<bool> CanHandleAsync(IElement parentElement);
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.Core.Models;
|
namespace FileTime.Core.Models;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{Path.Path}, {Type}")]
|
||||||
public class AbsolutePath
|
public class AbsolutePath
|
||||||
{
|
{
|
||||||
public ITimelessContentProvider TimelessProvider { get; }
|
public ITimelessContentProvider TimelessProvider { get; }
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ namespace FileTime.Core.Models;
|
|||||||
public static class Constants
|
public static class Constants
|
||||||
{
|
{
|
||||||
public const char SeparatorChar = '/';
|
public const char SeparatorChar = '/';
|
||||||
|
public const string SubContentProviderRootContainer = ":";
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace FileTime.Core.Models;
|
namespace FileTime.Core.Models;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{Path}")]
|
||||||
public record FullName(string Path)
|
public record FullName(string Path)
|
||||||
{
|
{
|
||||||
public FullName? GetParent()
|
public FullName? GetParent()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace FileTime.Core.Models;
|
namespace FileTime.Core.Models;
|
||||||
|
|
||||||
public sealed class ItemInitializationSettings
|
public readonly struct ItemInitializationSettings
|
||||||
{
|
{
|
||||||
public bool SkipChildInitialization { get; init; }
|
public bool SkipChildInitialization { get; init; }
|
||||||
public AbsolutePath? Parent { get; init; }
|
public AbsolutePath? Parent { get; init; }
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace FileTime.Core.Models;
|
namespace FileTime.Core.Models;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{Path}")]
|
||||||
public record NativePath(string Path)
|
public record NativePath(string Path)
|
||||||
{
|
{
|
||||||
public override string ToString() => Path;
|
public override string ToString() => Path;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Reactive.Subjects;
|
using DeclarativeProperty;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ namespace FileTime.Core.Timeline;
|
|||||||
|
|
||||||
public interface ITimelessContentProvider
|
public interface ITimelessContentProvider
|
||||||
{
|
{
|
||||||
BehaviorSubject<PointInTime> CurrentPointInTime { get; }
|
IDeclarativeProperty<PointInTime> CurrentPointInTime { get; }
|
||||||
|
|
||||||
Task<IItem> GetItemByFullNameAsync(FullName fullName,
|
Task<IItem> GetItemByFullNameAsync(FullName fullName,
|
||||||
PointInTime? pointInTime,
|
PointInTime? pointInTime,
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using FileTime.Core.Enums;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace FileTime.Core.ContentAccess;
|
namespace FileTime.Core.ContentAccess;
|
||||||
@@ -8,13 +11,16 @@ public class ContentProviderRegistry : IContentProviderRegistry
|
|||||||
private readonly object _lock = new();
|
private readonly object _lock = new();
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly ObservableCollection<IContentProvider> _contentProviders = new();
|
private readonly ObservableCollection<IContentProvider> _contentProviders = new();
|
||||||
|
private readonly ObservableCollection<ISubContentProvider> _subContentProviders = new();
|
||||||
private readonly ReadOnlyObservableCollection<IContentProvider> _contentProvidersReadOnly;
|
private readonly ReadOnlyObservableCollection<IContentProvider> _contentProvidersReadOnly;
|
||||||
|
private readonly ReadOnlyObservableCollection<ISubContentProvider> _subContentProvidersReadOnly;
|
||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
|
|
||||||
public ContentProviderRegistry(IServiceProvider serviceProvider)
|
public ContentProviderRegistry(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
_contentProvidersReadOnly = new ReadOnlyObservableCollection<IContentProvider>(_contentProviders);
|
_contentProvidersReadOnly = new ReadOnlyObservableCollection<IContentProvider>(_contentProviders);
|
||||||
|
_subContentProvidersReadOnly = new ReadOnlyObservableCollection<ISubContentProvider>(_subContentProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyObservableCollection<IContentProvider> ContentProviders
|
public ReadOnlyObservableCollection<IContentProvider> ContentProviders
|
||||||
@@ -26,20 +32,33 @@ public class ContentProviderRegistry : IContentProviderRegistry
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObservableCollection<ISubContentProvider> SubContentProviders
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
InitializeContentProviderListIfNeeded();
|
||||||
|
return _subContentProvidersReadOnly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void InitializeContentProviderListIfNeeded()
|
private void InitializeContentProviderListIfNeeded()
|
||||||
{
|
{
|
||||||
if (_initialized) return;
|
if (_initialized) return;
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (!_initialized)
|
if (_initialized) return;
|
||||||
{
|
|
||||||
foreach (var contentProvider in _serviceProvider.GetServices<IContentProvider>())
|
|
||||||
{
|
|
||||||
_contentProviders.Add(contentProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
_initialized = true;
|
foreach (var contentProvider in _serviceProvider.GetServices<IContentProvider>())
|
||||||
|
{
|
||||||
|
_contentProviders.Add(contentProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var subContentProvider in _serviceProvider.GetServices<ISubContentProvider>())
|
||||||
|
{
|
||||||
|
_subContentProviders.Add(subContentProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,4 +81,20 @@ public class ContentProviderRegistry : IContentProviderRegistry
|
|||||||
_contentProviders.Remove(contentProvider);
|
_contentProviders.Remove(contentProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<ISubContentProvider?> GetSubContentProviderForElement(IElement parentElement)
|
||||||
|
{
|
||||||
|
var subContentProviders = _serviceProvider
|
||||||
|
.GetServices<ISubContentProvider>()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var subContentProvider in subContentProviders)
|
||||||
|
{
|
||||||
|
if(!await subContentProvider.CanHandleAsync(parentElement)) continue;
|
||||||
|
|
||||||
|
return subContentProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using FileTime.Core.Enums;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Core.ContentAccess;
|
||||||
|
|
||||||
|
public abstract class SubContentProviderBase : ContentProviderBase
|
||||||
|
{
|
||||||
|
private readonly IContentProvider _parentContentProvider;
|
||||||
|
|
||||||
|
protected SubContentProviderBase(
|
||||||
|
IContentProvider parentContentProvider,
|
||||||
|
string name,
|
||||||
|
ITimelessContentProvider timelessContentProvider) : base(name, timelessContentProvider)
|
||||||
|
{
|
||||||
|
_parentContentProvider = parentContentProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<IItem> GetItemByNativePathAsync(
|
||||||
|
NativePath nativePath,
|
||||||
|
PointInTime pointInTime,
|
||||||
|
bool forceResolve = false,
|
||||||
|
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||||
|
ItemInitializationSettings itemInitializationSettings = default)
|
||||||
|
=> await _parentContentProvider.GetItemByNativePathAsync(
|
||||||
|
nativePath,
|
||||||
|
pointInTime,
|
||||||
|
forceResolve,
|
||||||
|
forceResolvePathType,
|
||||||
|
itemInitializationSettings);
|
||||||
|
|
||||||
|
public override async ValueTask<NativePath> GetNativePathAsync(FullName fullName)
|
||||||
|
=> await _parentContentProvider.GetNativePathAsync(fullName);
|
||||||
|
|
||||||
|
public override FullName GetFullName(NativePath nativePath)
|
||||||
|
=> _parentContentProvider.GetFullName(nativePath);
|
||||||
|
|
||||||
|
public override async Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default)
|
||||||
|
=> await _parentContentProvider.GetContentAsync(element, maxLength, cancellationToken);
|
||||||
|
|
||||||
|
public override async Task<bool> CanHandlePathAsync(NativePath path)
|
||||||
|
=> await _parentContentProvider.CanHandlePathAsync(path);
|
||||||
|
|
||||||
|
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path)
|
||||||
|
=> _parentContentProvider.GetVolumeSizeInfo(path);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Reactive.Subjects;
|
using DeclarativeProperty;
|
||||||
using FileTime.Core.ContentAccess;
|
using FileTime.Core.ContentAccess;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
@@ -10,8 +10,8 @@ public class TimelessContentProvider : ITimelessContentProvider
|
|||||||
{
|
{
|
||||||
private readonly IContentProviderRegistry _contentProviderRegistry;
|
private readonly IContentProviderRegistry _contentProviderRegistry;
|
||||||
private readonly Lazy<IRootContentProvider> _rootContentProvider;
|
private readonly Lazy<IRootContentProvider> _rootContentProvider;
|
||||||
|
private readonly DeclarativeProperty<PointInTime> _currentPointInTime = new(PointInTime.Present);
|
||||||
public BehaviorSubject<PointInTime> CurrentPointInTime { get; } = new(PointInTime.Present);
|
public IDeclarativeProperty<PointInTime> CurrentPointInTime => _currentPointInTime;
|
||||||
|
|
||||||
public TimelessContentProvider(
|
public TimelessContentProvider(
|
||||||
IContentProviderRegistry contentProviderRegistry,
|
IContentProviderRegistry contentProviderRegistry,
|
||||||
|
|||||||
@@ -147,6 +147,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Server.Tracker", "
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Server.Extensions.DependencyInjection", "Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj", "{25AA9F04-EEEE-49C4-870B-CDFF71717687}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Server.Extensions.DependencyInjection", "Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj", "{25AA9F04-EEEE-49C4-870B-CDFF71717687}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Tools.VirtualDiskSources", "Tools\FileTime.Tools.VirtualDiskSources\FileTime.Tools.VirtualDiskSources.csproj", "{DBCB10AD-9647-46AB-B9A1-3ACB9BCA46B9}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Tools.VirtualDiskSources.Abstractions", "Tools\FileTime.Tools.VirtualDiskSources.Abstractions\FileTime.Tools.VirtualDiskSources.Abstractions.csproj", "{53E5B762-B620-4106-B481-31A478A1E14F}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -405,6 +409,14 @@ Global
|
|||||||
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Release|Any CPU.Build.0 = Release|Any CPU
|
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DBCB10AD-9647-46AB-B9A1-3ACB9BCA46B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DBCB10AD-9647-46AB-B9A1-3ACB9BCA46B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DBCB10AD-9647-46AB-B9A1-3ACB9BCA46B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DBCB10AD-9647-46AB-B9A1-3ACB9BCA46B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{53E5B762-B620-4106-B481-31A478A1E14F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{53E5B762-B620-4106-B481-31A478A1E14F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{53E5B762-B620-4106-B481-31A478A1E14F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{53E5B762-B620-4106-B481-31A478A1E14F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -474,6 +486,8 @@ Global
|
|||||||
{0A29616B-5413-4DA9-96EC-B45D6AF632C9} = {3324D046-1E05-46B5-B1BA-82910D56B332}
|
{0A29616B-5413-4DA9-96EC-B45D6AF632C9} = {3324D046-1E05-46B5-B1BA-82910D56B332}
|
||||||
{BD382403-86D8-4E99-B279-370FC22F059C} = {778AAF38-20FF-438C-A9C3-60850C8B5A27}
|
{BD382403-86D8-4E99-B279-370FC22F059C} = {778AAF38-20FF-438C-A9C3-60850C8B5A27}
|
||||||
{25AA9F04-EEEE-49C4-870B-CDFF71717687} = {778AAF38-20FF-438C-A9C3-60850C8B5A27}
|
{25AA9F04-EEEE-49C4-870B-CDFF71717687} = {778AAF38-20FF-438C-A9C3-60850C8B5A27}
|
||||||
|
{DBCB10AD-9647-46AB-B9A1-3ACB9BCA46B9} = {8C3CFEFE-78A5-4940-B388-D15FCE02ECE9}
|
||||||
|
{53E5B762-B620-4106-B481-31A478A1E14F} = {8C3CFEFE-78A5-4940-B388-D15FCE02ECE9}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF}
|
SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF}
|
||||||
|
|||||||
@@ -113,9 +113,9 @@ public static class Program
|
|||||||
|
|
||||||
private static void NormalStartup(string[] args)
|
private static void NormalStartup(string[] args)
|
||||||
{
|
{
|
||||||
AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
|
/*AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
|
||||||
AppDomain.CurrentDomain.UnhandledException += OnAppDomainUnhandledException;
|
AppDomain.CurrentDomain.UnhandledException += OnAppDomainUnhandledException;
|
||||||
TaskScheduler.UnobservedTaskException += OnTaskSchedulerUnobservedTaskException;
|
TaskScheduler.UnobservedTaskException += OnTaskSchedulerUnobservedTaskException;*/
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
BuildAvaloniaApp()
|
BuildAvaloniaApp()
|
||||||
|
|||||||
@@ -12,15 +12,18 @@ namespace FileTime.Providers.Local;
|
|||||||
public sealed partial class LocalContentProvider : ContentProviderBase, ILocalContentProvider
|
public sealed partial class LocalContentProvider : ContentProviderBase, ILocalContentProvider
|
||||||
{
|
{
|
||||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
|
private readonly IContentProviderRegistry _contentProviderRegistry;
|
||||||
private readonly bool _isCaseInsensitive;
|
private readonly bool _isCaseInsensitive;
|
||||||
private readonly Lazy<ObservableCollection<RootDriveInfo>> _rootDriveInfos;
|
private readonly Lazy<ObservableCollection<RootDriveInfo>> _rootDriveInfos;
|
||||||
|
|
||||||
public LocalContentProvider(
|
public LocalContentProvider(
|
||||||
ITimelessContentProvider timelessContentProvider,
|
ITimelessContentProvider timelessContentProvider,
|
||||||
IServiceProvider serviceProvider)
|
IServiceProvider serviceProvider,
|
||||||
|
IContentProviderRegistry contentProviderRegistry)
|
||||||
: base(LocalContentProviderConstants.ContentProviderId, timelessContentProvider)
|
: base(LocalContentProviderConstants.ContentProviderId, timelessContentProvider)
|
||||||
{
|
{
|
||||||
_timelessContentProvider = timelessContentProvider;
|
_timelessContentProvider = timelessContentProvider;
|
||||||
|
_contentProviderRegistry = contentProviderRegistry;
|
||||||
_isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
_isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||||
|
|
||||||
_rootDriveInfos = new Lazy<ObservableCollection<RootDriveInfo>>(() => serviceProvider.GetRequiredService<IRootDriveInfoService>().RootDriveInfos);
|
_rootDriveInfos = new Lazy<ObservableCollection<RootDriveInfo>>(() => serviceProvider.GetRequiredService<IRootDriveInfoService>().RootDriveInfos);
|
||||||
@@ -45,12 +48,6 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
|
|
||||||
Items.Clear();
|
Items.Clear();
|
||||||
Items.AddRange(rootDirectories.Select(d => DirectoryToAbsolutePath(d, PointInTime.Present)));
|
Items.AddRange(rootDirectories.Select(d => DirectoryToAbsolutePath(d, PointInTime.Present)));
|
||||||
|
|
||||||
/*Items.Edit(actions =>
|
|
||||||
{
|
|
||||||
actions.Clear();
|
|
||||||
actions.AddOrUpdate(rootDirectories.Select(d => DirectoryToAbsolutePath(d, PointInTime.Present)));
|
|
||||||
});*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<bool> CanHandlePathAsync(NativePath path)
|
public override async Task<bool> CanHandlePathAsync(NativePath path)
|
||||||
@@ -79,11 +76,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
return new VolumeSizeInfo(rootDriveInfo.Size, rootDriveInfo.Free);
|
return new VolumeSizeInfo(rootDriveInfo.Size, rootDriveInfo.Free);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<IItem> GetItemByNativePathAsync(NativePath nativePath,
|
public override async Task<IItem> GetItemByNativePathAsync(NativePath nativePath,
|
||||||
PointInTime pointInTime,
|
PointInTime pointInTime,
|
||||||
bool forceResolve = false,
|
bool forceResolve = false,
|
||||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||||
ItemInitializationSettings? itemInitializationSettings = null)
|
ItemInitializationSettings itemInitializationSettings = default)
|
||||||
{
|
{
|
||||||
var path = nativePath.Path;
|
var path = nativePath.Path;
|
||||||
Exception? innerException;
|
Exception? innerException;
|
||||||
@@ -91,19 +88,40 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
{
|
{
|
||||||
if (path.Length == 0)
|
if (path.Length == 0)
|
||||||
{
|
{
|
||||||
return Task.FromResult((IItem) this);
|
return this;
|
||||||
}
|
}
|
||||||
else if (Directory.Exists(path))
|
|
||||||
|
if (Directory.Exists(path))
|
||||||
{
|
{
|
||||||
return Task.FromResult((IItem) DirectoryToContainer(
|
return DirectoryToContainer(
|
||||||
new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar),
|
new DirectoryInfo(path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar),
|
||||||
pointInTime,
|
pointInTime,
|
||||||
itemInitializationSettings)
|
itemInitializationSettings);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else if (File.Exists(path))
|
else if (File.Exists(path))
|
||||||
{
|
{
|
||||||
return Task.FromResult((IItem) FileToElement(new FileInfo(path), pointInTime));
|
return FileToElement(new FileInfo(path), pointInTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathParts = path.Split(Path.DirectorySeparatorChar).SelectMany(p => p.Split(Constants.SeparatorChar)).ToArray();
|
||||||
|
|
||||||
|
for (var i = pathParts.Length - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
var possibleFile = string.Join(Path.DirectorySeparatorChar, pathParts.Take(i));
|
||||||
|
if (!File.Exists(possibleFile)) continue;
|
||||||
|
|
||||||
|
var element = FileToElement(new FileInfo(possibleFile), pointInTime);
|
||||||
|
var subContentProvider = await _contentProviderRegistry.GetSubContentProviderForElement(element);
|
||||||
|
if (subContentProvider is null) break;
|
||||||
|
|
||||||
|
var subPath = string.Join(Constants.SeparatorChar, pathParts.Skip(i));
|
||||||
|
|
||||||
|
var resolvedItem = await subContentProvider.GetItemByFullNameAsync(element, new FullName(subPath), pointInTime, forceResolvePathType, itemInitializationSettings);
|
||||||
|
|
||||||
|
if (resolvedItem is not null)
|
||||||
|
{
|
||||||
|
return resolvedItem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = forceResolvePathType switch
|
var type = forceResolvePathType switch
|
||||||
@@ -134,14 +152,12 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
|
|
||||||
return forceResolvePathType switch
|
return forceResolvePathType switch
|
||||||
{
|
{
|
||||||
AbsolutePathType.Container => Task.FromResult(
|
AbsolutePathType.Container => CreateEmptyContainer(
|
||||||
(IItem) CreateEmptyContainer(
|
nativePath,
|
||||||
nativePath,
|
pointInTime,
|
||||||
pointInTime,
|
new List<Exception> {innerException}
|
||||||
new List<Exception>() {innerException}
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)),
|
AbsolutePathType.Element => CreateEmptyElement(nativePath),
|
||||||
_ => throw new Exception(
|
_ => throw new Exception(
|
||||||
$"Could not resolve path '{nativePath.Path}' and could not force create, because {nameof(forceResolvePathType)} is {nameof(AbsolutePathType.Unknown)}.",
|
$"Could not resolve path '{nativePath.Path}' and could not force create, because {nameof(forceResolvePathType)} is {nameof(AbsolutePathType.Unknown)}.",
|
||||||
innerException)
|
innerException)
|
||||||
@@ -211,10 +227,8 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Container DirectoryToContainer(DirectoryInfo directoryInfo, PointInTime pointInTime,
|
private Container DirectoryToContainer(DirectoryInfo directoryInfo, PointInTime pointInTime,
|
||||||
ItemInitializationSettings? initializationSettings = null)
|
ItemInitializationSettings initializationSettings = default)
|
||||||
{
|
{
|
||||||
initializationSettings ??= new();
|
|
||||||
|
|
||||||
var fullName = GetFullName(directoryInfo.FullName);
|
var fullName = GetFullName(directoryInfo.FullName);
|
||||||
var parentFullName = fullName.GetParent();
|
var parentFullName = fullName.GetParent();
|
||||||
var parent =
|
var parent =
|
||||||
@@ -336,17 +350,6 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<AbsolutePath> GetItemsByContainer(DirectoryInfo directoryInfo, PointInTime pointInTime)
|
|
||||||
=> directoryInfo
|
|
||||||
.GetDirectories()
|
|
||||||
.Select(d => DirectoryToAbsolutePath(d, pointInTime))
|
|
||||||
.Concat(
|
|
||||||
directoryInfo
|
|
||||||
.GetFiles()
|
|
||||||
.Select(f => FileToAbsolutePath(f, pointInTime))
|
|
||||||
)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
private Element FileToElement(FileInfo fileInfo, PointInTime pointInTime)
|
private Element FileToElement(FileInfo fileInfo, PointInTime pointInTime)
|
||||||
{
|
{
|
||||||
var fullName = GetFullName(fileInfo);
|
var fullName = GetFullName(fileInfo);
|
||||||
@@ -387,7 +390,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
nativePath.TrimStart(Constants.SeparatorChar).Split(Path.DirectorySeparatorChar)))
|
nativePath.TrimStart(Constants.SeparatorChar).Split(Path.DirectorySeparatorChar)))
|
||||||
.TrimEnd(Constants.SeparatorChar))!;
|
.TrimEnd(Constants.SeparatorChar))!;
|
||||||
|
|
||||||
public override ValueTask<NativePath> GetNativePathAsync(FullName fullName)
|
public override ValueTask<NativePath> GetNativePathAsync(FullName fullName)
|
||||||
=> ValueTask.FromResult(GetNativePath(fullName));
|
=> ValueTask.FromResult(GetNativePath(fullName));
|
||||||
|
|
||||||
public NativePath GetNativePath(FullName fullName)
|
public NativePath GetNativePath(FullName fullName)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.VirtualDiskSources.Abstractions;
|
||||||
|
|
||||||
|
public interface IVirtualDiskSubContentProvider : ISubContentProvider
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using FileTime.App.Core.Services;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
|
public class DiscUtilsInitializer : IPreStartupHandler
|
||||||
|
{
|
||||||
|
public Task InitAsync()
|
||||||
|
{
|
||||||
|
DiscUtils.Containers.SetupHelper.SetupContainers();
|
||||||
|
DiscUtils.FileSystems.SetupHelper.SetupFileSystems();
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Core\FileTime.Core.ContentAccess\FileTime.Core.ContentAccess.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Core\FileTime.Core.Models\FileTime.Core.Models.csproj" />
|
||||||
|
<ProjectReference Include="..\FileTime.Tools.VirtualDiskSources.Abstractions\FileTime.Tools.VirtualDiskSources.Abstractions.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LTRData.DiscUtils.Containers" Version="1.0.28" />
|
||||||
|
<PackageReference Include="LTRData.DiscUtils.FileSystems" Version="1.0.28" />
|
||||||
|
<PackageReference Include="LTRData.DiscUtils.OpticalDisk" Version="1.0.28" />
|
||||||
|
<PackageReference Include="LTRData.DiscUtils.VirtualFileSystem" Version="1.0.28" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
18
src/Tools/FileTime.Tools.VirtualDiskSources/Startup.cs
Normal file
18
src/Tools/FileTime.Tools.VirtualDiskSources/Startup.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using FileTime.App.Core.Services;
|
||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Tools.VirtualDiskSources.Abstractions;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
|
public static class Startup
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddVirtualDisk(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.TryAddSingleton<IVirtualDiskSubContentProvider, VirtualDiskSubContentProvider>();
|
||||||
|
services.AddSingleton<ISubContentProvider>(sp => sp.GetRequiredService<IVirtualDiskSubContentProvider>());
|
||||||
|
services.AddSingleton<IPreStartupHandler, DiscUtilsInitializer>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
|
public class VirtualDiskContentProvider : SubContentProviderBase
|
||||||
|
{
|
||||||
|
public VirtualDiskContentProvider(
|
||||||
|
IContentProvider parentContentProvider,
|
||||||
|
ITimelessContentProvider timelessContentProvider)
|
||||||
|
: base(parentContentProvider, "virtual-disk", timelessContentProvider)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using DiscUtils;
|
||||||
|
using DiscUtils.Iso9660;
|
||||||
|
using DiscUtils.Udf;
|
||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Core.Enums;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
using FileTime.Tools.VirtualDiskSources.Abstractions;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
|
public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
||||||
|
{
|
||||||
|
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||||
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
|
|
||||||
|
public VirtualDiskSubContentProvider(
|
||||||
|
IContentAccessorFactory contentAccessorFactory,
|
||||||
|
ITimelessContentProvider timelessContentProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_contentAccessorFactory = contentAccessorFactory;
|
||||||
|
_timelessContentProvider = timelessContentProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> CanHandleAsync(IElement parentElement)
|
||||||
|
=> Task.FromResult(parentElement.NativePath?.Path.EndsWith(".iso", StringComparison.OrdinalIgnoreCase) ?? false);
|
||||||
|
|
||||||
|
public async Task<IItem?> GetItemByFullNameAsync(
|
||||||
|
IElement parentElement,
|
||||||
|
FullName itemPath,
|
||||||
|
PointInTime pointInTime,
|
||||||
|
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||||
|
ItemInitializationSettings itemInitializationSettings = default)
|
||||||
|
{
|
||||||
|
var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider);
|
||||||
|
var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement);
|
||||||
|
|
||||||
|
await using var readerStream = reader.AsStream();
|
||||||
|
var discReader = new UdfReader(readerStream);
|
||||||
|
|
||||||
|
if (itemPath.Path.Length == 0 || itemPath.Path == Constants.SubContentProviderRootContainer)
|
||||||
|
{
|
||||||
|
var rootFullNameBase = parentElement.FullName!.Path + Constants.SeparatorChar + Constants.SubContentProviderRootContainer;
|
||||||
|
var rootNativePathBase = parentElement.NativePath!.Path + Constants.SeparatorChar + Constants.SubContentProviderRootContainer;
|
||||||
|
|
||||||
|
var rootFullName = new FullName(rootFullNameBase);
|
||||||
|
var rootNativePath = new NativePath(rootNativePathBase);
|
||||||
|
|
||||||
|
return CreateContainer(discReader.Root,
|
||||||
|
rootFullName,
|
||||||
|
rootNativePath,
|
||||||
|
parentElement.Provider,
|
||||||
|
parentElement.Parent!,
|
||||||
|
parentElement.PointInTime,
|
||||||
|
itemInitializationSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolveNonRootChild(discReader, parentElement, itemPath, pointInTime, itemInitializationSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IItem? ResolveNonRootChild(
|
||||||
|
UdfReader discReader,
|
||||||
|
IElement parentElement,
|
||||||
|
FullName itemPath,
|
||||||
|
PointInTime pointInTime,
|
||||||
|
ItemInitializationSettings itemInitializationSettings = default)
|
||||||
|
{
|
||||||
|
var pathParts = itemPath.Path.Split(Constants.SeparatorChar);
|
||||||
|
|
||||||
|
var childFullNameBase = parentElement.FullName!.Path + Constants.SeparatorChar + itemPath.Path;
|
||||||
|
var childNativePathBase = parentElement.NativePath!.Path + Constants.SeparatorChar + itemPath.Path;
|
||||||
|
|
||||||
|
var childFullName = new FullName(childFullNameBase);
|
||||||
|
var childNativePath = new NativePath(childNativePathBase);
|
||||||
|
|
||||||
|
var parent = new AbsolutePath(_timelessContentProvider, pointInTime, childFullName.GetParent()!, AbsolutePathType.Container);
|
||||||
|
|
||||||
|
var container = discReader.Root;
|
||||||
|
for (var i = 1; i < pathParts.Length - 1; i++)
|
||||||
|
{
|
||||||
|
if (container is null) break;
|
||||||
|
container = container.GetDirectories().FirstOrDefault(d => d.Name == pathParts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (container is null) return null;
|
||||||
|
|
||||||
|
if (container.GetDirectories().FirstOrDefault(d => d.Name == pathParts[^1]) is { } childContainer)
|
||||||
|
{
|
||||||
|
return CreateContainer(
|
||||||
|
childContainer,
|
||||||
|
childFullName,
|
||||||
|
childNativePath,
|
||||||
|
parentElement.Provider,
|
||||||
|
parent,
|
||||||
|
pointInTime,
|
||||||
|
itemInitializationSettings
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (container.GetFiles().FirstOrDefault(d => d.Name == pathParts[^1]) is { } childElement)
|
||||||
|
{
|
||||||
|
return CreateElement(
|
||||||
|
childElement,
|
||||||
|
childFullName,
|
||||||
|
childNativePath,
|
||||||
|
parentElement.Provider,
|
||||||
|
parent,
|
||||||
|
pointInTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IContainer CreateContainer(
|
||||||
|
DiscDirectoryInfo sourceContainer,
|
||||||
|
FullName fullname,
|
||||||
|
NativePath nativePath,
|
||||||
|
IContentProvider parentContentProvider,
|
||||||
|
AbsolutePath parent,
|
||||||
|
PointInTime pointInTime,
|
||||||
|
ItemInitializationSettings initializationSettings)
|
||||||
|
{
|
||||||
|
var children = new ObservableCollection<AbsolutePath>();
|
||||||
|
var exceptions = new ObservableCollection<Exception>();
|
||||||
|
var container = new Container(
|
||||||
|
sourceContainer.Name,
|
||||||
|
sourceContainer.Name,
|
||||||
|
fullname,
|
||||||
|
nativePath,
|
||||||
|
parent,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
sourceContainer.CreationTime,
|
||||||
|
sourceContainer.LastWriteTime,
|
||||||
|
SupportsDelete.False,
|
||||||
|
false,
|
||||||
|
FormatAttributes(sourceContainer.Attributes),
|
||||||
|
new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider),
|
||||||
|
false,
|
||||||
|
pointInTime,
|
||||||
|
exceptions,
|
||||||
|
new ReadOnlyExtensionCollection(new ExtensionCollection()),
|
||||||
|
children
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!initializationSettings.SkipChildInitialization)
|
||||||
|
{
|
||||||
|
ThreadPool.QueueUserWorkItem(_ => LoadChildren(container, sourceContainer, children, pointInTime, exceptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadChildren(
|
||||||
|
Container container,
|
||||||
|
DiscDirectoryInfo sourceContainer,
|
||||||
|
ObservableCollection<AbsolutePath> children,
|
||||||
|
PointInTime pointInTime,
|
||||||
|
ObservableCollection<Exception> exceptions
|
||||||
|
)
|
||||||
|
{
|
||||||
|
foreach (var discDirectoryInfo in sourceContainer.GetDirectories())
|
||||||
|
{
|
||||||
|
children.Add(new AbsolutePath(
|
||||||
|
_timelessContentProvider,
|
||||||
|
pointInTime,
|
||||||
|
container.FullName.GetChild(discDirectoryInfo.Name),
|
||||||
|
AbsolutePathType.Container)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var fileInfo in sourceContainer.GetFiles())
|
||||||
|
{
|
||||||
|
children.Add(new AbsolutePath(
|
||||||
|
_timelessContentProvider,
|
||||||
|
pointInTime,
|
||||||
|
container.FullName.GetChild(fileInfo.Name),
|
||||||
|
AbsolutePathType.Element)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IElement CreateElement(DiscFileInfo childElement,
|
||||||
|
FullName fullname,
|
||||||
|
NativePath nativePath,
|
||||||
|
IContentProvider parentContentProvider,
|
||||||
|
AbsolutePath parent,
|
||||||
|
PointInTime pointInTime)
|
||||||
|
{
|
||||||
|
var element = new Element(
|
||||||
|
childElement.Name,
|
||||||
|
childElement.Name,
|
||||||
|
fullname,
|
||||||
|
nativePath,
|
||||||
|
parent,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
childElement.CreationTime,
|
||||||
|
childElement.LastWriteTime,
|
||||||
|
SupportsDelete.False,
|
||||||
|
false,
|
||||||
|
FormatAttributes(childElement.Attributes),
|
||||||
|
childElement.Length,
|
||||||
|
new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider),
|
||||||
|
pointInTime,
|
||||||
|
new ObservableCollection<Exception>(),
|
||||||
|
new ReadOnlyExtensionCollection(new ExtensionCollection())
|
||||||
|
);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatAttributes(FileAttributes attributes)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user