EMBY显示EXTRAFANART剧照
1.前期准备
实现方式
1.使用油猴插件 + JS
2.Emby安装CustomCssJS Provider插件 + JS
CustomCssJS Provider项目地址:https://github.com/Shurelol/Emby.CustomCssJS
3.直接将JS 代码注入到 index.html (方法略)
JS代码:(来自 MICIMO 丨 主页)
// ==UserScript==
// @name Jellyfin/Emby extrafanart 显示
// @namespace https://www.micimo.love/
// @version 1.0.3
// @description Jellyfin/Emby 显示剧照
// @author MICIMO
// @updateURL https://www.micimo.love/jellyfin/extrafanart.js
// @include http*://*:8096/*
// ==/UserScript==
;(function () {
'use strict';
let startImageIndex = 0;
let endImageIndex = 0;
let currentZoomedImageIndex = -1;
let itemId = null;
const imageContainer = createImageContainer();
const zoomedMask = createZoomedMask();
document.body.appendChild(imageContainer);
document.body.appendChild(zoomedMask);
function getCurrentItemId() {
return location.hash.match(/id\=(\w+)/)?.[1] ?? null;
}
function getBackgroundImageSrc(index) {
const currentItemId = getCurrentItemId();
return currentItemId && `${location.origin}/Items/${currentItemId}/Images/Backdrop/${index}?maxWidth=1280`;
}
function createImageContainer() {
const container = document.createElement('div');
container.id = 'jv-image-container';
container.style.display = 'grid';
container.style.gridTemplateColumns = 'repeat(5, 1fr)'; // 每行 5 列
container.style.gap = '10px'; // 图片之间的间距
return container;
}
function createZoomedMask() {
const mask = document.createElement('div');
mask.id = 'jv-zoom-mask';
mask.style.position = 'fixed';
mask.style.left = '0';
mask.style.right = '0';
mask.style.top = '0';
mask.style.bottom = '0';
mask.style.background = 'rgba(0, 0, 0, 0.8)';
mask.style.display = 'none';
mask.style.justifyContent = 'center';
mask.style.alignItems = 'center';
mask.style.zIndex = '1100';
mask.style.cursor = 'zoom-out';
const zoomedImage = document.createElement('img');
zoomedImage.id = 'jv-zoom-img';
zoomedImage.style.maxWidth = '90%';
zoomedImage.style.maxHeight = '90%';
zoomedImage.style.borderRadius = '10px';
mask.appendChild(zoomedImage);
mask.onclick = hideZoomedMask;
return mask;
}
function showZoomedMask(imageSrc) {
const zoomedImage = document.getElementById('jv-zoom-img');
zoomedImage.src = imageSrc;
const zoomedMask = document.getElementById('jv-zoom-mask');
zoomedMask.style.display = 'flex';
document.body.style.overflow = 'hidden'; // 防止背景滚动
}
function hideZoomedMask() {
const zoomedMask = document.getElementById('jv-zoom-mask');
zoomedMask.style.display = 'none';
document.body.style.overflow = ''; // 恢复背景滚动
}
function createImageElement(index) {
const imageSrc = getBackgroundImageSrc(index);
const wrapper = document.createElement('div');
wrapper.className = 'jv-image-wrapper';
wrapper.style.width = '100%';
wrapper.style.height = '0';
wrapper.style.paddingBottom = '75%'; // 保持 4:3 比例框
wrapper.style.position = 'relative';
wrapper.style.overflow = 'hidden';
wrapper.style.boxSizing = 'border-box';
const imageElement = document.createElement('img');
imageElement.src = imageSrc;
imageElement.className = 'jv-image';
imageElement.style.position = 'absolute';
imageElement.style.top = '0';
imageElement.style.left = '0';
imageElement.style.width = '100%';
imageElement.style.height = '100%';
imageElement.style.objectFit = 'cover'; // 图片自适应填充网格
imageElement.style.cursor = 'zoom-in';
imageElement.style.userSelect = 'none';
imageElement.style.borderRadius = '10px';
imageElement.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
imageElement.onclick = function () {
showZoomedMask(imageSrc);
};
wrapper.appendChild(imageElement);
return wrapper;
}
function appendImagesToContainer(imageCount) {
const imageFragment = document.createDocumentFragment();
for (let index = startImageIndex; index <= imageCount; index++) {
const imageElement = createImageElement(index);
imageFragment.appendChild(imageElement);
}
imageContainer.appendChild(imageFragment);
}
function showContainer(imageCount) {
if (imageCount > 0) {
const primaryDiv =
document.querySelector('#itemDetailPage:not(.hide) #castCollapsible') || document.querySelector('.itemView:not(.hide) .peopleSection');
if (primaryDiv) {
imageContainer.style.display = 'grid';
primaryDiv.insertAdjacentElement('afterend', imageContainer);
}
}
}
function isDetailsPage() {
return location.hash.includes('/details?id=') || location.hash.includes('/item?id=');
}
async function getEndImageIndex() {
let left = startImageIndex;
let right = startImageIndex + 20;
let found = false;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
const newSrc = getBackgroundImageSrc(mid);
try {
const response = await fetch(newSrc, { method: 'HEAD' });
if (!response.ok) throw new Error('Image not found.');
found = true;
left = mid + 1;
} catch (error) {
right = mid - 1;
}
}
return found ? right : 0;
}
async function loadImages() {
if (!isDetailsPage()) return;
const currentItemId = getCurrentItemId();
if (!currentItemId) return;
if (itemId !== currentItemId) {
endImageIndex = await getEndImageIndex();
}
itemId = currentItemId;
imageContainer.innerHTML = '';
appendImagesToContainer(endImageIndex);
showContainer(endImageIndex);
}
document.addEventListener('viewshow', () => setTimeout(loadImages));
})();
2.实施
1.油猴方案:
直接将代码导入到插件即可
2.CustomCssJS Provider方案
Mac端在应用程序中显示包内容即可
将JS 代码 添加到插件中
在此处将JS 启用即可实现剧照显示
(注:目前无法做到自适应分辨率显示图片列数,因此手机推荐4-5列,PC推荐10列)
JS 代码中在此处修改图片列数
function createImageContainer() {
const container = document.createElement('div');
container.id = 'jv-image-container';
container.style.display = 'grid';
container.style.gridTemplateColumns = 'repeat(5, 1fr)'; // 每行 5 列
container.style.gap = '10px'; // 图片之间的间距
return container;
}