Browse ISOs
This commit is contained in:
@@ -28,7 +28,7 @@ public class ContainerSizeSizeScanProvider : ContentProviderBase, IContainerSize
|
||||
PointInTime pointInTime,
|
||||
bool forceResolve = false,
|
||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||
ItemInitializationSettings itemInitializationSettings = null
|
||||
ItemInitializationSettings itemInitializationSettings = default
|
||||
)
|
||||
{
|
||||
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
|
||||
{
|
||||
private readonly IEnumerable<IPreStartupHandler> _preStartupHandlers;
|
||||
private readonly IEnumerable<IExitHandler> _exitHandlers;
|
||||
private readonly IEnumerable<IStartupHandler> _startupHandlers;
|
||||
private readonly ILogger<LifecycleService> _logger;
|
||||
|
||||
public LifecycleService(
|
||||
IEnumerable<IPreStartupHandler> preStartupHandlers,
|
||||
IEnumerable<IStartupHandler> startupHandlers,
|
||||
IEnumerable<IExitHandler> exitHandlers,
|
||||
ILogger<LifecycleService> logger)
|
||||
{
|
||||
_preStartupHandlers = preStartupHandlers;
|
||||
_exitHandlers = exitHandlers;
|
||||
_startupHandlers = startupHandlers;
|
||||
_logger = logger;
|
||||
@@ -21,6 +24,18 @@ public class LifecycleService : ILifecycleService
|
||||
|
||||
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)
|
||||
{
|
||||
try
|
||||
@@ -29,7 +44,7 @@ public class LifecycleService : ILifecycleService
|
||||
}
|
||||
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.ViewModels;
|
||||
using FileTime.App.FrequencyNavigation.Services;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Services;
|
||||
@@ -29,6 +30,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
private readonly IUserCommunicationService _userCommunicationService;
|
||||
private readonly IFrequencyNavigationService _frequencyNavigationService;
|
||||
private readonly ICommandPaletteService _commandPaletteService;
|
||||
private readonly IContentProviderRegistry _contentProviderRegistry;
|
||||
private readonly ILogger<NavigationUserCommandHandlerService> _logger;
|
||||
private readonly ApplicationConfiguration _applicationConfiguration;
|
||||
private ITabViewModel? _selectedTab;
|
||||
@@ -46,6 +48,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
IUserCommunicationService userCommunicationService,
|
||||
IFrequencyNavigationService frequencyNavigationService,
|
||||
ICommandPaletteService commandPaletteService,
|
||||
IContentProviderRegistry contentProviderRegistry,
|
||||
ILogger<NavigationUserCommandHandlerService> logger,
|
||||
ApplicationConfiguration applicationConfiguration) : base(appState)
|
||||
{
|
||||
@@ -57,6 +60,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
_userCommunicationService = userCommunicationService;
|
||||
_frequencyNavigationService = frequencyNavigationService;
|
||||
_commandPaletteService = commandPaletteService;
|
||||
_contentProviderRegistry = contentProviderRegistry;
|
||||
_logger = logger;
|
||||
_applicationConfiguration = applicationConfiguration;
|
||||
|
||||
@@ -249,13 +253,40 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
await _appState.SetRapidTravelTextAsync("");
|
||||
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)
|
||||
{
|
||||
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();
|
||||
return t.Result;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using FileTime.Core;
|
||||
using FileTime.Providers.Local;
|
||||
using FileTime.Providers.LocalAdmin;
|
||||
using FileTime.Providers.Remote;
|
||||
using FileTime.Tools.VirtualDiskSources;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
@@ -28,6 +29,7 @@ public static class DependencyInjection
|
||||
.AddAppCoreDependencies(configuration)
|
||||
.AddLocalProviderServices()
|
||||
.AddLocalAdminProviderServices(configuration)
|
||||
.AddRemoteProviderServices();
|
||||
.AddRemoteProviderServices()
|
||||
.AddVirtualDisk();
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
<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.Local\FileTime.Providers.Local.csproj" />
|
||||
<ProjectReference Include="..\..\Tools\FileTime.Tools.VirtualDiskSources\FileTime.Tools.VirtualDiskSources.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.Core\FileTime.App.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ public class SearchContentProvider : ContentProviderBase, ISearchContentProvider
|
||||
PointInTime pointInTime,
|
||||
bool forceResolve = false,
|
||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||
ItemInitializationSettings itemInitializationSettings = null
|
||||
ItemInitializationSettings itemInitializationSettings = default
|
||||
)
|
||||
{
|
||||
if (fullName.Path == ContentProviderName)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Core.ContentAccess;
|
||||
|
||||
public interface IContentProviderRegistry
|
||||
{
|
||||
ReadOnlyObservableCollection<IContentProvider> ContentProviders { get; }
|
||||
ReadOnlyObservableCollection<ISubContentProvider> SubContentProviders { get; }
|
||||
void AddContentProvider(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.Timeline;
|
||||
|
||||
namespace FileTime.Core.Models;
|
||||
|
||||
[DebuggerDisplay("{Path.Path}, {Type}")]
|
||||
public class AbsolutePath
|
||||
{
|
||||
public ITimelessContentProvider TimelessProvider { get; }
|
||||
|
||||
@@ -3,4 +3,5 @@ namespace FileTime.Core.Models;
|
||||
public static class Constants
|
||||
{
|
||||
public const char SeparatorChar = '/';
|
||||
public const string SubContentProviderRootContainer = ":";
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace FileTime.Core.Models;
|
||||
|
||||
[DebuggerDisplay("{Path}")]
|
||||
public record FullName(string Path)
|
||||
{
|
||||
public FullName? GetParent()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace FileTime.Core.Models;
|
||||
|
||||
public sealed class ItemInitializationSettings
|
||||
public readonly struct ItemInitializationSettings
|
||||
{
|
||||
public bool SkipChildInitialization { get; init; }
|
||||
public AbsolutePath? Parent { get; init; }
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace FileTime.Core.Models;
|
||||
|
||||
[DebuggerDisplay("{Path}")]
|
||||
public record NativePath(string Path)
|
||||
{
|
||||
public override string ToString() => Path;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Reactive.Subjects;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace FileTime.Core.Timeline;
|
||||
|
||||
public interface ITimelessContentProvider
|
||||
{
|
||||
BehaviorSubject<PointInTime> CurrentPointInTime { get; }
|
||||
IDeclarativeProperty<PointInTime> CurrentPointInTime { get; }
|
||||
|
||||
Task<IItem> GetItemByFullNameAsync(FullName fullName,
|
||||
PointInTime? pointInTime,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace FileTime.Core.ContentAccess;
|
||||
@@ -8,13 +11,16 @@ public class ContentProviderRegistry : IContentProviderRegistry
|
||||
private readonly object _lock = new();
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ObservableCollection<IContentProvider> _contentProviders = new();
|
||||
private readonly ObservableCollection<ISubContentProvider> _subContentProviders = new();
|
||||
private readonly ReadOnlyObservableCollection<IContentProvider> _contentProvidersReadOnly;
|
||||
private readonly ReadOnlyObservableCollection<ISubContentProvider> _subContentProvidersReadOnly;
|
||||
private bool _initialized;
|
||||
|
||||
public ContentProviderRegistry(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_contentProvidersReadOnly = new ReadOnlyObservableCollection<IContentProvider>(_contentProviders);
|
||||
_subContentProvidersReadOnly = new ReadOnlyObservableCollection<ISubContentProvider>(_subContentProviders);
|
||||
}
|
||||
|
||||
public ReadOnlyObservableCollection<IContentProvider> ContentProviders
|
||||
@@ -26,20 +32,33 @@ public class ContentProviderRegistry : IContentProviderRegistry
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlyObservableCollection<ISubContentProvider> SubContentProviders
|
||||
{
|
||||
get
|
||||
{
|
||||
InitializeContentProviderListIfNeeded();
|
||||
return _subContentProvidersReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeContentProviderListIfNeeded()
|
||||
{
|
||||
if (_initialized) return;
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
if (_initialized) return;
|
||||
|
||||
foreach (var contentProvider in _serviceProvider.GetServices<IContentProvider>())
|
||||
{
|
||||
_contentProviders.Add(contentProvider);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
foreach (var subContentProvider in _serviceProvider.GetServices<ISubContentProvider>())
|
||||
{
|
||||
_subContentProviders.Add(subContentProvider);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,4 +81,20 @@ public class ContentProviderRegistry : IContentProviderRegistry
|
||||
_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.Enums;
|
||||
using FileTime.Core.Models;
|
||||
@@ -10,8 +10,8 @@ public class TimelessContentProvider : ITimelessContentProvider
|
||||
{
|
||||
private readonly IContentProviderRegistry _contentProviderRegistry;
|
||||
private readonly Lazy<IRootContentProvider> _rootContentProvider;
|
||||
|
||||
public BehaviorSubject<PointInTime> CurrentPointInTime { get; } = new(PointInTime.Present);
|
||||
private readonly DeclarativeProperty<PointInTime> _currentPointInTime = new(PointInTime.Present);
|
||||
public IDeclarativeProperty<PointInTime> CurrentPointInTime => _currentPointInTime;
|
||||
|
||||
public TimelessContentProvider(
|
||||
IContentProviderRegistry contentProviderRegistry,
|
||||
|
||||
@@ -147,6 +147,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Server.Tracker", "
|
||||
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}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -474,6 +486,8 @@ Global
|
||||
{0A29616B-5413-4DA9-96EC-B45D6AF632C9} = {3324D046-1E05-46B5-B1BA-82910D56B332}
|
||||
{BD382403-86D8-4E99-B279-370FC22F059C} = {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
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF}
|
||||
|
||||
@@ -113,9 +113,9 @@ public static class Program
|
||||
|
||||
private static void NormalStartup(string[] args)
|
||||
{
|
||||
AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
|
||||
/*AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
|
||||
AppDomain.CurrentDomain.UnhandledException += OnAppDomainUnhandledException;
|
||||
TaskScheduler.UnobservedTaskException += OnTaskSchedulerUnobservedTaskException;
|
||||
TaskScheduler.UnobservedTaskException += OnTaskSchedulerUnobservedTaskException;*/
|
||||
try
|
||||
{
|
||||
BuildAvaloniaApp()
|
||||
|
||||
@@ -12,15 +12,18 @@ namespace FileTime.Providers.Local;
|
||||
public sealed partial class LocalContentProvider : ContentProviderBase, ILocalContentProvider
|
||||
{
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
private readonly IContentProviderRegistry _contentProviderRegistry;
|
||||
private readonly bool _isCaseInsensitive;
|
||||
private readonly Lazy<ObservableCollection<RootDriveInfo>> _rootDriveInfos;
|
||||
|
||||
public LocalContentProvider(
|
||||
ITimelessContentProvider timelessContentProvider,
|
||||
IServiceProvider serviceProvider)
|
||||
IServiceProvider serviceProvider,
|
||||
IContentProviderRegistry contentProviderRegistry)
|
||||
: base(LocalContentProviderConstants.ContentProviderId, timelessContentProvider)
|
||||
{
|
||||
_timelessContentProvider = timelessContentProvider;
|
||||
_contentProviderRegistry = contentProviderRegistry;
|
||||
_isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
_rootDriveInfos = new Lazy<ObservableCollection<RootDriveInfo>>(() => serviceProvider.GetRequiredService<IRootDriveInfoService>().RootDriveInfos);
|
||||
@@ -45,12 +48,6 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
|
||||
Items.Clear();
|
||||
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)
|
||||
@@ -79,11 +76,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
return new VolumeSizeInfo(rootDriveInfo.Size, rootDriveInfo.Free);
|
||||
}
|
||||
|
||||
public override Task<IItem> GetItemByNativePathAsync(NativePath nativePath,
|
||||
public override async Task<IItem> GetItemByNativePathAsync(NativePath nativePath,
|
||||
PointInTime pointInTime,
|
||||
bool forceResolve = false,
|
||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||
ItemInitializationSettings? itemInitializationSettings = null)
|
||||
ItemInitializationSettings itemInitializationSettings = default)
|
||||
{
|
||||
var path = nativePath.Path;
|
||||
Exception? innerException;
|
||||
@@ -91,19 +88,40 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
{
|
||||
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(
|
||||
new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar),
|
||||
return DirectoryToContainer(
|
||||
new DirectoryInfo(path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar),
|
||||
pointInTime,
|
||||
itemInitializationSettings)
|
||||
);
|
||||
itemInitializationSettings);
|
||||
}
|
||||
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
|
||||
@@ -134,14 +152,12 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
|
||||
return forceResolvePathType switch
|
||||
{
|
||||
AbsolutePathType.Container => Task.FromResult(
|
||||
(IItem) CreateEmptyContainer(
|
||||
AbsolutePathType.Container => CreateEmptyContainer(
|
||||
nativePath,
|
||||
pointInTime,
|
||||
new List<Exception>() {innerException}
|
||||
)
|
||||
new List<Exception> {innerException}
|
||||
),
|
||||
AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)),
|
||||
AbsolutePathType.Element => CreateEmptyElement(nativePath),
|
||||
_ => throw new Exception(
|
||||
$"Could not resolve path '{nativePath.Path}' and could not force create, because {nameof(forceResolvePathType)} is {nameof(AbsolutePathType.Unknown)}.",
|
||||
innerException)
|
||||
@@ -211,10 +227,8 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
}
|
||||
|
||||
private Container DirectoryToContainer(DirectoryInfo directoryInfo, PointInTime pointInTime,
|
||||
ItemInitializationSettings? initializationSettings = null)
|
||||
ItemInitializationSettings initializationSettings = default)
|
||||
{
|
||||
initializationSettings ??= new();
|
||||
|
||||
var fullName = GetFullName(directoryInfo.FullName);
|
||||
var parentFullName = fullName.GetParent();
|
||||
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)
|
||||
{
|
||||
var fullName = GetFullName(fileInfo);
|
||||
|
||||
@@ -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