Skip to content

Commit

Permalink
Populate metadata from ID3 tags when changing beatmap audio track in …
Browse files Browse the repository at this point in the history
…editor

- Closes ppy#21189
- Supersedes / closes ppy/osu-framework#5627
- Supersedes / closes ppy#22235

The reason why I opted for a complete rewrite rather than a revival of
that aforementioned pull series is that it always felt quite gross to me
to be pulling framework's audio subsystem into the task of reading ID3
tags, and I also partially don't believe that BASS is *good* at reading
ID3 tags. Meanwhile, we already have another library pulled in that is
*explicitly* intended for reading multimedia metadata, and using it
does not require framework changes. (And it was pulled in explicitly for
use in the editor verify tab as well.)

The hard and dumb part of this diff is hacking the gibson such that
the metadata section on setup screen actually *updates itself*
after the resources section is done doing its thing. After significant
gnashing of teeth I just did the bare minimum to make work by caching
a common parent and exposing an `Action?` on it. If anyone has better
ideas, I'm all ears.
  • Loading branch information
bdach authored and TheDark98 committed Jan 14, 2025
1 parent ab5df92 commit 14dfb41
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 26 deletions.
53 changes: 34 additions & 19 deletions osu.Game/Screens/Edit/Setup/MetadataSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,29 @@ public partial class MetadataSection : SetupSection
public override LocalisableString Title => EditorSetupStrings.MetadataHeader;

[BackgroundDependencyLoader]
private void load()
private void load(SetupScreen setupScreen)
{
var metadata = Beatmap.Metadata;

Children = new[]
{
ArtistTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Artist,
!string.IsNullOrEmpty(metadata.ArtistUnicode) ? metadata.ArtistUnicode : metadata.Artist),
RomanisedArtistTextBox = createTextBox<FormRomanisedTextBox>(EditorSetupStrings.RomanisedArtist,
!string.IsNullOrEmpty(metadata.Artist) ? metadata.Artist : MetadataUtils.StripNonRomanisedCharacters(metadata.ArtistUnicode)),
TitleTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Title,
!string.IsNullOrEmpty(metadata.TitleUnicode) ? metadata.TitleUnicode : metadata.Title),
RomanisedTitleTextBox = createTextBox<FormRomanisedTextBox>(EditorSetupStrings.RomanisedTitle,
!string.IsNullOrEmpty(metadata.Title) ? metadata.Title : MetadataUtils.StripNonRomanisedCharacters(metadata.ArtistUnicode)),
creatorTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Creator, metadata.Author.Username),
difficultyTextBox = createTextBox<FormTextBox>(EditorSetupStrings.DifficultyName, Beatmap.BeatmapInfo.DifficultyName),
sourceTextBox = createTextBox<FormTextBox>(BeatmapsetsStrings.ShowInfoSource, metadata.Source),
tagsTextBox = createTextBox<FormTextBox>(BeatmapsetsStrings.ShowInfoTags, metadata.Tags)
ArtistTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Artist),
RomanisedArtistTextBox = createTextBox<FormRomanisedTextBox>(EditorSetupStrings.RomanisedArtist),
TitleTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Title),
RomanisedTitleTextBox = createTextBox<FormRomanisedTextBox>(EditorSetupStrings.RomanisedTitle),
creatorTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Creator),
difficultyTextBox = createTextBox<FormTextBox>(EditorSetupStrings.DifficultyName),
sourceTextBox = createTextBox<FormTextBox>(BeatmapsetsStrings.ShowInfoSource),
tagsTextBox = createTextBox<FormTextBox>(BeatmapsetsStrings.ShowInfoTags)
};

setupScreen.MetadataChanged += reloadMetadata;
reloadMetadata();
}

private TTextBox createTextBox<TTextBox>(LocalisableString label, string initialValue)
private TTextBox createTextBox<TTextBox>(LocalisableString label)
where TTextBox : FormTextBox, new()
=> new TTextBox
{
Caption = label,
Current = { Value = initialValue },
TabbableContentContainer = this
};

Expand Down Expand Up @@ -94,10 +90,29 @@ private void onCommit(TextBox sender, bool newText)

// for now, update on commit rather than making BeatmapMetadata bindables.
// after switching database engines we can reconsider if switching to bindables is a good direction.
updateMetadata();
setMetadata();
}

