Actualiser api.php

This commit is contained in:
2026-06-25 15:24:37 +02:00
parent fd91b968fe
commit 0efc35bcf7
+171 -166
View File
@@ -69,7 +69,7 @@ function httpGet($url, $timeout = 3, $ua = null) {
$ctx = stream_context_create(['http' => [
'timeout' => $timeout,
'user_agent' => $ua,
'header' => "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: fr-FR,fr;q=0.9\r\n"
'header' => "Accept: application/json\r\nAccept-Language: fr-FR,fr;q=0.9\r\n"
]]);
return @file_get_contents($url, false, $ctx);
}
@@ -82,10 +82,8 @@ function httpGet($url, $timeout = 3, $ua = null) {
CURLOPT_USERAGENT => $ua,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTPHEADER => [
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept: application/json',
'Accept-Language: fr-FR,fr;q=0.9,en;q=0.8',
'Accept-Encoding: identity',
'Connection: keep-alive',
],
]);
$res = curl_exec($ch);
@@ -115,190 +113,152 @@ function extractYear($dateStr) {
return '';
}
// ── DVDBlurayCovers.com Scraper ──
function fetchDVDBlurayCovers($title, $year = '', $format = 'bluray') {
// ── DVDCover.com via API REST WordPress ──
function fetchDVDCover($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;
// URL de recherche DVDBlurayCovers
$searchUrl = "https://www.dvdbluraycovers.com/?s=" . urlencode($cleanTitle);
// API REST WordPress de DVDcover
// Endpoint : /wp-json/wp/v2/posts?search=TITRE&_embed
$searchUrl = "https://www.dvdcover.com/wp-json/wp/v2/posts?search=" . urlencode($cleanTitle) . "&_embed&per_page=10";
$html = httpGet($searchUrl, 10, $ua);
if (!$html) {
error_log("DVDBlurayCovers: Échec recherche pour '$title'");
$json = httpGet($searchUrl, 10);
if (!$json) {
error_log("DVDCover API: Échec requête pour '$title'");
return null;
}
$result = [
'poster' => '',
'title' => '',
'format' => $format,
];
// Parser le HTML avec DOMDocument
$dom = new DOMDocument();
@$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
// 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');
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/")]');
$posts = json_decode($json, true);
if (!is_array($posts) || empty($posts)) {
error_log("DVDCover API: Aucun résultat pour '$title'");
return null;
}
$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
}
$cleanTitleLower = strtolower($cleanTitle);
$bestPost = null;
$bestScore = 0;
// Parcourir les pages de covers trouvées
foreach ($coverPages as $coverPage) {
$coverHtml = httpGet($coverPage, 10, $ua);
if (!$coverHtml) continue;
$domCover = new DOMDocument();
@$domCover->loadHTML($coverHtml);
$xpathCover = new DOMXPath($domCover);
// 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);
// Parcourir les posts et scorer leur pertinence
foreach ($posts as $post) {
$postTitle = $post['title']['rendered'] ?? '';
$postTitleLower = strtolower($postTitle);
$score = 0;
if (strpos($coverTitleLower, $searchTitleLower) !== false) {
// Le titre du post contient-il le film recherché ?
if (strpos($postTitleLower, $cleanTitleLower) !== false) {
$score += 50;
}
if (!empty($year) && strpos($coverTitle, $year) !== false) {
// Correspondance de l'année
if (!empty($year) && strpos($postTitle, $year) !== false) {
$score += 30;
}
if (stripos($coverTitle, $format) !== false) {
// Correspondance du format
$formatLower = strtolower($format);
if (stripos($postTitle, $format) !== false ||
stripos($postTitle, str_replace('-', ' ', $format)) !== false) {
$score += 20;
}
// Si le score est trop bas, on ignore ce résultat
if ($score < 30) {
continue;
// Bonus si le titre contient "cover" ou "jaquette"
if (stripos($postTitle, 'cover') !== false) {
$score += 10;
}
// Chercher l'image principale de la jaquette
$poster = '';
if ($score > $bestScore) {
$bestScore = $score;
$bestPost = $post;
}
// 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');
}
// Score parfait, on arrête
if ($score >= 100) break;
}
// 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;
// Si aucun post ne correspond assez bien, prendre le premier
if (!$bestPost && !empty($posts)) {
$bestPost = $posts[0];
}
if (!$bestPost) return null;
// Extraire l'image du post
$poster = '';
// Méthode 1 : Image mise en avant (_embedded.wp:featuredmedia)
if (isset($bestPost['_embedded']['wp:featuredmedia'][0])) {
$featured = $bestPost['_embedded']['wp:featuredmedia'][0];
// Chercher la plus grande taille disponible
if (!empty($featured['media_details']['sizes'])) {
$sizes = $featured['media_details']['sizes'];
// Ordre de préférence : full > large > medium > thumbnail
$preferredSizes = ['full', 'large', 'medium', 'thumbnail'];
foreach ($preferredSizes as $size) {
if (isset($sizes[$size]['source_url'])) {
$poster = $sizes[$size]['source_url'];
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;
// Fallback : source_url direct
if (empty($poster) && !empty($featured['source_url'])) {
$poster = $featured['source_url'];
}
}
return (!empty($result['poster'])) ? $result : null;
// Méthode 2 : Images dans le contenu du post
if (empty($poster) && !empty($bestPost['content']['rendered'])) {
$content = $bestPost['content']['rendered'];
// Chercher les balises img
if (preg_match_all('/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', $content, $matches)) {
foreach ($matches[1] as $img) {
// Filtrer les images non pertinentes
if (strpos($img, 'logo') === false &&
strpos($img, 'icon') === false &&
strpos($img, 'banner') === false &&
strpos($img, 'bg') === false &&
strpos($img, 'button') === false &&
strpos($img, 'social') === false &&
(strpos($img, '.jpg') !== false || strpos($img, '.jpeg') !== false || strpos($img, '.png') !== false || strpos($img, '.webp') !== false)) {
$poster = $img;
break;
}
}
}
}
// Méthode 3 : Images embarquées dans _embedded.wp:embedded
if (empty($poster) && isset($bestPost['_embedded']['wp:embedded'])) {
foreach ($bestPost['_embedded']['wp:embedded'] as $embedded) {
if (!empty($embedded['source_url'])) {
$poster = $embedded['source_url'];
break;
}
}
}
if (empty($poster)) {
error_log("DVDCover API: Pas d'image trouvée pour le post ID " . ($bestPost['id'] ?? '?'));
return null;
}
// S'assurer que l'URL est absolue
if (strpos($poster, 'http') !== 0) {
$poster = 'https://www.dvdcover.com' . (strpos($poster, '/') === 0 ? '' : '/') . ltrim($poster, '/');
}
// Supprimer les suffixes de taille WordPress pour avoir la version originale
// Ex: image-300x200.jpg → image.jpg
$poster = preg_replace('/-\d+x\d+(\.\w+)$/', '$1', $poster);
return [
'poster' => $poster,
'title' => $bestPost['title']['rendered'] ?? $cleanTitle,
'format' => $format,
];
}
// ── API TMDB (uniquement pour les critiques) ──
@@ -480,9 +440,9 @@ switch ($action) {
}
if ($type === 'videotheque') {
// DVDBlurayCovers pour vidéothèque
// DVDCover API pour vidéothèque
$format = $result['format'] ?: 'Blu-ray';
$dcData = fetchDVDBlurayCovers($titleForSearch, $result['year'], $format);
$dcData = fetchDVDCover($titleForSearch, $result['year'], $format);
if (!empty($dcData)) {
if (!empty($dcData['poster'])) $result['poster'] = $dcData['poster'];
if (!empty($dcData['title'])) $result['title'] = $dcData['title'];
@@ -526,10 +486,10 @@ 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
// Vidéothèque : DVDCover API pour la jaquette
if (empty($data['poster']) && !empty($data['title'])) {
$format = $data['format'] ?: 'Blu-ray';
$dcData = fetchDVDBlurayCovers($data['title'], $data['year'] ?? '', $format);
$dcData = fetchDVDCover($data['title'], $data['year'] ?? '', $format);
if (!empty($dcData['poster'])) {
$data['poster'] = $dcData['poster'];
}
@@ -634,10 +594,10 @@ switch ($action) {
$poster = $rowData['poster'] ?? '';
$director = '';
// Récupération jaquette via DVDBlurayCovers
// Récupération jaquette via DVDCover API
$cleanTitleForDC = cleanTitle($title);
if (!empty($cleanTitleForDC)) {
$dcData = fetchDVDBlurayCovers($cleanTitleForDC, $year, $format);
$dcData = fetchDVDCover($cleanTitleForDC, $year, $format);
if (!empty($dcData)) {
if (!empty($dcData['poster'])) {
$poster = $dcData['poster'];
@@ -685,7 +645,7 @@ switch ($action) {
}
break;
case 'debug_dvdbluraycovers':
case 'debug_dvdcover':
$title = $_GET['title'] ?? '';
$year = $_GET['year'] ?? '';
$format = $_GET['format'] ?? 'Blu-ray';
@@ -693,8 +653,53 @@ switch ($action) {
if (!$title) { echo json_encode(['error' => 'Titre manquant']); exit; }
$result = ['title' => $title, 'year' => $year, 'format' => $format];
$data = fetchDVDBlurayCovers($title, $year, $format);
$result['data'] = $data;
// Test direct de l'API
$cleanTitle = cleanTitle($title);
$apiUrl = "https://www.dvdcover.com/wp-json/wp/v2/posts?search=" . urlencode($cleanTitle) . "&_embed&per_page=5";
$result['api_url'] = $apiUrl;
$json = httpGet($apiUrl, 10);
if ($json) {
$posts = json_decode($json, true);
$result['posts_count'] = is_array($posts) ? count($posts) : 0;
$result['posts'] = [];
if (is_array($posts)) {
foreach ($posts as $post) {
$postData = [
'id' => $post['id'] ?? null,
'title' => $post['title']['rendered'] ?? '',
'link' => $post['link'] ?? '',
];
// Extraire les images
$imgs = [];
if (isset($post['_embedded']['wp:featuredmedia'][0])) {
$featured = $post['_embedded']['wp:featuredmedia'][0];
if (!empty($featured['source_url'])) {
$imgs[] = $featured['source_url'];
}
if (!empty($featured['media_details']['sizes'])) {
foreach ($featured['media_details']['sizes'] as $size => $data) {
if (!empty($data['source_url'])) {
$imgs[] = $data['source_url'] . " ($size)";
}
}
}
}
$postData['images'] = $imgs;
$result['posts'][] = $postData;
}
}
} else {
$result['api_error'] = 'Impossible de joindre l\'API';
}
// Test de la fonction complète
$data = fetchDVDCover($title, $year, $format);
$result['fetch_result'] = $data;
$result['status'] = $data ? 'OK' : 'AUCUN_RÉSULTAT';
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);