diff --git a/configuration/homeScreenAdvancedEditor.js b/configuration/homeScreenAdvancedEditor.js
index 14d5f6b..0e7d8ee 100644
--- a/configuration/homeScreenAdvancedEditor.js
+++ b/configuration/homeScreenAdvancedEditor.js
@@ -20,12 +20,33 @@
{ value: 'Custom', label: 'Custom', path: null }
];
+ const PEOPLE_CACHE_ITEM_TYPE_VALUES = [
+ { value: '', label: 'All' },
+ { value: 'Movie', label: 'Movie' },
+ { value: 'Series', label: 'Series' },
+ { value: 'Episode', label: 'Episode' }
+ ];
+
+ const PEOPLE_CACHE_ARGS = [
+ {
+ key: 'ItemType',
+ type: 'select',
+ label: 'Item Type',
+ possibleValues: PEOPLE_CACHE_ITEM_TYPE_VALUES
+ },
+ { key: 'MinCount', type: 'number', label: 'Min Count' }
+ ];
+
const CACHE_SOURCE_OPTIONS = [
{ value: 'MoviesCache.getImdbTop250Movies', label: 'IMDb Top 250 Movies' },
- { value: 'StudiosCache.getPopularTVNetworks', label: 'Popular TV Networks' },
- { value: 'PeopleCache.getTopActors', label: 'Top Actors' },
- { value: 'PeopleCache.getTopDirectors', label: 'Top Directors' },
- { value: 'PeopleCache.getTopWriters', label: 'Top Writers' }
+ {
+ value: 'StudiosCache.getPopularTVNetworks',
+ label: 'Popular TV Networks',
+ args: [{ key: 'MinCount', type: 'number', label: 'Min Count' }]
+ },
+ { value: 'PeopleCache.getTopActors', label: 'Top Actors', args: PEOPLE_CACHE_ARGS },
+ { value: 'PeopleCache.getTopDirectors', label: 'Top Directors', args: PEOPLE_CACHE_ARGS },
+ { value: 'PeopleCache.getTopWriters', label: 'Top Writers', args: PEOPLE_CACHE_ARGS }
];
const DEFAULT_SECTION_QUERY_LIMIT = window.KefinHomeScreenEditorConstants?.DEFAULT_SECTION_QUERY_LIMIT ?? 16;
@@ -1388,9 +1409,60 @@
if (list) list.innerHTML = buildStaticItemsListHTML(items);
}
+ function getCacheSourceOption(dataSource) {
+ const value = dataSource || CACHE_SOURCE_OPTIONS[0]?.value;
+ return CACHE_SOURCE_OPTIONS.find(o => o.value === value) || CACHE_SOURCE_OPTIONS[0] || null;
+ }
+
+ function buildCacheArgsHTML(queryIndex, dataSource, dataSourceOptions) {
+ const { buildSelect, buildTextInput } = getApi();
+ const source = getCacheSourceOption(dataSource);
+ const args = Array.isArray(source?.args) ? source.args : [];
+ if (!args.length) return '';
+
+ const fields = args.map((arg) => {
+ const id = `query-${queryIndex}-dsArg-${arg.key}`;
+ const raw = dataSourceOptions?.[arg.key];
+ const selected = raw != null ? String(raw) : (arg.default != null ? String(arg.default) : '');
+ if (arg.type === 'select') {
+ const options = Array.isArray(arg.possibleValues) ? arg.possibleValues : [];
+ return buildSelect(id, options, selected, arg.label || arg.key);
+ }
+ if (arg.type === 'number') {
+ return buildTextInput(id, selected, arg.label || arg.key, 'number', '', { step: 1 });
+ }
+ return buildTextInput(id, selected, arg.label || arg.key);
+ }).join('');
+
+ return `
${fields}
`;
+ }
+
+ function collectCacheArgsIntoDataSourceOptions(editor, queryIndex, dataSource, dataSourceOptions) {
+ const source = getCacheSourceOption(dataSource);
+ const args = Array.isArray(source?.args) ? source.args : [];
+ args.forEach((arg) => {
+ const el = editor.querySelector(`#query-${queryIndex}-dsArg-${arg.key}`);
+ if (!el) return;
+ const raw = el.value;
+ if (raw === '' || raw == null) return;
+ if (arg.type === 'number') {
+ const n = parseFloat(raw);
+ if (Number.isFinite(n)) dataSourceOptions[arg.key] = n;
+ return;
+ }
+ dataSourceOptions[arg.key] = raw;
+ });
+ }
+
function buildQueryCard(query, index, canDelete, isExpanded = true) {
const { buildSelect, buildTextInput, SORT_ORDERS, SORT_ORDER_DIRECTIONS } = getApi();
const queryOptions = query.queryOptions || {};
+ // Prefer dataSourceOptions; fall back to legacy ItemType/MinCount stored in queryOptions
+ const dataSourceOptions = {
+ ...(query.queryOptions?.ItemType != null ? { ItemType: query.queryOptions.ItemType } : {}),
+ ...(query.queryOptions?.MinCount != null ? { MinCount: query.queryOptions.MinCount } : {}),
+ ...(query.dataSourceOptions || {})
+ };
const sourceType = inferQuerySourceType(query);
const endpointValue = sourceType === 'static' ? 'Items' : inferEndpointValue(query);
const customPathDisplay = endpointValue === 'Custom' && sourceType === 'jellyfin' ? 'block' : 'none';
@@ -1398,8 +1470,10 @@
const endpointDisplay = sourceType === 'jellyfin' || sourceType === 'static' ? 'block' : 'none';
const jellyfinDisplay = sourceType === 'jellyfin' ? 'block' : 'none';
const staticDisplay = sourceType === 'static' ? 'block' : 'none';
- const sortExtrasDisplay = sourceType === 'jellyfin' || sourceType === 'static' ? 'block' : 'none';
+ const limitDisplay = sourceType === 'jellyfin' || sourceType === 'static' || sourceType === 'cache' ? 'block' : 'none';
+ const searchDisplay = sourceType === 'jellyfin' || sourceType === 'static' ? 'block' : 'none';
const staticItems = getQueryStaticItems(query);
+ const selectedDataSource = query.dataSource || CACHE_SOURCE_OPTIONS[0]?.value;
const parentSelection = getQueryParentSelection(query);
const includeTypes = Array.isArray(queryOptions.IncludeItemTypes)
@@ -1434,12 +1508,15 @@
${buildEndpointSelectHTML(index, endpointValue, sourceType === 'static')}
- ${buildSelect(`query-${index}-dataSource`, CACHE_SOURCE_OPTIONS, query.dataSource || CACHE_SOURCE_OPTIONS[0].value, 'Cache Source')}
+ ${buildSelect(`query-${index}-dataSource`, CACHE_SOURCE_OPTIONS, selectedDataSource, 'Cache Source')}
${buildTextInput(`query-${index}-path`, query.path || '', 'Custom Endpoint Path', 'text', '/Shows/Upcoming, etc.')}
+
+ ${sourceType === 'cache' ? buildCacheArgsHTML(index, selectedDataSource, dataSourceOptions) : ''}
+
@@ -1448,10 +1525,10 @@
${buildSelect(`query-${index}-SortOrder`, SORT_ORDER_DIRECTIONS, queryOptions.SortOrder || '', 'Sort Order')}
-