Actualiser api.php

This commit is contained in:
2026-06-25 14:14:43 +02:00
parent af9201941b
commit 04ce52f2d7
+150 -235
View File
@@ -93,6 +93,25 @@ function httpGet($url, $timeout = 3, $ua = null) {
return $res ?: 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);
}
function cleanTitle($title) { function cleanTitle($title) {
$clean = preg_replace('/\s*[\[\(].*?[\]\)]\s*/', '', $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); $clean = preg_replace('/\s*-\s*(Édition|Edition|Collector|Simple|Spéciale|Digibook|Ultimate|Intégrale|Combo|SteelBook|Boîtier).*$/i', '', $clean);
@@ -115,98 +134,101 @@ function extractYear($dateStr) {
return ''; return '';
} }
function fetchDVDCover($title, $year = '', $format = 'bluray') { // ── 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
function fetchCoverCentury($title, $year = '', $format = 'bluray') {
if (empty($title)) return null; 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); $cleanTitle = cleanTitle($title);
$searchUrl = "https://www.dvdcover.com/?s=" . urlencode($cleanTitle); if (empty($cleanTitle)) return null;
$html = httpGet($searchUrl, 10, $ua); // Mapping des formats pour CoverCentury
if (!$html) { $formatMap = [
error_log("DVDCover: Erreur réseau pour '$title'"); '4k ultra hd' => '4kultrahd',
return null; '4k' => '4kultrahd',
} 'blu-ray' => 'bluray',
'bluray' => 'bluray',
$dom = new DOMDocument(); 'dvd' => 'dvd',
@$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
// Les résultats sont dans des <h2><a>
$links = $xpath->query('//h2//a');
if ($links->length === 0) {
error_log("DVDCover: Aucun lien H2 trouvé pour '$title'");
return null;
}
// On cherche le meilleur lien : correspondance titre + année si possible
$bestUrl = null;
$cleanTitleLower = strtolower($cleanTitle);
foreach ($links as $a) {
$text = strtolower(trim($a->textContent));
$href = $a->getAttribute('href');
// Priorité : correspond au titre ET à l'année
if (!empty($year) && strpos($text, strtolower($cleanTitleLower)) !== false && strpos($text, $year) !== false) {
$bestUrl = $href;
break;
}
// Fallback : correspond au titre seul (premier trouvé)
if ($bestUrl === null && strpos($text, $cleanTitleLower) !== false) {
$bestUrl = $href;
}
}
// Dernier fallback : premier lien de la liste
if (!$bestUrl) {
$bestUrl = $links->item(0)->getAttribute('href');
}
if (!$bestUrl) return null;
// On scrape la page de la fiche
$coverHtml = httpGet($bestUrl, 10, $ua);
if (!$coverHtml) return null;
$domFiche = new DOMDocument();
@$domFiche->loadHTML($coverHtml);
$xpathFiche = new DOMXPath($domFiche);
// On cherche la plus grande image dans wp-content/uploads (pas les thumbnails -300x)
$imgs = $xpathFiche->query('//img[contains(@src,"wp-content/uploads")]');
$bestSrc = null;
$bestSize = 0;
foreach ($imgs as $img) {
$src = $img->getAttribute('src');
// Ignorer logos, icônes et thumbnails redimensionnés
if (strpos($src, 'logo') !== false) continue;
if (strpos($src, 'icon') !== false) continue;
// Préférer les images sans suffixe de redimensionnement (ex: -300x201)
$isThumb = preg_match('/-\d+x\d+\./', $src);
// Tenter de récupérer la version pleine taille (supprimer le suffixe -WxH)
$fullSrc = preg_replace('/-\d+x\d+(\.\w+)$/', '$1', $src);
// Scorer : pleine taille > thumbnail
$score = $isThumb ? 1 : 10;
if ($score > $bestSize) {
$bestSize = $score;
$bestSrc = $fullSrc;
}
}
if (!$bestSrc) return null;
return [
'poster' => $bestSrc,
'title' => $cleanTitle,
'format' => $format
]; ];
$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);
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);
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;
}
}
// Construire toutes les URLs candidates
$candidates = [];
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.)
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,
];
}
}
// Si aucun format ne marche, essayer les autres formats
$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));
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,
];
}
}
}
}
error_log("CoverCentury: Aucune jaquette trouvée pour '$title'");
return null;
} }
// ── API TMDB (uniquement pour les critiques) ── // ── API TMDB (uniquement pour les critiques) ──
@@ -387,12 +409,12 @@ switch ($action) {
} }
if ($type === 'videotheque') { if ($type === 'videotheque') {
// DVDCover pour vidéothèque // CoverCentury pour vidéothèque
$format = $result['format'] ?: 'Blu-ray'; $format = $result['format'] ?: 'Blu-ray';
$dcData = fetchDVDCover($titleForSearch, $result['year'], $format); $ccData = fetchCoverCentury($titleForSearch, $result['year'], $format);
if (!empty($dcData)) { if (!empty($ccData)) {
if (!empty($dcData['poster'])) $result['poster'] = $dcData['poster']; if (!empty($ccData['poster'])) $result['poster'] = $ccData['poster'];
if (!empty($dcData['title'])) $result['title'] = $dcData['title']; if (!empty($ccData['title'])) $result['title'] = $ccData['title'];
$result['format'] = $format; $result['format'] = $format;
} }
} else { } else {
@@ -433,12 +455,12 @@ 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 : DVDCover pour la jaquette // 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';
$dcData = fetchDVDCover($data['title'], $data['year'] ?? '', $format); $ccData = fetchCoverCentury($data['title'], $data['year'] ?? '', $format);
if (!empty($dcData['poster'])) { if (!empty($ccData['poster'])) {
$data['poster'] = $dcData['poster']; $data['poster'] = $ccData['poster'];
} }
} }
@@ -514,16 +536,16 @@ switch ($action) {
$poster = $rowData['poster'] ?? ''; $poster = $rowData['poster'] ?? '';
$director = ''; $director = '';
// Récupération jaquette via DVDCover // Récupération jaquette via CoverCentury
$cleanTitleForDC = cleanTitle($title); $cleanTitleForCC = cleanTitle($title);
if (!empty($cleanTitleForDC)) { if (!empty($cleanTitleForCC)) {
$dcData = fetchDVDCover($cleanTitleForDC, $year, $format); $ccData = fetchCoverCentury($cleanTitleForCC, $year, $format);
if (!empty($dcData)) { if (!empty($ccData)) {
if (!empty($dcData['poster'])) { if (!empty($ccData['poster'])) {
$poster = $dcData['poster']; $poster = $ccData['poster'];
} }
if (!empty($dcData['title']) && ($title === 'Sans titre' || empty($title))) { if (!empty($ccData['title']) && ($title === 'Sans titre' || empty($title))) {
$title = $dcData['title']; $title = $ccData['title'];
} }
} }
} }
@@ -590,141 +612,34 @@ switch ($action) {
} }
break; break;
case 'debug_dvdcover': case 'debug_covercentury':
$title = $_GET['title'] ?? ''; $title = $_GET['title'] ?? '';
$year = $_GET['year'] ?? ''; $year = $_GET['year'] ?? '';
$format = $_GET['format'] ?? 'Blu-ray'; $format = $_GET['format'] ?? 'Blu-ray';
if (!$title) { echo json_encode(['error' => 'Titre manquant']); exit; } if (!$title) { echo json_encode(['error' => 'Titre manquant']); exit; }
$ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
$searchUrl = "https://www.dvdcover.com/?s=" . urlencode(cleanTitle($title));
$html = httpGet($searchUrl, 10, $ua); $result = ['title' => $title, 'year' => $year, 'format' => $format];
$diag = [ // Afficher les variantes testées
'url' => $searchUrl, $cleanTitle = cleanTitle($title);
'html_length' => $html ? strlen($html) : 0, $normalized = preg_replace('/[^a-zA-Z0-9\s]/', '', $cleanTitle);
'html_snippet' => $html ? substr(strip_tags($html), 0, 500) : 'VIDE/ERREUR', $normalized = trim(preg_replace('/\s+/', '_', $normalized));
'cloudflare' => ($html && strpos($html, 'cloudflare') !== false) ? true : false, $withoutArticle = preg_replace('/^(The|Le|La|Les|Un|Une|A)\s+/i', '', $cleanTitle);
'captcha' => ($html && strpos($html, 'captcha') !== false) ? true : false, $withoutArticle = preg_replace('/[^a-zA-Z0-9\s]/', '', $withoutArticle);
$withoutArticle = trim(preg_replace('/\s+/', '_', $withoutArticle));
$result['variants'] = [
'normalized' => $normalized,
'without_article' => $withoutArticle,
]; ];
// Cherche les articles si HTML reçu $data = fetchCoverCentury($title, $year, $format);
if ($html) { $result['data'] = $data;
$dom = new DOMDocument(); $result['status'] = $data ? 'OK' : 'AUCUN_RÉSULTAT';
@$dom->loadHTML($html);
$xpath = new DOMXPath($dom); echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
$links = $xpath->query('//article[contains(@class,"post")]//a/@href');
$diag['articles_found'] = $links->length;
// Essai avec d'autres sélecteurs courants
$diag['alt1_h2_links'] = $xpath->query('//h2//a/@href')->length;
$h2Links = $xpath->query('//h2//a');
$diag['h2_links_list'] = [];
foreach ($h2Links as $a) {
$diag['h2_links_list'][] = [
'text' => trim($a->textContent),
'href' => $a->getAttribute('href')
];
}
// Et les imgs disponibles dans la page
$allImgs = $xpath->query('//img/@src');
$diag['img_samples'] = [];
$i = 0;
foreach ($allImgs as $img) {
if ($i++ >= 15) break;
$diag['img_samples'][] = $img->nodeValue;
}
$diag['alt2_figure_imgs'] = $xpath->query('//figure//img/@src')->length;
$diag['alt3_any_article'] = $xpath->query('//article')->length;
}
echo json_encode($diag, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit; exit;
break; break;
case 'test_dvdfr':
$title = urlencode($_GET['title'] ?? 'Matrix');
$url = "https://www.dvdfr.com/recherche/?q={$title}";
$html = httpGet($url, 5);
if (!$html) { echo json_encode(['error' => 'ERREUR réseau']); break; }
$dom = new DOMDocument();
@$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
// Cherche tous les liens qui ressemblent à des fiches produit
$allLinks = $xpath->query('//a[contains(@href,"/dvd/") or contains(@href,"/film/") or contains(@href,"/bluray/")]');
$links = [];
foreach ($allLinks as $a) {
$href = $a->getAttribute('href');
$text = trim($a->textContent);
if (!in_array($href, array_column($links, 'href')) && !empty($text)) {
$links[] = ['text' => substr($text, 0, 80), 'href' => $href];
}
if (count($links) >= 10) break;
}
// Cherche les images produit
$imgs = $xpath->query('//img[contains(@src,"cover") or contains(@src,"dvd") or contains(@src,"blu") or contains(@src,"jaquette")]');
$imgList = [];
foreach ($imgs as $img) {
$imgList[] = $img->getAttribute('src');
if (count($imgList) >= 10) break;
}
// Fallback : toutes les images wp-content ou cdn
if (empty($imgList)) {
$imgs2 = $xpath->query('//img/@src');
foreach ($imgs2 as $img) {
$imgList[] = $img->nodeValue;
if (count($imgList) >= 15) break;
}
}
echo json_encode([
'url' => $url,
'links' => $links,
'imgs' => $imgList
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
break;
case 'test_covercentury':
$title = $_GET['title'] ?? 'Matrix';
$urls = [
"https://www.covercentury.com/index.php?p=dvdcovers&recherche=" . urlencode($title),
"https://www.covercentury.com/index.php?p=blu-raycovers&recherche=" . urlencode($title),
"https://www.covercentury.com/index.php?p=dvdcovers&q=" . urlencode($title),
"https://www.covercentury.com/index.php?p=dvdcovers&s=" . urlencode($title),
];
$results = [];
foreach ($urls as $url) {
$html = httpGet($url, 8);
if (!$html) { $results[] = ['url' => $url, 'length' => 0, 'snippet' => 'ERREUR']; continue; }
$dom = new DOMDocument();
@$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
$allImgs = $xpath->query('//img');
$imgs = [];
foreach ($allImgs as $img) {
$src = $img->getAttribute('src');
if (strpos($src, 'logo') === false && strpos($src, 'assets') === false) {
$imgs[] = ['src' => $src, 'alt' => $img->getAttribute('alt')];
}
if (count($imgs) >= 10) break;
}
$results[] = [
'url' => $url,
'length' => strlen($html),
'imgs' => $imgs
];
}
echo json_encode($results, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
break;
} }
?> ?>