diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index e414792ba0..ced9d8748b 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -9,6 +9,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; @@ -210,6 +211,7 @@ namespace Emby.Server.Implementations.Collections List? itemList = null; var linkedChildrenList = collection.GetLinkedChildren(); + var itemListForRunTimeTicks = collection.GetLinkedChildren(); var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList(); foreach (var id in ids) @@ -226,6 +228,8 @@ namespace Emby.Server.Implementations.Collections (itemList ??= new()).Add(item); linkedChildrenList.Add(item); + + GetItemsForRunTimeTicksCount(item).ToList().ForEach((i) => itemListForRunTimeTicks.Add(i)); } } @@ -243,6 +247,8 @@ namespace Emby.Server.Implementations.Collections collection.LinkedChildren = newChildren; collection.UpdateRatingToItems(linkedChildrenList); + collection.UpdateRunTimeTicksToItems(itemListForRunTimeTicks); + await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); refreshOptions.ForceSave = true; @@ -291,6 +297,30 @@ namespace Emby.Server.Implementations.Collections collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray(); } + var actualItems = new List(); + + foreach (var child in collection.LinkedChildren) + { + if (child.ItemId.HasValue) + { + var item = _libraryManager.GetItemById((Guid)child.ItemId); + + if (item != null) + { + GetItemsForRunTimeTicksCount(item).ToList().ForEach((i) => actualItems.Add(i)); + } + } + } + + if (actualItems.Count > 0) + { + collection.UpdateRunTimeTicksToItems(actualItems); + } + else + { + collection.RunTimeTicks = 0; + } + await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); _providerManager.QueueRefresh( collection.Id, @@ -363,5 +393,41 @@ namespace Emby.Server.Implementations.Collections return results.Values; } + + /// + public IEnumerable GetItemsForRunTimeTicksCount(BaseItem item) + { + var itemsForCount = new List(); + + if (item.GetType() == typeof(Series)) + { + var seasons = _libraryManager.GetItemList( + new InternalItemsQuery() + { + ParentId = item.Id, + SeriesPresentationUniqueKey = item.PresentationUniqueKey + }).OfType(); + foreach (var season in seasons.ToList()) + { + var episodes = _libraryManager.GetItemList( + new InternalItemsQuery() + { + ParentId = season.Id + }).OfType(); + + foreach (var e in episodes) + { + var episode = _libraryManager.GetItemById(e.Id); + itemsForCount.Add(episode); + } + } + } + else + { + itemsForCount.Add(item); + } + + return itemsForCount; + } } } diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json index ff0c3d23df..168aba3d36 100644 --- a/Emby.Server.Implementations/Localization/Core/en-GB.json +++ b/Emby.Server.Implementations/Localization/Core/en-GB.json @@ -108,6 +108,8 @@ "TaskRefreshLibrary": "Scan Media Library", "TaskRefreshChapterImagesDescription": "Creates thumbnails for videos that have chapters.", "TaskRefreshChapterImages": "Extract Chapter Images", + "TaskCollectionDurationDescription": "Calculate the total duration of all collections.", + "TaskCollectionDuration": "Calculate collections duration", "TaskCleanCacheDescription": "Deletes cache files no longer needed by the system.", "TaskCleanCache": "Clean Cache Directory", "TasksChannelsCategory": "Internet Channels", diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json index 4ba31bee03..6240e1acc7 100644 --- a/Emby.Server.Implementations/Localization/Core/en-US.json +++ b/Emby.Server.Implementations/Localization/Core/en-US.json @@ -106,6 +106,8 @@ "TaskCleanCacheDescription": "Deletes cache files no longer needed by the system.", "TaskRefreshChapterImages": "Extract Chapter Images", "TaskRefreshChapterImagesDescription": "Creates thumbnails for videos that have chapters.", + "TaskCollectionDurationDescription": "Calculate the total duration of all collections.", + "TaskCollectionDuration": "Calculate collections duration", "TaskRefreshLibrary": "Scan Media Library", "TaskRefreshLibraryDescription": "Scans your media library for new files and refreshes metadata.", "TaskCleanLogs": "Clean Log Directory", diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 8d8311557e..3ac7ad7739 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -108,6 +108,8 @@ "TaskRefreshLibrary": "Scan Librerie", "TaskRefreshChapterImagesDescription": "Crea le thumbnail per i video che hanno capitoli.", "TaskRefreshChapterImages": "Estrai immagini capitolo", + "TaskCollectionDurationDescription": "Calcola la durata totale di tutte le collezioni.", + "TaskCollectionDuration": "Calcola durata collezioni", "TaskCleanCacheDescription": "Cancella i file di cache non piĆ¹ necessari al sistema.", "TaskCleanCache": "Pulisci la directory della cache", "TasksChannelsCategory": "Canali su Internet", diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CollectionDurationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CollectionDurationTask.cs new file mode 100644 index 0000000000..6ac9a7c665 --- /dev/null +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CollectionDurationTask.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Collections; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Tasks; + +namespace Emby.Server.Implementations.ScheduledTasks.Tasks +{ + /// + /// Class CollectionDurationTask. + /// + public class CollectionDurationTask : IScheduledTask + { + /// + /// The _library manager. + /// + private readonly ILibraryManager _libraryManager; + + private readonly ICollectionManager _collectionManager; + + private readonly ILocalizationManager _localization; + + /// + /// Initializes a new instance of the class. + /// + /// The library manager.. + /// The collection manager.. + /// The localization manager. + public CollectionDurationTask( + ILibraryManager libraryManager, + ICollectionManager collectionManager, + ILocalizationManager localization) + { + _libraryManager = libraryManager; + _collectionManager = collectionManager; + _localization = localization; + } + + /// + public string Name => _localization.GetLocalizedString("TaskCollectionDuration"); + + /// + public string Description => _localization.GetLocalizedString("TaskCollectionDurationDescription"); + + /// + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); + + /// + public string Key => "CollectionDuration"; + + /// + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerDaily, + TimeOfDayTicks = TimeSpan.FromHours(4).Ticks, + MaxRuntimeTicks = TimeSpan.FromHours(5).Ticks + } + }; + } + + /// + public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) + { + var collections = _libraryManager.GetItemList(new InternalItemsQuery + { + IsFolder = true, + Recursive = true, + SourceTypes = new SourceType[] { SourceType.Library }, + }) + .OfType() + .ToList(); + + var numComplete = 0; + + foreach (var collection in collections) + { + cancellationToken.ThrowIfCancellationRequested(); + + try + { + var collectionChildren = new List(); + + foreach (var child in collection.LinkedChildren) + { + if (child.ItemId.HasValue) + { + var item = _libraryManager.GetItemById((Guid)child.ItemId); + + if (item != null) + { + _collectionManager.GetItemsForRunTimeTicksCount(item).ToList().ForEach((i) => collectionChildren.Add(i)); + } + } + } + + collection.UpdateRunTimeTicksToItems(collectionChildren); + + await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(true); + + numComplete++; + double percent = numComplete; + percent /= collections.Count; + + progress.Report(100 * percent); + } + catch (Exception) + { + break; + } + } + } + } +} diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs index 38a78a67b5..900b38d277 100644 --- a/MediaBrowser.Controller/Collections/ICollectionManager.cs +++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs @@ -63,5 +63,12 @@ namespace MediaBrowser.Controller.Collections /// Will create the collection folder on the storage if set to true. /// The folder instance referencing the collection storage. Task GetCollectionsFolder(bool createIfNeeded); + + /// + /// Get Items For RunTimeTicks Count. + /// + /// The item. + /// IList{BaseItem}. + IEnumerable GetItemsForRunTimeTicksCount(BaseItem item); } } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index d7ccfd8aee..8ef47b7982 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -228,5 +228,28 @@ namespace MediaBrowser.Controller.Entities.Movies return new[] { item }; } + + /// + /// Updates the RunTimeTicks based on content and returns true or false indicating if it changed. + /// + /// Media children. + /// true if the total duration in ticks was updated; otherwise false. + public bool UpdateRunTimeTicksToItems(IList children) + { + var currentRunTimeTicks = RunTimeTicks; + + long ticks = 0; + foreach (var item in children) + { + if (item.RunTimeTicks != null && item.RunTimeTicks > 0) + { + ticks = ticks + (long)item.RunTimeTicks; + } + } + + RunTimeTicks = ticks; + + return currentRunTimeTicks != RunTimeTicks; + } } }