mirror of
https://github.com/IAmParadox27/jellyfin-plugin-media-bar.git
synced 2026-11-08 16:27:14 +01:00
Merge branch 'MakD:main' into main
This commit is contained in:
2 files changed
+126
-94
No files matched your search
@@ -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
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user