实现扩展Upscale功能

This commit is contained in:
Poker 2025-01-05 00:49:14 +08:00
parent 157408905d
commit 548b99c1c5
No known key found for this signature in database
GPG Key ID: 720AFAD63099D9CB
14 changed files with 177 additions and 176 deletions

View File

@ -179,8 +179,6 @@ public partial record AppSettings() : IWindowSettings
[AttributeIgnore(typeof(ResetAttribute))]
public DateTimeOffset LastCheckedUpdate { get; set; } = DateTimeOffset.MinValue;
public bool ShowUpscalerTeachingTip { get; set; } = true;
[SettingsEntry(Symbol.Box, nameof(PixivNameResolverHeaderText), nameof(PixivNameResolverDescriptionText))]
public string[] PixivAppApiNameResolver { get; set; } =
[

View File

@ -72,7 +72,7 @@ public partial class AppViewModel(App app) : IDisposable
var fileCache = await FileCache.CreateDefaultAsync();
var memoryCache = await MemoryCache.CreateDefaultAsync(200);
var extensionService = new ExtensionService();
extensionService.LoadAllExtensions();
extensionService.LoadAllHosts();
return new ServiceCollection()
.AddSingleton<IllustrationDownloadTaskFactory>()
.AddSingleton<NovelDownloadTaskFactory>()
@ -115,6 +115,7 @@ public partial class AppViewModel(App app) : IDisposable
public void Dispose()
{
AppServiceProvider?.GetRequiredService<LiteDatabase>().Dispose();
AppServiceProvider?.GetRequiredService<ExtensionService>().Dispose();
AppServiceProvider?.Dispose();
DownloadManager?.Dispose();
MakoClient?.Dispose();

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices.Marshalling;
using System.Runtime.InteropServices;
using Pixeval.AppManagement;
@ -17,7 +18,7 @@ using WinUI3Utilities;
namespace Pixeval.Extensions;
public class ExtensionService
public class ExtensionService : IDisposable
{
public IReadOnlyList<ExtensionsHostModel> ExtensionHosts => _extensionHosts;
@ -25,29 +26,43 @@ public class ExtensionService
public IEnumerable<KeyValuePair<ExtensionsHostModel, IReadOnlyList<IExtension>>> ActivePairs => ExtensionsLookup.Where(t => t.Key.IsActive);
public IReadOnlyList<ExtensionSettingsGroup> SettingsGroups => _settingsGroups;
public IReadOnlyList<IExtension> Extensions => ExtensionsLookup
.Aggregate(new List<IExtension>(), (o, t) => o.Apply(p => p.AddRange(t.Value)));
public IReadOnlyList<IExtension> ActiveExtensions => ActivePairs
.Aggregate(new List<IExtension>(), (o, t) => o.Apply(p => p.AddRange(t.Value)));
public IReadOnlyList<ExtensionSettingsGroup> SettingsGroups => _settingsGroups;
public IEnumerable<IImageTransformerExtension> ActiveImageTransformers => ActiveExtensions.OfType<IImageTransformerExtension>();
public IEnumerable<IImageTransformerExtension> ImageTransformers => ActiveExtensions.OfType<IImageTransformerExtension>();
private readonly List<ExtensionsHostModel> _extensionHosts = [];
private readonly ObservableCollection<ExtensionsHostModel> _extensionHosts = [];
private readonly Dictionary<ExtensionsHostModel, IReadOnlyList<IExtension>> _extensionsLookup = [];
private readonly List<ExtensionSettingsGroup> _settingsGroups = [];
public void LoadAllExtensions()
public void LoadAllHosts()
{
foreach (var dll in AppKnownFolders.Extensions.GetFiles("*.dll"))
foreach (var dll in AppKnownFolders.Extensions.GetFiles("*.dll"))
_ = TryLoadHost(dll);
}
public bool TryLoadHost(string path)
{
try
{
if (LoadExtension(dll) is not { } host)
continue;
if (LoadExtension(path) is not { } host)
return false;
host.Initialize(AppSettings.CurrentCulture.Name, AppKnownFolders.Temp.FullPath);
var model = new ExtensionsHostModel(host);
_extensionHosts.Add(model);
LoadExtensions(model);
return true;
}
catch
{
return false;
}
}
@ -191,4 +206,11 @@ public class ExtensionService
foreach (var imageTransformer in imageTransformers)
imageTransformer.OnExtensionLoaded();
}
public void Dispose()
{
foreach (var extension in Extensions)
extension.OnExtensionUnloaded();
GC.SuppressFinalize(this);
}
}

View File

@ -11,7 +11,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:media="using:Microsoft.Xaml.Interactions.Media"
xmlns:triggers="using:Pixeval.Util.Triggers"
xmlns:winUi="using:FluentIcons.WinUI"
xmlns:windowing="using:Pixeval.Controls.Windowing"
KeyboardAcceleratorPlacementMode="Hidden"
Loaded="IllustrationViewerPage_OnLoaded"
@ -256,27 +255,6 @@
Symbol="AppTitle"
Text="{x:Bind _viewModel.CurrentIllustration.Title, Mode=OneWay}" />
</CommandBar>
<!--<TextBlock
Grid.Column="1"
MaxHeight="45"
Margin="310,0,375,0"
Style="{StaticResource TextBlockStyle}"
Text="{x:Bind _viewModel.AdditionalText, Mode=OneWay}"
TextWrapping="WrapWholeWords"
Visibility="{x:Bind _viewModel.AdditionalTextBlockVisible, Mode=OneWay}" />
<StackPanel
Margin="320,10,375,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Visibility="{x:Bind _viewModel.UpscalerProgressBarVisible, Mode=OneWay}">
<ProgressBar Value="{x:Bind _viewModel.UpscalerProgress, Mode=OneWay}" />
<TextBlock
Margin="0,2,0,0"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind _viewModel.UpscalerProgressText}" />
</StackPanel>-->
<CommandBar Grid.Column="2" HorizontalAlignment="Stretch">
<AppBarElementContainer VerticalContentAlignment="Center">
<TextBlock Style="{StaticResource TextBlockStyle}" Visibility="{x:Bind controls:C.ToVisibility(_viewModel.CurrentIllustration.IsManga), Mode=OneWay}">
@ -286,20 +264,6 @@
</TextBlock>
</AppBarElementContainer>
<AppBarSeparator />
<AppBarButton
x:Name="UpscaleButton"
Click="UpscaleButton_OnTapped"
Icon="{winUi:SymbolIcon Symbol=EyeTracking}"
IsCompact="True"
IsEnabled="False"
LabelPosition="Collapsed" />
<!--<TeachingTip
x:Name="UpscaleTeachingTip"
x:Uid="/EntryViewerPage/UpscaleTeachingTip"
IconSource="{winUi:SymbolIconSource Symbol=CommentNote}"
IsEnabled="False"
IsLightDismissEnabled="True"
Target="{x:Bind UpscaleButton}" />-->
<AppBarSeparator Height="35" />
<AppBarButton
Command="{x:Bind _viewModel.FullScreenCommand}"

