diff --git a/src/Data/Song/Song.cs b/src/Data/Song/Song.cs index 028d32b..90b9fde 100644 --- a/src/Data/Song/Song.cs +++ b/src/Data/Song/Song.cs @@ -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}"; /// /// Pairs of level and chart designer. diff --git a/src/ExportOperation/Exporter.cs b/src/ExportOperation/Exporter.cs new file mode 100644 index 0000000..3cd6d8e --- /dev/null +++ b/src/ExportOperation/Exporter.cs @@ -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; + /// + /// Populated when status is Failed or WithWarnings. + /// + 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" }; + } +} \ No newline at end of file diff --git a/src/MercuryConverter.csproj b/src/MercuryConverter.csproj index 065ca8f..1de24e8 100644 --- a/src/MercuryConverter.csproj +++ b/src/MercuryConverter.csproj @@ -22,6 +22,7 @@ + diff --git a/src/Program.cs b/src/Program.cs index c10c255..a359412 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -33,5 +33,5 @@ class Program => AppBuilder.Configure() .UsePlatformDetect() .WithInterFont() - .LogToTrace(LogEventLevel.Debug); + .LogToTrace(LogEventLevel.Warning); } diff --git a/src/UI/Dialogs/DataScanning.axaml.cs b/src/UI/Dialogs/DataScanning.axaml.cs index fc48fd0..f3edcbb 100644 --- a/src/UI/Dialogs/DataScanning.axaml.cs +++ b/src/UI/Dialogs/DataScanning.axaml.cs @@ -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 BeginDirSelection(string? startDir = null) - { - IReadOnlyList? 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(() => diff --git a/src/UI/MainWindow.axaml b/src/UI/MainWindow.axaml index 1da9cc4..f2a5f5e 100644 --- a/src/UI/MainWindow.axaml +++ b/src/UI/MainWindow.axaml @@ -36,12 +36,12 @@ - + - + diff --git a/src/UI/Views/Export/Export.axaml b/src/UI/Views/Export/Export.axaml index 5c7f940..97b98eb 100644 --- a/src/UI/Views/Export/Export.axaml +++ b/src/UI/Views/Export/Export.axaml @@ -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" > - + - + - + @@ -26,46 +28,53 @@ - + - - + + - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/src/UI/Views/Export/Export.axaml.cs b/src/UI/Views/Export/Export.axaml.cs index 95b3034..b9f2fae 100644 --- a/src/UI/Views/Export/Export.axaml.cs +++ b/src/UI/Views/Export/Export.axaml.cs @@ -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 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 })); + } + } + + /// + /// Modify UI as needed; determine if we are in an exportable state to enable the button. + /// + 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); } } \ No newline at end of file diff --git a/src/UI/Views/Selection/Selection.axaml b/src/UI/Views/Selection/Selection.axaml index 696b406..1c70bf6 100644 --- a/src/UI/Views/Selection/Selection.axaml +++ b/src/UI/Views/Selection/Selection.axaml @@ -45,12 +45,14 @@ - + - +