Sort embedded collections in Nfo files

Because the Nfo files emit the collections as they are in-memory, the
files are not stable in format, genres, tags, albums, people, etc. are emitted in random orders. Add ordering of the collections when emitting the Nfo files so the file remains stable (unchanged) when underlying media information doesn't change.

In the process of this, it became clear that most of the providers and probes don't trim the strings like people's names, genre names, etc. so did a pass of Trim cleanup too.

Specific ordering: (alphabetical/numeric ascending after trimming blanks and defaulting to zero for missing numbers)

BaseItem: Directors, Writers, Trailers (by Url), Production Locations, Genres, Studios, Tags, Custom Provider Data (by key), Linked Children  (by Path>LibraryItemId), Backdrop Images (by path), Actors (by SortOrder>Name)

AlbumNfo: Artists, Album Artists, Tracks (by ParentIndexNumber>IndexNumber>Name)

ArtistNfo: Albums (by Production Year>SortName>Name)

MovieNfo: Artists

Fix Debug build lint


Fix CI debug build lint issue.


Fix review issues

Fixed debug-build lint issues.
Emits the `disc` number to NFO for tracks with a non-zero ParentIndexNumber and only emit `position` if non-zero.
Removed the exception filtering I put in for testing.

Don't emit actors for MusicAlbums or MusicArtists


Swap from String.Trimmed() to ?.Trim()
Addressing PR feedback

Can't use ReadOnlySpan in an async method

Removed now-unused namespace
This commit is contained in:
Marc Brooks
2023-05-15 00:38:27 -05:00
parent 569a41fc2a
commit 6dc61a430b
17 changed files with 123 additions and 75 deletions

View File