private void reloadMetadata()
{
var metadata = Beatmap.Metadata;

RomanisedArtistTextBox.ReadOnly = false;
RomanisedTitleTextBox.ReadOnly = false;

ArtistTextBox.Current.Value = !string.IsNullOrEmpty(metadata.ArtistUnicode) ? metadata.ArtistUnicode : metadata.Artist;
RomanisedArtistTextBox.Current.Value = !string.IsNullOrEmpty(metadata.Artist) ? metadata.Artist : MetadataUtils.StripNonRomanisedCharacters(metadata.ArtistUnicode);
TitleTextBox.Current.Value = !string.IsNullOrEmpty(metadata.TitleUnicode) ? metadata.TitleUnicode : metadata.Title;
RomanisedTitleTextBox.Current.Value = !string.IsNullOrEmpty(metadata.Title) ? metadata.Title : MetadataUtils.StripNonRomanisedCharacters(metadata.ArtistUnicode);
creatorTextBox.Current.Value = metadata.Author.Username;
difficultyTextBox.Current.Value = Beatmap.BeatmapInfo.DifficultyName;
sourceTextBox.Current.Value = metadata.Source;
tagsTextBox.Current.Value = metadata.Tags;

updateReadOnlyState();
}

private void updateMetadata()
private void setMetadata()
{
Beatmap.Metadata.ArtistUnicode = ArtistTextBox.Current.Value;
Beatmap.Metadata.Artist = RomanisedArtistTextBox.Current.Value;
Expand Down
36 changes: 29 additions & 7 deletions osu.Game/Screens/Edit/Setup/ResourcesSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public partial class ResourcesSection : SetupSection
[Resolved]
private Editor? editor { get; set; }

[Resolved]
private SetupScreen setupScreen { get; set; } = null!;

private SetupScreenHeaderBackground headerBackground = null!;

[BackgroundDependencyLoader]
Expand Down Expand Up @@ -93,15 +96,37 @@ public bool ChangeAudioTrack(FileInfo source, bool applyToAllDifficulties)
if (!source.Exists)
return false;

var tagSource = TagLib.File.Create(source.FullName);

changeResource(source, applyToAllDifficulties, @"audio",
metadata => metadata.AudioFile,
(metadata, name) => metadata.AudioFile = name);
(metadata, name) =>
{
metadata.AudioFile = name;

string artist = tagSource.Tag.JoinedAlbumArtists;

if (!string.IsNullOrWhiteSpace(artist))
{
metadata.ArtistUnicode = artist;
metadata.Artist = MetadataUtils.StripNonRomanisedCharacters(metadata.ArtistUnicode);
}

string title = tagSource.Tag.Title;

if (!string.IsNullOrEmpty(title))
{
metadata.TitleUnicode = title;
metadata.Title = MetadataUtils.StripNonRomanisedCharacters(metadata.TitleUnicode);
}
});

music.ReloadCurrentTrack();
setupScreen.MetadataChanged?.Invoke();
return true;
}

private void changeResource(FileInfo source, bool applyToAllDifficulties, string baseFilename, Func<BeatmapMetadata, string> readFilename, Action<BeatmapMetadata, string> writeFilename)
private void changeResource(FileInfo source, bool applyToAllDifficulties, string baseFilename, Func<BeatmapMetadata, string> readFilename, Action<BeatmapMetadata, string> writeMetadata)
{
var set = working.Value.BeatmapSetInfo;
var beatmap = working.Value.BeatmapInfo;
Expand Down Expand Up @@ -148,10 +173,7 @@ private void changeResource(FileInfo source, bool applyToAllDifficulties, string
{
foreach (var b in otherBeatmaps)
{
// This operation is quite expensive, so only perform it if required.
if (readFilename(b.Metadata) == newFilename) continue;

writeFilename(b.Metadata, newFilename);
writeMetadata(b.Metadata, newFilename);

// save the difficulty to re-encode the .osu file, updating any reference of the old filename.
//
Expand All @@ -162,7 +184,7 @@ private void changeResource(FileInfo source, bool applyToAllDifficulties, string
}
}

writeFilename(beatmap.Metadata, newFilename);
writeMetadata(beatmap.Metadata, newFilename);

// editor change handler cannot be aware of any file changes or other difficulties having their metadata modified.
// for simplicity's sake, trigger a save when changing any resource to ensure the change is correctly saved.
Expand Down
4 changes: 4 additions & 0 deletions osu.Game/Screens/Edit/Setup/SetupScreen.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
Expand All @@ -13,12 +14,15 @@

namespace osu.Game.Screens.Edit.Setup
{
[Cached]
public partial class SetupScreen : EditorScreen
{
public const float COLUMN_WIDTH = 450;
public const float SPACING = 28;
public const float MAX_WIDTH = 2 * COLUMN_WIDTH + SPACING;

public Action? MetadataChanged { get; set; }

public SetupScreen()
: base(EditorScreenMode.SongSetup)
{
Expand Down

0 comments on commit 14dfb41

Please sign in to comment.