diff --git a/api.php b/api.php index 6928a57..26fd310 100644 --- a/api.php +++ b/api.php @@ -350,10 +350,9 @@ function fetchFromBlurayCom($ean) { $ean = preg_replace('/[^0-9]/', '', (string)$ean); if (strlen($ean) < 8) return $empty; - // 1. Recherche sur Blu-ray.com via l'EAN (URL exacte extraite du site) - $searchUrl = "https://www.blu-ray.com/movies/search.php?ean=" . urlencode($ean) . "&action=search&submit=Search"; + // ÉTAPE 1 : Recherche par EAN sur Blu-ray.com + $searchUrl = "https://www.blu-ray.com/movies/search.php?ean=" . urlencode($ean) . "&action=search"; - // Requête CURL directe pour éviter les blocages (Referer cinemapassion.com bloqué par Blu-ray.com) $ch = curl_init($searchUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, @@ -365,94 +364,135 @@ function fetchFromBlurayCom($ean) { CURLOPT_HTTPHEADER => [ 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', - 'Referer: https://www.blu-ray.com/' // Referer interne pour ne pas être bloqué + 'Referer: https://www.blu-ray.com/' ] ]); - $html = curl_exec($ch); + $searchHtml = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); - if (!$html || $httpCode !== 200) { - error_log("Blu-ray.com: ❌ Échec requête pour EAN $ean (HTTP $httpCode)"); + if (!$searchHtml || $httpCode !== 200) { + error_log("Blu-ray.com: ❌ Échec recherche EAN $ean (HTTP $httpCode)"); return $empty; } - // 2. Extraction des résultats (Correction du Regex pour matcher l'URL absolue) - // Structure réelle : - if (preg_match('/href="(https?:\/\/www\.blu-ray\.com\/movies\/[^\/]+\/(\d+)\/)"[^>]*>.*?]+class="cover"[^>]+src="([^"]+)"/is', $html, $matches)) { - $movieUrl = $matches[1]; - $movieId = $matches[2]; - $posterUrl = $matches[3]; - - // Convertir l'affiche en haute résolution - $posterUrl = str_replace('_small.jpg', '_large.jpg', $posterUrl); - $posterUrl = str_replace('_medium.jpg', '_large.jpg', $posterUrl); - $empty['poster'] = $posterUrl; - - // 3. Récupérer la page du film pour les détails techniques - $ch2 = curl_init($movieUrl); - curl_setopt_array($ch2, [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_TIMEOUT => 15, - CURLOPT_CONNECTTIMEOUT => 5, - CURLOPT_SSL_VERIFYPEER => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36', - CURLOPT_HTTPHEADER => [ - 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', - 'Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', - 'Referer: https://www.blu-ray.com/' - ] - ]); - $movieHtml = curl_exec($ch2); - curl_close($ch2); - - if ($movieHtml) { - // Titre - if (preg_match('/]*>([^<]+)<\/h1>/i', $movieHtml, $m)) { - $empty['title'] = trim(strip_tags($m[1])); - } elseif (preg_match('/([^-<]+)\s*-\s*Blu-ray\.com/i', $movieHtml, $m)) { - $empty['title'] = trim($m[1]); - } - - // Année - if (preg_match('/\((\d{4})\)/', $movieHtml, $m)) { - $empty['year'] = $m[1]; - } - - // Durée (Runtime) - if (preg_match('/Runtime:[^<]*(\d+)\s*min/i', $movieHtml, $m)) { - $empty['length'] = $m[1] . ' min'; - } - - // Studio/Éditeur - if (preg_match('/Studio:[^<]*<a[^>]*>([^<]+)<\/a>/i', $movieHtml, $m)) { - $empty['publisher'] = trim($m[1]); - } - - // Nombre de disques - if (preg_match('/Discs:[^<]*(\d+)/i', $movieHtml, $m)) { - $empty['number_of_discs'] = (int)$m[1]; - } - - // Aspect Ratio - if (preg_match('/Aspect[\s-]*Ratio:[^<]*(\d+[\.\:]\d+)/i', $movieHtml, $m)) { - $empty['aspect_ratio'] = trim($m[1]); - } - - // Format - if (strpos($movieUrl, '/4k/') !== false) { - $empty['format'] = '4K Ultra HD'; - } elseif (strpos($movieUrl, '/3d/') !== false) { - $empty['format'] = '3D Blu-ray'; - } else { - $empty['format'] = 'Blu-ray'; - } - } - } else { - error_log("Blu-ray.com: ❌ Film non trouvé ou structure HTML modifiée pour EAN $ean"); + // Extraire l'URL du film depuis les résultats + 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]; + + // ÉTAPE 2 : Récupérer la page du film + $ch2 = curl_init($movieUrl); + curl_setopt_array($ch2, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 15, + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36', + CURLOPT_HTTPHEADER => [ + 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', + 'Referer: https://www.blu-ray.com/' + ] + ]); + $movieHtml = curl_exec($ch2); + curl_close($ch2); + + if (!$movieHtml) { + error_log("Blu-ray.com: ❌ Impossible de charger la page du film $movieUrl"); + return $empty; + } + + // === EXTRACTION DES DONNÉES === + + // Titre (depuis <h1>) + if (preg_match('/<h1[^>]*>([^<]+)<\/h1>/i', $movieHtml, $m)) { + $rawTitle = trim(strip_tags($m[1])); + $empty['title'] = trim(preg_replace('/\s*(Blu-ray|4K|3D|DVD|UHD).*$/i', '', $rawTitle)); + } + + // Année (depuis <a class="grey" href="...?year=2015"...>2015</a>) + if (preg_match('/href="[^"]*year=(\d{4})[^"]*"[^>]*>(\d{4})<\/a>/i', $movieHtml, $m)) { + $empty['year'] = $m[1]; + } + + // Studio/Éditeur (depuis <a class="grey" href="...?studioid=9"...>Warner Bros.</a>) + if (preg_match('/href="[^"]*studioid=\d+[^"]*"[^>]*>([^<]+)<\/a>/i', $movieHtml, $m)) { + $empty['publisher'] = trim($m[1]); + } + + // Durée (depuis <span id="runtime"...>120 min</span>) + if (preg_match('/<span[^>]*id="runtime"[^>]*>(\d+)\s*min<\/span>/i', $movieHtml, $m)) { + $empty['length'] = $m[1] . ' min'; + } + + // Aspect ratio (depuis "Aspect ratio: 2.40:1<br>") + if (preg_match('/Aspect[\s-]*ratio:\s*([\d\.]+:[\d\.]+)/i', $movieHtml, $m)) { + $empty['aspect_ratio'] = trim($m[1]); + } + + // Nombre de disques (depuis "Two-disc set" ou "Single disc set") + if (preg_match('/(\w+)-disc\s+set/i', $movieHtml, $m)) { + $wordToNum = [ + 'single' => 1, 'two' => 2, 'three' => 3, 'four' => 4, + 'five' => 5, 'six' => 6, 'seven' => 7, 'eight' => 8 + ]; + $word = strtolower($m[1]); + $empty['number_of_discs'] = $wordToNum[$word] ?? 1; + } elseif (preg_match('/(\d+)-disc\s+set/i', $movieHtml, $m)) { + $empty['number_of_discs'] = (int)$m[1]; + } + + // Format (déterminé depuis l'URL ou le contenu) + if (strpos($movieUrl, '/4k/') !== false || stripos($movieHtml, '4K Ultra HD') !== false) { + $empty['format'] = '4K Ultra HD'; + } elseif (strpos($movieUrl, '/3d/') !== false || stripos($movieHtml, '3D Blu-ray') !== false) { + $empty['format'] = '3D Blu-ray'; + } else { + $empty['format'] = 'Blu-ray'; + } + + // Affiche HD (depuis <img id="largefrontimage" src="..._front.jpg">) + 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="([^"]+)"/i', $movieHtml, $m)) { + $posterUrl = $m[1]; + $posterUrl = preg_replace('/_large\.jpg/', '_front.jpg', $posterUrl); + $empty['poster'] = $posterUrl; + } + + // Synopsis, Réalisateur, Acteurs (depuis <div id="movie_info">) + if (preg_match('/<div[^>]*id="movie_info"[^>]*>(.*?)<div[^>]*id="movie_review_intro"/is', $movieHtml, $infoBlock)) { + $infoHtml = $infoBlock[1]; + + // Synopsis (texte après </center><br> et avant <br><br><br>Director:) + if (preg_match('/<\/center><br>\s*(.*?)<br><br><br>Director:/is', $infoHtml, $m)) { + $synopsis = trim(strip_tags($m[1])); + $synopsis = preg_replace('/\s+/', ' ', $synopsis); + $empty['description'] = $synopsis; + } + + // Réalisateur (depuis "Director: <a...>George Miller</a>") + if (preg_match('/Director:\s*<a[^>]*>([^<]+)<\/a>/i', $infoHtml, $m)) { + $empty['director'] = trim($m[1]); + } + + // Acteurs (depuis "Starring: <a...>Tom Hardy</a>, <a...>Charlize Theron</a>, ...") + if (preg_match('/Starring:\s*(.*?)(?:<br>|<\/div>)/is', $infoHtml, $m)) { + $actorsHtml = $m[1]; + preg_match_all('/<a[^>]*>([^<]+)<\/a>/i', $actorsHtml, $actorMatches); + if (!empty($actorMatches[1])) { + $empty['actors'] = implode(', ', array_map('trim', array_slice($actorMatches[1], 0, 6))); + } + } + } + + error_log("Blu-ray.com: ✅ Données récupérées pour EAN $ean → " . $empty['title']); return $empty; } @@ -598,54 +638,60 @@ case 'import_batch': $pdo->beginTransaction(); $imported = 0; $skipped = 0; try { - if ($type === 'videotheque') { - $stmt = $pdo->prepare("INSERT INTO videotheque (id, title, year, format, poster, ean_isbn13, description, length, number_of_discs, aspect_ratio, actors, publisher, director) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE - title=VALUES(title), year=VALUES(year), format=VALUES(format), poster=VALUES(poster), ean_isbn13=VALUES(ean_isbn13), description=VALUES(description), length=VALUES(length), number_of_discs=VALUES(number_of_discs), aspect_ratio=VALUES(aspect_ratio), actors=VALUES(actors), publisher=VALUES(publisher), director=VALUES(director)"); + if ($type === 'videotheque') { + $stmt = $pdo->prepare("INSERT INTO videotheque (id, title, year, format, poster, ean_isbn13, description, length, number_of_discs, aspect_ratio, actors, publisher, director) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE + title=VALUES(title), year=VALUES(year), format=VALUES(format), poster=VALUES(poster), ean_isbn13=VALUES(ean_isbn13), description=VALUES(description), length=VALUES(length), number_of_discs=VALUES(number_of_discs), aspect_ratio=VALUES(aspect_ratio), actors=VALUES(actors), publisher=VALUES(publisher), director=VALUES(director)"); + + foreach ($items as $item) { + $ean = preg_replace('/[^0-9]/', '', (string)($item['ean'] ?? '')); + $csvTitle = trim($item['title'] ?? ''); // Titre issu du CSV (fallback) + + if (strlen($ean) < 8 && empty($csvTitle)) { + $skipped++; + continue; + } + + // 1. Données physiques via BLU-RAY.COM (prioritaire) + $blurayData = !empty($ean) ? fetchFromBlurayCom($ean) : []; + + // Si Blu-ray.com trouve le film, on prend ses données. Sinon, on utilise le titre du CSV. + $title = !empty($blurayData['title']) ? $blurayData['title'] : $csvTitle; + + if (empty($title)) { + $skipped++; + continue; + } + + $year = $blurayData['year'] ?? ''; + $format = $blurayData['format'] ?: detectFormat($title); + $publisher = $blurayData['publisher'] ?? ''; + $discs = $blurayData['number_of_discs'] ?: 1; + $aspect = $blurayData['aspect_ratio'] ?? ''; + $length = $blurayData['length'] ?? ''; + $poster = $blurayData['poster'] ?? ''; + $desc = $blurayData['description'] ?? ''; + $director = $blurayData['director'] ?? ''; + $actors = $blurayData['actors'] ?? ''; + + // 2. Fallback TMDB si Blu-ray.com n'a pas trouvé certaines données + if (empty($poster) || empty($director) || empty($actors) || empty($desc)) { + $tmdb = fetchTmdbPosterAndSynopsis($title, $year, $pdo); - foreach ($items as $item) { - $ean = preg_replace('/[^0-9]/', '', (string)($item['ean'] ?? '')); - $csvTitle = trim($item['title'] ?? ''); // Titre issu de votre CSV - - // 1. Données physiques via BLU-RAY.COM (prioritaire) - $blurayData = fetchFromBlurayCom($ean); - - // Si Blu-ray.com trouve le film, on prend son titre. Sinon, on prend celui du CSV. - $title = !empty($blurayData['title']) ? $blurayData['title'] : $csvTitle; - - if (empty($title)) { - $skipped++; - continue; - } - - $year = $blurayData['year'] ?? ''; - $format = $blurayData['format'] ?: detectFormat($title); - $publisher = $blurayData['publisher'] ?? ''; - $discs = $blurayData['number_of_discs'] ?: 1; - $aspect = $blurayData['aspect_ratio'] ?? ''; - $length = $blurayData['length'] ?? ''; - $poster = $blurayData['poster'] ?? ''; - - // 2. Données cinéma via TMDB (Synopsis, Réalisateur, Acteurs, Année) - $tmdb = fetchTmdbPosterAndSynopsis($title, $year, $pdo); - - // Fusion des données : TMDB complète ce qui manque - $desc = !empty($tmdb['description']) ? $tmdb['description'] : ''; - $director = !empty($tmdb['director']) ? $tmdb['director'] : ''; - $actors = !empty($tmdb['actors']) ? $tmdb['actors'] : ''; - - if (empty($year) && !empty($tmdb['year'])) $year = $tmdb['year']; - if (empty($length) && !empty($tmdb['length'])) $length = $tmdb['length']; - - // Si Blu-ray.com n'a pas trouvé d'affiche, on prend celle de TMDB - if (empty($poster) || $poster === 'assets/img/default_physical_media.jpg') { - $poster = $tmdb['poster']; - } - - $id = makeStableId('videotheque', $title, $year); - $stmt->execute([$id, $title, $year, $format, $poster, $ean, $desc, $length, $discs, $aspect, $actors, $publisher, $director]); - $imported++; + if (empty($poster) || $poster === 'assets/img/default_physical_media.jpg') { + $poster = $tmdb['poster']; } + if (empty($director)) $director = $tmdb['director'] ?? ''; + if (empty($actors)) $actors = $tmdb['actors'] ?? ''; + if (empty($desc)) $desc = $tmdb['description'] ?? ''; + if (empty($length) && !empty($tmdb['length'])) $length = $tmdb['length']; + if (empty($year) && !empty($tmdb['year'])) $year = $tmdb['year']; + } + + $id = makeStableId('videotheque', $title, $year); + $stmt->execute([$id, $title, $year, $format, $poster, $ean, $desc, $length, $discs, $aspect, $actors, $publisher, $director]); + $imported++; + } } else { // ── IMPORTATION CRITIQUES ── $stmtCritiques = $pdo->prepare("INSERT INTO critiques (id, title, year, director, poster, rating, review, streaming) VALUES (?, ?, ?, ?, ?, ?, ?, ?)