diff --git a/api.php b/api.php index d64751b..9a78c85 100644 --- a/api.php +++ b/api.php @@ -154,6 +154,232 @@ function fetchPosterTMDB($title, $year = '', $pdo = null) { return ['poster' => $defaultPoster, 'title' => $cleanTitle, 'format' => 'Blu-ray']; } +// ── FONCTION POUR RÉCUPÉRER LES INFOS PHYSIQUES DEPUIS BLU-RAY.COM ── +function fetchBluRayPhysicalInfo($ean, $title = '', $pdo = null) { + $defaultPoster = 'assets/img/default_physical_media.jpg'; + + $result = [ + 'poster' => $defaultPoster, + 'format' => '', + 'publisher' => '', + 'length' => '', + 'number_of_discs'=> '', + 'aspect_ratio' => '', + 'description' => '', + 'year' => '', + 'director' => '', + 'actors' => '' + ]; + + // Nettoyer l'EAN/UPC + $cleanEan = preg_replace('/[^0-9]/', '', $ean); + if (empty($cleanEan) && empty($title)) return $result; + + $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; + $productUrl = null; + + // ── ÉTAPE 1 : Recherche par EAN/UPC ── + if (!empty($cleanEan)) { + $searchUrl = "https://www.blu-ray.com/search/?quicksearch=1&searchtype=products&q=" . urlencode($cleanEan); + $searchRes = httpGet($searchUrl, 10, $userAgent); + + if ($searchRes && preg_match('/]*>/i', $searchRes, $matches)) { + $productUrl = "https://www.blu-ray.com/products/?id=" . $matches[1]; + } + } + + // ── ÉTAPE 2 : Fallback par titre ── + if (!$productUrl && !empty($title)) { + $cleanTitle = cleanTitle($title); + $searchUrl = "https://www.blu-ray.com/search/?quicksearch=1&searchtype=movies&q=" . urlencode($cleanTitle); + $searchRes = httpGet($searchUrl, 10, $userAgent); + + if ($searchRes && preg_match('/]*>/i', $searchRes, $matches)) { + $productUrl = "https://www.blu-ray.com/movies/?id=" . $matches[1]; + } + } + + if (!$productUrl) { + error_log("BluRay.com: Film non trouvé pour EAN '{$ean}' / titre '{$title}'"); + return $result; + } + + error_log("BluRay.com: Accès à {$productUrl}"); + $productRes = httpGet($productUrl, 10, $userAgent); + + if (!$productRes) { + error_log("BluRay.com: Page inaccessible {$productUrl}"); + return $result; + } + + // ── ÉTAPE 3 : Extraire l'affiche ── + if (preg_match('/]*id="home_release_img"[^>]*src="([^"]+)"/i', $productRes, $imgMatches)) { + $posterUrl = $imgMatches[1]; + $posterUrl = str_replace('/resized/', '/', $posterUrl); + $posterUrl = str_replace('http://', 'https://', $posterUrl); + $result['poster'] = $posterUrl; + } + + // ── ÉTAPE 4 : Extraire les informations techniques ── + + // Format (Blu-ray, 4K Ultra HD, DVD) + if (preg_match('/]*class="[^"]*productdetails[^"]*"[^>]*>.*?<\/div>/is', $productRes, $detailsBlock)) { + $detailsHtml = $detailsBlock[0]; + } else { + $detailsHtml = $productRes; + } + + // Détection du format + if (preg_match('/Format<\/b>[:\s]*<\/td>\s*]*>([^<]+)/i', $productRes, $m)) { + $formatRaw = trim($m[1]); + if (stripos($formatRaw, '4K') !== false || stripos($formatRaw, 'UHD') !== false) { + $result['format'] = '4K Ultra HD'; + } elseif (stripos($formatRaw, 'DVD') !== false) { + $result['format'] = 'DVD'; + } elseif (stripos($formatRaw, 'Blu') !== false) { + $result['format'] = 'Blu-ray'; + } + } + + // Nombre de disques + if (preg_match('/(?:Discs?|Disques?)<\/b>[:\s]*<\/td>\s*]*>(\d+)/i', $productRes, $m)) { + $result['number_of_discs'] = (int)$m[1]; + } + + // Aspect ratio + if (preg_match('/Aspect\s*ratio<\/b>[:\s]*<\/td>\s*]*>([^<]+)/i', $productRes, $m)) { + $result['aspect_ratio'] = trim($m[1]); + } + + // Durée (runtime) + if (preg_match('/(?:Runtime|Durée|Length)<\/b>[:\s]*<\/td>\s*]*>(\d+)/i', $productRes, $m)) { + $result['length'] = trim($m[1]) . ' min'; + } + + // Studio / Éditeur + if (preg_match('/(?:Studio|Label|Distributor)<\/b>[:\s]*<\/td>\s*]*>([^<]+)/i', $productRes, $m)) { + $result['publisher'] = trim($m[1]); + } + + // Année de sortie + if (preg_match('/(?:Release\s*Date|Country\s*&\s*Date)<\/b>[:\s]*<\/td>\s*]*>[^<]*?(\d{4})/i', $productRes, $m)) { + $result['year'] = $m[1]; + } + + // Synopsis + if (preg_match('/]*class="[^"]*synopsis[^"]*"[^>]*>(.*?)<\/div>/is', $productRes, $m)) { + $result['description'] = trim(strip_tags($m[1])); + } + + error_log("BluRay.com: Infos récupérées pour '{$title}' → " . json_encode($result)); + return $result; +} + +// ── FONCTION POUR RÉCUPÉRER LE SYNOPSIS DEPUIS TMDB ── +function fetchTmdbSynopsis($title, $year = '', $pdo = null) { + $tmdbKey = getTmdbApiKey($pdo); + if (!$tmdbKey || empty($title)) return ''; + + $cleanTitle = cleanTitle($title); + $searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$tmdbKey}&query=" . urlencode($cleanTitle); + if (!empty($year)) $searchUrl .= "&year={$year}"; + $searchUrl .= "&language=fr-FR"; + + $searchRes = httpGet($searchUrl, 5); + $searchData = $searchRes ? json_decode($searchRes, true) : []; + + if (empty($searchData['results'])) { + // Retry sans l'année + $searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$tmdbKey}&query=" . urlencode($cleanTitle) . "&language=fr-FR"; + $searchRes = httpGet($searchUrl, 5); + $searchData = $searchRes ? json_decode($searchRes, true) : []; + } + + if (!empty($searchData['results'][0]['overview'])) { + return $searchData['results'][0]['overview']; + } + + return ''; +} + +// ── FONCTION COMPLÈTE TMDB POUR POSTER + SYNOPSIS ── +function fetchTmdbPosterAndSynopsis($title, $year = '', $pdo = null) { + $defaultPoster = 'assets/img/default_physical_media.jpg'; + $tmdbKey = getTmdbApiKey($pdo); + + $result = [ + 'poster' => $defaultPoster, + 'description' => '', + 'director' => '', + 'actors' => '', + 'length' => '', + 'year' => $year + ]; + + if (!$tmdbKey || empty($title)) return $result; + + $cleanTitle = cleanTitle($title); + $searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$tmdbKey}&query=" . urlencode($cleanTitle); + if (!empty($year)) $searchUrl .= "&year={$year}"; + $searchUrl .= "&language=fr-FR"; + + $searchRes = httpGet($searchUrl, 5); + $searchData = $searchRes ? json_decode($searchRes, true) : []; + + if (empty($searchData['results'])) { + $searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$tmdbKey}&query=" . urlencode($cleanTitle) . "&language=fr-FR"; + $searchRes = httpGet($searchUrl, 5); + $searchData = $searchRes ? json_decode($searchRes, true) : []; + } + + if (empty($searchData['results'])) return $result; + + $movieId = $searchData['results'][0]['id']; + $result['poster'] = !empty($searchData['results'][0]['poster_path']) + ? "https://image.tmdb.org/t/p/w500" . $searchData['results'][0]['poster_path'] + : $defaultPoster; + $result['description'] = $searchData['results'][0]['overview'] ?? ''; + + if (!empty($searchData['results'][0]['release_date'])) { + $result['year'] = substr($searchData['results'][0]['release_date'], 0, 4); + } + + // Récupérer les détails pour réalisateur, acteurs, durée + $detailsUrl = "https://api.themoviedb.org/3/movie/{$movieId}?api_key={$tmdbKey}&append_to_response=credits&language=fr-FR"; + $detailsRes = httpGet($detailsUrl, 5); + if ($detailsRes) { + $details = json_decode($detailsRes, true); + + if (!empty($details['runtime'])) { + $result['length'] = $details['runtime'] . ' min'; + } + + // Réalisateur + if (!empty($details['credits']['crew'])) { + $directors = []; + foreach ($details['credits']['crew'] as $crew) { + if ($crew['job'] === 'Director') $directors[] = $crew['name']; + } + if (!empty($directors)) $result['director'] = implode(', ', $directors); + } + + // Acteurs (top 4) + if (!empty($details['credits']['cast'])) { + $actors = []; + $topCast = array_slice($details['credits']['cast'], 0, 4); + foreach ($topCast as $actor) $actors[] = $actor['name']; + if (!empty($actors)) $result['actors'] = implode(', ', $actors); + } + + // Synopsis complet si pas trouvé dans la recherche + if (empty($result['description']) && !empty($details['overview'])) { + $result['description'] = $details['overview']; + } + } + + return $result; +} + // ── ROUTEUR PRINCIPAL ── $action = $_GET['action'] ?? ''; $data = json_decode(file_get_contents('php://input'), true) ?? []; @@ -250,14 +476,47 @@ switch ($action) { $sql = "INSERT INTO critiques (id, title, year, director, poster, rating, review, streaming) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE title=VALUES(title), year=VALUES(year), director=VALUES(director), poster=VALUES(poster), rating=VALUES(rating), review=VALUES(review), streaming=VALUES(streaming)"; $stmt = $pdo->prepare($sql); $stmt->execute([$id, $data['title'] ?? '', $data['year'] ?? '', $data['director'] ?? '', $data['poster'] ?? '', $data['rating'] ?? 3.0, $data['review'] ?? '', $streaming]); - } else { - // ✅ NOUVEAU : Utiliser TMDB +} else { + // Récupérer les infos depuis BluRay.com et TMDB si poster vide if (empty($data['poster']) && !empty($data['title'])) { - $tmdbData = fetchPosterTMDB($data['title'], $data['year'] ?? '', $pdo); - if (!empty($tmdbData['poster']) && $tmdbData['poster'] !== 'assets/img/default_physical_media.jpg') { + $bluRayData = fetchBluRayPhysicalInfo($data['ean_isbn13'] ?? '', $data['title'], $pdo); + $tmdbData = fetchTmdbPosterAndSynopsis($data['title'], $data['year'] ?? '', $pdo); + + // Fusionner les données + if ($tmdbData['poster'] !== 'assets/img/default_physical_media.jpg') { $data['poster'] = $tmdbData['poster']; + } elseif ($bluRayData['poster'] !== 'assets/img/default_physical_media.jpg') { + $data['poster'] = $bluRayData['poster']; + } + + if (empty($data['description'])) { + $data['description'] = $tmdbData['description'] ?: $bluRayData['description']; + } + if (empty($data['director'])) { + $data['director'] = $tmdbData['director']; + } + if (empty($data['actors'])) { + $data['actors'] = $tmdbData['actors']; + } + if (empty($data['length'])) { + $data['length'] = $tmdbData['length'] ?: $bluRayData['length']; + } + if (empty($data['publisher'])) { + $data['publisher'] = $bluRayData['publisher']; + } + if (empty($data['number_of_discs']) || $data['number_of_discs'] == 1) { + if (!empty($bluRayData['number_of_discs'])) { + $data['number_of_discs'] = $bluRayData['number_of_discs']; + } + } + if (empty($data['aspect_ratio'])) { + $data['aspect_ratio'] = $bluRayData['aspect_ratio']; + } + if (empty($data['format'])) { + $data['format'] = $bluRayData['format'] ?: detectFormat($data['title'], $data['description'] ?? ''); } } + $sql = "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)"; $stmt = $pdo->prepare($sql); $stmt->execute([$id, $data['title'] ?? '', $data['year'] ?? '', $data['director'] ?? '', $data['poster'] ?? '', $data['format'] ?? '', $data['length'] ?? '', $data['publisher'] ?? '', $data['ean_isbn13'] ?? '', $data['number_of_discs'] ?? 1, $data['aspect_ratio'] ?? '', $data['description'] ?? '', $data['actors'] ?? '']); @@ -291,47 +550,94 @@ switch ($action) { $imported = 0; try { - if ($type === 'videotheque') { - $stmtVideo = $pdo->prepare("INSERT INTO videotheque (id, title, year, format, poster, ean_isbn13, description, length, number_of_discs, aspect_ratio, actors, publisher, director) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - ean_isbn13 = VALUES(ean_isbn13), - poster = VALUES(poster), - description = VALUES(description), - format = VALUES(format), - length = VALUES(length), - number_of_discs = VALUES(number_of_discs), - aspect_ratio = VALUES(aspect_ratio), - actors = VALUES(actors), - publisher = VALUES(publisher), - director = VALUES(director)"); + if ($type === 'videotheque') { + $stmtVideo = $pdo->prepare("INSERT INTO videotheque (id, title, year, format, poster, ean_isbn13, description, length, number_of_discs, aspect_ratio, actors, publisher, director) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + ean_isbn13 = VALUES(ean_isbn13), + poster = VALUES(poster), + description = VALUES(description), + format = VALUES(format), + length = VALUES(length), + number_of_discs = VALUES(number_of_discs), + aspect_ratio = VALUES(aspect_ratio), + actors = VALUES(actors), + publisher = VALUES(publisher), + director = VALUES(director), + year = VALUES(year)"); + + foreach ($items as $item) { + $title = $item['title'] ?? ''; + if (empty($title)) continue; - foreach ($items as $item) { - $title = $item['title'] ?? ''; - if (empty($title)) continue; - - $year = $item['year'] ?? ''; - $ean = $item['ean'] ?? ''; - $desc = $item['description'] ?? ''; - $length = $item['length'] ?? ''; - $discs = $item['number_of_discs'] ?? 1; - $aspect = $item['aspect_ratio'] ?? ''; - $actors = $item['actors'] ?? ''; - $publisher = $item['publisher'] ?? ''; - $director = $item['director'] ?? ''; - - $id = makeStableId('videotheque', $title, $year); - - $tmdbData = fetchPosterTMDB($title, $year, $pdo); - $poster = $tmdbData['poster']; - $format = detectFormat($title, $desc); - - $stmtVideo->execute([ - $id, $title, $year, $format, $poster, $ean, $desc, - $length, $discs, $aspect, $actors, $publisher, $director - ]); - $imported++; - } + $year = $item['year'] ?? ''; + $ean = $item['ean'] ?? ''; + $descCsv = $item['description'] ?? ''; + $lengthCsv = $item['length'] ?? ''; + $discsCsv = $item['number_of_discs'] ?? 1; + $aspectCsv = $item['aspect_ratio'] ?? ''; + $actorsCsv = $item['actors'] ?? ''; + $publisherCsv = $item['publisher'] ?? ''; + $directorCsv = $item['director'] ?? ''; + + $id = makeStableId('videotheque', $title, $year); + + // ── SOURCE 1 : BLU-RAY.COM (infos physiques) ── + $bluRayData = fetchBluRayPhysicalInfo($ean, $title, $pdo); + + // ── SOURCE 2 : TMDB (affiche + synopsis + métadonnées) ── + $tmdbData = fetchTmdbPosterAndSynopsis($title, $year, $pdo); + + // ── FUSION DES DONNÉES (priorité : CSV > BluRay.com > TMDB) ── + + // Affiche : TMDB en priorité (meilleure qualité) + $poster = ($tmdbData['poster'] !== 'assets/img/default_physical_media.jpg') + ? $tmdbData['poster'] + : $bluRayData['poster']; + + // Format : BluRay.com > détection depuis titre + $format = !empty($bluRayData['format']) + ? $bluRayData['format'] + : detectFormat($title, $descCsv); + + // Éditeur : CSV > BluRay.com + $publisher = !empty($publisherCsv) ? $publisherCsv : $bluRayData['publisher']; + + // Nombre de disques : CSV > BluRay.com + $discs = !empty($discsCsv) && $discsCsv != 1 + ? $discsCsv + : (!empty($bluRayData['number_of_discs']) ? $bluRayData['number_of_discs'] : ($discsCsv ?: 1)); + + // Aspect ratio : CSV > BluRay.com + $aspect = !empty($aspectCsv) ? $aspectCsv : $bluRayData['aspect_ratio']; + + // Durée : CSV > BluRay.com > TMDB + $length = !empty($lengthCsv) + ? $lengthCsv + : (!empty($bluRayData['length']) ? $bluRayData['length'] : $tmdbData['length']); + + // Description : CSV > TMDB > BluRay.com + $description = !empty($descCsv) + ? $descCsv + : (!empty($tmdbData['description']) ? $tmdbData['description'] : $bluRayData['description']); + + // Réalisateur : CSV > TMDB + $director = !empty($directorCsv) ? $directorCsv : $tmdbData['director']; + + // Acteurs : CSV > TMDB + $actors = !empty($actorsCsv) ? $actorsCsv : $tmdbData['actors']; + + // Année : CSV > TMDB > BluRay.com + $finalYear = !empty($year) ? $year : (!empty($tmdbData['year']) ? $tmdbData['year'] : $bluRayData['year']); + + error_log("Import vidéotheque '{$title}' : poster=" . ($poster !== 'assets/img/default_physical_media.jpg' ? 'OK' : 'DEFAULT') . ", format={$format}, discs={$discs}, aspect={$aspect}"); + + $stmtVideo->execute([ + $id, $title, $finalYear, $format, $poster, $ean, $description, + $length, $discs, $aspect, $actors, $publisher, $director + ]); + $imported++; + } } else { $stmtCritiques = $pdo->prepare("INSERT INTO critiques (id, title, year, director, poster, rating, review, streaming) VALUES (?, ?, ?, ?, ?, ?, ?, ?)