aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/Entities
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller/Entities')
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs48
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs2
-rw-r--r--MediaBrowser.Controller/Entities/MusicVideo.cs2
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs52
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs2
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs2
6 files changed, 88 insertions, 20 deletions
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 21a726aaec..209feac702 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -1546,15 +1546,27 @@ namespace MediaBrowser.Controller.Entities
var extras = LibraryManager.FindExtras(item, fileSystemChildren, options.DirectoryService).ToArray();
var newExtraIds = Array.ConvertAll(extras, x => x.Id);
- var currentExtraIds = LibraryManager.GetItemList(new InternalItemsQuery()
+ var currentExtras = LibraryManager.GetItemList(new InternalItemsQuery()
{
OwnerIds = [item.Id]
- }).Select(e => e.Id).ToArray();
+ });
+
+ var currentExtraIds = currentExtras.Select(e => e.Id).ToArray();
var extrasChanged = !currentExtraIds.OrderBy(x => x).SequenceEqual(newExtraIds.OrderBy(x => x));
if (!extrasChanged && !options.ReplaceAllMetadata && options.MetadataRefreshMode != MetadataRefreshMode.FullRefresh)
{
+ // The owner's dates may only have become known after its extras were created, so keep
+ // them in sync even when there is nothing to refresh.
+ foreach (var extra in currentExtras)
+ {
+ if (extra.ExtraType is not null && InheritDatesFromOwner(item, extra))
+ {
+ await extra.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
return false;
}
@@ -1570,6 +1582,7 @@ namespace MediaBrowser.Controller.Entities
i.OwnerId = ownerId;
i.ParentId = Guid.Empty;
+
return RefreshMetadataForOwnedItem(i, true, subOptions, cancellationToken);
});
@@ -2652,6 +2665,32 @@ namespace MediaBrowser.Controller.Entities
}
}
+ /// <summary>
+ /// Applies the owner's premiere date and production year to an owned item, returning whether anything changed.
+ /// </summary>
+ /// <param name="owner">The owner.</param>
+ /// <param name="ownedItem">The owned item.</param>
+ /// <returns><c>true</c> if the owned item was changed, else <c>false</c>.</returns>
+ internal static bool InheritDatesFromOwner(BaseItem owner, BaseItem ownedItem)
+ {
+ // Extras have no release date of their own, so the owner's is authoritative.
+ var changed = false;
+
+ if (owner.ProductionYear is not null && ownedItem.ProductionYear != owner.ProductionYear)
+ {
+ ownedItem.ProductionYear = owner.ProductionYear;
+ changed = true;
+ }
+
+ if (owner.PremiereDate is not null && ownedItem.PremiereDate != owner.PremiereDate)
+ {
+ ownedItem.PremiereDate = owner.PremiereDate;
+ changed = true;
+ }
+
+ return changed;
+ }
+
protected async Task RefreshMetadataForOwnedItem(BaseItem ownedItem, bool copyTitleMetadata, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var newOptions = new MetadataRefreshOptions(options)
@@ -2711,6 +2750,11 @@ namespace MediaBrowser.Controller.Entities
ownedItem.CustomRating = item.CustomRating;
newOptions.ForceSave = true;
}
+
+ if (InheritDatesFromOwner(item, ownedItem))
+ {
+ newOptions.ForceSave = true;
+ }
}
await ownedItem.RefreshMetadata(newOptions, cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index e8817a29cf..8c3ce2ff58 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -90,7 +90,7 @@ namespace MediaBrowser.Controller.Entities.Movies
{
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
- if (!ProductionYear.HasValue)
+ if (ProductionYear is null)
{
var info = LibraryManager.ParseName(Name);
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index 237ad5198c..effbf98820 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -40,7 +40,7 @@ namespace MediaBrowser.Controller.Entities
{
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
- if (!ProductionYear.HasValue)
+ if (ProductionYear is null)
{
var info = LibraryManager.ParseName(Name);
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 952187c6e1..3ce241aca8 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
@@ -82,16 +81,23 @@ namespace MediaBrowser.Controller.Entities.TV
{
var userdatakeys = GetUserDataKeys();
- if (userdatakeys.Count > 1)
+ // The first user data key is a stable cross-folder identity.
+ // When none exists, fall back to the (normalized) series name.
+ var groupingKey = userdatakeys.Count > 1
+ ? userdatakeys[0]
+ : GetNameBasedGroupingKey();
+
+ if (!string.IsNullOrEmpty(groupingKey))
{
- return AddLibrariesToPresentationUniqueKey(userdatakeys[0]);
+ return AppendPreferredLanguage(groupingKey);
}
}
return base.CreatePresentationUniqueKey();
}
- private string AddLibrariesToPresentationUniqueKey(string key)
+ // The owning libraries are deliberately NOT part of the key.
+ private string AppendPreferredLanguage(string key)
{
var lang = GetPreferredMetadataLanguage();
if (!string.IsNullOrEmpty(lang))
@@ -99,16 +105,15 @@ namespace MediaBrowser.Controller.Entities.TV
key += "-" + lang;
}
- var folders = LibraryManager.GetCollectionFolders(this)
- .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
- .ToArray();
-
- if (folders.Length == 0)
- {
- return key;
- }
+ return key;
+ }
- return key + "-" + string.Join('-', folders);
+ private string GetNameBasedGroupingKey()
+ {
+ // Prefix with the type so a series can never collide with a same-named item of another kind.
+ return string.IsNullOrEmpty(Name)
+ ? null
+ : "series-" + Name.ToLowerInvariant();
}
private static string GetUniqueSeriesKey(BaseItem series)
@@ -188,6 +193,25 @@ namespace MediaBrowser.Controller.Entities.TV
return list;
}
+ /// <inheritdoc />
+ protected override Guid[] GetExtraOwnerIds()
+ {
+ if (!LibraryManager.GetLibraryOptions(this).EnableAutomaticSeriesGrouping)
+ {
+ return base.GetExtraOwnerIds();
+ }
+
+ // Setting PresentationUniqueKey on the query disables presentation-key grouping, so this
+ // returns every folder-item of the merged series rather than the collapsed survivor.
+ var ids = LibraryManager.GetItemIds(new InternalItemsQuery
+ {
+ PresentationUniqueKey = GetPresentationUniqueKey(),
+ IncludeItemTypes = [BaseItemKind.Series]
+ });
+
+ return ids.Count == 0 ? base.GetExtraOwnerIds() : ids.ToArray();
+ }
+
public override IReadOnlyList<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
{
return GetSeasons(user, new DtoOptions(true));
@@ -507,7 +531,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
- if (!ProductionYear.HasValue)
+ if (ProductionYear is null)
{
var info = LibraryManager.ParseName(Name);
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index 939709215c..a2465eedf0 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -49,7 +49,7 @@ namespace MediaBrowser.Controller.Entities
{
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
- if (!ProductionYear.HasValue)
+ if (ProductionYear is null)
{
var info = LibraryManager.ParseName(Name);
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index c57ed2faf8..9ba103cc8b 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -730,7 +730,7 @@ namespace MediaBrowser.Controller.Entities
// Apply year filter
if (query.Years.Length > 0)
{
- if (!(item.ProductionYear.HasValue && query.Years.Contains(item.ProductionYear.Value)))
+ if (item.ProductionYear is null || !query.Years.Contains(item.ProductionYear.Value))
{
return false;
}