diff --git a/api.php b/api.php index 6e8acaa..96c1c45 100644 --- a/api.php +++ b/api.php @@ -2,6 +2,10 @@ ini_set('log_errors', 1); ini_set('error_log', __DIR__ . '/php_errors.log'); ini_set('display_errors', 0); +// Augmenter les limites pour éviter les timeouts +set_time_limit(600); // 10 minutes max +ini_set('max_execution_time', 600); +ini_set('memory_limit', '512M'); error_reporting(E_ALL); header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Origin: *"); @@ -340,8 +344,6 @@ function fetchTmdbPosterAndSynopsis($title, $year = '', $pdo = null) { function fetchFromBlurayCom($ean) { static $lastRequest = 0; - static $requestCount = 0; - $empty = [ 'title' => '', 'year' => '', 'director' => '', 'actors' => '', 'poster' => '', 'description' => '', 'length' => '', @@ -355,47 +357,33 @@ function fetchFromBlurayCom($ean) { return $empty; } - // ✅ THROTTLE : 5 secondes entre chaque requête (anti-blocage) + // Throttle: 3 secondes entre chaque requête $now = microtime(true); - if ($lastRequest > 0 && ($now - $lastRequest) < 5) { - $sleepTime = 5 - ($now - $lastRequest); - error_log("Blu-ray.com: ⏱️ Attente de " . round($sleepTime, 2) . "s avant requête"); + if ($lastRequest > 0 && ($now - $lastRequest) < 3) { + $sleepTime = 3 - ($now - $lastRequest); + error_log("Blu-ray.com: ⏱️ Attente de " . round($sleepTime, 2) . "s"); usleep((int)($sleepTime * 1000000)); } $lastRequest = microtime(true); - $requestCount++; - // ✅ 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"); - error_log("Blu-ray.com: 🔍 Recherche EAN $ean (requête #$requestCount)"); + // Recherche simple par EAN + $searchUrl = "https://www.blu-ray.com/movies/search.php?ean=" . urlencode($ean) . "&action=search"; $ch = curl_init($searchUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, - CURLOPT_TIMEOUT => 20, - CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_TIMEOUT => 15, + CURLOPT_CONNECTTIMEOUT => 5, 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/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', - '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 => '', + 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Language: fr-FR,fr;q=0.9', + 'Referer: https://www.blu-ray.com/' + ] ]); $searchHtml = curl_exec($ch); @@ -405,18 +393,10 @@ function fetchFromBlurayCom($ean) { if (!$searchHtml || $httpCode !== 200) { 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; } - // ✅ Regex amélioré pour extraire l'URL du film - // Structure réelle : + // 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; @@ -424,83 +404,64 @@ function fetchFromBlurayCom($ean) { $movieUrl = $matches[1]; $movieId = $matches[2]; - 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 + // Récupérer la page du film $ch2 = curl_init($movieUrl); curl_setopt_array($ch2, [ CURLOPT_RETURNTRANSFER => true, - CURLOPT_TIMEOUT => 20, - CURLOPT_CONNECTTIMEOUT => 10, + 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_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', CURLOPT_HTTPHEADER => [ - '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/movies/search.php', - 'Connection: keep-alive' - ], - CURLOPT_ENCODING => '', + 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Referer: https://www.blu-ray.com/' + ] ]); $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 (HTTP $httpCode2)"); + if (!$movieHtml) { + error_log("Blu-ray.com: ❌ Impossible de charger la page du film"); return $empty; } - // === EXTRACTION DES DONNÉES === - - // Titre + // Extraction des données if (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)); } - // Année if (preg_match('/href="[^"]*year=(\d{4})[^"]*"[^>]*>(\d{4})<\/a>/i', $movieHtml, $m)) { $empty['year'] = $m[1]; } - // Studio/Éditeur if (preg_match('/href="[^"]*studioid=\d+[^"]*"[^>]*>([^<]+)<\/a>/i', $movieHtml, $m)) { $empty['publisher'] = trim($m[1]); } - // Durée if (preg_match('/]*id="runtime"[^>]*>(\d+)\s*min<\/span>/i', $movieHtml, $m)) { $empty['length'] = $m[1] . ' min'; } - // Aspect ratio if (preg_match('/Aspect[\s-]*ratio:\s*([\d\.]+:[\d\.]+)/i', $movieHtml, $m)) { $empty['aspect_ratio'] = trim($m[1]); } - // Nombre de disques 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 - ]; + $wordToNum = ['single' => 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)) { $empty['number_of_discs'] = (int)$m[1]; } - // Format 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) { + } elseif (strpos($movieUrl, '/3d/') !== false) { $empty['format'] = '3D Blu-ray'; } else { $empty['format'] = 'Blu-ray'; @@ -510,38 +471,32 @@ function fetchFromBlurayCom($ean) { if (preg_match('/src="(https:\/\/images\.static-bluray\.com\/movies\/covers\/\d+_front\.jpg[^"]*)"/i', $movieHtml, $m)) { $empty['poster'] = $m[1]; } elseif (preg_match('/]*class="coverfront"[^>]*src="([^"]+)"/i', $movieHtml, $m)) { - $posterUrl = $m[1]; - $posterUrl = preg_replace('/_large\.jpg/', '_front.jpg', $posterUrl); - $empty['poster'] = $posterUrl; + $empty['poster'] = $m[1]; } // Synopsis, Réalisateur, Acteurs if (preg_match('/]*id="movie_info"[^>]*>(.*?)]*id="movie_review_intro"/is', $movieHtml, $infoBlock)) { $infoHtml = $infoBlock[1]; - // Synopsis if (preg_match('/<\/center>
\s*(.*?)


