Anotha one!

This commit is contained in:
HighImKevin
2026-09-15 01:21:54 -04:00
parent dc0cda699d
commit 04d3ef0e92
35 changed files with 7577 additions and 2002 deletions
+132 -5
View File
@@ -11,11 +11,13 @@
const QUERY_WIZARD_STEPS = ['type', 'parent', 'query', 'settings', 'appearance', 'confirm'];
const CUSTOM_WIZARD_STEPS = ['type', 'customItems', 'settings', 'appearance', 'confirm'];
const EXTERNAL_WIZARD_STEPS = ['type', 'externalList', 'settings', 'appearance', 'confirm'];
const WIZARD_STEP_LABELS = {
type: 'Type',
parent: 'Selection',
query: 'Query',
customItems: 'Create Items',
externalList: 'External List',
appearance: 'Appearance',
settings: 'Settings',
confirm: 'Confirm'
@@ -28,6 +30,13 @@
icon: 'widgets'
};
const EXTERNAL_TYPE = {
type: 'External',
label: 'External List Section',
description: 'Match an MDBList (or compatible) URL against your Jellyfin library by IMDb/TMDB provider IDs. Requires an MDBList API key in Home Screen General settings.',
icon: 'link'
};
const CUSTOM_IMAGE_FIELDS = [
{ key: 'posterUrl', label: 'Poster URL', required: true },
{ key: 'thumbUrl', label: 'Thumb URL' },
@@ -77,13 +86,19 @@
// --- Wizard parent data (live Jellyfin API) ---
function getWizardSteps(state) {
return state?.parentType === 'Custom' ? CUSTOM_WIZARD_STEPS : QUERY_WIZARD_STEPS;
if (state?.parentType === 'Custom') return CUSTOM_WIZARD_STEPS;
if (state?.parentType === 'External') return EXTERNAL_WIZARD_STEPS;
return QUERY_WIZARD_STEPS;
}
function isCustomType(state) {
return state?.parentType === 'Custom';
}
function isExternalType(state) {
return state?.parentType === 'External';
}
function getStepIndex(step, state) {
return getWizardSteps(state).indexOf(step);
}
@@ -251,6 +266,9 @@
case 'customItems':
collectCustomItemsFromForm(root, wizardState);
break;
case 'externalList':
collectExternalListFromForm(root, wizardState);
break;
case 'appearance':
collectAppearanceStepFromForm(root, wizardState);
break;
@@ -1093,6 +1111,12 @@
}))
});
}
if (isExternalType(wizardState)) {
return JSON.stringify({
parentType: 'External',
externalListUrls: wizardState.externalListUrls || []
});
}
const parents = wizardState.parents || [];
return JSON.stringify({
parentType: wizardState.parentType,
@@ -1372,6 +1396,12 @@
`;
}
function collectExternalListFromForm(root, wizardState) {
const inputs = root.querySelectorAll('[id^="hsse-external-list-url-"]');
wizardState.externalListUrls = Array.from(inputs).map((el) => (el.value || '').trim()).filter(Boolean);
if (!wizardState.externalListUrls.length) wizardState.externalListUrls = [''];
}
function collectSettingsFromForm(root, wizardState) {
const nameEl = root.querySelector('#hsse-wizard-sectionName');
const groupSelect = root.querySelector('#hsse-wizard-sectionGroup');
@@ -1410,19 +1440,23 @@
function renderConfirmOverview(wizardState, config) {
const isCustom = isCustomType(wizardState);
const meta = isCustom ? CUSTOM_TYPE : PARENT_TYPES.find(t => t.type === wizardState.parentType);
const isExternal = isExternalType(wizardState);
const meta = isCustom ? CUSTOM_TYPE
: (isExternal ? EXTERNAL_TYPE : PARENT_TYPES.find(t => t.type === wizardState.parentType));
const parents = wizardState.parents || [];
const selectionLabel = isCustom
? ((wizardState.customItems || []).map(i => i.name?.trim()).filter(Boolean).join(', ') || '—')
: (parents.length ? parents.map(p => p.name).join(', ') : '—');
: (isExternal
? ((wizardState.externalListUrls || []).filter(Boolean).join(', ') || '—')
: (parents.length ? parents.map(p => p.name).join(', ') : '—'));
const rows = [
['Section Name', wizardState.sectionName || '—'],
['Section Group', resolveGroupName(wizardState, config)],
['Based On', meta?.label || wizardState.parentType || '—'],
[isCustom ? 'Items' : 'Selection', selectionLabel]
[isCustom ? 'Items' : (isExternal ? 'List URLs' : 'Selection'), selectionLabel]
];
if (!isCustom) {
if (!isCustom && !isExternal) {
rows.push(
['Item Types', wizardState.includeItemTypes.length ? formatItemTypesLabel(wizardState.includeItemTypes) : 'All types'],
['Sort By', wizardState.sortBy === 'Default' ? 'Default' : `${humanizeLabel(wizardState.sortBy)} (${wizardState.sortOrder})`],
@@ -1547,6 +1581,39 @@
return section;
}
if (parentType === 'External') {
const urls = (wizardState.externalListUrls || []).map((u) => String(u || '').trim()).filter(Boolean);
const section = {
...baseSection,
id: baseSection.id,
name: sectionName || 'External List',
enabled: true,
isCustom: true,
renderMode: renderMode || 'Normal',
cardFormat: renderMode === 'Spotlight' ? undefined : (cardFormat || 'Poster'),
externalListUrls: urls,
queries: []
};
applySectionVisibilityToSection(section, wizardState, baseSection);
const targetGroup = resolveGroupName(wizardState, wizardState._config);
if (targetGroup) {
section._targetGroupName = targetGroup;
}
if (renderMode === 'Spotlight' || renderMode === 'Random') {
section.spotlightConfig = {
spotlightLayout: spotlightLayout || 'Border',
spotlightSize: spotlightSize || 'normal',
tileCount: Math.max(1, Math.min(3, parseInt(tileCount, 10) || 1)),
panAnimation: panAnimation !== false
};
}
return section;
}
const queryOptions = {
Limit: DEFAULT_SECTION_QUERY_LIMIT
};
@@ -1820,6 +1887,27 @@
`;
}
function renderExternalListHTML(wizardState) {
const urls = Array.isArray(wizardState.externalListUrls) && wizardState.externalListUrls.length
? wizardState.externalListUrls
: [''];
const apiKey = window.LibraryCacheUtils?.getLibraryCacheSettings?.()?.mdblistApiKey
|| window.KefinHomeConfig2?.LIBRARY_CACHE?.mdblistApiKey
|| '';
const warn = !String(apiKey || '').trim()
? `<p class="listItemBodyText secondary" style="color: var(--error-color, #e57373);">MDBList API key is not configured. Add it under Home Screen → General settings before using External Lists.</p>`
: '';
return `
<div class="hsse-section-block">
<div class="listItemBodyText hsse-section-label">External List URLs</div>
<p class="listItemBodyText secondary hsse-field-hint">Paste one or more MDBList URLs. Matched against your library by IMDb/TMDB ids.</p>
${warn}
${urls.map((url, index) => hsseBuildTextInput(`hsse-external-list-url-${index}`, url || '', `List URL ${index + 1}`, 'url', 'https://mdblist.com/...')).join('')}
<button type="button" class="emby-button raised" data-hsse-action="wizard-add-external-list-url" style="margin-top: 0.75em;">Add URL</button>
</div>
`;
}
function renderWizardBodyHTML(wizardState, config) {
const step = wizardState.step;
@@ -1842,6 +1930,13 @@
<div class="listItemBodyText secondary hsse-type-desc">${escapeHtml(CUSTOM_TYPE.description)}</div>
</div>
</button>
<button type="button" class="hsse-type-card hsse-type-card-span${wizardState.parentType === 'External' ? ' hsse-active' : ''}" data-hsse-action="wizard-select-type" data-type="External">
${renderMaterialIcon(EXTERNAL_TYPE.icon, 'hsse-type-icon')}
<div>
<div class="listItemBodyText hsse-type-label">${escapeHtml(EXTERNAL_TYPE.label)}</div>
<div class="listItemBodyText secondary hsse-type-desc">${escapeHtml(EXTERNAL_TYPE.description)}</div>
</div>
</button>
</div>
`;
}
@@ -1850,6 +1945,10 @@
return renderCustomItemsHTML(wizardState);
}
if (step === 'externalList') {
return renderExternalListHTML(wizardState);
}
if (step === 'parent') {
const meta = PARENT_TYPES.find(t => t.type === wizardState.parentType);
@@ -1960,6 +2059,7 @@
return `Choose a ${meta?.label?.toLowerCase() || 'item'}`;
}
if (wizardState.step === 'customItems') return 'Create Items';
if (wizardState.step === 'externalList') return 'External List';
return 'Create a Home Screen Section';
}
return 'Create a Section';
@@ -3748,6 +3848,7 @@
parentListError: null,
personSearchResults: [],
customItems: [],
externalListUrls: [''],
customItemAdditionalExpanded: {},
customItemEditorIndex: null,
includeItemTypes: [],
@@ -4116,20 +4217,38 @@
if (selected === 'Custom') {
wizardState.customItems = [createBlankCustomItem()];
wizardState.customItemEditorIndex = null;
wizardState.externalListUrls = [''];
advanceToStep(root, 'customItems');
} else if (selected === 'External') {
wizardState.customItems = [];
wizardState.externalListUrls = wizardState.externalListUrls?.length
? wizardState.externalListUrls
: [''];
advanceToStep(root, 'externalList');
} else {
wizardState.customItems = [];
wizardState.externalListUrls = [''];
advanceToStep(root, 'parent');
}
} else if (selected === 'Custom') {
wizardState.customItemEditorIndex = null;
advanceToStep(root, 'customItems');
} else if (selected === 'External') {
advanceToStep(root, 'externalList');
} else {
advanceToStep(root, 'parent');
}
break;
}
case 'wizard-add-external-list-url': {
collectExternalListFromForm(root, wizardState);
if (!Array.isArray(wizardState.externalListUrls)) wizardState.externalListUrls = [''];
wizardState.externalListUrls.push('');
refreshModal(root);
break;
}
case 'wizard-open-custom-item-editor': {
const openIndex = parseInt(el.dataset.itemIndex, 10);
if (Number.isNaN(openIndex)) break;
@@ -4297,6 +4416,14 @@
wizardState.sectionName = 'Custom Section';
}
}
if (wizardState.step === 'externalList') {
const urls = (wizardState.externalListUrls || []).map((u) => String(u || '').trim()).filter(Boolean);
if (!urls.length) break;
wizardState.externalListUrls = urls;
if (!wizardState.sectionName?.trim()) {
wizardState.sectionName = 'External List';
}
}
if (wizardState.step === 'settings' && wizardState.sectionVisibility === 'seasonal') {
if (!hasValidSeasonalDates(wizardState)) break;
}