View File

@ -73,10 +73,6 @@ public sealed partial class IllustrationViewerPage
// 此处this.XamlRoot为null
_viewModel = HWnd.GetIllustrationViewerPageViewModelFromHandle(parameter);
//_viewModel.CurrentImage.UpscalerMessageChannel.Reader.OnReceive(
// reader => reader == _viewModel.CurrentImage.UpscalerMessageChannel.Reader,
// OnReceiveUpscalerMessage);
_viewModel.DetailedPropertyChanged += (sender, args) =>
{
var vm = sender.To<IllustrationViewerPageViewModel>();
@ -105,10 +101,6 @@ public sealed partial class IllustrationViewerPage
}
Navigate<ImageViewerPage>(IllustrationImageShowcaseFrame, vm.CurrentImage, info);
//vm.CurrentImage.UpscalerMessageChannel.Reader.OnReceive(
// reader => reader == vm.CurrentImage.UpscalerMessageChannel.Reader,
// OnReceiveUpscalerMessage);
};
_viewModel.PropertyChanged += (sender, args) =>
@ -129,31 +121,6 @@ public sealed partial class IllustrationViewerPage
Navigate<ImageViewerPage>(IllustrationImageShowcaseFrame, _viewModel.CurrentImage);
}
//[GeneratedRegex(@"\d+\.\d+%")]
//private static partial Regex UpscalerMessagePercentageRegex();
//private void OnReceiveUpscalerMessage(string message)
//{
//_viewModel.UpscalerProgressBarVisible = UpscalerMessagePercentageRegex().IsMatch(message);
//_viewModel.AdditionalTextBlockVisible = !_viewModel.UpscalerProgressBarVisible;
//if (message == Upscaler.ProcessCompletedMark)
//{
// _viewModel.UpscalerProgressText = string.Empty;
// _viewModel.UpscalerProgress = 0;
// _viewModel.AdditionalText = $"{EntryViewerPageResources.AiUpscaled}";
// return;
//}
//if (UpscalerMessagePercentageRegex().IsMatch(message))
//{
// _viewModel.UpscalerProgressText = message;
// _viewModel.UpscalerProgress = (int) double.Parse(message[..^1]);
// return;
//}
//_viewModel.AdditionalText = message;
//}
private void IllustrationViewerPage_OnLoaded(object sender, RoutedEventArgs e)
{
if (!App.AppViewModel.AppSettings.BrowseOriginalImage)
@ -281,30 +248,6 @@ public sealed partial class IllustrationViewerPage
teachingTip.Target = appBarButton.IsInOverflow ? null : appBarButton;
}
private async void UpscaleButton_OnTapped(object sender, RoutedEventArgs e)
{
//if (!App.AppViewModel.AppSettings.ShowUpscalerTeachingTip)
//{
// _viewModel.CurrentImage.UpscaleCommand.Execute(null);
// return;
//}
//UpscaleTeachingTip.IsOpen = true;
//var dialog = await HWnd.CreateOkCancelAsync(EntryViewerPageResources.AiUpscalerWarningTitle,
// EntryViewerPageResources.AiUpscalerWarningContent,
// EntryViewerPageResources.AiUpscalerWarningOkButtonContent,
// EntryViewerPageResources.AiUpscalerWarningCancelButtonContent);
//if (dialog == ContentDialogResult.Primary)
//{
// _viewModel.CurrentImage.UpscaleCommand.Execute(null);
//}
//if (App.AppViewModel.AppSettings.ShowUpscalerTeachingTip)
//{
// App.AppViewModel.AppSettings.ShowUpscalerTeachingTip = false;
//}
}
public Visibility IsLogoVisible()
{
return WindowFactory.GetWindowForElement(this).HWnd != WindowFactory.RootWindow.HWnd

View File

@ -49,15 +49,6 @@ public partial class IllustrationViewerPageViewModel : DetailedUiObservableObjec
[ObservableProperty]
public partial bool AdditionalTextBlockVisible { get; set; } = true;
[ObservableProperty]
public partial bool UpscalerProgressBarVisible { get; set; }
[ObservableProperty]
public partial int UpscalerProgress { get; set; }
[ObservableProperty]
public partial string? UpscalerProgressText { get; set; }
/// <summary>
///
/// </summary>

View File

@ -43,7 +43,10 @@ using Pixeval.CoreApi.Net.Response;
using Pixeval.Download;
using Pixeval.Util.ComponentModels;
using Microsoft.Extensions.DependencyInjection;
using Pixeval.Extensions;
using Pixeval.Extensions.Common;
using Pixeval.Util.IO.Caching;
using Pixeval.Extensions.Common.Transformers;
namespace Pixeval.Pages.IllustrationViewer;
@ -51,8 +54,6 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
{
private bool _disposed;
private bool _upscaled;
[ObservableProperty]
public partial bool IsMirrored { get; set; }
@ -82,17 +83,10 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
[ObservableProperty]
public partial float Scale { get; set; } = 1;
//[ObservableProperty]
//public partial bool Upscaling { get; set; }
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsFit))]
public partial ZoomableImageMode ShowMode { get; set; }
//private Upscaler? _upscaler;
//public Channel<string> UpscalerMessageChannel { get; } = Channel.CreateUnbounded<string>();
/// <summary>
/// 由于多窗口,可能在加载图片后改变设置,所以此处缓存原图设置
/// </summary>
@ -199,9 +193,12 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
private void AdvancePhase(LoadingPhase phase, double progress = 0)
{
LoadingText = phase is LoadingPhase.DownloadingImage
? LoadingPhaseExtension.GetResource(LoadingPhase.DownloadingImage).Format((int) (LoadingProgress = progress))
: LoadingPhaseExtension.GetResource(phase);
LoadingProgress = progress;
LoadingText = phase switch
{
LoadingPhase.DownloadingImage => LoadingPhaseExtension.GetResource(LoadingPhase.DownloadingImage).Format((int)progress),
_ => LoadingPhaseExtension.GetResource(phase)
};
}
private async Task LoadImage()
@ -230,6 +227,7 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
var ugoiraUrl = metadata?.LargeUrl;
object? source = null;
// 原图动图(一张一张下)
if (ugoiraUrl is not null && _isOriginal)
{
var urls = await IllustrationViewModel.UgoiraOriginalUrlsAsync();
@ -253,12 +251,19 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
}
else
{
// 静图
if (ugoiraUrl is null)
{
ugoiraUrl = IllustrationViewModel.StaticUrl(_isOriginal);
if (await DownloadUrlAsync(ugoiraUrl) is { } s)
{
var newStream = await s.CopyToMemoryStreamAsync(false);
s.Position = 0;
source = (IReadOnlyList<Stream>) [s];
ApplyImageTransformers(newStream);
}
}
// 非原图动图(压缩包)
else
{
source = await DownloadUrlAsync(ugoiraUrl);
@ -290,6 +295,43 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
new Progress<double>(d => AdvancePhase(LoadingPhase.DownloadingImage, startProgress + ratio * d)),
cancellationToken: ImageLoadingCancellationTokenSource.Token);
}
// 传入的s一定是新建的流所以最后都会销毁s
async void ApplyImageTransformers(Stream s)
{
var extensionService = App.AppViewModel.AppServiceProvider.GetRequiredService<ExtensionService>();
var iStream = s.ToIStream();
var isIllustrationOrFirstPageManga = IllustrationViewModel.MangaIndex is -1 or 0;
var index = 1;
var token = ImageLoadingCancellationTokenSource.Token;
foreach (var transformer in extensionService.ActiveImageTransformers)
{
if (token.IsCancellationRequested)
break;
if (isIllustrationOrFirstPageManga)
HWnd.InfoGrowl(ImageViewerPageResources.ApplyingTransformerExtensionsFormatted.Format(index));
// 运行扩展
iStream.Seek(0, SeekOrigin.Begin, out _);
var next = await transformer.TransformAsync(iStream);
if (next is null)
continue;
// 最初的s会在最后一行销毁从别的扩展拿到的iStream无法销毁所以此处直接覆盖之前的流的行为没有问题
iStream = next;
// 下一次循环准备
var newStream = iStream.ToStream();
var memoryStream = await newStream.CopyToMemoryStreamAsync(false);
iStream.Seek(0, SeekOrigin.Begin, out _);
// 显示图片
var last = OriginalStreamsSource;
OriginalStreamsSource = (IReadOnlyList<Stream>)[memoryStream];
if (last is IReadOnlyList<Stream> and [IDisposable disposable])
disposable.Dispose();
++index;
}
if (!token.IsCancellationRequested && isIllustrationOrFirstPageManga)
HWnd.SuccessGrowl(ImageViewerPageResources.AllTransformerExtensionsFinished);
await s.DisposeAsync();
}
}
}
@ -334,18 +376,6 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
HWnd.ShowShareUi();
}
private async void UpscaleCommandOnExecuteRequested(XamlUICommand sender, ExecuteRequestedEventArgs args)
{
//_upscaled = true;
//UpscaleCommand.NotifyCanExecuteChanged();
//_upscaler ??= new Upscaler(new(OriginalStream!.Single(),
// App.AppViewModel.AppSettings.UpscalerModel,
// App.AppViewModel.AppSettings.UpscalerScaleRatio,
// App.AppViewModel.AppSettings.UpscalerOutputType));
//var newStream = await _upscaler.UpscaleAsync(UpscalerMessageChannel);
//OriginalStream = [newStream];
}
private void InitializeCommands()
{
PlayGifCommand.CanExecuteRequested += (_, e) => e.CanExecute = IllustrationViewModel.IsUgoira && LoadSuccessfully;
@ -378,9 +408,6 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
SetAsBackgroundCommand.CanExecuteRequested += IsNotUgoiraAndLoadingCompletedCanExecuteRequested;
SetAsBackgroundCommand.ExecuteRequested += SetAsBackgroundCommandOnExecuteRequested;
//UpscaleCommand.CanExecuteRequested += IsUpscaleCommandCanExecutedRequested;
//UpscaleCommand.ExecuteRequested += UpscaleCommandOnExecuteRequested;
}
private void UpdateCommandCanExecute()
@ -393,12 +420,8 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
RotateCounterclockwiseCommand.NotifyCanExecuteChanged();
MirrorCommand.NotifyCanExecuteChanged();
ShareCommand.NotifyCanExecuteChanged();
//UpscaleCommand.NotifyCanExecuteChanged();
}
private void IsUpscaleCommandCanExecutedRequested(XamlUICommand _, CanExecuteRequestedEventArgs args) =>
args.CanExecute = LoadSuccessfully && !IllustrationViewModel.IsUgoira && !_upscaled;
private void LoadingCompletedCanExecuteRequested(XamlUICommand _, CanExecuteRequestedEventArgs args) => args.CanExecute = LoadSuccessfully;
private void IsNotUgoiraAndLoadingCompletedCanExecuteRequested(XamlUICommand sender, CanExecuteRequestedEventArgs args) => args.CanExecute = !IllustrationViewModel.IsUgoira && LoadSuccessfully;
@ -459,5 +482,5 @@ public enum LoadingPhase
DownloadingImage,
[LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.LoadingImage))]
LoadingImage
LoadingImage,
}