Director:/is', $infoHtml, $m)) { $synopsis = trim(strip_tags($m[1])); $synopsis = preg_replace('/\s+/', ' ', $synopsis); $empty['description'] = $synopsis; } - // Réalisateur if (preg_match('/Director:\s*]*>([^<]+)<\/a>/i', $infoHtml, $m)) { $empty['director'] = trim($m[1]); } - // Acteurs if (preg_match('/Starring:\s*(.*?)(?:
|<\/div>)/is', $infoHtml, $m)) { - $actorsHtml = $m[1]; - preg_match_all('/]*>([^<]+)<\/a>/i', $actorsHtml, $actorMatches); + preg_match_all('/]*>([^<]+)<\/a>/i', $m[1], $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']); + error_log("Blu-ray.com: ✅ Données récupérées → " . $empty['title']); return $empty; } @@ -702,31 +657,22 @@ case 'import_batch': director=IF(VALUES(director)!='', VALUES(director), director)"); foreach ($items as $item) { + $csvTitle = trim($item['title'] ?? ''); $ean = preg_replace('/[^0-9]/', '', (string)($item['ean'] ?? '')); - $csvTitle = trim($item['title'] ?? ''); // ✅ Titre du CSV - if (strlen($ean) < 8 && empty($csvTitle)) { - $skipped++; - continue; - } - - // 1. Essayer Blu-ray.com (avec throttling) - $blurayData = !empty($ean) ? fetchFromBlurayCom($ean) : []; - - if (empty($blurayData['title']) && !empty($ean)) { - error_log("Import: 🔄 Blu-ray.com échoué, essai UPCMDB pour EAN $ean"); - $blurayData = fetchPhysicalByEan($ean, $pdo); - } - - // ✅ Si Blu-ray.com trouve le film, utiliser son titre. Sinon, utiliser celui du CSV. - $title = !empty($blurayData['title']) ? $blurayData['title'] : $csvTitle; - - if (empty($title)) { + if (empty($csvTitle)) { error_log("Import: ❌ Pas de titre pour EAN $ean - ignoré"); $skipped++; continue; } + error_log("Import: 🎬 Traitement de '$csvTitle' (EAN: $ean)"); + + // 1. Essayer Blu-ray.com (peut échouer sans bloquer) + $blurayData = !empty($ean) ? fetchFromBlurayCom($ean) : []; + + // 2. Utiliser le titre du CSV comme base + $title = $csvTitle; $year = $blurayData['year'] ?? ''; $format = $blurayData['format'] ?: detectFormat($title); $publisher = $blurayData['publisher'] ?? ''; @@ -738,26 +684,25 @@ case 'import_batch': $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) || empty($year)) { - error_log("Import: 🔄 Fallback TMDB pour '$title'"); - $tmdb = fetchTmdbPosterAndSynopsis($title, $year, $pdo); - - 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']; + // 3. COMPLÉTER avec TMDB (toujours appelé pour garantir les données) + error_log("Import: 🔄 Appel TMDB pour '$title'"); + $tmdb = fetchTmdbPosterAndSynopsis($title, $year, $pdo); + + // TMDB complète ce qui manque + 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++; - error_log("Import: ✅ Importé '$title' ($ean)"); + error_log("Import: ✅ Importé '$title' (ID: $id)"); } } else { // ── IMPORTATION CRITIQUES ── $stmtCritiques = $pdo->prepare("INSERT INTO critiques (id, title, year, director, poster, rating, review, streaming)