From 04ce52f2d7268c5f0c188b2e129acb8f33f0579c Mon Sep 17 00:00:00 2001 From: Cedric Date: Thu, 25 Jun 2026 14:14:43 +0200 Subject: [PATCH] Actualiser api.php --- api.php | 385 ++++++++++++++++++++++---------------------------------- 1 file changed, 150 insertions(+), 235 deletions(-) diff --git a/api.php b/api.php index 26d8cd4..9642482 100644 --- a/api.php +++ b/api.php @@ -93,6 +93,25 @@ function httpGet($url, $timeout = 3, $ua = null) { return $res ?: null; } +// Vérifie si une URL existe (HEAD request) +function urlExists($url, $timeout = 3) { + if (!function_exists('curl_init')) return false; + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_NOBODY => true, + CURLOPT_TIMEOUT => $timeout, + CURLOPT_CONNECTTIMEOUT => 2, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', + CURLOPT_FOLLOWLOCATION => true, + ]); + curl_exec($ch); + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + return ($code >= 200 && $code < 400); +} + 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); @@ -115,98 +134,101 @@ function extractYear($dateStr) { return ''; } -function fetchDVDCover($title, $year = '', $format = 'bluray') { +// ── COVERCENTURY.COM ── +// Structure : https://www.covercentury.com/covers/{format}/{première_lettre}/{Nom_Normalisé}{numéro}.jpg +// Exemple : https://www.covercentury.com/covers/dvd/t/The_Matrix_Reloaded1.jpg +function fetchCoverCentury($title, $year = '', $format = 'bluray') { if (empty($title)) return null; - - $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; + $cleanTitle = cleanTitle($title); - $searchUrl = "https://www.dvdcover.com/?s=" . urlencode($cleanTitle); - - $html = httpGet($searchUrl, 10, $ua); - if (!$html) { - error_log("DVDCover: Erreur réseau pour '$title'"); - return null; - } - - $dom = new DOMDocument(); - @$dom->loadHTML($html); - $xpath = new DOMXPath($dom); - - // Les résultats sont dans des

