diff --git a/api.php b/api.php index d2bb717..404951f 100644 --- a/api.php +++ b/api.php @@ -7,7 +7,6 @@ header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; } define('ENCRYPTION_KEY', 'MaCleSecreteSuperRobuste123!'); -define('CACHE_TTL', 604800); // 7 jours try { $pdo = new PDO("mysql:host=localhost;dbname=mon_cinema;charset=utf8mb4", "root", "", [ @@ -24,7 +23,6 @@ try { $pdo->exec("CREATE TABLE IF NOT EXISTS videotheque (id BIGINT PRIMARY KEY, title VARCHAR(255) NOT NULL, year VARCHAR(10), director VARCHAR(255), poster TEXT, format VARCHAR(50), length VARCHAR(50), publisher VARCHAR(255), ean_isbn13 VARCHAR(50), number_of_discs INT DEFAULT 1, aspect_ratio VARCHAR(50), description TEXT, actors TEXT)"); try { $pdo->exec("ALTER TABLE videotheque ADD COLUMN actors TEXT AFTER description"); } catch (\Exception $e) {} - $pdo->exec("CREATE TABLE IF NOT EXISTS cache_api (cache_key VARCHAR(120) PRIMARY KEY, data TEXT NOT NULL, source VARCHAR(20) NOT NULL, created_at INT NOT NULL)"); } catch (\PDOException $e) { echo json_encode(["error" => "Erreur BDD : " . $e->getMessage()]); exit; } // ── FONCTIONS UTILITAIRES ── @@ -76,22 +74,6 @@ function httpGet($url, $timeout = 5) { return $res ?: null; } -function getCache($pdo, $key) { - try { - $stmt = $pdo->prepare("SELECT data FROM cache_api WHERE cache_key = ? AND created_at > ?"); - $stmt->execute([$key, time() - CACHE_TTL]); - $row = $stmt->fetch(); - return $row ? json_decode($row['data'], true) : null; - } catch (\Exception $e) { return null; } -} - -function setCache($pdo, $key, $data, $source) { - try { - $stmt = $pdo->prepare("REPLACE INTO cache_api (cache_key, data, source, created_at) VALUES (?, ?, ?, ?)"); - $stmt->execute([$key, json_encode($data), $source, time()]); - } catch (\Exception $e) { /* ignore */ } -} - function cleanTitle($title) { $clean = preg_replace('/\s*[\[\(].*?[\]\)]\s*/', '', $title); $clean = preg_replace('/\s*-\s*(Édition|Edition|Collector|Simple|Spéciale|Digibook|Ultimate|Intégrale|Combo|SteelBook|Boîtier).*$/i', '', $clean); @@ -117,78 +99,111 @@ function extractYear($dateStr) { function fetchDVDFr($ean, $pdo) { if (empty($ean) || strlen($ean) < 8) return null; - $cacheKey = 'dvdfr_full_' . md5($ean); - $cached = getCache($pdo, $cacheKey); - if ($cached) return $cached; - $url = "https://www.dvdfr.com/api/search.php?gencode=" . urlencode($ean); - $res = httpGet($url, 10); // Augmenté à 10s pour éviter les timeouts - if (!$res) { - file_put_contents('dvdfr_error.log', "Échec requête pour EAN: $ean"); - return null; - } + $ua = 'MonCinema/1.0 (collection privée; contact@moncineapp.fr)'; - try { - $xml = @simplexml_load_string($res); - if ($xml && isset($xml->dvd[0])) { - $d = $xml->dvd[0]; - // On extrait tout ce que DVDfr nous donne - $data = [ - 'poster' => (string)$d->cover, - 'format' => (string)$d->media, - 'publisher' => (string)$d->editor, - 'length' => (string)$d->duration, - 'aspect' => (string)$d->ratio - ]; - - setCache($pdo, $cacheKey, $data, 'dvdfr'); - return $data; - } - } catch (\Exception $e) { return null; } - return null; + // Étape 1 : recherche par gencode + $searchUrl = "https://www.dvdfr.com/api/search.php?gencode=" . urlencode($ean); + $ch = curl_init($searchUrl); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 5, + CURLOPT_CONNECTTIMEOUT => 3, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_USERAGENT => $ua, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTPHEADER => ['Accept: application/xml, text/xml, */*'], + ]); + $res = curl_exec($ch); + curl_close($ch); + if (!$res) return null; + + libxml_use_internal_errors(true); + $xml = simplexml_load_string($res); + libxml_clear_errors(); + if (!$xml || !isset($xml->dvd[0]->id)) return null; + + $dvdId = (string)$xml->dvd[0]->id; + if (empty($dvdId)) return null; + + // Étape 2 : fiche complète + $ficheUrl = "https://www.dvdfr.com/api/dvd.php?id=" . urlencode($dvdId); + $ch2 = curl_init($ficheUrl); + curl_setopt_array($ch2, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 5, + CURLOPT_CONNECTTIMEOUT => 3, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_USERAGENT => $ua, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTPHEADER => ['Accept: application/xml, text/xml, */*'], + ]); + $res2 = curl_exec($ch2); + curl_close($ch2); + if (!$res2) return null; + + libxml_use_internal_errors(true); + $fiche = simplexml_load_string($res2); + libxml_clear_errors(); + if (!$fiche || !isset($fiche->dvd[0])) return null; + + $dvd = $fiche->dvd[0]; + + $poster = ''; + if (isset($dvd->cover)) $poster = (string)$dvd->cover; + if (empty($poster) && isset($dvd->covers->cover[0])) $poster = (string)$dvd->covers->cover[0]; + + $result = [ + 'poster' => $poster, + 'publisher' => isset($dvd->editeur) ? (string)$dvd->editeur : '', + 'format' => isset($dvd->type) ? (string)$dvd->type : '', + 'length' => isset($dvd->duree) ? (string)$dvd->duree : '', + 'aspect' => isset($dvd->formatimage) ? (string)$dvd->formatimage : '', + 'discs' => isset($dvd->nbdisques) ? (string)$dvd->nbdisques : '', + ]; + + return !empty($result['poster']) || !empty($result['publisher']) ? $result : null; } -// ── 2. API TMDB (Full Extract avec Synopsis et Acteurs) ── function fetchTMDBFull($title, $year, $apiKey, $pdo) { if (empty($apiKey) || empty($title)) return null; - $cleanTitle = cleanTitle($title); - $cacheKey = 'tmdb_full_' . md5(strtolower($cleanTitle) . '|' . $year); - $cached = getCache($pdo, $cacheKey); - if ($cached) return $cached; - // 1. Tenter avec l'année + $cleanTitle = cleanTitle($title); + + // 1. Recherche avec l'année $searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$apiKey}&query=" . urlencode($cleanTitle) . "&year={$year}&language=fr-FR"; $searchRes = httpGet($searchUrl, 5); - $searchData = json_decode($searchRes, true); + $searchData = $searchRes ? json_decode($searchRes, true) : []; - // 2. Fallback : Si rien trouvé, on réessaie SANS l'année (au cas où l'année dans le CSV soit fausse) + // 2. Fallback sans l'année if (empty($searchData['results'])) { $searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$apiKey}&query=" . urlencode($cleanTitle) . "&language=fr-FR"; $searchRes = httpGet($searchUrl, 5); - $searchData = json_decode($searchRes, true); + $searchData = $searchRes ? json_decode($searchRes, true) : []; } - + if (empty($searchData['results'])) return null; $movieId = $searchData['results'][0]['id']; + // 3. Détails complets $detailsUrl = "https://api.themoviedb.org/3/movie/{$movieId}?api_key={$apiKey}&append_to_response=credits,watch/providers&language=fr-FR"; $detailsRes = httpGet($detailsUrl, 5); if (!$detailsRes) return null; $details = json_decode($detailsRes, true); - // 🔥 Extraction de TOUS les Réalisateurs (pour les co-réalisations) + // Extraction Réalisateurs $director = ''; if (!empty($details['credits']['crew'])) { $directorsList = []; foreach ($details['credits']['crew'] as $crew) { - if ($crew['job'] === 'Director') { - $directorsList[] = $crew['name']; + if ($crew['job'] === 'Director') { + $directorsList[] = $crew['name']; } } $director = implode(', ', $directorsList); } - // 🔥 Extraction des 4 premiers Acteurs + // Extraction Acteurs (Top 4) $cast = []; if (!empty($details['credits']['cast'])) { $topCast = array_slice($details['credits']['cast'], 0, 4); @@ -197,10 +212,10 @@ function fetchTMDBFull($title, $year, $apiKey, $pdo) { } } - // 🔥 Extraction du Synopsis (Overview) + // Synopsis $overview = $details['overview'] ?? ''; - // Extraction Streaming + // Streaming $streaming = ''; $frProviders = $details['watch/providers']['results']['FR'] ?? []; $platforms = []; @@ -211,19 +226,16 @@ function fetchTMDBFull($title, $year, $apiKey, $pdo) { } if (!empty($platforms)) $streaming = implode(', ', array_unique($platforms)); - $result = [ - 'title' => $details['title'] ?? '', - 'year' => !empty($details['release_date']) ? substr($details['release_date'], 0, 4) : '', - 'director' => $director, - 'poster' => !empty($details['poster_path']) ? "https://image.tmdb.org/t/p/w500" . $details['poster_path'] : '', - 'length' => !empty($details['runtime']) ? $details['runtime'] . ' min' : '', + return [ + 'title' => $details['title'] ?? '', + 'year' => !empty($details['release_date']) ? substr($details['release_date'], 0, 4) : '', + 'director' => $director, + 'poster' => !empty($details['poster_path']) ? "https://image.tmdb.org/t/p/w500" . $details['poster_path'] : '', + 'length' => !empty($details['runtime']) ? $details['runtime'] . ' min' : '', 'streaming' => $streaming, - 'overview' => $overview, - 'cast' => $cast + 'overview' => $overview, + 'cast' => $cast ]; - - setCache($pdo, $cacheKey, $result, 'tmdb'); - return $result; } // ── ROUTEUR PRINCIPAL ──