diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 57d7398be9..1a98414e96 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -197,6 +197,7 @@ - [benedikt257](https://github.com/benedikt257) - [revam](https://github.com/revam) - [allesmi](https://github.com/allesmi) + - [ThunderClapLP](https://github.com/ThunderClapLP) # Emby Contributors diff --git a/Jellyfin.Server.Implementations/MediaSegments/MediaSegmentManager.cs b/Jellyfin.Server.Implementations/MediaSegments/MediaSegmentManager.cs index 28b6890b08..97c9d79f53 100644 --- a/Jellyfin.Server.Implementations/MediaSegments/MediaSegmentManager.cs +++ b/Jellyfin.Server.Implementations/MediaSegments/MediaSegmentManager.cs @@ -51,7 +51,7 @@ public class MediaSegmentManager : IMediaSegmentManager } /// - public async Task RunSegmentPluginProviders(BaseItem baseItem, LibraryOptions libraryOptions, bool overwrite, CancellationToken cancellationToken) + public async Task RunSegmentPluginProviders(BaseItem baseItem, LibraryOptions libraryOptions, bool forceOverwrite, CancellationToken cancellationToken) { var providers = _segmentProviders .Where(e => !libraryOptions.DisabledMediaSegmentProviders.Contains(GetProviderId(e.Name))) @@ -70,18 +70,13 @@ public class MediaSegmentManager : IMediaSegmentManager using var db = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false); - if (!overwrite && (await db.MediaSegments.AnyAsync(e => e.ItemId.Equals(baseItem.Id), cancellationToken).ConfigureAwait(false))) - { - _logger.LogDebug("Skip {MediaPath} as it already contains media segments", baseItem.Path); - return; - } - _logger.LogDebug("Start media segment extraction for {MediaPath} with {CountProviders} providers enabled", baseItem.Path, providers.Count); - await db.MediaSegments.Where(e => e.ItemId.Equals(baseItem.Id)).ExecuteDeleteAsync(cancellationToken).ConfigureAwait(false); - - // no need to recreate the request object every time. - var requestItem = new MediaSegmentGenerationRequest() { ItemId = baseItem.Id }; + if (forceOverwrite) + { + // delete all existing media segments if forceOverwrite is set. + await db.MediaSegments.Where(e => e.ItemId.Equals(baseItem.Id)).ExecuteDeleteAsync(cancellationToken).ConfigureAwait(false); + } foreach (var provider in providers) { @@ -91,15 +86,56 @@ public class MediaSegmentManager : IMediaSegmentManager continue; } + IQueryable existingSegments; + if (forceOverwrite) + { + existingSegments = Array.Empty().AsQueryable(); + } + else + { + existingSegments = db.MediaSegments.Where(e => e.ItemId.Equals(baseItem.Id) && e.SegmentProviderId == GetProviderId(provider.Name)); + } + + var requestItem = new MediaSegmentGenerationRequest() + { + ItemId = baseItem.Id, + ExistingSegments = existingSegments.Select(e => Map(e)).ToArray() + }; + try { var segments = await provider.GetMediaSegments(requestItem, cancellationToken) .ConfigureAwait(false); - if (segments.Count == 0) + + if (!forceOverwrite) + { + var existingSegmentsList = existingSegments.ToArray(); // Cannot use requestItem's list, as the provider might tamper with its items. + if (segments.Count == requestItem.ExistingSegments.Count && segments.All(e => existingSegmentsList.Any(f => + { + return + e.StartTicks == f.StartTicks && + e.EndTicks == f.EndTicks && + e.Type == f.Type; + }))) + { + _logger.LogDebug("Media Segment provider {ProviderName} did not modify any segments for {MediaPath}", provider.Name, baseItem.Path); + continue; + } + + // delete existing media segments that were re-generated. + await existingSegments.ExecuteDeleteAsync(cancellationToken).ConfigureAwait(false); + } + + if (segments.Count == 0 && !requestItem.ExistingSegments.Any()) { _logger.LogDebug("Media Segment provider {ProviderName} did not find any segments for {MediaPath}", provider.Name, baseItem.Path); continue; } + else if (segments.Count == 0 && requestItem.ExistingSegments.Any()) + { + _logger.LogDebug("Media Segment provider {ProviderName} deleted all segments for {MediaPath}", provider.Name, baseItem.Path); + continue; + } _logger.LogInformation("Media Segment provider {ProviderName} found {CountSegments} for {MediaPath}", provider.Name, segments.Count, baseItem.Path); var providerId = GetProviderId(provider.Name); diff --git a/MediaBrowser.Controller/MediaSegments/IMediaSegmentManager.cs b/MediaBrowser.Controller/MediaSegments/IMediaSegmentManager.cs index 720c607f12..4f13a7eccd 100644 --- a/MediaBrowser.Controller/MediaSegments/IMediaSegmentManager.cs +++ b/MediaBrowser.Controller/MediaSegments/IMediaSegmentManager.cs @@ -20,10 +20,10 @@ public interface IMediaSegmentManager /// /// The Item to evaluate. /// The library options. - /// If set, will remove existing segments and replace it with new ones otherwise will check for existing segments and if found any, stops. + /// If set, will force to remove existing segments and replace it with new ones otherwise will check for existing segments and if found any that should not be deleted, stops. /// The cancellation token. /// A task that indicates the Operation is finished. - Task RunSegmentPluginProviders(BaseItem baseItem, LibraryOptions libraryOptions, bool overwrite, CancellationToken cancellationToken); + Task RunSegmentPluginProviders(BaseItem baseItem, LibraryOptions libraryOptions, bool forceOverwrite, CancellationToken cancellationToken); /// /// Returns if this item supports media segments. diff --git a/MediaBrowser.Model/MediaSegments/MediaSegmentGenerationRequest.cs b/MediaBrowser.Model/MediaSegments/MediaSegmentGenerationRequest.cs index 8c1f44de8c..53d0173750 100644 --- a/MediaBrowser.Model/MediaSegments/MediaSegmentGenerationRequest.cs +++ b/MediaBrowser.Model/MediaSegments/MediaSegmentGenerationRequest.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using Jellyfin.Database.Implementations.Entities; +using MediaBrowser.Model.MediaSegments; namespace MediaBrowser.Model; @@ -11,4 +14,9 @@ public record MediaSegmentGenerationRequest /// Gets the Id to the BaseItem the segments should be extracted from. /// public Guid ItemId { get; init; } + + /// + /// Gets existing media segments generated on an earlier scan by this provider. + /// + public required IReadOnlyList ExistingSegments { get; init; } }