From f539f0664c9531cc7a6a2872d1c4a3dabc57f4fe Mon Sep 17 00:00:00 2001 From: Cedric Date: Wed, 1 Jul 2026 11:20:34 +0200 Subject: [PATCH] Actualiser api.php --- api.php | 239 ++++++++++++++++++++++---------------------------------- 1 file changed, 92 insertions(+), 147 deletions(-) diff --git a/api.php b/api.php index 69b483e..749d56a 100644 --- a/api.php +++ b/api.php @@ -419,7 +419,6 @@ function fetchFromBlurayCom($ean) { 'publisher' => '', 'format' => 'Blu-ray', 'number_of_discs' => 1, 'aspect_ratio' => '' ]; - $ean = preg_replace('/[^0-9]/', '', (string)$ean); if (strlen($ean) < 8) { error_log("Blu-ray.com: ❌ EAN invalide: $ean"); @@ -436,8 +435,6 @@ function fetchFromBlurayCom($ean) { $lastRequest = microtime(true); error_log("Blu-ray.com: 🔍 Recherche EAN $ean"); - - // Recherche par EAN $searchUrl = "https://www.blu-ray.com/movies/search.php?ean=" . urlencode($ean) . "&action=search"; $ch = curl_init($searchUrl); curl_setopt_array($ch, [ @@ -453,7 +450,6 @@ function fetchFromBlurayCom($ean) { 'Referer: https://www.blu-ray.com/' ] ]); - $searchHtml = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); @@ -464,20 +460,17 @@ function fetchFromBlurayCom($ean) { return $empty; } - // Extraire l'URL du film - regex améliorée + // Extraire l'URL du film if (!preg_match('/href="(https:\/\/www\.blu-ray\.com\/movies\/[^"]+\/(\d+)\/)"/i', $searchHtml, $matches)) { error_log("Blu-ray.com: ❌ Film non trouvé pour EAN $ean"); return $empty; } - $movieUrl = $matches[1]; $movieId = $matches[2]; error_log("Blu-ray.com: ✅ Film trouvé → $movieUrl"); - // Délai avant la 2ème requête - sleep(2); + sleep(2); // Délai avant la 2ème requête - // Récupérer la page du film $ch2 = curl_init($movieUrl); curl_setopt_array($ch2, [ CURLOPT_RETURNTRANSFER => true, @@ -491,7 +484,6 @@ function fetchFromBlurayCom($ean) { 'Referer: https://www.blu-ray.com/' ] ]); - $movieHtml = curl_exec($ch2); curl_close($ch2); @@ -500,26 +492,19 @@ function fetchFromBlurayCom($ean) { return $empty; } - // Extraction des données - regex robustes face à la refonte du site - // (le site a été redesigné : le

contient désormais un lien imbriqué, - // la div#movie_info / #movie_review_intro qui encadrait les infos a disparu, - // et le span#runtime a été remplacé par du texte brut dans une cellule de tableau) - - // Titre - le

peut désormais contenir un , donc on autorise les balises - // imbriquées avec (.*?) puis on nettoie avec strip_tags - if (preg_match('/]*>(.*?)<\/h1>/is', $movieHtml, $m)) { + // ── EXTRACTION AVEC LES NOUVELLES REGEX ── + + // Titre (dans

inside #movie_info) + if (preg_match('/]*id="movie_info"[^>]*>.*?

([^<]+)<\/h3>/is', $movieHtml, $m)) { + $empty['title'] = trim($m[1]); + } elseif (preg_match('/]*>([^<]+)<\/h1>/i', $movieHtml, $m)) { $rawTitle = trim(strip_tags($m[1])); $empty['title'] = trim(preg_replace('/\s*(Blu-ray|4K|3D|DVD|UHD).*$/i', '', $rawTitle)); } - if (empty($empty['title']) && preg_match('/([^<]+)<\/title>/i', $movieHtml, $m)) { - // Filet de sĂ©curitĂ© : la balise <title> de la page contient toujours le nom du film - $rawTitle = trim(html_entity_decode(strip_tags($m[1]))); - $empty['title'] = trim(preg_replace('/\s*(Blu-ray|4K|3D|DVD|UHD).*$/i', '', $rawTitle)); - } - - // AnnĂ©e - if (preg_match('/href="[^"]*year=(\d{4})[^"]*"[^>]*>(\d{4})<\/a>/i', $movieHtml, $m)) { - $empty['year'] = $m[1]; + + // AnnĂ©e (juste après le <h3>) + if (preg_match('/<h3[^>]*>([^<]+)<\/h3>\s*(?: )?\((\d{4})\)/i', $movieHtml, $m)) { + $empty['year'] = $m[2]; } // Studio/Éditeur @@ -527,26 +512,21 @@ function fetchFromBlurayCom($ean) { $empty['publisher'] = trim($m[1]); } - // DurĂ©e - le span#runtime n'existe plus ; on cherche "NNN min" Ă  proximitĂ© - // du lien "annĂ©e" dĂ©jĂ  trouvĂ© plus haut (ex: "... | 2012 | 123 min | Rated PG | ...") - if (preg_match('/movies\.php\?year=\d{4}[^>]*>\d{4}<\/a>.{0,80}?(\d{2,3})\s*min\b/is', $movieHtml, $m)) { + // DurĂ©e (dans un span avant "Rated") + if (preg_match('/(\d+)\s*min<\/span>/i', $movieHtml, $m)) { $empty['length'] = $m[1] . ' min'; - } elseif (preg_match('/\b(\d{2,3})\s*min\b/i', $movieHtml, $m)) { - // Filet de sĂ©curitĂ©, moins prĂ©cis + } elseif (preg_match('/(\d+)\s*min\s*\|\s*Rated/i', $movieHtml, $m)) { $empty['length'] = $m[1] . ' min'; } - // Aspect ratio - "Original aspect ratio: X.XX:1" est plus fiable que le premier - // "Aspect ratio:" qui peut lister plusieurs valeurs (plans diffĂ©rents dans le film) - if (preg_match('/Original aspect ratio:\s*([\d\.]+:[\d\.]+)/i', $movieHtml, $m)) { - $empty['aspect_ratio'] = trim($m[1]); - } elseif (preg_match('/Aspect[\s-]*ratio:\s*([\d\.]+:[\d\.]+)/i', $movieHtml, $m)) { + // Aspect ratio + if (preg_match('/Aspect[\s-]*ratio:\s*([\d\.]+:[\d\.]+)/i', $movieHtml, $m)) { $empty['aspect_ratio'] = trim($m[1]); } - // Nombre de disques - plusieurs patterns + // Nombre de disques (gère "Three-disc set" en plus des chiffres) if (preg_match('/(\w+)-disc\s+set/i', $movieHtml, $m)) { - $wordToNum = ['single' => 1, 'two' => 2, 'three' => 3, 'four' => 4, 'five' => 5, 'six' => 6]; + $wordToNum = ['single' => 1, 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4, 'five' => 5, 'six' => 6]; $word = strtolower($m[1]); $empty['number_of_discs'] = $wordToNum[$word] ?? 1; } elseif (preg_match('/(\d+)-disc\s+set/i', $movieHtml, $m)) { @@ -562,67 +542,43 @@ function fetchFromBlurayCom($ean) { $empty['format'] = 'Blu-ray'; } - // Affiche HD - le site charge dĂ©sormais les jaquettes en JS (lazy-load, - // le HTML statique ne contient qu'un visuel "Temporary cover art" en placeholder). - // On essaie d'abord les anciens motifs (au cas oĂą ils rĂ©apparaissent sur certaines - // pages), puis on retombe sur l'URL CDN prĂ©visible construite Ă  partir de l'ID du - // film, en la validant par une requĂŞte HEAD avant de l'utiliser. - if (preg_match('/(?:src|data-src)="(https:\/\/images\d*\.static-bluray\.com\/movies\/covers\/\d+_(?:front|large)\.jpg[^"]*)"/i', $movieHtml, $m)) { + // Affiche HD + if (preg_match('/src="(https:\/\/images\.static-bluray\.com\/movies\/covers\/\d+_front\.jpg[^"]*)"/i', $movieHtml, $m)) { $empty['poster'] = $m[1]; - } elseif (preg_match('/<img[^>]*class="[^"]*coverfront[^"]*"[^>]*(?:src|data-src)="([^"]+)"/i', $movieHtml, $m)) { - $posterUrl = preg_replace('/_large\.jpg/', '_front.jpg', $m[1]); + } elseif (preg_match('/<img[^>]*class="coverfront"[^>]*src="([^"]+)"/i', $movieHtml, $m)) { + $posterUrl = $m[1]; + $posterUrl = preg_replace('/_large\.jpg/', '_front.jpg', $posterUrl); $empty['poster'] = $posterUrl; - } else { - error_log("Blu-ray.com: ⚠️ Jaquette absente du HTML statique (lazy-load JS), tentative CDN prĂ©visible pour ID $movieId"); - foreach (['_large.jpg', '_front.jpg'] as $suffix) { - $candidate = "https://images.static-bluray.com/movies/covers/{$movieId}{$suffix}"; - if (urlExists($candidate)) { - $empty['poster'] = $candidate; - break; + } + + // Synopsis, RĂ©alisateur, Acteurs (dans #movie_info) + if (preg_match('/<div[^>]*id="movie_info"[^>]*>(.*?)<div[^>]*id="movie_review_intro"/is', $movieHtml, $infoBlock)) { + $infoHtml = $infoBlock[1]; + + // Synopsis (après la balise <font> vide) + if (preg_match('/<font[^>]*><br><\/font>\s*(.*?)<br><br><br>Directors:/is', $infoHtml, $m)) { + $synopsis = trim(strip_tags($m[1])); + $synopsis = preg_replace('/\s+/', ' ', $synopsis); + $empty['description'] = $synopsis; + } + + // RĂ©alisateur (extrait depuis les balises <a>) + if (preg_match('/Directors?:\s*(.*?)(?:<br>|<\/div>)/is', $infoHtml, $m)) { + preg_match_all('/<a[^>]*>([^<]+)<\/a>/i', $m[1], $dirMatches); + if (!empty($dirMatches[1])) { + $empty['director'] = implode(', ', array_map('trim', array_slice($dirMatches[1], 0, 2))); } } - if (empty($empty['poster'])) { - error_log("Blu-ray.com: ❌ Aucune URL de jaquette CDN valide trouvĂ©e pour ID $movieId (fallback TMDB attendu)"); + + // Acteurs (extrait depuis les balises <a>) + if (preg_match('/Starring:\s*(.*?)(?:<br>|<\/div>)/is', $infoHtml, $m)) { + preg_match_all('/<a[^>]*>([^<]+)<\/a>/i', $m[1], $actorMatches); + if (!empty($actorMatches[1])) { + $empty['actors'] = implode(', ', array_map('trim', array_slice($actorMatches[1], 0, 6))); + } } } - - // Synopsis, RĂ©alisateur, Acteurs - // La div#movie_info / #movie_review_intro qui encadrait ces infos a disparu de la - // refonte du site : "Director:" et "Starring:" apparaissent maintenant directement - // dans le flux HTML de la page, donc on cherche sur $movieHtml en entier plutĂ´t - // que dans un sous-bloc dĂ©limitĂ© par ces anciens ID. - - // RĂ©alisateur - if (preg_match('/Director:\s*<a[^>]*>([^<]+)<\/a>/i', $movieHtml, $m)) { - $empty['director'] = trim(html_entity_decode($m[1])); - } else { - error_log("Blu-ray.com: ⚠️ RĂ©alisateur non trouvĂ© (structure HTML probablement changĂ©e)"); - } - - // Acteurs - if (preg_match('/Starring:\s*(.*?)(?:<br\s*\/?>|<\/(?:p|div|td)>)/is', $movieHtml, $m)) { - preg_match_all('/<a[^>]*>([^<]+)<\/a>/i', $m[1], $actorMatches); - if (!empty($actorMatches[1])) { - $empty['actors'] = implode(', ', array_map(fn($a) => trim(html_entity_decode($a)), array_slice($actorMatches[1], 0, 6))); - } - } else { - error_log("Blu-ray.com: ⚠️ Acteurs non trouvĂ©s (structure HTML probablement changĂ©e)"); - } - - // Synopsis - on capture le dernier bloc de texte "libre" (hors balises) qui - // prĂ©cède immĂ©diatement "Director:", en tolĂ©rant quelques balises intermĂ©diaires - if (preg_match('/>([^<]{60,900})<(?:[^>]*>){0,5}\s*Director:/is', $movieHtml, $m)) { - $synopsis = trim(html_entity_decode(strip_tags($m[1]))); - $synopsis = preg_replace('/\s+/', ' ', $synopsis); - $empty['description'] = $synopsis; - } else { - error_log("Blu-ray.com: ⚠️ Synopsis non trouvĂ© (structure HTML probablement changĂ©e)"); - } - - if (empty($empty['title'])) { - error_log("Blu-ray.com: ⚠️ Titre non extrait malgrĂ© une page trouvĂ©e pour ID $movieId — vĂ©rifier la structure du <h1>/<title>"); - } - + error_log("Blu-ray.com: âś… DonnĂ©es rĂ©cupĂ©rĂ©es pour EAN $ean → " . $empty['title']); return $empty; } @@ -634,57 +590,55 @@ function fetchFromMovieCovers($title, $year = '') { 'publisher' => '', 'format' => 'DVD', 'number_of_discs' => 1, 'aspect_ratio' => '' ]; - if (empty($title)) return $empty; - - // Nettoyer le titre pour l'URL : moviecovers.com attend un titre en MAJUSCULES - // SANS accents ni apostrophes/ponctuation (ex: "L'Étrange NoĂ«l" -> "L+ETRANGE+NOEL"). - // C'est la cause principale des Ă©checs silencieux : un titre accentuĂ© produit une - // URL qui ne correspond Ă  aucune fiche et retombe sur la page "non trouvĂ©". - $normalized = removeAccentsForUrl($title); - $normalized = str_replace(['Ĺ“', 'Ĺ’'], ['OE', 'OE'], $normalized); - $normalized = preg_replace('/[^A-Za-z0-9 ]+/', ' ', $normalized); // apostrophes, ':', '-', etc. -> espace - $normalized = preg_replace('/\s{2,}/', ' ', trim($normalized)); - $urlTitle = strtoupper(str_replace(' ', '+', $normalized)); - - if ($urlTitle === '') { - error_log("MovieCovers: ❌ Titre vide après normalisation pour '$title'"); - return $empty; - } - + + // Nettoyer le titre pour l'URL (les espaces deviennent des +) + $urlTitle = strtoupper(str_replace(' ', '+', $title)); $url = "https://moviecovers.com/film/titre_{$urlTitle}.html"; - error_log("MovieCovers: 🔍 Recherche '$title' → $url"); - $html = httpGet($url, 10); - if (!$html) { - error_log("MovieCovers: ❌ Échec HTTP pour $url"); - return $empty; - } - - // Le site rĂ©pond en 200 mĂŞme quand le film n'existe pas ("Je n'ai pas trouvĂ© de film") - if (stripos($html, "n'ai pas trouv") !== false) { - error_log("MovieCovers: ❌ Film non trouvĂ© pour '$title' (URL: $url)"); - return $empty; - } - + if (!$html) return $empty; + // Extraire le titre - if (preg_match('/<TITLE>([^<]+)<\/TITLE>/i', $html, $m)) { + if (preg_match('/<meta property="og:title" content="([^"]+)"/i', $html, $m)) { + $empty['title'] = trim($m[1]); + } elseif (preg_match('/<TITLE>([^<]+)<\/TITLE>/i', $html, $m)) { $empty['title'] = trim($m[1]); - } elseif (preg_match('/<meta[^>]*property="og:title"[^>]*content="([^"]+)"/i', $html, $m)) { - $empty['title'] = trim(html_entity_decode($m[1])); } - // Extraire l'affiche - plusieurs filets de sĂ©curitĂ©, l'ordre des attributs - // (src/alt/title) dans le <img> n'Ă©tant pas garanti - if (preg_match('/<img[^>]*src="(https:\/\/moviecovers\.com\/DATA\/thumbs\/[^"]+)"[^>]*title="Recto/i', $html, $m)) { - $empty['poster'] = $m[1]; - } elseif (preg_match('/title="Recto[^"]*"[^>]*src="(https:\/\/moviecovers\.com\/DATA\/thumbs\/[^"]+)"/i', $html, $m)) { - // MĂŞme image mais avec title AVANT src dans la balise - $empty['poster'] = $m[1]; - } elseif (preg_match('/<meta[^>]*property="og:image"[^>]*content="([^"]+)"/i', $html, $m)) { - $empty['poster'] = $m[1]; - } elseif (preg_match('/<meta[^>]*(?:property|name)="twitter:image"[^>]*content="([^"]+)"/i', $html, $m)) { - $empty['poster'] = $m[1]; + // Extraire l'IDMC pour construire l'URL de l'image HD (comme dans votre script Python) + $idmc = ''; + if (preg_match('/IDMC.*?<PRE>([^<]+)<\/PRE>/is', $html, $m)) { + $idmc = trim($m[1]); + } elseif (preg_match('/<meta property="og:image" content="[^"]*\/getjpg\.html\/([^"\']+)\.jpg"/i', $html, $m)) { + $idmc = urldecode($m[1]); + } + + // Essayer de rĂ©cupĂ©rer l'affiche HD via les URLs du script Python + if ($idmc) { + $idmc_encoded = urlencode($idmc); + $hd_urls = [ + "https://moviecovers.com/DATA/zipcache/{$idmc_encoded}.jpg", + "https://moviecovers.com/getjpg.html/{$idmc_encoded}.jpg" + ]; + foreach ($hd_urls as $hd_url) { + $img_data = httpGet($hd_url, 5); + if ($img_data && strlen($img_data) > 5000) { // VĂ©rifie que c'est une vraie image (>5Ko) + $empty['poster'] = $hd_url; + break; + } + } + } + + // Fallback sur le thumbnail si pas d'image HD trouvĂ©e + if (empty($empty['poster'])) { + // CORRECTION : La regex gère le cas oĂą title= est avant src= + if (preg_match('/<img[^>]*title="Recto[^"]*"[^>]*src="([^"]+)"/i', $html, $m)) { + $empty['poster'] = $m[1]; + } elseif (preg_match('/<img[^>]*src="([^"]+)"[^>]*title="Recto/i', $html, $m)) { + $empty['poster'] = $m[1]; + } elseif (preg_match('/<meta[^>]*property="og:image"[^>]*content="([^"]+)"/i', $html, $m)) { + $empty['poster'] = $m[1]; + } } // Extraire le rĂ©alisateur @@ -712,15 +666,6 @@ function fetchFromMovieCovers($title, $year = '') { $empty['description'] = html_entity_decode($m[1]); } - // Format haute qualitĂ© de l'affiche - if ($empty['poster']) { - $empty['poster'] = str_replace('/thumbs/', '/films-l/', $empty['poster']); - } else { - error_log("MovieCovers: ⚠️ Jaquette non trouvĂ©e pour '$title' (URL: $url) — structure HTML probablement changĂ©e"); - } - - error_log("MovieCovers: " . (!empty($empty['title']) ? "âś… DonnĂ©es rĂ©cupĂ©rĂ©es pour '$title' → " . $empty['title'] : "⚠️ Page trouvĂ©e mais titre non extrait pour '$title'")); - return $empty; }