- $links = $xpath->query('//h2//a'); - if ($links->length === 0) { - error_log("DVDCover: Aucun lien H2 trouvé pour '$title'"); - return null; - } - - // On cherche le meilleur lien : correspondance titre + année si possible - $bestUrl = null; - $cleanTitleLower = strtolower($cleanTitle); - - foreach ($links as $a) { - $text = strtolower(trim($a->textContent)); - $href = $a->getAttribute('href'); - - // Priorité : correspond au titre ET à l'année - if (!empty($year) && strpos($text, strtolower($cleanTitleLower)) !== false && strpos($text, $year) !== false) { - $bestUrl = $href; - break; - } - - // Fallback : correspond au titre seul (premier trouvé) - if ($bestUrl === null && strpos($text, $cleanTitleLower) !== false) { - $bestUrl = $href; - } - } - - // Dernier fallback : premier lien de la liste - if (!$bestUrl) { - $bestUrl = $links->item(0)->getAttribute('href'); - } - - if (!$bestUrl) return null; - - // On scrape la page de la fiche - $coverHtml = httpGet($bestUrl, 10, $ua); - if (!$coverHtml) return null; - - $domFiche = new DOMDocument(); - @$domFiche->loadHTML($coverHtml); - $xpathFiche = new DOMXPath($domFiche); - - // On cherche la plus grande image dans wp-content/uploads (pas les thumbnails -300x) - $imgs = $xpathFiche->query('//img[contains(@src,"wp-content/uploads")]'); - $bestSrc = null; - $bestSize = 0; - - foreach ($imgs as $img) { - $src = $img->getAttribute('src'); - - // Ignorer logos, icônes et thumbnails redimensionnés - if (strpos($src, 'logo') !== false) continue; - if (strpos($src, 'icon') !== false) continue; - - // Préférer les images sans suffixe de redimensionnement (ex: -300x201) - $isThumb = preg_match('/-\d+x\d+\./', $src); - - // Tenter de récupérer la version pleine taille (supprimer le suffixe -WxH) - $fullSrc = preg_replace('/-\d+x\d+(\.\w+)$/', '$1', $src); - - // Scorer : pleine taille > thumbnail - $score = $isThumb ? 1 : 10; - if ($score > $bestSize) { - $bestSize = $score; - $bestSrc = $fullSrc; - } - } - - if (!$bestSrc) return null; - - return [ - 'poster' => $bestSrc, - 'title' => $cleanTitle, - 'format' => $format + if (empty($cleanTitle)) return null; + + // Mapping des formats pour CoverCentury + $formatMap = [ + '4k ultra hd' => '4kultrahd', + '4k' => '4kultrahd', + 'blu-ray' => 'bluray', + 'bluray' => 'bluray', + 'dvd' => 'dvd', ]; + $ccFormat = $formatMap[strtolower($format)] ?? 'bluray'; + + // Normaliser le titre pour CoverCentury + // Ex: "The Matrix" → "The_Matrix" + // CoverCentury utilise souvent le titre anglais, on va essayer plusieurs variantes + $titleVariants = []; + + // Variante 1 : titre original nettoyé + $normalized = $cleanTitle; + $normalized = preg_replace('/[^a-zA-Z0-9\s]/', '', $normalized); + $normalized = trim($normalized); + $normalized = preg_replace('/\s+/', '_', $normalized); + if (!empty($normalized)) $titleVariants[] = $normalized; + + // Variante 2 : sans article au début (The, Le, La, Les) + $withoutArticle = preg_replace('/^(The|Le|La|Les|Un|Une|A)\s+/i', '', $cleanTitle); + $withoutArticle = preg_replace('/[^a-zA-Z0-9\s]/', '', $withoutArticle); + $withoutArticle = trim($withoutArticle); + $withoutArticle = preg_replace('/\s+/', '_', $withoutArticle); + if (!empty($withoutArticle) && $withoutArticle !== $normalized) { + $titleVariants[] = $withoutArticle; + } + + // Variante 3 : ajouter l'article "The_" si absent + if (!preg_match('/^The_/i', $normalized)) { + $withThe = 'The_' . $normalized; + if (!in_array($withThe, $titleVariants)) { + $titleVariants[] = $withThe; + } + } + + // Construire toutes les URLs candidates + $candidates = []; + foreach ($titleVariants as $variant) { + if (empty($variant)) continue; + $firstLetter = strtolower(substr($variant, 0, 1)); + + // Essayer avec différents suffixes numériques (1, 2, 3, etc.) + for ($i = 1; $i <= 5; $i++) { + $candidates[] = "https://www.covercentury.com/covers/{$ccFormat}/{$firstLetter}/{$variant}{$i}.jpg"; + } + // Et sans numéro + $candidates[] = "https://www.covercentury.com/covers/{$ccFormat}/{$firstLetter}/{$variant}.jpg"; + } + + // Tester chaque URL + foreach ($candidates as $url) { + if (urlExists($url, 2)) { + return [ + 'poster' => $url, + 'title' => $cleanTitle, + 'format' => $format, + ]; + } + } + + // Si aucun format ne marche, essayer les autres formats + $otherFormats = ['dvd', 'bluray', '4kultrahd']; + foreach ($otherFormats as $altFormat) { + if ($altFormat === $ccFormat) continue; + foreach ($titleVariants as $variant) { + if (empty($variant)) continue; + $firstLetter = strtolower(substr($variant, 0, 1)); + for ($i = 1; $i <= 3; $i++) { + $url = "https://www.covercentury.com/covers/{$altFormat}/{$firstLetter}/{$variant}{$i}.jpg"; + if (urlExists($url, 2)) { + return [ + 'poster' => $url, + 'title' => $cleanTitle, + 'format' => $format, + ]; + } + } + } + } + + error_log("CoverCentury: Aucune jaquette trouvée pour '$title'"); + return null; } // ── API TMDB (uniquement pour les critiques) ── @@ -387,12 +409,12 @@ switch ($action) { } if ($type === 'videotheque') { - // DVDCover pour vidéothèque + // CoverCentury pour vidéothèque $format = $result['format'] ?: 'Blu-ray'; - $dcData = fetchDVDCover($titleForSearch, $result['year'], $format); - if (!empty($dcData)) { - if (!empty($dcData['poster'])) $result['poster'] = $dcData['poster']; - if (!empty($dcData['title'])) $result['title'] = $dcData['title']; + $ccData = fetchCoverCentury($titleForSearch, $result['year'], $format); + if (!empty($ccData)) { + if (!empty($ccData['poster'])) $result['poster'] = $ccData['poster']; + if (!empty($ccData['title'])) $result['title'] = $ccData['title']; $result['format'] = $format; } } else { @@ -433,12 +455,12 @@ switch ($action) { $stmt = $pdo->prepare($sql); $stmt->execute([$id, $data['title'] ?? '', $data['year'] ?? '', $data['director'] ?? '', $data['poster'] ?? '', $data['rating'] ?? 3.0, $data['review'] ?? '', $streaming]); } else { - // Vidéothèque : DVDCover pour la jaquette + // Vidéothèque : CoverCentury pour la jaquette if (empty($data['poster']) && !empty($data['title'])) { $format = $data['format'] ?: 'Blu-ray'; - $dcData = fetchDVDCover($data['title'], $data['year'] ?? '', $format); - if (!empty($dcData['poster'])) { - $data['poster'] = $dcData['poster']; + $ccData = fetchCoverCentury($data['title'], $data['year'] ?? '', $format); + if (!empty($ccData['poster'])) { + $data['poster'] = $ccData['poster']; } } @@ -514,16 +536,16 @@ switch ($action) { $poster = $rowData['poster'] ?? ''; $director = ''; - // Récupération jaquette via DVDCover - $cleanTitleForDC = cleanTitle($title); - if (!empty($cleanTitleForDC)) { - $dcData = fetchDVDCover($cleanTitleForDC, $year, $format); - if (!empty($dcData)) { - if (!empty($dcData['poster'])) { - $poster = $dcData['poster']; + // Récupération jaquette via CoverCentury + $cleanTitleForCC = cleanTitle($title); + if (!empty($cleanTitleForCC)) { + $ccData = fetchCoverCentury($cleanTitleForCC, $year, $format); + if (!empty($ccData)) { + if (!empty($ccData['poster'])) { + $poster = $ccData['poster']; } - if (!empty($dcData['title']) && ($title === 'Sans titre' || empty($title))) { - $title = $dcData['title']; + if (!empty($ccData['title']) && ($title === 'Sans titre' || empty($title))) { + $title = $ccData['title']; } } } @@ -590,141 +612,34 @@ switch ($action) { } break; - case 'debug_dvdcover': + case 'debug_covercentury': $title = $_GET['title'] ?? ''; - $year = $_GET['year'] ?? ''; + $year = $_GET['year'] ?? ''; $format = $_GET['format'] ?? 'Blu-ray'; + if (!$title) { echo json_encode(['error' => 'Titre manquant']); exit; } - - $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; - $searchUrl = "https://www.dvdcover.com/?s=" . urlencode(cleanTitle($title)); - $html = httpGet($searchUrl, 10, $ua); + $result = ['title' => $title, 'year' => $year, 'format' => $format]; - $diag = [ - 'url' => $searchUrl, - 'html_length' => $html ? strlen($html) : 0, - 'html_snippet' => $html ? substr(strip_tags($html), 0, 500) : 'VIDE/ERREUR', - 'cloudflare' => ($html && strpos($html, 'cloudflare') !== false) ? true : false, - 'captcha' => ($html && strpos($html, 'captcha') !== false) ? true : false, + // Afficher les variantes testées + $cleanTitle = cleanTitle($title); + $normalized = preg_replace('/[^a-zA-Z0-9\s]/', '', $cleanTitle); + $normalized = trim(preg_replace('/\s+/', '_', $normalized)); + $withoutArticle = preg_replace('/^(The|Le|La|Les|Un|Une|A)\s+/i', '', $cleanTitle); + $withoutArticle = preg_replace('/[^a-zA-Z0-9\s]/', '', $withoutArticle); + $withoutArticle = trim(preg_replace('/\s+/', '_', $withoutArticle)); + + $result['variants'] = [ + 'normalized' => $normalized, + 'without_article' => $withoutArticle, ]; - - // Cherche les articles si HTML reçu - if ($html) { - $dom = new DOMDocument(); - @$dom->loadHTML($html); - $xpath = new DOMXPath($dom); - $links = $xpath->query('//article[contains(@class,"post")]//a/@href'); - $diag['articles_found'] = $links->length; - - // Essai avec d'autres sélecteurs courants - $diag['alt1_h2_links'] = $xpath->query('//h2//a/@href')->length; - $h2Links = $xpath->query('//h2//a'); - $diag['h2_links_list'] = []; - foreach ($h2Links as $a) { - $diag['h2_links_list'][] = [ - 'text' => trim($a->textContent), - 'href' => $a->getAttribute('href') - ]; - } - - // Et les imgs disponibles dans la page - $allImgs = $xpath->query('//img/@src'); - $diag['img_samples'] = []; - $i = 0; - foreach ($allImgs as $img) { - if ($i++ >= 15) break; - $diag['img_samples'][] = $img->nodeValue; - } - $diag['alt2_figure_imgs'] = $xpath->query('//figure//img/@src')->length; - $diag['alt3_any_article'] = $xpath->query('//article')->length; - } - - echo json_encode($diag, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + + $data = fetchCoverCentury($title, $year, $format); + $result['data'] = $data; + $result['status'] = $data ? 'OK' : 'AUCUN_RÉSULTAT'; + + echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); exit; break; - - case 'test_dvdfr': - $title = urlencode($_GET['title'] ?? 'Matrix'); - $url = "https://www.dvdfr.com/recherche/?q={$title}"; - $html = httpGet($url, 5); - if (!$html) { echo json_encode(['error' => 'ERREUR réseau']); break; } - - $dom = new DOMDocument(); - @$dom->loadHTML($html); - $xpath = new DOMXPath($dom); - - // Cherche tous les liens qui ressemblent à des fiches produit - $allLinks = $xpath->query('//a[contains(@href,"/dvd/") or contains(@href,"/film/") or contains(@href,"/bluray/")]'); - $links = []; - foreach ($allLinks as $a) { - $href = $a->getAttribute('href'); - $text = trim($a->textContent); - if (!in_array($href, array_column($links, 'href')) && !empty($text)) { - $links[] = ['text' => substr($text, 0, 80), 'href' => $href]; - } - if (count($links) >= 10) break; - } - - // Cherche les images produit - $imgs = $xpath->query('//img[contains(@src,"cover") or contains(@src,"dvd") or contains(@src,"blu") or contains(@src,"jaquette")]'); - $imgList = []; - foreach ($imgs as $img) { - $imgList[] = $img->getAttribute('src'); - if (count($imgList) >= 10) break; - } - - // Fallback : toutes les images wp-content ou cdn - if (empty($imgList)) { - $imgs2 = $xpath->query('//img/@src'); - foreach ($imgs2 as $img) { - $imgList[] = $img->nodeValue; - if (count($imgList) >= 15) break; - } - } - - echo json_encode([ - 'url' => $url, - 'links' => $links, - 'imgs' => $imgList - ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); - break; - -case 'test_covercentury': - $title = $_GET['title'] ?? 'Matrix'; - $urls = [ - "https://www.covercentury.com/index.php?p=dvdcovers&recherche=" . urlencode($title), - "https://www.covercentury.com/index.php?p=blu-raycovers&recherche=" . urlencode($title), - "https://www.covercentury.com/index.php?p=dvdcovers&q=" . urlencode($title), - "https://www.covercentury.com/index.php?p=dvdcovers&s=" . urlencode($title), - ]; - - $results = []; - foreach ($urls as $url) { - $html = httpGet($url, 8); - if (!$html) { $results[] = ['url' => $url, 'length' => 0, 'snippet' => 'ERREUR']; continue; } - - $dom = new DOMDocument(); - @$dom->loadHTML($html); - $xpath = new DOMXPath($dom); - - $allImgs = $xpath->query('//img'); - $imgs = []; - foreach ($allImgs as $img) { - $src = $img->getAttribute('src'); - if (strpos($src, 'logo') === false && strpos($src, 'assets') === false) { - $imgs[] = ['src' => $src, 'alt' => $img->getAttribute('alt')]; - } - if (count($imgs) >= 10) break; - } - - $results[] = [ - 'url' => $url, - 'length' => strlen($html), - 'imgs' => $imgs - ]; - } - echo json_encode($results, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); - break; } ?> \ No newline at end of file