chrissix666-lab

Tree / Self Hosting / Movies & TV Shows / Jellyfin / Library View

Hover Preview Tiles

A hover preview that fans out posters, seasons and chapters.

Status
Green · Published
Type
script
Hours
about 10
Tokens
about 4.7 M

An extended version of malte9799's Collection Preview, and all credit for the idea stays with him.

GitHub

In this article
  1. Starting from a very good script
  2. Everything that can fan out now
  3. Seven card types, one global block
  4. The workaround behind direct play
  5. Also part of Essentials

Starting from a very good script

This is a fork and further development of Jellyfin – Collection Preview by malte9799, published as a GitHub Gist. All credit for the idea goes to him. His script fans out the posters of a collection above its card like a hand of cards. The preview follows your mouse, the nearest poster turns upright and shows its title, and a click opens that item. It is plain client-side JavaScript, and his three commits are still at the start of my repository.

The gap was in two places. The opening animation was not always consistent: sometimes the tiles just popped in. And it only covered collections. The goal was to calm the interaction down and bring the same effect to every kind of card.

Everything that can fan out now

Containers show their children. A collection shows its movies, a series its seasons, a season its episodes, and a home video folder its contents, which can be a mix of sub-folders and video files.

A collection opens like a hand of cards, and the tile under the pointer turns upright.

Single videos show their own chapters. On a movie, an episode or a home video file, the fan is built from Jellyfin's real chapter images. A click on a tile's play button jumps straight into the video at that timestamp. This is a completely different data path, since a video has no children.

Three additions change how it feels:

  • centerHoverOnly opens the fan only when the pointer is on the play button in the middle of the card, not anywhere on it.
  • subtileFocusPlayButton puts a small play button on the focused tile.
  • Single wrapper folders are skipped. If every home video folder holds one fixed subfolder called "videos", the fan jumps past it. With one folder like "videos Instagram" a fuzzy match still skips it, and with two candidates both stay visible.

A wrapper folder is my own habit for home videos: the outer folder can have any name, but one inner folder always has the same name and holds nothing but the video files. Cover art, photos, backups and leftover cuts sit next to it, so scripts always know where the videos are. Skipping works both when the fan opens and when you click into a folder tile.

The fan itself still works the way malte9799 designed it. The tile nearest the pointer rises, turns upright and shows its title with a highlight ring, while its neighbours part around it. A click on a tile opens that item. Everything is client-side, and nothing changes on the server.

Seven card types, one global block

The script has seven config blocks in two families, and one global block on top.

BlockCard typeWhat fans out
CONFIG_SETSCollectionsThe movies of the collection
CONFIG_TVSHOWSSeriesThe seasons
CONFIG_TVSEASONSSeasonsThe episodes, with the tile shape detected from their thumbnails
CONFIG_HOMEVIDEOSHome video foldersSub-folders and video files
CONFIG_MOVIESMoviesThe movie's chapters
CONFIG_EPISODESEpisodesThe episode's chapters
CONFIG_HOMEVIDEOFILESHome video filesThe file's chapters

Shared settings

Most fields repeat in the blocks with the same meaning, though not every block uses all of them. The defaults below are the usual ones.

