Jellyfin Forum
Finimalism [Updated for 10.11.X] - Printable Version

+- Jellyfin Forum (https://forum.jellyfin.org)
+-- Forum: Support (https://forum.jellyfin.org/f-support)
+--- Forum: Themes & Styles (https://forum.jellyfin.org/f-themes-styles)
+--- Thread: Finimalism [Updated for 10.11.X] (/t-finimalism-updated-for-10-11-x)

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22


RE: Finimalism [Updated for 10.11.X] - Blurryface - 2025-11-12

(2025-11-04, 01:02 PM)Ted Hinklater Wrote:
(2025-11-04, 03:48 AM)Blurryface Wrote: Funny enough that is exactly what I did, created the UI folder, copied the 2 into the folder. Replaced the home html with what you had and cleared the cache which then caused that like 2 login I displayed in the photo.

Reminds me of this post, they had saved the files from github incorrectly

What happened to the Finalism and the backgrounds? They no longer show as they did before. I do enjoy the new layout but the background is no longer accessible/visible when drilling down. I did enjoy that before.


RE: Finimalism [Updated for 10.11.X] - Ted Hinklater - 2025-11-12

Do you have this box ticked?

[Image: backdrops.png]

Click your profile icon, click Display, scroll down to Libraries

Edit: I've made this so it goes back to how it was before the animations 

Code:
@import url("https://cdn.jsdelivr.net/gh/tedhinklater/finimalism@latest/finimalism10.11.css");
@import url("https://cdn.jsdelivr.net/gh/tedhinklater/finimalism@main/no-animation.css");



RE: Finimalism [Updated for 10.11.X] - Blurryface - 2025-11-12

(2025-11-12, 04:22 PM)Ted Hinklater Wrote: Do you have this box ticked?

[Image: backdrops.png]

Click your profile icon, click Display, scroll down to Libraries

Edit: I've made this so it goes back to how it was before the animations 

Code:
@import url("https://cdn.jsdelivr.net/gh/tedhinklater/finimalism@latest/finimalism10.11.css");
@import url("https://cdn.jsdelivr.net/gh/tedhinklater/finimalism@main/no-animation.css");

The new edit with the no-animation fixed it back to how it was before. Thanks!


RE: Finimalism [Updated for 10.11.X] - 2020 - 2025-11-14

Few things in the latest update, first
https://github.com/lachlandcp/jellyfin-editors-choice-plugin breaks splitting all the banners.
[Image: Screenshot-2025-11-14-185045.png]

2nd I cant seam to get spotlight to work but editors choice and media bar work fine.

3rd when you add @import url("https://cdn.jsdelivr.net/gh/tedhinklater/finimalism@main/no-animation.css"); the poster covers part of the text.
I have followed the instructions 
1. Download spotlight.html and List.txt. Go to your jellyfin-web folder (C:\Program Files\Jellyfin\Server\jellyfin-web) and create a folder named ui and drop spotlight.html and List.txt in that folder. ui lower case.

2. use Notepad++ for this In the jellyfin-web folder, open the file home-html.RANDOMSTRINGHERE.chunk.js. Replace everything with this code
("use strict";(self.webpackChunk=self.webpackChunk||[]).push([[8372], {
  5939: function(a, e, t) {
    t.r(e),
    e.default =
    <div id="indexPage" style="outline:0" data-role="page" data-dom-cache="true" class="page homePage libraryPage allLibraryPage backdropPage pageWithAbsoluteTabs withTabs" data-backdroptype="movie,series,book">
      <style>
        .featurediframe {width: 97.5vw; height: 23.5em; display: block; border: 0px solid #000; margin: 0 auto; margin-bottom: 0em; margin-top: 1em;}
    @media (max-width: 1000px) and (orientation: portrait) {.featurediframe {height: 25em; margin-bottom: -8em;} }
    @media (max-width: 1000px) and (orientation: landscape) {.featurediframe {height: 26em; margin-bottom: -6em;} }
    @media (max-width: 421px) and (orientation: portrait) {.featurediframe {height: 25em; margin-bottom: -3em;} }
    @media (max-height: 421px) and (orientation: landscape) {.featurediframe {height: 83vh; margin-top: -1em;} }
      </style>
      <div class="tabContent pageTabContent" id="homeTab" data-index="0"><iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe><div class="sections"></div></div><div class="tabContent pageTabContent" id="favoritesTab" data-index="1"> <div class="sections"></div></div></div>
;}}]);
document.addEventListener("DOMContentLoaded", () => {
  const homeTab = document.getElementById("homeTab");
  const spotlightIframe = homeTab.querySelector(".featurediframe");

  const observer = new MutationObserver(() => {
  const isHomeTabActive = homeTab.classList.contains("is-active");
    spotlightIframe.style.display = isHomeTabActive ? "block" : "none";
  });
  observer.observe(homeTab, { attributes: true, attributeFilter: ["class"] });
})Winking-face

3. Save the file. and  tested in private window.


RE: Finimalism [Updated for 10.11.X] - Ted Hinklater - 2025-11-15

Hey 2020 hope you're well,

Can I ask what version of Jellyfin you're on?

Private window might still have a cache issue if you're using a reverse proxy?


RE: Finimalism [Updated for 10.11.X] - 2020 - 2025-11-15

(2025-11-15, 01:24 AM)Ted Hinklater Wrote: Hey 2020 hope you're well,

Can I ask what version of Jellyfin you're on?

Private window might still have a cache issue if you're using a reverse proxy?

Thank you for the reply, I'm good how about yourself. 

I'm on the latest 10.11
I've done it in a new browser in private mode and after a 5mins it started working on the main browser. but I still have the issue with EditorsChoice

is it possible to add the following features directly into your Jellyfin Featured Content Bar all that would be needed I assume would be the extra js file I assume https://github.com/CodeDevMLH/Jellyfin-Featured-Content-Bar/tree/CodeDevMLH's-Version

another question is it possible to make the bar bigger it looks a little small when you compare it to Disney+ and others unless it meant to look like that.


RE: Finimalism [Updated for 10.11.X] - Ted Hinklater - 2025-11-15

(2025-11-15, 02:05 AM)2020 Wrote:
(2025-11-15, 01:24 AM)Ted Hinklater Wrote: Hey 2020 hope you're well,

Can I ask what version of Jellyfin you're on?

Private window might still have a cache issue if you're using a reverse proxy?

Thank you for the reply, I'm good how about yourself. 

I'm on the latest 10.11
I've done it in a new browser in private mode and after a 5mins it started working on the main browser. but I still have the issue with EditorsChoice

is it possible to add the following features directly into your Jellyfin Featured Content Bar all that would be needed I assume would be the extra js file I assume https://github.com/CodeDevMLH/Jellyfin-Featured-Content-Bar/tree/CodeDevMLH's-Version

another question is it possible to make the bar bigger it looks a little small when you compare it to Disney+ and others unless it meant to look like that.

I'm good thanks, a variables file would be a good idea! I'll do that next. Currently the variables are down on line 102 in spotlight.html

Here's a taller version https://github.com/tedhinklater/Jellyfin-Featured-Content-Bar/blob/main/50vh%20version/spotlight.html

[Image: biggerspotlight.gif]

You would also need to change line 7 of home-html.chunk.js to

        .featurediframe {width: 97.5vw; height: 50vh; display: block; border: 0px solid #000; margin: 0 auto; margin-bottom: 0em; margin-top: 1em; }


RE: Finimalism [Updated for 10.11.X] - Ruby - 2025-11-15

Stupid question I apologize, but are the steps any different if I am attempting this in docker on a ugreen nas? I created the ui folder and edited the files to my liking that was simple enough but how do I mount it or if that's the wrong term how do I get jellyfin to see it and use it? I just started messing around with this stuff a couple weeks ago and this theme looks beautiful.


RE: Finimalism [Updated for 10.11.X] - Ted Hinklater - 2025-11-15

(2025-11-15, 04:29 AM)Ruby Wrote: Stupid question I apologize, but are the steps any different if I am attempting this in docker on a ugreen nas? I created the ui folder and edited the files to my liking that was simple enough but how do I mount it or if that's the wrong term how do I get jellyfin to see it and use it? I just started messing around with this stuff a couple weeks ago and this theme looks beautiful.

I think you need to edit the jellyfin container's volume mounts to include

Code:
/localpathtojellyfin/jellyfin/appdata/ui:/usr/share/jellyfin/web/ui:ro

(wherever you put the ui folder in the nas) : (ui folder inside the container) : (read only)

change the first path to fit your filesystem


RE: Finimalism [Updated for 10.11.X] - 2020 - 2025-11-15

(2025-11-15, 03:46 AM)Ted Hinklater Wrote:
(2025-11-15, 02:05 AM)2020 Wrote:
(2025-11-15, 01:24 AM)Ted Hinklater Wrote: Hey 2020 hope you're well,

Can I ask what version of Jellyfin you're on?

Private window might still have a cache issue if you're using a reverse proxy?

Thank you for the reply, I'm good how about yourself. 

I'm on the latest 10.11
I've done it in a new browser in private mode and after a 5mins it started working on the main browser. but I still have the issue with EditorsChoice

is it possible to add the following features directly into your Jellyfin Featured Content Bar all that would be needed I assume would be the extra js file I assume https://github.com/CodeDevMLH/Jellyfin-Featured-Content-Bar/tree/CodeDevMLH's-Version

another question is it possible to make the bar bigger it looks a little small when you compare it to Disney+ and others unless it meant to look like that.

I'm good thanks, a variables file would be a good idea! I'll do that next. Currently the variables are down on line 102 in spotlight.html

Here's a taller version https://github.com/tedhinklater/Jellyfin-Featured-Content-Bar/blob/main/50vh%20version/spotlight.html

[Image: biggerspotlight.gif]

You would also need to change line 7 of home-html.chunk.js to

        .featurediframe {width: 97.5vw; height: 50vh; display: block; border: 0px solid #000; margin: 0 auto; margin-bottom: 0em; margin-top: 1em; }


Good thanks, I just making the changes, just wounding since I don't code, could I take code from https://github.com/CodeDevMLH/Jellyfin-Featured-Content-Bar/blob/CodeDevMLH's-Version/script.js    and add it under 102 in spotlight.html example the following, would it work ?  

Code:
let title = 'Spotlight'; // Title of the slideshow TBD
let listFileName = 'list.txt'; // Name of the file containing the list of movie IDs
let moviesSeriesBoth = 3; // 1 for movies, 2 for series, 3 for both
let shuffleInterval = 15000; // Time in milliseconds before the next slide is shown, unless trailer is playing
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 true to place the slideshow title above the banner
let disableTrailerControls = false; // Set to false to enable trailer controls
let setMutedHover = 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 skipIntro = true; // Set to true to skip the intro segment of the trailer
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 startTrailerMuted = true; // Default value true; set to false to start the video unmuted

// Seasonal lists configuration
let useSeasonalLists = false; // Set to true to enable automatic seasonal list switching
const seasonalLists = {
    spring: 'spring_list.txt',        // spring (march-may)
    summer: 'summer_list.txt',        // summer (june-august)
    autumn: 'autumn_list.txt',        // autumn (september-november)
    winter: 'winter_list.txt',        // winter (december-february)
    newyear: 'newyear_list.txt',      // new year (1.-7. januar)
    valentine: 'valentine_list.txt',  // valentines day (10.-20. februar)
    easter: 'easter_list.txt',        // easter (variable dates, March-April)
    halloween: 'halloween_list.txt'  // halloween (20.-31. october)
};


// Language specific strings
// currently implemented: en, de, fr, es, it, pl, nl
const seasonTerms = ["Season", "Staffel", "Saison", "Temporada", "Stagione", "Sezon", "Seizoen"];
const seasonsTerms = ["Seasons", "Staffeln", "Saisons", "Temporadas", "Stagioni", "Sezony", "Seizoenen"];
const unknownSeasonsTerms = ["Unknown seasons", "Unbekannte Staffeln","Saisons inconnues", "Temporadas desconocidas", "Stagioni sconosciute", "Nieznane sezony", "Onbekende seizoenen"];
const movieTerms = ["Movie", "Film", "Film", "Película", "Film", "Film", "Film"];
const moviesTerms = ["Movies", "Filme", "Films", "Películas", "Film", "Filmy", "Films"];
const unknownMoviesTerms = ["Unknown movies", "Unbekannte Filme", "Films inconnus", "Películas desconocidas", "Film sconosciuti", "Nieznane filmy", "Onbekende films"];
const unknownYearTerms = ["Unknown year", "Unbekanntes Jahr", "Année inconnue", "Año desconocido", "Anno sconosciuto", "Nieznany rok", "Onbekend jaar"];
const unknownRuntimeTerms = ["Unknown Runtime", "Unbekannte Länge", "Durée inconnue", "Duración desconocida", "Durata sconosciuta", "Nieznany czas trwania", "Onbekende duur"];

// get the Jellyfin credentials from the local storage (api token and user id)
const getJellyfinCredentials = () => {
    const jellyfinCreds = localStorage.getItem("jellyfin_credentials");
 
    try {
      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 { token, userId } = getJellyfinCredentials();


// variables
let isChangingSlide = false, player = null, slideChangeTimeout = null, isHomePageActive = false;
let currentLocation = window.top.location.href;
let movieList = [], currentMovieIndex = 0;
let previousMovies = [];
let forwardMovies = [];
let monitorOutroInterval = null; // Global interval variable to monitor the outro segment of the trailer
let isMuted = startTrailerMuted; userInteracted = false;
const baseBackdropOverlapVW = 11.4;
let activeTrailerLayout = null;


// Get the current browser language
const getBrowserLanguage = () => {
    const language = navigator.language || navigator.userLanguage;
    return language.split('-')[0]; // Return the language code (e.g., 'en', 'de')
};
const browserLanguage = getBrowserLanguage();

// set corresponding language index
const setLanguage = (language) => {
    switch (language) {
        case 'de':
            return 1;
        case 'fr':
            return 2;
        case 'es':
            return 3;
        case 'it':
            return 4;
        case 'pl':
            return 5;
        case 'nl':
            return 6;
        default:
            return 0;
    }
};

const languageIndex = setLanguage(browserLanguage);

// Seasonal list detection
const getCurrentSeason = () => {
    if (!useSeasonalLists) {
        return null;
    }
   
    const now = new Date();
    const month = now.getMonth() + 1; // 1-12
    const day = now.getDate();
   
    // Special events (take precedence over seasons)
    // new year: 1-7 january
    if (month === 1 && day <= 7) return 'newyear';
   
    // valentines day: 10-20 february
    if (month === 2 && day >= 10 && day <= 20) return 'valentine';
   
    // halloween: 20-31 cotober
    if (month === 10 && day >= 20) return 'halloween';
   
    // Easter calculation (simplified - around March-April)
    const easterStart = getEasterPeriod(now.getFullYear());
    if (isInEasterPeriod(now, easterStart)) return 'easter';
   
    // Regular seasons
    if (month >= 3 && month <= 5) return 'spring';    // march-may
    if (month >= 6 && month <= 8) return 'summer';    // june-august
    if (month >= 9 && month <= 11) return 'autumn';    // september-november
    if (month === 12 || month <= 2) return 'winter';  // december-february
   
    return null;
};

// Simplified Easter calculation (Western Easter)
const getEasterPeriod = (year) => {
    const a = year % 19;
    const b = Math.floor(year / 100);
    const c = year % 100;
    const d = Math.floor(b / 4);
    const e = b % 4;
    const f = Math.floor((b + 8) / 25);
    const g = Math.floor((b - f + 1) / 3);
    const h = (19 * a + b - d - g + 15) % 30;
    const i = Math.floor(c / 4);
    const k = c % 4;
    const l = (32 + 2 * e + 2 * i - h - k) % 7;
    const m = Math.floor((a + 11 * h + 22 * l) / 451);
    const month = Math.floor((h + l - 7 * m + 114) / 31);
    const day = ((h + l - 7 * m + 114) % 31) + 1;
   
    return { month, day };
};

// Check if current date is in Easter period (2 weeks around Easter)
const isInEasterPeriod = (currentDate, easter) => {
    const easterDate = new Date(currentDate.getFullYear(), easter.month - 1, easter.day);
    const timeDiff = Math.abs(currentDate.getTime() - easterDate.getTime());
    const daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24));
    return daysDiff <= 7; // 1 week before and after Easter
};

// Get the appropriate list filename based on season
const getSeasonalListFileName = async () => {
    const season = getCurrentSeason();
    if (!season || !seasonalLists[season]) {
        console.log('Using default list:', listFileName);
        return listFileName;
    }
   
    // Check if seasonal file exists
    const seasonalFile = seasonalLists[season];
    try {
        const response = await fetch(seasonalFile + '?' + new Date().getTime(), { method: 'HEAD' });
        if (response.ok) {
            console.log(`Using seasonal list for ${season}:`, seasonalFile);
            return seasonalFile;
        } else {
            console.warn(`Seasonal file ${seasonalFile} not found, falling back to default list:`, listFileName);
            return listFileName;
        }
    } catch (error) {
        console.warn(`Error checking seasonal file ${seasonalFile}:`, error.message, '- falling back to default list:', listFileName);
        return listFileName;
    }
};

// Get SponsorBlock-Data for the outro/intro segment of the trailer
const fetchSponsorBlockData = async (videoId) => {
    try {
        const response = await fetch(`https://sponsor.ajay.app/api/skipSegments?videoID=${videoId}&categories=["intro","outro"]`);
        const segments = await response.json();

        let intro = null;
        let outro = null;

        segments.forEach(segment => {
            if (segment.category === "intro" && Array.isArray(segment.segment)) {
                intro = segment.segment; // [start, end] of the intro
            } else if (segment.category === "outro" && Array.isArray(segment.segment)) {
                outro = segment.segment; // [start, end] of the outro
            }
        });
        return { intro, outro };
    } catch (error) {
        console.error('Error fetching SponsorBlock data:', error);
        return { intro: null, outro: null };

    }
};
  


or have  have a separate file .js under  and add in spotlight.html  
<script src="https://www.youtube.com/iframe_api"></script>
    <script src="script.js"></script>