View File

@ -173,6 +173,7 @@
</NavigationView.MenuItems>
<NavigationView.FooterMenuItems>
<NavigationViewItem
x:Name="ExtensionsTab"
x:Uid="/MainPage/ExtensionsTab"
Icon="{controls:BitmapImageIcon Uri=ms-appx:///Assets/Images/Icons/extensions.png}"
Tag="{x:Bind _viewModel.ExtensionsTag}" />

View File

@ -132,7 +132,7 @@ public sealed partial class MainPage
return;
}
if (Equals(selectedItem, DownloadListTab) || Equals(selectedItem, SettingsTab) || Equals(selectedItem, TagsTab))
if (Equals(selectedItem, DownloadListTab) || Equals(selectedItem, SettingsTab) || Equals(selectedItem, TagsTab)|| Equals(selectedItem, ExtensionsTab))
Navigate(MainPageRootFrame, tag);
else
MainPageRootFrame.NavigateTag(tag, new SuppressNavigationTransitionInfo());

View File

@ -1,20 +1,27 @@
<Page
<controls:EnhancedWindowPage
x:Class="Pixeval.Pages.Misc.ExtensionsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:controls="using:Pixeval.Controls"
xmlns:controls1="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:extensions="using:Pixeval.Extensions"
xmlns:fluent="using:FluentIcons.WinUI"
xmlns:local="using:Pixeval.Pages.Misc"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<controls:DockPanel VerticalSpacing="10">
<controls1:DockPanel VerticalSpacing="10">
<TextBlock
x:Uid="/ExtensionsPage/TitleTextBlock"
Margin="20,40,20,20"
controls:DockPanel.Dock="Top"
controls1:DockPanel.Dock="Top"
Style="{StaticResource TitleTextBlockStyle}" />
<CommandBar controls1:DockPanel.Dock="Top" DefaultLabelPosition="Right">
<AppBarButton
x:Uid="/ExtensionsPage/AddExtensionsButton"
Click="AddExtensionsOnClick"
Icon="{fluent:SymbolIcon Symbol=PlugConnectedAdd}" />
</CommandBar>
<ScrollView>
<ItemsRepeater Margin="20,0,20,20" ItemsSource="{x:Bind Models}">
<ItemsRepeater.Layout>
@ -22,8 +29,8 @@
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="extensions:ExtensionsHostModel">
<controls:SettingsCard Description="{x:Bind Description}" HeaderIcon="{fluent:SymbolIcon Symbol=PuzzlePiece}">
<controls:SettingsCard.Header>
<controls1:SettingsCard Description="{x:Bind Description}" HeaderIcon="{fluent:SymbolIcon Symbol=PuzzlePiece}">
<controls1:SettingsCard.Header>
<TextBlock>
<Run FontWeight="Bold" Text="{x:Bind Name}" />
<Run
@ -36,14 +43,14 @@
FontWeight="Bold"
Text="{x:Bind Author}" />
</TextBlock>
</controls:SettingsCard.Header>
<controls:SettingsCard.Content>
</controls1:SettingsCard.Header>
<controls1:SettingsCard.Content>
<ToggleSwitch IsOn="{x:Bind IsActive, Mode=TwoWay}" />
</controls:SettingsCard.Content>
</controls:SettingsCard>
</controls1:SettingsCard.Content>
</controls1:SettingsCard>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</ScrollView>
</controls:DockPanel>
</Page>
</controls1:DockPanel>
</controls:EnhancedWindowPage>

