diff --git a/MediaBrowser.Model/Entities/MetadataProvider.cs b/MediaBrowser.Model/Entities/MetadataProvider.cs index bd8db99416..fe4e10050a 100644 --- a/MediaBrowser.Model/Entities/MetadataProvider.cs +++ b/MediaBrowser.Model/Entities/MetadataProvider.cs @@ -84,6 +84,11 @@ namespace MediaBrowser.Model.Entities /// /// The TvMaze provider. /// - TvMaze = 19 + TvMaze = 19, + + /// + /// The Jellyfin provider. + /// + Jellyfin = 20 } } diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index b25cfc83f3..d0d6e4de1d 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -92,6 +92,8 @@ namespace MediaBrowser.XbmcMetadata.Savers "musicbrainzreleasegroupid", "tvdbid", "collectionitem", + "jellyfinid", + "uniqueid", "isuserfavorite", "userrating", @@ -296,6 +298,11 @@ namespace MediaBrowser.XbmcMetadata.Savers } } + protected virtual MetadataProvider? GetDefaultProvider() + { + return null; + } + protected abstract void WriteCustomElements(BaseItem item, XmlWriter writer); public static void AddMediaInfo(T item, XmlWriter writer) @@ -439,6 +446,17 @@ namespace MediaBrowser.XbmcMetadata.Savers { var writtenProviderIds = new HashSet(StringComparer.OrdinalIgnoreCase); + var defaultProvider = GetDefaultProvider(); + + if (defaultProvider != null) + { + var defaultProviderId = item.GetProviderId((MetadataProvider)defaultProvider); + if (!string.IsNullOrEmpty(defaultProviderId)) + { + writer.WriteElementString("id", defaultProviderId); + } + } + var overview = (item.Overview ?? string.Empty) .StripHtml() .Replace(""", "'", StringComparison.Ordinal); @@ -544,46 +562,17 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteElementString("aspectratio", hasAspectRatio.AspectRatio); } - var tmdbCollection = item.GetProviderId(MetadataProvider.TmdbCollection); + AddProviderId(writer, item, MetadataProvider.TmdbCollection, writtenProviderIds, "collectionnumber"); - if (!string.IsNullOrEmpty(tmdbCollection)) + var imdbTagName = "imdbid"; + if (item is Series) { - writer.WriteElementString("collectionnumber", tmdbCollection); - writtenProviderIds.Add(MetadataProvider.TmdbCollection.ToString()); + imdbTagName = "imdb_id"; } - var imdb = item.GetProviderId(MetadataProvider.Imdb); - if (!string.IsNullOrEmpty(imdb)) - { - if (item is Series) - { - writer.WriteElementString("imdb_id", imdb); - } - else - { - writer.WriteElementString("imdbid", imdb); - } - - writtenProviderIds.Add(MetadataProvider.Imdb.ToString()); - } - - // Series xml saver already saves this - if (item is not Series) - { - var tvdb = item.GetProviderId(MetadataProvider.Tvdb); - if (!string.IsNullOrEmpty(tvdb)) - { - writer.WriteElementString("tvdbid", tvdb); - writtenProviderIds.Add(MetadataProvider.Tvdb.ToString()); - } - } - - var tmdb = item.GetProviderId(MetadataProvider.Tmdb); - if (!string.IsNullOrEmpty(tmdb)) - { - writer.WriteElementString("tmdbid", tmdb); - writtenProviderIds.Add(MetadataProvider.Tmdb.ToString()); - } + AddProviderIdAndUniqueId(writer, item, MetadataProvider.Imdb, writtenProviderIds, false, imdbTagName); + AddProviderIdAndUniqueId(writer, item, MetadataProvider.Tvdb, writtenProviderIds); + AddProviderIdAndUniqueId(writer, item, MetadataProvider.Tmdb, writtenProviderIds); if (!string.IsNullOrEmpty(item.PreferredMetadataLanguage)) { @@ -687,68 +676,16 @@ namespace MediaBrowser.XbmcMetadata.Savers } } - var externalId = item.GetProviderId(MetadataProvider.AudioDbArtist); - - if (!string.IsNullOrEmpty(externalId)) - { - writer.WriteElementString("audiodbartistid", externalId); - writtenProviderIds.Add(MetadataProvider.AudioDbArtist.ToString()); - } - - externalId = item.GetProviderId(MetadataProvider.AudioDbAlbum); - - if (!string.IsNullOrEmpty(externalId)) - { - writer.WriteElementString("audiodbalbumid", externalId); - writtenProviderIds.Add(MetadataProvider.AudioDbAlbum.ToString()); - } - - externalId = item.GetProviderId(MetadataProvider.Zap2It); - - if (!string.IsNullOrEmpty(externalId)) - { - writer.WriteElementString("zap2itid", externalId); - writtenProviderIds.Add(MetadataProvider.Zap2It.ToString()); - } - - externalId = item.GetProviderId(MetadataProvider.MusicBrainzAlbum); - - if (!string.IsNullOrEmpty(externalId)) - { - writer.WriteElementString("musicbrainzalbumid", externalId); - writtenProviderIds.Add(MetadataProvider.MusicBrainzAlbum.ToString()); - } - - externalId = item.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist); - - if (!string.IsNullOrEmpty(externalId)) - { - writer.WriteElementString("musicbrainzalbumartistid", externalId); - writtenProviderIds.Add(MetadataProvider.MusicBrainzAlbumArtist.ToString()); - } - - externalId = item.GetProviderId(MetadataProvider.MusicBrainzArtist); - - if (!string.IsNullOrEmpty(externalId)) - { - writer.WriteElementString("musicbrainzartistid", externalId); - writtenProviderIds.Add(MetadataProvider.MusicBrainzArtist.ToString()); - } - - externalId = item.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup); + AddProviderId(writer, item, MetadataProvider.AudioDbArtist, writtenProviderIds); + AddProviderId(writer, item, MetadataProvider.AudioDbAlbum, writtenProviderIds); + AddProviderId(writer, item, MetadataProvider.Zap2It, writtenProviderIds); + AddProviderId(writer, item, MetadataProvider.MusicBrainzAlbum, writtenProviderIds); + AddProviderId(writer, item, MetadataProvider.MusicBrainzAlbumArtist, writtenProviderIds); + AddProviderId(writer, item, MetadataProvider.MusicBrainzArtist, writtenProviderIds); + AddProviderId(writer, item, MetadataProvider.MusicBrainzReleaseGroup, writtenProviderIds); + AddProviderId(writer, item, MetadataProvider.TvRage, writtenProviderIds); - if (!string.IsNullOrEmpty(externalId)) - { - writer.WriteElementString("musicbrainzreleasegroupid", externalId); - writtenProviderIds.Add(MetadataProvider.MusicBrainzReleaseGroup.ToString()); - } - - externalId = item.GetProviderId(MetadataProvider.TvRage); - if (!string.IsNullOrEmpty(externalId)) - { - writer.WriteElementString("tvrageid", externalId); - writtenProviderIds.Add(MetadataProvider.TvRage.ToString()); - } + AddProviderIdAndUniqueId(writer, item, MetadataProvider.Jellyfin, writtenProviderIds, true, null, item.Id.ToString("N", CultureInfo.InvariantCulture)); if (item.ProviderIds is not null) { @@ -764,7 +701,10 @@ namespace MediaBrowser.XbmcMetadata.Savers XmlConvert.VerifyName(tagName); Logger.LogDebug("Saving custom provider tagname {0}", tagName); - writer.WriteElementString(GetTagForProviderKey(providerKey), providerId); + writer.WriteElementString(tagName, providerId); + + var typeName = GetUniqueIdTypeForProviderKey(providerKey); + AddUniqueId(writer, item, typeName, providerId, IsDefaultProvider(providerKey)); } catch (ArgumentException) { @@ -793,6 +733,56 @@ namespace MediaBrowser.XbmcMetadata.Savers } } + private void AddProviderIdAndUniqueId(XmlWriter writer, BaseItem item, MetadataProvider provider, HashSet writtenProviderIds, bool isDefault = false, string? tagName = null, string? externalId = null) + { + AddProviderIdImpl(writer, item, provider, writtenProviderIds, true, isDefault, tagName, externalId); + } + + private void AddProviderId(XmlWriter writer, BaseItem item, MetadataProvider provider, HashSet writtenProviderIds, string? tagName = null, string? externalId = null) + { + AddProviderIdImpl(writer, item, provider, writtenProviderIds, false, false, tagName, externalId); + } + + private void AddProviderIdImpl(XmlWriter writer, BaseItem item, MetadataProvider provider, HashSet writtenProviderIds, bool addUniqueId, bool isDefault, string? tagName = null, string? externalId = null) + { + if (string.IsNullOrEmpty(externalId)) + { + externalId = item.GetProviderId(provider); + } + + if (!string.IsNullOrEmpty(externalId)) + { + var providerKey = provider.ToString(); + + if (tagName is null) + { + tagName = GetTagForProviderKey(providerKey); + } + + writer.WriteElementString(tagName, externalId); + writtenProviderIds.Add(providerKey); + + if (addUniqueId) + { + AddUniqueId(writer, item, GetUniqueIdTypeForProviderKey(providerKey), externalId, isDefault); + } + } + } + + private void AddUniqueId(XmlWriter writer, BaseItem item, string providerTypeName, string externalId, bool isDefault) + { + writer.WriteStartElement("uniqueid"); + writer.WriteAttributeString("type", providerTypeName); + + if (isDefault) + { + writer.WriteAttributeString("default", "true"); + } + + writer.WriteString(externalId); + writer.WriteEndElement(); + } + private void AddCollectionItems(Folder item, XmlWriter writer) { var items = item.LinkedChildren @@ -1024,5 +1014,31 @@ namespace MediaBrowser.XbmcMetadata.Savers private string GetTagForProviderKey(string providerKey) => providerKey.ToLowerInvariant() + "id"; + + private string GetUniqueIdTypeForProviderKey(string providerKey) + => providerKey.ToLowerInvariant(); + + private bool IsDefaultProvider(string providerKey) + { + var defaultProvider = GetDefaultProvider(); + if (defaultProvider != null) + { + var defaultProviderKey = defaultProvider.ToString(); + return providerKey.Equals(defaultProviderKey, StringComparison.OrdinalIgnoreCase); + } + + return false; + } + + private bool IsDefaultProvider(MetadataProvider provider) + { + var defaultProvider = GetDefaultProvider(); + if (defaultProvider != null) + { + return provider == defaultProvider; + } + + return false; + } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs index 7ac465205e..de7736d52e 100644 --- a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.XbmcMetadata.Configuration; using Microsoft.Extensions.Logging; @@ -49,6 +50,12 @@ namespace MediaBrowser.XbmcMetadata.Savers public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType) => item.SupportsLocalMetadata && item is Episode && updateType >= MinimumUpdateType; + /// + protected override MetadataProvider? GetDefaultProvider() + { + return MetadataProvider.Tvdb; + } + /// protected override void WriteCustomElements(BaseItem item, XmlWriter writer) { diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs index 8fa22fad94..60e1fc6342 100644 --- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs @@ -91,16 +91,15 @@ namespace MediaBrowser.XbmcMetadata.Savers return false; } + /// + protected override MetadataProvider? GetDefaultProvider() + { + return MetadataProvider.Imdb; + } + /// protected override void WriteCustomElements(BaseItem item, XmlWriter writer) { - var imdb = item.GetProviderId(MetadataProvider.Imdb); - - if (!string.IsNullOrEmpty(imdb)) - { - writer.WriteElementString("id", imdb); - } - if (item is MusicVideo musicVideo) { foreach (var artist in musicVideo.Artists) diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs index 083f22e5d2..6b473b6e4c 100644 --- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs @@ -49,6 +49,12 @@ namespace MediaBrowser.XbmcMetadata.Savers public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType) => item.SupportsLocalMetadata && item is Series && updateType >= MinimumUpdateType; + /// + protected override MetadataProvider? GetDefaultProvider() + { + return MetadataProvider.Tvdb; + } + /// protected override void WriteCustomElements(BaseItem item, XmlWriter writer) { @@ -58,8 +64,6 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(tvdb)) { - writer.WriteElementString("id", tvdb); - writer.WriteStartElement("episodeguide"); var language = item.GetPreferredMetadataLanguage();