Spaces:
Running
Running
<html lang="fr"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>CineStream - Films avec streaming automatique</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<script> | |
tailwind.config = { | |
theme: { | |
extend: { | |
colors: { | |
primary: '#6d28d9', | |
secondary: '#8b5cf6', | |
dark: '#1e1b4b', | |
light: '#f5f3ff', | |
}, | |
fontFamily: { | |
sans: ['Inter', 'sans-serif'], | |
}, | |
} | |
} | |
} | |
</script> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); | |
body { | |
font-family: 'Inter', sans-serif; | |
background-color: #0f172a; | |
color: white; | |
} | |
.movie-card { | |
transition: all 0.3s ease; | |
} | |
.movie-card:hover { | |
transform: translateY(-10px); | |
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); | |
} | |
.movie-poster { | |
height: 450px; | |
object-fit: cover; | |
} | |
.loading-spinner { | |
animation: spin 1s linear infinite; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.gradient-text { | |
background: linear-gradient(90deg, #8b5cf6, #ec4899); | |
-webkit-background-clip: text; | |
background-clip: text; | |
color: transparent; | |
} | |
.search-input:focus { | |
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.5); | |
} | |
.error-message { | |
background-color: rgba(239, 68, 68, 0.2); | |
border-left: 4px solid #ef4444; | |
} | |
.placeholder-poster { | |
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
} | |
/* Style pour le lecteur vidéo */ | |
.video-container { | |
position: relative; | |
padding-bottom: 56.25%; /* 16:9 */ | |
height: 0; | |
overflow: hidden; | |
} | |
.video-container iframe { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
border: none; | |
} | |
/* Style pour les boutons de streaming */ | |
.stream-btn { | |
transition: all 0.2s ease; | |
} | |
.stream-btn:hover { | |
transform: scale(1.05); | |
} | |
/* Style pour la fenêtre de streaming */ | |
.streaming-window { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background-color: rgba(0, 0, 0, 0.9); | |
z-index: 1000; | |
display: flex; | |
flex-direction: column; | |
} | |
.streaming-header { | |
padding: 15px; | |
background-color: rgba(0, 0, 0, 0.7); | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
} | |
.streaming-content { | |
flex: 1; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
} | |
.streaming-controls { | |
padding: 15px; | |
background-color: rgba(0, 0, 0, 0.7); | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
} | |
/* Style pour la sélection de serveur */ | |
.server-list { | |
display: flex; | |
gap: 10px; | |
flex-wrap: wrap; | |
margin-top: 10px; | |
} | |
.server-btn { | |
padding: 8px 12px; | |
border-radius: 20px; | |
font-size: 14px; | |
cursor: pointer; | |
transition: all 0.2s; | |
} | |
.server-btn.active { | |
background-color: #6d28d9; | |
color: white; | |
} | |
.server-btn.inactive { | |
background-color: #334155; | |
color: #94a3b8; | |
} | |
/* Animation de chargement du streaming */ | |
.stream-loading { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
gap: 20px; | |
} | |
.stream-loading-spinner { | |
width: 50px; | |
height: 50px; | |
border: 5px solid rgba(255, 255, 255, 0.1); | |
border-radius: 50%; | |
border-top-color: #6d28d9; | |
animation: spin 1s linear infinite; | |
} | |
</style> | |
</head> | |
<body class="min-h-screen"> | |
<!-- Navigation --> | |
<nav class="bg-gray-900 bg-opacity-90 backdrop-blur-md fixed w-full z-50 shadow-lg"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
<div class="flex items-center justify-between h-16"> | |
<div class="flex items-center"> | |
<div class="flex-shrink-0"> | |
<span class="text-2xl font-bold gradient-text">CineStream</span> | |
</div> | |
<div class="hidden md:block ml-10"> | |
<div class="flex space-x-4"> | |
<a href="#" class="text-white px-3 py-2 rounded-md text-sm font-medium bg-primary">Accueil</a> | |
<a href="#" class="text-gray-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Films</a> | |
<a href="#" class="text-gray-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Séries</a> | |
<a href="#" class="text-gray-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Tendances</a> | |
</div> | |
</div> | |
</div> | |
<div class="hidden md:block"> | |
<div class="ml-4 flex items-center md:ml-6"> | |
<div class="relative mx-4"> | |
<input type="text" id="search-input" placeholder="Rechercher un film..." | |
class="search-input bg-gray-800 text-white px-4 py-2 rounded-full w-64 focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-200"> | |
<button onclick="searchMovies()" class="absolute right-3 top-2 text-gray-400 hover:text-white"> | |
<i class="fas fa-search"></i> | |
</button> | |
</div> | |
<button class="p-1 rounded-full text-gray-400 hover:text-white focus:outline-none"> | |
<i class="fas fa-user-circle text-xl"></i> | |
</button> | |
</div> | |
</div> | |
<div class="-mr-2 flex md:hidden"> | |
<button class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none"> | |
<i class="fas fa-bars"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
</nav> | |
<!-- Main Content --> | |
<main class="pt-24 pb-12"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
<!-- Featured Section --> | |
<section class="mb-12"> | |
<div class="flex justify-between items-center mb-6"> | |
<h2 class="text-2xl font-bold text-white">Films populaires</h2> | |
<div class="flex space-x-2"> | |
<button onclick="filterMovies('popular')" class="px-4 py-1 rounded-full text-sm bg-primary text-white">Populaires</button> | |
<button onclick="filterMovies('top_rated')" class="px-4 py-1 rounded-full text-sm bg-gray-700 text-white">Mieux notés</button> | |
<button onclick="filterMovies('upcoming')" class="px-4 py-1 rounded-full text-sm bg-gray-700 text-white">Prochainement</button> | |
</div> | |
</div> | |
<div id="movies-container" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> | |
<!-- Movies will be loaded here --> | |
<div class="flex justify-center items-center py-12 col-span-full"> | |
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary loading-spinner"></div> | |
</div> | |
</div> | |
<div id="load-more-container" class="mt-8 flex justify-center"> | |
<button onclick="loadMoreMovies()" class="px-6 py-2 bg-primary text-white rounded-full hover:bg-secondary transition flex items-center"> | |
<span>Voir plus</span> | |
<i class="fas fa-chevron-down ml-2"></i> | |
</button> | |
</div> | |
</section> | |
<!-- Genres Section --> | |
<section class="mb-12"> | |
<h2 class="text-2xl font-bold text-white mb-6">Par genres</h2> | |
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4"> | |
<button onclick="filterByGenre(28)" class="genre-btn px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-full text-sm">Action</button> | |
<button onclick="filterByGenre(12)" class="genre-btn px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-full text-sm">Aventure</button> | |
<button onclick="filterByGenre(16)" class="genre-btn px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-full text-sm">Animation</button> | |
<button onclick="filterByGenre(35)" class="genre-btn px-4 py-2 bg-yellow-600 hover:bg-yellow-700 text-white rounded-full text-sm">Comédie</button> | |
<button onclick="filterByGenre(80)" class="genre-btn px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-full text-sm">Crime</button> | |
<button onclick="filterByGenre(18)" class="genre-btn px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-full text-sm">Drame</button> | |
</div> | |
</section> | |
</div> | |
</main> | |
<!-- Footer --> | |
<footer class="bg-gray-900 text-gray-400 py-8"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
<div class="grid grid-cols-1 md:grid-cols-4 gap-8"> | |
<div> | |
<h3 class="text-white text-lg font-semibold mb-4">CineStream</h3> | |
<p class="text-sm">La meilleure plateforme pour regarder des films avec streaming automatique.</p> | |
</div> | |
<div> | |
<h3 class="text-white text-lg font-semibold mb-4">Navigation</h3> | |
<ul class="space-y-2 text-sm"> | |
<li><a href="#" class="hover:text-white">Accueil</a></li> | |
<li><a href="#" class="hover:text-white">Films</a></li> | |
<li><a href="#" class="hover:text-white">Séries</a></li> | |
<li><a href="#" class="hover:text-white">Nouveautés</a></li> | |
</ul> | |
</div> | |
<div> | |
<h3 class="text-white text-lg font-semibold mb-4">Légal</h3> | |
<ul class="space-y-2 text-sm"> | |
<li><a href="#" class="hover:text-white">Conditions d'utilisation</a></li> | |
<li><a href="#" class="hover:text-white">Politique de confidentialité</a></li> | |
<li><a href="#" class="hover:text-white">DMCA</a></li> | |
</ul> | |
</div> | |
<div> | |
<h3 class="text-white text-lg font-semibold mb-4">Contact</h3> | |
<div class="flex space-x-4"> | |
<a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-facebook-f"></i></a> | |
<a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-twitter"></i></a> | |
<a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-instagram"></i></a> | |
<a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-discord"></i></a> | |
</div> | |
</div> | |
</div> | |
<div class="border-t border-gray-800 mt-8 pt-8 text-sm text-center"> | |
<p>© 2023 CineStream. Tous droits réservés.</p> | |
</div> | |
</div> | |
</footer> | |
<!-- Movie Details Modal --> | |
<div id="movie-modal" class="fixed inset-0 z-50 hidden overflow-y-auto"> | |
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | |
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> | |
<div class="absolute inset-0 bg-gray-900 opacity-75"></div> | |
</div> | |
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> | |
<div class="inline-block align-bottom bg-gray-800 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full"> | |
<div class="px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | |
<div class="sm:flex sm:items-start"> | |
<div id="modal-poster" class="flex-shrink-0 w-full sm:w-1/3 mb-4 sm:mb-0 sm:mr-6"> | |
<img class="w-full rounded-lg shadow-lg" src="" alt="Movie Poster"> | |
</div> | |
<div class="mt-3 text-center sm:mt-0 sm:text-left w-full sm:w-2/3"> | |
<h3 id="modal-title" class="text-2xl leading-6 font-bold text-white mb-2"></h3> | |
<div class="flex items-center mb-4"> | |
<span id="modal-rating" class="bg-yellow-500 text-white text-xs font-semibold px-2 py-1 rounded mr-3"></span> | |
<span id="modal-year" class="text-gray-300 text-sm"></span> | |
<span id="modal-runtime" class="ml-3 text-gray-300 text-sm"></span> | |
</div> | |
<p id="modal-overview" class="text-gray-300 mb-4"></p> | |
<div id="modal-genres" class="flex flex-wrap gap-2 mb-4"></div> | |
<!-- Serveurs de streaming --> | |
<div class="mb-4"> | |
<h4 class="text-sm font-semibold text-gray-400 mb-2">CHOISIR UN SERVEUR :</h4> | |
<div class="server-list"> | |
<button onclick="selectServer('hdvip')" class="server-btn active" data-server="hdvip"> | |
<i class="fas fa-crown mr-1"></i> HD VIP | |
</button> | |
<button onclick="selectServer('streamlare')" class="server-btn inactive" data-server="streamlare"> | |
<i class="fas fa-play-circle mr-1"></i> Streamlare | |
</button> | |
<button onclick="selectServer('streamtape')" class="server-btn inactive" data-server="streamtape"> | |
<i class="fas fa-tape mr-1"></i> Streamtape | |
</button> | |
<button onclick="selectServer('younetu')" class="server-btn inactive" data-server="younetu"> | |
<i class="fas fa-film mr-1"></i> Younetu | |
</button> | |
<button onclick="selectServer('uptostream')" class="server-btn inactive" data-server="uptostream"> | |
<i class="fas fa-cloud mr-1"></i> Uptostream | |
</button> | |
<button onclick="selectServer('vidsrc')" class="server-btn inactive" data-server="vidsrc"> | |
<i class="fas fa-server mr-1"></i> VidSrc | |
</button> | |
</div> | |
</div> | |
<div class="mt-4"> | |
<button type="button" onclick="closeModal()" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary text-base font-medium text-white hover:bg-secondary focus:outline-none sm:ml-3 sm:w-auto sm:text-sm"> | |
Fermer | |
</button> | |
<button id="watch-now-btn" onclick="startStreaming()" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-600 text-base font-medium text-white hover:bg-green-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm"> | |
<i class="fas fa-play mr-2"></i> Lecture auto | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Streaming Window (hidden by default) --> | |
<div id="streaming-window" class="streaming-window hidden"> | |
<div class="streaming-header"> | |
<h3 id="streaming-title" class="text-xl font-bold text-white"></h3> | |
<button onclick="closeStreamingWindow()" class="text-gray-400 hover:text-white"> | |
<i class="fas fa-times text-xl"></i> | |
</button> | |
</div> | |
<div class="streaming-content"> | |
<div id="streaming-player-container" class="w-full h-full" style="max-width: 1200px;"> | |
<!-- Contenu du lecteur sera chargé ici --> | |
<div id="stream-loading" class="stream-loading w-full h-full"> | |
<div class="stream-loading-spinner"></div> | |
<p class="text-gray-300">Chargement du flux vidéo...</p> | |
</div> | |
</div> | |
</div> | |
<div class="streaming-controls"> | |
<div class="flex space-x-2"> | |
<button onclick="changeQuality('720p')" class="stream-btn px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded-full text-sm">720p</button> | |
<button onclick="changeQuality('1080p')" class="stream-btn px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded-full text-sm">1080p</button> | |
<button onclick="changeQuality('4k')" class="stream-btn px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded-full text-sm">4K</button> | |
</div> | |
<div> | |
<button onclick="toggleFullscreen()" class="stream-btn px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded-full text-sm"> | |
<i class="fas fa-expand mr-1"></i> Plein écran | |
</button> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Configuration | |
const API_KEY = 'c45a857c193f6302f2b5061c3b85e743'; // Clé API TMDB valide | |
const BASE_URL = 'https://api.themoviedb.org/3'; | |
// Serveurs de streaming avec URLs de base fonctionnelles | |
const STREAMING_SERVERS = { | |
hdvip: { | |
name: 'Lecteur HD VIP', | |
url: 'https://database.gdriveplayer.us/player.php?tmdb=', | |
autoPlay: true, | |
requiresTMDB: true, | |
requiresIMDB: false | |
}, | |
streamlare: { | |
name: 'Streamlare', | |
url: 'https://streamlare.com/e/', | |
autoPlay: true, | |
requiresTMDB: false, | |
requiresIMDB: true | |
}, | |
streamtape: { | |
name: 'Streamtape', | |
url: 'https://streamtape.com/e/', | |
autoPlay: true, | |
requiresTMDB: false, | |
requiresIMDB: true | |
}, | |
younetu: { | |
name: 'Younetu', | |
url: 'https://younetu.com/embed/', | |
autoPlay: true, | |
requiresTMDB: false, | |
requiresIMDB: true | |
}, | |
uptostream: { | |
name: 'Uptostream', | |
url: 'https://uptostream.com/iframe/', | |
autoPlay: true, | |
requiresTMDB: false, | |
requiresIMDB: true | |
}, | |
vidsrc: { | |
name: 'VidSrc', | |
url: 'https://vidsrc.to/embed/movie/', | |
autoPlay: true, | |
requiresTMDB: true, | |
requiresIMDB: false | |
} | |
}; | |
let currentPage = 1; | |
let currentFilter = 'popular'; | |
let currentGenre = null; | |
let currentSearch = ''; | |
let totalPages = 1; | |
let isLoading = false; | |
let currentMovieId = null; | |
let currentIMDBId = null; | |
let selectedServer = 'hdvip'; | |
// Fonction utilitaire pour faire les requêtes API | |
async function fetchAPI(endpoint, params = {}) { | |
const url = new URL(`${BASE_URL}${endpoint}`); | |
// Ajouter les paramètres par défaut | |
const defaultParams = { | |
language: 'fr-FR', | |
page: currentPage, | |
api_key: API_KEY | |
}; | |
// Fusionner les paramètres | |
const queryParams = {...defaultParams, ...params}; | |
// Ajouter les paramètres à l'URL | |
Object.keys(queryParams).forEach(key => { | |
if (queryParams[key] !== undefined && queryParams[key] !== null) { | |
url.searchParams.append(key, queryParams[key]); | |
} | |
}); | |
try { | |
const response = await fetch(url); | |
if (!response.ok) { | |
throw new Error(`Erreur HTTP: ${response.status}`); | |
} | |
return await response.json(); | |
} catch (error) { | |
console.error('Erreur API:', error); | |
throw error; | |
} | |
} | |
// Charger les films au démarrage | |
document.addEventListener('DOMContentLoaded', function() { | |
loadMovies(); | |
// Écouter la touche Entrée dans la recherche | |
document.getElementById('search-input').addEventListener('keypress', function(e) { | |
if (e.key === 'Enter') { | |
searchMovies(); | |
} | |
}); | |
}); | |
// Charger les films | |
async function loadMovies() { | |
if (isLoading) return; | |
isLoading = true; | |
showLoadingState(); | |
try { | |
let data; | |
if (currentSearch) { | |
data = await fetchAPI('/search/movie', { | |
query: currentSearch, | |
include_adult: false, | |
page: currentPage | |
}); | |
} else if (currentGenre) { | |
data = await fetchAPI('/discover/movie', { | |
include_adult: false, | |
include_video: false, | |
sort_by: 'popularity.desc', | |
with_genres: currentGenre, | |
page: currentPage | |
}); | |
} else { | |
data = await fetchAPI(`/movie/${currentFilter}`, { | |
page: currentPage | |
}); | |
} | |
totalPages = data.total_pages || 1; | |
if (currentPage === 1) { | |
document.getElementById('movies-container').innerHTML = ''; | |
} | |
if (data.results && data.results.length > 0) { | |
displayMovies(data.results); | |
updateLoadMoreButton(); | |
} else { | |
showNoResults(); | |
} | |
} catch (error) { | |
console.error('Error fetching movies:', error); | |
showErrorState(error.message); | |
} finally { | |
isLoading = false; | |
} | |
} | |
// Afficher l'état de chargement | |
function showLoadingState() { | |
const container = document.getElementById('movies-container'); | |
container.innerHTML = ` | |
<div class="flex justify-center items-center py-12 col-span-full"> | |
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary loading-spinner"></div> | |
</div> | |
`; | |
} | |
// Afficher les films avec boutons de streaming | |
function displayMovies(movies) { | |
const container = document.getElementById('movies-container'); | |
movies.forEach(movie => { | |
const movieCard = document.createElement('div'); | |
movieCard.className = 'movie-card bg-gray-800 rounded-lg overflow-hidden shadow-lg hover:shadow-xl transition-all duration-300'; | |
// Utiliser l'image originale en haute qualité si disponible | |
const posterPath = movie.poster_path | |
? `https://image.tmdb.org/t/p/w500${movie.poster_path}` | |
: 'https://via.placeholder.com/500x750?text=Affiche+non+disponible'; | |
movieCard.innerHTML = ` | |
<div> | |
<div class="movie-poster w-full rounded-t-lg overflow-hidden ${!movie.poster_path ? 'placeholder-poster' : ''}"> | |
<img src="${posterPath}" | |
alt="${movie.title || 'Titre inconnu'}" | |
class="w-full h-full object-cover" | |
onerror="this.src='https://via.placeholder.com/500x750?text=Image+non+chargée'; this.classList.add('placeholder-poster')"> | |
</div> | |
<div class="p-4"> | |
<h3 class="text-lg font-semibold text-white truncate">${movie.title || 'Titre inconnu'}</h3> | |
<div class="flex items-center mt-2"> | |
<span class="text-yellow-400 text-sm"> | |
<i class="fas fa-star"></i> ${movie.vote_average ? movie.vote_average.toFixed(1) : 'N/A'} | |
</span> | |
<span class="text-gray-400 text-sm ml-auto">${movie.release_date ? movie.release_date.substring(0, 4) : 'N/A'}</span> | |
</div> | |
<div class="mt-3"> | |
<button onclick="startAutoStreaming(${movie.id}, '${movie.title || 'Film'}')" class="w-full stream-btn px-3 py-2 bg-green-600 hover:bg-green-700 text-white rounded text-sm"> | |
<i class="fas fa-play-circle mr-1"></i> Lecture auto | |
</button> | |
<button onclick="showMovieDetails(${movie.id})" class="w-full mt-2 px-3 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded text-sm"> | |
<i class="fas fa-info-circle mr-1"></i> Détails | |
</button> | |
</div> | |
</div> | |
</div> | |
`; | |
container.appendChild(movieCard); | |
}); | |
} | |
// Afficher un message d'erreur | |
function showErrorState(errorMessage) { | |
const container = document.getElementById('movies-container'); | |
container.innerHTML = ` | |
<div class="col-span-full py-6 px-4 error-message rounded-lg"> | |
<div class="flex items-center"> | |
<i class="fas fa-exclamation-triangle text-red-500 text-xl mr-3"></i> | |
<div> | |
<h3 class="text-lg font-medium text-white">Erreur lors du chargement des films</h3> | |
<p class="text-gray-300 mt-1">${errorMessage || 'Veuillez réessayer plus tard'}</p> | |
<button onclick="retryLoading()" class="mt-3 px-4 py-2 bg-primary text-white rounded-full hover:bg-secondary transition text-sm"> | |
<i class="fas fa-sync-alt mr-2"></i> Réessayer | |
</button> | |
</div> | |
</div> | |
</div> | |
`; | |
document.getElementById('load-more-container').classList.add('hidden'); | |
} | |
// Afficher un message quand aucun résultat n'est trouvé | |
function showNoResults() { | |
const container = document.getElementById('movies-container'); | |
container.innerHTML = ` | |
<div class="col-span-full text-center py-12"> | |
<i class="fas fa-film text-4xl text-gray-500 mb-4"></i> | |
<h3 class="text-xl text-white">Aucun film trouvé</h3> | |
<p class="text-gray-400 mt-2">Essayez avec d'autres critères de recherche</p> | |
</div> | |
`; | |
document.getElementById('load-more-container').classList.add('hidden'); | |
} | |
// Réessayer le chargement | |
function retryLoading() { | |
currentPage = 1; | |
loadMovies(); | |
} | |
// Mettre à jour le bouton "Voir plus" | |
function updateLoadMoreButton() { | |
const loadMoreContainer = document.getElementById('load-more-container'); | |
if (currentPage >= totalPages) { | |
loadMoreContainer.innerHTML = ` | |
<p class="text-gray-400">Vous avez atteint la fin des résultats</p> | |
`; | |
} else { | |
loadMoreContainer.classList.remove('hidden'); | |
} | |
} | |
// Filtrer les films | |
function filterMovies(filter) { | |
currentFilter = filter; | |
currentGenre = null; | |
currentSearch = ''; | |
currentPage = 1; | |
document.getElementById('search-input').value = ''; | |
loadMovies(); | |
// Mettre à jour les boutons actifs | |
document.querySelectorAll('button').forEach(btn => { | |
if (btn.textContent.toLowerCase().includes(filter.replace('_', ' '))) { | |
btn.classList.remove('bg-gray-700'); | |
btn.classList.add('bg-primary'); | |
} else { | |
btn.classList.remove('bg-primary'); | |
btn.classList.add('bg-gray-700'); | |
} | |
}); | |
} | |
// Filtrer par genre | |
function filterByGenre(genreId) { | |
currentGenre = genreId; | |
currentSearch = ''; | |
currentPage = 1; | |
document.getElementById('search-input').value = ''; | |
loadMovies(); | |
// Mettre à jour les boutons actifs | |
document.querySelectorAll('.genre-btn').forEach(btn => { | |
btn.classList.remove('bg-primary'); | |
if (btn.onclick.toString().includes(genreId.toString())) { | |
btn.classList.add('bg-primary'); | |
} | |
}); | |
} | |
// Rechercher des films | |
function searchMovies() { | |
const query = document.getElementById('search-input').value.trim(); | |
if (query) { | |
currentSearch = query; | |
currentGenre = null; | |
currentPage = 1; | |
loadMovies(); | |
} | |
} | |
// Charger plus de films | |
function loadMoreMovies() { | |
if (currentPage < totalPages) { | |
currentPage++; | |
loadMovies(); | |
} | |
} | |
// Afficher les détails du film | |
async function showMovieDetails(movieId) { | |
try { | |
const movie = await fetchAPI(`/movie/${movieId}`); | |
currentMovieId = movieId; | |
// Récupérer l'ID IMDB si disponible | |
if (movie.imdb_id) { | |
currentIMDBId = movie.imdb_id; | |
} else { | |
// Si l'ID IMDB n'est pas disponible, essayer de le récupérer via l'API | |
const externalIds = await fetchAPI(`/movie/${movieId}/external_ids`); | |
currentIMDBId = externalIds.imdb_id || null; | |
} | |
// Remplir la modal | |
const modalPoster = document.querySelector('#modal-poster img'); | |
modalPoster.src = movie.poster_path | |
? `https://image.tmdb.org/t/p/w500${movie.poster_path}` | |
: 'https://via.placeholder.com/500x750?text=Affiche+non+disponible'; | |
modalPoster.onerror = function() { | |
this.src = 'https://via.placeholder.com/500x750?text=Image+non+chargée'; | |
}; | |
document.getElementById('modal-title').textContent = movie.title || 'Titre inconnu'; | |
document.getElementById('modal-rating').textContent = `${movie.vote_average ? movie.vote_average.toFixed(1) : 'N/A'}/10`; | |
document.getElementById('modal-year').textContent = movie.release_date ? movie.release_date.substring(0, 4) : 'N/A'; | |
document.getElementById('modal-runtime').textContent = movie.runtime ? `${movie.runtime} min` : ''; | |
document.getElementById('modal-overview').textContent = movie.overview || 'Aucun synopsis disponible.'; | |
// Afficher les genres | |
const genresContainer = document.getElementById('modal-genres'); | |
genresContainer.innerHTML = ''; | |
if (movie.genres && movie.genres.length > 0) { | |
movie.genres.forEach(genre => { | |
const genreBadge = document.createElement('span'); | |
genreBadge.className = 'bg-gray-700 text-white text-xs px-2 py-1 rounded'; | |
genreBadge.textContent = genre.name; | |
genresContainer.appendChild(genreBadge); | |
}); | |
} else { | |
genresContainer.innerHTML = '<span class="text-gray-400 text-sm">Aucun genre spécifié</span>'; | |
} | |
// Afficher la modal | |
document.getElementById('movie-modal').classList.remove('hidden'); | |
} catch (error) { | |
console.error('Error fetching movie details:', error); | |
alert('Erreur lors du chargement des détails du film: ' + (error.message || 'Veuillez réessayer')); | |
} | |
} | |
// Fermer la modal de détails | |
function closeModal() { | |
document.getElementById('movie-modal').classList.add('hidden'); | |
} | |
// Sélectionner un serveur de streaming | |
function selectServer(serverId) { | |
selectedServer = serverId; | |
// Mettre à jour l'état des boutons | |
document.querySelectorAll('.server-btn').forEach(btn => { | |
if (btn.dataset.server === serverId) { | |
btn.classList.remove('inactive'); | |
btn.classList.add('active'); | |
} else { | |
btn.classList.remove('active'); | |
btn.classList.add('inactive'); | |
} | |
}); | |
} | |
// Démarrer le streaming depuis la modal | |
function startStreaming() { | |
if (currentMovieId) { | |
const movieTitle = document.getElementById('modal-title').textContent; | |
startAutoStreaming(currentMovieId, movieTitle); | |
closeModal(); | |
} | |
} | |
// Démarrer le streaming automatique dans une nouvelle fenêtre | |
async function startAutoStreaming(movieId, movieTitle) { | |
currentMovieId = movieId; | |
// Afficher la fenêtre de streaming | |
const streamingWindow = document.getElementById('streaming-window'); | |
document.getElementById('streaming-title').textContent = movieTitle; | |
// Afficher l'état de chargement | |
const playerContainer = document.getElementById('streaming-player-container'); | |
playerContainer.innerHTML = ` | |
<div id="stream-loading" class="stream-loading w-full h-full"> | |
<div class="stream-loading-spinner"></div> | |
<p class="text-gray-300">Chargement du flux vidéo...</p> | |
</div> | |
`; | |
// Afficher la fenêtre de streaming | |
streamingWindow.classList.remove('hidden'); | |
// Récupérer les informations du film pour l'ID IMDB si nécessaire | |
let imdbId = currentIMDBId; | |
if (!imdbId && STREAMING_SERVERS[selectedServer].requiresIMDB) { | |
try { | |
const externalIds = await fetchAPI(`/movie/${movieId}/external_ids`); | |
imdbId = externalIds.imdb_id; | |
if (!imdbId) { | |
throw new Error("ID IMDB non disponible"); | |
} | |
} catch (error) { | |
console.error("Erreur lors de la récupération de l'ID IMDB:", error); | |
playerContainer.innerHTML = ` | |
<div class="flex flex-col items-center justify-center h-full text-red-400"> | |
<i class="fas fa-exclamation-triangle text-4xl mb-4"></i> | |
<p class="text-lg">Impossible de trouver l'ID IMDB pour ce film</p> | |
<p class="text-sm text-gray-400 mt-2">Essayez avec un autre serveur</p> | |
</div> | |
`; | |
return; | |
} | |
} | |
// Construire l'URL de streaming en fonction du serveur sélectionné | |
let streamingUrl; | |
const serverConfig = STREAMING_SERVERS[selectedServer]; | |
if (serverConfig.requiresTMDB) { | |
streamingUrl = serverConfig.url + movieId; | |
} else if (serverConfig.requiresIMDB && imdbId) { | |
streamingUrl = serverConfig.url + imdbId; | |
} else { | |
// Si aucun ID n'est disponible, utiliser TMDB par défaut | |
streamingUrl = serverConfig.url + movieId; | |
} | |
// Ajouter le paramètre de lecture automatique si pris en charge | |
if (serverConfig.autoPlay) { | |
streamingUrl += '/auto'; | |
} | |
// Créer l'iframe pour le lecteur vidéo | |
const iframe = document.createElement('iframe'); | |
iframe.id = 'streaming-player'; | |
iframe.src = streamingUrl; | |
iframe.setAttribute('allowfullscreen', ''); | |
iframe.setAttribute('scrolling', 'no'); | |
iframe.setAttribute('frameborder', '0'); | |
iframe.setAttribute('marginwidth', '0'); | |
iframe.setAttribute('marginheight', '0'); | |
iframe.style.width = '100%'; | |
iframe.style.height = '100%'; | |
iframe.style.border = 'none'; | |
// Gestion des erreurs de chargement | |
iframe.onerror = function() { | |
playerContainer.innerHTML = ` | |
<div class="flex flex-col items-center justify-center h-full text-red-400"> | |
<i class="fas fa-exclamation-triangle text-4xl mb-4"></i> | |
<p class="text-lg">Erreur lors du chargement du flux vidéo</p> | |
<p class="text-sm text-gray-400 mt-2">Le serveur peut être temporairement indisponible</p> | |
<button onclick="tryAlternativeServer()" class="mt-4 px-4 py-2 bg-primary text-white rounded hover:bg-secondary transition"> | |
<i class="fas fa-sync-alt mr-2"></i> Essayer un autre serveur | |
</button> | |
</div> | |
`; | |
}; | |
// Remplacer le chargement par le lecteur | |
playerContainer.innerHTML = ''; | |
playerContainer.appendChild(iframe); | |
// Mettre le focus sur la fenêtre de streaming | |
streamingWindow.focus(); | |
// Tentative de lecture automatique (peut être bloquée par le navigateur) | |
setTimeout(() => { | |
try { | |
iframe.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*'); | |
} catch (e) { | |
console.log("Auto-play blocked by browser"); | |
} | |
}, 2000); | |
} | |
// Essayer un autre serveur en cas d'erreur | |
function tryAlternativeServer() { | |
const servers = Object.keys(STREAMING_SERVERS); | |
const currentIndex = servers.indexOf(selectedServer); | |
const nextIndex = (currentIndex + 1) % servers.length; | |
selectedServer = servers[nextIndex]; | |
startAutoStreaming(currentMovieId, document.getElementById('streaming-title').textContent); | |
} | |
// Fermer la fenêtre de streaming | |
function closeStreamingWindow() { | |
const playerContainer = document.getElementById('streaming-player-container'); | |
playerContainer.innerHTML = ''; | |
document.getElementById('streaming-window').classList.add('hidden'); | |
} | |
// Changer la qualité de streaming (simulé) | |
function changeQuality(quality) { | |
alert(`Qualité changée en ${quality}. Note: Ceci est une démonstration.`); | |
// En réalité, vous devriez mettre à jour la source du lecteur vidéo | |
} | |
// Basculer en plein écran | |
function toggleFullscreen() { | |
const playerContainer = document.getElementById('streaming-player-container'); | |
if (playerContainer.requestFullscreen) { | |
playerContainer.requestFullscreen(); | |
} else if (playerContainer.webkitRequestFullscreen) { | |
playerContainer.webkitRequestFullscreen(); | |
} else if (playerContainer.msRequestFullscreen) { | |
playerContainer.msRequestFullscreen(); | |
} | |
} | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=docto41/cinestream" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |