mirror of
https://github.com/muskit/MercuryConverter.git
synced 2026-06-02 20:24:26 -07:00
more export UI code
This commit is contained in:
@@ -28,6 +28,7 @@ public class Song
|
||||
public required float PreviewTime { get; set; }
|
||||
public required float PreviewLen { get; set; }
|
||||
public string SourceName => Consts.NUM_SOURCE[Source];
|
||||
public string FolderName => $"{Artist} - {Name}";
|
||||
|
||||
/// <summary>
|
||||
/// Pairs of level and chart designer.
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.IO;
|
||||
using MercuryConverter.Data;
|
||||
using MercuryConverter.UI.Views;
|
||||
|
||||
namespace MercuryConverter.ExportOperation;
|
||||
|
||||
public class ExportResult
|
||||
{
|
||||
public enum Status
|
||||
{
|
||||
Successful, Failed, WithWarnings
|
||||
}
|
||||
public required Status status;
|
||||
/// <summary>
|
||||
/// Populated when status is Failed or WithWarnings.
|
||||
/// </summary>
|
||||
public string? message;
|
||||
}
|
||||
|
||||
public class Exporter
|
||||
{
|
||||
public static ExportResult Run(string outputPath, Song song)
|
||||
{
|
||||
var exportPath = Path.Combine(outputPath, song.FolderName);
|
||||
Console.WriteLine($"Exporting to {exportPath}...");
|
||||
return new ExportResult { status = ExportResult.Status.Failed, message = "Unimplemented" };
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="Deadpikle.AvaloniaProgressRing" Version="0.10.10" />
|
||||
<PackageReference Include="DialogHost.Avalonia" Version="0.9.3" />
|
||||
<PackageReference Include="ffmpegcore" Version="5.2.0" />
|
||||
<PackageReference Include="ini-parser" Version="2.5.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
+1
-1
@@ -33,5 +33,5 @@ class Program
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.WithInterFont()
|
||||
.LogToTrace(LogEventLevel.Debug);
|
||||
.LogToTrace(LogEventLevel.Warning);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using MercuryConverter.Data;
|
||||
using MercuryConverter.Utility;
|
||||
|
||||
namespace MercuryConverter.UI.Dialogs;
|
||||
|
||||
@@ -30,7 +31,8 @@ public partial class DataScanning : UserControl
|
||||
{
|
||||
if (Settings.I!.DataPath == "" || requiresUser) // no data path saved
|
||||
{
|
||||
var selectedPath = await BeginDirSelection(Settings.I!.DataPath);
|
||||
UISelectMode();
|
||||
var selectedPath = await Utils.BeginDirSelection("Locate Data Folder", Settings.I!.DataPath);
|
||||
if (selectedPath == "") // cancelled opening folder
|
||||
{
|
||||
// TODO:
|
||||
@@ -133,35 +135,6 @@ public partial class DataScanning : UserControl
|
||||
|
||||
}
|
||||
|
||||
private async Task<string> BeginDirSelection(string? startDir = null)
|
||||
{
|
||||
IReadOnlyList<IStorageFolder>? dirSelection = null;
|
||||
|
||||
UISelectMode();
|
||||
|
||||
await Dispatcher.UIThread.Invoke(async () =>
|
||||
{
|
||||
await Task.Delay(250);
|
||||
var tl = TopLevel.GetTopLevel(MainWindow.Instance)!;
|
||||
dirSelection = await tl.StorageProvider.OpenFolderPickerAsync
|
||||
(
|
||||
new FolderPickerOpenOptions
|
||||
{
|
||||
Title = "Locate Data Folder",
|
||||
AllowMultiple = false,
|
||||
SuggestedStartLocation = startDir == null ? null : await tl.StorageProvider.TryGetFolderFromPathAsync(startDir),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (dirSelection!.Count <= 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return dirSelection!.First().TryGetLocalPath()!;
|
||||
}
|
||||
|
||||
private void CloseHandler(object sender, RoutedEventArgs args)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
|
||||
@@ -36,12 +36,12 @@
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<TabControl>
|
||||
<TabControl Name="TabControl">
|
||||
<TabItem Name="TabSelection" Header="selection">
|
||||
<views:Selection />
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="export">
|
||||
<TabItem Name="TabExport" Header="export">
|
||||
<views:Export />
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
|
||||
@@ -8,15 +8,17 @@
|
||||
x:Class="MercuryConverter.UI.Views.Export"
|
||||
|
||||
xmlns:views="clr-namespace:MercuryConverter.UI.Views"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:util="clr-namespace:MercuryConverter.Utility"
|
||||
>
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="12 0 0 3">
|
||||
<StackPanel Name="ExportSelectionPane" DockPanel.Dock="Top" Orientation="Horizontal" Margin="12 0 0 3">
|
||||
<RadioButton GroupName="ExportTarget" Content="Export selected" IsChecked="true" Margin="0 0 24 0"/>
|
||||
<RadioButton GroupName="ExportTarget" Content="Export All"/>
|
||||
<RadioButton Name="RadioExportAll" GroupName="ExportTarget" Content="Export All"/>
|
||||
</StackPanel>
|
||||
|
||||
<Grid RowDefinitions="*, Auto" DockPanel.Dock="Right" Width="200" Margin="12 0 0 0">
|
||||
<StackPanel Grid.Row="0">
|
||||
<StackPanel Name="ExportOptionsPane" Grid.Row="0">
|
||||
<TextBlock Text="Chart Format" FontWeight="Medium" Margin="0 0 0 4"/>
|
||||
<ComboBox Name="ChartFormat" Margin="0 0 0 16" SelectedIndex="2">
|
||||
<ComboBoxItem Content=".sat (V1)"/>
|
||||
@@ -26,46 +28,53 @@
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Text="Audio Format" FontWeight="Medium" Margin="0 0 0 4"/>
|
||||
<StackPanel Margin="10 0 0 36">
|
||||
<StackPanel Margin="10 0 0 42">
|
||||
<RadioButton GroupName="AudioFormat" IsChecked="true" Content="Leave as WAV"/>
|
||||
<RadioButton Name="ShouldAudioConvertRadio" GroupName="AudioFormat">
|
||||
<ComboBox Name="AudioConvertFormat" IsEnabled="false" PlaceholderText="Convert to...">
|
||||
<RadioButton Name="RadioShouldAudioConvert" GroupName="AudioFormat">
|
||||
<ComboBox Name="ListSelectAudioConvertFormat" IsEnabled="false" PlaceholderText="Convert to...">
|
||||
<ComboBoxItem Content="mp3"/>
|
||||
<ComboBoxItem Content="ogg"/>
|
||||
</ComboBox>
|
||||
</RadioButton>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Content="Exclude videos"/>
|
||||
<CheckBox Content="Export to source subfolders"/>
|
||||
<CheckBox Content="Delete original files"/>
|
||||
<CheckBox Name="TickExcludeVideos" Content="Exclude videos"/>
|
||||
<CheckBox Name="TickGroupExports" Content="Group song folders by release version"/>
|
||||
<!-- <CheckBox Content="Delete original files"/> -->
|
||||
|
||||
<TextBlock Margin="0 24 0 4" Text="# Concurrent Operations"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Name="NumThreads" Width="36" HorizontalAlignment="Left"/>
|
||||
<TextBlock VerticalAlignment="Center" Padding="4 0 0 0">
|
||||
<Run Text="/"/>
|
||||
<Run Text="{x:Static util:Utils.CoreCount}"/>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Margin="0 24 0 4" Text="Threads"/>
|
||||
<TextBox Width="36" Text="4" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="1" >
|
||||
<Button HorizontalAlignment="Stretch">
|
||||
<Button Name="BtnExport" HorizontalAlignment="Stretch" Command="{Binding OnExportClick}">
|
||||
<TextBlock Text="Export" TextAlignment="Center"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<UserControl Background="{DynamicResource DataGridContentBackground}">
|
||||
<ScrollViewer Name="TableContainer">
|
||||
<DataGrid Name="ListingTable" IsReadOnly="True" SelectionMode="Extended" IsHitTestVisible="False" ItemsSource="{x:Static views:Selection.Selections}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Status"/>
|
||||
<DataGridTextColumn Header="ID" Width="90" Binding="{Binding Id}"/>
|
||||
<DataGridTextColumn Header="Title" Width="*" Binding="{Binding Name}"/>
|
||||
<DataGridTextColumn Header="Artist" Width="*" Binding="{Binding Artist}"/>
|
||||
</DataGrid.Columns>
|
||||
<DataGrid.Styles>
|
||||
<Style Selector="DataGridCell">
|
||||
<Setter Property="ToolTip.Tip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text}" />
|
||||
</Style>
|
||||
</DataGrid.Styles>
|
||||
</DataGrid>
|
||||
</ScrollViewer>
|
||||
<DataGrid Name="ListingTable" IsReadOnly="True" ItemsSource="{Binding Rows}"
|
||||
CanUserResizeColumns="True" CanUserSortColumns="False"
|
||||
>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Status" Binding="{Binding StatusTxt}"/>
|
||||
<DataGridTextColumn Header="ID" Width="90" Binding="{Binding Song.Id}"/>
|
||||
<DataGridTextColumn Header="Title" Width="*" Binding="{Binding Song.Name}"/>
|
||||
<DataGridTextColumn Header="Artist" Width="*" Binding="{Binding Song.Artist}"/>
|
||||
</DataGrid.Columns>
|
||||
<DataGrid.Styles>
|
||||
<Style Selector="DataGridCell">
|
||||
<Setter Property="ToolTip.Tip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text}" />
|
||||
</Style>
|
||||
</DataGrid.Styles>
|
||||
</DataGrid>
|
||||
</UserControl>
|
||||
</DockPanel>
|
||||
</Panel>
|
||||
@@ -1,25 +1,143 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Threading;
|
||||
using UAssetAPI.PropertyTypes.Structs;
|
||||
using UAssetAPI.UnrealTypes.EngineEnums;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using MercuryConverter.Data;
|
||||
using MercuryConverter.ExportOperation;
|
||||
using MercuryConverter.Utility;
|
||||
|
||||
namespace MercuryConverter.UI.Views;
|
||||
|
||||
public enum ExportStatus
|
||||
{
|
||||
NotStarted, Working, Error, Finished, FinishedWithMessages
|
||||
}
|
||||
|
||||
public partial class ExportRow : ObservableObject
|
||||
{
|
||||
public required Song Song { get; set; }
|
||||
public ExportStatus Status { get; set; } = ExportStatus.NotStarted;
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
public partial class Export : Panel
|
||||
{
|
||||
public ObservableCollection<ExportRow> Rows { get; } = new();
|
||||
|
||||
public Export()
|
||||
{
|
||||
InitializeComponent();
|
||||
ShouldAudioConvertRadio.IsCheckedChanged += AudioConvertCheckChanged;
|
||||
DataContext = this;
|
||||
RadioShouldAudioConvert.IsCheckedChanged += OnUIChange;
|
||||
NumThreads.PropertyChanged += OnUIChange;
|
||||
RadioExportAll.IsCheckedChanged += OnExportSelectionChg;
|
||||
ListingTable.PointerPressed += OnClick;
|
||||
|
||||
NumThreads.Text = (Environment.ProcessorCount / 2).ToString();
|
||||
|
||||
ToolTip.SetTip(TickGroupExports,
|
||||
"Group exported songs into subfolders named after the version they released in. For example:\n" +
|
||||
$"\"Exports/{Consts.NUM_SOURCE[5]}/Ado - うっせぇわ\"");
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(100);
|
||||
MainWindow.Instance!.TabControl.SelectionChanged += OnExportSelectionChg;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void AudioConvertCheckChanged(object? sender, RoutedEventArgs args)
|
||||
private void OnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
RadioButton btn = (RadioButton)args.Source!;
|
||||
AudioConvertFormat.IsEnabled = (bool)btn.IsChecked!;
|
||||
ListingTable.SelectedItems.Clear();
|
||||
}
|
||||
|
||||
private void OnUIChange(object? sender, AvaloniaPropertyChangedEventArgs e) => UpdateUIConditions();
|
||||
private void OnUIChange(object? sender, RoutedEventArgs args) => UpdateUIConditions();
|
||||
|
||||
|
||||
private void OnExportSelectionChg(object? sender, RoutedEventArgs args)
|
||||
{
|
||||
UpdateUIConditions();
|
||||
UpdateRows();
|
||||
}
|
||||
|
||||
public void OnExportClick()
|
||||
{
|
||||
Console.WriteLine("export clicked!");
|
||||
Task.Run(ExportFlow);
|
||||
}
|
||||
|
||||
private void UpdateRows()
|
||||
{
|
||||
Console.WriteLine("Updating rows!");
|
||||
Rows.Clear();
|
||||
|
||||
if ((bool)RadioExportAll.IsChecked!)
|
||||
{
|
||||
Console.WriteLine("Adding DB songs to rows...");
|
||||
Database.Songs.ToList().ForEach((s) => Rows.Add(new ExportRow { Song = s }));
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Adding selections to rows...");
|
||||
Selection.Selections.ToList().ForEach((s) => Rows.Add(new ExportRow { Song = s }));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modify UI as needed; determine if we are in an exportable state to enable the button.
|
||||
/// </summary>
|
||||
private void UpdateUIConditions()
|
||||
{
|
||||
ListSelectAudioConvertFormat.IsEnabled = (bool)RadioShouldAudioConvert.IsChecked!;
|
||||
|
||||
Console.WriteLine($"input threads: {NumThreads.Text}");
|
||||
BtnExport.IsEnabled =
|
||||
( // ensure we have selections
|
||||
Selection.Selections.Count > 0
|
||||
|| ((bool)RadioExportAll.IsChecked! && Database.Songs.Count > 0)
|
||||
) &&
|
||||
( // ensure audio format is set
|
||||
!(bool)RadioShouldAudioConvert.IsChecked || ListSelectAudioConvertFormat.SelectedIndex != -1
|
||||
) &&
|
||||
(
|
||||
int.TryParse(NumThreads.Text, out var thr) && thr <= Environment.ProcessorCount
|
||||
);
|
||||
}
|
||||
|
||||
private void UIExportingMode(bool isExporting)
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
BtnExport.IsEnabled = !isExporting;
|
||||
ExportOptionsPane.IsEnabled = !isExporting;
|
||||
MainWindow.Instance!.TabSelection.IsEnabled = !isExporting;
|
||||
ExportSelectionPane.IsEnabled = !isExporting;
|
||||
});
|
||||
}
|
||||
|
||||
private async void ExportFlow()
|
||||
{
|
||||
UIExportingMode(true);
|
||||
|
||||
string path = await Utils.BeginDirSelection("Choose your export path...");
|
||||
Console.WriteLine($"Exporting to {path}");
|
||||
|
||||
int i = 0;
|
||||
var mtx = new Mutex();
|
||||
|
||||
foreach (var r in Rows)
|
||||
{
|
||||
Exporter.Run(path, r.Song);
|
||||
}
|
||||
|
||||
UIExportingMode(false);
|
||||
}
|
||||
}
|
||||
@@ -45,12 +45,14 @@
|
||||
<TextBox Watermark="Search for title, artist, designer..."/>
|
||||
</DockPanel>
|
||||
<UserControl Background="{DynamicResource DataGridContentBackground}">
|
||||
<DataGrid Name="ListingTable" IsReadOnly="True" SelectionMode="Extended" ItemsSource="{x:Static data:Database.Songs}">
|
||||
<DataGrid Name="ListingTable" IsReadOnly="True" SelectionMode="Extended" ItemsSource="{x:Static data:Database.Songs}"
|
||||
CanUserResizeColumns="True"
|
||||
>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="ID" Width="90" Binding="{Binding Id}"/>
|
||||
<DataGridTextColumn Header="Title" Width="*" Binding="{Binding Name}"/>
|
||||
<DataGridTextColumn Header="Artist" Width="*" Binding="{Binding Artist}"/>
|
||||
<DataGridTextColumn Header="Source" Width="150" Binding="{Binding SourceName}" SortMemberPath="Source"/>
|
||||
<DataGridTextColumn Header="Version Released" Width="150" Binding="{Binding SourceName}" SortMemberPath="Source"/>
|
||||
</DataGrid.Columns>
|
||||
<DataGrid.Styles>
|
||||
<Style Selector="DataGridCell">
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using MercuryConverter.UI;
|
||||
|
||||
namespace MercuryConverter.Utility;
|
||||
|
||||
public static class Utils
|
||||
{
|
||||
public static string CoreCount => Environment.ProcessorCount.ToString();
|
||||
public static async Task<string> BeginDirSelection(string title, string? startDir = null)
|
||||
{
|
||||
IReadOnlyList<IStorageFolder>? dirSelection = null;
|
||||
|
||||
await Dispatcher.UIThread.Invoke(async () =>
|
||||
{
|
||||
await Task.Delay(250);
|
||||
var tl = TopLevel.GetTopLevel(MainWindow.Instance)!;
|
||||
dirSelection = await tl.StorageProvider.OpenFolderPickerAsync
|
||||
(
|
||||
new FolderPickerOpenOptions
|
||||
{
|
||||
Title = title,
|
||||
AllowMultiple = false,
|
||||
SuggestedStartLocation = startDir == null ? null : await tl.StorageProvider.TryGetFolderFromPathAsync(startDir),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (dirSelection!.Count <= 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return dirSelection!.First().TryGetLocalPath()!;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user