From dd1d1bb0bb9a74744178eb57d2334cf5c4566e19 Mon Sep 17 00:00:00 2001 From: Czer0xx <1kwr41ka@anonaddy.me> Date: Sun, 20 Jul 2025 11:21:18 +0200 Subject: [PATCH 1/5] Added 'Import all' button in 'Maintenance/Import files', that imports all osu files from specified directory --- osu.Game/Screens/Import/FileImportScreen.cs | 82 ++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 1bdacae87f..677e96fd29 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -3,6 +3,8 @@ #nullable disable +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -11,6 +13,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -29,6 +32,7 @@ namespace osu.Game.Screens.Import private TextFlowContainer currentFileText; private RoundedButton importButton; + private RoundedButton importAllButton; private const float duration = 300; private const float button_height = 50; @@ -105,6 +109,19 @@ namespace osu.Game.Screens.Import Width = 0.9f, Margin = new MarginPadding { Vertical = button_vertical_margin }, Action = () => startImport(fileSelector.CurrentFile.Value?.FullName) + }, + + importAllButton = new RoundedButton + { + Text = "Import all", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = button_height, + Width = 0.9f, + TooltipText = "Imports all osu files from selected directory", + Margin = new MarginPadding { Bottom = button_height + button_vertical_margin * 2 }, + Action = () => startDirectoryImport(fileSelector.CurrentPath.Value?.FullName) } } } @@ -131,10 +148,37 @@ namespace osu.Game.Screens.Import return base.OnExiting(e); } - private void directoryChanged(ValueChangedEvent _) + private void directoryChanged(ValueChangedEvent directoryChangedEvent) { // this should probably be done by the selector itself, but let's do it here for now. fileSelector.CurrentFile.Value = null; + + // enable the "importDirectoryButton" only when there is at least 1 file that matches the extension + importAllButton.Enabled.Value = false; + + if (directoryChangedEvent.NewValue == null) + return; + + DirectoryInfo directoryInfo = directoryChangedEvent.NewValue; + + if (!directoryInfo.Exists) + return; + + try + { + foreach (FileInfo file in directoryInfo.EnumerateFiles()) + { + if (game.HandledExtensions.Contains(file.Extension)) + { + importAllButton.Enabled.Value = true; + break; + } + } + } + catch (Exception ex) + { + Logger.Error(ex, "Could not enumerate files in selected directory!"); + } } private void fileChanged(ValueChangedEvent selectedFile) @@ -160,5 +204,41 @@ namespace osu.Game.Screens.Import }); }, TaskCreationOptions.LongRunning); } + + private void startDirectoryImport(string path) + { + if (string.IsNullOrEmpty(path)) + return; + + List filesToImport = new List(); + + try + { + foreach (string file in Directory.EnumerateFiles(path)) + { + // check if the file ends in a valid extension + if (game.HandledExtensions.Contains(Path.GetExtension(file))) + filesToImport.Add(file); + } + } + catch (Exception ex) + { + Logger.Error(ex, "Could not enumerate files in selected directory!"); + } + + if (filesToImport.Count <= 0) + return; + + Task.Factory.StartNew(async () => + { + await game.Import(filesToImport.ToArray()).ConfigureAwait(false); + + // Refresh the filepicker after importing + Schedule(() => + { + fileSelector.CurrentPath.TriggerChange(); + }); + }, TaskCreationOptions.LongRunning); + } } } From 80539be1ff39786f510a0740ef20727e0767a44b Mon Sep 17 00:00:00 2001 From: Czer0xx <1kwr41ka@anonaddy.me> Date: Tue, 22 Jul 2025 01:10:07 +0200 Subject: [PATCH 2/5] Applied changes from review - Removed the try/catch block. (It was throwing an error only on Linux, when opened a symlink to directory to which user doesn't have access, idk if i should keep it or not.) - Replaced foreach loops with LINQ queries. - Replaced the copy-pasted startImport function with a call to it and changed its parameter type to "params string[]". --- osu.Game/Screens/Import/FileImportScreen.cs | 60 +++++---------------- 1 file changed, 14 insertions(+), 46 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 677e96fd29..b5d983ff75 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -153,9 +154,9 @@ namespace osu.Game.Screens.Import // this should probably be done by the selector itself, but let's do it here for now. fileSelector.CurrentFile.Value = null; - // enable the "importDirectoryButton" only when there is at least 1 file that matches the extension importAllButton.Enabled.Value = false; + // Fixes crashing the game on Linux when clicking on Computer in "file tree" if (directoryChangedEvent.NewValue == null) return; @@ -164,21 +165,10 @@ namespace osu.Game.Screens.Import if (!directoryInfo.Exists) return; - try - { - foreach (FileInfo file in directoryInfo.EnumerateFiles()) - { - if (game.HandledExtensions.Contains(file.Extension)) - { - importAllButton.Enabled.Value = true; - break; - } - } - } - catch (Exception ex) - { - Logger.Error(ex, "Could not enumerate files in selected directory!"); - } + // enable the "importDirectoryButton" only when there is at least 1 file that matches the extension + importAllButton.Enabled.Value = directoryInfo.EnumerateFiles() + .Where(file => game.HandledExtensions.Contains(file.Extension)) + .Any(); } private void fileChanged(ValueChangedEvent selectedFile) @@ -187,14 +177,14 @@ namespace osu.Game.Screens.Import currentFileText.Text = selectedFile.NewValue?.Name ?? "Select a file"; } - private void startImport(string path) + private void startImport(params string[] paths) { - if (string.IsNullOrEmpty(path)) + if (paths.Length == 0) return; Task.Factory.StartNew(async () => { - await game.Import(path).ConfigureAwait(false); + await game.Import(paths).ConfigureAwait(false); // some files will be deleted after successful import, so we want to refresh the view. Schedule(() => @@ -210,35 +200,13 @@ namespace osu.Game.Screens.Import if (string.IsNullOrEmpty(path)) return; - List filesToImport = new List(); - - try - { - foreach (string file in Directory.EnumerateFiles(path)) - { - // check if the file ends in a valid extension - if (game.HandledExtensions.Contains(Path.GetExtension(file))) - filesToImport.Add(file); - } - } - catch (Exception ex) - { - Logger.Error(ex, "Could not enumerate files in selected directory!"); - } - - if (filesToImport.Count <= 0) + // get only files that match extensions handled by the game + IEnumerable filesToImport = Directory.EnumerateFiles(path) + .Where(file => game.HandledExtensions.Contains(Path.GetExtension(file))); + if (!filesToImport.Any()) return; - Task.Factory.StartNew(async () => - { - await game.Import(filesToImport.ToArray()).ConfigureAwait(false); - - // Refresh the filepicker after importing - Schedule(() => - { - fileSelector.CurrentPath.TriggerChange(); - }); - }, TaskCreationOptions.LongRunning); + startImport(filesToImport.ToArray()); } } } From dad68d0f998a4631c102449b549b734900b57814 Mon Sep 17 00:00:00 2001 From: Czer0xx <1kwr41ka@anonaddy.me> Date: Tue, 22 Jul 2025 01:22:58 +0200 Subject: [PATCH 3/5] Removed unused directive, Replaced ".Where().Any()" with single call to Any(). --- osu.Game/Screens/Import/FileImportScreen.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index b5d983ff75..e86d85e4d4 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -3,18 +3,15 @@ #nullable disable -using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -156,7 +153,7 @@ namespace osu.Game.Screens.Import importAllButton.Enabled.Value = false; - // Fixes crashing the game on Linux when clicking on Computer in "file tree" + // Fixes crashing the game on Linux when clicking on "Computer" in the path/navigation bar if (directoryChangedEvent.NewValue == null) return; @@ -167,8 +164,7 @@ namespace osu.Game.Screens.Import // enable the "importDirectoryButton" only when there is at least 1 file that matches the extension importAllButton.Enabled.Value = directoryInfo.EnumerateFiles() - .Where(file => game.HandledExtensions.Contains(file.Extension)) - .Any(); + .Any(file => game.HandledExtensions.Contains(file.Extension)); } private void fileChanged(ValueChangedEvent selectedFile) From 33bb060e27937e6a2623ab2e179bc6cf924a56bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Jul 2025 12:53:46 +0200 Subject: [PATCH 4/5] Improve condition (and surrounding commentary) --- osu.Game/Screens/Import/FileImportScreen.cs | 23 ++++++++------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index e86d85e4d4..5eef3d9ffc 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -151,20 +151,15 @@ namespace osu.Game.Screens.Import // this should probably be done by the selector itself, but let's do it here for now. fileSelector.CurrentFile.Value = null; - importAllButton.Enabled.Value = false; - - // Fixes crashing the game on Linux when clicking on "Computer" in the path/navigation bar - if (directoryChangedEvent.NewValue == null) - return; - - DirectoryInfo directoryInfo = directoryChangedEvent.NewValue; - - if (!directoryInfo.Exists) - return; - - // enable the "importDirectoryButton" only when there is at least 1 file that matches the extension - importAllButton.Enabled.Value = directoryInfo.EnumerateFiles() - .Any(file => game.HandledExtensions.Contains(file.Extension)); + DirectoryInfo newDirectory = directoryChangedEvent.NewValue; + importAllButton.Enabled.Value = + // this will be `null` if the user clicked the "Computer" option (showing drives) + // handling that is difficult due to platform differences, and nobody sane wants that to work with the "import all" button anyway + newDirectory != null + // extra safety against various I/O errors (lack of access, deleted directory, etc.) + && newDirectory.Exists + // there must be at least one file in the current directory for the game to import (non-recursive) + && newDirectory.EnumerateFiles().Any(file => game.HandledExtensions.Contains(file.Extension)); } private void fileChanged(ValueChangedEvent selectedFile) From 4f1929992ff91e1e68707666352bd92591836cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Jul 2025 13:03:39 +0200 Subject: [PATCH 5/5] Fix broken layout & use better copy --- osu.Game/Screens/Import/FileImportScreen.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 5eef3d9ffc..bd35b8131e 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -76,7 +76,7 @@ namespace osu.Game.Screens.Import new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Bottom = button_height + button_vertical_margin * 2 }, + Padding = new MarginPadding { Bottom = button_height * 2 + button_vertical_margin * 3 }, Child = new OsuScrollContainer { RelativeSizeAxes = Axes.Both, @@ -99,26 +99,26 @@ namespace osu.Game.Screens.Import }, importButton = new RoundedButton { - Text = "Import", + Text = "Import selected file", Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.X, Height = button_height, Width = 0.9f, - Margin = new MarginPadding { Vertical = button_vertical_margin }, + Margin = new MarginPadding { Bottom = button_height + button_vertical_margin * 2 }, Action = () => startImport(fileSelector.CurrentFile.Value?.FullName) }, importAllButton = new RoundedButton { - Text = "Import all", + Text = "Import all files from directory", Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.X, Height = button_height, Width = 0.9f, TooltipText = "Imports all osu files from selected directory", - Margin = new MarginPadding { Bottom = button_height + button_vertical_margin * 2 }, + Margin = new MarginPadding { Vertical = button_vertical_margin }, Action = () => startDirectoryImport(fileSelector.CurrentPath.Value?.FullName) } }