Merge branch 'MakD:main' into main

This commit is contained in:
Zach Ross-Clyne authored and GitHub committed 2025-02-12 23:36:04 +00:00
commit b987cc3f70
2 files changed
+126 -94

No files matched your search

+37 -57
View File
@@ -51,63 +51,43 @@ Here I present my version of the same with some code improvements, loading optim
2. Search for `</body></html>`
3. Just before the `</body`, plug the below code
```
<script>
(function () {
// List of CSS selectors for Home buttons
const buttonSelectors = [
".headerHomeButton.barsMenuButton",
".css-17c09up",
".mainDrawer-scrollContainer > a:nth-child(2)"
];
<script>
function saveCredentialsToSessionStorage(credentials) {
try {
sessionStorage.setItem(
"json-credentials",
JSON.stringify(credentials)
);
console.log("Credentials saved to sessionStorage.");
} catch (error) {
console.error("Error saving credentials:", error);
}
}
function saveApiKey(apiKey) {
try {
sessionStorage.setItem("api-key", apiKey);
console.log("API key saved to sessionStorage.");
} catch (error) {
console.error("Error saving API key:", error);
}
}
(function () {
var originalConsoleLog = console.log;
console.log = function (message) {
originalConsoleLog.apply(console, arguments);
if (
typeof message === "string" &&
message.startsWith("Stored JSON credentials:")
) {
try {
var jsonString = message.substring(
"Stored JSON credentials: ".length
);
var credentials = JSON.parse(jsonString);
saveCredentialsToSessionStorage(credentials);
} catch (error) {
console.error("Error parsing credentials:", error);
}
// Polling interval to check for buttons
const intervalId = setInterval(function () {
buttonSelectors.forEach(selector => {
// Try to find the button
const homeButton = document.querySelector(selector);
// If the button is found
if (homeButton) {
// Attach the click event listener if not already added
if (!homeButton.hasAttribute("data-home-handler")) {
homeButton.addEventListener("click", function (event) {
event.preventDefault(); // Prevent default behavior if necessary
window.location.href = "/web/index.html#/home.html";
});
// Mark the button as handled
homeButton.setAttribute("data-home-handler", "true");
}
if (
typeof message === "string" &&
message.startsWith("opening web socket with url:")
) {
try {
var url = message.split("url:")[1].trim();
var urlParams = new URL(url).searchParams;
var apiKey = urlParams.get("api_key");
if (apiKey) {
saveApiKey(apiKey);
}
} catch (error) {
console.error("Error extracting API key:", error);
}
}
};
})();
</script>
}
});
// Stop polling if all buttons are found
if (buttonSelectors.every(selector => document.querySelector(selector))) {
clearInterval(intervalId);
}
}, 100); // Check every 100ms
})();
</script>
<link rel="preload" href="/web/avatars/slideshowpure.css" as="style" />
<link rel="stylesheet" href="/web/avatars/slideshowpure.css" />
<script defer src="/web/avatars/slideshowpure.js"></script>
@@ -129,8 +109,8 @@ Here I present my version of the same with some code improvements, loading optim
<summary>main.jellyfin.bundle.js</summary>
1. Similarly, search for `main.jellyfin.bundle.js` in the `jellyfin-web` directory. Open it with any code editor with administrator privileges.
2. Search for `this.playbackManager = e;`
3. Right after the `;`, paste the code block `window.PlaybackManager = this.playbackManager;console.log("PlaybackManager is now globally available:", window.PlaybackManager);`
2. Search for `this.playbackManager = e,`
3. Right after the `,`, paste the code block `window.PlaybackManager = this.playbackManager;console.log("PlaybackManager is now globally available:", window.PlaybackManager);`
</details>
+89 -37
View File
@@ -1,34 +1,73 @@
/**
* Credentials for a given server
* @typedef {Object} ServerCredentials
* @property {string} Id
* @property {string} AccessToken
*/
/**
* Credentials
* @typedef {Object} Credentials
* @property {ServerCredentials[]} Servers
*/
/**
* Fetches the credentials inside of localstorage
* @returns {{token: string, userId: string}} Credential resource
*/
const getJellyfinCredentials = () => {
const jellyfinCreds = localStorage.getItem("jellyfin_credentials");
try {
/**
* @type {Credentials}
*/
const serverCredentials = JSON.parse(jellyfinCreds);
const firstServer = serverCredentials.Servers[0];
if (!firstServer) {
console.error("Could not find credentials for the client");
return;
}
return { token: firstServer.AccessToken, userId: firstServer.UserId };
} catch (e) {
console.error("Could not parse jellyfin credentials", e);
}
};
const slidesInit = () => {
if (window.hasInitializedSlideshow) {
console.log("Slideshow already initialized. Skipping re-init.");
return;
}
window.hasInitializedSlideshow = true;
const shuffleInterval = 8000;
let isTransitioning = false;
const listFileName = `${window.location.origin}/web/avatars/list.txt`;
const jsonCredentials = sessionStorage.getItem("json-credentials");
let userId = null;
let token = null;
//const deviceId = localStorage.getItem("_deviceId2");
//console.log(deviceId);
if (jsonCredentials) {
const credentials = JSON.parse(jsonCredentials);
userId = credentials.Servers[0].UserId;
token = credentials.Servers[0].AccessToken;
}
const { token, userId } = getJellyfinCredentials();
const shuffleArray = (array) => array.sort(() => Math.random() - 0.5);
const truncateText = (element, maxLength) => {
let text = element.innerText;
if (text.length > maxLength)
element.innerText = text.substring(0, maxLength) + "...";
};
const createSlideElement = (item, title) => {
const itemId = item.Id;
const plot = item.Overview || "No overview available";
const rating = item.CommunityRating;
const tomato = item.CriticRating;
const criticRating = item.CriticRating;
const runtime = item.RunTimeTicks;
const genre = item.Genres;
const genresArray = item.Genres;
const youtube = item.RemoteTrailers;
const age = item.OfficialRating;
const date = item.PremiereDate;
const season = item.ChildCount;
function createSeparator() {
const separatorHtml =
'<i class="material-icons radio_button_off separator-icon"></i>';
@@ -46,33 +85,43 @@ const slidesInit = () => {
backdrop.className = "backdrop";
backdrop.src = `${window.location.origin}/Items/${itemId}/Images/Backdrop/0`;
backdrop.alt = "Backdrop";
const backdropOverlay = document.createElement("div");
backdropOverlay.className = "backdrop-overlay";
const backdropContainer = document.createElement("div");
backdropContainer.className = "backdrop-container";
backdropContainer.appendChild(backdrop);
backdropContainer.appendChild(backdropOverlay);
const logo = document.createElement("img");
logo.className = "logo";
logo.src = `${window.location.origin}/Items/${itemId}/Images/Logo`;
logo.alt = "Logo";
const logoContainer = document.createElement("div");
logoContainer.className = "logo-container";
logoContainer.appendChild(logo);
const featuredContent = document.createElement("div");
featuredContent.className = "featured-content";
featuredContent.textContent = title;
const plotElement = document.createElement("div");
plotElement.className = "plot";
plotElement.textContent = plot;
truncateText(plotElement, 360);
const plotContainer = document.createElement("div");
plotContainer.className = "plot-container";
plotContainer.appendChild(plotElement);
const gradientOverlay = document.createElement("div");
gradientOverlay.className = "gradient-overlay";
const runTimeElement = document.createElement("div");
runTimeElement.className = "runTime";
if (season === undefined) {
const milliseconds = runtime / 10000;
const currentTime = new Date();
@@ -83,8 +132,10 @@ const slidesInit = () => {
} else {
runTimeElement.textContent = `${season} Season${season > 1 ? "s" : ""}`;
}
const ratingTest = document.createElement("div");
ratingTest.className = "rating-value";
const imdbLogo = document.createElement("img");
imdbLogo.className = "imdb-logo";
imdbLogo.src =
@@ -93,6 +144,7 @@ const slidesInit = () => {
imdbLogo.style.width = "30px";
imdbLogo.style.height = "30px";
ratingTest.appendChild(imdbLogo);
if (typeof rating === "number") {
const formattedRating = rating.toFixed(1);
ratingTest.innerHTML += `${formattedRating} ⭐`;
@@ -101,21 +153,25 @@ const slidesInit = () => {
ratingTest.innerHTML += `N/A ⭐`;
}
ratingTest.appendChild(createSeparator());
const tomatoRatingDiv = document.createElement("div");
tomatoRatingDiv.className = "tomato-rating";
const criticRating = item.CriticRating;
const tomatoLogo = document.createElement("img");
tomatoLogo.className = "tomato-logo";
const criticLogo = document.createElement("img");
criticLogo.className = "critic-logo";
tomatoLogo.src =
"https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Rotten_Tomatoes_positive_audience.svg/1920px-Rotten_Tomatoes_positive_audience.svg.png";
const criticLogo = document.createElement("img");
criticLogo.className = "critic-logo";
let valueElement;
if (typeof criticRating === "number") {
valueElement = document.createTextNode(`${criticRating}% `);
} else {
valueElement = document.createTextNode(`N/A `);
}
if (criticRating === undefined || criticRating <= 59) {
criticLogo.src =
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/52/Rotten_Tomatoes_rotten.svg/1024px-Rotten_Tomatoes_rotten.svg.png";
@@ -128,22 +184,28 @@ const slidesInit = () => {
tomatoLogo.style.height = "17px";
criticLogo.style.width = "15px";
criticLogo.style.height = "15px";
tomatoRatingDiv.appendChild(tomatoLogo);
tomatoRatingDiv.appendChild(valueElement);
tomatoRatingDiv.appendChild(criticLogo);
tomatoRatingDiv.appendChild(createSeparator());
const ageRatingDiv = document.createElement("div");
ageRatingDiv.className = "age-rating";
if (item.OfficialRating) {
ageRatingDiv.innerHTML = `${item.OfficialRating}`;
} else {
ageRatingDiv.innerHTML = `N/A`;
}
const calender = "📅";
const premiereDate = document.createElement("div");
premiereDate.className = "date";
const year = date ? new Date(date).getFullYear() : "N/A";
premiereDate.textContent = isNaN(year) ? "N/A" : year;
ratingTest.appendChild(tomatoRatingDiv);
ratingTest.appendChild(document.createTextNode(calender));
ratingTest.appendChild(premiereDate);
@@ -151,7 +213,7 @@ const slidesInit = () => {
ratingTest.appendChild(ageRatingDiv);
ratingTest.appendChild(createSeparator());
ratingTest.appendChild(runTimeElement);
const genresArray = item.Genres;
function parseGenres(genresArray) {
if (genresArray && genresArray.length > 0) {
return genresArray.slice(0, 3).join(" 🔹 ");
@@ -159,9 +221,11 @@ const slidesInit = () => {
return "No Genre Available";
}
}
const genreElement = document.createElement("div");
genreElement.className = "genre";
genreElement.innerHTML = parseGenres(genresArray);
const infoContainer = document.createElement("div");
infoContainer.className = "info-container";
infoContainer.appendChild(ratingTest);
@@ -271,14 +335,11 @@ const slidesInit = () => {
}
};
const fetchItemDetails = async (itemId) => {
const response = await fetch(
`${window.location.origin}/Users/${userId}/Items/${itemId}`,
{
headers: {
Authorization: `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"`,
},
}
);
const response = await fetch(`${window.location.origin}/Items/${itemId}`, {
headers: {
Authorization: `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"`,
},
});
const item = await response.json();
console.log("Item Title:", item.Name);
return item;
@@ -302,7 +363,7 @@ const slidesInit = () => {
const fetchItemsFromServer = async () => {
try {
const response = await fetch(
`${window.location.origin}/Users/${userId}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&isPlayed=False&Limit=150`,
`${window.location.origin}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&SortBy=random&isPlayed=False&Limit=500`,
{
headers: {
Authorization: `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"`,
@@ -352,6 +413,8 @@ const slidesInit = () => {
const dots = document.querySelectorAll(".dot");
slides.forEach((slide, i) => {
if (i === index) {
console.log(`Showing slide ${index}`); // Debugging
slide.style.removeProperty("display");
slide.style.display = "block";
slide.offsetHeight;
slide.style.opacity = "1";
@@ -363,6 +426,7 @@ const slidesInit = () => {
slide.style.display = "none";
}, 500);
}
document.getElementById("slides-container").offsetHeight;
});
dots.forEach((dot, i) => {
dot.classList.toggle("active", i === index % 5);
@@ -471,15 +535,3 @@ const slidesInit = () => {
};
initializeSlides();
};
document.addEventListener("DOMContentLoaded", () => {
document.body.addEventListener("click", (event) => {
const homeButton = event.target.matches(
".headerHomeButton, .css-17c09up, .mainDrawer-scrollContainer > a:nth-child(2)"
);
if (homeButton) {
event.preventDefault();
window.location.href = "/web/index.html#/home.html";
setTimeout(handleHomeNavigation, 300);
}
});
});