Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Plugins;

Expand Down Expand Up @@ -75,6 +76,10 @@ public class PluginConfiguration : BasePluginConfiguration
public int NumberOfTrailers { get; set; }
public bool TrailerConsumeMode { get; set; }

public string TmdbApiKey { get; set; }

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to consider if you implement this in your own plugin is that jellyfin actually stores trailer URLs from TMDB or other metadata providers in the media meta data. This is how it plays trailers from youtube when you dont have local trailers with your media.

You should just be able to query the jellyfin server for the youtube urls without needing to reach out to TMDB with an API key. Could simplify things for you and your users.


public string[] TrailerDownloadLibraries { get; set; }

public PluginConfiguration()
{
TrailerPreRollsLibrary = "-";
Expand All @@ -90,6 +95,10 @@ public PluginConfiguration()
NumberOfTrailers = 2;
EnforceRatingLimitTrailers = true;
TrailerConsumeMode = false;

TrailerDownloadLibraries = [];

TmdbApiKey = string.Empty;
}
}
}
150 changes: 147 additions & 3 deletions Jellyfin.Plugin.CinemaMode/Configuration/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ <h2 class="sectionTitle">Cinema Mode</h2>
Playback settings. For more information, access the user guide via the above help button.
Pro-tip: Skip any pre-roll or trailer by pressing the next button in the player.
</p>
<p>
NEW: It is also possible to automatically download trailers from TMDb / Youtube on a daily
schedule as a task.
The trailers will be stored locally, alongside the movies as "xyz-trailer.mp4".
</p>
<div>
<img alt="Cinema Mode Diagram" width="800" height="auto" src="https://github.com/CherryFloors/jellyfin-plugin-cinemamode/raw/main/Jellyfin.Plugin.CinemaMode/Images/cinema-mode-diagram.png"/>
<div class="fieldDescription">
Expand Down Expand Up @@ -197,8 +202,49 @@ <h3 class="sectionTitle">Seasonal Tag Definitions</h3>
<div class="paperList folderList" id="seasonal-tag-definitions">
</div>
</fieldset>
<br/>
<button is="emby-button" type="submit" class="raised button-submit block"><span>${Save}</span></button>

<!-- Auto Trailer Download -->
<fieldset class="verticalSection verticalSection-extrabottompadding">
<legend>
<h3 class="sectionTitle">Auto Trailer Download</h3>
</legend>
<div class="inputContainer">
<div>
<label class="inputLabel inputLabelUnfocused" for="tmdb-api-key">TMDb API Key:</label>
<a is="emby-linkbutton" class="raised button-alt headerHelpButton emby-button" target="_blank" href="https://github.com/CherryFloors/jellyfin-plugin-cinemamode#auto-trailer-download">Help</a>
</div>
<input type="password" id="tmdb-api-key" name="tmdb-api-key" is="emby-input" />
<div class="fieldDescription">
The API Key, which must be obtained from <a
href="https://developer.themoviedb.org/docs/getting-started"
target="_blank">TMDb</a> for the downloads to work.
</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="trailer-download-include-all-libraries"
name="trailer-download-include-all-libraries" />
<span>Include All Libraries</span>
<a is="emby-linkbutton" class="raised button-alt headerHelpButton emby-button" target="_blank" href="https://github.com/CherryFloors/jellyfin-plugin-cinemamode#auto-trailer-download">Help</a>
</label>
<div class="fieldDescription checkboxFieldDescription">
When checked, trailers will be downloaded for all movie libraries. When unchecked, only
trailers for selected libraries below will be downloaded.
</div>
</div>
<div class="sectionTitleContainer flex align-items-center">
<h4 class="sectionTitle">Selected Libraries</h4>
</div>
<div class="paperList folderList" id="trailer-download-libraries-list">
</div>
<div class="fieldDescription">
Select only specific movie libraries to download trailers from. If no libraries are
selected and "Include All Libraries" is unchecked, no trailers will be downloaded at all.
</div>
</fieldset>
<br />
<button is="emby-button" type="submit"
class="raised button-submit block"><span>${Save}</span></button>
</form>
</div>
</div>
Expand Down Expand Up @@ -469,6 +515,93 @@ <h3 class="sectionTitle">Seasonal Tag Definitions</h3>
});
};

function initLibraryCheckboxes(selectedLibraries, selectAllID, libraryListID, prefix) {
var container = document.getElementById(libraryListID);
container.innerHTML = "";

// Check if "all libraries" should be selected
var selectedArray = selectedLibraries;
var includeAllLibraries = selectedArray.includes("*") && selectedArray.length === 1;

var selectAllCheckBox = document.getElementById(selectAllID);
selectAllCheckBox.checked = includeAllLibraries;

mediaFolders.then((folderArray) => {

// Add movie libraries as checkboxes
for (const folder of folderArray) {
if (folder.CollectionType == "movies") {
var listItem = document.createElement('div');
listItem.setAttribute("class", "listItem listItem-border");

var isSelected = selectedArray.includes(folder.ItemId);


// Create label
var label = document.createElement('label');

// Create checkbox input
var input = document.createElement('input');
input.setAttribute('is', 'emby-checkbox');
input.setAttribute('type', 'checkbox');
input.setAttribute('id', prefix + folder.ItemId);
input.setAttribute('name', prefix + folder.ItemId);
input.setAttribute('data-itemId', folder.ItemId);
if (isSelected) {
input.setAttribute('checked', 'true');
}

// Create span for label text
var span = document.createElement('span');
span.textContent = folder.Name;

// Assemble elements
label.appendChild(input);
label.appendChild(span);
listItem.appendChild(label);

container.appendChild(listItem);
}
}

// Add event listener for "Include All Libraries" checkbox
document.getElementById(selectAllID).addEventListener('change', function () {
var checkboxes = container.querySelectorAll('input[type="checkbox"]');
for (let checkbox of checkboxes) {
checkbox.checked = this.checked;
checkbox.disabled = this.checked;
}
});

// Run same logic right away: if "includeAll" is ticked, all other checkboxes should be checked and disabled
if (includeAllLibraries) {
var checkboxes = container.querySelectorAll('input[type="checkbox"]');
for (let checkbox of checkboxes) {
checkbox.checked = true;
checkbox.disabled = true;
}
}
});
}

