mirror of
https://github.com/stashapp/CommunityScripts.git
synced 2025-12-15 14:23:15 -06:00
797 lines
42 KiB
JavaScript
797 lines
42 KiB
JavaScript
"use strict";
|
|
(function () {
|
|
const api = window.PluginApi;
|
|
const csLib = window.csLib;
|
|
|
|
let currentVideoIndex = 0;
|
|
let videoUrls = [];
|
|
let slideshowInterval = null; // Keeping this variable to clear it when navigating
|
|
let currentVideoElement = null; // Keep track of the currently displayed video element for transitions
|
|
let fadeOutTimeout = null; // To keep track of the fade out fallback timeout
|
|
|
|
// Store plugin settings
|
|
let pluginSettings = {};
|
|
|
|
function displayVideo(bannerElement, videoUrl) {
|
|
// console.log(`displayVideo called with URL: ${videoUrl}`);
|
|
|
|
if (!videoUrl) {
|
|
console.warn("displayVideo called with null or undefined URL, skipping.");
|
|
// Try displaying the next video if the current URL is invalid
|
|
currentVideoIndex = (currentVideoIndex + 1) % videoUrls.length;
|
|
if (videoUrls.length > 0) {
|
|
// Add a small delay before trying the next video to prevent rapid calls
|
|
setTimeout(() => {
|
|
displayVideo(bannerElement, videoUrls[currentVideoIndex]);
|
|
}, 50);
|
|
} else {
|
|
// console.log("No video URLs available to display.");
|
|
// Handle case where no valid videos are found (e.g., display a placeholder)
|
|
// Ensure any existing video element is removed with transition
|
|
if (currentVideoElement) {
|
|
// console.log("No video URLs, removing current video element.");
|
|
currentVideoElement.style.opacity = '0';
|
|
const noVideosTransitionEndHandler = () => {
|
|
// console.log("No videos transitionend event fired.");
|
|
if(currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
currentVideoElement.remove();
|
|
// console.log("Removed old video element when no new videos found after fade out.");
|
|
}
|
|
currentVideoElement = null; // Clear reference
|
|
if (fadeOutTimeout) { clearTimeout(fadeOutTimeout); fadeOutTimeout = null; }
|
|
};
|
|
currentVideoElement.addEventListener('transitionend', noVideosTransitionEndHandler, { once: true });
|
|
// Fallback removal
|
|
fadeOutTimeout = setTimeout(() => {
|
|
// console.log("No videos removal fallback timeout triggered.");
|
|
if (currentVideoElement && currentVideoElement.parentElement === bannerElement && currentVideoElement.style.opacity === '0') {
|
|
currentVideoElement.remove();
|
|
// console.log("Fallback: Removed old video element after timeout when no new videos found.");
|
|
} else if (currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
// console.log("Fallback timeout: Current video still in DOM when no new videos found.");
|
|
} else {
|
|
// console.log("Fallback timeout: Current video element already removed when no new videos found.");
|
|
}
|
|
currentVideoElement = null;
|
|
fadeOutTimeout = null;
|
|
}, 600);
|
|
}
|
|
|
|
// Remove gradient overlay if no videos are found
|
|
const existingOverlay = bannerElement.querySelector('.gradient-overlay');
|
|
if (existingOverlay) {
|
|
existingOverlay.remove();
|
|
// console.log("Removed gradient overlay as no videos were found.");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
// Create the new video element
|
|
const videoElement = document.createElement('video');
|
|
|
|
// Add error handler before setting src
|
|
videoElement.onerror = () => {
|
|
console.warn(`Error loading video from URL: ${videoUrl}, skipping.`);
|
|
// Clean up the failed video element
|
|
if (videoElement && videoElement.parentElement) {
|
|
videoElement.parentElement.removeChild(videoElement);
|
|
// console.log("Removed failed video element from DOM.");
|
|
}
|
|
|
|
// Try displaying the next video
|
|
currentVideoIndex = (currentVideoIndex + 1) % videoUrls.length;
|
|
if (videoUrls.length > 0) {
|
|
// Add a small delay before trying the next video to prevent rapid error loop
|
|
setTimeout(() => {
|
|
displayVideo(bannerElement, videoUrls[currentVideoIndex]);
|
|
}, 50);
|
|
} else {
|
|
// console.log("No more video URLs to try after error.");
|
|
// Handle case where no valid videos are found after errors
|
|
// Ensure any existing video element (if any was left) is removed
|
|
if (currentVideoElement) {
|
|
// console.log("No more video URLs, removing current video element.");
|
|
currentVideoElement.style.opacity = '0';
|
|
const errorHandlerTransitionEndHandler = () => {
|
|
// console.log("Error handler transitionend event fired.");
|
|
if(currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
currentVideoElement.remove();
|
|
// console.log("Removed old video element in error handler after fade out.");
|
|
}
|
|
currentVideoElement = null; // Clear reference
|
|
if (fadeOutTimeout) { clearTimeout(fadeOutTimeout); fadeOutTimeout = null; }
|
|
};
|
|
currentVideoElement.addEventListener('transitionend', errorHandlerTransitionEndHandler, { once: true });
|
|
// Fallback removal
|
|
fadeOutTimeout = setTimeout(() => {
|
|
// console.log("Error handler removal fallback timeout triggered.");
|
|
if (currentVideoElement && currentVideoElement.parentElement === bannerElement && currentVideoElement.style.opacity === '0') {
|
|
currentVideoElement.remove();
|
|
// console.log("Fallback: Removed old video element in error handler after timeout.");
|
|
} else if (currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
// console.log("Fallback timeout: Current video still in DOM in error handler.");
|
|
} else {
|
|
// console.log("Fallback timeout: Current video element already removed in error handler.");
|
|
}
|
|
currentVideoElement = null;
|
|
fadeOutTimeout = null;
|
|
}, 600);
|
|
}
|
|
// Remove gradient overlay if no videos are found
|
|
const existingOverlay = bannerElement.querySelector('.gradient-overlay');
|
|
if (existingOverlay) {
|
|
existingOverlay.remove();
|
|
// console.log("Removed gradient overlay as no videos were found after errors.");
|
|
}
|
|
}
|
|
};
|
|
|
|
videoElement.src = videoUrl; // Set src AFTER adding onerror
|
|
|
|
videoElement.autoplay = true;
|
|
videoElement.loop = false; // Do not loop, rely on 'ended' event or timeout
|
|
videoElement.muted = true; // Start muted for autoplay
|
|
videoElement.style.position = 'absolute';
|
|
videoElement.style.top = '0';
|
|
videoElement.style.left = '0';
|
|
videoElement.style.width = '100%';
|
|
videoElement.style.height = '100%';
|
|
videoElement.style.objectFit = 'cover';
|
|
videoElement.style.zIndex = '-1'; // Behind the content
|
|
|
|
// Apply brightness filter from settings
|
|
const brightnessPercentage = pluginSettings.videoBrightness || 65; // Use setting or default to 65%
|
|
videoElement.style.filter = `brightness(${brightnessPercentage}%)`;
|
|
// console.log(`Applying brightness filter: ${brightnessPercentage}%`);
|
|
|
|
|
|
videoElement.style.opacity = '0'; // Start hidden for fade in
|
|
videoElement.style.transition = 'opacity 0.5s ease-in-out'; // Add transition for fade
|
|
|
|
// Add the new video element to the banner BEFORE fading out the old one
|
|
// Use a small delay to ensure the DOM is ready for the new element
|
|
setTimeout(() => {
|
|
bannerElement.prepend(videoElement); // Add video as the first child
|
|
// console.log("Created and added new video element to DOM.");
|
|
|
|
// Reference the old video element before updating currentVideoElement
|
|
const oldVideoElement = currentVideoElement;
|
|
|
|
// Update the current video element reference to the NEW element
|
|
currentVideoElement = videoElement;
|
|
|
|
// Start fade in for the new video
|
|
// Use a very small delay to ensure the element is in DOM and transition can apply
|
|
setTimeout(() => {
|
|
if(videoElement === currentVideoElement) { // Ensure this is still the current video
|
|
videoElement.style.opacity = '1';
|
|
// console.log("Starting fade in transition for new video.");
|
|
} else {
|
|
// console.log("New video element changed before fade in could start.");
|
|
}
|
|
}, 50); // Small delay for fade in
|
|
|
|
// Start fade out for the old video and remove it after transition
|
|
if (oldVideoElement) {
|
|
// console.log("Old video element found, starting fade out process.");
|
|
// Add a small delay before starting fade out to overlap with new video fade in
|
|
setTimeout(() => {
|
|
// console.log("Setting old video opacity to 0.");
|
|
oldVideoElement.style.opacity = '0';
|
|
|
|
// Remove the old video element after the transition completes
|
|
const transitionEndHandler = () => {
|
|
// console.log("Old video transitionend event fired.");
|
|
if(oldVideoElement && oldVideoElement.parentElement === bannerElement) { // Check if still in DOM and is the element we expect
|
|
oldVideoElement.remove();
|
|
// console.log("Removed old video element after fade out transition.");
|
|
}
|
|
// Clear the fallback timeout if transitionend worked
|
|
if (fadeOutTimeout) {
|
|
clearTimeout(fadeOutTimeout);
|
|
fadeOutTimeout = null;
|
|
// console.log("Cleared fade out fallback timeout.");
|
|
}
|
|
};
|
|
oldVideoElement.addEventListener('transitionend', transitionEndHandler, { once: true }); // Use { once: true } to automatically remove the listener
|
|
|
|
// Fallback removal for the old element just in case transitionend doesn't fire
|
|
fadeOutTimeout = setTimeout(() => {
|
|
// console.log("Fade out fallback timeout triggered.");
|
|
if (oldVideoElement && oldVideoElement.parentElement === bannerElement && oldVideoElement.style.opacity === '0') {
|
|
oldVideoElement.remove();
|
|
// console.log("Fallback: Removed old video element after timeout.");
|
|
} else if (oldVideoElement && oldVideoElement.parentElement === bannerElement) {
|
|
// console.log("Fallback timeout: Old video still in DOM but opacity not 0, may indicate transition issue.");
|
|
} else {
|
|
// console.log("Fallback timeout: Old video element already removed.");
|
|
}
|
|
fadeOutTimeout = null;
|
|
}, 600); // Slightly longer than transition duration
|
|
|
|
}, 10); // Small delay to overlap fades
|
|
} else {
|
|
// console.log("No old video element found.");
|
|
}
|
|
|
|
|
|
// Listen for the new video to end to switch to the next one
|
|
videoElement.onended = () => {
|
|
// console.log("New video ended, switching to next.");
|
|
currentVideoIndex = (currentVideoIndex + 1) % videoUrls.length;
|
|
// Use a small delay before displaying the next video to allow transition overlap
|
|
setTimeout(() => {
|
|
displayVideo(bannerElement, videoUrls[currentVideoIndex]);
|
|
}, 200); // Delay for overlap
|
|
|
|
};
|
|
|
|
// If the new video is short or duration is not available, set a timeout to switch
|
|
videoElement.onloadedmetadata = () => {
|
|
// console.log("New video metadata loaded.");
|
|
const duration = videoElement.duration;
|
|
const minDisplayTime = 5; // Display each video for at least 5 seconds as a fallback
|
|
// console.log(`New video duration: ${duration}`);
|
|
if (duration === Infinity || (duration > 0 && duration < minDisplayTime)) {
|
|
// console.log(`New video duration is infinity or less than ${minDisplayTime}s (${duration}), setting timeout fallback.`);
|
|
// Clear any previous timeout for this video element
|
|
if (videoElement.timeoutId) {
|
|
clearTimeout(videoElement.timeoutId);
|
|
}
|
|
videoElement.timeoutId = setTimeout(() => {
|
|
// console.log("Timeout triggered for new video, switching to next video.");
|
|
// Only switch if this is still the current video element
|
|
if (videoElement === currentVideoElement) {
|
|
currentVideoIndex = (currentVideoIndex + 1) % videoUrls.length;
|
|
// Use a small delay before displaying the next video to allow transition overlap
|
|
setTimeout(() => {
|
|
displayVideo(bannerElement, videoUrls[currentVideoIndex]);
|
|
}, 200); // Delay for overlap
|
|
} else {
|
|
// console.log("Timeout triggered but video element is no longer current.");
|
|
}
|
|
}, minDisplayTime * 1000);
|
|
}
|
|
};
|
|
|
|
}, 50); // Initial delay before creating and adding the new element
|
|
}
|
|
|
|
// Function to handle the banner logic for both studio and performer pages
|
|
async function handleBannerLogic(bannerElement) {
|
|
// console.log("Handle banner logic triggered for element:", bannerElement);
|
|
|
|
// --- Start: Get plugin settings ---
|
|
try {
|
|
// Fetch plugin settings using the correct plugin ID
|
|
pluginSettings = await csLib.getConfiguration("VideoBanner", { videoBrightness: 65 }); // Default brightness to 65%
|
|
// console.log("Plugin settings loaded:", pluginSettings);
|
|
} catch (error) {
|
|
console.error("Error loading plugin settings:", error);
|
|
// Use default settings if loading fails
|
|
pluginSettings = { videoBrightness: 65 };
|
|
// console.log("Using default plugin settings:", pluginSettings);
|
|
}
|
|
// --- End: Get plugin settings ---
|
|
|
|
|
|
// Add a gradient overlay to the banner element to soften edges
|
|
// (Keep this to soften the video edges outside the detail container)
|
|
bannerElement.style.position = 'relative'; // Ensure positioning context for the gradient overlay
|
|
// Check if a gradient overlay already exists and update/replace it if it does
|
|
let gradientOverlay = bannerElement.querySelector('.gradient-overlay');
|
|
if (!gradientOverlay) {
|
|
// Create a new div for the gradient overlay if it doesn't exist
|
|
gradientOverlay = document.createElement('div');
|
|
gradientOverlay.classList.add('gradient-overlay'); // Add a class for easy identification
|
|
gradientOverlay.style.position = 'absolute';
|
|
gradientOverlay.style.top = '0';
|
|
gradientOverlay.style.left = '0';
|
|
gradientOverlay.style.width = '100%';
|
|
gradientOverlay.style.height = '100%';
|
|
gradientOverlay.style.zIndex = '-1'; // Below the content but above the video
|
|
bannerElement.appendChild(gradientOverlay); // Add the gradient overlay
|
|
// console.log("Added gradient overlay div.");
|
|
}
|
|
// Update the gradient style to a linear gradient from left (dark) to right (transparent)
|
|
gradientOverlay.style.background = 'linear-gradient(to right, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0) 100%)';
|
|
// console.log("Applied/Updated linear gradient overlay style to banner element.");
|
|
|
|
|
|
// Clear any existing slideshow interval and handle removal of the current video element on navigation
|
|
if (slideshowInterval) {
|
|
clearInterval(slideshowInterval);
|
|
slideshowInterval = null;
|
|
// console.log("Cleared existing slideshow interval.");
|
|
}
|
|
// Clear any pending fade out fallback timeout
|
|
if (fadeOutTimeout) {
|
|
clearTimeout(fadeOutTimeout);
|
|
fadeOutTimeout = null;
|
|
// console.log("Cleared fade out fallback timeout on navigation.");
|
|
}
|
|
|
|
if (currentVideoElement) {
|
|
// console.log("Removing current video element on navigation.");
|
|
// Start fade out and remove after transition on navigation
|
|
currentVideoElement.style.opacity = '0';
|
|
const navigationTransitionEndHandler = () => {
|
|
// console.log("Navigation transitionend event fired.");
|
|
if(currentVideoElement && currentVideoElement.parentElement === bannerElement) { // Check if still in DOM and is the element we expect
|
|
currentVideoElement.remove();
|
|
// console.log("Removed current video element on navigation after fade out.");
|
|
}
|
|
};
|
|
// Use a small delay before adding the listener to ensure opacity change takes effect
|
|
setTimeout(() => {
|
|
if(currentVideoElement) {
|
|
currentVideoElement.addEventListener('transitionend', navigationTransitionEndHandler, { once: true });
|
|
}
|
|
}, 10);
|
|
|
|
// Fallback just in case transitionend doesn't fire (e.g., element removed before transition ends)
|
|
setTimeout(() => {
|
|
// console.log("Navigation removal fallback timeout triggered.");
|
|
// Only remove if it hasn't been replaced by a new video yet and is invisible
|
|
if (currentVideoElement && currentVideoElement.parentElement === bannerElement && currentVideoElement.style.opacity === '0') {
|
|
currentVideoElement.remove();
|
|
// console.log("Fallback: Removed old video element on navigation after timeout.");
|
|
} else if (currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
// console.log("Fallback timeout: Current video still in DOM on navigation, may indicate transition issue.");
|
|
} else {
|
|
// console.log("Fallback timeout: Current video element already removed on navigation.");
|
|
}
|
|
// Note: currentVideoElement is cleared below after this if block
|
|
}, 600); // Slightly longer than transition duration
|
|
|
|
currentVideoElement = null; // Clear reference immediately on navigation start
|
|
}
|
|
// Also clear any pending timeouts from previous videos
|
|
// This is handled within the displayVideo function now by checking currentVideoElement
|
|
|
|
// Extract ID from the URL and determine page type
|
|
let id = null;
|
|
let queryFilter = null;
|
|
const studioIdMatch = window.location.pathname.match(/\/studios\/([^\/]+)/);
|
|
const performerIdMatch = window.location.pathname.match(/\/performers\/([^\/]+)/);
|
|
const groupIdMatch = window.location.pathname.match(/\/groups\/([^\/]+)/);
|
|
const tagIdMatch = window.location.pathname.match(/\/tags\/([^\/]+)/);
|
|
|
|
// console.log("Current pathname:", window.location.pathname);
|
|
// console.log("Studio ID match result:", studioIdMatch);
|
|
// console.log("Performer ID match result:", performerIdMatch);
|
|
// console.log("Group ID match result:", groupIdMatch);
|
|
// console.log("Tag ID match result:", tagIdMatch);
|
|
|
|
if (studioIdMatch) {
|
|
id = studioIdMatch[1];
|
|
queryFilter = "studios";
|
|
// console.log("Processing Studio ID:", id);
|
|
} else if (performerIdMatch) {
|
|
id = performerIdMatch[1];
|
|
queryFilter = "performers";
|
|
// console.log("Processing Performer ID:", id);
|
|
} else if (groupIdMatch) {
|
|
id = groupIdMatch[1];
|
|
queryFilter = "groups";
|
|
// console.log("Processing Group ID:", id);
|
|
} else if (tagIdMatch) {
|
|
id = tagIdMatch[1];
|
|
queryFilter = "tags";
|
|
// console.log("Processing Tag ID:", id);
|
|
} else {
|
|
console.error("Could not extract Studio or Performer or Group or Tag ID from URL");
|
|
// Remove any existing video element and gradient if navigation fails
|
|
if (currentVideoElement) {
|
|
// console.log("Navigation failed, removing current video element.");
|
|
currentVideoElement.style.opacity = '0';
|
|
const navFailTransitionEndHandler = () => {
|
|
// console.log("Navigation fail transitionend event fired.");
|
|
if(currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
currentVideoElement.remove();
|
|
// console.log("Removed old video element on navigation fail after fade out.");
|
|
}
|
|
currentVideoElement = null; // Clear reference
|
|
if (fadeOutTimeout) { clearTimeout(fadeOutTimeout); fadeOutTimeout = null; }
|
|
};
|
|
currentVideoElement.addEventListener('transitionend', navFailTransitionEndHandler, { once: true });
|
|
// Fallback removal
|
|
fadeOutTimeout = setTimeout(() => {
|
|
// console.log("Navigation fail removal fallback timeout triggered.");
|
|
if (currentVideoElement && currentVideoElement.parentElement === bannerElement && currentVideoElement.style.opacity === '0') {
|
|
currentVideoElement.remove();
|
|
// console.log("Fallback: Removed old video element after timeout on navigation fail.");
|
|
} else if (currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
// console.log("Fallback timeout: Current video still in DOM on navigation fail.");
|
|
} else {
|
|
// console.log("Fallback timeout: Current video element already removed on navigation fail.");
|
|
}
|
|
currentVideoElement = null;
|
|
fadeOutTimeout = null;
|
|
}, 600);
|
|
}
|
|
// Remove gradient overlay if navigation fails
|
|
const existingOverlay = bannerElement.querySelector('.gradient-overlay');
|
|
if (existingOverlay) {
|
|
existingOverlay.remove();
|
|
// console.log("Removed
|
|
}
|
|
return; // Exit if no valid ID found
|
|
}
|
|
|
|
// console.log("Extracted ID:", id);
|
|
// console.log("Determined query filter:", queryFilter);
|
|
|
|
// Handle data fetching based on page type
|
|
if (queryFilter === "tags") {
|
|
// Determine if it's the scenes, markers, or main tag page
|
|
const pathname = window.location.pathname;
|
|
const isScenesTab = pathname.endsWith('/scenes');
|
|
const isMarkersTab = pathname.endsWith('/markers');
|
|
const isGroupsTab = pathname.endsWith('/groups');
|
|
const isPerformersTab = pathname.endsWith('/performers');
|
|
// console.log("Tag page details:", { pathname, isScenesTab, isMarkersTab, isGroupsTab, isPerformersTab });
|
|
|
|
let scenePreviews = [];
|
|
let markerPreviews = [];
|
|
let groupScenePreviews = []; // Videos from scenes in groups linked to the tag
|
|
let performerScenePreviews = []; // Videos from scenes with performers who have the tag
|
|
|
|
const scenesQuery = `
|
|
query FindScenesForTag($id: ID!) {
|
|
findScenes(scene_filter: { tags: { value: [$id], modifier: INCLUDES_ALL } }) {
|
|
scenes {
|
|
id
|
|
paths {
|
|
preview
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
const markersQuery = `
|
|
query FindSceneMarkersForTag($id: ID!) {
|
|
findSceneMarkers(scene_marker_filter: { tags: { value: [$id], modifier: INCLUDES_ALL } }) {
|
|
scene_markers {
|
|
id
|
|
scene {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
try {
|
|
if (isGroupsTab) { // Fetch scenes from groups linked to the tag for the groups tab
|
|
// console.log("Fetching scenes from groups for tag page...");
|
|
// First, find groups associated with the tag
|
|
const groupsQuery = `
|
|
query FindGroupsForTag($tagId: ID!) {
|
|
findGroups(group_filter: { tags: { value: [$tagId], modifier: INCLUDES_ALL } }) {
|
|
groups {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
const groupsResponse = await csLib.callGQL({ query: groupsQuery, variables: { tagId: id } });
|
|
// console.log("GraphQL Groups Response (Tags page, Groups tab):", groupsResponse);
|
|
|
|
const groupIds = groupsResponse?.findGroups?.groups?.map(group => group.id) || [];
|
|
// console.log("Group IDs associated with tag:", groupIds);
|
|
|
|
if (groupIds.length > 0) {
|
|
// Then, find scenes associated with these groups
|
|
const scenesByGroupQuery = `
|
|
query FindScenesByGroupIds($groupIds: [ID!]) {
|
|
findScenes(scene_filter: { groups: { value: $groupIds, modifier: INCLUDES } }) {
|
|
scenes {
|
|
id
|
|
paths {
|
|
preview
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
const scenesByGroupResponse = await csLib.callGQL({ query: scenesByGroupQuery, variables: { groupIds } });
|
|
// console.log("GraphQL Scenes by Group Response (Tags page, Groups tab):", scenesByGroupResponse);
|
|
|
|
groupScenePreviews = scenesByGroupResponse?.findScenes?.scenes
|
|
?.filter(scene => scene.paths?.preview)
|
|
.map(scene => `${scene.paths.preview}?_ts=${Date.now()}`) || [];
|
|
// console.log("Group scene preview URLs (Tags page, Groups tab):", groupScenePreviews);
|
|
|
|
// Shuffle the collected group scene preview URLs
|
|
for (let i = groupScenePreviews.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[groupScenePreviews[i], groupScenePreviews[j]] = [groupScenePreviews[j], groupScenePreviews[i]];
|
|
}
|
|
// console.log("Shuffled group scene preview URLs (Tags page, Groups tab):", groupScenePreviews);
|
|
} else {
|
|
// console.log("No groups found for this tag.");
|
|
}
|
|
|
|
}
|
|
|
|
if (isPerformersTab) { // Fetch scenes from performers who have the tag for the performers tab
|
|
// console.log("Fetching scenes from performers for tag page...");
|
|
// First, find performers associated with the tag
|
|
const performersQuery = `
|
|
query FindPerformersForTag($tagId: ID!) {
|
|
findPerformers(performer_filter: { tags: { value: [$tagId], modifier: INCLUDES_ALL } }) {
|
|
performers {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
const performersResponse = await csLib.callGQL({ query: performersQuery, variables: { tagId: id } });
|
|
// console.log("GraphQL Performers Response (Tags page, Performers tab):", performersResponse);
|
|
|
|
const performerIds = performersResponse?.findPerformers?.performers?.map(performer => performer.id) || [];
|
|
// console.log("Performer IDs associated with tag:", performerIds);
|
|
|
|
if (performerIds.length > 0) {
|
|
// Then, find scenes associated with these performers
|
|
const scenesByPerformerQuery = `
|
|
query FindScenesByPerformerIds($performerIds: [ID!]) {
|
|
findScenes(scene_filter: { performers: { value: $performerIds, modifier: INCLUDES } }) {
|
|
scenes {
|
|
id
|
|
paths {
|
|
preview
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
const scenesByPerformerResponse = await csLib.callGQL({ query: scenesByPerformerQuery, variables: { performerIds } });
|
|
// console.log("GraphQL Scenes by Performer Response (Tags page, Performers tab):", scenesByPerformerResponse);
|
|
|
|
performerScenePreviews = scenesByPerformerResponse?.findScenes?.scenes
|
|
?.filter(scene => scene.paths?.preview)
|
|
.map(scene => `${scene.paths.preview}?_ts=${Date.now()}`) || [];
|
|
// console.log("Performer scene preview URLs (Tags page, Performers tab):", performerScenePreviews);
|
|
|
|
// Shuffle the collected performer scene preview URLs
|
|
for (let i = performerScenePreviews.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[performerScenePreviews[i], performerScenePreviews[j]] = [performerScenePreviews[j], performerScenePreviews[i]];
|
|
}
|
|
// console.log("Shuffled performer scene preview URLs (Tags page, Performers tab):", performerScenePreviews);
|
|
} else {
|
|
// console.log("No performers found for this tag.");
|
|
}
|
|
|
|
}
|
|
|
|
if (isScenesTab || (!isScenesTab && !isMarkersTab && !isGroupsTab && !isPerformersTab)) { // Fetch scenes for scenes tab or main tag page
|
|
// console.log("Fetching scenes for tag page...");
|
|
const scenesResponse = await csLib.callGQL({ query: scenesQuery, variables: { id } });
|
|
// console.log("GraphQL Scenes Response (Tags page):", scenesResponse);
|
|
scenePreviews = scenesResponse?.findScenes?.scenes
|
|
?.filter(scene => scene.paths?.preview)
|
|
.map(scene => `${scene.paths.preview}?_ts=${Date.now()}`) || [];
|
|
// console.log("Scene preview URLs (Tags page):", scenePreviews);
|
|
}
|
|
|
|
if (isMarkersTab || (!isScenesTab && !isMarkersTab && !isGroupsTab && !isPerformersTab)) { // Fetch markers for markers tab or main tag page
|
|
// console.log("Fetching markers for tag page...");
|
|
const markersResponse = await csLib.callGQL({ query: markersQuery, variables: { id } });
|
|
// console.log("GraphQL Markers Response (Tags page):", markersResponse);
|
|
markerPreviews = markersResponse?.findSceneMarkers?.scene_markers
|
|
?.filter(marker => marker.id && marker.scene?.id)
|
|
.map(marker => `/scene/${marker.scene.id}/scene_marker/${marker.id}/stream?_ts=${Date.now()}`) || [];
|
|
// console.log("Marker preview URLs (Tags page):", markerPreviews);
|
|
}
|
|
|
|
// Combine URLs based on the tab, prioritizing markers on the main page
|
|
if (isGroupsTab) {
|
|
videoUrls = groupScenePreviews; // Only group scene previews on groups tab
|
|
// console.log("Video URLs (Groups tab):", videoUrls);
|
|
} else if (isPerformersTab) {
|
|
videoUrls = performerScenePreviews; // Only performer scene previews on performers tab
|
|
// console.log("Video URLs (Performers tab):", videoUrls);
|
|
} else if (isMarkersTab) {
|
|
videoUrls = markerPreviews; // Only marker previews on markers tab
|
|
// console.log("Video URLs (Markers tab):", videoUrls);
|
|
} else if (isScenesTab) {
|
|
videoUrls = scenePreviews; // Only scene previews on scenes tab
|
|
// console.log("Video URLs (Scenes tab):", videoUrls);
|
|
} else { // Main tag page
|
|
videoUrls = [...markerPreviews, ...scenePreviews]; // Marker previews then scene previews
|
|
// console.log("Video URLs (Main Tags page, prioritized markers):", videoUrls);
|
|
}
|
|
|
|
// Proceed with displaying videos using the collected list
|
|
if (!videoUrls || videoUrls.length === 0) {
|
|
// console.log("No preview videos found for this tag based on the current tab.");
|
|
// Handle case where no valid videos are found
|
|
if (currentVideoElement) {
|
|
// console.log("No videos found, removing current video element.");
|
|
currentVideoElement.style.opacity = '0';
|
|
const noVideosTransitionEndHandler = () => {
|
|
// console.log("No videos transitionend event fired.");
|
|
if(currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
currentVideoElement.remove();
|
|
// console.log("Removed old video element when no new videos found after fade out.");
|
|
}
|
|
currentVideoElement = null; // Clear reference
|
|
if (fadeOutTimeout) { clearTimeout(fadeOutTimeout); fadeOutTimeout = null; }
|
|
};
|
|
currentVideoElement.addEventListener('transitionend', noVideosTransitionEndHandler, { once: true });
|
|
// Fallback removal
|
|
fadeOutTimeout = setTimeout(() => {
|
|
// console.log("No videos removal fallback timeout triggered.");
|
|
if (currentVideoElement && currentVideoElement.parentElement === bannerElement && currentVideoElement.style.opacity === '0') {
|
|
currentVideoElement.remove();
|
|
// console.log("Fallback: Removed old video element after timeout when no new videos found.");
|
|
} else if (currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
// console.log("Fallback timeout: Current video still in DOM when no new videos found.");
|
|
} else {
|
|
// console.log("Fallback timeout: Current video element already removed when no new videos found.");
|
|
}
|
|
currentVideoElement = null;
|
|
fadeOutTimeout = null;
|
|
}, 600);
|
|
}
|
|
|
|
// Remove gradient overlay if no videos are found
|
|
const existingOverlay = bannerElement.querySelector('.gradient-overlay');
|
|
if (existingOverlay) {
|
|
existingOverlay.remove();
|
|
// console.log("Removed gradient overlay as no videos were found.");
|
|
}
|
|
|
|
return; // Exit if no videos found
|
|
}
|
|
|
|
// Select a random starting video index based on the tab
|
|
currentVideoIndex = Math.floor(Math.random() * videoUrls.length);
|
|
if (isGroupsTab) {
|
|
// console.log(`Starting slideshow with a random group scene preview at index: ${currentVideoIndex} (out of ${videoUrls.length} total videos) on Groups tab.`);
|
|
} else if (isPerformersTab) {
|
|
// console.log(`Starting slideshow with a random performer scene preview at index: ${currentVideoIndex} (out of ${videoUrls.length} total videos) on Performers tab.`);
|
|
} else if (isMarkersTab) {
|
|
// console.log(`Starting slideshow with a random marker preview at index: ${currentVideoIndex} (out of ${videoUrls.length} total videos) on Markers tab.`);
|
|
} else if (isScenesTab) {
|
|
// console.log(`Starting slideshow with a random scene preview at index: ${currentVideoIndex} (out of ${videoUrls.length} total videos) on Scenes tab.`);
|
|
} else if (markerPreviews.length > 0) { // Main tag page, prioritize markers
|
|
currentVideoIndex = Math.floor(Math.random() * markerPreviews.length);
|
|
// console.log(`Starting slideshow with a random marker preview at index: ${currentVideoIndex} (out of ${markerPreviews.length} markers) on Main tag page.`);
|
|
} else { // Main tag page, no markers, use scenes
|
|
// console.log(`No marker previews found. Starting slideshow with a random scene preview at index: ${currentVideoIndex} (out of ${videoUrls.length} total videos) on Main tag page.`);
|
|
}
|
|
|
|
// Display the first video
|
|
displayVideo(bannerElement, videoUrls[currentVideoIndex]);
|
|
|
|
} catch (error) {
|
|
console.error("Error fetching data for tag page:", error);
|
|
if (error.response) {
|
|
console.error("GraphQL Error Response Details:", error.response);
|
|
}
|
|
// Optionally handle error display or fallback UI for tag pages
|
|
}
|
|
} else { // Handle studios, performers, groups with the existing logic
|
|
// GraphQL query to get scenes for the studio, performer, or group with generated preview paths
|
|
// Dynamically build the filter based on page type
|
|
const query = `
|
|
query FindScenes($id: ID!) {
|
|
findScenes(scene_filter: { ${queryFilter}: { value: [$id], modifier: INCLUDES_ALL } }) {
|
|
scenes {
|
|
id
|
|
paths {
|
|
preview
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
// console.log("Generated GraphQL query:", query);
|
|
|
|
try {
|
|
const response = await csLib.callGQL({ query, variables: { id } });
|
|
// console.log("GraphQL Response:", response);
|
|
|
|
// Log the scenes array before filtering
|
|
const allScenes = response?.findScenes?.scenes;
|
|
// console.log(`All scenes returned by query (${queryFilter} page):`, allScenes);
|
|
|
|
// Populate videoUrls with all scenes that have a truthy preview path
|
|
// Add cache-busting timestamp here
|
|
videoUrls = allScenes?.filter(scene => scene.paths?.preview).map(scene => `${scene.paths.preview}?_ts=${Date.now()}`);
|
|
|
|
if (!videoUrls || videoUrls.length === 0) {
|
|
// console.log(`No preview videos found for this ${queryFilter}.`);
|
|
// TODO: Handle case where no valid videos are found (e.g., display a placeholder)
|
|
// If no videos are found, ensure any existing video element is removed with transition
|
|
if (currentVideoElement) {
|
|
// console.log("No videos found, removing current video element.");
|
|
currentVideoElement.style.opacity = '0';
|
|
const noVideosTransitionEndHandler = () => {
|
|
// console.log("No videos transitionend event fired.");
|
|
if(currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
currentVideoElement.remove();
|
|
// console.log("Removed old video element when no new videos found after fade out.");
|
|
}
|
|
currentVideoElement = null; // Clear reference
|
|
if (fadeOutTimeout) { clearTimeout(fadeOutTimeout); fadeOutTimeout = null; }
|
|
};
|
|
currentVideoElement.addEventListener('transitionend', noVideosTransitionEndHandler, { once: true });
|
|
// Fallback removal
|
|
fadeOutTimeout = setTimeout(() => {
|
|
// console.log("No videos removal fallback timeout triggered.");
|
|
if (currentVideoElement && currentVideoElement.parentElement === bannerElement && currentVideoElement.style.opacity === '0') {
|
|
currentVideoElement.remove();
|
|
// console.log("Fallback: Removed old video element after timeout when no new videos found.");
|
|
} else if (currentVideoElement && currentVideoElement.parentElement === bannerElement) {
|
|
// console.log("Fallback timeout: Current video still in DOM when no new videos found.");
|
|
} else {
|
|
// console.log("Fallback timeout: Current video element already removed when no new videos found.");
|
|
}
|
|
currentVideoElement = null;
|
|
fadeOutTimeout = null;
|
|
}, 600);
|
|
}
|
|
|
|
// Remove gradient overlay if no videos are found
|
|
const existingOverlay = bannerElement.querySelector('.gradient-overlay');
|
|
if (existingOverlay) {
|
|
existingOverlay.remove();
|
|
// console.log("Removed gradient overlay as no videos were found.");
|
|
}
|
|
|
|
return; // Exit if no videos found
|
|
}
|
|
|
|
// console.log(`Collected potential video URLs for slideshow (${queryFilter} page):`, videoUrls);
|
|
|
|
// Select a random starting video index
|
|
currentVideoIndex = Math.floor(Math.random() * videoUrls.length);
|
|
// console.log(`Starting slideshow at random index: ${currentVideoIndex}`);
|
|
|
|
// Display the first video (error handling for 404 will be done during playback attempt)
|
|
displayVideo(bannerElement, videoUrls[currentVideoIndex]);
|
|
|
|
|
|
} catch (error) {
|
|
console.error(`Error fetching scenes or processing data for ${queryFilter} page:`, error);
|
|
// Log the error response details if available
|
|
if (error.response) {
|
|
console.error("GraphQL Error Response Details:", error.response);
|
|
}
|
|
// Optionally handle error display or fallback UI for other page types
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use separate PathElementListeners for studios and performers
|
|
csLib.PathElementListener("/studios/", "div.detail-header", async (bannerElement) => {
|
|
// console.log("Studio banner element found:", bannerElement);
|
|
handleBannerLogic(bannerElement);
|
|
});
|
|
|
|
csLib.PathElementListener("/performers/", "div.detail-header", async (bannerElement) => {
|
|
// console.log("Performer banner element found:", bannerElement);
|
|
handleBannerLogic(bannerElement);
|
|
});
|
|
|
|
// Use PathElementListener for groups
|
|
csLib.PathElementListener("/groups/", "div.detail-header", async (bannerElement) => {
|
|
// console.log("Group banner element found:", bannerElement);
|
|
handleBannerLogic(bannerElement);
|
|
});
|
|
|
|
// Use PathElementListener for tags
|
|
csLib.PathElementListener("/tags/", "div.detail-header", async (bannerElement) => {
|
|
// console.log("Tag banner element found:", bannerElement);
|
|
handleBannerLogic(bannerElement);
|
|
});
|
|
})(); |