Skip to content

Commit

Permalink
Performers tagging overhaul resolves #39
Browse files Browse the repository at this point in the history
- Parse all performers returned in the Track.Performers string field by the Qobuz API
- Get Album Artists from Album.Artists list returned by the Aobuz API
- Add Featured Artists to Album Artists
- Add Featured Artists to Track Artists
- Get Composers from Track.Performers field
- Get Producers from Track.Performers field
- Get Label from Album.Label field
- Add Settings for new tags
- Write Album Artists, Track Artists, Composers & Producers (FLAC only) using ID3 / VORBIS COMMENT standards by default
- Add setting to merge above fields into singular fields.
- Add settings to determine initial and final delimiter strings for merging performers.
- When mergine Album & Track Artists, featured artists a added at the end, delimited by "Feat."
  • Loading branch information
DJDoubleD committed Aug 13, 2023
1 parent 33b5f12 commit f88f931
Show file tree
Hide file tree
Showing 7 changed files with 547 additions and 74 deletions.
100 changes: 96 additions & 4 deletions QobuzDownloaderX/Models/Download/DownloadItemInfo.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using QobuzApiSharp.Models.Content;
using QobuzDownloaderX.Shared;
using System.Collections.Generic;
using System.Linq;

namespace QobuzDownloaderX.Models
{
Expand All @@ -9,18 +11,27 @@ public class DownloadItemInfo

// Important strings
public string DowloadItemID { get; set; }

public string Stream { get; set; }

// Info for creating paths
public DownloadItemPaths CurrentDownloadPaths { get; set; }

// Info / Tagging strings
public string TrackVersionName { get; set; }

public bool? Advisory { get; set; }
public string AlbumArtist { get; set; }
public string[] AlbumArtists { get; set; }
public string AlbumName { get; set; }
public string PerformerName { get; set; }
public string[] PerformerNames { get; set; }
public string ComposerName { get; set; }
public string[] ComposerNames { get; set; }
public string ProducerName { get; set; }
public string[] ProducerNames { get; set; }
public string LabelName { get; set; }
public string InvolvedPeople { get; set; }
public string TrackName { get; set; }
public string Copyright { get; set; }
public string Genre { get; set; }
Expand All @@ -34,6 +45,7 @@ public class DownloadItemInfo

// Info / Tagging numbers
public int DiscNumber { get; set; }

public int DiscTotal { get; set; }
public int TrackNumber { get; set; }
public int TrackTotal { get; set; }
Expand All @@ -55,7 +67,9 @@ private void ClearAlbumTaggingInfo()
{
// Clear tag strings
AlbumArtist = null;
AlbumArtists = null;
AlbumName = null;
LabelName = null;
Genre = null;
ReleaseDate = null;
Upc = null;
Expand All @@ -74,7 +88,12 @@ private void ClearTrackTaggingInfo()
{
// Clear tag strings
PerformerName = null;
PerformerNames = null;
ComposerName = null;
ComposerNames = null;
ProducerName = null;
ProducerNames = null;
InvolvedPeople = null;
TrackName = null;
TrackVersionName = null;
Advisory = null;
Expand Down Expand Up @@ -107,11 +126,26 @@ public void SetAlbumTaggingInfo(Album qobuzAlbum)
{
ClearAlbumTaggingInfo();

AlbumArtist = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Artist.Name);
AlbumArtists = GetArtistNames(qobuzAlbum.Artists, InvolvedPersonRoleType.MainArtist);
string[] featuredArtists = GetArtistNames(qobuzAlbum.Artists, InvolvedPersonRoleType.FeaturedArtist);
string albumArtists = MergeFeaturedArtistsWithMainArtists(AlbumArtists, featuredArtists);
// Add Features Artists to Album Artists.
AlbumArtists = AlbumArtists.Concat(featuredArtists).ToArray();
if (!string.IsNullOrEmpty(albumArtists) && Globals.TaggingOptions.MergePerformers)
{
// User Main-Artists by default
AlbumArtist = albumArtists;
}
else
{
AlbumArtist = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Artist.Name);
}

AlbumName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Title.Trim());
string albumVersionName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Version?.Trim());
// Add album version to AlbumName if present
AlbumName += (albumVersionName == null ? "" : " (" + albumVersionName + ")");
LabelName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Label.Name);

