Actualiser api.php

This commit is contained in:
2026-06-28 14:29:54 +02:00
parent b1bbd89292
commit daf68f8728
+109 -191
View File
@@ -139,97 +139,75 @@ function emptyPhysicalResult() {
]; ];
} }
// ── FONCTIONS API PHYSIQUE (UPCitemdb → UPCMDB fallback) ──
function throttleUpcLookup() { function throttleUpcLookup() {
static $lastLookupAt = 0; static $last = 0;
$elapsed = microtime(true) - $lastLookupAt; $elapsed = microtime(true) - $last;
if ($lastLookupAt > 0 && $elapsed < 2) { if ($last > 0 && $elapsed < 2) usleep((int)((2 - $elapsed) * 1000000));
usleep((int)((2 - $elapsed) * 1000000)); $last = microtime(true);
}
$lastLookupAt = microtime(true);
} }
// ── API GRATUITE UPCitemdb : métadonnées physiques via EAN/UPC ──
function fetchPhysicalFromUpcitemdb($ean) { function fetchPhysicalFromUpcitemdb($ean) {
$empty = emptyPhysicalResult(); $empty = ['title'=>'','publisher'=>'','format'=>'','length'=>'','number_of_discs'=>1,'aspect_ratio'=>'','year'=>''];
$ean = preg_replace('/[^0-9]/', '', (string)$ean); $ean = preg_replace('/[^0-9]/', '', (string)$ean);
if (strlen($ean) < 8) return $empty; if (strlen($ean) < 8) return $empty;
throttleUpcLookup(); throttleUpcLookup();
$res = httpGet("https://api.upcitemdb.com/prod/trial/lookup?upc=" . urlencode($ean), 10);
$url = 'https://api.upcitemdb.com/prod/trial/lookup?upc=' . urlencode($ean);
$res = httpGet($url, 10, 'MonPetitCinema/1.0');
if (!$res) return $empty; if (!$res) return $empty;
$data = json_decode($res, true); $data = json_decode($res, true);
if (empty($data['items'][0])) return $empty; if (empty($data['items'][0])) return $empty;
$item = $data['items'][0]; $item = $data['items'][0];
$rawTitle = $item['title'] ?? ''; $raw = $item['title'] ?? '';
$title = cleanUpcTitle($rawTitle) ?: trim($rawTitle); $clean = cleanUpcTitle($raw) ?: $raw;
$publisher = trim($item['brand'] ?? '');
return [ return [
'title' => $title, 'title' => $clean,
'publisher' => $publisher, 'publisher' => trim($item['brand'] ?? $item['manufacturer'] ?? ''),
'format' => detectFormat($rawTitle), 'format' => detectFormat($raw),
'length' => '', 'number_of_discs' => parseDiscCountFromTitle($raw),
'number_of_discs' => parseDiscCountFromTitle($rawTitle),
'aspect_ratio' => '', 'aspect_ratio' => '',
'year' => '' 'year' => '',
'length' => ''
]; ];
} }
unction fetchPhysicalFromUpcmdb($ean, $pdo) {
// ── API UPCMDB (clé gratuite) : fallback spécialisé DVD/Blu-ray/4K ── $empty = ['title'=>'','publisher'=>'','format'=>'','length'=>'','number_of_discs'=>1,'aspect_ratio'=>'','year'=>''];
function fetchPhysicalFromUpcmdb($ean, $pdo) {
$empty = emptyPhysicalResult();
$apiKey = getUpcmdbApiKey($pdo); $apiKey = getUpcmdbApiKey($pdo);
if (!$apiKey) return $empty; if (!$apiKey) return $empty;
$ean = preg_replace('/[^0-9]/', '', (string)$ean); $ean = preg_replace('/[^0-9]/', '', (string)$ean);
if (strlen($ean) < 8) return $empty; if (strlen($ean) < 8) return $empty;
$url = 'https://upcmdb.com/api/v1/lookup/' . urlencode($ean); $url = "https://upcmdb.com/api/v1/lookup/" . urlencode($ean);
$res = httpGet($url, 10, 'MonPetitCinema/1.0', [ $res = httpGet($url, 10, 'MonPetitCinema/1.0', ['Accept: application/json', 'X-API-Key: ' . $apiKey]);
'Accept: application/json',
'X-API-Key: ' . $apiKey
]);
if (!$res || $res[0] === '<') return $empty; if (!$res || $res[0] === '<') return $empty;
$data = json_decode($res, true); $data = json_decode($res, true);
if (($data['status'] ?? '') !== 'success' || empty($data['data'])) return $empty; if (($data['status'] ?? '') !== 'success' || empty($data['data'])) return $empty;
$item = $data['data']; $item = $data['data'];
$rawTitle = trim($item['title'] ?? ''); $raw = trim($item['title'] ?? '');
if ($rawTitle === '') return $empty; if (!$raw) return $empty;
$title = cleanUpcTitle($rawTitle) ?: $rawTitle;
$format = trim($item['format'] ?? '');
$runtime = trim($item['runtime'] ?? '');
return [ return [
'title' => $title, 'title' => cleanUpcTitle($raw) ?: $raw,
'publisher' => trim($item['studio'] ?? $item['publisher'] ?? ''), 'publisher' => trim($item['studio'] ?? $item['publisher'] ?? ''),
'format' => $format !== '' ? $format : detectFormat($rawTitle), 'format' => trim($item['format'] ?: detectFormat($raw)),
'length' => $runtime, 'length' => trim($item['runtime'] ?? ''),
'number_of_discs' => parseDiscCountFromTitle($rawTitle), 'number_of_discs' => (int)($item['discs'] ?? parseDiscCountFromTitle($raw)) ?: 1,
'aspect_ratio' => trim($item['aspect_ratio'] ?? ''), 'aspect_ratio' => trim($item['aspect_ratio'] ?? ''),
'year' => trim($item['year'] ?? '') 'year' => trim($item['year'] ?? '')
]; ];
} }
function fetchPhysicalByEan($ean, $pdo = null) { function fetchPhysicalByEan($ean, $pdo = null) {
$result = fetchPhysicalFromUpcitemdb($ean); $res = fetchPhysicalFromUpcitemdb($ean);
if (!empty($result['title'])) return $result; if (!empty($res['title'])) return $res;
if ($pdo) { if ($pdo) {
$fallback = fetchPhysicalFromUpcmdb($ean, $pdo); $fb = fetchPhysicalFromUpcmdb($ean, $pdo);
if (!empty($fallback['title'])) { if (!empty($fb['title'])) return $fb;
error_log("UPCMDB: ✅ Fallback trouvé pour EAN {$ean}{$fallback['title']}");
return $fallback;
} }
} return $res;
return $result;
} }
// ── FONCTION POUR RÉCUPÉRER LES AFFICHES DEPUIS TMDB ── // ── FONCTION POUR RÉCUPÉRER LES AFFICHES DEPUIS TMDB ──
@@ -313,82 +291,51 @@ function fetchTmdbSynopsis($title, $year = '', $pdo = null) {
return ''; return '';
} }
// ── FONCTION COMPLÈTE TMDB POUR POSTER + SYNOPSIS ── // ── FONCTION TMDB COMPLÈTE (Affiche + Métadonnées) ──
function fetchTmdbPosterAndSynopsis($title, $year = '', $pdo = null) { function fetchTmdbPosterAndSynopsis($title, $year = '', $pdo = null) {
$defaultPoster = 'assets/img/default_physical_media.jpg'; $default = ['poster'=>'assets/img/default_physical_media.jpg','title'=>'','description'=>'','director'=>'','actors'=>'','length'=>'','year'=>''];
if (empty($title)) return $default;
$tmdbKey = getTmdbApiKey($pdo); $tmdbKey = getTmdbApiKey($pdo);
if (!$tmdbKey) return $default;
$result = [ $clean = cleanTitle($title);
'poster' => $defaultPoster, $searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$tmdbKey}&query=" . urlencode($clean) . "&language=fr-FR";
'description' => '', if ($year) $searchUrl .= "&year={$year}";
'director' => '',
'actors' => '',
'length' => '',
'year' => $year
];
if (!$tmdbKey || empty($title)) return $result; $res = httpGet($searchUrl, 5);
$data = $res ? json_decode($res, true) : [];
$cleanTitle = cleanTitle($title); // Retry sans année si échec
$searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$tmdbKey}&query=" . urlencode($cleanTitle); if (empty($data['results']) && $year) {
if (!empty($year)) $searchUrl .= "&year={$year}"; $res = httpGet("https://api.themoviedb.org/3/search/movie?api_key={$tmdbKey}&query=" . urlencode($clean) . "&language=fr-FR", 5);
$searchUrl .= "&language=fr-FR"; $data = $res ? json_decode($res, true) : [];
$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($data['results'])) return $default;
if (empty($searchData['results'])) return $result; $movie = $data['results'][0];
$movieId = $movie['id'];
$default['poster'] = !empty($movie['poster_path']) ? "https://image.tmdb.org/t/p/w500{$movie['poster_path']}" : $default['poster'];
$default['year'] = !empty($movie['release_date']) ? substr($movie['release_date'], 0, 4) : $year;
$default['description'] = $movie['overview'] ?? '';
$default['title'] = $clean;
$movieId = $searchData['results'][0]['id']; // Détails supplémentaires (réalisateur, acteurs, durée)
$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"; $detailsUrl = "https://api.themoviedb.org/3/movie/{$movieId}?api_key={$tmdbKey}&append_to_response=credits&language=fr-FR";
$detailsRes = httpGet($detailsUrl, 5); $detRes = httpGet($detailsUrl, 5);
if ($detailsRes) { if ($detRes) {
$details = json_decode($detailsRes, true); $det = json_decode($detRes, true);
$default['length'] = !empty($det['runtime']) ? "{$det['runtime']} min" : '';
if (!empty($details['runtime'])) { if (!empty($det['credits']['crew'])) {
$result['length'] = $details['runtime'] . ' min'; $dirs = array_filter($det['credits']['crew'], fn($c) => $c['job'] === 'Director');
$default['director'] = $dirs ? implode(', ', array_map(fn($c) => $c['name'], array_slice($dirs, 0, 2))) : '';
} }
if (!empty($det['credits']['cast'])) {
// Réalisateur $default['actors'] = implode(', ', array_map(fn($c) => $c['name'], array_slice($det['credits']['cast'], 0, 5)));
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 $default;
return $result;
} }
// ── ROUTEUR PRINCIPAL ── // ── ROUTEUR PRINCIPAL ──
@@ -420,33 +367,30 @@ switch ($action) {
echo json_encode(["success" => true]); echo json_encode(["success" => true]);
break; break;
case 'get_config_keys': // ── CONFIGURATION ──
checkAuth($pdo); case 'get_config_keys':
$stmt = $pdo->prepare("SELECT key_name, key_value FROM config WHERE key_name IN ('tmdb_api_key', 'fanart_api_key', 'upcmdb_api_key')"); $keys = ['tmdb_api_key', 'upcmdb_api_key'];
$stmt->execute();
$rows = $stmt->fetchAll();
$config = []; $config = [];
foreach ($rows as $row) { foreach ($keys as $k) {
$config[$row['key_name']] = $row['key_value'] ? '••••••••' : ''; $stmt = $pdo->prepare("SELECT key_value FROM config WHERE key_name = ?");
$stmt->execute([$k]);
$row = $stmt->fetch();
$config[$k] = $row ? decryptData($row['key_value']) : '';
} }
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); echo json_encode($config);
break; break;
case 'save_config': case 'save_config':
checkAuth($pdo); checkAuth($pdo);
$keyName = $data['key_name'] ?? ''; $name = $data['key_name'] ?? '';
$keyValue = $data['key_value'] ?? ''; $val = trim($data['key_value'] ?? '');
if (in_array($keyName, ['tmdb_api_key', 'fanart_api_key', 'upcmdb_api_key']) && !empty($keyValue)) { if (!in_array($name, ['tmdb_api_key', 'upcmdb_api_key'])) {
$stmt = $pdo->prepare("REPLACE INTO config (key_name, key_value) VALUES (?, ?)"); http_response_code(400); echo json_encode(["error" => "Clé invalide."]); break;
$stmt->execute([$keyName, encryptData($keyValue)]);
echo json_encode(["success" => true]);
} else {
http_response_code(400);
echo json_encode(["error" => "Données invalides."]);
} }
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; break;
case 'get_films': case 'get_films':
@@ -526,73 +470,47 @@ switch ($action) {
else { http_response_code(400); echo json_encode(["success" => false, "error" => "Aucun élément sélectionné."]); } else { http_response_code(400); echo json_encode(["success" => false, "error" => "Aucun élément sélectionné."]); }
break; break;
// ── IMPORT BATCH (VIDÉOTHÈQUE) ──
case 'import_batch': case 'import_batch':
checkAuth($pdo); checkAuth($pdo);
$data = json_decode(file_get_contents("php://input"), true); $data = json_decode(file_get_contents("php://input"), true);
$type = $data['type'] ?? 'critique'; $type = $data['type'] ?? '';
$items = $data['items'] ?? []; $items = $data['items'] ?? [];
$pdo->beginTransaction(); $pdo->beginTransaction();
$imported = 0; $imported = 0; $skipped = 0;
$skipped = 0; try {
try {
if ($type === 'videotheque') { 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) $stmt = $pdo->prepare("INSERT INTO videotheque (id, title, year, format, poster, ean_isbn13, description, length, number_of_discs, aspect_ratio, actors, publisher, director)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE
ON DUPLICATE KEY UPDATE title=VALUES(title), year=VALUES(year), format=VALUES(format), poster=VALUES(poster), ean_isbn13=VALUES(ean_isbn13), description=VALUES(description), length=VALUES(length), number_of_discs=VALUES(number_of_discs), aspect_ratio=VALUES(aspect_ratio), actors=VALUES(actors), publisher=VALUES(publisher), director=VALUES(director)");
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) { foreach ($items as $item) {
// ── SOURCE 1 : CSV → EAN uniquement ──
$ean = preg_replace('/[^0-9]/', '', (string)($item['ean'] ?? '')); $ean = preg_replace('/[^0-9]/', '', (string)($item['ean'] ?? ''));
if (strlen($ean) < 8) { $skipped++; continue; } if (strlen($ean) < 8) { $skipped++; continue; }
// ── SOURCE 2 : UPCitemdb (gratuit) → métadonnées physiques ── // 1. Données physiques via UPC (UPCitemdb → UPCMDB)
$physical = fetchPhysicalByEan($ean, $pdo); $phys = fetchPhysicalByEan($ean, $pdo);
$title = $physical['title'] ?? ''; $title = $phys['title'] ?? '';
if (empty($title)) { $skipped++; continue; } if (empty($title)) { $skipped++; continue; }
$year = $physical['year'] ?? ''; $year = $phys['year'] ?? '';
$format = $physical['format'] ?? ''; $format = $phys['format'] ?: detectFormat($title);
$publisher = $physical['publisher'] ?? ''; $publisher = $phys['publisher'] ?? '';
$discs = $physical['number_of_discs'] ?? 1; $discs = $phys['number_of_discs'] ?? 1;
$aspect = $physical['aspect_ratio'] ?? ''; $aspect = $phys['aspect_ratio'] ?? '';
$length = $physical['length'] ?? ''; $length = $phys['length'] ?? '';
$id = makeStableId('videotheque', $ean, $title);
$description = ''; // 2. Données cinéma via TMDB (Affiche, Réalisateur, Acteurs, Synopsis, Durée)
$director = ''; $tmdb = fetchTmdbPosterAndSynopsis($title, $year, $pdo);
$actors = ''; $poster = $tmdb['poster'];
$poster = 'assets/img/default_physical_media.jpg'; $director = $tmdb['director'] ?? '';
$actors = $tmdb['actors'] ?? '';
$desc = $tmdb['description'] ?? '';
if (!empty($tmdb['length'])) $length = $tmdb['length'];
if (empty($year) && !empty($tmdb['year'])) $year = $tmdb['year'];
// ── SOURCE 3 : TMDB → affiche, synopsis, réalisateur, acteurs ── $id = makeStableId('videotheque', $title, $year);
$tmdbData = fetchTmdbPosterAndSynopsis($title, $year, $pdo); $stmt->execute([$id, $title, $year, $format, $poster, $ean, $desc, $length, $discs, $aspect, $actors, $publisher, $director]);
if ($tmdbData) {
if ($tmdbData['poster'] !== 'assets/img/default_physical_media.jpg') {
$poster = $tmdbData['poster'];
}
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
]);
$imported++; $imported++;
} }
} else { // ── IMPORTATION CRITIQUES ── } else { // ── IMPORTATION CRITIQUES ──