diff options
| author | JPVenson <JPVenson@users.noreply.github.com> | 2024-09-07 22:56:51 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-09-07 14:56:51 -0600 |
| commit | 5ceedced1c4a8bac5b5b7a5f2bd0913783bd427b (patch) | |
| tree | e655f4307f550761b855c2acaddb83dd6ecfa11c /Jellyfin.Server.Implementations | |
| parent | fc247dab9243b3de42193c4be351636204dbc2f2 (diff) | |
Feature/media segments plugin api (#12359)
Diffstat (limited to 'Jellyfin.Server.Implementations')
| -rw-r--r-- | Jellyfin.Server.Implementations/MediaSegments/MediaSegmentManager.cs | 102 |
1 files changed, 101 insertions, 1 deletions
diff --git a/Jellyfin.Server.Implementations/MediaSegments/MediaSegmentManager.cs b/Jellyfin.Server.Implementations/MediaSegments/MediaSegmentManager.cs index 7916d15c9..9953c05be 100644 --- a/Jellyfin.Server.Implementations/MediaSegments/MediaSegmentManager.cs +++ b/Jellyfin.Server.Implementations/MediaSegments/MediaSegmentManager.cs @@ -1,14 +1,23 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Globalization; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model; using MediaBrowser.Model.MediaSegments; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Implementations.MediaSegments; @@ -17,15 +26,89 @@ namespace Jellyfin.Server.Implementations.MediaSegments; /// </summary> public class MediaSegmentManager : IMediaSegmentManager { + private readonly ILogger<MediaSegmentManager> _logger; private readonly IDbContextFactory<JellyfinDbContext> _dbProvider; + private readonly IMediaSegmentProvider[] _segmentProviders; + private readonly ILibraryManager _libraryManager; /// <summary> /// Initializes a new instance of the <see cref="MediaSegmentManager"/> class. /// </summary> + /// <param name="logger">Logger.</param> /// <param name="dbProvider">EFCore Database factory.</param> - public MediaSegmentManager(IDbContextFactory<JellyfinDbContext> dbProvider) + /// <param name="segmentProviders">List of all media segment providers.</param> + /// <param name="libraryManager">Library manager.</param> + public MediaSegmentManager( + ILogger<MediaSegmentManager> logger, + IDbContextFactory<JellyfinDbContext> dbProvider, + IEnumerable<IMediaSegmentProvider> segmentProviders, + ILibraryManager libraryManager) { + _logger = logger; _dbProvider = dbProvider; + + _segmentProviders = segmentProviders + .OrderBy(i => i is IHasOrder hasOrder ? hasOrder.Order : 0) + .ToArray(); + _libraryManager = libraryManager; + } + + /// <inheritdoc/> + public async Task RunSegmentPluginProviders(BaseItem baseItem, bool overwrite, CancellationToken cancellationToken) + { + var libraryOptions = _libraryManager.GetLibraryOptions(baseItem); + var providers = _segmentProviders + .Where(e => !libraryOptions.DisabledMediaSegmentProviders.Contains(GetProviderId(e.Name))) + .OrderBy(i => + { + var index = libraryOptions.MediaSegmentProvideOrder.IndexOf(i.Name); + return index == -1 ? int.MaxValue : index; + }) + .ToList(); + + _logger.LogInformation("Start media segment extraction from providers with {CountProviders} enabled", providers.Count); + 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.LogInformation("Skip {MediaPath} as it already contains media segments", baseItem.Path); + return; + } + + _logger.LogInformation("Clear existing Segments for {MediaPath}", baseItem.Path); + + 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 }; + + foreach (var provider in providers) + { + if (!await provider.Supports(baseItem).ConfigureAwait(false)) + { + _logger.LogDebug("Media Segment provider {ProviderName} does not support item with path {Path}", provider.Name, baseItem.Path); + continue; + } + + _logger.LogDebug("Run Media Segment provider {ProviderName}", provider.Name); + try + { + var segments = await provider.GetMediaSegments(requestItem, cancellationToken) + .ConfigureAwait(false); + + _logger.LogInformation("Media Segment provider {ProviderName} found {CountSegments} for {MediaPath}", provider.Name, segments.Count, baseItem.Path); + var providerId = GetProviderId(provider.Name); + foreach (var segment in segments) + { + segment.ItemId = baseItem.Id; + await CreateSegmentAsync(segment, providerId).ConfigureAwait(false); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Provider {ProviderName} failed to extract segments from {MediaPath}", provider.Name, baseItem.Path); + } + } } /// <inheritdoc /> @@ -103,4 +186,21 @@ public class MediaSegmentManager : IMediaSegmentManager { return baseItem.MediaType is Data.Enums.MediaType.Video or Data.Enums.MediaType.Audio; } + + /// <inheritdoc/> + public IEnumerable<(string Name, string Id)> GetSupportedProviders(BaseItem item) + { + if (item is not (Video or Audio)) + { + return []; + } + + return _segmentProviders + .Select(p => (p.Name, GetProviderId(p.Name))); + } + + private string GetProviderId(string name) + => name.ToLowerInvariant() + .GetMD5() + .ToString("N", CultureInfo.InvariantCulture); } |