Genre = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Genre.Name);
ReleaseDate = StringTools.FormatDateTimeOffset(qobuzAlbum.ReleaseDateStream);
Expand Down Expand Up @@ -141,13 +175,42 @@ public void SetTrackTaggingInfo(Track qobuzTrack)
{
ClearTrackTaggingInfo();

PerformerName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Performer?.Name);
PerformersParser performersParser = new PerformersParser(qobuzTrack.Performers);

PerformerNames = performersParser.GetPerformersWithRole(InvolvedPersonRoleType.MainArtist);
string[] featuredArtists = performersParser.GetPerformersWithRole(InvolvedPersonRoleType.FeaturedArtist);
string trackArtists = MergeFeaturedArtistsWithMainArtists(PerformerNames, featuredArtists);
// Add Features Artists to Album Artists.
PerformerNames = PerformerNames.Concat(featuredArtists).ToArray();
if (!string.IsNullOrEmpty(trackArtists) && Globals.TaggingOptions.MergePerformers)
{
// User MainArtist Performers by default
PerformerName = trackArtists;
}
else
{
PerformerName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Performer?.Name);
}
// If no performer name, use album artist
if (string.IsNullOrEmpty(PerformerName))
{
PerformerName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Album?.Artist?.Name);
}
ComposerName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Composer?.Name);

ComposerNames = performersParser.GetPerformersWithRole(InvolvedPersonRoleType.Composer).ToArray();
string composers = StringTools.MergeDoubleDelimitedList(ComposerNames, Globals.TaggingOptions.PrimaryListSeparator, Globals.TaggingOptions.ListEndSeparator);
if (!string.IsNullOrEmpty(composers) && Globals.TaggingOptions.MergePerformers)
{
ComposerName = composers;
}
else
{
ComposerName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Composer?.Name);
}

ProducerNames = performersParser.GetPerformersWithRole(InvolvedPersonRoleType.Producer).ToArray();
ProducerName = StringTools.MergeDoubleDelimitedList(ProducerNames, Globals.TaggingOptions.PrimaryListSeparator, Globals.TaggingOptions.ListEndSeparator);
InvolvedPeople = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Performers);
TrackName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Title.Trim());
TrackVersionName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Version?.Trim());
// Add track version to TrackName
Expand All @@ -172,5 +235,34 @@ private void SetTrackPaths()
CurrentDownloadPaths.PerformerNamePath = StringTools.TrimToMaxLength(StringTools.GetSafeFilename(PerformerName), Globals.MaxLength);
CurrentDownloadPaths.TrackNamePath = StringTools.GetSafeFilename(TrackName);
}

/// <summary>
/// Get the Artist names with given role as an array
/// </summary>
/// <param name="artists"></param>
/// <param name="role"></param>
/// <returns></returns>
public string[] GetArtistNames(List<Artist> artists, InvolvedPersonRoleType role)
{
return artists.Where(artist => artist.Roles.Exists(roleString => InvolvedPersonRoleMapping.GetRoleByString(roleString) == role))
.Select(artist => artist.Name)
.ToArray();
}

