From 62f4570d7c5715f24995cd966e7ee93b6152a020 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sun, 28 Jun 2026 14:12:40 +0200 Subject: [PATCH] Actualiser api.php --- api.php | 197 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 166 insertions(+), 31 deletions(-) diff --git a/api.php b/api.php index 23323ed..56b377e 100644 --- a/api.php +++ b/api.php @@ -60,10 +60,22 @@ function getTmdbApiKey($pdo) { return $row ? decryptData($row['key_value']) : null; } -function httpGet($url, $timeout = 5, $ua = null) { +function getUpcmdbApiKey($pdo) { + $stmt = $pdo->prepare("SELECT key_value FROM config WHERE key_name = 'upcmdb_api_key'"); + $stmt->execute(); + $row = $stmt->fetch(); + return $row ? decryptData($row['key_value']) : null; +} + +function httpGet($url, $timeout = 5, $ua = null, $extraHeaders = []) { if (!$ua) $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'; + $headers = array_merge( + ['Accept: application/json, text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language: fr-FR,fr;q=0.8'], + $extraHeaders + ); if (!function_exists('curl_init')) { - $ctx = stream_context_create(['http' => ['timeout' => $timeout, 'user_agent' => $ua]]); + $headerLines = implode("\r\n", $headers); + $ctx = stream_context_create(['http' => ['timeout' => $timeout, 'user_agent' => $ua, 'header' => $headerLines]]); $res = @file_get_contents($url, false, $ctx); return $res ?: null; } @@ -76,7 +88,7 @@ function httpGet($url, $timeout = 5, $ua = null) { CURLOPT_USERAGENT => $ua, CURLOPT_FOLLOWLOCATION => true, CURLOPT_REFERER => 'https://www.cinemapassion.com/', - CURLOPT_HTTPHEADER => ['Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language: fr-FR,fr;q=0.8'], + CURLOPT_HTTPHEADER => $headers, ]); $res = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); @@ -101,6 +113,125 @@ function detectFormat($title, $desc = '') { return 'Blu-ray'; } +function parseDiscCountFromTitle($title) { + if (preg_match('/(\d+)\s*(?:dvd|blu-?ray|bluray|bd|disc|disque)/i', $title, $m)) { + return max(1, (int)$m[1]); + } + if (preg_match('/(?:coffret|pack|collection|trilogie|anthologie).*?(\d+)/i', $title, $m)) { + return max(1, (int)$m[1]); + } + if (preg_match('/\btrilogie\b/i', $title)) return 3; + return 1; +} + +function cleanUpcTitle($title) { + $clean = trim($title); + $clean = preg_replace('/^[\s"]*(DVD|Blu-ray|Blu Ray|4K|UHD)[\s"]*/i', '', $clean); + $clean = preg_replace('/(blu-ray|bluray|dvd|4k|ultra hd|combo|vhs|blister pack|new blister).*$/i', '', $clean); + $clean = preg_replace('/[\s"]+New[\s"]*$/i', '', $clean); + return cleanTitle($clean); +} + +function emptyPhysicalResult() { + return [ + 'title' => '', 'publisher' => '', 'format' => '', 'length' => '', + 'number_of_discs' => 1, 'aspect_ratio' => '', 'year' => '' + ]; +} + +function throttleUpcLookup() { + static $lastLookupAt = 0; + $elapsed = microtime(true) - $lastLookupAt; + if ($lastLookupAt > 0 && $elapsed < 2) { + usleep((int)((2 - $elapsed) * 1000000)); + } + $lastLookupAt = microtime(true); +} + +// ── API GRATUITE UPCitemdb : métadonnées physiques via EAN/UPC ── +function fetchPhysicalFromUpcitemdb($ean) { + $empty = emptyPhysicalResult(); + $ean = preg_replace('/[^0-9]/', '', (string)$ean); + if (strlen($ean) < 8) return $empty; + + throttleUpcLookup(); + + $url = 'https://api.upcitemdb.com/prod/trial/lookup?upc=' . urlencode($ean); + $res = httpGet($url, 10, 'MonPetitCinema/1.0'); + if (!$res) return $empty; + + $data = json_decode($res, true); + if (empty($data['items'][0])) return $empty; + + $item = $data['items'][0]; + $rawTitle = $item['title'] ?? ''; + $title = cleanUpcTitle($rawTitle) ?: trim($rawTitle); + $publisher = trim($item['brand'] ?? ''); + + return [ + 'title' => $title, + 'publisher' => $publisher, + 'format' => detectFormat($rawTitle), + 'length' => '', + 'number_of_discs' => parseDiscCountFromTitle($rawTitle), + 'aspect_ratio' => '', + 'year' => '' + ]; +} + +// ── API UPCMDB (clé gratuite) : fallback spécialisé DVD/Blu-ray/4K ── +function fetchPhysicalFromUpcmdb($ean, $pdo) { + $empty = emptyPhysicalResult(); + $apiKey = getUpcmdbApiKey($pdo); + if (!$apiKey) return $empty; + + $ean = preg_replace('/[^0-9]/', '', (string)$ean); + if (strlen($ean) < 8) return $empty; + + $url = 'https://upcmdb.com/api/v1/lookup/' . urlencode($ean); + $res = httpGet($url, 10, 'MonPetitCinema/1.0', [ + 'Accept: application/json', + 'X-API-Key: ' . $apiKey + ]); + if (!$res || $res[0] === '<') return $empty; + + $data = json_decode($res, true); + if (($data['status'] ?? '') !== 'success' || empty($data['data'])) return $empty; + + $item = $data['data']; + $rawTitle = trim($item['title'] ?? ''); + if ($rawTitle === '') return $empty; + + $title = cleanUpcTitle($rawTitle) ?: $rawTitle; + $format = trim($item['format'] ?? ''); + $runtime = trim($item['runtime'] ?? ''); + + return [ + 'title' => $title, + 'publisher' => trim($item['studio'] ?? $item['publisher'] ?? ''), + 'format' => $format !== '' ? $format : detectFormat($rawTitle), + 'length' => $runtime, + 'number_of_discs' => parseDiscCountFromTitle($rawTitle), + 'aspect_ratio' => trim($item['aspect_ratio'] ?? ''), + 'year' => trim($item['year'] ?? '') + ]; +} + +function fetchPhysicalByEan($ean, $pdo = null) { + $result = fetchPhysicalFromUpcitemdb($ean); + if (!empty($result['title'])) return $result; + + if ($pdo) { + $fallback = fetchPhysicalFromUpcmdb($ean, $pdo); + if (!empty($fallback['title'])) { + error_log("UPCMDB: ✅ Fallback trouvé pour EAN {$ean} → {$fallback['title']}"); + return $fallback; + } + } + + return $result; +} + // ── FONCTION POUR RÉCUPÉRER LES AFFICHES DEPUIS TMDB ── function fetchPosterTMDB($title, $year = '', $pdo = null) { $defaultPoster = 'assets/img/default_physical_media.jpg'; @@ -291,7 +422,7 @@ switch ($action) { case 'get_config_keys': checkAuth($pdo); - $stmt = $pdo->prepare("SELECT key_name, key_value FROM config WHERE key_name IN ('tmdb_api_key', 'fanart_api_key')"); + $stmt = $pdo->prepare("SELECT key_name, key_value FROM config WHERE key_name IN ('tmdb_api_key', 'fanart_api_key', 'upcmdb_api_key')"); $stmt->execute(); $rows = $stmt->fetchAll(); $config = []; @@ -300,6 +431,7 @@ switch ($action) { } if (!isset($config['tmdb_api_key'])) $config['tmdb_api_key'] = ''; if (!isset($config['fanart_api_key'])) $config['fanart_api_key'] = ''; + if (!isset($config['upcmdb_api_key'])) $config['upcmdb_api_key'] = ''; echo json_encode($config); break; @@ -307,7 +439,7 @@ switch ($action) { checkAuth($pdo); $keyName = $data['key_name'] ?? ''; $keyValue = $data['key_value'] ?? ''; - if (in_array($keyName, ['tmdb_api_key', 'fanart_api_key']) && !empty($keyValue)) { + if (in_array($keyName, ['tmdb_api_key', 'fanart_api_key', 'upcmdb_api_key']) && !empty($keyValue)) { $stmt = $pdo->prepare("REPLACE INTO config (key_name, key_value) VALUES (?, ?)"); $stmt->execute([$keyName, encryptData($keyValue)]); echo json_encode(["success" => true]); @@ -319,10 +451,10 @@ switch ($action) { case 'get_films': $sql = " - SELECT id, title, year, director, poster, rating, review, NULL AS description, streaming, 'critique' AS type + SELECT id, title, year, director, poster, rating, review, NULL AS description, streaming, 'critique' AS type, NULL AS format FROM critiques UNION ALL - SELECT id, title, year, director, poster, NULL AS rating, NULL AS review, description, NULL AS streaming, 'videotheque' AS type + SELECT id, title, year, director, poster, NULL AS rating, NULL AS review, description, NULL AS streaming, 'videotheque' AS type, format FROM videotheque ORDER BY id DESC "; @@ -343,7 +475,7 @@ switch ($action) { $id = !empty($data['id']) ? $data['id'] : makeStableId($type, $data['title'] ?? '', $data['year'] ?? '0000'); if ($type === 'critique' && (empty($data['director']) || empty($data['poster']))) { - $tmdbData = fetchTMDBFull($data['title'] ?? '', $data['year'] ?? '', getTmdbApiKey($pdo), $pdo); + $tmdbData = fetchTmdbPosterAndSynopsis($data['title'] ?? '', $data['year'] ?? '', $pdo); if ($tmdbData) { if (empty($data['director'])) $data['director'] = $tmdbData['director']; if (empty($data['poster'])) $data['poster'] = $tmdbData['poster']; @@ -401,6 +533,7 @@ $type = $data['type'] ?? 'critique'; $items = $data['items'] ?? []; $pdo->beginTransaction(); $imported = 0; +$skipped = 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) @@ -419,41 +552,43 @@ try { year = VALUES(year)"); foreach ($items as $item) { - $title = $item['title'] ?? ''; - if (empty($title)) continue; - $year = $item['year'] ?? ''; - $id = makeStableId('videotheque', $title, $year); - - // ── SOURCE 1 : CSV (Informations physiques uniquement) ── - $ean = $item['ean'] ?? ''; - $format = $item['format'] ?? ''; - $publisher = $item['publisher'] ?? ''; - $discs = $item['number_of_discs'] ?? 1; - $aspect = $item['aspect_ratio'] ?? ''; - $length = $item['length'] ?? ''; - - // On initialise avec le CSV, mais l'API TMDB va écraser ces valeurs si elle trouve mieux - $description = $item['description'] ?? ''; - $director = $item['director'] ?? ''; - $actors = $item['actors'] ?? ''; + // ── SOURCE 1 : CSV → EAN uniquement ── + $ean = preg_replace('/[^0-9]/', '', (string)($item['ean'] ?? '')); + if (strlen($ean) < 8) { $skipped++; continue; } + + // ── SOURCE 2 : UPCitemdb (gratuit) → métadonnées physiques ── + $physical = fetchPhysicalByEan($ean, $pdo); + $title = $physical['title'] ?? ''; + if (empty($title)) { $skipped++; continue; } + + $year = $physical['year'] ?? ''; + $format = $physical['format'] ?? ''; + $publisher = $physical['publisher'] ?? ''; + $discs = $physical['number_of_discs'] ?? 1; + $aspect = $physical['aspect_ratio'] ?? ''; + $length = $physical['length'] ?? ''; + $id = makeStableId('videotheque', $ean, $title); + + $description = ''; + $director = ''; + $actors = ''; $poster = 'assets/img/default_physical_media.jpg'; - - // ── SOURCE 2 : API TMDB (Priorité absolue pour les métadonnées) ── + + // ── SOURCE 3 : TMDB → affiche, synopsis, réalisateur, acteurs ── $tmdbData = fetchTmdbPosterAndSynopsis($title, $year, $pdo); if ($tmdbData) { if ($tmdbData['poster'] !== 'assets/img/default_physical_media.jpg') { $poster = $tmdbData['poster']; } - // Écrasement des données CSV par l'API TMDB si disponibles if (!empty($tmdbData['description'])) $description = $tmdbData['description']; if (!empty($tmdbData['director'])) $director = $tmdbData['director']; if (!empty($tmdbData['actors'])) $actors = $tmdbData['actors']; if (!empty($tmdbData['length'])) $length = $tmdbData['length']; if (!empty($tmdbData['year'])) $year = $tmdbData['year']; } - + if (empty($format)) $format = detectFormat($title, $description); - + $stmtVideo->execute([ $id, $title, $year, $format, $poster, $ean, $description, $length, $discs, $aspect, $actors, $publisher, $director @@ -491,7 +626,7 @@ try { } } $pdo->commit(); - echo json_encode(["success" => true, "imported" => $imported]); + echo json_encode(["success" => true, "imported" => $imported, "skipped" => $skipped]); } catch (Throwable $e) { // ── CORRECTION ICI : Throwable capture les Crash Fatals PHP ── $pdo->rollBack();