diff options
6 files changed, 49 insertions, 12 deletions
diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index 1984682a42..2dc4f5652b 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -111,5 +111,15 @@ "LyricDownloadFailureFromForItem": "فشل تحميل الكلمات من {0} إلى {1}", "NameExtraBehindTheScenes": "خلف المشاهد", "NameExtraClip": "مقطع", - "NameExtraDeletedScene": "المشهد المحذوف" + "NameExtraDeletedScene": "المشهد المحذوف", + "NameExtraInterview": "مقابلة", + "NameExtraNumbered": "{0} {1}", + "NameExtraSample": "عيّنة", + "NameExtraScene": "مشهد", + "NameExtraShort": "قصير", + "NameExtraThemeSong": "الاغنية السمة", + "NameExtraThemeVideo": "الفيديو السمة", + "NameExtraFeaturette": "فيلم قصير إضافي", + "NameExtraTrailer": "إعلان ترويجي", + "NameExtraUnknown": "إضافي" } diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json index b889a073a2..a25857e510 100644 --- a/Emby.Server.Implementations/Localization/Core/is.json +++ b/Emby.Server.Implementations/Localization/Core/is.json @@ -108,5 +108,18 @@ "Original": "Upprunaleg", "TaskExtractMediaSegmentsDescription": "Sækir myndbúta úr viðbótum þar sem MediaSegment er virkt.", "TaskMoveTrickplayImages": "Flytja geymslustað fyrir Trickplay-myndir", - "TaskMoveTrickplayImagesDescription": "Flytur fyrirliggjandi Trickplay-skrár í samræmi við stillingar safnsins." + "TaskMoveTrickplayImagesDescription": "Flytur fyrirliggjandi Trickplay-skrár í samræmi við stillingar safnsins.", + "NameExtraBehindTheScenes": "Bak við tjöldin", + "NameExtraClip": "Brot", + "NameExtraDeletedScene": "Eydd atriði", + "NameExtraFeaturette": "Stutt heimildarmynd", + "NameExtraInterview": "Viðtal", + "NameExtraNumbered": "{0} {1}", + "NameExtraSample": "Sýnishorn", + "NameExtraScene": "Sena", + "NameExtraShort": "Stuttmynd", + "NameExtraThemeSong": "Þema lag", + "NameExtraThemeVideo": "Þema myndband", + "NameExtraTrailer": "Stikla", + "NameExtraUnknown": "Aukaefni" } diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index fe3b11d1cd..a5351f299f 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -119,5 +119,6 @@ "NameExtraThemeVideo": "Teminis vaizdo įrašas", "NameExtraTrailer": "Anonsas", "NameExtraUnknown": "Papildomas", - "Original": "Originalus" + "Original": "Originalus", + "NameExtraFeaturette": "Trumpametražis filmas" } diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index 2582ed9df0..e81edc82c6 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -223,7 +223,7 @@ namespace Emby.Server.Implementations.Session if (inactive.Count > 0) { - _logger.LogInformation("Sending ForceKeepAlive message to {0} inactive WebSockets.", inactive.Count); + _logger.LogDebug("Sending ForceKeepAlive message to {0} inactive WebSockets.", inactive.Count); } foreach (var webSocket in inactive) diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs index d1af8e3527..ca60085e4d 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs @@ -499,16 +499,21 @@ public sealed partial class BaseItemRepository var inProgress = context.UserData .Where(ud => ud.UserId == userId && ud.PlaybackPositionTicks > 0); - // Folders are resumable when a descendant is in progress, or when they hold both played and - // unplayed descendants (partially watched). Alternate versions keep their own progress, so - // they count towards the in-progress check but not towards the played/unplayed one. + // Series and Seasons are resumable when a descendant is in progress, or when they hold both + // played and unplayed descendants (partially watched). Alternate versions keep their own + // progress, so they count towards the in-progress check but not towards the played/unplayed one. var leafItems = GetAccessFilteredLeafItemsQuery(context, filter.User!); var inProgressLeafItems = GetAccessFilteredLeafItemsQuery(context, filter.User!, includeOwnedItems: true) .Where(e => e.UserData!.Any(ud => ud.UserId == userId && ud.PlaybackPositionTicks > 0)); - var folderResumableFilter = BuildHasDescendantFilter(context, inProgressLeafItems) - .Or(BuildHasDescendantFilter(context, leafItems.Where(e => e.UserData!.Any(ud => ud.UserId == userId && ud.Played))) - .And(BuildHasDescendantFilter(context, leafItems.Where(e => !e.UserData!.Any(ud => ud.UserId == userId && ud.Played))))); + // Every other folder kind is a container rather than one continuous piece of media + var resumableFolderTypes = _resumableFolderKinds + .Select(kind => _itemTypeLookup.BaseItemKindNames.GetValueOrDefault(kind)) + .ToArray(); + var folderIsResumableFilter = IsFolderFilter.And(e => resumableFolderTypes.Contains(e.Type)) + .And(BuildHasDescendantFilter(context, inProgressLeafItems) + .Or(BuildHasDescendantFilter(context, leafItems.Where(e => e.UserData!.Any(ud => ud.UserId == userId && ud.Played))) + .And(BuildHasDescendantFilter(context, leafItems.Where(e => !e.UserData!.Any(ud => ud.UserId == userId && ud.Played)))))); if (isResumable) { @@ -516,7 +521,7 @@ public sealed partial class BaseItemRepository // Match each version on its own progress rather than coalescing onto the primary. var inProgressIds = inProgress.Select(ud => ud.ItemId); - baseQuery = baseQuery.Where(IsFolderFilter.And(folderResumableFilter) + baseQuery = baseQuery.Where(folderIsResumableFilter .Or(IsFolderFilter.Not().And(e => inProgressIds.Contains(e.Id)))); // When several versions of the same item are in progress, keep only the most recently played one, use id as tiebreaker. @@ -543,7 +548,7 @@ public sealed partial class BaseItemRepository var resumableMovieIds = inProgress .Join(context.BaseItems, ud => ud.ItemId, bi => bi.Id, (ud, bi) => bi.PrimaryVersionId ?? bi.Id); - baseQuery = baseQuery.Where(IsFolderFilter.And(folderResumableFilter.Not()) + baseQuery = baseQuery.Where(IsFolderFilter.And(folderIsResumableFilter.Not()) .Or(IsFolderFilter.Not().And(e => !resumableMovieIds.Contains(e.Id)))); } } diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs index 57041276b7..6622fb3aa6 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs @@ -46,6 +46,14 @@ public sealed partial class BaseItemRepository private static readonly IReadOnlyList<ItemValueType> _getStudiosValueTypes = [ItemValueType.Studios]; private static readonly IReadOnlyList<ItemValueType> _getGenreValueTypes = [ItemValueType.Genre]; + // The only folder kinds whose children form a single viewing sequence, so playback progress on a + // child rolls up to them. Every other folder kind is a container that cannot be resumed. + private static readonly BaseItemKind[] _resumableFolderKinds = + [ + BaseItemKind.Series, + BaseItemKind.Season + ]; + /// <summary> /// Initializes a new instance of the <see cref="BaseItemRepository"/> class. /// </summary> |
