Add seasonal content support and enhance custom media ID handling
This commit is contained in:
@@ -1319,7 +1319,7 @@ const ApiUtils = {
|
||||
async fetchCollectionItems(collectionId) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${STATE.jellyfinData.serverAddress}/Items?ParentId=${collectionId}&Recursive=true&IncludeItemTypes=Movie,Series&fields=Id&userId=${STATE.jellyfinData.userId}`,
|
||||
`${STATE.jellyfinData.serverAddress}/Items?ParentId=${collectionId}&Recursive=true&IncludeItemTypes=Movie,Series&fields=Id,Type&userId=${STATE.jellyfinData.userId}`,
|
||||
{
|
||||
headers: this.getAuthHeaders(),
|
||||
}
|
||||
@@ -1333,7 +1333,7 @@ const ApiUtils = {
|
||||
const data = await response.json();
|
||||
const items = data.Items || [];
|
||||
console.log(`Resolved collection ${collectionId} to ${items.length} items`);
|
||||
return items.map(i => i.Id);
|
||||
return items.map(i => ({ Id: i.Id, Type: i.Type }));
|
||||
} catch (error) {
|
||||
console.error(`Error fetching collection items for ${collectionId}:`, error);
|
||||
return [];
|
||||
@@ -1783,7 +1783,7 @@ const SlideCreator = {
|
||||
},
|
||||
'onStateChange': (event) => {
|
||||
if (event.data === YT.PlayerState.ENDED) {
|
||||
SlideshowManager.nextSlide();
|
||||
SlideshowManager.nextSlide();
|
||||
}
|
||||
},
|
||||
'onError': (event) => {
|
||||
@@ -1841,7 +1841,7 @@ const SlideCreator = {
|
||||
});
|
||||
|
||||
backdrop.addEventListener('ended', () => {
|
||||
SlideshowManager.nextSlide();
|
||||
SlideshowManager.nextSlide();
|
||||
});
|
||||
|
||||
backdrop.addEventListener('error', () => {
|
||||
@@ -2703,7 +2703,7 @@ const SlideshowManager = {
|
||||
|
||||
videoBackdrop.play().catch(() => {
|
||||
setTimeout(() => {
|
||||
if (videoBackdrop.paused) {
|
||||
if (videoBackdrop.paused && slide.classList.contains('active')) {
|
||||
console.warn(`Autoplay blocked for ${itemId}, attempting muted fallback`);
|
||||
videoBackdrop.muted = true;
|
||||
videoBackdrop.play().catch(err => console.error("Muted fallback failed", err));
|
||||
@@ -2721,6 +2721,9 @@ const SlideshowManager = {
|
||||
startSeconds: player._startTime || 0,
|
||||
endSeconds: player._endTime
|
||||
});
|
||||
|
||||
// Explicitly call playVideo to ensure it starts
|
||||
player.playVideo();
|
||||
|
||||
if (STATE.slideshow.isMuted) {
|
||||
player.mute();
|
||||
@@ -2730,6 +2733,8 @@ const SlideshowManager = {
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (!slide.classList.contains('active')) return;
|
||||
|
||||
if (player.getPlayerState &&
|
||||
player.getPlayerState() !== YT.PlayerState.PLAYING &&
|
||||
player.getPlayerState() !== YT.PlayerState.BUFFERING) {
|
||||
@@ -2739,10 +2744,6 @@ const SlideshowManager = {
|
||||
}
|
||||
}, 1000);
|
||||
return true;
|
||||
} else if (player && typeof player.seekTo === 'function') {
|
||||
player.seekTo(player._startTime || 0);
|
||||
player.playVideo();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -2969,22 +2970,96 @@ const SlideshowManager = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses custom media IDs, handling seasonal content if enabled
|
||||
* Parses custom media IDs, handling seasonal content if enabled.
|
||||
* If Seasonal Content is enabled:
|
||||
* - Check if any defined season matches the current date.
|
||||
* - If match: Return IDs from that season.
|
||||
* - If NO match: Fall back to Default Custom IDs.
|
||||
* If Custom Media IDs are enabled (and no seasonal match):
|
||||
* - Return Default Custom IDs.
|
||||
* If no Custom Media IDs are enabled:
|
||||
* - Return empty array (triggering random fallback).
|
||||
* @returns {string[]} Array of media IDs
|
||||
*/
|
||||
parseCustomIds() {
|
||||
if (!CONFIG.enableSeasonalContent) {
|
||||
return CONFIG.customMediaIds
|
||||
.split(/[\n,]/).map((line) => {
|
||||
let idsString = CONFIG.customMediaIds;
|
||||
let usingSeasonal = false;
|
||||
|
||||
if (CONFIG.enableSeasonalContent) {
|
||||
try {
|
||||
const sections = JSON.parse(CONFIG.seasonalSections || "[]");
|
||||
const currentDate = new Date();
|
||||
const currentMonth = currentDate.getMonth() + 1; // 1-12
|
||||
const currentDay = currentDate.getDate(); // 1-31
|
||||
|
||||
for (const section of sections) {
|
||||
const startDay = parseInt(section.StartDay);
|
||||
const startMonth = parseInt(section.StartMonth);
|
||||
const endDay = parseInt(section.EndDay);
|
||||
const endMonth = parseInt(section.EndMonth);
|
||||
|
||||
let isInRange = false;
|
||||
|
||||
if (startMonth === endMonth) {
|
||||
if (currentMonth === startMonth && currentDay >= startDay && currentDay <= endDay) {
|
||||
isInRange = true;
|
||||
}
|
||||
} else if (startMonth < endMonth) {
|
||||
// Normal range
|
||||
if (
|
||||
(currentMonth > startMonth && currentMonth < endMonth) ||
|
||||
(currentMonth === startMonth && currentDay >= startDay) ||
|
||||
(currentMonth === endMonth && currentDay <= endDay)
|
||||
) {
|
||||
isInRange = true;
|
||||
}
|
||||
} else {
|
||||
// Wrap around year
|
||||
if (
|
||||
(currentMonth > startMonth || currentMonth < endMonth) ||
|
||||
(currentMonth === startMonth && currentDay >= startDay) ||
|
||||
(currentMonth === endMonth && currentDay <= endDay)
|
||||
) {
|
||||
isInRange = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInRange) {
|
||||
console.log(`Seasonal match found: ${section.Name}`);
|
||||
idsString = section.MediaIds;
|
||||
usingSeasonal = true;
|
||||
break; // Use first matching season
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error parsing seasonal sections in JS:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// If NOT using seasonal content (disabled or no match),
|
||||
// Custom IDs are disabled, return empty to skip to random
|
||||
if (!usingSeasonal && !CONFIG.enableCustomMediaIds) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Parse the resulting string (either seasonal or default)
|
||||
if (!idsString) return [];
|
||||
|
||||
return idsString
|
||||
.split(/[\n,]/)
|
||||
.map((line) => {
|
||||
const urlMatch = line.match(/\[(.*?)\]/);
|
||||
let id = line;
|
||||
if (urlMatch) {
|
||||
const url = urlMatch[1];
|
||||
// Remove the [url] part from the ID string for parsing
|
||||
id = line.replace(/\[.*?\]/, '').trim();
|
||||
// Attempt to extract GUID if present
|
||||
const guidMatch = id.match(/([0-9a-f]{32})/i);
|
||||
if (guidMatch) {
|
||||
id = guidMatch[1];
|
||||
} else {
|
||||
// Fallback: split by pipe if used
|
||||
id = id.split('|')[0].trim();
|
||||
}
|
||||
STATE.slideshow.customTrailerUrls[id] = url;
|
||||
@@ -2993,83 +3068,6 @@ const SlideshowManager = {
|
||||
})
|
||||
.map((id) => id.trim())
|
||||
.filter((id) => id);
|
||||
} else {
|
||||
return this.parseSeasonalIds();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses custom media IDs, handling seasonal content if enabled
|
||||
* @returns {string[]} Array of media IDs
|
||||
*/
|
||||
parseSeasonalIds() {
|
||||
console.log("Using Seasonal Content Mode");
|
||||
const lines = CONFIG.customMediaIds.split('\n');
|
||||
const currentDate = new Date();
|
||||
const currentMonth = currentDate.getMonth() + 1; // 1-12
|
||||
const currentDay = currentDate.getDate(); // 1-31
|
||||
const rawIds = [];
|
||||
|
||||
for (const line of lines) {
|
||||
const match = line.match(/^\s*(\d{1,2})\.(\d{1,2})-(\d{1,2})\.(\d{1,2})\s*\|.*\|(.*)$/) ||
|
||||
line.match(/^\s*(\d{1,2})\.(\d{1,2})-(\d{1,2})\.(\d{1,2})\s*\|(.*)$/);
|
||||
|
||||
if (match) {
|
||||
const startDay = parseInt(match[1]);
|
||||
const startMonth = parseInt(match[2]);
|
||||
const endDay = parseInt(match[3]);
|
||||
const endMonth = parseInt(match[4]);
|
||||
const idsPart = match[5];
|
||||
|
||||
let isInRange = false;
|
||||
|
||||
if (startMonth === endMonth) {
|
||||
if (currentMonth === startMonth && currentDay >= startDay && currentDay <= endDay) {
|
||||
isInRange = true;
|
||||
}
|
||||
} else if (startMonth < endMonth) {
|
||||
// Normal range spanning months (e.g. 15.06 - 15.08)
|
||||
if (
|
||||
(currentMonth > startMonth && currentMonth < endMonth) ||
|
||||
(currentMonth === startMonth && currentDay >= startDay) ||
|
||||
(currentMonth === endMonth && currentDay <= endDay)
|
||||
) {
|
||||
isInRange = true;
|
||||
}
|
||||
} else {
|
||||
// Wrap around year (e.g. 01.12 - 15.01)
|
||||
if (
|
||||
(currentMonth > startMonth || currentMonth < endMonth) ||
|
||||
(currentMonth === startMonth && currentDay >= startDay) ||
|
||||
(currentMonth === endMonth && currentDay <= endDay)
|
||||
) {
|
||||
isInRange = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInRange) {
|
||||
console.log(`Seasonal match found: ${line}`);
|
||||
const ids = idsPart.split(/[,]/).map(line => {
|
||||
const urlMatch = line.match(/\[(.*?)\]/);
|
||||
let id = line;
|
||||
if (urlMatch) {
|
||||
const url = urlMatch[1];
|
||||
id = line.replace(/\[.*?\]/, '').trim();
|
||||
const guidMatch = id.match(/([0-9a-f]{32})/i);
|
||||
if (guidMatch) {
|
||||
id = guidMatch[1];
|
||||
} else {
|
||||
id = id.split('|')[0].trim();
|
||||
}
|
||||
STATE.slideshow.customTrailerUrls[id] = url;
|
||||
}
|
||||
return id.trim();
|
||||
}).filter(id => id);
|
||||
rawIds.push(...ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rawIds;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -3111,7 +3109,7 @@ const SlideshowManager = {
|
||||
const children = await ApiUtils.fetchCollectionItems(id);
|
||||
finalIds.push(...children);
|
||||
} else if (item) {
|
||||
finalIds.push(id);
|
||||
finalIds.push({ Id: item.Id, Type: item.Type });
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`Error resolving item ${rawId}:`, e);
|
||||
@@ -3129,15 +3127,40 @@ const SlideshowManager = {
|
||||
let itemIds = [];
|
||||
|
||||
// 1. Try Custom Media/Collection IDs from Config & seasonal content
|
||||
if (CONFIG.enableCustomMediaIds && CONFIG.customMediaIds) {
|
||||
if (CONFIG.enableCustomMediaIds || CONFIG.enableSeasonalContent) {
|
||||
console.log("Using Custom Media IDs from configuration");
|
||||
const rawIds = this.parseCustomIds();
|
||||
itemIds = await this.resolveCollectionsAndItems(rawIds);
|
||||
const resolvedItems = await this.resolveCollectionsAndItems(rawIds);
|
||||
|
||||
// Apply max items limit to custom IDs if enabled
|
||||
if (CONFIG.applyLimitsToCustomIds && itemIds.length > CONFIG.maxItems) {
|
||||
console.log(`Limiting custom IDs from ${itemIds.length} to ${CONFIG.maxItems}`);
|
||||
itemIds = itemIds.slice(0, CONFIG.maxItems);
|
||||
if (CONFIG.applyLimitsToCustomIds) {
|
||||
let movieCount = 0;
|
||||
let showCount = 0;
|
||||
let keptItems = [];
|
||||
|
||||
for (const item of resolvedItems) {
|
||||
if (keptItems.length >= CONFIG.maxItems) break;
|
||||
|
||||
if (item.Type === 'Movie') {
|
||||
if (movieCount < CONFIG.maxMovies) {
|
||||
movieCount++;
|
||||
keptItems.push(item);
|
||||
}
|
||||
} else if (item.Type === 'Series' || item.Type === 'Season' || item.Type === 'Episode') {
|
||||
// Count Seasons/Episodes as TV Shows
|
||||
if (showCount < CONFIG.maxTvShows) {
|
||||
showCount++;
|
||||
keptItems.push(item);
|
||||
}
|
||||
} else {
|
||||
// Other types: count towards total only
|
||||
keptItems.push(item);
|
||||
}
|
||||
}
|
||||
itemIds = keptItems.map(i => i.Id);
|
||||
console.log(`Applied limits to custom IDs: ${itemIds.length} items (Movies: ${movieCount}, Shows: ${showCount})`);
|
||||
} else {
|
||||
itemIds = resolvedItems.map(i => i.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user