View File

@ -1,29 +1,55 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Pixeval.AppManagement;
using Pixeval.Controls;
using Pixeval.Extensions;
using Pixeval.Util.IO;
using Pixeval.Util.UI;
using Pixeval.Utilities;
namespace Pixeval.Pages.Misc;
public sealed partial class ExtensionsPage : Page
public sealed partial class ExtensionsPage
{
private IReadOnlyList<ExtensionsHostModel> Models =>
App.AppViewModel.AppServiceProvider.GetRequiredService<ExtensionService>().ExtensionHosts;
public ExtensionsPage()
public ExtensionsPage() => InitializeComponent();
private async void AddExtensionsOnClick(object sender, RoutedEventArgs e)
{
InitializeComponent();
var extensionService = App.AppViewModel.AppServiceProvider.GetRequiredService<ExtensionService>();
var files = await HWnd.OpenMultipleDllsOpenPickerAsync();
foreach (var file in files)
{
var fileInfo = new FileInfo(file.Path);
var dllFileName = fileInfo.Name;
var newDllPath = AppKnownFolders.Extensions.CombinePath(dllFileName);
if (File.Exists(newDllPath))
{
HWnd.ErrorGrowl(ExtensionsPageResources.DllFileExistedErrorFormatted.Format(dllFileName));
continue;
}
try
{
fileInfo.CopyTo(newDllPath);
if (extensionService.TryLoadHost(newDllPath))
{
HWnd.SuccessGrowl(ExtensionsPageResources.DllLoadedSuccessfullyFormatted.Format(dllFileName));
continue;
}
}
catch
{
// ignored
}
HWnd.ErrorGrowl(ExtensionsPageResources.DllLoadFailedFormatted.Format(dllFileName));
}
}
}

