Actualiser api.php

This commit is contained in:
2026-06-25 14:28:31 +02:00
parent 04ce52f2d7
commit 0ab6330069
+119 -98
View File
@@ -93,23 +93,57 @@ function httpGet($url, $timeout = 3, $ua = null) {
return $res ?: null; return $res ?: null;
} }
// Vérifie si une URL existe (HEAD request) // OPTIMISATION MAJEURE : Vérification des URLs en parallèle
function urlExists($url, $timeout = 3) { function checkUrlsParallel($urls, $timeout = 3) {
if (!function_exists('curl_init')) return false; if (empty($urls)) return false;
$ch = curl_init($url); if (!function_exists('curl_multi_init')) return false;
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true, $multiHandle = curl_multi_init();
CURLOPT_NOBODY => true, $curlHandles = [];
CURLOPT_TIMEOUT => $timeout,
CURLOPT_CONNECTTIMEOUT => 2, foreach ($urls as $url) {
CURLOPT_SSL_VERIFYPEER => false, $ch = curl_init($url);
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', curl_setopt_array($ch, [
CURLOPT_FOLLOWLOCATION => true, CURLOPT_RETURNTRANSFER => true,
]); CURLOPT_NOBODY => true, // On télécharge juste l'entête, pas l'image
curl_exec($ch); CURLOPT_TIMEOUT => $timeout,
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE); CURLOPT_CONNECTTIMEOUT => 2,
curl_close($ch); CURLOPT_SSL_VERIFYPEER => false,
return ($code >= 200 && $code < 400); 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) { function cleanTitle($title) {
@@ -134,16 +168,13 @@ function extractYear($dateStr) {
return ''; return '';
} }
// ── COVERCENTURY.COM ── // ── COVERCENTURY.COM (Version Parallélisée) ──
// 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
function fetchCoverCentury($title, $year = '', $format = 'bluray') { function fetchCoverCentury($title, $year = '', $format = 'bluray') {
if (empty($title)) return null; if (empty($title)) return null;
$cleanTitle = cleanTitle($title); $cleanTitle = cleanTitle($title);
if (empty($cleanTitle)) return null; if (empty($cleanTitle)) return null;
// Mapping des formats pour CoverCentury
$formatMap = [ $formatMap = [
'4k ultra hd' => '4kultrahd', '4k ultra hd' => '4kultrahd',
'4k' => '4kultrahd', '4k' => '4kultrahd',
@@ -153,80 +184,67 @@ function fetchCoverCentury($title, $year = '', $format = 'bluray') {
]; ];
$ccFormat = $formatMap[strtolower($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 = []; $titleVariants = [];
// Variante 1 : titre original nettoyé $normalized = trim(preg_replace('/\s+/', '_', preg_replace('/[^a-zA-Z0-9\s]/', '', $cleanTitle)));
$normalized = $cleanTitle;
$normalized = preg_replace('/[^a-zA-Z0-9\s]/', '', $normalized);
$normalized = trim($normalized);
$normalized = preg_replace('/\s+/', '_', $normalized);
if (!empty($normalized)) $titleVariants[] = $normalized; if (!empty($normalized)) $titleVariants[] = $normalized;
// Variante 2 : sans article au début (The, Le, La, Les) $withoutArticle = trim(preg_replace('/\s+/', '_', preg_replace('/[^a-zA-Z0-9\s]/', '', preg_replace('/^(The|Le|La|Les|Un|Une|A)\s+/i', '', $cleanTitle))));
$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);
if (!empty($withoutArticle) && $withoutArticle !== $normalized) { if (!empty($withoutArticle) && $withoutArticle !== $normalized) {
$titleVariants[] = $withoutArticle; $titleVariants[] = $withoutArticle;
} }
// Variante 3 : ajouter l'article "The_" si absent
if (!preg_match('/^The_/i', $normalized)) { if (!preg_match('/^The_/i', $normalized)) {
$withThe = 'The_' . $normalized; $titleVariants[] = 'The_' . $normalized;
if (!in_array($withThe, $titleVariants)) {
$titleVariants[] = $withThe;
}
} }
// Construire toutes les URLs candidates // Groupement 1 : Le format exact (Testé en parallèle)
$candidates = []; $primaryCandidates = [];
foreach ($titleVariants as $variant) { foreach ($titleVariants as $variant) {
if (empty($variant)) continue; if (empty($variant)) continue;
$firstLetter = strtolower(substr($variant, 0, 1)); $firstLetter = strtolower(substr($variant, 0, 1));
$primaryCandidates[] = "https://www.covercentury.com/covers/{$ccFormat}/{$firstLetter}/{$variant}.jpg";
// Essayer avec différents suffixes numériques (1, 2, 3, etc.)
for ($i = 1; $i <= 5; $i++) { for ($i = 1; $i <= 5; $i++) {
$candidates[] = "https://www.covercentury.com/covers/{$ccFormat}/{$firstLetter}/{$variant}{$i}.jpg"; $primaryCandidates[] = "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,
];
} }
} }
// 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']; $otherFormats = ['dvd', 'bluray', '4kultrahd'];
foreach ($otherFormats as $altFormat) { foreach ($otherFormats as $altFormat) {
if ($altFormat === $ccFormat) continue; if ($altFormat === $ccFormat) continue;
foreach ($titleVariants as $variant) { foreach ($titleVariants as $variant) {
if (empty($variant)) continue; if (empty($variant)) continue;
$firstLetter = strtolower(substr($variant, 0, 1)); $firstLetter = strtolower(substr($variant, 0, 1));
$fallbackCandidates[] = "https://www.covercentury.com/covers/{$altFormat}/{$firstLetter}/{$variant}.jpg";
for ($i = 1; $i <= 3; $i++) { for ($i = 1; $i <= 3; $i++) {
$url = "https://www.covercentury.com/covers/{$altFormat}/{$firstLetter}/{$variant}{$i}.jpg"; $fallbackCandidates[] = "https://www.covercentury.com/covers/{$altFormat}/{$firstLetter}/{$variant}{$i}.jpg";
if (urlExists($url, 2)) {
return [
'poster' => $url,
'title' => $cleanTitle,
'format' => $format,
];
}
} }
} }
} }
$validUrl = checkUrlsParallel($fallbackCandidates, 2);
if ($validUrl) {
return [
'poster' => $validUrl,
'title' => $cleanTitle,
'format' => $format,
];
}
error_log("CoverCentury: Aucune jaquette trouvée pour '$title'"); error_log("CoverCentury: Aucune jaquette trouvée pour '$title'");
return null; return null;
} }
@@ -366,12 +384,14 @@ switch ($action) {
"; ";
$result = $pdo->query($sql)->fetchAll(); $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) { if ($row['rating'] !== null) {
$ratingVal = (float)$row['rating']; $ratingVal = (float)$row['rating'];
$row['rating'] = ($ratingVal == floor($ratingVal)) ? (int)$ratingVal : $ratingVal; $row['rating'] = ($ratingVal == floor($ratingVal)) ? (int)$ratingVal : $ratingVal;
} }
} }
unset($row);
echo json_encode($result); echo json_encode($result);
break; break;
@@ -409,7 +429,6 @@ switch ($action) {
} }
if ($type === 'videotheque') { if ($type === 'videotheque') {
// CoverCentury pour vidéothèque
$format = $result['format'] ?: 'Blu-ray'; $format = $result['format'] ?: 'Blu-ray';
$ccData = fetchCoverCentury($titleForSearch, $result['year'], $format); $ccData = fetchCoverCentury($titleForSearch, $result['year'], $format);
if (!empty($ccData)) { if (!empty($ccData)) {
@@ -455,7 +474,6 @@ switch ($action) {
$stmt = $pdo->prepare($sql); $stmt = $pdo->prepare($sql);
$stmt->execute([$id, $data['title'] ?? '', $data['year'] ?? '', $data['director'] ?? '', $data['poster'] ?? '', $data['rating'] ?? 3.0, $data['review'] ?? '', $streaming]); $stmt->execute([$id, $data['title'] ?? '', $data['year'] ?? '', $data['director'] ?? '', $data['poster'] ?? '', $data['rating'] ?? 3.0, $data['review'] ?? '', $streaming]);
} else { } else {
// Vidéothèque : CoverCentury pour la jaquette
if (empty($data['poster']) && !empty($data['title'])) { if (empty($data['poster']) && !empty($data['title'])) {
$format = $data['format'] ?: 'Blu-ray'; $format = $data['format'] ?: 'Blu-ray';
$ccData = fetchCoverCentury($data['title'], $data['year'] ?? '', $format); $ccData = fetchCoverCentury($data['title'], $data['year'] ?? '', $format);
@@ -498,6 +516,34 @@ switch ($action) {
$pdo->beginTransaction(); $pdo->beginTransaction();
try { 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) { foreach ($items as $rowData) {
$title = $rowData['title'] ?? $rowData['Name'] ?? $rowData['Title'] ?? 'Sans titre'; $title = $rowData['title'] ?? $rowData['Name'] ?? $rowData['Title'] ?? 'Sans titre';
$publishDate = $rowData['publish_date'] ?? $rowData['Year'] ?? $rowData['year'] ?? $rowData['Date'] ?? ''; $publishDate = $rowData['publish_date'] ?? $rowData['Year'] ?? $rowData['year'] ?? $rowData['Date'] ?? '';
@@ -536,7 +582,6 @@ switch ($action) {
$poster = $rowData['poster'] ?? ''; $poster = $rowData['poster'] ?? '';
$director = ''; $director = '';
// Récupération jaquette via CoverCentury
$cleanTitleForCC = cleanTitle($title); $cleanTitleForCC = cleanTitle($title);
if (!empty($cleanTitleForCC)) { if (!empty($cleanTitleForCC)) {
$ccData = fetchCoverCentury($cleanTitleForCC, $year, $format); $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) // Exécution avec la requête préparée hors de la boucle
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) $stmtVideotheque->execute([$id, $title, $year, $director, $poster, $format, $length, $publisher, $ean, $discs, $aspect, $description, $actors]);
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]);
} else { } else {
$ratingRaw = $rowData['Rating'] ?? $rowData['rating'] ?? ''; $ratingRaw = $rowData['Rating'] ?? $rowData['rating'] ?? '';
@@ -585,17 +616,8 @@ switch ($action) {
} }
if (empty($streaming)) $streaming = 'Support physique / Cinéma'; if (empty($streaming)) $streaming = 'Support physique / Cinéma';
$sql = "INSERT INTO critiques (id, title, year, director, poster, rating, review, streaming) // Exécution avec la requête préparée hors de la boucle
VALUES (?, ?, ?, ?, ?, ?, ?, ?) $stmtCritiques->execute([$id, $title, $year, $director, $poster, $rating, $review, $streaming]);
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]);
} }
$imported++; $imported++;
} }
@@ -621,7 +643,6 @@ switch ($action) {
$result = ['title' => $title, 'year' => $year, 'format' => $format]; $result = ['title' => $title, 'year' => $year, 'format' => $format];
// Afficher les variantes testées
$cleanTitle = cleanTitle($title); $cleanTitle = cleanTitle($title);
$normalized = preg_replace('/[^a-zA-Z0-9\s]/', '', $cleanTitle); $normalized = preg_replace('/[^a-zA-Z0-9\s]/', '', $cleanTitle);
$normalized = trim(preg_replace('/\s+/', '_', $normalized)); $normalized = trim(preg_replace('/\s+/', '_', $normalized));