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> |