From 536365e7497260c59fb067855d37856468beccc8 Mon Sep 17 00:00:00 2001
From: Admin9705 <9705@duck.com>
Date: Fri, 6 Feb 2026 22:58:26 -0500
Subject: [PATCH] update
---
frontend/static/css/movie-hunt-detail.css | 5 +-
.../js/modules/features/movie-hunt-detail.js | 138 ++---
.../features/requestarr/requestarr-content.js | 7 +-
.../features/requestarr/requestarr-detail.js | 533 ++++++++++++++++++
frontend/templates/components/scripts.html | 1 +
5 files changed, 611 insertions(+), 73 deletions(-)
create mode 100644 frontend/static/js/modules/features/requestarr/requestarr-detail.js
diff --git a/frontend/static/css/movie-hunt-detail.css b/frontend/static/css/movie-hunt-detail.css
index beccd914..1595cb0d 100644
--- a/frontend/static/css/movie-hunt-detail.css
+++ b/frontend/static/css/movie-hunt-detail.css
@@ -208,7 +208,7 @@
/* Instance Selector */
.movie-detail-instance-selector {
- display: flex;
+ display: inline-flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
@@ -217,6 +217,7 @@
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
+ max-width: 400px;
}
.movie-detail-instance-selector label {
@@ -227,6 +228,7 @@
align-items: center;
gap: 8px;
margin: 0;
+ white-space: nowrap;
}
.movie-detail-instance-selector label i {
@@ -243,6 +245,7 @@
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
+ min-width: 200px;
}
.movie-detail-select:hover {
diff --git a/frontend/static/js/modules/features/movie-hunt-detail.js b/frontend/static/js/modules/features/movie-hunt-detail.js
index 67d493cd..ef865e47 100644
--- a/frontend/static/js/modules/features/movie-hunt-detail.js
+++ b/frontend/static/js/modules/features/movie-hunt-detail.js
@@ -8,8 +8,8 @@
window.MovieHuntDetail = {
currentMovie: null,
tmdbApiKey: null,
- radarrInstances: [],
- selectedInstance: null,
+ movieHuntInstances: [],
+ selectedInstanceId: null,
init() {
console.log('[MovieHuntDetail] Module initialized');
@@ -18,9 +18,9 @@
window.addEventListener('popstate', (e) => {
if (e.state && e.state.movieDetail) {
// User navigated to a movie detail via back/forward
- this.openDetail(e.state.movieDetail, e.state.options || {}, true);
- } else {
- // User navigated away from movie detail
+ this.openDetail(e.state.movieDetail, {}, true);
+ } else if (!e.state || !e.state.requestarrMovieDetail) {
+ // User navigated away from movie detail (and not to Requestarr detail)
this.closeDetail(true);
}
});
@@ -68,21 +68,20 @@
},
/**
- * Open detail view for a movie
+ * Open detail view for a movie from Movie Hunt
* @param {Object} movie - Movie data with at least title, year, tmdb_id
- * @param {Object} options - Optional config: { source: 'movie-hunt' | 'requestarr', suggestedInstance: string }
+ * @param {Object} options - Optional config (unused, kept for compatibility)
* @param {Boolean} fromHistory - True if opened from browser history (don't push state again)
*/
async openDetail(movie, options = {}, fromHistory = false) {
if (!movie) return;
this.currentMovie = movie;
- this.options = options || {};
- console.log('[MovieHuntDetail] Opening detail for:', movie.title, 'from', this.options.source || 'unknown');
+ console.log('[MovieHuntDetail] Opening detail for:', movie.title);
- // Load Radarr instances if not already loaded
- if (this.radarrInstances.length === 0) {
- await this.loadRadarrInstances();
+ // Load Movie Hunt instances if not already loaded
+ if (this.movieHuntInstances.length === 0) {
+ await this.loadMovieHuntInstances();
}
// Get or create detail view container
@@ -104,7 +103,7 @@
const tmdbId = movie.tmdb_id || movie.id;
const url = `${window.location.pathname}${window.location.search}#movie/${tmdbId}`;
history.pushState(
- { movieDetail: movie, options: this.options },
+ { movieDetail: movie },
movie.title,
url
);
@@ -124,6 +123,7 @@
const details = await this.fetchMovieDetails(tmdbId);
if (details) {
+ console.log('[MovieHuntDetail] Rendering detail with instances:', this.movieHuntInstances.length);
detailView.innerHTML = this.renderMovieDetail(details, movie);
this.setupDetailInteractions();
} else {
@@ -246,20 +246,30 @@
}
}
- // Build instance selector HTML
+ // Build Movie Hunt instance selector (always show, even if loading)
let instanceSelectorHTML = '';
- if (this.radarrInstances.length > 0) {
+ if (this.movieHuntInstances.length > 0) {
instanceSelectorHTML = `
`;
+ } else {
+ // Show loading state if instances haven't loaded yet
+ instanceSelectorHTML = `
+
+
+
+
+ `;
}
return `
@@ -395,8 +405,8 @@
const instanceSelect = document.getElementById('movie-detail-instance-select');
if (instanceSelect) {
instanceSelect.addEventListener('change', async () => {
- this.selectedInstance = instanceSelect.value;
- console.log('[MovieHuntDetail] Instance changed to:', this.selectedInstance);
+ this.selectedInstanceId = parseInt(instanceSelect.value);
+ console.log('[MovieHuntDetail] Instance changed to:', this.selectedInstanceId);
// Update movie status for new instance
await this.updateMovieStatus();
@@ -410,18 +420,7 @@
const requestBtn = document.getElementById('movie-detail-request-btn');
if (requestBtn && this.currentMovie) {
requestBtn.addEventListener('click', () => {
- // Don't close detail page - just open the modal on top of it
-
- // If called from Requestarr, use Requestarr modal with selected instance
- if (this.options.source === 'requestarr' && window.RequestarrDiscover && window.RequestarrDiscover.modal) {
- window.RequestarrDiscover.modal.openModal(
- this.currentMovie.tmdb_id,
- 'movie',
- this.selectedInstance
- );
- }
- // Otherwise use Movie Hunt request modal
- else if (window.MovieHunt && window.MovieHunt.openMovieHuntRequestModal) {
+ if (window.MovieHunt && window.MovieHunt.openMovieHuntRequestModal) {
window.MovieHunt.openMovieHuntRequestModal(this.currentMovie);
}
});
@@ -446,11 +445,10 @@
backdrop_path: details.backdrop_path,
overview: details.overview,
vote_average: details.vote_average,
- in_library: false,
- in_cooldown: false
+ in_library: false
};
// Open detail and update URL (fromHistory = false)
- this.openDetail(movieData, this.options || {}, false);
+ this.openDetail(movieData, {}, false);
}
} catch (error) {
console.error('[MovieHuntDetail] Error opening similar movie:', error);
@@ -469,55 +467,67 @@
document.addEventListener('keydown', escHandler);
},
- async loadRadarrInstances() {
+ async loadMovieHuntInstances() {
+ console.log('[MovieHuntDetail] Starting to load Movie Hunt instances...');
try {
- const response = await fetch('./api/requestarr/instances/radarr');
+ const response = await fetch('./api/movie-hunt/instances');
const data = await response.json();
+ console.log('[MovieHuntDetail] Instances API response:', data);
if (data.instances && data.instances.length > 0) {
- this.radarrInstances = data.instances;
+ this.movieHuntInstances = data.instances;
- // Set initial selected instance (prioritize suggested instance from options)
- if (this.options.suggestedInstance) {
- this.selectedInstance = this.options.suggestedInstance;
- } else if (!this.selectedInstance) {
- this.selectedInstance = this.radarrInstances[0].name;
+ // Set initial selected instance (Movie Hunt uses integer IDs)
+ if (!this.selectedInstanceId) {
+ // Get current instance from server
+ const currentResponse = await fetch('./api/movie-hunt/current-instance');
+ const currentData = await currentResponse.json();
+ this.selectedInstanceId = currentData.instance_id || this.movieHuntInstances[0].id;
}
- console.log('[MovieHuntDetail] Loaded', this.radarrInstances.length, 'Radarr instances, selected:', this.selectedInstance);
+ console.log('[MovieHuntDetail] Loaded', this.movieHuntInstances.length, 'Movie Hunt instances, selected:', this.selectedInstanceId);
} else {
- this.radarrInstances = [];
- this.selectedInstance = null;
+ console.log('[MovieHuntDetail] No instances found in response');
+ this.movieHuntInstances = [];
+ this.selectedInstanceId = null;
}
} catch (error) {
- console.error('[MovieHuntDetail] Error loading Radarr instances:', error);
- this.radarrInstances = [];
- this.selectedInstance = null;
+ console.error('[MovieHuntDetail] Error loading Movie Hunt instances:', error);
+ this.movieHuntInstances = [];
+ this.selectedInstanceId = null;
}
},
- async checkMovieStatus(tmdbId, instanceName) {
- if (!instanceName) return { in_library: false, in_cooldown: false };
+ async checkMovieStatus(tmdbId, instanceId) {
+ if (!instanceId) return { in_library: false };
try {
- const response = await fetch(`./api/requestarr/movie-status?tmdb_id=${tmdbId}&instance=${encodeURIComponent(instanceName)}`);
+ // Check Movie Hunt collection for this instance
+ const response = await fetch(`./api/movie-hunt/collection?instance_id=${instanceId}`);
const data = await response.json();
- return {
- in_library: data.in_library || false,
- in_cooldown: (data.cooldown_status && data.cooldown_status.in_cooldown) || false
- };
+ // Find this movie in the collection
+ const items = data.items || [];
+ const movie = items.find(item => item.tmdb_id === tmdbId);
+
+ if (movie) {
+ return {
+ in_library: movie.status === 'available'
+ };
+ }
+
+ return { in_library: false };
} catch (error) {
console.error('[MovieHuntDetail] Error checking movie status:', error);
- return { in_library: false, in_cooldown: false };
+ return { in_library: false };
}
},
async updateMovieStatus() {
- if (!this.currentMovie || !this.selectedInstance) return;
+ if (!this.currentMovie || !this.selectedInstanceId) return;
const tmdbId = this.currentMovie.tmdb_id || this.currentMovie.id;
- const status = await this.checkMovieStatus(tmdbId, this.selectedInstance);
+ const status = await this.checkMovieStatus(tmdbId, this.selectedInstanceId);
// Update the action button based on new status
const actionsContainer = document.querySelector('.movie-detail-actions');
@@ -526,8 +536,6 @@
if (status.in_library) {
actionButton = '';
- } else if (status.in_cooldown) {
- actionButton = '';
} else {
actionButton = '';
}
@@ -538,13 +546,7 @@
const requestBtn = document.getElementById('movie-detail-request-btn');
if (requestBtn) {
requestBtn.addEventListener('click', () => {
- if (this.options.source === 'requestarr' && window.RequestarrDiscover && window.RequestarrDiscover.modal) {
- window.RequestarrDiscover.modal.openModal(
- this.currentMovie.tmdb_id,
- 'movie',
- this.selectedInstance
- );
- } else if (window.MovieHunt && window.MovieHunt.openMovieHuntRequestModal) {
+ if (window.MovieHunt && window.MovieHunt.openMovieHuntRequestModal) {
window.MovieHunt.openMovieHuntRequestModal(this.currentMovie);
}
});
diff --git a/frontend/static/js/modules/features/requestarr/requestarr-content.js b/frontend/static/js/modules/features/requestarr/requestarr-content.js
index 540fa180..968b8bca 100644
--- a/frontend/static/js/modules/features/requestarr/requestarr-content.js
+++ b/frontend/static/js/modules/features/requestarr/requestarr-content.js
@@ -982,8 +982,8 @@ export class RequestarrContent {
return;
}
- // For movies, open detail page if available
- if (item.media_type === 'movie' && window.MovieHuntDetail && window.MovieHuntDetail.openDetail) {
+ // For movies, open Requestarr detail page if available
+ if (item.media_type === 'movie' && window.RequestarrDetail && window.RequestarrDetail.openDetail) {
const movieData = {
tmdb_id: item.tmdb_id,
id: item.tmdb_id,
@@ -996,8 +996,7 @@ export class RequestarrContent {
in_library: inLibrary,
in_cooldown: inCooldown
};
- window.MovieHuntDetail.openDetail(movieData, {
- source: 'requestarr',
+ window.RequestarrDetail.openDetail(movieData, {
suggestedInstance: card.suggestedInstance
});
} else {
diff --git a/frontend/static/js/modules/features/requestarr/requestarr-detail.js b/frontend/static/js/modules/features/requestarr/requestarr-detail.js
new file mode 100644
index 00000000..fdc4a738
--- /dev/null
+++ b/frontend/static/js/modules/features/requestarr/requestarr-detail.js
@@ -0,0 +1,533 @@
+/**
+ * Requestarr Movie Detail Page - Detail view for Requestarr movies
+ * Handles Radarr instances and movie status checking
+ */
+(function() {
+ 'use strict';
+
+ window.RequestarrDetail = {
+ currentMovie: null,
+ tmdbApiKey: null,
+ radarrInstances: [],
+ selectedInstanceName: null,
+
+ init() {
+ console.log('[RequestarrDetail] Module initialized');
+
+ // Listen for browser back/forward buttons
+ window.addEventListener('popstate', (e) => {
+ if (e.state && e.state.requestarrMovieDetail) {
+ // User navigated to a movie detail via back/forward
+ this.openDetail(e.state.requestarrMovieDetail, e.state.options || {}, true);
+ } else if (e.state && !e.state.movieDetail) {
+ // User navigated away from movie detail (but not to Movie Hunt detail)
+ this.closeDetail(true);
+ }
+ });
+ },
+
+ /**
+ * Open detail view for a movie from Requestarr
+ * @param {Object} movie - Movie data with at least title, year, tmdb_id
+ * @param {Object} options - Optional config: { suggestedInstance: string }
+ * @param {Boolean} fromHistory - True if opened from browser history (don't push state again)
+ */
+ async openDetail(movie, options = {}, fromHistory = false) {
+ if (!movie) return;
+
+ this.currentMovie = movie;
+ this.options = options || {};
+ console.log('[RequestarrDetail] Opening detail for:', movie.title);
+
+ // Load Radarr instances if not already loaded
+ if (this.radarrInstances.length === 0) {
+ await this.loadRadarrInstances();
+ }
+
+ // Get or create detail view container
+ let detailView = document.getElementById('requestarr-detail-view');
+ if (!detailView) {
+ detailView = document.createElement('div');
+ detailView.id = 'requestarr-detail-view';
+ detailView.className = 'movie-detail-view';
+ document.body.appendChild(detailView);
+ }
+
+ // Show loading state
+ detailView.innerHTML = this.getLoadingHTML();
+ detailView.classList.add('active');
+
+ // Add to browser history (so back button works)
+ if (!fromHistory) {
+ const tmdbId = movie.tmdb_id || movie.id;
+ const url = `${window.location.pathname}${window.location.search}#requestarr-movie/${tmdbId}`;
+ history.pushState(
+ { requestarrMovieDetail: movie, options: this.options },
+ movie.title,
+ url
+ );
+ }
+
+ // Setup close button
+ setTimeout(() => {
+ const closeBtn = detailView.querySelector('.movie-detail-close');
+ if (closeBtn) {
+ closeBtn.addEventListener('click', () => this.closeDetail());
+ }
+ }, 0);
+
+ try {
+ // Fetch full movie details from TMDB
+ const tmdbId = movie.tmdb_id || movie.id;
+ const details = await this.fetchMovieDetails(tmdbId);
+
+ if (details) {
+ detailView.innerHTML = this.renderMovieDetail(details, movie);
+ this.setupDetailInteractions();
+ } else {
+ detailView.innerHTML = this.getErrorHTML('Failed to load movie details');
+ }
+ } catch (error) {
+ console.error('[RequestarrDetail] Error loading details:', error);
+ detailView.innerHTML = this.getErrorHTML('Failed to load movie details');
+ }
+ },
+
+ closeDetail(fromHistory = false) {
+ const detailView = document.getElementById('requestarr-detail-view');
+ if (detailView) {
+ detailView.classList.remove('active');
+ }
+
+ // Remove from browser history if not already navigating
+ if (!fromHistory && detailView && detailView.classList.contains('active')) {
+ history.back();
+ }
+ },
+
+ async fetchMovieDetails(tmdbId) {
+ if (!tmdbId) return null;
+
+ try {
+ // Get TMDB API key from Movie Hunt (shared endpoint)
+ if (!this.tmdbApiKey) {
+ const keyResponse = await fetch('./api/movie-hunt/tmdb-key');
+ if (!keyResponse.ok) throw new Error('TMDB key endpoint failed: ' + keyResponse.status);
+ const keyData = await keyResponse.json();
+ this.tmdbApiKey = keyData.api_key;
+ }
+
+ if (!this.tmdbApiKey) {
+ console.error('[RequestarrDetail] No TMDB API key available');
+ return null;
+ }
+
+ // Fetch movie details with credits and similar movies
+ const url = `https://api.themoviedb.org/3/movie/${tmdbId}?api_key=${this.tmdbApiKey}&append_to_response=credits,similar,videos,release_dates`;
+ const response = await fetch(url);
+
+ if (!response.ok) {
+ throw new Error(`TMDB API returned ${response.status}`);
+ }
+
+ const data = await response.json();
+ return data;
+ } catch (error) {
+ console.error('[RequestarrDetail] Error fetching from TMDB:', error);
+ return null;
+ }
+ },
+
+ async loadRadarrInstances() {
+ try {
+ const response = await fetch('./api/requestarr/instances/radarr');
+ const data = await response.json();
+
+ if (data.instances && data.instances.length > 0) {
+ this.radarrInstances = data.instances;
+
+ // Set initial selected instance (prioritize suggested instance from options)
+ if (this.options.suggestedInstance) {
+ this.selectedInstanceName = this.options.suggestedInstance;
+ } else if (!this.selectedInstanceName) {
+ this.selectedInstanceName = this.radarrInstances[0].name;
+ }
+
+ console.log('[RequestarrDetail] Loaded', this.radarrInstances.length, 'Radarr instances, selected:', this.selectedInstanceName);
+ } else {
+ this.radarrInstances = [];
+ this.selectedInstanceName = null;
+ }
+ } catch (error) {
+ console.error('[RequestarrDetail] Error loading Radarr instances:', error);
+ this.radarrInstances = [];
+ this.selectedInstanceName = null;
+ }
+ },
+
+ async checkMovieStatus(tmdbId, instanceName) {
+ if (!instanceName) return { in_library: false, in_cooldown: false };
+
+ try {
+ const response = await fetch(`./api/requestarr/movie-status?tmdb_id=${tmdbId}&instance=${encodeURIComponent(instanceName)}`);
+ const data = await response.json();
+
+ return {
+ in_library: data.in_library || false,
+ in_cooldown: (data.cooldown_status && data.cooldown_status.in_cooldown) || false
+ };
+ } catch (error) {
+ console.error('[RequestarrDetail] Error checking movie status:', error);
+ return { in_library: false, in_cooldown: false };
+ }
+ },
+
+ async updateMovieStatus() {
+ if (!this.currentMovie || !this.selectedInstanceName) return;
+
+ const tmdbId = this.currentMovie.tmdb_id || this.currentMovie.id;
+ const status = await this.checkMovieStatus(tmdbId, this.selectedInstanceName);
+
+ // Update the action button based on new status
+ const actionsContainer = document.querySelector('.movie-detail-actions');
+ if (actionsContainer) {
+ let actionButton = '';
+
+ if (status.in_library) {
+ actionButton = '';
+ } else if (status.in_cooldown) {
+ actionButton = '';
+ } else {
+ actionButton = '';
+ }
+
+ actionsContainer.innerHTML = actionButton;
+
+ // Re-setup request button if it exists
+ const requestBtn = document.getElementById('requestarr-detail-request-btn');
+ if (requestBtn) {
+ requestBtn.addEventListener('click', () => {
+ if (window.RequestarrDiscover && window.RequestarrDiscover.modal) {
+ window.RequestarrDiscover.modal.openModal(
+ this.currentMovie.tmdb_id,
+ 'movie',
+ this.selectedInstanceName
+ );
+ }
+ });
+ }
+ }
+ },
+
+ renderMovieDetail(details, originalMovie) {
+ const backdropUrl = details.backdrop_path
+ ? `https://image.tmdb.org/t/p/original${details.backdrop_path}`
+ : (details.poster_path ? `https://image.tmdb.org/t/p/original${details.poster_path}` : '');
+
+ const posterUrl = details.poster_path
+ ? `https://image.tmdb.org/t/p/w500${details.poster_path}`
+ : './static/images/blackout.jpg';
+
+ const rating = details.vote_average ? Number(details.vote_average).toFixed(1) : 'N/A';
+ const year = details.release_date ? new Date(details.release_date).getFullYear() : 'N/A';
+ const runtime = details.runtime ? `${Math.floor(details.runtime / 60)}h ${details.runtime % 60}m` : 'N/A';
+
+ const genres = details.genres && details.genres.length > 0
+ ? details.genres.map(g => `${this.escapeHtml(g.name)}`).join('')
+ : 'Unknown';
+
+ const overview = details.overview || 'No overview available.';
+
+ // Check status from original movie data
+ const inLibrary = originalMovie.in_library || false;
+ const inCooldown = originalMovie.in_cooldown || false;
+
+ let actionButton = '';
+
+ if (inLibrary) {
+ actionButton = '';
+ } else if (inCooldown) {
+ actionButton = '';
+ } else {
+ actionButton = '';
+ }
+
+ // Director and main cast
+ let director = 'N/A';
+ let mainCast = [];
+
+ if (details.credits) {
+ if (details.credits.crew) {
+ const directorObj = details.credits.crew.find(c => c.job === 'Director');
+ if (directorObj) director = directorObj.name;
+ }
+ if (details.credits.cast) {
+ mainCast = details.credits.cast.slice(0, 10);
+ }
+ }
+
+ // Similar movies
+ let similarMovies = [];
+ if (details.similar && details.similar.results) {
+ similarMovies = details.similar.results.slice(0, 6);
+ }
+
+ // Certification/Rating
+ let certification = 'Not Rated';
+ if (details.release_dates && details.release_dates.results) {
+ const usRelease = details.release_dates.results.find(r => r.iso_3166_1 === 'US');
+ if (usRelease && usRelease.release_dates && usRelease.release_dates.length > 0) {
+ const cert = usRelease.release_dates[0].certification;
+ if (cert) certification = cert;
+ }
+ }
+
+ // Build Radarr instance selector
+ let instanceSelectorHTML = '';
+ if (this.radarrInstances.length > 0) {
+ instanceSelectorHTML = `
+
+
+
+
+ `;
+ }
+
+ return `
+
+
+
+
+
+

+
+
+
${this.escapeHtml(details.title)}
+ ${instanceSelectorHTML}
+
+
${genres}
+
${this.escapeHtml(overview)}
+
+ ${actionButton}
+
+
+
+
+
+
+
+
+
Movie Details
+
+
+
Director
+
${this.escapeHtml(director)}
+
+
+
Release Date
+
${details.release_date || 'N/A'}
+
+
+
Rating
+
${certification}
+
+
+
Budget
+
${details.budget ? '$' + (details.budget / 1000000).toFixed(1) + 'M' : 'N/A'}
+
+
+
Revenue
+
${details.revenue ? '$' + (details.revenue / 1000000).toFixed(1) + 'M' : 'N/A'}
+
+
+
Language
+
${details.original_language ? details.original_language.toUpperCase() : 'N/A'}
+
+
+
+
+ ${mainCast.length > 0 ? `
+
+
+
Cast
+
+ ${mainCast.map(actor => this.renderCastCard(actor)).join('')}
+
+
+ ` : ''}
+
+ ${similarMovies.length > 0 ? `
+
+
+
Similar Movies
+
+ ${similarMovies.map(movie => this.renderSimilarCard(movie)).join('')}
+
+
+ ` : ''}
+
+ `;
+ },
+
+ renderCastCard(actor) {
+ const photoUrl = actor.profile_path
+ ? `https://image.tmdb.org/t/p/w185${actor.profile_path}`
+ : './static/images/blackout.jpg';
+
+ return `
+
+
+

+
+
+
${this.escapeHtml(actor.name)}
+
${this.escapeHtml(actor.character || 'Unknown')}
+
+
+ `;
+ },
+
+ renderSimilarCard(movie) {
+ const posterUrl = movie.poster_path
+ ? `https://image.tmdb.org/t/p/w185${movie.poster_path}`
+ : './static/images/blackout.jpg';
+
+ return `
+
+ `;
+ },
+
+ setupDetailInteractions() {
+ // Close button
+ const closeBtn = document.querySelector('.movie-detail-close');
+ if (closeBtn) {
+ closeBtn.addEventListener('click', () => this.closeDetail());
+ }
+
+ // Instance selector change handler
+ const instanceSelect = document.getElementById('requestarr-detail-instance-select');
+ if (instanceSelect) {
+ instanceSelect.addEventListener('change', async () => {
+ this.selectedInstanceName = instanceSelect.value;
+ console.log('[RequestarrDetail] Instance changed to:', this.selectedInstanceName);
+
+ // Update movie status for new instance
+ await this.updateMovieStatus();
+ });
+
+ // Initial status check for selected instance
+ this.updateMovieStatus();
+ }
+
+ // Request button
+ const requestBtn = document.getElementById('requestarr-detail-request-btn');
+ if (requestBtn && this.currentMovie) {
+ requestBtn.addEventListener('click', () => {
+ if (window.RequestarrDiscover && window.RequestarrDiscover.modal) {
+ window.RequestarrDiscover.modal.openModal(
+ this.currentMovie.tmdb_id,
+ 'movie',
+ this.selectedInstanceName
+ );
+ }
+ });
+ }
+
+ // Similar movie cards - open their details
+ const similarCards = document.querySelectorAll('.movie-detail-similar .media-card');
+ similarCards.forEach(card => {
+ card.addEventListener('click', async () => {
+ const tmdbId = card.getAttribute('data-tmdb-id');
+ if (tmdbId) {
+ try {
+ const details = await this.fetchMovieDetails(tmdbId);
+ if (details) {
+ const movieData = {
+ tmdb_id: details.id,
+ id: details.id,
+ title: details.title,
+ year: details.release_date ? new Date(details.release_date).getFullYear() : null,
+ poster_path: details.poster_path,
+ backdrop_path: details.backdrop_path,
+ overview: details.overview,
+ vote_average: details.vote_average,
+ in_library: false,
+ in_cooldown: false
+ };
+ this.openDetail(movieData, this.options || {}, false);
+ }
+ } catch (error) {
+ console.error('[RequestarrDetail] Error opening similar movie:', error);
+ }
+ }
+ });
+ });
+
+ // ESC key to close
+ const escHandler = (e) => {
+ if (e.key === 'Escape') {
+ this.closeDetail();
+ document.removeEventListener('keydown', escHandler);
+ }
+ };
+ document.addEventListener('keydown', escHandler);
+ },
+
+ getLoadingHTML() {
+ return `
+
+
+
+
Loading movie details...
+
+ `;
+ },
+
+ getErrorHTML(message) {
+ return `
+
+
+
+
${this.escapeHtml(message)}
+
+ `;
+ },
+
+ escapeHtml(text) {
+ if (!text) return '';
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+ };
+
+ // Initialize on load
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', () => window.RequestarrDetail.init());
+ } else {
+ window.RequestarrDetail.init();
+ }
+})();
diff --git a/frontend/templates/components/scripts.html b/frontend/templates/components/scripts.html
index e3fa2fe8..6a832ce4 100644
--- a/frontend/templates/components/scripts.html
+++ b/frontend/templates/components/scripts.html
@@ -71,6 +71,7 @@
+