From 0ab63300695a7fb2796a8f4ee6d657b6e2682128 Mon Sep 17 00:00:00 2001 From: Cedric Date: Thu, 25 Jun 2026 14:28:31 +0200 Subject: [PATCH] Actualiser api.php --- api.php | 217 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 119 insertions(+), 98 deletions(-) diff --git a/api.php b/api.php index 9642482..7717333 100644 --- a/api.php +++ b/api.php @@ -93,23 +93,57 @@ 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); +// OPTIMISATION MAJEURE : Vérification des URLs en parallèle +function checkUrlsParallel($urls, $timeout = 3) { + if (empty($urls)) return false; + if (!function_exists('curl_multi_init')) return false; + + $multiHandle = curl_multi_init(); + $curlHandles = []; + + foreach ($urls as $url) { + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_NOBODY => true, // On télécharge juste l'entête, pas l'image + 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_multi_add_handle($multiHandle, $ch); + $curlHandles[] = $ch; + } + + $active = null; + $validUrl = false; + + do { + $status = curl_multi_exec($multiHandle, $active); + if ($active) curl_multi_select($multiHandle, 0.1); + + while ($info = curl_multi_info_read($multiHandle)) { + $ch = $info['handle']; + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + // Si on trouve UNE url valide, on arrête tout immédiatement + if ($code >= 200 && $code < 400) { + $validUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); + break 2; + } + curl_multi_remove_handle($multiHandle, $ch); + } + } while ($active && $status == CURLM_OK); + + // Nettoyage de la mémoire + foreach ($curlHandles as $ch) { + curl_multi_remove_handle($multiHandle, $ch); + curl_close($ch); + } + curl_multi_close($multiHandle); + + return $validUrl; } function cleanTitle($title) { @@ -134,16 +168,13 @@ function extractYear($dateStr) { return ''; } -// ── 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 +// ── COVERCENTURY.COM (Version Parallélisée) ── function fetchCoverCentury($title, $year = '', $format = 'bluray') { if (empty($title)) return null; $cleanTitle = cleanTitle($title); if (empty($cleanTitle)) return null; - // Mapping des formats pour CoverCentury $formatMap = [ '4k ultra hd' => '4kultrahd', '4k' => '4kultrahd', @@ -153,80 +184,67 @@ function fetchCoverCentury($title, $year = '', $format = 'bluray') { ]; $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); + $normalized = trim(preg_replace('/\s+/', '_', preg_replace('/[^a-zA-Z0-9\s]/', '', $cleanTitle))); 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); + $withoutArticle = trim(preg_replace('/\s+/', '_', preg_replace('/[^a-zA-Z0-9\s]/', '', preg_replace('/^(The|Le|La|Les|Un|Une|A)\s+/i', '', $cleanTitle)))); 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; - } + $titleVariants[] = 'The_' . $normalized; } - // Construire toutes les URLs candidates - $candidates = []; + // Groupement 1 : Le format exact (Testé en parallèle) + $primaryCandidates = []; 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.) + $primaryCandidates[] = "https://www.covercentury.com/covers/{$ccFormat}/{$firstLetter}/{$variant}.jpg"; 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, - ]; + $primaryCandidates[] = "https://www.covercentury.com/covers/{$ccFormat}/{$firstLetter}/{$variant}{$i}.jpg"; } } - // Si aucun format ne marche, essayer les autres formats + // On teste toutes les URLs du format désiré d'un seul coup (ultra rapide) + $validUrl = checkUrlsParallel($primaryCandidates, 2); + + if ($validUrl) { + return [ + 'poster' => $validUrl, + 'title' => $cleanTitle, + 'format' => $format, + ]; + } + + // Groupement 2 : Si échec, on cherche sur les autres formats en parallèle + $fallbackCandidates = []; $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)); + $fallbackCandidates[] = "https://www.covercentury.com/covers/{$altFormat}/{$firstLetter}/{$variant}.jpg"; 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, - ]; - } + $fallbackCandidates[] = "https://www.covercentury.com/covers/{$altFormat}/{$firstLetter}/{$variant}{$i}.jpg"; } } } + $validUrl = checkUrlsParallel($fallbackCandidates, 2); + + if ($validUrl) { + return [ + 'poster' => $validUrl, + 'title' => $cleanTitle, + 'format' => $format, + ]; + } + error_log("CoverCentury: Aucune jaquette trouvée pour '$title'"); return null; } @@ -366,12 +384,14 @@ switch ($action) { "; $result = $pdo->query($sql)->fetchAll(); - foreach ($result as $row) { + // CORRECTION BUG: L'esperluette "&" est indispensable pour modifier la variable dans le tableau + foreach ($result as &$row) { if ($row['rating'] !== null) { $ratingVal = (float)$row['rating']; $row['rating'] = ($ratingVal == floor($ratingVal)) ? (int)$ratingVal : $ratingVal; } } + unset($row); echo json_encode($result); break; @@ -409,7 +429,6 @@ switch ($action) { } if ($type === 'videotheque') { - // CoverCentury pour vidéothèque $format = $result['format'] ?: 'Blu-ray'; $ccData = fetchCoverCentury($titleForSearch, $result['year'], $format); if (!empty($ccData)) { @@ -455,7 +474,6 @@ 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 : CoverCentury pour la jaquette if (empty($data['poster']) && !empty($data['title'])) { $format = $data['format'] ?: 'Blu-ray'; $ccData = fetchCoverCentury($data['title'], $data['year'] ?? '', $format); @@ -498,6 +516,34 @@ switch ($action) { $pdo->beginTransaction(); try { + // OPTIMISATION MAJEURE : On prépare les requêtes SQL UNE SEULE FOIS en dehors de la boucle + $sqlVideotheque = "INSERT INTO videotheque (id, title, year, director, poster, format, length, publisher, ean_isbn13, number_of_discs, aspect_ratio, description, actors) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + title=VALUES(title), year=VALUES(year), + director=IF(VALUES(director)!='',VALUES(director),director), + poster=IF(VALUES(poster)!='',VALUES(poster),poster), + format=IF(VALUES(format)!='',VALUES(format),format), + length=IF(VALUES(length)!='',VALUES(length),length), + publisher=IF(VALUES(publisher)!='',VALUES(publisher),publisher), + ean_isbn13=IF(VALUES(ean_isbn13)!='',VALUES(ean_isbn13),ean_isbn13), + number_of_discs=IF(VALUES(number_of_discs)!=1,VALUES(number_of_discs),number_of_discs), + aspect_ratio=IF(VALUES(aspect_ratio)!='',VALUES(aspect_ratio),aspect_ratio), + description=IF(VALUES(description)!='',VALUES(description),description), + actors=IF(VALUES(actors)!='',VALUES(actors),actors)"; + $stmtVideotheque = $pdo->prepare($sqlVideotheque); + + $sqlCritiques = "INSERT INTO critiques (id, title, year, director, poster, rating, review, streaming) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + title=VALUES(title), year=VALUES(year), + rating=VALUES(rating), + review=IF(VALUES(review)!='',VALUES(review),review), + director=IF(VALUES(director)!='',VALUES(director),director), + poster=IF(VALUES(poster)!='',VALUES(poster),poster), + streaming=IF(VALUES(streaming)!='',VALUES(streaming),streaming)"; + $stmtCritiques = $pdo->prepare($sqlCritiques); + foreach ($items as $rowData) { $title = $rowData['title'] ?? $rowData['Name'] ?? $rowData['Title'] ?? 'Sans titre'; $publishDate = $rowData['publish_date'] ?? $rowData['Year'] ?? $rowData['year'] ?? $rowData['Date'] ?? ''; @@ -536,7 +582,6 @@ switch ($action) { $poster = $rowData['poster'] ?? ''; $director = ''; - // Récupération jaquette via CoverCentury $cleanTitleForCC = cleanTitle($title); if (!empty($cleanTitleForCC)) { $ccData = fetchCoverCentury($cleanTitleForCC, $year, $format); @@ -550,22 +595,8 @@ switch ($action) { } } - $sql = "INSERT INTO videotheque (id, title, year, director, poster, format, length, publisher, ean_isbn13, number_of_discs, aspect_ratio, description, actors) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - title=VALUES(title), year=VALUES(year), - director=IF(VALUES(director)!='',VALUES(director),director), - poster=IF(VALUES(poster)!='',VALUES(poster),poster), - format=IF(VALUES(format)!='',VALUES(format),format), - length=IF(VALUES(length)!='',VALUES(length),length), - publisher=IF(VALUES(publisher)!='',VALUES(publisher),publisher), - ean_isbn13=IF(VALUES(ean_isbn13)!='',VALUES(ean_isbn13),ean_isbn13), - number_of_discs=IF(VALUES(number_of_discs)!=1,VALUES(number_of_discs),number_of_discs), - aspect_ratio=IF(VALUES(aspect_ratio)!='',VALUES(aspect_ratio),aspect_ratio), - description=IF(VALUES(description)!='',VALUES(description),description), - actors=IF(VALUES(actors)!='',VALUES(actors),actors)"; - $stmt = $pdo->prepare($sql); - $stmt->execute([$id, $title, $year, $director, $poster, $format, $length, $publisher, $ean, $discs, $aspect, $description, $actors]); + // Exécution avec la requête préparée hors de la boucle + $stmtVideotheque->execute([$id, $title, $year, $director, $poster, $format, $length, $publisher, $ean, $discs, $aspect, $description, $actors]); } else { $ratingRaw = $rowData['Rating'] ?? $rowData['rating'] ?? ''; @@ -585,17 +616,8 @@ switch ($action) { } if (empty($streaming)) $streaming = 'Support physique / Cinéma'; - $sql = "INSERT INTO critiques (id, title, year, director, poster, rating, review, streaming) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - title=VALUES(title), year=VALUES(year), - rating=VALUES(rating), - review=IF(VALUES(review)!='',VALUES(review),review), - director=IF(VALUES(director)!='',VALUES(director),director), - poster=IF(VALUES(poster)!='',VALUES(poster),poster), - streaming=IF(VALUES(streaming)!='',VALUES(streaming),streaming)"; - $stmt = $pdo->prepare($sql); - $stmt->execute([$id, $title, $year, $director, $poster, $rating, $review, $streaming]); + // Exécution avec la requête préparée hors de la boucle + $stmtCritiques->execute([$id, $title, $year, $director, $poster, $rating, $review, $streaming]); } $imported++; } @@ -621,7 +643,6 @@ switch ($action) { $result = ['title' => $title, 'year' => $year, 'format' => $format]; - // Afficher les variantes testées $cleanTitle = cleanTitle($title); $normalized = preg_replace('/[^a-zA-Z0-9\s]/', '', $cleanTitle); $normalized = trim(preg_replace('/\s+/', '_', $normalized));