Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b0d6f137d | ||
|
|
2208b86a47 | ||
|
|
5a1048687c | ||
|
|
d3f6641158 | ||
|
|
c214a620e4 | ||
|
|
f0c9462878 |
@@ -449,7 +449,7 @@
|
|||||||
ApiClient.getPluginConfiguration(MediaBarEnhancedConfigurationPage.pluginId).then(function (config) {
|
ApiClient.getPluginConfiguration(MediaBarEnhancedConfigurationPage.pluginId).then(function (config) {
|
||||||
|
|
||||||
var keys = [
|
var keys = [
|
||||||
'MediaBarIsEnabled', 'ShuffleInterval', 'RetryInterval', 'MinSwipeDistance',
|
'ShuffleInterval', 'RetryInterval', 'MinSwipeDistance',
|
||||||
'LoadingCheckInterval', 'MaxPlotLength', 'MaxMovies', 'MaxTvShows',
|
'LoadingCheckInterval', 'MaxPlotLength', 'MaxMovies', 'MaxTvShows',
|
||||||
'MaxItems', 'PreloadCount', 'FadeTransitionDuration', 'MaxPaginationDots',
|
'MaxItems', 'PreloadCount', 'FadeTransitionDuration', 'MaxPaginationDots',
|
||||||
'SlideAnimationEnabled', 'EnableVideoBackdrop', 'UseSponsorBlock',
|
'SlideAnimationEnabled', 'EnableVideoBackdrop', 'UseSponsorBlock',
|
||||||
@@ -462,6 +462,12 @@
|
|||||||
'IncludeWatchedContent'
|
'IncludeWatchedContent'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Manual mapping for MediaBarIsEnabled -> IsEnabled, to avoid conflicts with other plugins
|
||||||
|
var mediaBarEnabledCheckbox = page.querySelector('#MediaBarIsEnabled');
|
||||||
|
if (mediaBarEnabledCheckbox) {
|
||||||
|
mediaBarEnabledCheckbox.checked = config.IsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
keys.forEach(function (key) {
|
keys.forEach(function (key) {
|
||||||
var el = page.querySelector('#' + key);
|
var el = page.querySelector('#' + key);
|
||||||
if (el) {
|
if (el) {
|
||||||
@@ -539,8 +545,15 @@
|
|||||||
if (seasonalInput) seasonalInput.value = sectionsJson;
|
if (seasonalInput) seasonalInput.value = sectionsJson;
|
||||||
|
|
||||||
var config = {};
|
var config = {};
|
||||||
|
|
||||||
|
// Manual mapping for MediaBarIsEnabled -> IsEnabled, to avoid conflicts with other plugins
|
||||||
|
var mediaBarEnabledCheckbox = page.querySelector('#MediaBarIsEnabled');
|
||||||
|
if (mediaBarEnabledCheckbox) {
|
||||||
|
config.IsEnabled = mediaBarEnabledCheckbox.checked;
|
||||||
|
}
|
||||||
|
|
||||||
var keys = [
|
var keys = [
|
||||||
'MediaBarIsEnabled', 'ShuffleInterval', 'RetryInterval', 'MinSwipeDistance',
|
'ShuffleInterval', 'RetryInterval', 'MinSwipeDistance',
|
||||||
'LoadingCheckInterval', 'MaxPlotLength', 'MaxMovies', 'MaxTvShows',
|
'LoadingCheckInterval', 'MaxPlotLength', 'MaxMovies', 'MaxTvShows',
|
||||||
'MaxItems', 'PreloadCount', 'FadeTransitionDuration', 'MaxPaginationDots',
|
'MaxItems', 'PreloadCount', 'FadeTransitionDuration', 'MaxPaginationDots',
|
||||||
'SlideAnimationEnabled', 'EnableVideoBackdrop', 'UseSponsorBlock',
|
'SlideAnimationEnabled', 'EnableVideoBackdrop', 'UseSponsorBlock',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> -->
|
<!-- <TreatWarningsAsErrors>false</TreatWarningsAsErrors> -->
|
||||||
<Title>Jellyfin Media Bar Enhanced Plugin</Title>
|
<Title>Jellyfin Media Bar Enhanced Plugin</Title>
|
||||||
<Authors>CodeDevMLH</Authors>
|
<Authors>CodeDevMLH</Authors>
|
||||||
<Version>1.6.5.2</Version>
|
<Version>1.6.6.1</Version>
|
||||||
<RepositoryUrl>https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced</RepositoryUrl>
|
<RepositoryUrl>https://github.com/CodeDevMLH/jellyfin-plugin-media-bar-enhanced</RepositoryUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const CONFIG = {
|
|||||||
sortOrder: "Ascending",
|
sortOrder: "Ascending",
|
||||||
applyLimitsToCustomIds: false,
|
applyLimitsToCustomIds: false,
|
||||||
seasonalSections: "[]",
|
seasonalSections: "[]",
|
||||||
|
isEnabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// State management
|
// State management
|
||||||
@@ -1638,6 +1639,7 @@ const SlideCreator = {
|
|||||||
"data-item-id": itemId,
|
"data-item-id": itemId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let videoBackdrop;
|
||||||
let backdrop;
|
let backdrop;
|
||||||
let isVideo = false;
|
let isVideo = false;
|
||||||
let trailerUrl = null;
|
let trailerUrl = null;
|
||||||
@@ -1721,11 +1723,19 @@ const SlideCreator = {
|
|||||||
// Create container for YouTube API
|
// Create container for YouTube API
|
||||||
const videoClass = CONFIG.fullWidthVideo ? "video-backdrop-full" : "video-backdrop-default";
|
const videoClass = CONFIG.fullWidthVideo ? "video-backdrop-full" : "video-backdrop-default";
|
||||||
|
|
||||||
backdrop = SlideUtils.createElement("div", {
|
// Create a wrapper for opacity transition
|
||||||
|
videoBackdrop = SlideUtils.createElement("div", {
|
||||||
className: `backdrop video-backdrop ${videoClass}`,
|
className: `backdrop video-backdrop ${videoClass}`,
|
||||||
id: `youtube-player-${itemId}`
|
style: "opacity: 0; transition: opacity 1.2s ease-in-out;" // Start interrupted/transparent
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ytPlayerDiv = SlideUtils.createElement("div", {
|
||||||
|
id: `youtube-player-${itemId}`,
|
||||||
|
style: "width: 100%; height: 100%;"
|
||||||
|
});
|
||||||
|
|
||||||
|
videoBackdrop.appendChild(ytPlayerDiv);
|
||||||
|
|
||||||
// Initialize YouTube Player
|
// Initialize YouTube Player
|
||||||
SlideUtils.loadYouTubeIframeAPI().then(() => {
|
SlideUtils.loadYouTubeIframeAPI().then(() => {
|
||||||
// Fetch SponsorBlock data
|
// Fetch SponsorBlock data
|
||||||
@@ -1782,6 +1792,9 @@ const SlideCreator = {
|
|||||||
event.target._endTime = playerVars.end || undefined;
|
event.target._endTime = playerVars.end || undefined;
|
||||||
event.target._videoId = videoId;
|
event.target._videoId = videoId;
|
||||||
|
|
||||||
|
// Store reference to wrapper for fading
|
||||||
|
event.target._wrapperDiv = videoBackdrop;
|
||||||
|
|
||||||
if (STATE.slideshow.isMuted) {
|
if (STATE.slideshow.isMuted) {
|
||||||
event.target.mute();
|
event.target.mute();
|
||||||
} else {
|
} else {
|
||||||
@@ -1830,6 +1843,13 @@ const SlideCreator = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'onStateChange': (event) => {
|
'onStateChange': (event) => {
|
||||||
|
// Fade in when playing
|
||||||
|
if (event.data === YT.PlayerState.PLAYING) {
|
||||||
|
if (event.target._wrapperDiv) {
|
||||||
|
event.target._wrapperDiv.style.opacity = "1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (event.data === YT.PlayerState.ENDED) {
|
if (event.data === YT.PlayerState.ENDED) {
|
||||||
const slide = document.querySelector(`.slide[data-item-id="${itemId}"]`);
|
const slide = document.querySelector(`.slide[data-item-id="${itemId}"]`);
|
||||||
if (slide && slide.classList.contains('active')) {
|
if (slide && slide.classList.contains('active')) {
|
||||||
@@ -1859,17 +1879,17 @@ const SlideCreator = {
|
|||||||
preload: "none",
|
preload: "none",
|
||||||
disablePictureInPicture: true,
|
disablePictureInPicture: true,
|
||||||
"data-src": videoSrc,
|
"data-src": videoSrc,
|
||||||
style: "object-fit: cover; object-position: center center; width: 100%; height: 100%; position: absolute; top: 0; left: 0; pointer-events: none;"
|
style: "object-fit: cover; object-position: center center; width: 100%; height: 100%; position: absolute; top: 0; left: 0; pointer-events: none; opacity: 0; transition: opacity 1.2s ease-in-out;"
|
||||||
};
|
};
|
||||||
|
|
||||||
videoAttributes.muted = "";
|
videoAttributes.muted = "";
|
||||||
|
|
||||||
backdrop = SlideUtils.createElement("video", videoAttributes);
|
videoBackdrop = SlideUtils.createElement("video", videoAttributes);
|
||||||
backdrop.volume = 0.4;
|
videoBackdrop.volume = 0.4;
|
||||||
|
|
||||||
STATE.slideshow.videoPlayers[itemId] = backdrop;
|
STATE.slideshow.videoPlayers[itemId] = videoBackdrop;
|
||||||
|
|
||||||
backdrop.addEventListener('play', (event) => {
|
videoBackdrop.addEventListener('play', (event) => {
|
||||||
const slide = document.querySelector(`.slide[data-item-id="${itemId}"]`);
|
const slide = document.querySelector(`.slide[data-item-id="${itemId}"]`);
|
||||||
if (!slide || !slide.classList.contains('active')) {
|
if (!slide || !slide.classList.contains('active')) {
|
||||||
console.log(`Local video ${itemId} started playing but slide is not active, pausing.`);
|
console.log(`Local video ${itemId} started playing but slide is not active, pausing.`);
|
||||||
@@ -1877,19 +1897,23 @@ const SlideCreator = {
|
|||||||
event.target.currentTime = 0;
|
event.target.currentTime = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fade in
|
||||||
|
event.target.style.opacity = "1";
|
||||||
|
|
||||||
if (CONFIG.waitForTrailerToEnd && STATE.slideshow.slideInterval) {
|
if (CONFIG.waitForTrailerToEnd && STATE.slideshow.slideInterval) {
|
||||||
STATE.slideshow.slideInterval.stop();
|
STATE.slideshow.slideInterval.stop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
backdrop.addEventListener('ended', (event) => {
|
videoBackdrop.addEventListener('ended', (event) => {
|
||||||
const slide = event.target.closest('.slide');
|
const slide = event.target.closest('.slide');
|
||||||
if (slide && slide.classList.contains('active')) {
|
if (slide && slide.classList.contains('active')) {
|
||||||
SlideshowManager.nextSlide();
|
SlideshowManager.nextSlide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
backdrop.addEventListener('error', (event) => {
|
videoBackdrop.addEventListener('error', (event) => {
|
||||||
console.warn(`Local video error for item ${itemId}`);
|
console.warn(`Local video error for item ${itemId}`);
|
||||||
const slide = event.target.closest('.slide');
|
const slide = event.target.closest('.slide');
|
||||||
if (slide && slide.classList.contains('active')) {
|
if (slide && slide.classList.contains('active')) {
|
||||||
@@ -1899,13 +1923,18 @@ const SlideCreator = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isVideo) {
|
// Always create a static backdrop image (to show while video loads or if no video)
|
||||||
backdrop = SlideUtils.createElement("img", {
|
backdrop = SlideUtils.createElement("img", {
|
||||||
className: "backdrop high-quality",
|
className: "backdrop high-quality",
|
||||||
src: this.buildImageUrl(item, "Backdrop", 0, serverAddress, 60),
|
src: this.buildImageUrl(item, "Backdrop", 0, serverAddress, 60),
|
||||||
alt: LocalizationUtils.getLocalizedString('Backdrop', 'Backdrop'),
|
alt: LocalizationUtils.getLocalizedString('Backdrop', 'Backdrop'),
|
||||||
loading: "eager",
|
loading: "eager",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If video, static backdrop should be strictly a background (no animation)
|
||||||
|
if (isVideo) {
|
||||||
|
backdrop.style.animation = "none";
|
||||||
|
backdrop.style.transition = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
const backdropOverlay = SlideUtils.createElement("div", {
|
const backdropOverlay = SlideUtils.createElement("div", {
|
||||||
@@ -1915,8 +1944,14 @@ const SlideCreator = {
|
|||||||
const backdropContainer = SlideUtils.createElement("div", {
|
const backdropContainer = SlideUtils.createElement("div", {
|
||||||
className: "backdrop-container" + (isVideo && CONFIG.fullWidthVideo ? " full-width-video" : ""),
|
className: "backdrop-container" + (isVideo && CONFIG.fullWidthVideo ? " full-width-video" : ""),
|
||||||
});
|
});
|
||||||
|
|
||||||
backdropContainer.append(backdrop, backdropOverlay);
|
backdropContainer.append(backdrop, backdropOverlay);
|
||||||
|
|
||||||
|
// If video exists, append on top of static backdrop
|
||||||
|
if (isVideo && videoBackdrop) {
|
||||||
|
backdropContainer.appendChild(videoBackdrop);
|
||||||
|
}
|
||||||
|
|
||||||
const logo = SlideUtils.createElement("img", {
|
const logo = SlideUtils.createElement("img", {
|
||||||
className: "logo high-quality",
|
className: "logo high-quality",
|
||||||
src: this.buildImageUrl(item, "Logo", undefined, serverAddress, 40),
|
src: this.buildImageUrl(item, "Logo", undefined, serverAddress, 40),
|
||||||
|
|||||||
@@ -8,6 +8,14 @@
|
|||||||
"category": "General",
|
"category": "General",
|
||||||
"imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/raw/branch/main/logo.png",
|
"imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/raw/branch/main/logo.png",
|
||||||
"versions": [
|
"versions": [
|
||||||
|
{
|
||||||
|
"version": "1.6.6.1",
|
||||||
|
"changelog": "- feat: add static backdrop also for video backdrops\n- fix: renaming issue of settings (avoiding conflict with other plugins)",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/jellyfin-plugin-media-bar-enhanced/releases/download/v1.6.6.1/Jellyfin.Plugin.MediaBarEnhanced.zip",
|
||||||
|
"checksum": "b601594ce1b9928a6e4a41d94c866346",
|
||||||
|
"timestamp": "2026-02-19T02:25:33Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "1.6.5.2",
|
"version": "1.6.5.2",
|
||||||
"changelog": "- refactored seasonal UI settings",
|
"changelog": "- refactored seasonal UI settings",
|
||||||
|
|||||||
Reference in New Issue
Block a user