diff --git a/api.php b/api.php index 744f033..c12b7a5 100644 --- a/api.php +++ b/api.php @@ -2,7 +2,7 @@ ini_set('log_errors', 1); ini_set('error_log', __DIR__ . '/php_errors.log'); ini_set('display_errors', 0); -set_time_limit(600); // Protection timeout pour les gros imports +set_time_limit(600); error_reporting(E_ALL); header("Content-Type: application/json; charset=UTF-8"); @@ -19,7 +19,6 @@ try { PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]); - // Initialisation BDD simplifiée $pdo->exec("CREATE TABLE IF NOT EXISTS users (id INT PRIMARY KEY, username VARCHAR(50), password_hash VARCHAR(255))"); $pdo->exec("CREATE TABLE IF NOT EXISTS config (key_name VARCHAR(50) PRIMARY KEY, key_value TEXT)"); $pdo->exec("CREATE TABLE IF NOT EXISTS critiques (id BIGINT PRIMARY KEY, title VARCHAR(255), year VARCHAR(10), director VARCHAR(255), poster TEXT, rating DECIMAL(3,1), review TEXT, streaming VARCHAR(255))"); @@ -27,30 +26,12 @@ try { } catch (PDOException $e) { die(json_encode(["error" => "Connexion BDD échouée"])); } // --- Fonctions Utilitaires --- -// Récupère le token d'authentification envoyé par le client, en tolérant les -// configurations Apache/WAMP qui ne transmettent pas HTTP_AUTHORIZATION à PHP -// par défaut (l'en-tête est bien envoyé par le navigateur, mais Apache le -// "mange" avant qu'il n'atteigne $_SERVER, sauf si CGIPassAuth est activé). function getAuthToken() { - if (!empty($_SERVER['HTTP_AUTHORIZATION'])) { - return $_SERVER['HTTP_AUTHORIZATION']; - } - // Cas fréquent avec RewriteRule / certains proxys : le header est déplacé ici - if (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { - return $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; - } - // Filet de sécurité générique : relire les en-têtes bruts de la requête + if (!empty($_SERVER['HTTP_AUTHORIZATION'])) return $_SERVER['HTTP_AUTHORIZATION']; + if (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) return $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; if (function_exists('getallheaders')) { foreach (getallheaders() as $name => $value) { - if (strcasecmp($name, 'Authorization') === 0) { - return $value; - } - } - } elseif (function_exists('apache_request_headers')) { - foreach (apache_request_headers() as $name => $value) { - if (strcasecmp($name, 'Authorization') === 0) { - return $value; - } + if (strcasecmp($name, 'Authorization') === 0) return $value; } } return ''; @@ -60,7 +41,6 @@ function checkAuth($pdo) { if ($pdo->query("SELECT COUNT(*) FROM users")->fetchColumn() == 0) return true; $token = getAuthToken(); if ($token !== md5(ENCRYPTION_KEY . 'session')) { - error_log("Auth: ❌ Token invalide ou absent (HTTP_AUTHORIZATION reçu: " . (empty($token) ? "VIDE — vérifier CGIPassAuth/config Apache" : "présent mais différent") . ")"); http_response_code(403); exit; } } @@ -84,13 +64,6 @@ function getTmdbApiKey($pdo) { return $row ? decryptData($row['key_value']) : 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 = 10, $headers = []) { $ch = curl_init($url); curl_setopt_array($ch, [ @@ -106,26 +79,6 @@ function httpGet($url, $timeout = 10, $headers = []) { return ($code === 200) ? $res : null; } -// Vérifie qu'une URL d'image répond bien en 200 (utilisé pour valider les URLs -// de couverture "devinées" à partir d'un ID, sans avoir à parser du HTML fragile) -function urlExists($url) { - if (empty($url)) return false; - $ch = curl_init($url); - curl_setopt_array($ch, [ - CURLOPT_NOBODY => true, // requête HEAD - CURLOPT_RETURNTRANSFER => true, - CURLOPT_TIMEOUT => 8, - CURLOPT_CONNECTTIMEOUT => 5, - CURLOPT_SSL_VERIFYPEER => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0', - ]); - curl_exec($ch); - $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - return $code === 200; -} - function removeAccentsForUrl($str) { $str = str_replace(['œ', 'Œ'], ['oe', 'OE'], $str); $str = str_replace(['æ', 'Æ'], ['ae', 'AE'], $str); @@ -134,7 +87,6 @@ function removeAccentsForUrl($str) { return $str; } - function cleanTitle($title) { $clean = preg_replace('/\s*[\[\(].*?[\]\)]\s*/', '', $title); $clean = preg_replace('/\s*-\s*(Édition|Edition|Collector|Simple|Spéciale|Digibook|Ultimate|Intégrale|Combo|SteelBook|Boîtier).*$/i', '', $clean); @@ -152,276 +104,137 @@ 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) { - // Recherche la position du premier guillemet (") - $firstQuote = strpos($title, '"'); - // Recherche la position du dernier guillemet (") - $lastQuote = strrpos($title, '"'); - - // Si on a trouvé au moins deux guillemets différents - if ($firstQuote !== false && $lastQuote !== false && $firstQuote !== $lastQuote) { - // On extrait et nettoie ce qu'il y a strictement entre les deux - return trim(substr($title, $firstQuote + 1, $lastQuote - $firstQuote - 1)); - } - - // Si pas de guillemets trouvés, on retourne le titre original (fallback de sécurité) - return trim($title); +function makeStableId($type, $title, $year) { + return (abs(crc32(strtolower(trim($type ?? '')) . '|' . strtolower(trim($title ?? '')) . '|' . trim($year ?? ''))) % 2000000000) + 100000000; } function emptyPhysicalResult() { return [ 'title' => '', 'publisher' => '', 'format' => '', 'length' => '', - 'number_of_discs' => 1, 'aspect_ratio' => '', 'year' => '' + 'number_of_discs' => 1, 'aspect_ratio' => '', 'year' => '', + 'director' => '', 'actors' => '', 'poster' => '', 'description' => '' ]; } -function makeStableId($type, $title, $year) { - return (abs(crc32(strtolower(trim($type ?? '')) . '|' . strtolower(trim($title ?? '')) . '|' . trim($year ?? ''))) % 2000000000) + 100000000; -} +// ── SCRAPPING FNAC ── +function fetchFromFnac($ean) { + $empty = emptyPhysicalResult(); + $url = "https://www.fnac.com/SearchResult/ResultList.aspx?Search=" . urlencode($ean); + $html = httpGet($url, 10); + if (!$html) return $empty; -// ── FONCTIONS API PHYSIQUE (UPCitemdb → UPCMDB fallback) ── -function throttleUpcLookup() { - static $last = 0; - $elapsed = microtime(true) - $last; - if ($last > 0 && $elapsed < 2) usleep((int)((2 - $elapsed) * 1000000)); - $last = microtime(true); -} - -function fetchPhysicalFromUpcitemdb($ean) { - $empty = ['title'=>'','publisher'=>'','format'=>'','length'=>'','number_of_discs'=>1,'aspect_ratio'=>'','year'=>'']; - $ean = preg_replace('/[^0-9]/', '', (string)$ean); - if (strlen($ean) < 8) return $empty; - throttleUpcLookup(); - $res = httpGet("https://api.upcitemdb.com/prod/trial/lookup?upc=" . urlencode($ean), 10); - if (!$res) return $empty; - $data = json_decode($res, true); - if (empty($data['items'][0])) return $empty; - - $item = $data['items'][0]; - $raw = $item['title'] ?? ''; - $clean = cleanUpcTitle($raw) ?: $raw; - - return [ - 'title' => $clean, - 'publisher' => trim($item['brand'] ?? $item['manufacturer'] ?? ''), - 'format' => detectFormat($raw), - 'number_of_discs' => parseDiscCountFromTitle($raw), - 'aspect_ratio' => '', - 'year' => '', - 'length' => '' - ]; -} - -function fetchFromUpcIndex($ean) { - $url = "https://www.upcindex.com/" . urlencode($ean); - $html = httpGet($url, 15); - - if ($html) { - // 1. Priorité absolue : la balise meta twitter:title qui contient le vrai titre propre - // Exemple : - if (preg_match('/]*name="twitter:title"[^>]*content="([^"]+)"/i', $html, $m)) { - // html_entity_decode gère les apostrophes encodées (') - return html_entity_decode(trim($m[1]), ENT_QUOTES | ENT_HTML5, 'UTF-8'); - } - - // 2. Alternative : on cherche dans les données structurées JSON-LD Schema.org - // Exemple : "name":"L'Arme fatale - \u00c9dition Sp\u00e9ciale" - if (preg_match('/"@type":"Product"[^}]+?"name":"([^"]+)"/is', $html, $m)) { - // json_decode permet de transformer proprement les \u00c9 en é, etc. - return json_decode('"' . $m[1] . '"'); - } - - // 3. Fallback sur le h1 (qui contient souvent le titre sale) - if (preg_match('/
([^<]+)<\/PRE>/is', $html, $m)) {
$idmc = trim($m[1]);
@@ -668,10 +301,7 @@ function fetchFromMovieCovers($title, $year = '') {
if ($idmc) {
$idmc_encoded = urlencode($idmc);
- $hd_urls = [
- "https://moviecovers.com/DATA/zipcache/{$idmc_encoded}.jpg",
- "https://moviecovers.com/getjpg.html/{$idmc_encoded}.jpg"
- ];
+ $hd_urls = ["https://moviecovers.com/DATA/zipcache/{$idmc_encoded}.jpg", "https://moviecovers.com/getjpg.html/{$idmc_encoded}.jpg"];
foreach ($hd_urls as $hd_url) {
$img_data = httpGet($hd_url, 5);
if ($img_data && strlen($img_data) > 5000) {
@@ -683,7 +313,6 @@ function fetchFromMovieCovers($title, $year = '') {
if (empty($empty['poster'])) {
if (preg_match('/
]*title="Recto[^"]*"[^>]*src="([^"]+)"/i', $html, $m)) $empty['poster'] = $m[1];
- elseif (preg_match('/
]*src="([^"]+)"[^>]*title="Recto/i', $html, $m)) $empty['poster'] = $m[1];
elseif (preg_match('/]*property="og:image"[^>]*content="([^"]+)"/i', $html, $m)) $empty['poster'] = $m[1];
}
@@ -696,79 +325,33 @@ function fetchFromMovieCovers($title, $year = '') {
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);
- $string = preg_replace('#&([A-za-z]{2})(?:lig);#', '\1', $string);
- $string = preg_replace('#&[^;]+;#', '', $string);
- return $string;
-}
-
-function fetchAndDownloadMovieCovers($title, $ean) {
- if (empty($title) || empty($ean)) return null;
-
- // Création du dossier d'images s'il n'existe pas
- $dir = __DIR__ . '/../assets/img/covers';
- if (!is_dir($dir)) mkdir($dir, 0777, true);
-
- $localPath = "assets/img/covers/{$ean}.jpg";
- $fullPath = __DIR__ . '/../' . $localPath;
-
- // Si la jaquette a déjà été téléchargée lors d'un précédent import, on la réutilise direct
- if (file_exists($fullPath)) return $localPath;
-
- // Nettoyage du titre pour la recherche MovieCovers
- $cleanTitle = removeAccents(trim(preg_replace('/(blu-ray|bluray|dvd|4k|ultra hd).*$/i', '', $title)));
- $searchUrl = "https://www.moviecovers.com/multicrit.html?titre=" . urlencode(utf8_decode($cleanTitle));
-
- $html = file_get_contents($searchUrl, false, stream_context_create([
- 'http' => ['method' => 'GET', 'header' => "Referer: https://www.moviecovers.com/\r\nUser-Agent: Mozilla/5.0\r\n", 'timeout' => 10]
- ]));
- if (!$html) return null;
-
- $filmHtml = null;
- if (preg_match('/href=["\']?\/?(film\/titre_[^"\']+)\.html["\']?/i', $html, $m)) {
- $filmUrl = "https://www.moviecovers.com/" . $m[1] . ".html";
- usleep(500000); // Pause de 0.5s pour ne pas spammer le serveur
- $filmHtml = file_get_contents($filmUrl, false, stream_context_create([
- 'http' => ['method' => 'GET', 'header' => "Referer: https://www.moviecovers.com/\r\nUser-Agent: Mozilla/5.0\r\n", 'timeout' => 10]
- ]));
- } else if (stripos($html, 'IDMC') !== false) {
- $filmHtml = $html; // Accès direct
+// ── AGGREGATEUR PHYSIQUE (FNAC -> BLU-RAY.COM) ──
+function fetchPhysicalByEan($ean, $pdo = null) {
+ // 1. Tenter la FNAC (Titre français garanti)
+ $fnacData = fetchFromFnac($ean);
+ if (!empty($fnacData['title'])) {
+ $res = $fnacData;
+ $mc = fetchFromMovieCovers($res['title'], $res['year']);
+ if (!empty($mc['poster'])) $res['poster'] = $mc['poster'];
+ if (!empty($mc['director'])) $res['director'] = $mc['director'];
+ if (!empty($mc['actors'])) $res['actors'] = $mc['actors'];
+ if (!empty($mc['description'])) $res['description'] = $mc['description'];
+ return $res;
}
- if (!$filmHtml) return null;
-
- // Extraction de l'identifiant IDMC
- $idmc = null;
- if (preg_match('/IDMC.*?([^<]+)<\/PRE>/is', $filmHtml, $m)) {
- $idmc = trim($m[1]);
- } elseif (preg_match('/ ['method' => 'GET', 'header' => "Referer: https://www.moviecovers.com/\r\nUser-Agent: Mozilla/5.0\r\n", 'timeout' => 15]
- ]));
-
- // Si l'image est valide et fait plus de 5Ko
- if ($imgData && strlen($imgData) > 5000) {
- file_put_contents($fullPath, $imgData);
- return $localPath;
- }
- }
- }
- return null;
+ return emptyPhysicalResult();
}
// ── ROUTEUR PRINCIPAL ──
@@ -800,90 +383,60 @@ switch ($action) {
echo json_encode(["success" => true]);
break;
-// ── CONFIGURATION ──
-case 'get_config_keys':
- $keys = ['tmdb_api_key', 'upcmdb_api_key'];
- $config = [];
- foreach ($keys as $k) {
- $stmt = $pdo->prepare("SELECT key_value FROM config WHERE key_name = ?");
- $stmt->execute([$k]);
- $row = $stmt->fetch();
- $config[$k] = $row ? decryptData($row['key_value']) : '';
- }
- echo json_encode($config);
- break;
+ case 'get_config_keys':
+ $stmt = $pdo->prepare("SELECT key_value FROM config WHERE key_name = 'tmdb_api_key'");
+ $stmt->execute(); $row = $stmt->fetch();
+ echo json_encode(['tmdb_api_key' => $row ? decryptData($row['key_value']) : '']);
+ break;
-case 'save_config':
- checkAuth($pdo);
- $name = $data['key_name'] ?? '';
- $val = trim($data['key_value'] ?? '');
- if (!in_array($name, ['tmdb_api_key', 'upcmdb_api_key'])) {
- http_response_code(400); echo json_encode(["error" => "Clé invalide."]); break;
- }
- if (empty($val)) break; // Ne rien écraser si vide
- $stmt = $pdo->prepare("REPLACE INTO config (key_name, key_value) VALUES (?, ?)");
- $stmt->execute([$name, encryptData($val)]);
- echo json_encode(["success" => true]);
- break;
-
-case 'get_films':
- // 1. Récupération des critiques
- $critiques = $pdo->query("SELECT id, title, year, director, poster, rating, review, NULL AS description, streaming, 'critique' AS type, NULL AS format FROM critiques ORDER BY id DESC")->fetchAll();
+ case 'save_config':
+ checkAuth($pdo);
+ $name = $data['key_name'] ?? '';
+ $val = trim($data['key_value'] ?? '');
+ if ($name !== 'tmdb_api_key' || empty($val)) { http_response_code(400); echo json_encode(["error" => "Clé invalide."]); break; }
+ $stmt = $pdo->prepare("REPLACE INTO config (key_name, key_value) VALUES (?, ?)");
+ $stmt->execute([$name, encryptData($val)]);
+ echo json_encode(["success" => true]);
+ break;
- // CORRECTION : Utilisation du & pour modifier le tableau original
+ case 'get_films':
+ $critiques = $pdo->query("SELECT id, title, year, director, poster, rating, review, NULL AS description, streaming, 'critique' AS type, NULL AS format FROM critiques ORDER BY id DESC")->fetchAll();
foreach ($critiques as $row) {
if ($row['rating'] !== null) {
$ratingVal = (float)$row['rating'];
$row['rating'] = ($ratingVal == floor($ratingVal)) ? (int)$ratingVal : $ratingVal;
}
}
- unset($row); // Sécurité pour libérer la référence
-
- // 2. Récupération de la vidéothèque
+ unset($row);
$videotheque = $pdo->query("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")->fetchAll();
-
- // LOGGING : Utile pour voir dans php_errors.log si les tables sont vides
- error_log("API get_films - Critiques trouvées: " . count($critiques) . " | Vidéothèque: " . count($videotheque));
-
- echo json_encode([
- 'critique' => $critiques,
- 'videotheque' => $videotheque
- ]);
+ echo json_encode(['critique' => $critiques, 'videotheque' => $videotheque]);
break;
-
+
case 'save_film':
checkAuth($pdo);
$type = $data['type'] ?? 'critique';
$id = !empty($data['id']) ? $data['id'] : makeStableId($type, $data['title'] ?? '', $data['year'] ?? '0000');
- if ($type === 'critique' && (empty($data['director']) || empty($data['poster']))) {
- $tmdbData = fetchTmdbPosterAndSynopsis($data['title'] ?? '', $data['year'] ?? '', $pdo);
- if ($tmdbData) {
+ if ($type === 'critique') {
+ if (empty($data['director']) || empty($data['poster'])) {
+ $tmdbData = fetchTmdbPosterAndSynopsis($data['title'] ?? '', $data['year'] ?? '', $pdo);
if (empty($data['director'])) $data['director'] = $tmdbData['director'];
if (empty($data['poster'])) $data['poster'] = $tmdbData['poster'];
}
- }
-
- if ($type === 'critique') {
- $streaming = $data['streaming'] ?? '';
- if (empty($streaming)) $streaming = 'Support physique / Cinéma';
+ $streaming = $data['streaming'] ?? 'Support physique / Cinéma';
$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 {
if (empty($data['poster']) && !empty($data['title'])) {
$tmdbData = fetchTmdbPosterAndSynopsis($data['title'], $data['year'] ?? '', $pdo);
-
- if ($tmdbData['poster'] !== 'assets/img/default_physical_media.jpg') {
- $data['poster'] = $tmdbData['poster'];
- }
+ if ($tmdbData['poster'] !== 'assets/img/default_physical_media.jpg') $data['poster'] = $tmdbData['poster'];
if (empty($data['description'])) $data['description'] = $tmdbData['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'];
if (empty($data['format'])) $data['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'] ?? '']);
@@ -906,203 +459,107 @@ case 'get_films':
if (!empty($ids)) { $placeholders = implode(',', array_fill(0, count($ids), '?')); $stmt = $pdo->prepare("DELETE FROM $table WHERE id IN ($placeholders)"); $stmt->execute($ids); echo json_encode(["success" => true]); }
else { http_response_code(400); echo json_encode(["success" => false, "error" => "Aucun élément sélectionné."]); }
break;
-
- case 'add_item_by_ean':
- $data = json_decode(file_get_contents("php://input"), true);
- $ean = preg_replace('/[^0-9]/', '', $data['ean'] ?? '');
-
- if (strlen($ean) < 8) {
- echo json_encode(["success" => false, "error" => "EAN invalide"]);
- exit;
- }
-
- error_log("--- DÉBUT RECHERCHE EAN : $ean ---");
+
+ case 'add_item_by_ean':
+ $ean = preg_replace('/[^0-9]/', '', $data['ean'] ?? '');
+ if (strlen($ean) < 8) { echo json_encode(["success" => false, "error" => "EAN invalide"]); exit; }
+
+ $physicalData = fetchPhysicalByEan($ean, $pdo);
+ $rawTitle = $physicalData['title'] ?? '';
+
+ $tmdbData = fetchTmdbPosterAndSynopsis($rawTitle, $physicalData['year'] ?? '', $pdo);
+
+ $finalTitle = !empty($tmdbData['title']) ? $tmdbData['title'] : $rawTitle;
+ $poster = ($tmdbData['poster'] !== 'assets/img/default_physical_media.jpg') ? $tmdbData['poster'] : ($physicalData['poster'] ?? '');
+
+ $year = $tmdbData['year'] ?? $physicalData['year'] ?? '';
+ $id = makeStableId('videotheque', $finalTitle, $year);
+
+ $stmt = $pdo->prepare("INSERT INTO videotheque (id, title, year, poster, description, director, actors, ean_isbn13, format, length, publisher, number_of_discs, aspect_ratio) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE title=VALUES(title), year=VALUES(year), poster=IF(VALUES(poster)!='', VALUES(poster), poster), description=IF(VALUES(description)!='', VALUES(description), description), director=IF(VALUES(director)!='', VALUES(director), director), actors=IF(VALUES(actors)!='', VALUES(actors), actors)");
+ $stmt->execute([
+ $id, $finalTitle, $year, $poster,
+ $tmdbData['description'] ?? $physicalData['description'] ?? '',
+ $tmdbData['director'] ?: ($physicalData['director'] ?? ''),
+ $tmdbData['actors'] ?: ($physicalData['actors'] ?? ''),
+ $ean, $physicalData['format'] ?? detectFormat($rawTitle),
+ $tmdbData['length'] ?: ($physicalData['length'] ?? ''),
+ $physicalData['publisher'] ?? '', $physicalData['number_of_discs'] ?? 1, $physicalData['aspect_ratio'] ?? ''
+ ]);
- // 1. Récupérer les données physiques
- $physicalData = fetchPhysicalByEan($ean, $pdo);
- $rawTitle = $physicalData['title'] ?? '';
- $directorFromEan = $physicalData['director'] ?? '';
-
- error_log("UPCitemdb titre brut : '$rawTitle'");
- if ($directorFromEan) error_log("UPCitemdb réalisateur : '$directorFromEan'");
-
- // 2. Chercher sur TMDB avec validation par réalisateur
- $tmdbData = smartSearchTmdbWithDirector(
- $rawTitle,
- $directorFromEan,
- $physicalData['year'] ?? '',
- $pdo
- );
-
- // 3. Fusionner les données
- $finalTitle = !empty($tmdbData['title']) ? $tmdbData['title'] : $rawTitle;
- $poster = ($tmdbData['poster'] !== 'assets/img/default_physical_media.jpg')
- ? $tmdbData['poster']
- : ($physicalData['poster'] ?? '');
-
- error_log("=== RÉSULTAT FINAL ===");
- error_log("Titre FR : $finalTitle");
- error_log("Réalisateur : " . ($tmdbData['director'] ?: $directorFromEan));
-
- // 4. Insertion en base
- $year = $tmdbData['year'] ?? $physicalData['year'] ?? '';
- $id = makeStableId('videotheque', $finalTitle, $year);
-
- $stmt = $pdo->prepare("INSERT INTO videotheque
- (id, title, year, poster, description, director, actors, ean_isbn13, format, length, publisher, number_of_discs, aspect_ratio)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- ON DUPLICATE KEY UPDATE
- title=VALUES(title), year=VALUES(year), poster=IF(VALUES(poster)!='', VALUES(poster), poster),
- description=IF(VALUES(description)!='', VALUES(description), description),
- director=IF(VALUES(director)!='', VALUES(director), director),
- actors=IF(VALUES(actors)!='', VALUES(actors), actors)");
-
- $stmt->execute([
- $id, $finalTitle, $year, $poster,
- $tmdbData['description'] ?? '',
- $tmdbData['director'] ?: $directorFromEan,
- $tmdbData['actors'] ?? '',
- $ean,
- $physicalData['format'] ?? detectFormat($rawTitle),
- $tmdbData['length'] ?? '',
- $physicalData['publisher'] ?? '',
- $physicalData['number_of_discs'] ?? 1,
- $physicalData['aspect_ratio'] ?? ''
- ]);
-
- echo json_encode([
- "success" => true,
- "title" => $finalTitle,
- "director" => $tmdbData['director'] ?: $directorFromEan,
- "year" => $year
- ]);
- break;
-
-case 'import_batch':
- checkAuth($pdo);
- $data = json_decode(file_get_contents("php://input"), true);
- $type = $data['type'] ?? '';
- $items = $data['items'] ?? [];
- $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)");
-
- foreach ($items as $item) {
- // ✅ ON NE RÉCUPÈRE QUE L'EAN
- $ean = preg_replace('/[^0-9]/', '', (string)($item['ean'] ?? ''));
+ echo json_encode(["success" => true, "title" => $finalTitle, "director" => $tmdbData['director'] ?: ($physicalData['director'] ?? ''), "year" => $year]);
+ break;
+
+ case 'import_batch':
+ checkAuth($pdo);
+ $type = $data['type'] ?? '';
+ $items = $data['items'] ?? [];
+ $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)");
- if (strlen($ean) < 8) {
- $skipped++;
- continue;
+ foreach ($items as $item) {
+ $ean = preg_replace('/[^0-9]/', '', (string)($item['ean'] ?? ''));
+ if (strlen($ean) < 8) { $skipped++; continue; }
+
+ $physicalData = fetchPhysicalByEan($ean, $pdo);
+ $title = $physicalData['title'] ?? '';
+ $year = $physicalData['year'] ?? '';
+ $format = $physicalData['format'] ?: detectFormat($title);
+ $publisher = $physicalData['publisher'] ?? '';
+ $discs = $physicalData['number_of_discs'] ?: 1;
+ $aspect = $physicalData['aspect_ratio'] ?? '';
+ $length = $physicalData['length'] ?? '';
+ $poster = $physicalData['poster'] ?? '';
+ $desc = $physicalData['description'] ?? '';
+ $director = $physicalData['director'] ?? '';
+ $actors = $physicalData['actors'] ?? '';
+
+ if (empty($poster) || empty($director) || empty($actors) || empty($desc) || empty($title)) {
+ $tmdb = fetchTmdbPosterAndSynopsis($title, $year, $pdo);
+ if (empty($title)) $title = $tmdb['title'] ?? "EAN: $ean";
+ 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'];
+ }
+
+ if (empty($title)) { $skipped++; continue; }
+ $id = makeStableId('videotheque', $title, $year);
+ $stmt->execute([$id, $title, $year, $format, $poster, $ean, $desc, $length, $discs, $aspect, $actors, $publisher, $director]);
+ $imported++;
}
-
- // 1. BLU-RAY.COM (Métadonnées physiques et affiche de base)
- $blurayData = fetchFromBlurayCom($ean);
- if (!is_array($blurayData)) $blurayData = [];
-
- $title = $blurayData['title'] ?? '';
- $year = $blurayData['year'] ?? '';
- $format = $blurayData['format'] ?: detectFormat($title);
- $publisher = $blurayData['publisher'] ?? '';
- $discs = $blurayData['number_of_discs'] ?: 1;
- $aspect = $blurayData['aspect_ratio'] ?? '';
- $length = $blurayData['length'] ?? '';
- $poster = $blurayData['poster'] ?? '';
- $desc = $blurayData['description'] ?? '';
- $director = $blurayData['director'] ?? '';
- $actors = $blurayData['actors'] ?? '';
-
- // 2. MOVIECOVERS (Pour l'affiche HD physique et complément d'infos)
- if (!empty($title)) {
- $mcData = fetchFromMovieCovers($title, $year);
- if (!is_array($mcData)) $mcData = [];
-
- // Priorité à l'affiche HD de MovieCovers
- if (!empty($mcData['poster'])) $poster = $mcData['poster'];
- if (empty($director) && !empty($mcData['director'])) $director = $mcData['director'];
- if (empty($actors) && !empty($mcData['actors'])) $actors = $mcData['actors'];
- if (empty($desc) && !empty($mcData['description'])) $desc = $mcData['description'];
- if (empty($length) && !empty($mcData['length'])) $length = $mcData['length'];
- if (empty($year) && !empty($mcData['year'])) $year = $mcData['year'];
+ } else {
+ $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;
+ $year = !empty($rowData['publish_date']) ? substr($rowData['publish_date'], 0, 4) : ($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'] ?? $rowData['description'] ?? '';
+ $director = ''; $poster = 'assets/img/default_physical_media.jpg'; $streaming = 'Support physique / Cinéma';
+ $tmdbData = fetchTmdbPosterAndSynopsis($title, $year, $pdo);
+ 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'];
+ }
+ $stmtCritiques->execute([$id, $title, $year, $director, $poster, $rating, $review, $streaming]);
+ $imported++;
}
-
- // 3. FALLBACK TMDB (Si des données manquent encore)
- if (empty($poster) || empty($director) || empty($actors) || empty($desc) || empty($title)) {
- $tmdb = fetchTmdbPosterAndSynopsis($title, $year, $pdo);
- if (empty($title)) $title = $tmdb['title'] ?? "EAN: $ean";
- 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'];
- }
-
- if (empty($title)) {
- $skipped++;
- continue;
- }
-
- $id = makeStableId('videotheque', $title, $year);
- $stmt->execute([$id, $title, $year, $format, $poster, $ean, $desc, $length, $discs, $aspect, $actors, $publisher, $director]);
- $imported++;
- }
- } else {
- // Import critiques (inchangé)
- $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;
-
- $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'] ?? $rowData['description'] ?? '';
-
- $director = '';
- $poster = 'assets/img/default_physical_media.jpg';
- $streaming = 'Support physique / Cinéma';
-
- $tmdbData = fetchTmdbPosterAndSynopsis($title, $year, $pdo);
- 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'];
- }
-
- $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();
+ http_response_code(500);
+ echo json_encode(["success" => false, "error" => $e->getMessage()]);
}
- $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;
+ break;
}
\ No newline at end of file