public string MergeFeaturedArtistsWithMainArtists(string[] mainArtists, string[] featuresArtists)
{
string mergedMainArtists = StringTools.MergeDoubleDelimitedList(mainArtists, Globals.TaggingOptions.PrimaryListSeparator, Globals.TaggingOptions.ListEndSeparator);
string mergedFeaturedArtists = StringTools.MergeDoubleDelimitedList(featuresArtists, Globals.TaggingOptions.PrimaryListSeparator, Globals.TaggingOptions.ListEndSeparator);

if (string.IsNullOrEmpty(mergedFeaturedArtists))
{
return mergedMainArtists;
}
else
{
return $"{mergedMainArtists} Feat. {mergedFeaturedArtists}";
}

}
}
}
}
6 changes: 6 additions & 0 deletions QobuzDownloaderX/Shared/TaggingOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ internal class TaggingOptions
public bool WriteExplicitTag { get; set; }
public bool WriteCommentTag { get; set; }
public bool WriteCoverImageTag { get; set; }
public bool WriteProducerTag { get; set; }
public bool WriteLabelTag { get; set; }
public bool WriteInvolvedPeopleTag { get; set; }
public bool MergePerformers { get; set; }
public string CommentTag { get; set; }
public string ArtSize { get; set; }
public string PrimaryListSeparator { get; set; }
public string ListEndSeparator { get; set; }
}
}
123 changes: 108 additions & 15 deletions QobuzDownloaderX/Shared/Tools/AudioFileTagger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,53 @@ public static void AddMetaDataTags(DownloadItemInfo fileInfo, string tagFilePath
// Album Title tag, version is already added to name if available
if (Globals.TaggingOptions.WriteAlbumNameTag) { tfile.Tag.Album = fileInfo.AlbumName; }

// Album Artits tag
if (Globals.TaggingOptions.WriteAlbumArtistTag) { tfile.Tag.AlbumArtists = new string[] { fileInfo.AlbumArtist }; }
// Album Artist tag
if (Globals.TaggingOptions.WriteAlbumArtistTag)
{
if (Globals.TaggingOptions.MergePerformers)
{
tfile.Tag.AlbumArtists = new string[] { fileInfo.AlbumArtist };
}
else
{
tfile.Tag.AlbumArtists = fileInfo.AlbumArtists;
}
}

// Track Artist tag
if (Globals.TaggingOptions.WriteTrackArtistTag) { tfile.Tag.Performers = new string[] { fileInfo.PerformerName }; }
if (Globals.TaggingOptions.WriteTrackArtistTag)
{
if (Globals.TaggingOptions.MergePerformers)
{
tfile.Tag.Performers = new string[] { fileInfo.PerformerName };
}
else
{
tfile.Tag.Performers = fileInfo.PerformerNames;
}
}

// Composer tag
if (Globals.TaggingOptions.WriteComposerTag) { tfile.Tag.Composers = new string[] { fileInfo.ComposerName }; }
if (Globals.TaggingOptions.WriteComposerTag)
{
if (Globals.TaggingOptions.MergePerformers)
{
tfile.Tag.Composers = new string[] { fileInfo.ComposerName };
}
else
{
tfile.Tag.Composers = fileInfo.ComposerNames;
}
}

// Label tag
if (Globals.TaggingOptions.WriteLabelTag) { tfile.Tag.Publisher = fileInfo.LabelName; }

// InvolvedPeople tag
if (Globals.TaggingOptions.WriteInvolvedPeopleTag) { customId3v2.SetTextFrame("TIPL", fileInfo.InvolvedPeople); }

// Release Date tag
if (Globals.TaggingOptions.WriteReleaseYearTag)
// Release Year tag
if (Globals.TaggingOptions.WriteReleaseYearTag)
{
fileInfo.ReleaseDate = fileInfo.ReleaseDate.Substring(0, 4);
tfile.Tag.Year = UInt32.Parse(fileInfo.ReleaseDate);
Expand Down Expand Up @@ -99,7 +135,7 @@ public static void AddMetaDataTags(DownloadItemInfo fileInfo, string tagFilePath
if (Globals.TaggingOptions.WriteCopyrightTag) { tfile.Tag.Copyright = fileInfo.Copyright; }

// ISRC tag
if (Globals.TaggingOptions.WriteIsrcTag) { customId3v2.SetTextFrame("TSRC", fileInfo.Isrc); }
if (Globals.TaggingOptions.WriteIsrcTag) { tfile.Tag.ISRC = fileInfo.Isrc; }

// Release Type tag
if (fileInfo.MediaType != null && Globals.TaggingOptions.WriteMediaTypeTag) { customId3v2.SetTextFrame("TMED", fileInfo.MediaType); }
Expand Down Expand Up @@ -145,16 +181,72 @@ public static void AddMetaDataTags(DownloadItemInfo fileInfo, string tagFilePath
if (Globals.TaggingOptions.WriteAlbumNameTag) { tfile.Tag.Album = fileInfo.AlbumName; }

// Album Artist tag
if (Globals.TaggingOptions.WriteAlbumArtistTag) { custom.SetField("ALBUMARTIST", fileInfo.AlbumArtist); }
if (Globals.TaggingOptions.WriteAlbumArtistTag)
{
if (Globals.TaggingOptions.MergePerformers)
{
tfile.Tag.AlbumArtists = new string[] { fileInfo.AlbumArtist };
}
else
{
tfile.Tag.AlbumArtists = fileInfo.AlbumArtists;
}
}

// Track Artist tag
if (Globals.TaggingOptions.WriteTrackArtistTag) { custom.SetField("ARTIST", fileInfo.PerformerName); }
if (Globals.TaggingOptions.WriteTrackArtistTag)
{
if (Globals.TaggingOptions.MergePerformers)
{
tfile.Tag.Performers = new string[] { fileInfo.PerformerName };
}
else
{
tfile.Tag.Performers = fileInfo.PerformerNames;
}
}

// Composer tag
if (Globals.TaggingOptions.WriteComposerTag) { custom.SetField("COMPOSER", fileInfo.ComposerName); }
if (Globals.TaggingOptions.WriteComposerTag)
{
if (Globals.TaggingOptions.MergePerformers)
{
tfile.Tag.Composers = new string[] { fileInfo.ComposerName };
}
else
{
tfile.Tag.Composers = fileInfo.ComposerNames;
}
}

// Label tag
if (Globals.TaggingOptions.WriteLabelTag) { tfile.Tag.Publisher = fileInfo.LabelName; }

// Producer tag
if (Globals.TaggingOptions.WriteProducerTag)
{
if (Globals.TaggingOptions.MergePerformers)
{
custom.SetField("PRODUCER", fileInfo.ProducerName);
}
else
{
custom.SetField("PRODUCER", fileInfo.ProducerNames);
}
}

// InvolvedPeople tag
if (Globals.TaggingOptions.WriteInvolvedPeopleTag) { custom.SetField("INVOLVEDPEOPLE", fileInfo.InvolvedPeople); }

// Original Release Date tag
if (Globals.TaggingOptions.WriteReleaseYearTag) { custom.SetField("ORIGINALRELEASETIME", fileInfo.ReleaseDate); }

// Release Date tag
if (Globals.TaggingOptions.WriteReleaseYearTag) { custom.SetField("YEAR", fileInfo.ReleaseDate); }
// Release Year tag
if (Globals.TaggingOptions.WriteReleaseYearTag)
{
fileInfo.ReleaseDate = fileInfo.ReleaseDate.Substring(0, 4);
tfile.Tag.Year = UInt32.Parse(fileInfo.ReleaseDate);
}

// Genre tag
if (Globals.TaggingOptions.WriteGenreTag) { tfile.Tag.Genres = new string[] { fileInfo.Genre }; }
Expand All @@ -178,15 +270,16 @@ public static void AddMetaDataTags(DownloadItemInfo fileInfo, string tagFilePath
if (Globals.TaggingOptions.WriteTrackTotalTag) { tfile.Tag.TrackCount = Convert.ToUInt32(fileInfo.TrackTotal); }

// Comment tag
if (Globals.TaggingOptions.WriteCommentTag) { custom.SetField("COMMENT", Globals.TaggingOptions.CommentTag); }
if (Globals.TaggingOptions.WriteCommentTag) { tfile.Tag.Comment = Globals.TaggingOptions.CommentTag; }

// Copyright tag
if (Globals.TaggingOptions.WriteCopyrightTag) { custom.SetField("COPYRIGHT", fileInfo.Copyright); }
if (Globals.TaggingOptions.WriteCopyrightTag) { tfile.Tag.Copyright = fileInfo.Copyright; }

// UPC tag
if (Globals.TaggingOptions.WriteUpcTag) { custom.SetField("UPC", fileInfo.Upc); }

// ISRC tag
if (Globals.TaggingOptions.WriteIsrcTag) { custom.SetField("ISRC", fileInfo.Isrc); }
if (Globals.TaggingOptions.WriteIsrcTag) { tfile.Tag.ISRC = fileInfo.Isrc; }

// Release Type tag
if (fileInfo.MediaType != null && Globals.TaggingOptions.WriteMediaTypeTag)
Expand Down
22 changes: 22 additions & 0 deletions QobuzDownloaderX/Shared/Tools/StringTools.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace QobuzDownloaderX.Shared
Expand Down Expand Up @@ -74,7 +75,28 @@ public static string FormatDurationInSeconds(double durationInSeconds)
return duration.TotalHours < 1 ?
$"{duration:mm\\:ss}" :
$"{duration:hh\\:mm\\:ss}";
}

public static string MergeDoubleDelimitedList(string[] stringList, string initialDelimiter, string finalDelimiter)
{
if (stringList != null)
{
string result;
if (stringList.Length > 1)
{
result = string.Join(initialDelimiter, stringList.Take(stringList.Length - 1)) + finalDelimiter + stringList.LastOrDefault();
}
else
{
result = stringList.FirstOrDefault();
}

return DecodeEncodedNonAsciiCharacters(result);
}
else
{
return "";
}
}
}
}
Loading

0 comments on commit f88f931

Please sign in to comment.