SettingStarts atWhat it does
enabledtrueMaster switch for this card type
hoverDelay220Milliseconds before the fan opens on a full card hover
maxPosters100Most tiles at once, and the rest collapse into a "+N more" tile (33 for home videos)
posterWidth110Tile width in pixels for portrait content
posterHeight165Tile height in pixels for widescreen content, used instead of the width
maxSpreadDeg110Maximum total angle of the fan
degPerPoster16Extra angle per tile, up to that ceiling
lift130How far the fan rises above the card, in pixels
overlap0.5How much of a tile stays inside the card, 0.5 is half in, half out
hoverPushDeg11How far neighbours move apart from the focused tile
hoverSpreadScale1.06Widens the fan slightly while the pointer tracks across it
smoothing0.18How closely the fan follows the mouse, higher is snappier
staggerMs28Delay between the opening of each tile
closeMs220Length of the closing animation
scrollSettleMs160Scroll silence needed before the preview may reopen
sortBy"PremiereDate"Sort field, from SortName, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, ProductionYear, Runtime and Random to Budget, Revenue and the album fields
sortOrder"Ascending"Ascending or Descending
centerHoverOnlytrueOpens only on the play button in the middle of the card
centerHoverOnlyDelay1500Delay for that mode, replacing hoverDelay
centerHoverZoneRatio0.22Size of an invisible round zone on cards with no play button, such as folders
subtileFocusPlayButtontruePlay button on the focused tile
subtilePlayButtonSizeRatio0.4Its diameter as a fraction of the tile
directPlayButtonTimeoutMs4000How long to wait for the real Play button on the target page
directPlayRevealDelayMs500How long the black overlay stays after the click
autoDetectAspectRatiotrueWorks out the tile shape from the fetched images, on seasons and home videos
aspectRatioCandidates"2/3, 1/1, 4/3, 16/9"The clean shapes a detected average snaps to
fallbackAspectRatio"2/3"The shape used without detection, and "16/9" for chapters

In centre hover mode the fan closes as soon as the pointer leaves the zone, with one exception: moving onto the open tiles keeps it open, otherwise you could never reach them. Chapter blocks have no sorting, because chapters always come in order.

Two details are easy to miss. Seasons and home videos work out their tile shape from the fetched images and snap the average to a clean ratio such as 2/3 or 16/9, so square covers and landscape thumbnails are not forced into poster shape. Scrolling counts as well: the preview waits for a short silence before it may reopen, so moving through a long library does not set off a fan on every card the pointer crosses.

Settings of single card types

  • Collections: showYearInTitle shows each tile as name and year.
  • Series: skipSingleSeason stops a pointless fan with one tile, and singleSeasonShowEpisodesInstead shows that season's episodes instead, using the season block's settings. Both are on.
  • Seasons: showSeasonEpisodeNumberInTitle puts the season and episode number in front of the title.
  • Home video sorting: with separateSortForFoldersAndVideos, off by default, folderSortBy, folderSortOrder, videoSortBy and videoSortOrder apply separately, and mixedOrderMode chooses foldersFirst or videosFirst.
  • Wrapper folders: skipFolderLayerNamesEnabled is the master switch, skipFolderLayerNames lists the names, here videos, skipFolderLayerExactMatchEnabled and skipFolderLayerFuzzyFallbackEnabled allow the two kinds of match, and maxFolderSkipDepth of 1 is a safety cap against circular structures.

Global settings

SettingStarts atWhat it does
enablePreviewsOnHometruePreview on the Home page, for every card type
enablePreviewsOnSimilarSectiontruePreview in "More Like This"
enablePreviewsOnNextUptruePreview in "Next Up" for TV shows

If the fan feels too busy, keep centerHoverOnly on, which is how it ships. If you want it faster, lower centerHoverOnlyDelay first, and leave the geometry alone until the timing feels right. It also respects the browser's reduced motion setting.

The fan open with the focused poster tilted to the left of the others.
The fan follows the pointer: the focused tile rises and its neighbours part around it.

The workaround behind direct play

Direct play from a tile was the real hurdle. After a lot of experimenting and testing I found no other way than to go to the item's detail page and trigger its Play button by script. A black fade covers the step, so it is barely noticeable, or ideally not noticeable at all. The README says so openly.

A trick like that has consequences, and most of them came out in a later audit by Claude sessions, who also did the port to Jellyfin 12. The black overlay could stay on screen when playback did not start, so a timeout now takes it away. Chapter play ran into saved progress: the Play button on a detail page can mean Resume, so the chapter could lose to an old position. An option that reset the position sometimes marked an episode as fully watched, so that option went away.

Growing beyond collections was the other big step. TV shows and home videos were first only planned in the README. Once they were in, the script had nearly tripled in size. It now has about 2,450 lines, and it still stands on malte9799's idea.

Also part of Essentials

This script now also runs inside my Essentials plugin, with its settings on a normal dashboard page instead of values at the top of the code. On its own, through the JavaScript Injector, it works as before.

Connected

Next: Details Extended