mirror of
https://github.com/ranaldsgift/KefinTweaks.git
synced 2026-11-05 15:52:47 +01:00
Add some example sections to the community area, and ensure sections/groups cannot be imported in duplicate
This commit is contained in:
@@ -4930,48 +4930,42 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
|
|||||||
});
|
});
|
||||||
|
|
||||||
confirmBtn?.addEventListener('click', async () => {
|
confirmBtn?.addEventListener('click', async () => {
|
||||||
const selectedIndices = Array.from(dialog.querySelectorAll('.import-community-check:checked'))
|
const selectedIndices = Array.from(dialog.querySelectorAll('.import-community-check:checked:not(:disabled)'))
|
||||||
.map(c => parseInt(c.dataset.index, 10));
|
.map(c => parseInt(c.dataset.index, 10));
|
||||||
|
|
||||||
if (selectedIndices.length === 0) {
|
if (selectedIndices.length === 0) {
|
||||||
showToast('Please select at least one section to import.');
|
showToast('Please select at least one new section to import.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentConfig.CUSTOM_SECTION_GROUPS) currentConfig.CUSTOM_SECTION_GROUPS = [];
|
|
||||||
|
|
||||||
const selectedSections = selectedIndices.map(i => {
|
const selectedSections = selectedIndices.map(i => {
|
||||||
const section = JSON.parse(JSON.stringify(collection.sections[i]));
|
const section = JSON.parse(JSON.stringify(collection.sections[i]));
|
||||||
return convertToHomeScreenSection(section);
|
return convertToHomeScreenSection(section);
|
||||||
});
|
});
|
||||||
|
|
||||||
const importedGroup = {
|
const { importedCount, skippedCount } = importCommunitySectionsIntoConfig(
|
||||||
name: collection.name || 'Imported Collection',
|
collection,
|
||||||
author: collection.author,
|
selectedSections
|
||||||
description: collection.description,
|
);
|
||||||
sections: selectedSections.map(section => {
|
|
||||||
if (section.id && sectionIdExists(currentConfig, section.id)) {
|
|
||||||
section.id = `${section.id}_community_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|
||||||
}
|
|
||||||
return section;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
currentConfig.CUSTOM_SECTION_GROUPS.push(importedGroup);
|
if (importedCount === 0) {
|
||||||
const importedCount = selectedSections.length;
|
showToast(skippedCount > 0
|
||||||
|
? 'Selected sections are already imported.'
|
||||||
|
: 'Nothing new to import.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (importedCount > 0) {
|
confirmBtn.disabled = true;
|
||||||
confirmBtn.disabled = true;
|
try {
|
||||||
try {
|
await saveConfig(currentConfig);
|
||||||
await saveConfig(currentConfig);
|
window.ModalSystem.close(COMMUNITY_IMPORT_MODAL_ID);
|
||||||
window.ModalSystem.close(COMMUNITY_IMPORT_MODAL_ID);
|
const skipNote = skippedCount > 0 ? ` (${skippedCount} already present)` : '';
|
||||||
showToast(`Successfully imported ${importedCount} sections. Refreshing...`);
|
showToast(`Successfully imported ${importedCount} section${importedCount !== 1 ? 's' : ''}${skipNote}. Refreshing...`);
|
||||||
refreshMainModal();
|
refreshMainModal();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ERR('Error importing community sections:', e);
|
ERR('Error importing community sections:', e);
|
||||||
showToast('Error importing sections');
|
showToast('Error importing sections');
|
||||||
confirmBtn.disabled = false;
|
confirmBtn.disabled = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -4992,19 +4986,28 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const existingGroup = findCommunityImportGroup(currentConfig.CUSTOM_SECTION_GROUPS || [], collection);
|
||||||
|
let availableCount = 0;
|
||||||
|
|
||||||
sectionsList.innerHTML = `
|
sectionsList.innerHTML = `
|
||||||
<div style="background: rgba(0,0,0,0.2); border-radius: 4px; padding: 0.5em;">
|
<div style="background: rgba(0,0,0,0.2); border-radius: 4px; padding: 0.5em;">
|
||||||
${collection.sections.map((s, i) => {
|
${collection.sections.map((s, i) => {
|
||||||
const name = escapeHtml(s.name || 'Unnamed');
|
const name = escapeHtml(s.name || 'Unnamed');
|
||||||
|
const alreadyImported = isCommunitySectionInGroup(existingGroup, s);
|
||||||
|
if (!alreadyImported) availableCount++;
|
||||||
const desc = s.description
|
const desc = s.description
|
||||||
? `<div class="listItemBodyText secondary" style="font-size: 0.85em; margin-top: 0.25em;">${escapeHtml(String(s.description))}</div>`
|
? `<div class="listItemBodyText secondary" style="font-size: 0.85em; margin-top: 0.25em;">${escapeHtml(String(s.description))}</div>`
|
||||||
: '';
|
: '';
|
||||||
|
const importedNote = alreadyImported
|
||||||
|
? `<div class="listItemBodyText secondary" style="font-size: 0.8em; margin-top: 0.25em; opacity: 0.7;">Already imported</div>`
|
||||||
|
: '';
|
||||||
return `
|
return `
|
||||||
<label class="checkboxContainer" style="display: flex; align-items: flex-start; padding: 0.5em; margin-bottom: 0.25em;">
|
<label class="checkboxContainer" style="display: flex; align-items: flex-start; padding: 0.5em; margin-bottom: 0.25em; ${alreadyImported ? 'opacity: 0.55;' : ''}">
|
||||||
<input type="checkbox" class="import-community-check" data-index="${i}" checked style="margin-top: 0.2em;">
|
<input type="checkbox" class="import-community-check" data-index="${i}" ${alreadyImported ? 'disabled' : 'checked'} style="margin-top: 0.2em;">
|
||||||
<div style="margin-left: 0.5em; flex: 1; min-width: 0;">
|
<div style="margin-left: 0.5em; flex: 1; min-width: 0;">
|
||||||
<span class="listItemBodyText">${name}</span>
|
<span class="listItemBodyText">${name}</span>
|
||||||
${desc}
|
${desc}
|
||||||
|
${importedNote}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
`;
|
`;
|
||||||
@@ -5014,7 +5017,11 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
|
|||||||
|
|
||||||
if (sectionsContainer) sectionsContainer.style.display = 'block';
|
if (sectionsContainer) sectionsContainer.style.display = 'block';
|
||||||
if (sectionsEmpty) sectionsEmpty.style.display = 'none';
|
if (sectionsEmpty) sectionsEmpty.style.display = 'none';
|
||||||
if (confirmBtn) confirmBtn.style.display = 'inline-block';
|
if (confirmBtn) {
|
||||||
|
confirmBtn.style.display = 'inline-block';
|
||||||
|
confirmBtn.disabled = availableCount === 0;
|
||||||
|
confirmBtn.textContent = availableCount === 0 ? 'Already Imported' : 'Import Selected';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -5147,6 +5154,82 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
|
|||||||
return allIds.includes(sectionId);
|
return allIds.includes(sectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an existing custom group matching a community collection (name + author).
|
||||||
|
*/
|
||||||
|
function findCommunityImportGroup(groups, collection) {
|
||||||
|
const name = collection?.name || 'Imported Collection';
|
||||||
|
const author = collection?.author;
|
||||||
|
return (groups || []).find((g) => {
|
||||||
|
if (g.name !== name) return false;
|
||||||
|
if (author) return g.author === author;
|
||||||
|
return !g.author;
|
||||||
|
}) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a community section is already present in a target group.
|
||||||
|
* Matches exact id, prior community re-import ids, or same section name.
|
||||||
|
*/
|
||||||
|
function isCommunitySectionInGroup(group, section) {
|
||||||
|
if (!group?.sections?.length || !section) return false;
|
||||||
|
const id = section.id;
|
||||||
|
const name = section.name;
|
||||||
|
return group.sections.some((s) => {
|
||||||
|
if (id && s.id === id) return true;
|
||||||
|
if (id && s.id && String(s.id).startsWith(`${id}_community_`)) return true;
|
||||||
|
if (name && s.name === name) return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import selected community sections into CUSTOM_SECTION_GROUPS,
|
||||||
|
* merging into an existing name+author group and skipping duplicates.
|
||||||
|
* @returns {{ importedCount: number, skippedCount: number, group: Object|null }}
|
||||||
|
*/
|
||||||
|
function importCommunitySectionsIntoConfig(collection, selectedSections) {
|
||||||
|
if (!currentConfig.CUSTOM_SECTION_GROUPS) currentConfig.CUSTOM_SECTION_GROUPS = [];
|
||||||
|
|
||||||
|
const existingGroup = findCommunityImportGroup(currentConfig.CUSTOM_SECTION_GROUPS, collection);
|
||||||
|
const toAdd = [];
|
||||||
|
let skippedCount = 0;
|
||||||
|
|
||||||
|
selectedSections.forEach((section) => {
|
||||||
|
if (isCommunitySectionInGroup(existingGroup, section)) {
|
||||||
|
skippedCount++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (section.id && sectionIdExists(currentConfig, section.id)) {
|
||||||
|
section.id = `${section.id}_community_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
||||||
|
}
|
||||||
|
toAdd.push(section);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (toAdd.length === 0) {
|
||||||
|
return { importedCount: 0, skippedCount, group: existingGroup };
|
||||||
|
}
|
||||||
|
|
||||||
|
let targetGroup = existingGroup;
|
||||||
|
if (!targetGroup) {
|
||||||
|
targetGroup = {
|
||||||
|
name: collection.name || 'Imported Collection',
|
||||||
|
author: collection.author,
|
||||||
|
description: collection.description,
|
||||||
|
sections: []
|
||||||
|
};
|
||||||
|
currentConfig.CUSTOM_SECTION_GROUPS.push(targetGroup);
|
||||||
|
} else if (collection.description && !targetGroup.description) {
|
||||||
|
targetGroup.description = collection.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetGroup.sections) targetGroup.sections = [];
|
||||||
|
targetGroup.sections.push(...toAdd);
|
||||||
|
targetGroup.sections.sort((a, b) => (a.order || 0) - (b.order || 0));
|
||||||
|
|
||||||
|
return { importedCount: toAdd.length, skippedCount, group: targetGroup };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show community import dialog
|
* Show community import dialog
|
||||||
*/
|
*/
|
||||||
@@ -5229,15 +5312,24 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
|
|||||||
|
|
||||||
// Render sections list
|
// Render sections list
|
||||||
let html = '';
|
let html = '';
|
||||||
|
let availableCount = 0;
|
||||||
if (selectedCollection.sections && selectedCollection.sections.length > 0) {
|
if (selectedCollection.sections && selectedCollection.sections.length > 0) {
|
||||||
|
const existingGroup = findCommunityImportGroup(
|
||||||
|
currentConfig.CUSTOM_SECTION_GROUPS || [],
|
||||||
|
selectedCollection
|
||||||
|
);
|
||||||
html += `
|
html += `
|
||||||
<div style="background: rgba(0,0,0,0.2); border-radius: 4px; padding: 0.5em;">
|
<div style="background: rgba(0,0,0,0.2); border-radius: 4px; padding: 0.5em;">
|
||||||
${selectedCollection.sections.map((s, i) => `
|
${selectedCollection.sections.map((s, i) => {
|
||||||
<label class="checkboxContainer" style="display: flex; align-items: center; padding: 0.5em;">
|
const alreadyImported = isCommunitySectionInGroup(existingGroup, s);
|
||||||
<input type="checkbox" class="import-community-check" data-index="${i}" checked>
|
if (!alreadyImported) availableCount++;
|
||||||
<span class="listItemBodyText" style="margin-left: 0.5em;">${s.name || 'Unnamed'}</span>
|
return `
|
||||||
|
<label class="checkboxContainer" style="display: flex; align-items: center; padding: 0.5em; ${alreadyImported ? 'opacity: 0.55;' : ''}">
|
||||||
|
<input type="checkbox" class="import-community-check" data-index="${i}" ${alreadyImported ? 'disabled' : 'checked'}>
|
||||||
|
<span class="listItemBodyText" style="margin-left: 0.5em;">${escapeHtml(s.name || 'Unnamed')}${alreadyImported ? ' (already imported)' : ''}</span>
|
||||||
</label>
|
</label>
|
||||||
`).join('')}
|
`;
|
||||||
|
}).join('')}
|
||||||
</div>`;
|
</div>`;
|
||||||
} else {
|
} else {
|
||||||
html = '<div class="listItemBodyText secondary">No sections found in this collection.</div>';
|
html = '<div class="listItemBodyText secondary">No sections found in this collection.</div>';
|
||||||
@@ -5246,51 +5338,43 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
|
|||||||
selectionList.innerHTML = html;
|
selectionList.innerHTML = html;
|
||||||
selectionContainer.style.display = 'block';
|
selectionContainer.style.display = 'block';
|
||||||
confirmBtn.style.display = 'inline-block';
|
confirmBtn.style.display = 'inline-block';
|
||||||
|
confirmBtn.disabled = availableCount === 0;
|
||||||
|
confirmBtn.textContent = availableCount === 0 ? 'Already Imported' : 'Import Selected';
|
||||||
});
|
});
|
||||||
|
|
||||||
confirmBtn.addEventListener('click', async () => {
|
confirmBtn.addEventListener('click', async () => {
|
||||||
if (!selectedCollection) return;
|
if (!selectedCollection) return;
|
||||||
|
|
||||||
const selectedIndices = Array.from(content.querySelectorAll('.import-community-check:checked')).map(c => parseInt(c.dataset.index));
|
const selectedIndices = Array.from(content.querySelectorAll('.import-community-check:checked:not(:disabled)'))
|
||||||
|
.map(c => parseInt(c.dataset.index));
|
||||||
|
|
||||||
if (selectedIndices.length === 0) {
|
if (selectedIndices.length === 0) {
|
||||||
showToast('Please select at least one section to import.');
|
showToast('Please select at least one new section to import.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import logic - import entire groups to CUSTOM_SECTION_GROUPS
|
|
||||||
if (!currentConfig.CUSTOM_SECTION_GROUPS) currentConfig.CUSTOM_SECTION_GROUPS = [];
|
|
||||||
|
|
||||||
const selectedSections = selectedIndices.map(i => {
|
const selectedSections = selectedIndices.map(i => {
|
||||||
const section = JSON.parse(JSON.stringify(selectedCollection.sections[i])); // Deep copy
|
const section = JSON.parse(JSON.stringify(selectedCollection.sections[i]));
|
||||||
return convertToHomeScreenSection(section);
|
return convertToHomeScreenSection(section);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a group from the collection
|
const { importedCount, skippedCount } = importCommunitySectionsIntoConfig(
|
||||||
const importedGroup = {
|
selectedCollection,
|
||||||
name: selectedCollection.name || 'Imported Collection',
|
selectedSections
|
||||||
author: selectedCollection.author,
|
);
|
||||||
description: selectedCollection.description,
|
|
||||||
sections: selectedSections.map(section => {
|
|
||||||
// Check for ID collision - only regenerate if needed
|
|
||||||
if (section.id && sectionIdExists(currentConfig, section.id)) {
|
|
||||||
section.id = `${section.id}_community_${Date.now()}_${Math.floor(Math.random()*1000)}`;
|
|
||||||
}
|
|
||||||
return section;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
currentConfig.CUSTOM_SECTION_GROUPS.push(importedGroup);
|
if (importedCount === 0) {
|
||||||
const importedCount = selectedSections.length;
|
showToast(skippedCount > 0
|
||||||
|
? 'Selected sections are already imported.'
|
||||||
if (importedCount > 0) {
|
: 'Nothing new to import.');
|
||||||
await saveConfig(currentConfig);
|
return;
|
||||||
showToast(`Successfully imported ${importedCount} sections. Refreshing...`);
|
|
||||||
refreshMainModal();
|
|
||||||
window.ModalSystem.close('kefinCommunityImportModal');
|
|
||||||
} else {
|
|
||||||
window.ModalSystem.close('kefinCommunityImportModal');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await saveConfig(currentConfig);
|
||||||
|
const skipNote = skippedCount > 0 ? ` (${skippedCount} already present)` : '';
|
||||||
|
showToast(`Successfully imported ${importedCount} section${importedCount !== 1 ? 's' : ''}${skipNote}. Refreshing...`);
|
||||||
|
refreshMainModal();
|
||||||
|
window.ModalSystem.close('kefinCommunityImportModal');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -86,67 +86,665 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Genre Sections",
|
"name": "Multi Query Examples",
|
||||||
"author": "HighImKevin",
|
"author": "HighImKevin",
|
||||||
|
"description": "Custom section examples that use the multi-query picker.",
|
||||||
"sections": [
|
"sections": [
|
||||||
{
|
{
|
||||||
"name": "Popular Movie Genres",
|
"id": "custom-1791197522382",
|
||||||
|
"name": "Our Top Movie Choices",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"order": 22,
|
"order": 100,
|
||||||
"discoveryEnabled": false,
|
"category": "none",
|
||||||
"renderMode": "Normal",
|
"cardFormat": "Poster",
|
||||||
"cardFormat": "Thumb",
|
"ttl": 86400000,
|
||||||
"queries": [
|
"queries": [
|
||||||
{
|
{
|
||||||
"path": "/Genres",
|
"name": "Best of Action",
|
||||||
"queryOptions": {
|
"queryOptions": {
|
||||||
"IncludeItemTypes": [
|
"IncludeItemTypes": [
|
||||||
"Movie"
|
"Movie"
|
||||||
],
|
],
|
||||||
"SortBy": "ChildCount",
|
"Genres": "Action",
|
||||||
"SortOrder": "Descending",
|
"SortBy": "Random",
|
||||||
"Limit": 20
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Best of Comedy",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"Genres": "Comedy",
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Best of Drama",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"Genres": "Drama",
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Best of Horror",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"Genres": "Horror",
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Best of Science Fiction",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"Genres": "Science Fiction",
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Best of Romance",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"Genres": "Romance",
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"flattenSeries": false,
|
"userConfigurable": true,
|
||||||
"id": "custom-1768961093582",
|
"renderMode": "Normal",
|
||||||
"ttl": 86400000,
|
"discoveryEnabled": false,
|
||||||
"type": "custom"
|
"type": "home",
|
||||||
|
"useRandomQuery": true,
|
||||||
|
"useMultiQueryPicker": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"description": "Our top offerings by genre",
|
||||||
|
"useGaplessCards": false,
|
||||||
|
"useParentCard": false,
|
||||||
|
"itemLimit": 16,
|
||||||
|
"useQueryNamesForSection": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Popular TV Genres",
|
"id": "custom-1791256768605",
|
||||||
"enabled": true,
|
"name": "Movies by the Decade",
|
||||||
|
"enabled": false,
|
||||||
"order": 100,
|
"order": 100,
|
||||||
"discoveryEnabled": false,
|
"category": "none",
|
||||||
"renderMode": "Normal",
|
"cardFormat": "Poster",
|
||||||
"cardFormat": "Thumb",
|
"ttl": 86400000,
|
||||||
"queries": [
|
"queries": [
|
||||||
{
|
{
|
||||||
"path": "/Genres",
|
"name": "50s Movies",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"1950",
|
||||||
|
"1951",
|
||||||
|
"1952",
|
||||||
|
"1953",
|
||||||
|
"1954",
|
||||||
|
"1955",
|
||||||
|
"1956",
|
||||||
|
"1957",
|
||||||
|
"1958",
|
||||||
|
"1959"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "60s Movies",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"1960",
|
||||||
|
"1961",
|
||||||
|
"1962",
|
||||||
|
"1963",
|
||||||
|
"1964",
|
||||||
|
"1965",
|
||||||
|
"1966",
|
||||||
|
"1967",
|
||||||
|
"1968",
|
||||||
|
"1969"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "70s Movies",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"1970",
|
||||||
|
"1971",
|
||||||
|
"1972",
|
||||||
|
"1973",
|
||||||
|
"1974",
|
||||||
|
"1975",
|
||||||
|
"1976",
|
||||||
|
"1977",
|
||||||
|
"1978",
|
||||||
|
"1979"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "80s Movies",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"1980",
|
||||||
|
"1981",
|
||||||
|
"1982",
|
||||||
|
"1983",
|
||||||
|
"1984",
|
||||||
|
"1985",
|
||||||
|
"1986",
|
||||||
|
"1987",
|
||||||
|
"1988",
|
||||||
|
"1989"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "90s Movies",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"1990",
|
||||||
|
"1991",
|
||||||
|
"1992",
|
||||||
|
"1993",
|
||||||
|
"1994",
|
||||||
|
"1995",
|
||||||
|
"1996",
|
||||||
|
"1997",
|
||||||
|
"1998",
|
||||||
|
"1999"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2000s Movies",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"2000",
|
||||||
|
"2001",
|
||||||
|
"2002",
|
||||||
|
"2003",
|
||||||
|
"2004",
|
||||||
|
"2005",
|
||||||
|
"2006",
|
||||||
|
"2007",
|
||||||
|
"2008",
|
||||||
|
"2009"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2010s Movies",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"2010",
|
||||||
|
"2011",
|
||||||
|
"2012",
|
||||||
|
"2013",
|
||||||
|
"2014",
|
||||||
|
"2015",
|
||||||
|
"2016",
|
||||||
|
"2017",
|
||||||
|
"2018",
|
||||||
|
"2019"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2020s Movies",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"2020",
|
||||||
|
"2021",
|
||||||
|
"2022",
|
||||||
|
"2023",
|
||||||
|
"2024",
|
||||||
|
"2025",
|
||||||
|
"2026",
|
||||||
|
"2027",
|
||||||
|
"2028",
|
||||||
|
"2029"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"caption": "Browse by decade",
|
||||||
|
"userConfigurable": true,
|
||||||
|
"renderMode": "Normal",
|
||||||
|
"discoveryEnabled": false,
|
||||||
|
"type": "home",
|
||||||
|
"useRandomQuery": true,
|
||||||
|
"useMultiQueryPicker": true,
|
||||||
|
"multiQueryPickerLabel": "Pick a Decade",
|
||||||
|
"useQueryNamesForSection": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"description": "Find something to watch from your favorite decade",
|
||||||
|
"useGaplessCards": false,
|
||||||
|
"useParentCard": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custom-1791256768606",
|
||||||
|
"name": "Our Top Series Choices",
|
||||||
|
"enabled": true,
|
||||||
|
"order": 100,
|
||||||
|
"category": "none",
|
||||||
|
"cardFormat": "Poster",
|
||||||
|
"ttl": 86400000,
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"name": "Best of Action",
|
||||||
"queryOptions": {
|
"queryOptions": {
|
||||||
"IncludeItemTypes": [
|
"IncludeItemTypes": [
|
||||||
"Series"
|
"Series"
|
||||||
],
|
],
|
||||||
"SortBy": "ChildCount",
|
"Genres": "Action",
|
||||||
"SortOrder": "Descending",
|
"SortBy": "Random",
|
||||||
"Limit": 20
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Best of Comedy",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"Genres": "Comedy",
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Best of Drama",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"Genres": "Drama",
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Best of Horror",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"Genres": "Horror",
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Best of Science Fiction",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"Genres": "Science Fiction",
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Best of Romance",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"Genres": "Romance",
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 50,
|
||||||
|
"MinCommunityRating": 7.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"flattenSeries": false,
|
"userConfigurable": true,
|
||||||
"id": "custom-1770808132827",
|
"renderMode": "Normal",
|
||||||
|
"discoveryEnabled": false,
|
||||||
|
"type": "home",
|
||||||
|
"useRandomQuery": true,
|
||||||
|
"useMultiQueryPicker": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"description": "Our top series offerings by genre",
|
||||||
|
"useGaplessCards": false,
|
||||||
|
"useParentCard": false,
|
||||||
|
"itemLimit": 16,
|
||||||
|
"useQueryNamesForSection": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custom-1791256768607",
|
||||||
|
"name": "Series by the Decade",
|
||||||
|
"enabled": false,
|
||||||
|
"order": 100,
|
||||||
|
"category": "none",
|
||||||
|
"cardFormat": "Poster",
|
||||||
"ttl": 86400000,
|
"ttl": 86400000,
|
||||||
"type": "custom"
|
"queries": [
|
||||||
|
{
|
||||||
|
"name": "50s Series",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"1950",
|
||||||
|
"1951",
|
||||||
|
"1952",
|
||||||
|
"1953",
|
||||||
|
"1954",
|
||||||
|
"1955",
|
||||||
|
"1956",
|
||||||
|
"1957",
|
||||||
|
"1958",
|
||||||
|
"1959"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "60s Series",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"1960",
|
||||||
|
"1961",
|
||||||
|
"1962",
|
||||||
|
"1963",
|
||||||
|
"1964",
|
||||||
|
"1965",
|
||||||
|
"1966",
|
||||||
|
"1967",
|
||||||
|
"1968",
|
||||||
|
"1969"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "70s Series",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"1970",
|
||||||
|
"1971",
|
||||||
|
"1972",
|
||||||
|
"1973",
|
||||||
|
"1974",
|
||||||
|
"1975",
|
||||||
|
"1976",
|
||||||
|
"1977",
|
||||||
|
"1978",
|
||||||
|
"1979"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "80s Series",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"1980",
|
||||||
|
"1981",
|
||||||
|
"1982",
|
||||||
|
"1983",
|
||||||
|
"1984",
|
||||||
|
"1985",
|
||||||
|
"1986",
|
||||||
|
"1987",
|
||||||
|
"1988",
|
||||||
|
"1989"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "90s Series",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"1990",
|
||||||
|
"1991",
|
||||||
|
"1992",
|
||||||
|
"1993",
|
||||||
|
"1994",
|
||||||
|
"1995",
|
||||||
|
"1996",
|
||||||
|
"1997",
|
||||||
|
"1998",
|
||||||
|
"1999"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2000s Series",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"2000",
|
||||||
|
"2001",
|
||||||
|
"2002",
|
||||||
|
"2003",
|
||||||
|
"2004",
|
||||||
|
"2005",
|
||||||
|
"2006",
|
||||||
|
"2007",
|
||||||
|
"2008",
|
||||||
|
"2009"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2010s Series",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"2010",
|
||||||
|
"2011",
|
||||||
|
"2012",
|
||||||
|
"2013",
|
||||||
|
"2014",
|
||||||
|
"2015",
|
||||||
|
"2016",
|
||||||
|
"2017",
|
||||||
|
"2018",
|
||||||
|
"2019"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2020s Series",
|
||||||
|
"queryOptions": {
|
||||||
|
"IncludeItemTypes": [
|
||||||
|
"Series"
|
||||||
|
],
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Filters": "IsUnplayed",
|
||||||
|
"Limit": 20,
|
||||||
|
"Years": [
|
||||||
|
"2020",
|
||||||
|
"2021",
|
||||||
|
"2022",
|
||||||
|
"2023",
|
||||||
|
"2024",
|
||||||
|
"2025",
|
||||||
|
"2026",
|
||||||
|
"2027",
|
||||||
|
"2028",
|
||||||
|
"2029"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"caption": "Browse by decade",
|
||||||
|
"userConfigurable": true,
|
||||||
|
"renderMode": "Normal",
|
||||||
|
"discoveryEnabled": false,
|
||||||
|
"type": "home",
|
||||||
|
"useRandomQuery": true,
|
||||||
|
"useMultiQueryPicker": true,
|
||||||
|
"multiQueryPickerLabel": "Pick a Decade",
|
||||||
|
"useQueryNamesForSection": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"description": "Find a series from your favorite decade",
|
||||||
|
"useGaplessCards": false,
|
||||||
|
"useParentCard": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Prolific People [Discovery]",
|
"name": "Prolific People [Discovery]",
|
||||||
"author": "HighImKevin",
|
"author": "HighImKevin",
|
||||||
|
"description": "Discovery sections featuring prolific movie people, plus top series actors.",
|
||||||
"sections": [
|
"sections": [
|
||||||
{
|
{
|
||||||
"name": "Prolific Writers",
|
"name": "Prolific Movie Writers",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"order": 100,
|
"order": 100,
|
||||||
"discoveryEnabled": true,
|
"discoveryEnabled": true,
|
||||||
@@ -155,6 +753,9 @@
|
|||||||
"queries": [
|
"queries": [
|
||||||
{
|
{
|
||||||
"dataSource": "PeopleCache.getTopWriters",
|
"dataSource": "PeopleCache.getTopWriters",
|
||||||
|
"dataSourceOptions": {
|
||||||
|
"ItemType": "Movie"
|
||||||
|
},
|
||||||
"queryOptions": {
|
"queryOptions": {
|
||||||
"SortBy": "Random",
|
"SortBy": "Random",
|
||||||
"SortOrder": "Ascending",
|
"SortOrder": "Ascending",
|
||||||
@@ -167,7 +768,7 @@
|
|||||||
"type": "custom"
|
"type": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Prolific Directors",
|
"name": "Prolific Movie Directors",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"order": 100,
|
"order": 100,
|
||||||
"discoveryEnabled": true,
|
"discoveryEnabled": true,
|
||||||
@@ -176,6 +777,9 @@
|
|||||||
"queries": [
|
"queries": [
|
||||||
{
|
{
|
||||||
"dataSource": "PeopleCache.getTopDirectors",
|
"dataSource": "PeopleCache.getTopDirectors",
|
||||||
|
"dataSourceOptions": {
|
||||||
|
"ItemType": "Movie"
|
||||||
|
},
|
||||||
"queryOptions": {
|
"queryOptions": {
|
||||||
"SortBy": "Random",
|
"SortBy": "Random",
|
||||||
"SortOrder": "Ascending",
|
"SortOrder": "Ascending",
|
||||||
@@ -188,7 +792,7 @@
|
|||||||
"type": "custom"
|
"type": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Prolific Actors",
|
"name": "Prolific Movie Actors",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"order": 250,
|
"order": 250,
|
||||||
"discoveryEnabled": true,
|
"discoveryEnabled": true,
|
||||||
@@ -197,6 +801,9 @@
|
|||||||
"queries": [
|
"queries": [
|
||||||
{
|
{
|
||||||
"dataSource": "PeopleCache.getTopActors",
|
"dataSource": "PeopleCache.getTopActors",
|
||||||
|
"dataSourceOptions": {
|
||||||
|
"ItemType": "Movie"
|
||||||
|
},
|
||||||
"queryOptions": {
|
"queryOptions": {
|
||||||
"SortBy": "Random",
|
"SortBy": "Random",
|
||||||
"SortOrder": "Ascending",
|
"SortOrder": "Ascending",
|
||||||
@@ -207,6 +814,30 @@
|
|||||||
"flattenSeries": false,
|
"flattenSeries": false,
|
||||||
"id": "custom-1768973523242",
|
"id": "custom-1768973523242",
|
||||||
"type": "custom"
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Prolific Series Actors",
|
||||||
|
"enabled": true,
|
||||||
|
"order": 250,
|
||||||
|
"discoveryEnabled": true,
|
||||||
|
"renderMode": "Normal",
|
||||||
|
"cardFormat": "Poster",
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"dataSource": "PeopleCache.getTopActors",
|
||||||
|
"dataSourceOptions": {
|
||||||
|
"ItemType": "Series"
|
||||||
|
},
|
||||||
|
"queryOptions": {
|
||||||
|
"SortBy": "Random",
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"Limit": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flattenSeries": false,
|
||||||
|
"id": "custom-1791300000001",
|
||||||
|
"type": "custom"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user