diff --git a/api.php b/api.php index 2629653..eb2cc8b 100644 --- a/api.php +++ b/api.php @@ -583,6 +583,7 @@ function fetchFromBlurayCom($ean) { return $empty; } +// ── CORRECTION : Utilisation de removeAccentsForUrl pour MovieCovers ── function fetchFromMovieCovers($title, $year = '') { $empty = [ 'title' => '', 'year' => '', 'director' => '', 'actors' => '', @@ -592,20 +593,17 @@ function fetchFromMovieCovers($title, $year = '') { ]; if (empty($title)) return $empty; - // Nettoyer le titre pour l'URL (les espaces deviennent des +) - $urlTitle = strtoupper(str_replace(' ', '+', $title)); + // Nettoyage robuste du titre pour l'URL + $cleanTitle = removeAccentsForUrl($title); + $urlTitle = strtoupper(str_replace(' ', '+', $cleanTitle)); $url = "https://moviecovers.com/film/titre_{$urlTitle}.html"; + $html = httpGet($url, 10); if (!$html) return $empty; + + if (preg_match('/([^<]+)<\/TITLE>/i', $html, $m)) $empty['title'] = trim($m[1]); - // Extraire le titre - if (preg_match('/<meta property="og:title" content="([^"]+)"/i', $html, $m)) { - $empty['title'] = trim($m[1]); - } elseif (preg_match('/<TITLE>([^<]+)<\/TITLE>/i', $html, $m)) { - $empty['title'] = trim($m[1]); - } - - // Extraire l'IDMC pour construire l'URL de l'image HD (comme dans votre script Python) + // Récupération IDMC pour HD $idmc = ''; if (preg_match('/IDMC.*?<PRE>([^<]+)<\/PRE>/is', $html, $m)) { $idmc = trim($m[1]); @@ -613,7 +611,6 @@ function fetchFromMovieCovers($title, $year = '') { $idmc = urldecode($m[1]); } - // Essayer de récupérer l'affiche HD via les URLs du script Python if ($idmc) { $idmc_encoded = urlencode($idmc); $hd_urls = [ @@ -622,53 +619,29 @@ function fetchFromMovieCovers($title, $year = '') { ]; foreach ($hd_urls as $hd_url) { $img_data = httpGet($hd_url, 5); - if ($img_data && strlen($img_data) > 5000) { // Vérifie que c'est une vraie image (>5Ko) + if ($img_data && strlen($img_data) > 5000) { $empty['poster'] = $hd_url; break; } } } - // Fallback sur le thumbnail si pas d'image HD trouvée if (empty($empty['poster'])) { - // CORRECTION : La regex gère le cas où title= est avant src= - if (preg_match('/<img[^>]*title="Recto[^"]*"[^>]*src="([^"]+)"/i', $html, $m)) { - $empty['poster'] = $m[1]; - } elseif (preg_match('/<img[^>]*src="([^"]+)"[^>]*title="Recto/i', $html, $m)) { - $empty['poster'] = $m[1]; - } elseif (preg_match('/<meta[^>]*property="og:image"[^>]*content="([^"]+)"/i', $html, $m)) { - $empty['poster'] = $m[1]; - } + if (preg_match('/<img[^>]*title="Recto[^"]*"[^>]*src="([^"]+)"/i', $html, $m)) $empty['poster'] = $m[1]; + elseif (preg_match('/<img[^>]*src="([^"]+)"[^>]*title="Recto/i', $html, $m)) $empty['poster'] = $m[1]; + elseif (preg_match('/<meta[^>]*property="og:image"[^>]*content="([^"]+)"/i', $html, $m)) $empty['poster'] = $m[1]; } - - // Extraire le réalisateur - if (preg_match('/Réalisateur<\/TH>\s*<TD[^>]*>.*?<a[^>]*>([^<]+)<\/a>/is', $html, $m)) { - $empty['director'] = trim($m[1]); - } - - // Extraire l'année - if (preg_match('/Année<\/TH>\s*<TD[^>]*>.*?<a[^>]*>(\d{4})<\/a>/is', $html, $m)) { - $empty['year'] = $m[1]; - } - - // Extraire la durée - if (preg_match('/Durée<\/TH>\s*<TD[^>]*>([\dH]+[\dmin]*)/is', $html, $m)) { - $empty['length'] = trim($m[1]); - } - - // Extraire les acteurs - if (preg_match_all('/<a href="\/multicrit\.html\?acteur=[^"]*">([^<]+)<\/a>/i', $html, $matches)) { - $empty['actors'] = implode(', ', array_slice($matches[1], 0, 5)); - } - - // Extraire le synopsis - if (preg_match('/<meta[^>]*property="og:description"[^>]*content="([^"]+)"/i', $html, $m)) { - $empty['description'] = html_entity_decode($m[1]); - } - + + if (preg_match('/Réalisateur<\/TH>\s*<TD[^>]*>.*?<a[^>]*>([^<]+)<\/a>/is', $html, $m)) $empty['director'] = trim($m[1]); + if (preg_match('/Année<\/TH>\s*<TD[^>]*>.*?<a[^>]*>(\d{4})<\/a>/is', $html, $m)) $empty['year'] = $m[1]; + if (preg_match('/Durée<\/TH>\s*<TD[^>]*>([\dH]+[\dmin]*)/is', $html, $m)) $empty['length'] = trim($m[1]); + if (preg_match_all('/<a href="\/multicrit\.html\?acteur=[^"]*">([^<]+)<\/a>/i', $html, $matches)) $empty['actors'] = implode(', ', array_slice($matches[1], 0, 5)); + if (preg_match('/<meta[^>]*property="og:description"[^>]*content="([^"]+)"/i', $html, $m)) $empty['description'] = html_entity_decode($m[1]); + return $empty; } + function removeAccents($string) { $string = htmlentities($string, ENT_NOQUOTES, 'utf-8'); $string = preg_replace('#&([A-za-z])(?:acute|cedil|caron|circ|grave|orn|ring|slash|th|tilde|uml);#', '\1', $string); @@ -881,100 +854,124 @@ case 'import_batch': $data = json_decode(file_get_contents("php://input"), true); $type = $data['type'] ?? ''; $items = $data['items'] ?? []; - set_time_limit(0); - - $data = json_decode(file_get_contents("php://input"), true); - $rows = $data['rows'] ?? []; - - $imported = 0; - $skipped = 0; - - try { - $pdo->beginTransaction(); + $pdo->beginTransaction(); + $imported = 0; $skipped = 0; + + try { + if ($type === 'videotheque') { + $stmt = $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 + title=VALUES(title), year=VALUES(year), format=VALUES(format), + poster=IF(VALUES(poster)!='assets/img/default_physical_media.jpg', VALUES(poster), poster), + ean_isbn13=IF(VALUES(ean_isbn13)!='', VALUES(ean_isbn13), ean_isbn13), + description=IF(VALUES(description)!='', VALUES(description), description), + length=IF(VALUES(length)!='', VALUES(length), length), + 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), + actors=IF(VALUES(actors)!='', VALUES(actors), actors), + publisher=IF(VALUES(publisher)!='', VALUES(publisher), publisher), + director=IF(VALUES(director)!='', VALUES(director), director)"); - // Préparation de la requête d'insertion (Adaptée avec ON DUPLICATE KEY pour mettre à jour si réimport) - $stmtCritiques = $pdo->prepare(" - INSERT INTO critiques (id, title, year, director, poster, rating, review, streaming) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - director = VALUES(director), - poster = VALUES(poster), - rating = VALUES(rating), - review = VALUES(review) - "); + foreach ($items as $item) { + $ean = preg_replace('/[^0-9]/', '', (string)($item['ean'] ?? '')); + $csvTitle = trim($item['title'] ?? ''); + + // Récupération des données déjà présentes dans le CSV + $csvYear = trim($item['year'] ?? ''); + $csvPublisher = trim($item['publisher'] ?? ''); + $csvLength = trim($item['length'] ?? ''); + $csvDiscs = (int)($item['number_of_discs'] ?? 1); + $csvAspect = trim($item['aspect_ratio'] ?? ''); + $csvActors = trim($item['actors'] ?? ''); + $csvSynopsis = trim($item['synopsis'] ?? ''); - foreach ($rows as $rowData) { - // Extraction des données du CSV - $title = $rowData['title'] ?? $rowData['Title'] ?? ''; - $yearRaw = $rowData['publish_date'] ?? $rowData['Year'] ?? $rowData['year'] ?? ''; - $year = substr($yearRaw, 0, 4); + if (strlen($ean) < 8 && empty($csvTitle)) { $skipped++; continue; } + + // 1. BLU-RAY.COM (Métadonnées physiques) + $blurayData = (!empty($ean)) ? fetchFromBlurayCom($ean) : []; + if (!is_array($blurayData)) $blurayData = []; - // Récupération de l'EAN (Code-barres Blu-ray.com) - $ean = $rowData['ean_isbn13'] ?? $rowData['upc_isbn10'] ?? ''; + $title = !empty($blurayData['title']) ? $blurayData['title'] : $csvTitle; + $year = $blurayData['year'] ?? $csvYear; + $format = $blurayData['format'] ?: detectFormat($title); + $publisher = $blurayData['publisher'] ?: $csvPublisher; + $discs = $blurayData['number_of_discs'] ?: ($csvDiscs ?: 1); + $aspect = $blurayData['aspect_ratio'] ?: $csvAspect; + $length = $blurayData['length'] ?: (!empty($csvLength) ? $csvLength . ' min' : ''); - if (empty($title)) { - $skipped++; - continue; + // 2. MOVIECOVERS (Jaquettes HD Physiques) + $mcData = fetchFromMovieCovers($title, $year); + if (!is_array($mcData)) $mcData = []; + + $poster = !empty($mcData['poster']) ? $mcData['poster'] : (!empty($blurayData['poster']) ? $blurayData['poster'] : ''); + $desc = !empty($csvSynopsis) ? $csvSynopsis : (!empty($mcData['description']) ? $mcData['description'] : ($blurayData['description'] ?? '')); + $director = !empty($mcData['director']) ? $mcData['director'] : ($blurayData['director'] ?? ''); + $actors = !empty($csvActors) ? $csvActors : (!empty($mcData['actors']) ? $mcData['actors'] : ($blurayData['actors'] ?? '')); + + // 3. FALLBACK TMDB (Si des données manquent encore) + if (empty($poster) || empty($director) || empty($actors) || empty($desc)) { + $tmdb = fetchTmdbPosterAndSynopsis($title, $year, $pdo); + if (empty($poster) || $poster === 'assets/img/default_physical_media.jpg') $poster = $tmdb['poster']; + if (empty($director)) $director = $tmdb['director'] ?? ''; + if (empty($actors)) $actors = $tmdb['actors'] ?? ''; + if (empty($desc)) $desc = $tmdb['description'] ?? ''; + if (empty($length) && !empty($tmdb['length'])) $length = $tmdb['length']; + if (empty($year) && !empty($tmdb['year'])) $year = $tmdb['year']; } - $id = generateStableId('critique', $title, $year); + $id = makeStableId('videotheque', $title, $year); + $stmt->execute([$id, $title, $year, $format, $poster, $ean, $desc, $length, $discs, $aspect, $actors, $publisher, $director]); + $imported++; + } + } else { + // IMPORT CRITIQUES + $stmtCritiques = $pdo->prepare("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)"); + + foreach ($items as $rowData) { + $title = $rowData['Title'] ?? $rowData['title'] ?? ''; + if (empty($title)) continue; + + // Extraction de l'année depuis publish_date si disponible + $year = ''; + if (!empty($rowData['publish_date'])) { + $year = substr($rowData['publish_date'], 0, 4); + } else { + $year = $rowData['Year'] ?? $rowData['year'] ?? ''; + } + + $id = makeStableId('critique', $title, $year); + $ratingRaw = $rowData['Rating'] ?? $rowData['rating'] ?? ''; $rating = ($ratingRaw !== '' && $ratingRaw !== null) ? (float)$ratingRaw : null; - $review = $rowData['Review'] ?? $rowData['review'] ?? ''; - - $director = ''; - $poster = 'assets/img/default_physical_media.jpg'; + + // Utilise 'review' sinon retombe sur 'description' (cas de votre CSV) + $review = $rowData['Review'] ?? $rowData['review'] ?? $rowData['description'] ?? ''; + + $director = ''; + $poster = 'assets/img/default_physical_media.jpg'; $streaming = 'Support physique / Cinéma'; - - // ========================================================= - // 1. TENTATIVE : TÉLÉCHARGEMENT DE LA JAQUETTE MOVIECOVERS - // ========================================================= - $localCover = fetchAndDownloadMovieCovers($title, $ean); - - // ========================================================= - // 2. RÉCUPÉRATION : METADONNÉES TMDB (Casting, Synopsis...) - // ========================================================= + $tmdbData = fetchTmdbPosterAndSynopsis($title, $year, $pdo); - - // ========================================================= - // 3. LOGIQUE DE PRIORITÉ DES AFFICHES - // ========================================================= - if ($localCover) { - // ➔ SUCCÈS MOVIECOVERS : On utilise l'image physique HD téléchargée - $poster = $localCover; - - // On récupère quand même le réalisateur depuis TMDB - if ($tmdbData && !empty($tmdbData['director'])) { - $director = $tmdbData['director']; - } - } else { - // ➔ ÉCHEC MOVIECOVERS : Repli sur l'affiche TMDB - if ($tmdbData) { - if (!empty($tmdbData['director'])) { - $director = $tmdbData['director']; - } - if ($tmdbData['poster'] !== 'assets/img/default_physical_media.jpg') { - $poster = $tmdbData['poster']; - } - if (empty($year) && !empty($tmdbData['year'])) { - $year = $tmdbData['year']; // Corrige l'année si absente du CSV - } - } + if ($tmdbData) { + if (!empty($tmdbData['director'])) $director = $tmdbData['director']; + if ($tmdbData['poster'] !== 'assets/img/default_physical_media.jpg') $poster = $tmdbData['poster']; + if (empty($year) && !empty($tmdbData['year'])) $year = $tmdbData['year']; } - - // Exécution de l'insertion ou de la mise à jour + $stmtCritiques->execute([$id, $title, $year, $director, $poster, $rating, $review, $streaming]); $imported++; } - - $pdo->commit(); - echo json_encode(["success" => true, "imported" => $imported, "skipped" => $skipped]); - - } catch (Throwable $e) { - $pdo->rollBack(); - error_log("import_batch error: " . $e->getMessage()); - http_response_code(500); - echo json_encode(["success" => false, "error" => "Erreur lors de l'import : " . $e->getMessage()]); } - break; + $pdo->commit(); + echo json_encode(["success" => true, "imported" => $imported, "skipped" => $skipped]); + } catch (Throwable $e) { + $pdo->rollBack(); + error_log("import_batch error: " . $e->getMessage()); + http_response_code(500); + echo json_encode(["success" => false, "error" => $e->getMessage(), "line" => $e->getLine()]); + } + break; } \ No newline at end of file