Add support for Thematic Collections to Enhanced Home Screen

This commit is contained in:
HighImKevin
2026-03-14 15:49:47 -04:00
parent e661e315ca
commit fbc3665f76
6 changed files with 588 additions and 150 deletions
+83 -21
View File
@@ -1,27 +1,10 @@
/* Please use the CSS commented below to customize the order of KefinTweaks custom home sections */
/*
[data-custom-section-id="watchlist"] {
order: 60 !important;
}
[data-custom-section-id="popular-tv-networks"] {
order: 61 !important;
}
[data-custom-section-id="new-movies"] {
order: 30 !important;
}
[data-custom-section-id="new-episodes"] {
order: 31 !important;
}
*/
@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@200..700&display=swap');
.sections.homeSectionsContainer {
display: flex;
flex-direction: column;
padding-bottom: 100px;
gap: 1rem;
}
body[data-seasonal-background="true"]:has(.homePage:not(.hide)) #reactRoot:has(.libraryPage:not(.hide) .pageTabContent.is-active[data-index="0"])>.backgroundContainer::after {
@@ -238,6 +221,85 @@ div#discovery-loading-indicator:not(.show)::after {
display: none !important;
}
.homeSectionsContainer > * {
content-visibility: auto;
.homeSectionsContainer .card[data-type="Folder"] .cardScalable > a.cardImageContainer::after {
content: attr(aria-label);
font-family: 'Oswald';
font-size: 1.5rem;
line-height: 1.8rem;
max-width: calc(100% - 2rem);
white-space: pre-wrap;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
text-transform: uppercase;
position: absolute;
top: 1rem;
left: 1rem;
background: none;
}
.homeSectionsContainer .card[data-type="Folder"] .cardScalable > a.cardImageContainer::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(#000000, rgb(0 0 0 / 0%) 50%);
box-shadow: inset 0px 0px 20px 5px black;
}
.layout-mobile .homeSectionsContainer .card[data-type="Folder"] a.cardImageContainer::after {
top: 0.5rem;
left: 0.5rem;
font-size: 1.5rem !important;
line-height: 1.8rem !important;
}
.homeSectionsContainer .card[data-type="Folder"] .cardText {
display: none;
}
[data-card-format="Square"] [data-type="Person"] .cardScalable,
[data-card-format="Square"] [data-type="Actor"] .cardScalable,
[data-card-format="Square"] [data-type="Writer"] .cardScalable,
[data-card-format="Square"] [data-type="Director"] .cardScalable {
border-radius: 50% !important;
overflow: hidden;
}
[data-card-format="Square"] [data-type="Actor"] .cardText {
display: flex;
justify-content: center;
}
.homeSectionsContainer .section0,
.homeSectionsContainer .section1,
.homeSectionsContainer .section2,
.homeSectionsContainer .section3,
.homeSectionsContainer .section4,
.homeSectionsContainer .section5,
.homeSectionsContainer .section6,
.homeSectionsContainer .section7,
.homeSectionsContainer .section8,
.homeSectionsContainer .section9 {
display: none !important;
}
.homeSectionsContainer .card[data-type="TvChannel"] a.cardImageContainer:not([style])::after {
content: attr(aria-label);
font-size: 2rem;
text-wrap: auto;
text-align: center;
}
.emby-scroller-container[data-section-id="liveTv-channels"] .itemsContainer {
gap: 4rem
}
.layout-mobile .emby-scroller-container[data-section-id="liveTv-channels"] .itemsContainer {
gap: 2rem;
}
[data-section-id="upcoming"] .missingIndicator {
display: none;
}
+45 -4
View File
@@ -571,12 +571,12 @@
async function mergeHomeSectionConfigs(defaultSections) {
const performanceStartTime = performance.now();
const kefinHomeScreenConfig = await window.KefinHomeScreen.getConfig();
const kefinSections = await window.KefinHomeScreen.getSections();
const performanceEndTime = performance.now();
const performanceDuration = performanceEndTime - performanceStartTime;
LOG(`Home Screen v3 Get kefin home screen config initialization time: ${performanceDuration.toFixed(2)}ms`);
return kefinHomeScreenConfig.ENABLED_NORMAL_SECTIONS;
return kefinSections.enabledHomeSections;
// Return all enabled sections from HOME_SECTION_GROUPS
const homeSections = flattenSectionGroups(kefinHomeScreenConfig.HOME_SECTION_GROUPS).filter(s => s.enabled === true);
@@ -1306,9 +1306,50 @@
}
}
// Ensure queries array exists
// Static items: normalize and return immediately (no queries, no dataPromise)
if (sectionConfig.items && sectionConfig.items.length > 0) {
const kefinTweaksRoot = window.KefinTweaksConfig?.kefinTweaksRoot || '';
const serverId = ApiClient.serverId();
const normalizeTemplate = (value) => (value || '')
.replace(/\$\{kefinTweaksRoot\}/g, kefinTweaksRoot)
.replace(/\$\{serverId\}/g, serverId);
const normalizedItems = sectionConfig.items.map((item, index) => {
const posterUrl = normalizeTemplate(item.posterUrl);
const thumbUrl = normalizeTemplate(item.thumbUrl);
const squareUrl = normalizeTemplate(item.squareUrl);
const imageUrl = normalizeTemplate(item.imageUrl);
const cardUrl = normalizeTemplate(item.cardUrl);
return {
Name: item.Name,
Id: item.Id || 'static-' + sectionConfig.id + '-' + index,
Type: item.Type || 'Folder',
posterUrl: posterUrl,
thumbUrl: thumbUrl,
squareUrl: squareUrl,
imageUrl: imageUrl,
cardUrl: cardUrl,
CustomFooterText: item.cardFooter || undefined
};
});
const loadSectionTimerEnd = performance.now();
const loadSectionDuration = loadSectionTimerEnd - loadSectionTimerStart;
LOG(`Section ${sectionConfig.id} (static items) loaded for rendering in time: ${loadSectionDuration.toFixed(2)}ms`);
let postProcessedItems = normalizedItems;
if (window.cardBuilder.postProcessItems) {
postProcessedItems = window.cardBuilder.postProcessItems(sectionConfig, normalizedItems);
}
return {
config: sectionConfig,
result: { data: postProcessedItems }
};
}
// Ensure queries array exists when section has no static items
if (!sectionConfig.queries || !Array.isArray(sectionConfig.queries) || sectionConfig.queries.length === 0) {
WARN(`Section ${sectionConfig.id} has no queries array`);
WARN(`Section ${sectionConfig.id} has no queries array and no static items`);
return null;
}