Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e518b79c36 | ||
|
|
6bb23dc9c4 | ||
|
|
5f7a76feb5 | ||
|
|
adfa570569 | ||
|
|
99798b5174 | ||
|
|
c76ddbc352 | ||
|
|
e9d9543941 | ||
|
|
ba56926dc2 | ||
|
|
2ef39853eb |
@@ -3,8 +3,8 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<RootNamespace>Jellyfin.Plugin.CinemaTrailers4Jellyfins</RootNamespace>
|
||||
<AssemblyVersion>1.0.0.5</AssemblyVersion>
|
||||
<FileVersion>1.0.0.5</FileVersion>
|
||||
<AssemblyVersion>1.0.0.9</AssemblyVersion>
|
||||
<FileVersion>1.0.0.9</FileVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
|
||||
@@ -48,6 +48,16 @@ namespace Jellyfin.Plugin.CinemaTrailers4Jellyfins.Services
|
||||
var preRollEnabled = !string.IsNullOrEmpty(config.TrailerPreRollLibraryId);
|
||||
var featurePreRollEnabled = !string.IsNullOrEmpty(config.FeaturePreRollLibraryId);
|
||||
|
||||
_logger.LogInformation(
|
||||
"|CinemaTrailers4Jellyfins| GetIntros called for {Type} '{Name}' (Path={Path}). "
|
||||
+ "trailersEnabled={Trailers} preRollEnabled={PreRoll} featurePreRollEnabled={FeaturePreRoll}",
|
||||
item.GetType().Name,
|
||||
item.Name,
|
||||
item.Path,
|
||||
trailersEnabled,
|
||||
preRollEnabled,
|
||||
featurePreRollEnabled);
|
||||
|
||||
if (!trailersEnabled && !preRollEnabled && !featurePreRollEnabled)
|
||||
return Task.FromResult(Enumerable.Empty<IntroInfo>());
|
||||
|
||||
@@ -65,16 +75,23 @@ namespace Jellyfin.Plugin.CinemaTrailers4Jellyfins.Services
|
||||
|
||||
case Episode episode:
|
||||
if (!config.TrailersForEpisodes)
|
||||
{
|
||||
_logger.LogInformation("|CinemaTrailers4Jellyfins| Skipping: TrailersForEpisodes is disabled.");
|
||||
return Task.FromResult(Enumerable.Empty<IntroInfo>());
|
||||
}
|
||||
|
||||
if (!IsFirstEpisodeOfDay(user.Id))
|
||||
{
|
||||
_logger.LogInformation("|CinemaTrailers4Jellyfins| Skipping: not the first episode of the day for this user.");
|
||||
return Task.FromResult(Enumerable.Empty<IntroInfo>());
|
||||
}
|
||||
|
||||
feature = episode.Series ?? (BaseItem)episode;
|
||||
isEpisode = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
_logger.LogInformation("|CinemaTrailers4Jellyfins| Skipping: item type {Type} is not a Movie or Episode.", item.GetType().Name);
|
||||
return Task.FromResult(Enumerable.Empty<IntroInfo>());
|
||||
}
|
||||
|
||||
@@ -85,13 +102,16 @@ namespace Jellyfin.Plugin.CinemaTrailers4Jellyfins.Services
|
||||
// (covers both the fake-movie files and their trailer extras).
|
||||
if (!string.IsNullOrEmpty(outputFolder)
|
||||
&& item.Path?.StartsWith(outputFolder, StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
_logger.LogInformation("|CinemaTrailers4Jellyfins| Skipping: item path is inside the output folder ({Folder}).", outputFolder);
|
||||
return Task.FromResult(Enumerable.Empty<IntroInfo>());
|
||||
}
|
||||
|
||||
var intros = new List<IntroInfo>();
|
||||
|
||||
if (preRollEnabled)
|
||||
{
|
||||
var preRoll = GetRandomLibraryMovieIntro(config.TrailerPreRollLibraryId, item.Id);
|
||||
var preRoll = GetRandomLibraryMovieIntro("Trailer Pre-Roll", config.TrailerPreRollLibraryId, item.Id, user);
|
||||
if (preRoll != null)
|
||||
intros.Add(preRoll);
|
||||
}
|
||||
@@ -109,6 +129,9 @@ namespace Jellyfin.Plugin.CinemaTrailers4Jellyfins.Services
|
||||
.Where(m =>
|
||||
m.Path?.StartsWith(outputFolder, StringComparison.OrdinalIgnoreCase) == true
|
||||
&& m.LocalTrailers.Count > 0
|
||||
// Skip items the user can't actually play (e.g. library not in their access list) —
|
||||
// some clients hang indefinitely trying to play an inaccessible item.
|
||||
&& m.IsVisibleStandalone(user)
|
||||
// Keep TV show trailers for episodes and movie trailers for movies separate.
|
||||
&& m.Tags.Contains(TrailerTags.TvShow, StringComparer.OrdinalIgnoreCase) == isEpisode)
|
||||
.SelectMany(m => m.LocalTrailers.Select(t => (Movie: m, Trailer: t)))
|
||||
@@ -145,11 +168,16 @@ namespace Jellyfin.Plugin.CinemaTrailers4Jellyfins.Services
|
||||
|
||||
if (featurePreRollEnabled)
|
||||
{
|
||||
var featureRoll = GetRandomLibraryMovieIntro(config.FeaturePreRollLibraryId, item.Id);
|
||||
var featureRoll = GetRandomLibraryMovieIntro("Feature Pre-Roll", config.FeaturePreRollLibraryId, item.Id, user);
|
||||
if (featureRoll != null)
|
||||
intros.Add(featureRoll);
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
"|CinemaTrailers4Jellyfins| Returning {Count} intro(s): {Paths}",
|
||||
intros.Count,
|
||||
string.Join(", ", intros.Select(i => i.Path)));
|
||||
|
||||
return Task.FromResult<IEnumerable<IntroInfo>>(intros);
|
||||
}
|
||||
|
||||
@@ -157,26 +185,47 @@ namespace Jellyfin.Plugin.CinemaTrailers4Jellyfins.Services
|
||||
/// Picks a random Movie from the given Jellyfin library (VirtualFolder ItemId) to use as a
|
||||
/// pre-roll/post-roll bumper, excluding the item currently being played.
|
||||
/// </summary>
|
||||
private IntroInfo? GetRandomLibraryMovieIntro(string libraryId, Guid excludeId)
|
||||
private IntroInfo? GetRandomLibraryMovieIntro(string label, string libraryId, Guid excludeId, User user)
|
||||
{
|
||||
if (!Guid.TryParse(libraryId, out var parsedId))
|
||||
return null;
|
||||
|
||||
var movies = _libraryManager
|
||||
.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
IncludeItemTypes = new[] { BaseItemKind.Movie },
|
||||
TopParentIds = new[] { parsedId },
|
||||
Recursive = true,
|
||||
})
|
||||
_logger.LogWarning(
|
||||
"|CinemaTrailers4Jellyfins| {Label} library ID '{LibraryId}' is not a valid GUID — check the plugin settings.",
|
||||
label,
|
||||
libraryId);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_libraryManager.GetItemById(parsedId) is not Folder folder)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"|CinemaTrailers4Jellyfins| {Label} library {LibraryId} could not be found.",
|
||||
label,
|
||||
parsedId);
|
||||
return null;
|
||||
}
|
||||
|
||||
var movies = folder.GetRecursiveChildren()
|
||||
.OfType<Movie>()
|
||||
.Where(m => m.Id != excludeId)
|
||||
.Where(m => m.Id != excludeId && m.IsVisibleStandalone(user))
|
||||
.ToList();
|
||||
|
||||
if (movies.Count == 0)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"|CinemaTrailers4Jellyfins| {Label} library {LibraryId} has no eligible Movie items visible to this user. "
|
||||
+ "Ensure the library has been scanned, contains at least one other Movie, and is in this user's library access list.",
|
||||
label,
|
||||
parsedId);
|
||||
return null;
|
||||
}
|
||||
|
||||
var movie = movies[_rng.Next(movies.Count)];
|
||||
_logger.LogInformation(
|
||||
"|CinemaTrailers4Jellyfins| {Label}: picked '{Title}' ({Path}).",
|
||||
label,
|
||||
movie.Name,
|
||||
movie.Path);
|
||||
return new IntroInfo { ItemId = movie.Id, Path = movie.Path };
|
||||
}
|
||||
|
||||
|
||||
10
build.yaml
10
build.yaml
@@ -1,5 +1,5 @@
|
||||
---
|
||||
version: 1.0.0.5
|
||||
version: 1.0.0.9
|
||||
name: CinemaTrailers4Jellyfins
|
||||
guid: b581493e-1046-40ed-b6dc-cb8027624984
|
||||
description: >
|
||||
@@ -12,10 +12,10 @@ category: General
|
||||
owner: 514mart
|
||||
targetAbi: 10.11.0.0
|
||||
changelog:
|
||||
- Add Trailer Pre-Roll and Feature Pre-Roll bumpers via IIntroProvider — pick
|
||||
existing Jellyfin Movie libraries in settings and a random movie from each is
|
||||
played before the trailer block ("Now Playing") and/or right before the
|
||||
feature ("Feature Presentation"); both are independent and disabled by default
|
||||
- Skip trailers and pre-roll/feature pre-roll bumpers the current user
|
||||
can't actually access (e.g. a library not in their library access list)
|
||||
— some Cinema Mode clients hang indefinitely trying to play an
|
||||
inaccessible item, so these are now filtered out per-user instead
|
||||
|
||||
dotnetProjects:
|
||||
- name: Jellyfin.Plugin.CinemaTrailers4Jellyfins
|
||||
|
||||
@@ -8,6 +8,46 @@
|
||||
"owner": "514mart",
|
||||
"imageUrl": "https://www.git.quarantinedstudio.com/mvezina/CinemaTrailers4Jellyfins/raw/branch/main/Jellyfin.Plugin.CinemaTrailers4Jellyfins/Images/logo.svg",
|
||||
"versions": [
|
||||
{
|
||||
"checksum": "9594d14a5fed94ee5eea55d72c71728c",
|
||||
"changelog": "- Skip trailers and pre-roll/feature pre-roll bumpers the current user can't actually access (e.g. a library not in their library access list) \u2014 some Cinema Mode clients hang indefinitely trying to play an inaccessible item, so these are now filtered out per-user instead\n",
|
||||
"targetAbi": "10.11.0.0",
|
||||
"sourceUrl": "https://www.git.quarantinedstudio.com/mvezina/CinemaTrailers4Jellyfins/releases/download/v1.0.0.9/cinematrailers4jellyfins_1.0.0.9.zip",
|
||||
"timestamp": "2026-06-10T17:07:24Z",
|
||||
"version": "1.0.0.9"
|
||||
},
|
||||
{
|
||||
"checksum": "89c66caa789da56ead7d60e01d3207a5",
|
||||
"changelog": "- Fix Trailer Pre-Roll / Feature Pre-Roll bumpers not being found \u2014 the library lookup now walks the configured library folder's children directly instead of relying on TopParentIds matching, which could fail to match movies even when they appear correctly in the library\n",
|
||||
"targetAbi": "10.11.0.0",
|
||||
"sourceUrl": "https://www.git.quarantinedstudio.com/mvezina/CinemaTrailers4Jellyfins/releases/download/v1.0.0.8/cinematrailers4jellyfins_1.0.0.8.zip",
|
||||
"timestamp": "2026-06-10T15:28:02Z",
|
||||
"version": "1.0.0.8"
|
||||
},
|
||||
{
|
||||
"checksum": "a52f1354c9737cf9ec92f588bb4e750e",
|
||||
"changelog": "- Add Information-level diagnostic logging to IIntroProvider.GetIntros \u2014 logs every call (item, path, and which features are enabled), why an item is skipped, and the outcome of Trailer/Feature Pre-Roll lookups, to help troubleshoot why a pre-roll bumper isn't playing\n",
|
||||
"targetAbi": "10.11.0.0",
|
||||
"sourceUrl": "https://www.git.quarantinedstudio.com/mvezina/CinemaTrailers4Jellyfins/releases/download/v1.0.0.7/cinematrailers4jellyfins_1.0.0.7.zip",
|
||||
"timestamp": "2026-06-10T15:10:56Z",
|
||||
"version": "1.0.0.7"
|
||||
},
|
||||
{
|
||||
"checksum": "96e0624e5173e5e6bf829d47118e1b40",
|
||||
"changelog": "- Add diagnostic logging for Trailer Pre-Roll and Feature Pre-Roll bumpers \u2014 logs a warning if the configured library ID is invalid, and a debug message when no eligible movie is found or which movie was picked, to help troubleshoot why a bumper isn't playing\n",
|
||||
"targetAbi": "10.11.0.0",
|
||||
"sourceUrl": "https://www.git.quarantinedstudio.com/mvezina/CinemaTrailers4Jellyfins/releases/download/v1.0.0.6/cinematrailers4jellyfins_1.0.0.6.zip",
|
||||
"timestamp": "2026-06-10T14:35:52Z",
|
||||
"version": "1.0.0.6"
|
||||
},
|
||||
{
|
||||
"checksum": "6c88aaeb38023b327e9b042181dc0708",
|
||||
"changelog": "- Add Trailer Pre-Roll and Feature Pre-Roll bumpers via IIntroProvider \u2014 pick existing Jellyfin Movie libraries in settings and a random movie from each is played before the trailer block (\"Now Playing\") and/or right before the feature (\"Feature Presentation\"); both are independent and disabled by default\n",
|
||||
"targetAbi": "10.11.0.0",
|
||||
"sourceUrl": "https://www.git.quarantinedstudio.com/mvezina/CinemaTrailers4Jellyfins/releases/download/v1.0.0.5/cinematrailers4jellyfins_1.0.0.5.zip",
|
||||
"timestamp": "2026-06-10T05:14:23Z",
|
||||
"version": "1.0.0.5"
|
||||
},
|
||||
{
|
||||
"checksum": "af6051b002939e4e5cd6ef4baf5c85d4",
|
||||
"changelog": "- Add TV show trailer downloads \u2014 separate TMDB sources (Airing Today, On The Air, Popular, Top Rated) and a \"Max TV show trailers per run\" cap independent from movies; set either cap to 0 to skip that category entirely\n- Add trailers before TV episodes via IIntroProvider \u2014 only the first episode a user watches each day gets trailers, picked using the show's genre/rating for filtering\n- Movie and TV trailers are now kept separate \u2014 movies only ever get movie trailers and episodes only ever get TV show trailers, via a tag written to each trailer's NFO at download time\n",
|
||||
|
||||
Reference in New Issue
Block a user