Actualiser api.php
This commit is contained in:
@@ -93,59 +93,6 @@ function httpGet($url, $timeout = 3, $ua = null) {
|
||||
return $res ?: null;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
$clean = preg_replace('/\s*[\[\(].*?[\]\)]\s*/', '', $title);
|
||||
$clean = preg_replace('/\s*-\s*(Édition|Edition|Collector|Simple|Spéciale|Digibook|Ultimate|Intégrale|Combo|SteelBook|Boîtier).*$/i', '', $clean);
|
||||
@@ -168,85 +115,190 @@ function extractYear($dateStr) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// ── COVERCENTURY.COM (Version Parallélisée) ──
|
||||
function fetchCoverCentury($title, $year = '', $format = 'bluray') {
|
||||
// ── DVDBlurayCovers.com Scraper ──
|
||||
function fetchDVDBlurayCovers($title, $year = '', $format = 'bluray') {
|
||||
if (empty($title)) return null;
|
||||
|
||||
$ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
||||
$cleanTitle = cleanTitle($title);
|
||||
if (empty($cleanTitle)) return null;
|
||||
|
||||
$formatMap = [
|
||||
'4k ultra hd' => '4kultrahd',
|
||||
'4k' => '4kultrahd',
|
||||
'blu-ray' => 'bluray',
|
||||
'bluray' => 'bluray',
|
||||
'dvd' => 'dvd',
|
||||
// URL de recherche DVDBlurayCovers
|
||||
$searchUrl = "https://www.dvdbluraycovers.com/?s=" . urlencode($cleanTitle);
|
||||
|
||||
$html = httpGet($searchUrl, 10, $ua);
|
||||
if (!$html) {
|
||||
error_log("DVDBlurayCovers: Échec recherche pour '$title'");
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = [
|
||||
'poster' => '',
|
||||
'title' => '',
|
||||
'format' => $format,
|
||||
];
|
||||
$ccFormat = $formatMap[strtolower($format)] ?? 'bluray';
|
||||
|
||||
$titleVariants = [];
|
||||
// Parser le HTML avec DOMDocument
|
||||
$dom = new DOMDocument();
|
||||
@$dom->loadHTML($html);
|
||||
$xpath = new DOMXPath($dom);
|
||||
|
||||
$normalized = trim(preg_replace('/\s+/', '_', preg_replace('/[^a-zA-Z0-9\s]/', '', $cleanTitle)));
|
||||
if (!empty($normalized)) $titleVariants[] = $normalized;
|
||||
// Chercher les liens vers les pages de covers individuelles
|
||||
// DVDBlurayCovers utilise des articles avec des liens vers les covers
|
||||
$links = $xpath->query('//article[contains(@class,"post") or contains(@class,"entry")]//a/@href');
|
||||
|
||||
$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;
|
||||
if ($links->length === 0) {
|
||||
// Fallback : chercher tous les liens qui ressemblent à des pages de covers
|
||||
$links = $xpath->query('//a[contains(@href,"/cover/") or contains(@href,"/covers/") or contains(@href,"/bluray/") or contains(@href,"/dvd/")]');
|
||||
}
|
||||
|
||||
if (!preg_match('/^The_/i', $normalized)) {
|
||||
$titleVariants[] = 'The_' . $normalized;
|
||||
}
|
||||
|
||||
// Groupement 1 : Le format exact (Testé en parallèle)
|
||||
$primaryCandidates = [];
|
||||
foreach ($titleVariants as $variant) {
|
||||
if (empty($variant)) continue;
|
||||
$firstLetter = strtolower(substr($variant, 0, 1));
|
||||
$primaryCandidates[] = "https://www.covercentury.com/covers/{$ccFormat}/{$firstLetter}/{$variant}.jpg";
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$primaryCandidates[] = "https://www.covercentury.com/covers/{$ccFormat}/{$firstLetter}/{$variant}{$i}.jpg";
|
||||
$coverPages = [];
|
||||
foreach ($links as $link) {
|
||||
$href = $link->nodeValue;
|
||||
if (strpos($href, 'http') !== 0) {
|
||||
$href = 'https://www.dvdbluraycovers.com' . $href;
|
||||
}
|
||||
// Éviter les doublons et les pages de catégories/tags
|
||||
if (!in_array($href, $coverPages) &&
|
||||
strpos($href, '/category/') === false &&
|
||||
strpos($href, '/tag/') === false &&
|
||||
strpos($href, '/page/') === false) {
|
||||
$coverPages[] = $href;
|
||||
}
|
||||
if (count($coverPages) >= 5) break; // Limiter à 5 résultats max
|
||||
}
|
||||
|
||||
// On teste toutes les URLs du format désiré d'un seul coup (ultra rapide)
|
||||
$validUrl = checkUrlsParallel($primaryCandidates, 2);
|
||||
// Parcourir les pages de covers trouvées
|
||||
foreach ($coverPages as $coverPage) {
|
||||
$coverHtml = httpGet($coverPage, 10, $ua);
|
||||
if (!$coverHtml) continue;
|
||||
|
||||
if ($validUrl) {
|
||||
return [
|
||||
'poster' => $validUrl,
|
||||
'title' => $cleanTitle,
|
||||
'format' => $format,
|
||||
];
|
||||
}
|
||||
$domCover = new DOMDocument();
|
||||
@$domCover->loadHTML($coverHtml);
|
||||
$xpathCover = new DOMXPath($domCover);
|
||||
|
||||
// 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++) {
|
||||
$fallbackCandidates[] = "https://www.covercentury.com/covers/{$altFormat}/{$firstLetter}/{$variant}{$i}.jpg";
|
||||
// Extraire le titre de la page
|
||||
$coverTitle = '';
|
||||
$titleNodes = $xpathCover->query('//h1[contains(@class,"entry-title") or contains(@class,"post-title")]');
|
||||
if ($titleNodes->length > 0) {
|
||||
$coverTitle = trim($titleNodes->item(0)->textContent);
|
||||
} else {
|
||||
$titleNodes = $xpathCover->query('//title');
|
||||
if ($titleNodes->length > 0) {
|
||||
$coverTitle = trim($titleNodes->item(0)->textContent);
|
||||
// Nettoyer le titre (enlever " - DVDBlurayCovers" etc.)
|
||||
$coverTitle = preg_replace('/\s*[-|]\s*DVDBlurayCovers.*$/i', '', $coverTitle);
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier que le titre correspond au film recherché
|
||||
$coverTitleLower = strtolower($coverTitle);
|
||||
$searchTitleLower = strtolower($cleanTitle);
|
||||
|
||||
$score = 0;
|
||||
if (strpos($coverTitleLower, $searchTitleLower) !== false) {
|
||||
$score += 50;
|
||||
}
|
||||
if (!empty($year) && strpos($coverTitle, $year) !== false) {
|
||||
$score += 30;
|
||||
}
|
||||
if (stripos($coverTitle, $format) !== false) {
|
||||
$score += 20;
|
||||
}
|
||||
|
||||
// Si le score est trop bas, on ignore ce résultat
|
||||
if ($score < 30) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Chercher l'image principale de la jaquette
|
||||
$poster = '';
|
||||
|
||||
// Méthode 1 : Chercher dans le contenu principal (entry-content)
|
||||
$contentNodes = $xpathCover->query('//div[contains(@class,"entry-content") or contains(@class,"post-content")]//img');
|
||||
if ($contentNodes->length > 0) {
|
||||
foreach ($contentNodes as $img) {
|
||||
$src = $img->getAttribute('src');
|
||||
if (empty($src)) {
|
||||
$src = $img->getAttribute('data-src');
|
||||
}
|
||||
if (empty($src)) {
|
||||
$src = $img->getAttribute('data-lazy-src');
|
||||
}
|
||||
|
||||
// Vérifier que c'est une vraie image (pas un logo, icône, etc.)
|
||||
if (!empty($src) &&
|
||||
strpos($src, 'logo') === false &&
|
||||
strpos($src, 'icon') === false &&
|
||||
strpos($src, 'banner') === false &&
|
||||
strpos($src, 'bg') === false &&
|
||||
strpos($src, 'button') === false &&
|
||||
strpos($src, 'social') === false &&
|
||||
(strpos($src, '.jpg') !== false || strpos($src, '.jpeg') !== false || strpos($src, '.png') !== false || strpos($src, '.webp') !== false)) {
|
||||
$poster = $src;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode 2 : Chercher dans les figures
|
||||
if (empty($poster)) {
|
||||
$figureNodes = $xpathCover->query('//figure//img');
|
||||
if ($figureNodes->length > 0) {
|
||||
foreach ($figureNodes as $img) {
|
||||
$src = $img->getAttribute('src');
|
||||
if (empty($src)) {
|
||||
$src = $img->getAttribute('data-src');
|
||||
}
|
||||
if (!empty($src) &&
|
||||
strpos($src, 'logo') === false &&
|
||||
strpos($src, 'icon') === false &&
|
||||
(strpos($src, '.jpg') !== false || strpos($src, '.jpeg') !== false || strpos($src, '.png') !== false)) {
|
||||
$poster = $src;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode 3 : Meta Open Graph
|
||||
if (empty($poster)) {
|
||||
$ogNodes = $xpathCover->query('//meta[@property="og:image"]/@content');
|
||||
if ($ogNodes->length > 0) {
|
||||
$poster = $ogNodes->item(0)->nodeValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode 4 : Toutes les images dans wp-content/uploads
|
||||
if (empty($poster)) {
|
||||
$allImgs = $xpathCover->query('//img[contains(@src,"wp-content/uploads")]');
|
||||
if ($allImgs->length > 0) {
|
||||
foreach ($allImgs as $img) {
|
||||
$src = $img->getAttribute('src');
|
||||
if (strpos($src, 'logo') === false &&
|
||||
strpos($src, 'icon') === false &&
|
||||
strpos($src, '300x') === false &&
|
||||
strpos($src, '150x') === false) {
|
||||
$poster = $src;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si on a trouvé une image, on la garde
|
||||
if (!empty($poster)) {
|
||||
// S'assurer que l'URL est absolue
|
||||
if (strpos($poster, 'http') !== 0) {
|
||||
$poster = 'https://www.dvdbluraycovers.com' . (strpos($poster, '/') === 0 ? '' : '/') . ltrim($poster, '/');
|
||||
}
|
||||
|
||||
$result['poster'] = $poster;
|
||||
$result['title'] = $coverTitle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$validUrl = checkUrlsParallel($fallbackCandidates, 2);
|
||||
|
||||
if ($validUrl) {
|
||||
return [
|
||||
'poster' => $validUrl,
|
||||
'title' => $cleanTitle,
|
||||
'format' => $format,
|
||||
];
|
||||
}
|
||||
|
||||
error_log("CoverCentury: Aucune jaquette trouvée pour '$title'");
|
||||
return null;
|
||||
return (!empty($result['poster'])) ? $result : null;
|
||||
}
|
||||
|
||||
// ── API TMDB (uniquement pour les critiques) ──
|
||||
@@ -384,7 +436,6 @@ switch ($action) {
|
||||
";
|
||||
$result = $pdo->query($sql)->fetchAll();
|
||||
|
||||
// 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'];
|
||||
@@ -429,11 +480,12 @@ switch ($action) {
|
||||
}
|
||||
|
||||
if ($type === 'videotheque') {
|
||||
// DVDBlurayCovers pour vidéothèque
|
||||
$format = $result['format'] ?: 'Blu-ray';
|
||||
$ccData = fetchCoverCentury($titleForSearch, $result['year'], $format);
|
||||
if (!empty($ccData)) {
|
||||
if (!empty($ccData['poster'])) $result['poster'] = $ccData['poster'];
|
||||
if (!empty($ccData['title'])) $result['title'] = $ccData['title'];
|
||||
$dcData = fetchDVDBlurayCovers($titleForSearch, $result['year'], $format);
|
||||
if (!empty($dcData)) {
|
||||
if (!empty($dcData['poster'])) $result['poster'] = $dcData['poster'];
|
||||
if (!empty($dcData['title'])) $result['title'] = $dcData['title'];
|
||||
$result['format'] = $format;
|
||||
}
|
||||
} else {
|
||||
@@ -474,11 +526,12 @@ 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 : DVDBlurayCovers pour la jaquette
|
||||
if (empty($data['poster']) && !empty($data['title'])) {
|
||||
$format = $data['format'] ?: 'Blu-ray';
|
||||
$ccData = fetchCoverCentury($data['title'], $data['year'] ?? '', $format);
|
||||
if (!empty($ccData['poster'])) {
|
||||
$data['poster'] = $ccData['poster'];
|
||||
$dcData = fetchDVDBlurayCovers($data['title'], $data['year'] ?? '', $format);
|
||||
if (!empty($dcData['poster'])) {
|
||||
$data['poster'] = $dcData['poster'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,7 +569,6 @@ 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
|
||||
@@ -582,20 +634,20 @@ switch ($action) {
|
||||
$poster = $rowData['poster'] ?? '';
|
||||
$director = '';
|
||||
|
||||
$cleanTitleForCC = cleanTitle($title);
|
||||
if (!empty($cleanTitleForCC)) {
|
||||
$ccData = fetchCoverCentury($cleanTitleForCC, $year, $format);
|
||||
if (!empty($ccData)) {
|
||||
if (!empty($ccData['poster'])) {
|
||||
$poster = $ccData['poster'];
|
||||
// Récupération jaquette via DVDBlurayCovers
|
||||
$cleanTitleForDC = cleanTitle($title);
|
||||
if (!empty($cleanTitleForDC)) {
|
||||
$dcData = fetchDVDBlurayCovers($cleanTitleForDC, $year, $format);
|
||||
if (!empty($dcData)) {
|
||||
if (!empty($dcData['poster'])) {
|
||||
$poster = $dcData['poster'];
|
||||
}
|
||||
if (!empty($ccData['title']) && ($title === 'Sans titre' || empty($title))) {
|
||||
$title = $ccData['title'];
|
||||
if (!empty($dcData['title']) && ($title === 'Sans titre' || empty($title))) {
|
||||
$title = $dcData['title'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -616,7 +668,6 @@ switch ($action) {
|
||||
}
|
||||
if (empty($streaming)) $streaming = 'Support physique / Cinéma';
|
||||
|
||||
// Exécution avec la requête préparée hors de la boucle
|
||||
$stmtCritiques->execute([$id, $title, $year, $director, $poster, $rating, $review, $streaming]);
|
||||
}
|
||||
$imported++;
|
||||
@@ -634,7 +685,7 @@ switch ($action) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'debug_covercentury':
|
||||
case 'debug_dvdbluraycovers':
|
||||
$title = $_GET['title'] ?? '';
|
||||
$year = $_GET['year'] ?? '';
|
||||
$format = $_GET['format'] ?? 'Blu-ray';
|
||||
@@ -642,20 +693,7 @@ switch ($action) {
|
||||
if (!$title) { echo json_encode(['error' => 'Titre manquant']); exit; }
|
||||
|
||||
$result = ['title' => $title, 'year' => $year, 'format' => $format];
|
||||
|
||||
$cleanTitle = cleanTitle($title);
|
||||
$normalized = preg_replace('/[^a-zA-Z0-9\s]/', '', $cleanTitle);
|
||||
$normalized = trim(preg_replace('/\s+/', '_', $normalized));
|
||||
$withoutArticle = preg_replace('/^(The|Le|La|Les|Un|Une|A)\s+/i', '', $cleanTitle);
|
||||
$withoutArticle = preg_replace('/[^a-zA-Z0-9\s]/', '', $withoutArticle);
|
||||
$withoutArticle = trim(preg_replace('/\s+/', '_', $withoutArticle));
|
||||
|
||||
$result['variants'] = [
|
||||
'normalized' => $normalized,
|
||||
'without_article' => $withoutArticle,
|
||||
];
|
||||
|
||||
$data = fetchCoverCentury($title, $year, $format);
|
||||
$data = fetchDVDBlurayCovers($title, $year, $format);
|
||||
$result['data'] = $data;
|
||||
$result['status'] = $data ? 'OK' : 'AUCUN_RÉSULTAT';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user