Actualiser api.php
This commit is contained in:
@@ -139,97 +139,75 @@ function emptyPhysicalResult() {
|
||||
];
|
||||
}
|
||||
|
||||
// ── FONCTIONS API PHYSIQUE (UPCitemdb → UPCMDB fallback) ──
|
||||
function throttleUpcLookup() {
|
||||
static $lastLookupAt = 0;
|
||||
$elapsed = microtime(true) - $lastLookupAt;
|
||||
if ($lastLookupAt > 0 && $elapsed < 2) {
|
||||
usleep((int)((2 - $elapsed) * 1000000));
|
||||
}
|
||||
$lastLookupAt = microtime(true);
|
||||
static $last = 0;
|
||||
$elapsed = microtime(true) - $last;
|
||||
if ($last > 0 && $elapsed < 2) usleep((int)((2 - $elapsed) * 1000000));
|
||||
$last = microtime(true);
|
||||
}
|
||||
|
||||
// ── API GRATUITE UPCitemdb : métadonnées physiques via EAN/UPC ──
|
||||
function fetchPhysicalFromUpcitemdb($ean) {
|
||||
$empty = emptyPhysicalResult();
|
||||
$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();
|
||||
|
||||
$url = 'https://api.upcitemdb.com/prod/trial/lookup?upc=' . urlencode($ean);
|
||||
$res = httpGet($url, 10, 'MonPetitCinema/1.0');
|
||||
$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];
|
||||
$rawTitle = $item['title'] ?? '';
|
||||
$title = cleanUpcTitle($rawTitle) ?: trim($rawTitle);
|
||||
$publisher = trim($item['brand'] ?? '');
|
||||
$raw = $item['title'] ?? '';
|
||||
$clean = cleanUpcTitle($raw) ?: $raw;
|
||||
|
||||
return [
|
||||
'title' => $title,
|
||||
'publisher' => $publisher,
|
||||
'format' => detectFormat($rawTitle),
|
||||
'length' => '',
|
||||
'number_of_discs' => parseDiscCountFromTitle($rawTitle),
|
||||
'title' => $clean,
|
||||
'publisher' => trim($item['brand'] ?? $item['manufacturer'] ?? ''),
|
||||
'format' => detectFormat($raw),
|
||||
'number_of_discs' => parseDiscCountFromTitle($raw),
|
||||
'aspect_ratio' => '',
|
||||
'year' => ''
|
||||
'year' => '',
|
||||
'length' => ''
|
||||
];
|
||||
}
|
||||
|
||||
// ── API UPCMDB (clé gratuite) : fallback spécialisé DVD/Blu-ray/4K ──
|
||||
function fetchPhysicalFromUpcmdb($ean, $pdo) {
|
||||
$empty = emptyPhysicalResult();
|
||||
unction fetchPhysicalFromUpcmdb($ean, $pdo) {
|
||||
$empty = ['title'=>'','publisher'=>'','format'=>'','length'=>'','number_of_discs'=>1,'aspect_ratio'=>'','year'=>''];
|
||||
$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
|
||||
]);
|
||||
$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'] ?? '');
|
||||
$raw = trim($item['title'] ?? '');
|
||||
if (!$raw) return $empty;
|
||||
|
||||
return [
|
||||
'title' => $title,
|
||||
'title' => cleanUpcTitle($raw) ?: $raw,
|
||||
'publisher' => trim($item['studio'] ?? $item['publisher'] ?? ''),
|
||||
'format' => $format !== '' ? $format : detectFormat($rawTitle),
|
||||
'length' => $runtime,
|
||||
'number_of_discs' => parseDiscCountFromTitle($rawTitle),
|
||||
'format' => trim($item['format'] ?: detectFormat($raw)),
|
||||
'length' => trim($item['runtime'] ?? ''),
|
||||
'number_of_discs' => (int)($item['discs'] ?? parseDiscCountFromTitle($raw)) ?: 1,
|
||||
'aspect_ratio' => trim($item['aspect_ratio'] ?? ''),
|
||||
'year' => trim($item['year'] ?? '')
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function fetchPhysicalByEan($ean, $pdo = null) {
|
||||
$result = fetchPhysicalFromUpcitemdb($ean);
|
||||
if (!empty($result['title'])) return $result;
|
||||
|
||||
$res = fetchPhysicalFromUpcitemdb($ean);
|
||||
if (!empty($res['title'])) return $res;
|
||||
if ($pdo) {
|
||||
$fallback = fetchPhysicalFromUpcmdb($ean, $pdo);
|
||||
if (!empty($fallback['title'])) {
|
||||
error_log("UPCMDB: ✅ Fallback trouvé pour EAN {$ean} → {$fallback['title']}");
|
||||
return $fallback;
|
||||
$fb = fetchPhysicalFromUpcmdb($ean, $pdo);
|
||||
if (!empty($fb['title'])) return $fb;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
return $res;
|
||||
}
|
||||
|
||||
// ── FONCTION POUR RÉCUPÉRER LES AFFICHES DEPUIS TMDB ──
|
||||
@@ -313,82 +291,51 @@ function fetchTmdbSynopsis($title, $year = '', $pdo = null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// ── FONCTION COMPLÈTE TMDB POUR POSTER + SYNOPSIS ──
|
||||
// ── FONCTION TMDB COMPLÈTE (Affiche + Métadonnées) ──
|
||||
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);
|
||||
if (!$tmdbKey) return $default;
|
||||
|
||||
$result = [
|
||||
'poster' => $defaultPoster,
|
||||
'description' => '',
|
||||
'director' => '',
|
||||
'actors' => '',
|
||||
'length' => '',
|
||||
'year' => $year
|
||||
];
|
||||
$clean = cleanTitle($title);
|
||||
$searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$tmdbKey}&query=" . urlencode($clean) . "&language=fr-FR";
|
||||
if ($year) $searchUrl .= "&year={$year}";
|
||||
|
||||
if (!$tmdbKey || empty($title)) return $result;
|
||||
$res = httpGet($searchUrl, 5);
|
||||
$data = $res ? json_decode($res, true) : [];
|
||||
|
||||
$cleanTitle = cleanTitle($title);
|
||||
$searchUrl = "https://api.themoviedb.org/3/search/movie?api_key={$tmdbKey}&query=" . urlencode($cleanTitle);
|
||||
if (!empty($year)) $searchUrl .= "&year={$year}";
|
||||
$searchUrl .= "&language=fr-FR";
|
||||
|
||||
$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) : [];
|
||||
// Retry sans année si échec
|
||||
if (empty($data['results']) && $year) {
|
||||
$res = httpGet("https://api.themoviedb.org/3/search/movie?api_key={$tmdbKey}&query=" . urlencode($clean) . "&language=fr-FR", 5);
|
||||
$data = $res ? json_decode($res, 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'];
|
||||
$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
|
||||
// Détails supplémentaires (réalisateur, acteurs, durée)
|
||||
$detailsUrl = "https://api.themoviedb.org/3/movie/{$movieId}?api_key={$tmdbKey}&append_to_response=credits&language=fr-FR";
|
||||
$detailsRes = httpGet($detailsUrl, 5);
|
||||
if ($detailsRes) {
|
||||
$details = json_decode($detailsRes, true);
|
||||
$detRes = httpGet($detailsUrl, 5);
|
||||
if ($detRes) {
|
||||
$det = json_decode($detRes, true);
|
||||
$default['length'] = !empty($det['runtime']) ? "{$det['runtime']} min" : '';
|
||||
|
||||
if (!empty($details['runtime'])) {
|
||||
$result['length'] = $details['runtime'] . ' min';
|
||||
if (!empty($det['credits']['crew'])) {
|
||||
$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))) : '';
|
||||
}
|
||||
|
||||
// Réalisateur
|
||||
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'];
|
||||
if (!empty($det['credits']['cast'])) {
|
||||
$default['actors'] = implode(', ', array_map(fn($c) => $c['name'], array_slice($det['credits']['cast'], 0, 5)));
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
return $default;
|
||||
}
|
||||
|
||||
// ── ROUTEUR PRINCIPAL ──
|
||||
@@ -420,33 +367,30 @@ switch ($action) {
|
||||
echo json_encode(["success" => true]);
|
||||
break;
|
||||
|
||||
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', 'upcmdb_api_key')");
|
||||
$stmt->execute();
|
||||
$rows = $stmt->fetchAll();
|
||||
// ── CONFIGURATION ──
|
||||
case 'get_config_keys':
|
||||
$keys = ['tmdb_api_key', 'upcmdb_api_key'];
|
||||
$config = [];
|
||||
foreach ($rows as $row) {
|
||||
$config[$row['key_name']] = $row['key_value'] ? '••••••••' : '';
|
||||
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']) : '';
|
||||
}
|
||||
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;
|
||||
|
||||
case 'save_config':
|
||||
case 'save_config':
|
||||
checkAuth($pdo);
|
||||
$keyName = $data['key_name'] ?? '';
|
||||
$keyValue = $data['key_value'] ?? '';
|
||||
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]);
|
||||
} else {
|
||||
http_response_code(400);
|
||||
echo json_encode(["error" => "Données invalides."]);
|
||||
$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':
|
||||
@@ -526,73 +470,47 @@ switch ($action) {
|
||||
else { http_response_code(400); echo json_encode(["success" => false, "error" => "Aucun élément sélectionné."]); }
|
||||
break;
|
||||
|
||||
// ── IMPORT BATCH (VIDÉOTHÈQUE) ──
|
||||
case 'import_batch':
|
||||
checkAuth($pdo);
|
||||
$data = json_decode(file_get_contents("php://input"), true);
|
||||
$type = $data['type'] ?? 'critique';
|
||||
$items = $data['items'] ?? [];
|
||||
$pdo->beginTransaction();
|
||||
$imported = 0;
|
||||
$skipped = 0;
|
||||
try {
|
||||
$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') {
|
||||
$stmtVideo = $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
|
||||
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)");
|
||||
$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=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)");
|
||||
|
||||
foreach ($items as $item) {
|
||||
// ── 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'] ?? '';
|
||||
// 1. Données physiques via UPC (UPCitemdb → UPCMDB)
|
||||
$phys = fetchPhysicalByEan($ean, $pdo);
|
||||
$title = $phys['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);
|
||||
$year = $phys['year'] ?? '';
|
||||
$format = $phys['format'] ?: detectFormat($title);
|
||||
$publisher = $phys['publisher'] ?? '';
|
||||
$discs = $phys['number_of_discs'] ?? 1;
|
||||
$aspect = $phys['aspect_ratio'] ?? '';
|
||||
$length = $phys['length'] ?? '';
|
||||
|
||||
$description = '';
|
||||
$director = '';
|
||||
$actors = '';
|
||||
$poster = 'assets/img/default_physical_media.jpg';
|
||||
// 2. Données cinéma via TMDB (Affiche, Réalisateur, Acteurs, Synopsis, Durée)
|
||||
$tmdb = fetchTmdbPosterAndSynopsis($title, $year, $pdo);
|
||||
$poster = $tmdb['poster'];
|
||||
$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 ──
|
||||
$tmdbData = fetchTmdbPosterAndSynopsis($title, $year, $pdo);
|
||||
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
|
||||
]);
|
||||
$id = makeStableId('videotheque', $title, $year);
|
||||
$stmt->execute([$id, $title, $year, $format, $poster, $ean, $desc, $length, $discs, $aspect, $actors, $publisher, $director]);
|
||||
$imported++;
|
||||
}
|
||||
} else { // ── IMPORTATION CRITIQUES ──
|
||||
|
||||
Reference in New Issue
Block a user