using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Jellyfin.Data.Enums; using Jellyfin.Database.Implementations.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using Microsoft.Extensions.Logging; namespace Jellyfin.Plugin.CinemaTrailers4Jellyfins.Services { public class TrailerIntroProvider : IIntroProvider { private static readonly Random _rng = new(); private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; public string Name => "CinemaTrailers4Jellyfins"; public TrailerIntroProvider(ILibraryManager libraryManager, ILogger logger) { _libraryManager = libraryManager; _logger = logger; } public Task> GetIntros(BaseItem item, User user) { var config = Plugin.Instance?.Configuration; if (config == null || config.TrailersPerMovie <= 0) return Task.FromResult(Enumerable.Empty()); if (item is not Movie) return Task.FromResult(Enumerable.Empty()); var outputFolder = config.DownloadFolder?.TrimEnd( Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); if (string.IsNullOrEmpty(outputFolder)) return Task.FromResult(Enumerable.Empty()); // Break potential recursion: don't inject intros before items that live inside // our output folder. This covers both the fake-movie files and their trailer // extras, so Wholphin can't trigger a getIntros loop on the intro item itself. if (item.Path?.StartsWith(outputFolder, StringComparison.OrdinalIgnoreCase) == true) return Task.FromResult(Enumerable.Empty()); // Server-scoped query (no User parameter) so library visibility settings don't // prevent trailer discovery even when the output library is hidden from users. var fakeMovies = _libraryManager .GetItemList(new InternalItemsQuery { IncludeItemTypes = new[] { BaseItemKind.Movie }, Recursive = true, }) .OfType() .Where(m => m.Path?.StartsWith(outputFolder, StringComparison.OrdinalIgnoreCase) == true && m.LocalTrailers.Count > 0) .ToList(); if (fakeMovies.Count == 0) { _logger.LogDebug( "|CinemaTrailers4Jellyfins| No indexed trailers found in {Folder}. " + "Ensure the output folder is added as a Jellyfin Movies library and scanned.", outputFolder); return Task.FromResult(Enumerable.Empty()); } var selected = fakeMovies .OrderBy(_ => _rng.Next()) .Take(config.TrailersPerMovie) .Select(m => { var t = m.LocalTrailers.ElementAt(_rng.Next(m.LocalTrailers.Count)); return new IntroInfo { ItemId = t.Id, Path = t.Path }; }) .ToList(); return Task.FromResult>(selected); } } }