PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]); $pdo->exec("CREATE TABLE IF NOT EXISTS users (id INT PRIMARY KEY, username VARCHAR(50) NOT NULL, password_hash VARCHAR(255) NOT NULL)"); $pdo->exec("CREATE TABLE IF NOT EXISTS config (key_name VARCHAR(50) PRIMARY KEY, key_value TEXT NOT NULL)"); $pdo->exec("CREATE TABLE IF NOT EXISTS critiques (id BIGINT PRIMARY KEY, title VARCHAR(255) NOT NULL, year VARCHAR(10), director VARCHAR(255), poster TEXT, rating DECIMAL(3,1) DEFAULT 3.0, review TEXT, streaming VARCHAR(255))"); $pdo->exec("ALTER TABLE critiques MODIFY COLUMN rating DECIMAL(3,1) DEFAULT 3.0;"); // 🔥 CRÉATION DE LA TABLE VIDÉOTHÈQUE AVEC LA COLONNE ACTORS $pdo->exec("CREATE TABLE IF NOT EXISTS videotheque (id BIGINT PRIMARY KEY, title VARCHAR(255) NOT NULL, year VARCHAR(10), director VARCHAR(255), poster TEXT, format VARCHAR(50), length VARCHAR(50), publisher VARCHAR(255), ean_isbn13 VARCHAR(50), number_of_discs INT DEFAULT 1, aspect_ratio VARCHAR(50), description TEXT, actors TEXT)"); try { $pdo->exec("ALTER TABLE videotheque ADD COLUMN actors TEXT AFTER description"); } catch (\Exception $e) {} } catch (\PDOException $e) { echo json_encode(["error" => "Erreur BDD : " . $e->getMessage()]); exit; } // ── FONCTIONS UTILITAIRES ── function makeStableId($type, $title, $year) { // 🔥 Isolation stricte : le type est inclus dans le hash pour éviter les collisions entre les deux tables return (abs(crc32(strtolower(trim($type ?? '')) . '|' . strtolower(trim($title ?? '')) . '|' . trim($year ?? ''))) % 2000000000) + 100000000; } function checkAuth($pdo) { if ($pdo->query("SELECT COUNT(*) FROM users")->fetchColumn() == 0) return true; $token = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; if (empty($token) && function_exists('apache_request_headers')) { $headers = apache_request_headers(); $token = $headers['Authorization'] ?? $headers['authorization'] ?? ''; } if ($token !== md5(ENCRYPTION_KEY . 'session')) { http_response_code(403); echo json_encode(["error" => "Accès interdit."]); exit; } } function encryptData($data) { $iv = openssl_random_pseudo_bytes(16); $key = hash('sha256', ENCRYPTION_KEY, true); return base64_encode(openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv) . '::' . $iv); } function decryptData($str) { $decoded = base64_decode($str); if (strpos($decoded, '::') === false) return null; list($enc, $iv) = explode('::', $decoded, 2); return openssl_decrypt($enc, 'AES-256-CBC', hash('sha256', ENCRYPTION_KEY, true), OPENSSL_RAW_DATA, substr($iv, 0, 16)); } function getTmdbApiKey($pdo) { $stmt = $pdo->prepare("SELECT key_value FROM config WHERE key_name = 'tmdb_api_key'"); $stmt->execute(); $row = $stmt->fetch(); return $row ? decryptData($row['key_value']) : null; } function httpGet($url, $timeout = 5) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $timeout, CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', CURLOPT_SSL_VERIFYPEER => false ]); $res = curl_exec($ch); curl_close($ch); return $res ?: null; } 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); $clean = preg_replace('/(blu-ray|bluray|dvd|4k|ultra hd|combo|vhs|bdrip).*$/i', '', $clean); return trim(preg_replace('/\s{2,}/', ' ', $clean)); } function detectFormat($title, $desc = '') { $t = strtoupper($title . ' ' . $desc); if (strpos($t, '4K') !== false || strpos($t, 'UHD') !== false) return '4K Ultra HD'; if (strpos($t, 'BLU-RAY') !== false || strpos($t, 'BLURAY') !== false) return 'Blu-ray'; if (strpos($t, 'DVD') !== false) return 'DVD'; if (strpos($t, 'VHS') !== false) return 'VHS'; if (strpos($t, 'COFFRET') !== false || strpos($t, 'TRILOGIE') !== false) return 'Coffret'; return 'Blu-ray'; } function extractYear($dateStr) { if (preg_match('/(\d{4})/', $dateStr, $m)) return $m[1]; return ''; } function fetchDVDFr($ean, $pdo) { if (empty($ean) || strlen($ean) < 8) return null; $ua = 'MonCinema/1.0 (collection privée; contact@moncineapp.fr)'; // Étape 1 : recherche par gencode $searchUrl = "https://www.dvdfr.com/api/search.php?gencode=" . urlencode($ean); $ch = curl_init($searchUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_CONNECTTIMEOUT => 3, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_USERAGENT => $ua, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTPHEADER => ['Accept: application/xml, text/xml, */*'], ]); $res = curl_exec($ch); curl_close($ch); if (!$res) return null; libxml_use_internal_errors(true); $xml = simplexml_load_string($res); libxml_clear_errors(); if (!$xml || !isset($xml->dvd[0]->id)) return null; $dvdId = (string)$xml->dvd[0]->id; if (empty($dvdId)) return null; // Étape 2 : fiche complète $ficheUrl = "https://www.dvdfr.com/api/dvd.php?id=" . urlencode($dvdId); $ch2 = curl_init($ficheUrl); curl_setopt_array($ch2, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_CONNECTTIMEOUT => 3, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_USERAGENT => $ua, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTPHEADER => ['Accept: application/xml, text/xml, */*'], ]); $res2 = curl_exec($ch2); curl_close($ch2); if (!$res2) return null; libxml_use_internal_errors(true); $fiche = simplexml_load_string($res2); libxml_clear_errors(); if (!$fiche || !isset($fiche->dvd[0])) return null; $dvd = $fiche->dvd[0]; $poster = ''; if (isset($dvd->cover)) $poster = (string)$dvd->cover; if (empty($poster) && isset($dvd->covers->cover[0])) $poster = (string)$dvd->covers->cover[0]; $result = [ 'poster' => $poster, 'publisher' => isset($dvd->editeur) ? (string)$dvd->editeur : '', 'format' => isset($dvd->type) ? (string)$dvd->type : '', 'length' => isset($dvd->duree) ? (string)$dvd->duree : '', 'aspect' => isset($dvd->formatimage) ? (string)$dvd->formatimage : '', 'discs' => isset($dvd->nbdisques) ? (string)$dvd->nbdisques : '', ]; return !empty($result['poster']) || !empty($result['publisher']) ? $result : null; } function fetchTMDBFull($title, $year, $apiKey, $pdo) { if (empty($apiKey) || empty($title)) return null; $cleanTitle = cleanTitle($title); // 1. Recherche avec l'année $searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$apiKey}&query=" . urlencode($cleanTitle) . "&year={$year}&language=fr-FR"; $searchRes = httpGet($searchUrl, 5); $searchData = $searchRes ? json_decode($searchRes, true) : []; // 2. Fallback sans l'année if (empty($searchData['results'])) { $searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$apiKey}&query=" . urlencode($cleanTitle) . "&language=fr-FR"; $searchRes = httpGet($searchUrl, 5); $searchData = $searchRes ? json_decode($searchRes, true) : []; } if (empty($searchData['results'])) return null; $movieId = $searchData['results'][0]['id']; // 3. Détails complets $detailsUrl = "https://api.themoviedb.org/3/movie/{$movieId}?api_key={$apiKey}&append_to_response=credits,watch/providers&language=fr-FR"; $detailsRes = httpGet($detailsUrl, 5); if (!$detailsRes) return null; $details = json_decode($detailsRes, true); // Extraction Réalisateurs $director = ''; if (!empty($details['credits']['crew'])) { $directorsList = []; foreach ($details['credits']['crew'] as $crew) { if ($crew['job'] === 'Director') { $directorsList[] = $crew['name']; } } $director = implode(', ', $directorsList); } // Extraction Acteurs (Top 4) $cast = []; if (!empty($details['credits']['cast'])) { $topCast = array_slice($details['credits']['cast'], 0, 4); foreach ($topCast as $actor) { $cast[] = $actor['name']; } } // Synopsis $overview = $details['overview'] ?? ''; // Streaming $streaming = ''; $frProviders = $details['watch/providers']['results']['FR'] ?? []; $platforms = []; if (!empty($frProviders['flatrate'])) { foreach ($frProviders['flatrate'] as $p) $platforms[] = $p['provider_name']; } if (empty($platforms)) { if (!empty($frProviders['rent'])) { foreach ($frProviders['rent'] as $p) $platforms[] = $p['provider_name'] . ' (loc.)'; } if (!empty($frProviders['buy'])) { foreach ($frProviders['buy'] as $p) $platforms[] = $p['provider_name'] . ' (achat)'; } } if (!empty($platforms)) $streaming = implode(', ', array_unique($platforms)); return [ 'title' => $details['title'] ?? '', 'year' => !empty($details['release_date']) ? substr($details['release_date'], 0, 4) : '', 'director' => $director, 'poster' => !empty($details['poster_path']) ? "https://image.tmdb.org/t/p/w500" . $details['poster_path'] : '', 'length' => !empty($details['runtime']) ? $details['runtime'] . ' min' : '', 'streaming' => $streaming, 'overview' => $overview, 'cast' => $cast ]; } // ── ROUTEUR PRINCIPAL ── $action = $_GET['action'] ?? ''; $data = json_decode(file_get_contents('php://input'), true) ?? []; switch ($action) { case 'check_security_status': echo json_encode(["is_blank" => ($pdo->query("SELECT COUNT(*) FROM users")->fetchColumn() == 0)]); break; case 'login': if ($pdo->query("SELECT COUNT(*) FROM users")->fetchColumn() == 0) { echo json_encode(["success" => true, "token" => md5(ENCRYPTION_KEY . 'session'), "blank" => true]); } else { $stmt = $pdo->prepare("SELECT password_hash FROM users WHERE username = 'admin'"); $stmt->execute(); $user = $stmt->fetch(); if ($user && password_verify($data['password'] ?? '', $user['password_hash'])) { echo json_encode(["success" => true, "token" => md5(ENCRYPTION_KEY . 'session'), "blank" => false]); } else { http_response_code(401); echo json_encode(["error" => "Mot de passe incorrect."]); } } break; case 'setup_admin': case 'update_password': checkAuth($pdo); $pwd = $data['password'] ?? $data['new_password'] ?? ''; $stmt = $pdo->prepare("REPLACE INTO users (id, username, password_hash) VALUES (1, 'admin', :pass)"); $stmt->execute([':pass' => password_hash($pwd, PASSWORD_BCRYPT)]); echo json_encode(["success" => true]); break; case 'get_config_keys': checkAuth($pdo); $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 ? '••••••••' : '']); break; case 'save_config': checkAuth($pdo); $keyName = $data['key_name'] ?? ''; $keyValue = $data['key_value'] ?? ''; if ($keyName === 'tmdb_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]); } else { http_response_code(400); echo json_encode(["error" => "Données invalides."]); } break; case 'get_films': // Fusion parfaite des deux tables avec des colonnes neutres (NULL) pour harmoniser le flux $sql = " SELECT id, title, year, director, poster, rating, review, NULL AS description, streaming, 'critique' AS type FROM critiques UNION ALL SELECT id, title, year, director, poster, NULL AS rating, NULL AS review, description, NULL AS streaming, 'videotheque' AS type FROM videotheque ORDER BY id DESC "; $result = $pdo->query($sql)->fetchAll(); echo json_encode($result); break; case 'search_ean_full': $ean = $_GET['ean'] ?? ''; if (!$ean) { echo json_encode(['error' => 'EAN manquant']); exit; } $result = [ 'ean' => $ean, 'title' => '', 'director' => '', 'year' => '', 'poster' => '', 'publisher' => '', 'format' => '', 'length' => '', 'number_of_discs' => 1, 'aspect_ratio' => '', 'actors' => '' ]; // 🔥 UNIQUEMENT DVDFr $dvdfrData = fetchDVDFr($ean, $pdo); if (!empty($dvdfrData)) { if (!empty($dvdfrData['poster'])) $result['poster'] = $dvdfrData['poster']; if (!empty($dvdfrData['publisher'])) $result['publisher'] = $dvdfrData['publisher']; if (!empty($dvdfrData['format'])) $result['format'] = $dvdfrData['format']; if (!empty($dvdfrData['length'])) $result['length'] = $dvdfrData['length']; if (!empty($dvdfrData['aspect'])) $result['aspect_ratio'] = $dvdfrData['aspect']; if (!empty($dvdfrData['discs'])) $result['number_of_discs'] = (int)$dvdfrData['discs']; } // TMDB pour le titre, réalisateur, année et acteurs $tmdbKey = getTmdbApiKey($pdo); if ($tmdbKey && !empty($result['publisher'])) { $tmdbData = fetchTMDBFull($result['publisher'], '', $tmdbKey, $pdo); if ($tmdbData) { if (!empty($tmdbData['title'])) $result['title'] = $tmdbData['title']; if (!empty($tmdbData['year'])) $result['year'] = $tmdbData['year']; if (!empty($tmdbData['director'])) $result['director'] = $tmdbData['director']; if (!empty($tmdbData['cast'])) $result['actors'] = implode(', ', $tmdbData['cast']); } } echo json_encode(['success' => true, 'data' => $result]); break; case 'save_film': checkAuth($pdo); $type = $data['type'] ?? 'critique'; $id = !empty($data['id']) ? $data['id'] : makeStableId($type, $data['title'] ?? '', $data['year'] ?? '0000'); // 🔥 Récupération des données manquantes via TMDB uniquement (plus de UPC) if (empty($data['director']) || empty($data['poster'])) { $tmdbData = fetchTMDBFull($data['title'] ?? '', $data['year'] ?? '', getTmdbApiKey($pdo), $pdo); if ($tmdbData) { if (empty($data['director'])) $data['director'] = $tmdbData['director']; if (empty($data['poster'])) $data['poster'] = $tmdbData['poster']; if (empty($data['length']) && !empty($tmdbData['length'])) $data['length'] = $tmdbData['length']; } } if ($type === 'critique') { $streaming = $data['streaming'] ?? ''; if (empty($streaming)) $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 { // Remplacez votre bloc SQL dans 'save_film' par celui-ci : $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'] ?? '']); } echo json_encode(["success" => true]); break; case 'delete_film': checkAuth($pdo); $type = $_GET['type'] ?? 'critique'; $table = ($type === 'videotheque') ? 'videotheque' : 'critiques'; $id = $_GET['id'] ?? null; if (!$id) { http_response_code(400); echo json_encode(["error" => "ID manquant."]); break; } $stmt = $pdo->prepare("DELETE FROM $table WHERE id = ?"); $stmt->execute([$id]); echo json_encode(["success" => true]); break; case 'bulk_delete': checkAuth($pdo); $ids = $data['ids'] ?? []; $type = $data['type'] ?? 'critique'; $table = ($type === 'videotheque') ? 'videotheque' : 'critiques'; 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; // ── IMPORT PAR LOTS CSV (CROISEMENT UPC + TMDB) ── case 'import_batch': checkAuth($pdo); set_time_limit(0); $items = $data['items'] ?? []; $type = $data['type'] ?? 'videotheque'; $tmdbApiKey = getTmdbApiKey($pdo); $imported = 0; $debugLog = []; // 🔥 LOG DE DÉBOGAGE try { $pdo->beginTransaction(); foreach ($items as $index => $rowData) { $title = $rowData['title'] ?? $rowData['Name'] ?? $rowData['Title'] ?? 'Sans titre'; $publishDate = $rowData['publish_date'] ?? $rowData['Year'] ?? $rowData['year'] ?? $rowData['Date'] ?? ''; $year = extractYear($publishDate); $id = makeStableId($type, $title, $year); if ($type === 'videotheque') { // 🔥 EXTRACTION CORRECTE DES ACTEURS DEPUIS LE CSV $csvActors = ''; if (!empty($rowData['ensemble'])) { $csvActors = $rowData['ensemble']; } elseif (!empty($rowData['creators'])) { $csvActors = $rowData['creators']; } $actors = ''; if (!empty($csvActors)) { $actorsArray = array_map('trim', explode(',', $csvActors)); $actors = implode(', ', array_slice($actorsArray, 0, 4)); } // Normalisation EAN $ean = $rowData['ean_isbn13'] ?? $rowData['EAN'] ?? ''; if (!empty($ean)) { $eanFloat = floatval($ean); if ($eanFloat > 0) $ean = (string) round($eanFloat); $ean = preg_replace('/[^0-9]/', '', $ean); } $lengthRaw = $rowData['length'] ?? ''; $length = ''; if ($lengthRaw !== '' && $lengthRaw !== null) { $lengthVal = floatval($lengthRaw); if ($lengthVal > 0) $length = (string) round($lengthVal); } $discsRaw = $rowData['number_of_discs'] ?? ''; $discs = (is_numeric($discsRaw) && floatval($discsRaw) > 0) ? (int) round(floatval($discsRaw)) : 1; $description = $rowData['description'] ?? $rowData['Description'] ?? ''; $publisher = $rowData['publisher'] ?? ''; $aspect = $rowData['aspect_ratio'] ?? ''; $format = $rowData['format'] ?? detectFormat($title, $description); $poster = $rowData['poster'] ?? ''; $director = ''; // Remplacez votre bloc de gestion DVDFr actuel par ceci : $dvdfrData = fetchDVDFr($ean, $pdo); if (!empty($dvdfrData)) { if (empty($poster) && !empty($dvdfrData['poster'])) $poster = $dvdfrData['poster']; if (empty($format) && !empty($dvdfrData['format'])) $format = $dvdfrData['format']; if (empty($publisher) && !empty($dvdfrData['publisher'])) $publisher = $dvdfrData['publisher']; if (empty($length) && !empty($dvdfrData['length'])) $length = $dvdfrData['length']; if (empty($aspect) && !empty($dvdfrData['aspect'])) $aspect = $dvdfrData['aspect']; } // 2. TMDB (Données Officielles & Synopsis) if ($tmdbApiKey && !empty($title)) { // 🔥 CRÉER UNE VERSION PROPRE DU TITRE POUR TMDB $tmdbTitle = $title; // Supprimer les suffixes d'édition $tmdbTitle = preg_replace('/\s*[\[\(].*?[\]\)]\s*/', '', $tmdbTitle); $tmdbTitle = preg_replace('/\s*-\s*(Édition|Edition|Collector|Simple|Spéciale|Digibook|Ultimate|Intégrale|Combo|SteelBook|Boîtier|Coffret).*$/i', '', $tmdbTitle); $tmdbTitle = preg_replace('/\s*(Blu-ray|Bluray|DVD|4K|Ultra HD|Combo|VHS|BDRip|\[.*\]).*$/i', '', $tmdbTitle); $tmdbTitle = preg_replace('/\s*(Coffret|Trilogie|Quadrilogie|Collection|Anthologie).*$/i', '', $tmdbTitle); // Séparer les titres multiples (ex: "Alien / Aliens") $tmdbTitle = preg_split('/\s*(\/|\+|:)\s*/', $tmdbTitle)[0]; // Séparer les sous-titres après tiret $tmdbTitle = explode(' - ', $tmdbTitle)[0]; $tmdbTitle = trim($tmdbTitle); // 🔥 LOG POUR DÉBOGAGE if ($tmdbTitle !== $title) { $debugLog[] = "Titre original: '$title' -> Titre TMDB: '$tmdbTitle'"; } $tmdbData = fetchTMDBFull($tmdbTitle, $year, $tmdbApiKey, $pdo); // Si TMDB ne trouve rien, essayer avec le titre original if (!$tmdbData && $tmdbTitle !== $title) { $tmdbData = fetchTMDBFull($title, $year, $tmdbApiKey, $pdo); } if ($tmdbData) { if (!empty($tmdbData['title'])) $title = $tmdbData['title']; if (empty($director)) $director = $tmdbData['director'] ?? ''; if (empty($year) && !empty($tmdbData['year'])) $year = $tmdbData['year']; if (empty($length) && !empty($tmdbData['length'])) $length = $tmdbData['length']; if (!empty($tmdbData['overview'])) $description = $tmdbData['overview']; // 🔥 NE PAS ÉCRASER LES ACTEURS DU CSV SI TMDB N'A PAS D'ACTEURS if (!empty($tmdbData['cast'])) { $actors = implode(', ', $tmdbData['cast']); } } else { $debugLog[] = "TMDB n'a pas trouvé: '$tmdbTitle' (année: $year)"; } } // 🔥 LOG FINAL $debugLog[] = "Film #$id: '$title' | Réal: '$director' | Acteurs: '$actors' | Durée: '$length'"; // INSERT SQL $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, $title, $year, $director, $poster, $format, $length, $publisher, $ean, $discs, $aspect, $description, $actors]); } else { // Pour les critiques (code inchangé) $ratingRaw = $rowData['Rating'] ?? $rowData['rating'] ?? ''; $rating = ($ratingRaw !== '' && $ratingRaw !== null) ? (float)$ratingRaw : null; $review = $rowData['Review'] ?? $rowData['review'] ?? ''; $director = ''; $poster = ''; $streaming = ''; if ($tmdbApiKey && !empty($title)) { $tmdbData = fetchTMDBFull($title, $year, $tmdbApiKey, $pdo); if ($tmdbData) { $director = $tmdbData['director']; $poster = $tmdbData['poster']; $streaming = $tmdbData['streaming']; if(empty($year)) $year = $tmdbData['year']; } } if (empty($streaming)) $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), rating=VALUES(rating), review=IF(VALUES(review)!='',VALUES(review),review), director=IF(VALUES(director)!='',VALUES(director),director), poster=IF(VALUES(poster)!='',VALUES(poster),poster), streaming=IF(VALUES(streaming)!='',VALUES(streaming),streaming)"; $stmt = $pdo->prepare($sql); $stmt->execute([$id, $title, $year, $director, $poster, $rating, $review, $streaming]); } $imported++; } $pdo->commit(); // 🔥 RENVOYER LES LOGS DE DÉBOGAGE echo json_encode([ "success" => true, "imported" => $imported, "debug" => $debugLog ]); } catch (\Throwable $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } http_response_code(500); echo json_encode([ "success" => false, "error" => "Erreur serveur : " . $e->getMessage(), "debug" => $debugLog ]); } break; }