@@ -8,6 +8,7 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
@@ -55,12 +56,12 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var album = (MusicAlbum)item;
foreach (var artist in album.Artists)
foreach (var artist in album.Artists.Trimmed().OrderBy(artist => artist))
{
writer.WriteElementString("artist", artist);
}
foreach (var artist in album.AlbumArtists)
foreach (var artist in album.AlbumArtists.Trimmed().OrderBy(artist => artist))
{
writer.WriteElementString("albumartist", artist);
}
@@ -70,11 +71,19 @@ namespace MediaBrowser.XbmcMetadata.Savers
private void AddTracks(IEnumerable<BaseItem> tracks, XmlWriter writer)
{
foreach (var track in tracks.OrderBy(i => i.ParentIndexNumber ?? 0).ThenBy(i => i.IndexNumber ?? 0))
foreach (var track in tracks
.OrderBy(i => i.ParentIndexNumber ?? 0)
.ThenBy(i => i.IndexNumber ?? 0)
.ThenBy(i => i.Name?.Trim()))
{
writer.WriteStartElement("track");
if (track.IndexNumber.HasValue)
if (track.ParentIndexNumber.HasValue && track.ParentIndexNumber.Value != 0)
{
writer.WriteElementString("disc", track.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture));
}
if (track.IndexNumber.HasValue && track.IndexNumber.Value != 0)
{
writer.WriteElementString("position", track.IndexNumber.Value.ToString(CultureInfo.InvariantCulture));
}

View File

@@ -1,11 +1,13 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.IO;
using MediaBrowser.XbmcMetadata.Configuration;
using Microsoft.Extensions.Logging;
@@ -69,7 +71,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
private void AddAlbums(IList<BaseItem> albums, XmlWriter writer)
{
foreach (var album in albums)
foreach (var album in albums
.OrderBy(album => album.ProductionYear ?? 0)
.ThenBy(album => album.SortName?.Trim())
.ThenBy(album => album.Name?.Trim()))
{
writer.WriteStartElement("album");

View File

@@ -19,6 +19,7 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
@@ -488,7 +489,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
var directors = people
.Where(i => i.IsType(PersonKind.Director))
.Select(i => i.Name)
.Select(i => i.Name?.Trim())
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(i => i)
.ToList();
foreach (var person in directors)
@@ -498,8 +501,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
var writers = people
.Where(i => i.IsType(PersonKind.Writer))
.Select(i => i.Name)
.Select(i => i.Name?.Trim())
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(i => i)
.ToList();
foreach (var person in writers)
@@ -512,7 +516,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("credits", person);
}
foreach (var trailer in item.RemoteTrailers)
foreach (var trailer in item.RemoteTrailers.OrderBy(t => t.Url?.Trim()))
{
writer.WriteElementString("trailer", GetOutputTrailerUrl(trailer.Url));
}
@@ -660,22 +664,22 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("tagline", item.Tagline);
}
foreach (var country in item.ProductionLocations)
foreach (var country in item.ProductionLocations.Trimmed().OrderBy(country => country))
{
writer.WriteElementString("country", country);
}
foreach (var genre in item.Genres)
foreach (var genre in item.Genres.Trimmed().OrderBy(genre => genre))
{
writer.WriteElementString("genre", genre);
}
foreach (var studio in item.Studios)
foreach (var studio in item.Studios.Trimmed().OrderBy(studio => studio))
{
writer.WriteElementString("studio", studio);
}
foreach (var tag in item.Tags)
foreach (var tag in item.Tags.Trimmed().OrderBy(tag => tag))
{
if (item is MusicAlbum || item is MusicArtist)
{
@@ -752,7 +756,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (item.ProviderIds is not null)
{
foreach (var providerKey in item.ProviderIds.Keys)
foreach (var providerKey in item.ProviderIds.Keys.OrderBy(providerKey => providerKey))
{
var providerId = item.ProviderIds[providerKey];
if (!string.IsNullOrEmpty(providerId) && !writtenProviderIds.Contains(providerKey))
@@ -764,7 +768,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
XmlConvert.VerifyName(tagName);
Logger.LogDebug("Saving custom provider tagname {0}", tagName);
writer.WriteElementString(GetTagForProviderKey(providerKey), providerId);
writer.WriteElementString(tagName, providerId);
}
catch (ArgumentException)
{
@@ -785,7 +789,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
AddUserData(item, writer, userManager, userDataRepo, options);
AddActors(people, writer, libraryManager, options.SaveImagePathsInNfo);
if (item is not MusicAlbum && item is not MusicArtist)
{
AddActors(people, writer, libraryManager, options.SaveImagePathsInNfo);
}
if (item is BoxSet folder)
{
@@ -797,6 +804,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var items = item.LinkedChildren
.Where(i => i.Type == LinkedChildType.Manual)
.OrderBy(i => i.Path?.Trim())
.ThenBy(i => i.LibraryItemId?.Trim())
.ToList();
foreach (var link in items)
@@ -839,7 +848,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("poster", GetImagePathToSave(image, libraryManager));
}
foreach (var backdrop in item.GetImages(ImageType.Backdrop))
foreach (var backdrop in item.GetImages(ImageType.Backdrop).OrderBy(b => b.Path?.Trim()))
{
writer.WriteElementString("fanart", GetImagePathToSave(backdrop, libraryManager));
}
@@ -913,7 +922,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
private void AddActors(List<PersonInfo> people, XmlWriter writer, ILibraryManager libraryManager, bool saveImagePath)
{
foreach (var person in people)
foreach (var person in people
.OrderBy(person => person.SortOrder ?? 0)
.ThenBy(person => person.Name?.Trim()))
{
if (person.IsType(PersonKind.Director) || person.IsType(PersonKind.Writer))
{

View File

@@ -8,6 +8,7 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
@@ -100,7 +101,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (item is MusicVideo musicVideo)
{
foreach (var artist in musicVideo.Artists)
foreach (var artist in musicVideo.Artists.Trimmed().OrderBy(artist => artist))
{
writer.WriteElementString("artist", artist);
}