Actualiser api.php

This commit is contained in:
2026-06-25 12:07:16 +02:00
parent 3364c5885c
commit 19b9b245f6
+80 -132
View File
@@ -115,31 +115,19 @@ function extractYear($dateStr) {
return ''; return '';
} }
// ── DVDcover.com (avec vérification du titre) ── // ── NOUVEAU SCRAPER : DVDFr (Refonte 2026) ──
function fetchDVDCover($title, $year = '', $format = 'bluray') { function fetchDVDFrCover($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'; $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);
$cleanTitleLower = strtolower($cleanTitle);
// Mapping des formats // 1. Simuler une recherche sur le nouveau site DVDFr
$formatMap = [ $searchUrl = "https://www.dvdfr.com/search/search.php?titre=" . urlencode($cleanTitle);
'4k ultra hd' => '4k-ultra-hd',
'4k' => '4k-ultra-hd',
'blu-ray' => 'blu-ray',
'bluray' => 'blu-ray',
'dvd' => 'dvd',
];
$dcFormat = $formatMap[strtolower($format)] ?? 'blu-ray';
// URL de recherche DVDcover
$searchUrl = "https://www.dvdcover.com/?s=" . urlencode($cleanTitle);
$html = httpGet($searchUrl, 8, $ua); $html = httpGet($searchUrl, 8, $ua);
if (!$html) { if (!$html) {
error_log("DVDCover: Échec recherche pour '$title'"); error_log("DVDFr Scraper: Échec de connexion pour '$title'");
return null; return null;
} }
@@ -149,109 +137,67 @@ function fetchDVDCover($title, $year = '', $format = 'bluray') {
'format' => $format, 'format' => $format,
]; ];
// Chercher tous les articles/résultats // 2. Parser le HTML de la recherche
preg_match_all('/<article[^>]*>(.*?)<\/article>/is', $html, $articles); $dom = new DOMDocument();
@$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
if (!empty($articles[1])) { // 3. Chercher le lien vers la fiche de l'édition (les liens DVDFr commencent souvent par /dvd/f)
$bestMatch = null; $links = $xpath->query('//a[contains(@href, "/dvd/f")]');
$bestScore = 0; $ficheUrl = '';
foreach ($articles[1] as $article) { if ($links->length > 0) {
// Extraire le lien vers la page foreach ($links as $link) {
if (!preg_match('/<a[^>]+href=["\']([^"\']+)["\'][^>]*>/i', $article, $linkMatch)) { $href = $link->getAttribute('href');
continue; $ficheUrl = strpos($href, 'http') === 0 ? $href : "https://www.dvdfr.com" . (strpos($href, '/') === 0 ? '' : '/') . ltrim($href, '/');
} break;
$coverPage = $linkMatch[1];
if (strpos($coverPage, 'http') !== 0) {
$coverPage = 'https://www.dvdcover.com' . $coverPage;
}
// Extraire le titre de l'article
$pageTitle = '';
if (preg_match('/<h2[^>]*class=["\'][^"\']*entry-title[^"\']*["\'][^>]*>([^<]+)<\/h2>/i', $article, $titleMatch)) {
$pageTitle = trim(strip_tags($titleMatch[1]));
} elseif (preg_match('/title=["\']([^"\']+)["\']/i', $article, $titleMatch)) {
$pageTitle = trim($titleMatch[1]);
}
if (empty($pageTitle)) continue;
// Calculer un score de correspondance
$pageTitleLower = strtolower($pageTitle);
$score = 0;
// Le titre recherché est-il dans le titre de la page ?
if (strpos($pageTitleLower, $cleanTitleLower) !== false) {
$score += 100;
}
// L'année correspond-elle ?
if (!empty($year) && strpos($pageTitle, $year) !== false) {
$score += 50;
}
// Le format correspond-il ?
if (stripos($pageTitle, $format) !== false ||
($format === 'Blu-ray' && stripos($pageTitle, 'bluray') !== false) ||
($format === '4K Ultra HD' && (stripos($pageTitle, '4k') !== false || stripos($pageTitle, 'ultra') !== false))) {
$score += 25;
}
// Si le score est suffisant, visiter la page
if ($score >= 50) {
$coverHtml = httpGet($coverPage, 8, $ua);
if ($coverHtml) {
// Chercher l'image principale
$poster = '';
// Méthode 1 : Chercher dans le contenu de l'article
if (preg_match('/<div[^>]*class=["\'][^"\']*entry-content[^"\']*["\'][^>]*>.*?<img[^>]+src=["\']([^"\']+\/wp-content\/uploads\/[^"\']+\.(?:jpg|jpeg|webp))["\'][^>]*>/is', $coverHtml, $imgMatch)) {
$poster = $imgMatch[1];
}
// Méthode 2 : Chercher toutes les images uploads
elseif (preg_match_all('/<img[^>]+src=["\']([^"\']+\/wp-content\/uploads\/[^"\']+\.(?:jpg|jpeg|webp))["\'][^>]*>/i', $coverHtml, $allImages)) {
foreach ($allImages[1] as $img) {
// Exclure 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, 'sidebar') === false) {
$poster = $img;
break;
}
}
}
if (!empty($poster)) {
// Extraire le titre de la page
$finalTitle = '';
if (preg_match('/<h1[^>]*class=["\'][^"\']*entry-title[^"\']*["\'][^>]*>([^<]+)<\/h1>/i', $coverHtml, $titleMatch)) {
$finalTitle = trim(strip_tags($titleMatch[1]));
}
// Garder le meilleur résultat
if ($score > $bestScore) {
$bestScore = $score;
$bestMatch = [
'poster' => $poster,
'title' => $finalTitle ?: $pageTitle,
'format' => $format,
];
}
}
}
}
}
if ($bestMatch) {
return $bestMatch;
} }
} }
return null; // Si la recherche nous a redirigé directement sur la fiche du film (1 seul résultat)
if (empty($ficheUrl) && stripos($html, 'jaquette') !== false) {
$ficheHtml = $html;
} elseif (!empty($ficheUrl)) {
// Sinon on charge la page de la fiche
$ficheHtml = httpGet($ficheUrl, 8, $ua);
} else {
return null;
}
// 4. Extraire la jaquette de la fiche produit
if (!empty($ficheHtml)) {
$domFiche = new DOMDocument();
@$domFiche->loadHTML($ficheHtml);
$xpathFiche = new DOMXPath($domFiche);
// Récupération du titre exact de l'édition
$titles = $xpathFiche->query('//h1');
if ($titles->length > 0) {
$result['title'] = trim(strip_tags($titles->item(0)->nodeValue));
}
// On cherche une image qui contient cover, jaquette, ou dans le répertoire des éditions
$images = $xpathFiche->query('//img');
foreach ($images as $img) {
$src = $img->getAttribute('src');
if (preg_match('/cover|jaquette|front|\/images\/dvd\//i', $src)) {
// On exclut les miniatures techniques ou de design
if (strpos($src, 'logo') === false && strpos($src, 'icon') === false && strpos($src, 'stars') === false) {
$imgUrl = strpos($src, 'http') === 0 ? $src : "https://www.dvdfr.com" . (strpos($src, '/') === 0 ? '' : '/') . ltrim($src, '/');
// Transformation de l'URL pour choper la haute résolution (retirer le "small" ou "medium")
$imgUrl = preg_replace('/_s\./i', '.', $imgUrl);
$imgUrl = preg_replace('/_m\./i', '.', $imgUrl);
$result['poster'] = $imgUrl;
break;
}
}
}
}
return (!empty($result['poster'])) ? $result : null;
} }
// ── API TMDB (uniquement pour les critiques) ── // ── API TMDB (uniquement pour les critiques) ──
@@ -388,6 +334,15 @@ switch ($action) {
ORDER BY id DESC ORDER BY id DESC
"; ";
$result = $pdo->query($sql)->fetchAll(); $result = $pdo->query($sql)->fetchAll();
// Formatage des notes : suppression du .0 pour les comptes ronds
foreach ($result as &$row) {
if ($row['rating'] !== null) {
$ratingVal = (float)$row['rating'];
$row['rating'] = ($ratingVal == floor($ratingVal)) ? (int)$ratingVal : $ratingVal;
}
}
echo json_encode($result); echo json_encode($result);
break; break;
@@ -401,12 +356,10 @@ switch ($action) {
'length' => '', 'number_of_discs' => 1, 'aspect_ratio' => '', 'actors' => '' 'length' => '', 'number_of_discs' => 1, 'aspect_ratio' => '', 'actors' => ''
]; ];
// Pour DVDcover, on a besoin d'un titre
$tmdbKey = getTmdbApiKey($pdo); $tmdbKey = getTmdbApiKey($pdo);
$titleForSearch = ''; $titleForSearch = '';
if ($tmdbKey) { if ($tmdbKey) {
// Essayer de trouver via TMDB d'abord
$searchUrl = "https://api.themoviedb.org/3/find/{$ean}?api_key={$tmdbKey}&external_source=imdb_id"; $searchUrl = "https://api.themoviedb.org/3/find/{$ean}?api_key={$tmdbKey}&external_source=imdb_id";
$searchRes = httpGet($searchUrl, 5); $searchRes = httpGet($searchUrl, 5);
$searchData = $searchRes ? json_decode($searchRes, true) : []; $searchData = $searchRes ? json_decode($searchRes, true) : [];
@@ -425,18 +378,16 @@ switch ($action) {
exit; exit;
} }
// Récupération jaquette
if ($type === 'videotheque') { if ($type === 'videotheque') {
// DVDcover pour vidéothèque // DVDFr pour vidéothèque
$format = $result['format'] ?: 'Blu-ray'; $format = $result['format'] ?: 'Blu-ray';
$dcData = fetchDVDCover($titleForSearch, $result['year'], $format); $dcData = fetchDVDFrCover($titleForSearch, $result['year'], $format);
if (!empty($dcData)) { if (!empty($dcData)) {
if (!empty($dcData['poster'])) $result['poster'] = $dcData['poster']; if (!empty($dcData['poster'])) $result['poster'] = $dcData['poster'];
if (!empty($dcData['title'])) $result['title'] = $dcData['title']; if (!empty($dcData['title'])) $result['title'] = $dcData['title'];
$result['format'] = $format; $result['format'] = $format;
} }
} else { } else {
// TMDB pour critiques
if ($tmdbKey) { if ($tmdbKey) {
$tmdbData = fetchTMDBFull($titleForSearch, $result['year'], $tmdbKey, $pdo); $tmdbData = fetchTMDBFull($titleForSearch, $result['year'], $tmdbKey, $pdo);
if ($tmdbData) { if ($tmdbData) {
@@ -458,7 +409,6 @@ switch ($action) {
$type = $data['type'] ?? 'critique'; $type = $data['type'] ?? 'critique';
$id = !empty($data['id']) ? $data['id'] : makeStableId($type, $data['title'] ?? '', $data['year'] ?? '0000'); $id = !empty($data['id']) ? $data['id'] : makeStableId($type, $data['title'] ?? '', $data['year'] ?? '0000');
// TMDB uniquement pour les critiques
if ($type === 'critique' && (empty($data['director']) || empty($data['poster']))) { if ($type === 'critique' && (empty($data['director']) || empty($data['poster']))) {
$tmdbData = fetchTMDBFull($data['title'] ?? '', $data['year'] ?? '', getTmdbApiKey($pdo), $pdo); $tmdbData = fetchTMDBFull($data['title'] ?? '', $data['year'] ?? '', getTmdbApiKey($pdo), $pdo);
if ($tmdbData) { if ($tmdbData) {
@@ -475,10 +425,10 @@ 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 : DVDFr 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); $dcData = fetchDVDFrCover($data['title'], $data['year'] ?? '', $format);
if (!empty($dcData['poster'])) { if (!empty($dcData['poster'])) {
$data['poster'] = $dcData['poster']; $data['poster'] = $dcData['poster'];
} }
@@ -525,7 +475,7 @@ switch ($action) {
$id = makeStableId($type, $title, $year); $id = makeStableId($type, $title, $year);
if ($type === 'videotheque') { if ($type === 'videotheque') {
// ── VIDÉOTHÈQUE : DVDcover pour jaquette HD ── // ── VIDÉOTHÈQUE : DVDFr pour jaquette ──
$csvActors = $rowData['ensemble'] ?? $rowData['creators'] ?? ''; $csvActors = $rowData['ensemble'] ?? $rowData['creators'] ?? '';
$actors = ''; $actors = '';
if (!empty($csvActors)) { if (!empty($csvActors)) {
@@ -557,10 +507,10 @@ switch ($action) {
$poster = $rowData['poster'] ?? ''; $poster = $rowData['poster'] ?? '';
$director = ''; $director = '';
// Récupération jaquette via DVDcover // Récupération jaquette via DVDFr
$cleanTitleForDC = cleanTitle($title); $cleanTitleForDC = cleanTitle($title);
if (!empty($cleanTitleForDC)) { if (!empty($cleanTitleForDC)) {
$dcData = fetchDVDCover($cleanTitleForDC, $year, $format); $dcData = fetchDVDFrCover($cleanTitleForDC, $year, $format);
if (!empty($dcData)) { if (!empty($dcData)) {
if (!empty($dcData['poster'])) { if (!empty($dcData['poster'])) {
$poster = $dcData['poster']; $poster = $dcData['poster'];
@@ -571,8 +521,6 @@ switch ($action) {
} }
} }
// PAS DE TMDB POUR VIDÉOTHÈQUE
$sql = "INSERT INTO videotheque (id, title, year, director, poster, format, length, publisher, ean_isbn13, number_of_discs, aspect_ratio, description, actors) $sql = "INSERT INTO videotheque (id, title, year, director, poster, format, length, publisher, ean_isbn13, number_of_discs, aspect_ratio, description, actors)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
@@ -591,7 +539,7 @@ switch ($action) {
$stmt->execute([$id, $title, $year, $director, $poster, $format, $length, $publisher, $ean, $discs, $aspect, $description, $actors]); $stmt->execute([$id, $title, $year, $director, $poster, $format, $length, $publisher, $ean, $discs, $aspect, $description, $actors]);
} else { } else {
// ── CRITIQUES : TMDB complet ── // ── CRITIQUES : TMDB ──
$ratingRaw = $rowData['Rating'] ?? $rowData['rating'] ?? ''; $ratingRaw = $rowData['Rating'] ?? $rowData['rating'] ?? '';
$rating = ($ratingRaw !== '' && $ratingRaw !== null) ? (float)$ratingRaw : null; $rating = ($ratingRaw !== '' && $ratingRaw !== null) ? (float)$ratingRaw : null;
$review = $rowData['Review'] ?? $rowData['review'] ?? ''; $review = $rowData['Review'] ?? $rowData['review'] ?? '';
@@ -636,7 +584,7 @@ switch ($action) {
} }
break; break;
case 'debug_dvdcover': case 'debug_scraper':
$title = $_GET['title'] ?? ''; $title = $_GET['title'] ?? '';
$year = $_GET['year'] ?? ''; $year = $_GET['year'] ?? '';
$format = $_GET['format'] ?? 'Blu-ray'; $format = $_GET['format'] ?? 'Blu-ray';
@@ -644,7 +592,7 @@ switch ($action) {
if (!$title) { echo json_encode(['error' => 'Titre manquant']); exit; } if (!$title) { echo json_encode(['error' => 'Titre manquant']); exit; }
$result = ['title' => $title, 'year' => $year, 'format' => $format]; $result = ['title' => $title, 'year' => $year, 'format' => $format];
$data = fetchDVDCover($title, $year, $format); $data = fetchDVDFrCover($title, $year, $format);
$result['data'] = $data; $result['data'] = $data;
$result['status'] = $data ? 'OK' : 'AUCUN_RÉSULTAT'; $result['status'] = $data ? 'OK' : 'AUCUN_RÉSULTAT';