function getSelectedLibrariesFromCheckboxes(selectAllID, libraryListID, prefix) {
var includeAllLibraries = document.getElementById(selectAllID).checked;
if (includeAllLibraries) {
return ["*"];
}

var container = document.getElementById(libraryListID);
var checkboxes = container.querySelectorAll('input[type="checkbox"]:checked');
var selected = [];

for (let checkbox of checkboxes) {
var id = checkbox.getAttribute("data-itemId");
selected.push(id);
}

return selected;
}

$('.cinemaModeConfigurationPage').on('pageshow', function () {
Dashboard.showLoadingMsg();
clearList('trailerPreRollLibrarySelect');
Expand All @@ -480,6 +613,7 @@ <h3 class="sectionTitle">Seasonal Tag Definitions</h3>
var page = this;
ApiClient.getPluginConfiguration(pluginId).then(function (config) {
$('#number-trailers', page).val(config.NumberOfTrailers);
$('#tmdb-api-key', page).val(config.TmdbApiKey);
document.getElementById('enforce-rating-limit-trailers').checked = config.EnforceRatingLimitTrailers;
initLibrarySelector('trailerPreRollLibrarySelect', config.TrailerPreRollsLibrary, config.TrailerPreRollsLibrary);
initLibrarySelector('featurePreRollLibrarySelect', config.FeaturePreRollsLibrary, config.FeaturePreRollsLibrary);
Expand All @@ -494,6 +628,13 @@ <h3 class="sectionTitle">Seasonal Tag Definitions</h3>

initSeasonalTagDefs(config.SeasonalTagDefinitions);
initTrailerRules(config.TrailerSelectionRules);

initLibraryCheckboxes(config.TrailerDownloadLibraries,
'trailer-download-include-all-libraries',
'trailer-download-libraries-list',
'trailer-download-list-'
);

Dashboard.hideLoadingMsg();
});
});
Expand All @@ -504,6 +645,7 @@ <h3 class="sectionTitle">Seasonal Tag Definitions</h3>
var TrailerPreRollsLibrary = $('#trailerPreRollLibrarySelect').val();
var FeaturePreRollsLibrary = $('#featurePreRollLibrarySelect').val();
var NumberOfTrailers = $('#number-trailers').val();
var TmdbApiKey = $('#tmdb-api-key').val();
ApiClient.getPluginConfiguration(pluginId).then(function (config) {

config.TrailerPreRollsLibrary = TrailerPreRollsLibrary;
Expand All @@ -517,8 +659,10 @@ <h3 class="sectionTitle">Seasonal Tag Definitions</h3>
config.SeasonalTagDefinitions = getSeasonalTagDefs();
config.TrailerSelectionRules = getTrailerRules();
config.NumberOfTrailers = parseInt(NumberOfTrailers);
config.TmdbApiKey = TmdbApiKey;
config.EnforceRatingLimitTrailers = document.getElementById('enforce-rating-limit-trailers').checked;
config.TrailerConsumeMode = document.getElementById('trailer-rules-consume-mode').checked;
config.TrailerDownloadLibraries = getSelectedLibrariesFromCheckboxes('trailer-download-include-all-libraries', 'trailer-download-libraries-list', 'trailer-download-list-');

ApiClient.updatePluginConfiguration(pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
Expand All @@ -528,4 +672,4 @@ <h3 class="sectionTitle">Seasonal Tag Definitions</h3>
</script>
</div>
</body>
</html>
</html>
2 changes: 2 additions & 0 deletions Jellyfin.Plugin.CinemaMode/Jellyfin.Plugin.CinemaMode.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="TMDbLib" Version="2.2.0" />
<PackageReference Include="YoutubeDlSharp" Version="1.1.2" />
<PackageReference Include="Jellyfin.Controller" Version="10.11.0" />
<PackageReference Include="Jellyfin.Model" Version="10.11.0" />
</ItemGroup>
Expand Down
19 changes: 19 additions & 0 deletions Jellyfin.Plugin.CinemaMode/TrailerDownloader/MediaItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;

namespace Jellyfin.Plugin.CinemaMode.TrailerDownloader;

public class MediaItem
{
public string Id { get; internal set; }
public string Title { get; internal set; }
public IEnumerable<string> Files { get; internal set; }
public string TmdbId { get; internal set; }
}
Loading