mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 20:12:57 +08:00
Populate metadata from ID3 tags when changing beatmap audio track in editor
- Closes https://github.com/ppy/osu/issues/21189 - Supersedes / closes https://github.com/ppy/osu-framework/pull/5627 - Supersedes / closes https://github.com/ppy/osu/pull/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.
This commit is contained in:
parent
fa0d2f4af2
commit
6a6db5a22b
@ -28,33 +28,29 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
public override LocalisableString Title => EditorSetupStrings.MetadataHeader;
|
public override LocalisableString Title => EditorSetupStrings.MetadataHeader;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(SetupScreen setupScreen)
|
||||||
{
|
{
|
||||||
var metadata = Beatmap.Metadata;
|
|
||||||
|
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
ArtistTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Artist,
|
ArtistTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Artist),
|
||||||
!string.IsNullOrEmpty(metadata.ArtistUnicode) ? metadata.ArtistUnicode : metadata.Artist),
|
RomanisedArtistTextBox = createTextBox<FormRomanisedTextBox>(EditorSetupStrings.RomanisedArtist),
|
||||||
RomanisedArtistTextBox = createTextBox<FormRomanisedTextBox>(EditorSetupStrings.RomanisedArtist,
|
TitleTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Title),
|
||||||
!string.IsNullOrEmpty(metadata.Artist) ? metadata.Artist : MetadataUtils.StripNonRomanisedCharacters(metadata.ArtistUnicode)),
|
RomanisedTitleTextBox = createTextBox<FormRomanisedTextBox>(EditorSetupStrings.RomanisedTitle),
|
||||||
TitleTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Title,
|
creatorTextBox = createTextBox<FormTextBox>(EditorSetupStrings.Creator),
|
||||||
!string.IsNullOrEmpty(metadata.TitleUnicode) ? metadata.TitleUnicode : metadata.Title),
|
difficultyTextBox = createTextBox<FormTextBox>(EditorSetupStrings.DifficultyName),
|
||||||
RomanisedTitleTextBox = createTextBox<FormRomanisedTextBox>(EditorSetupStrings.RomanisedTitle,
|
sourceTextBox = createTextBox<FormTextBox>(BeatmapsetsStrings.ShowInfoSource),
|
||||||
!string.IsNullOrEmpty(metadata.Title) ? metadata.Title : MetadataUtils.StripNonRomanisedCharacters(metadata.ArtistUnicode)),
|
tagsTextBox = createTextBox<FormTextBox>(BeatmapsetsStrings.ShowInfoTags)
|
||||||
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)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setupScreen.MetadataChanged += reloadMetadata;
|
||||||
|
reloadMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TTextBox createTextBox<TTextBox>(LocalisableString label, string initialValue)
|
private TTextBox createTextBox<TTextBox>(LocalisableString label)
|
||||||
where TTextBox : FormTextBox, new()
|
where TTextBox : FormTextBox, new()
|
||||||
=> new TTextBox
|
=> new TTextBox
|
||||||
{
|
{
|
||||||
Caption = label,
|
Caption = label,
|
||||||
Current = { Value = initialValue },
|
|
||||||
TabbableContentContainer = this
|
TabbableContentContainer = this
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -94,10 +90,29 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
|
|
||||||
// for now, update on commit rather than making BeatmapMetadata bindables.
|
// 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.
|
// after switching database engines we can reconsider if switching to bindables is a good direction.
|
||||||
updateMetadata();
|
setMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMetadata()
|
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 setMetadata()
|
||||||
{
|
{
|
||||||
Beatmap.Metadata.ArtistUnicode = ArtistTextBox.Current.Value;
|
Beatmap.Metadata.ArtistUnicode = ArtistTextBox.Current.Value;
|
||||||
Beatmap.Metadata.Artist = RomanisedArtistTextBox.Current.Value;
|
Beatmap.Metadata.Artist = RomanisedArtistTextBox.Current.Value;
|
||||||
|
@ -35,6 +35,9 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private Editor? editor { get; set; }
|
private Editor? editor { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private SetupScreen setupScreen { get; set; } = null!;
|
||||||
|
|
||||||
private SetupScreenHeaderBackground headerBackground = null!;
|
private SetupScreenHeaderBackground headerBackground = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -93,15 +96,37 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
if (!source.Exists)
|
if (!source.Exists)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
var tagSource = TagLib.File.Create(source.FullName);
|
||||||
|
|
||||||
changeResource(source, applyToAllDifficulties, @"audio",
|
changeResource(source, applyToAllDifficulties, @"audio",
|
||||||
metadata => metadata.AudioFile,
|
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();
|
music.ReloadCurrentTrack();
|
||||||
|
setupScreen.MetadataChanged?.Invoke();
|
||||||
return true;
|
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 set = working.Value.BeatmapSetInfo;
|
||||||
var beatmap = working.Value.BeatmapInfo;
|
var beatmap = working.Value.BeatmapInfo;
|
||||||
@ -148,10 +173,7 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
{
|
{
|
||||||
foreach (var b in otherBeatmaps)
|
foreach (var b in otherBeatmaps)
|
||||||
{
|
{
|
||||||
// This operation is quite expensive, so only perform it if required.
|
writeMetadata(b.Metadata, newFilename);
|
||||||
if (readFilename(b.Metadata) == newFilename) continue;
|
|
||||||
|
|
||||||
writeFilename(b.Metadata, newFilename);
|
|
||||||
|
|
||||||
// save the difficulty to re-encode the .osu file, updating any reference of the old filename.
|
// save the difficulty to re-encode the .osu file, updating any reference of the old filename.
|
||||||
//
|
//
|
||||||
@ -162,7 +184,7 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
// 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.
|
// for simplicity's sake, trigger a save when changing any resource to ensure the change is correctly saved.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -13,12 +14,15 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Setup
|
namespace osu.Game.Screens.Edit.Setup
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
public partial class SetupScreen : EditorScreen
|
public partial class SetupScreen : EditorScreen
|
||||||
{
|
{
|
||||||
public const float COLUMN_WIDTH = 450;
|
public const float COLUMN_WIDTH = 450;
|
||||||
public const float SPACING = 28;
|
public const float SPACING = 28;
|
||||||
public const float MAX_WIDTH = 2 * COLUMN_WIDTH + SPACING;
|
public const float MAX_WIDTH = 2 * COLUMN_WIDTH + SPACING;
|
||||||
|
|
||||||
|
public Action? MetadataChanged { get; set; }
|
||||||
|
|
||||||
public SetupScreen()
|
public SetupScreen()
|
||||||
: base(EditorScreenMode.SongSetup)
|
: base(EditorScreenMode.SongSetup)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user