View File

@ -117,6 +117,18 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AddExtensionsButton.Label" xml:space="preserve">
<value>添加扩展</value>
</data>
<data name="DllFileExistedErrorFormatted" xml:space="preserve">
<value>名称为“{0}”的文件已在扩展文件夹中,将跳过该文件</value>
</data>
<data name="DllLoadedSuccessfullyFormatted" xml:space="preserve">
<value>已加载DLL“{0}”</value>
</data>
<data name="DllLoadFailedFormatted" xml:space="preserve">
<value>加载DLL“{0}”失败</value>
</data>
<data name="TitleTextBlock.Text" xml:space="preserve">
<value>Pixeval 扩展管理器</value>
</data>

View File

@ -117,6 +117,12 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ApplyingTransformerExtensionsFormatted" xml:space="preserve">
<value>正在应用图片转换扩展:第 {0} 个</value>
</data>
<data name="AllTransformerExtensionsFinished" xml:space="preserve">
<value>所有图片转换扩展应用完成</value>
</data>
<data name="CheckingCache" xml:space="preserve">
<value>正在检查缓存...</value>
</data>
@ -132,4 +138,4 @@
<data name="MergingUgoiraFrames" xml:space="preserve">
<value>正在合成动图...</value>
</data>
</root>
</root>

