1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-20 00:20:21 +08:00

Merge pull request #36101 from bdach/guest-difficulty-export

Add explicit menu item for exporting guest difficulties from editor
This commit is contained in:
Dean Herbert
2025-12-24 00:45:19 +09:00
committed by GitHub
Unverified
5 changed files with 76 additions and 22 deletions
+4 -2
View File
@@ -476,9 +476,11 @@ namespace osu.Game.Beatmaps
public Task<ExternalEditOperation<BeatmapSetInfo>> BeginExternalEditing(BeatmapSetInfo model) =>
beatmapImporter.BeginExternalEditing(model);
public Task Export(BeatmapSetInfo beatmap) => beatmapExporter.ExportAsync(beatmap.ToLive(Realm));
public Task Export(BeatmapSetInfo beatmapSet) => beatmapExporter.ExportAsync(beatmapSet.ToLive(Realm));
public Task ExportLegacy(BeatmapSetInfo beatmap) => legacyBeatmapExporter.ExportAsync(beatmap.ToLive(Realm));
public Task ExportLegacy(BeatmapSetInfo beatmapSet) => legacyBeatmapExporter.ExportAsync(beatmapSet.ToLive(Realm));
public Task ExportLegacy(BeatmapInfo beatmap) => legacyBeatmapExporter.ExportAsync(beatmap.ToLive(Realm));
private void updateHashAndMarkDirty(BeatmapSetInfo setInfo)
{
@@ -2,17 +2,22 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.Timing;
using osu.Game.Extensions;
using osu.Game.IO;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning;
using osu.Game.Utils;
using osuTK;
namespace osu.Game.Database
@@ -175,5 +180,54 @@ namespace osu.Game.Database
}
protected override string FileExtension => @".osz";
public Task ExportAsync(Live<BeatmapInfo> beatmap) => Task.Run(() =>
{
string itemFilename = Path.GetFileNameWithoutExtension(beatmap.PerformRead(s => s.File!.Filename.GetValidFilename()));
const string osu_extension = @".osu";
if (itemFilename.Length > MAX_FILENAME_LENGTH - osu_extension.Length)
itemFilename = itemFilename.Remove(MAX_FILENAME_LENGTH - osu_extension.Length);
IEnumerable<string> existingExports = ExportStorage
.GetFiles(string.Empty, $"{itemFilename}*{osu_extension}")
.Concat(ExportStorage.GetDirectories(string.Empty));
string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{osu_extension}");
ProgressNotification notification = new ProgressNotification
{
State = ProgressNotificationState.Active,
Text = $"Exporting {itemFilename}...",
};
PostNotification?.Invoke(notification);
try
{
beatmap.PerformRead(b =>
{
using var exportStream = ExportStorage.CreateFileSafely(filename);
using var inputFile = GetFileContents(b.BeatmapSet!, b.File!);
if (inputFile == null)
throw new InvalidOperationException($"Beatmap file {b.File!.Filename} could not be opened!");
inputFile.CopyTo(exportStream);
});
}
catch
{
notification.State = ProgressNotificationState.Cancelled;
// cleanup if export is failed or canceled.
ExportStorage.Delete(filename);
throw;
}
notification.CompletionText = $"Exported {itemFilename}! Click to view.";
notification.CompletionClickAction = () => ExportStorage.PresentFileExternally(filename);
notification.State = ProgressNotificationState.Completed;
});
}
}
+7 -7
View File
@@ -41,13 +41,13 @@ namespace osu.Game.Database
protected abstract string FileExtension { get; }
protected readonly Storage UserFileStorage;
private readonly Storage exportStorage;
protected readonly Storage ExportStorage;
public Action<Notification>? PostNotification { get; set; }
protected LegacyExporter(Storage storage)
{
exportStorage = (storage as OsuStorage)?.GetExportStorage() ?? storage.GetStorageForDirectory(@"exports");
ExportStorage = (storage as OsuStorage)?.GetExportStorage() ?? storage.GetStorageForDirectory(@"exports");
UserFileStorage = storage.GetStorageForDirectory(@"files");
}
@@ -74,9 +74,9 @@ namespace osu.Game.Database
if (itemFilename.Length > MAX_FILENAME_LENGTH - FileExtension.Length)
itemFilename = itemFilename.Remove(MAX_FILENAME_LENGTH - FileExtension.Length);
IEnumerable<string> existingExports = exportStorage
IEnumerable<string> existingExports = ExportStorage
.GetFiles(string.Empty, $"{itemFilename}*{FileExtension}")
.Concat(exportStorage.GetDirectories(string.Empty));
.Concat(ExportStorage.GetDirectories(string.Empty));
string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
@@ -92,7 +92,7 @@ namespace osu.Game.Database
try
{
using (var stream = exportStorage.CreateFileSafely(filename))
using (var stream = ExportStorage.CreateFileSafely(filename))
{
await ExportToStreamAsync(model, stream, notification, linkedSource.Token).ConfigureAwait(false);
}
@@ -102,12 +102,12 @@ namespace osu.Game.Database
notification.State = ProgressNotificationState.Cancelled;
// cleanup if export is failed or canceled.
exportStorage.Delete(filename);
ExportStorage.Delete(filename);
throw;
}
notification.CompletionText = $"Exported {itemFilename}! Click to view.";
notification.CompletionClickAction = () => exportStorage.PresentFileExternally(filename);
notification.CompletionClickAction = () => ExportStorage.PresentFileExternally(filename);
notification.State = ProgressNotificationState.Completed;
}
+5
View File
@@ -54,6 +54,11 @@ namespace osu.Game.Localisation
/// </summary>
public static LocalisableString ExportForCompatibility => new TranslatableString(getKey(@"export_for_compatibility"), @"For compatibility (.osz)");
/// <summary>
/// "Guest difficulty (.osu)"
/// </summary>
public static LocalisableString ExportGuestDifficulty => new TranslatableString(getKey(@"export_guest_difficulty"), @"Guest difficulty (.osu)");
/// <summary>
/// "Create new difficulty"
/// </summary>
+6 -13
View File
@@ -1329,8 +1329,9 @@ namespace osu.Game.Screens.Edit
{
var exportItems = new List<MenuItem>
{
new EditorMenuItem(EditorStrings.ExportForEditing, MenuItemType.Standard, () => exportBeatmap(false)),
new EditorMenuItem(EditorStrings.ExportForCompatibility, MenuItemType.Standard, () => exportBeatmap(true)),
new EditorMenuItem(EditorStrings.ExportForEditing, MenuItemType.Standard, () => runExport(manager => manager.Export(Beatmap.Value.BeatmapSetInfo))),
new EditorMenuItem(EditorStrings.ExportForCompatibility, MenuItemType.Standard, () => runExport(manager => manager.ExportLegacy(Beatmap.Value.BeatmapSetInfo))),
new EditorMenuItem(EditorStrings.ExportGuestDifficulty, MenuItemType.Standard, () => runExport(manager => manager.ExportLegacy(Beatmap.Value.BeatmapInfo))),
};
return new EditorMenuItem(CommonStrings.Export) { Items = exportItems };
@@ -1396,7 +1397,7 @@ namespace osu.Game.Screens.Edit
void startSubmission() => this.Push(new BeatmapSubmissionScreen());
}
private void exportBeatmap(bool legacy)
private void runExport(Func<BeatmapManager, Task> exportAction)
{
if (HasUnsavedChanges)
{
@@ -1405,20 +1406,12 @@ namespace osu.Game.Screens.Edit
if (!Save())
return Task.CompletedTask;
return runExport();
return exportAction.Invoke(beatmapManager);
})));
}
else
{
attemptAsyncMutationOperation(runExport);
}
Task runExport()
{
if (legacy)
return beatmapManager.ExportLegacy(Beatmap.Value.BeatmapSetInfo);
else
return beatmapManager.Export(Beatmap.Value.BeatmapSetInfo);
attemptAsyncMutationOperation(() => exportAction(beatmapManager));
}
}