del old files

This commit is contained in:
MLH
2024-12-27 00:33:57 +01:00
parent 8018010aae
commit 7b2f5acb21
4 changed files with 483 additions and 1988 deletions

341
script.js
View File

@@ -1,4 +1,4 @@
let title = 'Spotlight'; // Title of the slideshow
let title = 'Spotlight'; // Title of the slideshow TBD
let listFileName = 'list.txt'; // Name of the file containing the list of movie IDs
let token = 'YOURAPIKEYHERE'; // Your Jellyfin API key
let moviesSeriesBoth = 3; // 1 for movies, 2 for series, 3 for both
@@ -6,12 +6,94 @@ let shuffleInterval = 15000; // Time in milliseconds before the next slide is sh
let useTrailers = true; // Set to false to disable trailers
let setRandomMovie = true; // Set to false to disable random movie selection from the list
let showOnOtherPages = false; // Set to true to show the slideshow on all pages eg. favorites tab, requests tab, etc.
let showTitle = false; // Set to false to hide the title
//let showTitle = false; // Set to false to hide the title TBD
let disableTrailerControls = true; // Set to false to enable trailer controls
let setMuted = true; // Set to false to disable unmuting the video on hover
let umuteOnHover = true; // Set to false to disable unmuting the video on hover
let unmutedVolume = 20; // Set the volume level when the video is unmuted
let useSponsorBlock = true; // Set to true to use SponsorBlock data to skip intro/outro segments of trailers
let plotMaxLength = 550; // Maximum number of characters in the plot
let trailerMaxLength = 0; // Default value 0; length measured in ms, set to 0 to disable, could be used instead of SponsorBlock
let isMuted = true; // Default value true; set to false to start the video unmuted
// variables
let isChangingSlide = false, player = null, slideChangeTimeout = null, isHomePageActive = false;
let currentLocation = window.top.location.href;
let movieList = [], currentMovieIndex = 0;
let previousMovies = [];
let forwardMovies = [];
if (setMuted) {
const slidesContainer = document.getElementById('slides-container');
slidesContainer.addEventListener('mouseenter', () => {
if (player) {
player.unMute();
isMuted = false;
}
});
slidesContainer.addEventListener('mouseleave', () => {
if (player) {
player.mute();
isMuted = true;
}
});
}
// Get SponsorBlock-Data for the outro segment of the trailer
//function fetchSponsorBlockOutro(videoId) {
// @deprecated
const fetchSponsorBlockOutroOLD = (videoId) => {
return fetch(`https://sponsor.ajay.app/api/skipSegments?videoID=${videoId}&category=outro`)
.then(response => response.json())
.then(segments => {
return segments.length > 0 ? segments[0].segment : null;
})
.catch(error => {
console.error('Error fetching SponsorBlock data:', error);
return null;
});
};
const fetchSponsorBlockOutro = async (videoId) => {
try {
const response = await fetch(`https://sponsor.ajay.app/api/skipSegments?videoID=${videoId}&category=outro`);
const segments = await response.json();
if (segments.length > 0 && Array.isArray(segments[0].segment)) {
return segments[0].segment; // returns array: [start, end]
}
return null;
} catch (error) {
console.error('Error fetching SponsorBlock data:', error);
return null;
}
};
// Monitor the video player for the outro segment
let monitorOutroInterval = null; // Global interval variable
function monitorOutro(player, outroSegment) {
if (monitorOutroInterval) { // Clear the interval if it's already running
clearInterval(monitorOutroInterval);
}
monitorOutroInterval = setInterval(() => {
if (!outroSegment || !player) {
console.log('Invalid outro segment or player not initialized');
clearInterval(monitorOutroInterval);
return;
}
const currentTime = player.getCurrentTime();
if (currentTime >= outroSegment[0] && currentTime < outroSegment[1]) {
clearInterval(monitorOutroInterval);
player.stopVideo(); // stop video
setTimeout(fetchRandomMovie, 100); // fetch next movie
}
}, 500); // check every 500ms
}
const createElem = (tag, className, textContent, src, alt) => {
const elem = document.createElement(tag);
@@ -30,8 +112,12 @@ function isMobile() {
const truncateText = (text, maxLength) => text.length > maxLength ? text.substr(0, maxLength) + '...' : text;
const cleanup = () => {
if (player) { player.destroy(); player = null; }
if (player && typeof player.destroy === 'function') {
player.destroy();
}
player = null;
clearTimeout(slideChangeTimeout);
slideChangeTimeout = null;
const container = document.getElementById('slides-container');
if (container) container.innerHTML = '';
};
@@ -40,14 +126,35 @@ const createSlideElement = (movie, hasVideo = false) => {
cleanup();
const container = document.getElementById('slides-container');
const slide = createElem('div', 'slide');
if (movie && (!previousMovies.length || previousMovies[previousMovies.length - 1].Id !== movie.Id)) {
previousMovies.push(movie);
}
if (previousMovies.length > 50) {
previousMovies.shift();
}
['backdrop', 'logo'].forEach(type => slide.appendChild(createElem('img', type, null, `/Items/${movie.Id}/Images/${type.charAt(0).toUpperCase() + type.slice(1)}${type === 'backdrop' ? '/0' : ''}`, type)));
slide.appendChild(createElem('div', 'heading', title));
const textContainer = createElem('div', 'text-container');
const premiereYear = movie.PremiereDate ? new Date(movie.PremiereDate).getFullYear() : 'Unknown';
const additionalInfo = movie.Type === 'Series' ?
(movie.ChildCount ? `${movie.ChildCount} Season${movie.ChildCount > 1 ? 's' : ''}` : 'Unknown Seasons') :
(movie.RunTimeTicks ? `${Math.round(movie.RunTimeTicks / 600000000)} min` : 'Unknown Runtime');
let additionalInfo;
if (movie.Type === 'Series') {
additionalInfo = movie.ChildCount
? `${movie.ChildCount} Season${movie.ChildCount > 1 ? 's' : ''}`
: 'Unknown Seasons';
} else if (movie.Type === 'BoxSet') {
additionalInfo = movie.ChildCount
? `${movie.ChildCount} Movie${movie.ChildCount > 1 ? 's' : ''}`
: 'Unknown Movies';
} else {
additionalInfo = movie.RunTimeTicks
? `${Math.round(movie.RunTimeTicks / 600000000)} min`
: 'Unknown Runtime';
}
let loremText = `
<span style="background: transparent; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; margin-left: 1em;">${additionalInfo}</span>
@@ -60,7 +167,7 @@ const createSlideElement = (movie, hasVideo = false) => {
}
if (movie.CriticRating) {
loremText += `<span style="background: transparent; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; margin-left: 1em;">
<img src="https://i.imgur.com/rMvyQMt.png" alt="Rotten Tomatoes" style="width: 1.05em; height: 1.25em; font-size: 0.9em; padding-right: 0.1em; margin-left: -0.1em; vertical-align: bottom;">
<img src="https://i.imgur.com/aoLXyXx.png" alt="Rotten Tomatoes" style="width: 1.05em; height: 1.25em; font-size: 0.83em; padding-right: 0.4em; margin-left: -0.1em; vertical-align: middle; padding-bottom: 0.3em;">
${movie.CriticRating}%</span>`;
}
@@ -111,15 +218,22 @@ const createSlideElement = (movie, hasVideo = false) => {
const backButton = createElem('div', 'back-button');
const backIcon = createElem('i', 'material-icons');
backIcon.textContent = 'chevron_left';
backButton.appendChild(backIcon);
backButton.onclick = (e) => {
e.stopPropagation();
navigateBack();
};
// Skip button logic
const skipButton = createElem('div', 'skip-button');
const skipIcon = createElem('i', 'material-icons');
skipIcon.textContent = 'chevron_right';
backButton.appendChild(backIcon);
skipButton.appendChild(skipIcon);
skipButton.onclick = (e) => {
e.stopPropagation();
navigateForward();
};
skipIcon.onclick = (e) => { e.stopPropagation(); fetchRandomMovie(); };
backIcon.onclick = (e) => { e.stopPropagation(); previousMovie(); };
slide.appendChild(backButton);
slide.appendChild(skipButton);
@@ -178,10 +292,42 @@ const createSlideElement = (movie, hasVideo = false) => {
width: '100%',
videoId,
playerVars: {
mute: 1 // Start the video muted
mute: isMuted ? 1 : 0, // CHeck if the video should start muted
controls: disableTrailerControls ? 0 : 1, // Hide the controls
disablekb: 1, // Disable keyboard controls
fs: 1, // Enavle fullscreen
iv_load_policy: 3, // Disable annotations
},
events: {
'onReady': event => {
'onReady': event => {
if (useSponsorBlock) {
fetchSponsorBlockOutro(videoId).then(outroSegment => {
if (outroSegment) {
console.log(`SponsorBlock outro segment: Start - ${outroSegment[0]}s, End - ${outroSegment[1]}s`);
monitorOutro(player, outroSegment);
} else {
console.log('No outro segment found/provided');
}
}).catch(error => {
console.error('Error reading SponsorBlock outro segment:', error);
});
}
if (trailerMaxLength > 0) {
clearTimeout(slideChangeTimeout);
slideChangeTimeout = setTimeout(() => {
if (player) {
player.stopVideo();
player.destroy();
player = null;
}
fetchRandomMovie();
}, trailerMaxLength);
}
if (isMuted) {
event.target.setVolume(0); // Mute the video if the setting is MuteOn
} else {
event.target.setVolume(unmutedVolume); // Set the volume, value between 0 and 100
}
event.target.playVideo();
},
'onStateChange': event => {
@@ -192,15 +338,22 @@ const createSlideElement = (movie, hasVideo = false) => {
backdrop.style.width = 'calc(100% - 23vw)';
backdrop.style.left = '0vw';
}
const plot = document.querySelector('.plot');
if (plot) plot.style.width = 'calc(100% - 36.4vw)';
if (plot) {
plot.style.left = '33vw';
plot.style.maxWidth = '63vw';
}
const genres = document.querySelector('.genres');
if (genres) {
genres.style.left = 'calc(50% - 17vw)';
}
const loremIpsum = document.querySelector('.lorem-ipsum');
if (loremIpsum) loremIpsum.style.paddingRight = '32.4vw';
if (loremIpsum) loremIpsum.style.left = 'calc(50% - 17vw)';
const logo = document.querySelector('.logo');
if (logo) logo.style.left = 'calc(50% - 14.2vw)';
if (logo) logo.style.left = 'calc(50% - 17vw)';
videoContainer.style.width = '34.4vw';
} else if (event.data === YT.PlayerState.ENDED) {
@@ -210,6 +363,7 @@ const createSlideElement = (movie, hasVideo = false) => {
'onError': () => {
console.error(`YouTube prevented playback of '${movie.Name}'`);
if (player) {
clearInterval(monitorOutroInterval);
player.destroy();
player = null;
}
@@ -233,6 +387,7 @@ const createSlideElement = (movie, hasVideo = false) => {
}
}
});
} else {
startSlideChangeTimer();
}
@@ -242,17 +397,6 @@ const createSlideElement = (movie, hasVideo = false) => {
container.appendChild(slide);
};
// Fetch the previous movie in the list
function previousMovie() {
if (isChangingSlide) return;
isChangingSlide = true;
// Reset index or set to the end of the list if we are at the first element
currentMovieIndex = (currentMovieIndex - 2 + movieList.length) % movieList.length;
fetchNextMovie();
}
function addSwipeListeners(slide) {
let startX, startY, distX, distY;
const threshold = 50;
@@ -283,6 +427,33 @@ function addSwipeListeners(slide) {
});
}
const navigateBack = () => {
if (previousMovies.length < 2) {
console.log("No previous slides in history.");
return;
}
const currentMovie = previousMovies.pop();
forwardMovies.unshift(currentMovie);
const previousMovie = previousMovies[previousMovies.length - 1];
createSlideElement(previousMovie, true);
};
const navigateForward = () => {
if (forwardMovies.length === 0) {
console.log("No forward slides in history.");
fetchRandomMovie();
return;
}
const currentMovie = forwardMovies.shift();
previousMovies.push(currentMovie);
createSlideElement(currentMovie, true);
};
// Show the video overlay
function showVideoOverlay(trailerUrl) {
const videoOverlay = document.getElementById('video-overlay');
@@ -353,53 +524,65 @@ const readCustomList = () =>
fetch(listFileName + '?' + new Date().getTime())
.then(response => response.ok ? response.text() : null)
.then(text => {
if (!text) return null;
if (!text || !text.trim()) {
console.warn('List.txt is empty or could not be loaded.');
return null; // Fallback to random selection
}
const lines = text.split('\n').filter(Boolean);
title = lines.shift() || title;
return lines.map(line => line.trim().substring(0, 32));
const firstLine = lines.shift().trim();
const [parsedTitle, muteSetting] = firstLine.split(/\s+/);
title = parsedTitle || title;
// Check for mute
// MARK: set mute
isMuted = muteSetting === "MuteOn";
// Remaining lines are media IDs
const mediaList = lines.map(line => line.trim().substring(0, 32));
if (setRandomMovie) {
return shuffleArray(mediaList); // Shuffle the list before returning it if set
}
return mediaList; // return exact list
})
.catch(() => null);
.catch(() => {
console.error('Error reading List.txt. Falling back to random selection.');
return null;
});
// using Fisher-Yates shuffle algorithm if list is available and setRandomMovie is set to true
const shuffleArray = (array) => {
for (let i = array.length - 1; i > 0; i--) {
// Generate a random index between 0 and i
const j = Math.floor(Math.random() * (i + 1));
// Swap elements at indices i and j
[array[i], array[j]] = [array[j], array[i]];
//var temp = array[i];
//array[i] = array[j];
//array[j] = temp;
}
return array;
};
//const shuffleArray = (array) => array.sort(() => Math.random() - 0.5); //better use Fisher-Yates shuffle algorithm
const fetchRandomMovie = () => {
if (isChangingSlide) return;
isChangingSlide = true;
if (movieList.length === 0) {
readCustomList().then(list => {
if (list) {
if (list && list.length > 0) {
movieList = list;
//// Shuffle the list if it was set by the user
//if (setRandomMovie) {
// shuffleArray(movieList);
//}
currentMovieIndex = 0;
fetchNextMovie();
} else {
console.warn("Fallback to random selection.");
fetchNextMovie(true);
}
fetchNextMovie();
});
} else fetchNextMovie();
} else {
fetchNextMovie();
}
};
const fetchNextMovie = () => {
const shuffleArray = (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
};
const fetchNextMovie = (useRandom = false) => {
const fetchCurrentUserId = () =>
fetch('/Sessions', {
headers: { 'Authorization': `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"` }
})
fetch('/Sessions', { headers: { 'Authorization': `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"` } })
.then(response => response.json())
.then(sessions => {
const currentSession = sessions.find(session => session.UserId);
@@ -410,12 +593,13 @@ const fetchNextMovie = () => {
fetchCurrentUserId().then(currentUserId => {
if (!currentUserId) {
console.error('Could not retrieve the current user ID.');
isChangingSlide = false;
return;
}
const headers = { 'Authorization': `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"` };
if (movieList.length > 0) {
if (!useRandom && movieList.length > 0) {
if (currentMovieIndex >= movieList.length) currentMovieIndex = 0;
const movieId = movieList[currentMovieIndex];
currentMovieIndex++;
@@ -438,22 +622,22 @@ const fetchNextMovie = () => {
const checkNavigation = () => {
const newLocation = window.top.location.href;
// Check if the user is navigating to or from the homepage
if (newLocation !== currentLocation) {
currentLocation = newLocation;
const isHomePage = url => url.includes('/home') || url.endsWith('/web/') || url.endsWith('/web/index.html');
if (isHomePage(newLocation)) {
if (!isHomePageActive) {
console.log("Returning to homepage, reactivating slideshow");
isHomePageActive = true;
cleanup();
fetchRandomMovie();
}
} else if (isHomePageActive) {
console.log("Leaving homepage, cleaning up slideshow");
if (!isHomePage(newLocation) && isHomePageActive) {
console.log("Leaving homepage, cleaning up slideshow and stopping video");
isHomePageActive = false;
stopBackgroundVideo();
cleanup();
}
if (isHomePage(newLocation) && !isHomePageActive) {
console.log("Returning to homepage, reactivating slideshow");
isHomePageActive = true;
fetchRandomMovie();
}
}
// Check if parent is available and if the iframe is in an embedded environment
@@ -479,6 +663,14 @@ const checkNavigation = () => {
}
};
const stopBackgroundVideo = () => {
if (player && typeof player.stopVideo === 'function') {
player.stopVideo();
player.destroy();
}
player = null;
};
setInterval(checkNavigation, 60);
document.addEventListener('DOMContentLoaded', () => {
@@ -487,14 +679,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (isHomePage(window.top.location.href)) {
isHomePageActive = true;
readCustomList().then(list => {
if (list) {
movieList = list;
// Shuffle the list if it was set by the user
if (setRandomMovie) {
shuffleArray(movieList);
}
currentMovieIndex = 0;
}
if (list) { movieList = list; currentMovieIndex = 0; }
fetchRandomMovie();
});
}