View File

@ -19,6 +19,7 @@
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.IO;
@ -52,6 +53,7 @@ using Size = Windows.Foundation.Size;
using Symbol = FluentIcons.Common.Symbol;
using SymbolIcon = FluentIcons.WinUI.SymbolIcon;
using SymbolIconSource = FluentIcons.WinUI.SymbolIconSource;
using WinRT.Interop;
namespace Pixeval.Util.UI;
@ -242,14 +244,19 @@ public static partial class UiHelper
box.Document.SetText(TextSetOptions.None, "");
}
public static IAsyncOperation<StorageFolder?> OpenFolderPickerAsync(this Window window) => window.PickSingleFolderAsync(PickerLocationId.PicturesLibrary);
public static IAsyncOperation<StorageFile?> OpenFileOpenPickerAsync(this Window window) => window.PickSingleFileAsync(PickerLocationId.PicturesLibrary);
public static IAsyncOperation<StorageFolder?> OpenFolderPickerAsync(this ulong hWnd) => WindowFactory.ForkedWindows[hWnd].PickSingleFolderAsync(PickerLocationId.PicturesLibrary);
public static IAsyncOperation<StorageFile?> OpenFileOpenPickerAsync(this ulong hWnd) => WindowFactory.ForkedWindows[hWnd].PickSingleFileAsync(PickerLocationId.PicturesLibrary);
public static IAsyncOperation<IReadOnlyList<StorageFile>> OpenMultipleDllsOpenPickerAsync(this ulong hWnd)
{
var fileOpenPicker = new FileOpenPicker();
fileOpenPicker.FileTypeFilter.Add(".dll");
fileOpenPicker.SuggestedStartLocation = PickerLocationId.Desktop;
InitializeWithWindow.Initialize(fileOpenPicker, (nint)hWnd);
return fileOpenPicker.PickMultipleFilesAsync();
}
public static async Task<T> AwaitPageTransitionAsync<T>(this Frame root) where T : Page
{
await ThreadingHelper.SpinWaitAsync(() => root.Content is not T { IsLoaded: true });