diff --git a/api.php b/api.php index 446ebc7..c2512f6 100644 --- a/api.php +++ b/api.php @@ -338,9 +338,10 @@ function fetchTmdbPosterAndSynopsis($title, $year = '', $pdo = null) { return $default; } -// ── FONCTION POUR RÉCUPÉRER LES DONNÉES DEPUIS BLU-RAY.COM ── function fetchFromBlurayCom($ean) { static $lastRequest = 0; + static $requestCount = 0; + $empty = [ 'title' => '', 'year' => '', 'director' => '', 'actors' => '', 'poster' => '', 'description' => '', 'length' => '', @@ -349,44 +350,74 @@ function fetchFromBlurayCom($ean) { ]; $ean = preg_replace('/[^0-9]/', '', (string)$ean); - if (strlen($ean) < 8) return $empty; + if (strlen($ean) < 8) { + error_log("Blu-ray.com: ❌ EAN invalide: $ean"); + return $empty; + } - // ✅ THROTTLE : 2 secondes entre chaque requête + // ✅ THROTTLE : 5 secondes entre chaque requête (anti-blocage) $now = microtime(true); - if ($lastRequest > 0 && ($now - $lastRequest) < 2) { - usleep((int)((2 - ($now - $lastRequest)) * 1000000)); + if ($lastRequest > 0 && ($now - $lastRequest) < 5) { + $sleepTime = 5 - ($now - $lastRequest); + error_log("Blu-ray.com: ⏱️ Attente de " . round($sleepTime, 2) . "s avant requête"); + usleep((int)($sleepTime * 1000000)); } $lastRequest = microtime(true); + $requestCount++; - // ÉTAPE 1 : Recherche par EAN sur Blu-ray.com - $searchUrl = "https://www.blu-ray.com/movies/search.php?ean=" . urlencode($ean) . "&action=search"; + // ✅ URL de recherche complète (comme dans le navigateur) + $searchUrl = "https://www.blu-ray.com/movies/search.php?keyword=&studioid=&videocodec=&disc=&yearfrom=&yearto=®ioncoding=&aspectratio=&aspectratio_original=&releaseyear=&synopsis=&retailerexclusive=&mpaa=&runtimemin=&runtimemax=&videoresolutionid=&sourceid=&subtitles=&audio=&upc=&ean=" . urlencode($ean) . "&asin=&casingid=&slipcoverfront=&slipcoverback=&submit=Search&action=search"; + + error_log("Blu-ray.com: 🔍 Recherche EAN $ean (requête #$requestCount)"); $ch = curl_init($searchUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, - CURLOPT_TIMEOUT => 15, - CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_TIMEOUT => 20, + CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 3, + // ✅ User-Agent réaliste (Chrome complet) 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', + // ✅ Headers complets CURLOPT_HTTPHEADER => [ - 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;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/', - 'Connection: keep-alive' - ] + 'Accept-Encoding: gzip, deflate, br', + 'Referer: https://www.blu-ray.com/movies/', + 'Connection: keep-alive', + 'Upgrade-Insecure-Requests: 1', + 'Sec-Fetch-Dest: document', + 'Sec-Fetch-Mode: navigate', + 'Sec-Fetch-Site: same-origin', + 'Sec-Fetch-User: ?1', + 'Cache-Control: max-age=0' + ], + // ✅ Compression + CURLOPT_ENCODING => '', ]); + $searchHtml = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlError = curl_error($ch); curl_close($ch); if (!$searchHtml || $httpCode !== 200) { - error_log("Blu-ray.com: ❌ Échec recherche EAN $ean (HTTP $httpCode)"); + error_log("Blu-ray.com: ❌ Échec recherche EAN $ean (HTTP $httpCode) - Erreur: $curlError"); + + // ✅ Si bloqué (429), attendre plus longtemps + if ($httpCode === 429) { + error_log("Blu-ray.com: ⚠️ Rate limit atteint, attente de 30s"); + sleep(30); + } + return $empty; } - // Extraire l'URL du film depuis les résultats - if (!preg_match('/href="(https?:\/\/www\.blu-ray\.com\/movies\/[^\/]+\/(\d+)\/)"/i', $searchHtml, $matches)) { + // ✅ Regex amélioré pour extraire l'URL du film + // Structure réelle : + 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; } @@ -394,31 +425,35 @@ function fetchFromBlurayCom($ean) { $movieUrl = $matches[1]; $movieId = $matches[2]; - // ✅ THROTTLE avant la 2ème requête - usleep(1000000); + error_log("Blu-ray.com: ✅ Film trouvé → $movieUrl"); + + // ✅ Délai avant la 2ème requête + sleep(3); // É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_TIMEOUT => 20, + CURLOPT_CONNECTTIMEOUT => 10, 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: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,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: https://www.blu-ray.com/movies/search.php', 'Connection: keep-alive' - ] + ], + CURLOPT_ENCODING => '', ]); + $movieHtml = curl_exec($ch2); $httpCode2 = curl_getinfo($ch2, CURLINFO_HTTP_CODE); curl_close($ch2); if (!$movieHtml || $httpCode2 !== 200) { - error_log("Blu-ray.com: ❌ Impossible de charger la page du film $movieUrl (HTTP $httpCode2)"); + error_log("Blu-ray.com: ❌ Impossible de charger la page du film (HTTP $httpCode2)"); return $empty; } @@ -652,7 +687,7 @@ case 'import_batch': $pdo->beginTransaction(); $imported = 0; $skipped = 0; try { - if ($type === 'videotheque') { + 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), @@ -668,22 +703,21 @@ case 'import_batch': foreach ($items as $item) { $ean = preg_replace('/[^0-9]/', '', (string)($item['ean'] ?? '')); - $csvTitle = trim($item['title'] ?? ''); // ✅ Titre issu du CSV + $csvTitle = trim($item['title'] ?? ''); // ✅ Titre du CSV - // ✅ On ne skip que si on n'a ni EAN ni titre if (strlen($ean) < 8 && empty($csvTitle)) { $skipped++; continue; } - // 1. ✅ Données physiques via BLU-RAY.COM (prioritaire) + // 1. Essayer Blu-ray.com (avec throttling) $blurayData = !empty($ean) ? fetchFromBlurayCom($ean) : []; - // ✅ Si Blu-ray.com trouve le film, on prend son titre. Sinon, on prend celui du CSV. + // ✅ Si Blu-ray.com trouve le film, utiliser son titre. Sinon, utiliser celui du CSV. $title = !empty($blurayData['title']) ? $blurayData['title'] : $csvTitle; - // ✅ Si toujours pas de titre, on skip if (empty($title)) { + error_log("Import: ❌ Pas de titre pour EAN $ean - ignoré"); $skipped++; continue; } @@ -701,6 +735,7 @@ case 'import_batch': // 2. ✅ FALLBACK TMDB si Blu-ray.com n'a pas trouvé certaines données if (empty($poster) || empty($director) || empty($actors) || empty($desc) || empty($year)) { + error_log("Import: 🔄 Fallback TMDB pour '$title'"); $tmdb = fetchTmdbPosterAndSynopsis($title, $year, $pdo); if (empty($poster) || $poster === 'assets/img/default_physical_media.jpg') { @@ -716,6 +751,8 @@ case 'import_batch': $id = makeStableId('videotheque', $title, $year); $stmt->execute([$id, $title, $year, $format, $poster, $ean, $desc, $length, $discs, $aspect, $actors, $publisher, $director]); $imported++; + + error_log("Import: ✅ Importé '$title' ($ean)"); } } else { // ── IMPORTATION CRITIQUES ── $stmtCritiques = $pdo->prepare("INSERT INTO critiques (id, title, year, director, poster, rating, review, streaming)