Actualiser js/admin.js

This commit is contained in:
2026-06-15 22:39:20 +02:00
parent 753fe7aba8
commit 4ef219c213
+9 -36
View File
@@ -244,7 +244,7 @@ function saveFilm() {
format: '', length: '', publisher: '', ean_isbn13: '', number_of_discs: '', aspect_ratio: '', description: ''
};
if (editingFilmId !== null) {
if (editingFilmId !== null) {
const index = films.findIndex(f => f.id === editingFilmId);
if (index !== -1) {
films[index] = {
@@ -256,7 +256,7 @@ if (editingFilmId !== null) {
poster: document.getElementById('f-poster').value.trim(),
rating: type === 'critique' ? (currentRating || 1) : 0,
review: type === 'critique' ? document.getElementById('f-review').value.trim() : '',
streaming: films[index].streaming || "Disponible au cinéma ou support physique", // Conserve ou ajoute par défaut
streaming: films[index].streaming || "Disponible au cinéma ou support physique",
...metadataVideotheque
};
}
@@ -270,7 +270,7 @@ if (editingFilmId !== null) {
poster: document.getElementById('f-poster').value.trim(),
rating: type === 'critique' ? (currentRating || 1) : 0,
review: type === 'critique' ? document.getElementById('f-review').value.trim() : '',
streaming: "Disponible au cinéma ou support physique", // Valeur par défaut à la création
streaming: "Disponible au cinéma ou support physique",
...metadataVideotheque
};
films.unshift(film);
@@ -295,7 +295,6 @@ function renderAdminTable() {
if (!tbody) return;
// Reset select-all + bulk-delete button BEFORE rebuilding DOM
const selectAll = document.getElementById('th-select-all');
if (selectAll) selectAll.checked = false;
const btnBulkReset = document.getElementById('btn-bulk-delete');
@@ -321,7 +320,6 @@ function renderAdminTable() {
if (emptyState) emptyState.style.display = 'none';
// Update film count display
const countEl = document.getElementById('films-count');
if (countEl) countEl.textContent = filteredFilms.length;
@@ -395,7 +393,6 @@ function deleteSelectedFilms() {
}
}
// ── Letterboxd CSV parser (handles quoted commas correctly) ──────────────
function parseLetterboxdCSV(text) {
const rows = [];
const lines = text.split(/\r\n|\n/);
@@ -420,7 +417,6 @@ function parseLetterboxdCSV(text) {
return rows;
}
// ── Show import progress overlay ─────────────────────────────────────────
function showImportProgress(total) {
let overlay = document.getElementById('import-progress-overlay');
if (!overlay) {
@@ -464,7 +460,6 @@ function hideImportProgress() {
if (el) el.remove();
}
// ── TMDB poster lookup (single film) ─────────────────────────────────────
async function fetchTmdbPoster(title, year) {
const key = localStorage.getItem('tmdb-api-key');
if (!key) return '';
@@ -477,7 +472,6 @@ async function fetchTmdbPoster(title, year) {
if (data.results && data.results.length > 0 && data.results[0].poster_path) {
return `https://image.tmdb.org/t/p/w500${data.results[0].poster_path}`;
}
// Retry without year if no result
if (year) {
const res2 = await fetch(
`https://api.themoviedb.org/3/search/movie?api_key=${key}&query=${encodeURIComponent(title)}&language=fr-FR`
@@ -491,9 +485,7 @@ async function fetchTmdbPoster(title, year) {
return '';
}
// ── DVD/Blu-ray cover: Open Library (EAN) → TMDB fallback ────────────────
async function fetchDvdCover(ean, title, year) {
// Step 1 — Open Library ISBN/EAN lookup (no key needed)
if (ean) {
try {
const olRes = await fetch(
@@ -503,7 +495,6 @@ async function fetchDvdCover(ean, title, year) {
const key = `ISBN:${ean}`;
if (olData[key]) {
const book = olData[key];
// Prefer large cover, fall back to medium/small
if (book.cover) {
const coverUrl = book.cover.large || book.cover.medium || book.cover.small || '';
if (coverUrl) return coverUrl;
@@ -511,7 +502,6 @@ async function fetchDvdCover(ean, title, year) {
}
} catch (e) {}
// Step 1b — Open Library cover API directly by ISBN (faster path)
try {
const testUrl = `https://covers.openlibrary.org/b/isbn/${ean}-L.jpg?default=false`;
const testRes = await fetch(testUrl);
@@ -520,12 +510,9 @@ async function fetchDvdCover(ean, title, year) {
}
} catch (e) {}
}
// Step 2 — TMDB fallback (uses existing key)
return await fetchTmdbPoster(title, year);
}
// ── Main Letterboxd import (reviews.csv OR ratings.csv) ──────────────────
function importFromCSV(input) {
const file = input.files[0];
if (!file) return;
@@ -542,10 +529,8 @@ function importFromCSV(input) {
}
const headers = rows[0].map(h => h.trim().toLowerCase());
// Detect file type: reviews.csv has a "Review" column, ratings.csv doesn't
const isReviews = headers.includes('review');
const iCol = { // column indices by header name
const iCol = {
name: headers.indexOf('name'),
year: headers.indexOf('year'),
rating: headers.indexOf('rating'),
@@ -553,7 +538,6 @@ function importFromCSV(input) {
watchedDate: headers.indexOf('watched date'),
};
// Collect rows to import (skip duplicates first pass)
const toImport = [];
let duplicateCount = 0;
@@ -564,7 +548,6 @@ function importFromCSV(input) {
const year = iCol.year >= 0 ? (cols[iCol.year]?.trim() || '') : '';
const ratingRaw = iCol.rating >= 0 ? parseFloat(cols[iCol.rating]) : 0;
// Letterboxd uses 0.55 scale; convert to our 15 integer scale
const rating = Math.round(Math.min(5, Math.max(1, ratingRaw)));
const review = (isReviews && iCol.review >= 0) ? (cols[iCol.review]?.trim() || '') : '';
@@ -594,11 +577,10 @@ function importFromCSV(input) {
let streaming = "Disponible au cinéma ou support physique";
if (hasTmdb) {
// On récupère les deux infos en un seul traitement
// Double appel unifié (Affiche + Streaming)
const details = await fetchTmdbDetails(title, year);
poster = details.poster;
streaming = details.streaming;
// Augmentation légère du délai pour respecter les limites de l'API avec le double appel
await new Promise(r => setTimeout(r, 350));
}
@@ -611,8 +593,9 @@ function importFromCSV(input) {
rating,
review,
poster,
streaming, // Ajout du nouveau champ
format: '', length: '', publisher: '', ean_isbn13: '', number_of_discs: '1', aspect_ratio: '', description: ''
streaming,
format: '',
length: '', publisher: '', ean_isbn13: '', number_of_discs: '1', aspect_ratio: '', description: ''
});
importedCount++;
}
@@ -626,7 +609,7 @@ function importFromCSV(input) {
renderAdminTable();
const typeLabel = isReviews ? 'reviews.csv (avec critiques)' : 'ratings.csv (notes seules)';
const posterInfo = hasTmdb ? ' · Affiches récupérées via TMDB' : ' · ⚠️ Clé TMDB absente, affiches non récupérées';
const posterInfo = hasTmdb ? ' · Affiches et plateformes récupérées via TMDB' : ' · ⚠️ Clé TMDB absente';
alert(
`Import Letterboxd — ${typeLabel}\n` +
`${importedCount} film(s) importé(s)${posterInfo}\n` +
@@ -670,7 +653,6 @@ function importVideothequeFromInput(input) {
}
async function importVideothequeCSV(text) {
// ── Parse CSV (handles quoted commas) ──
const lines = [];
let row = [""];
let inQuotes = false;
@@ -691,7 +673,6 @@ async function importVideothequeCSV(text) {
if (row.length > 1 || row[0] !== '') lines.push(row);
if (lines.length <= 1) return;
// ── Collect rows to import ──
const toImport = [];
let duplicateCount = 0;
@@ -734,7 +715,6 @@ async function importVideothequeCSV(text) {
return;
}
// ── Progress bar ──
showImportProgress(toImport.length);
let importedCount = 0;
let coversFound = 0;
@@ -745,14 +725,12 @@ async function importVideothequeCSV(text) {
const sourceLabel = ean ? `EAN ${ean}` : title;
updateImportProgress(i, toImport.length, `📀 ${sourceLabel}`);
// ── Fetch cover: Open Library (EAN) → TMDB ──
let poster = '';
try {
poster = await fetchDvdCover(ean, title, year);
if (poster) coversFound++;
} catch(e) {}
// Respectful delay
await new Promise(r => setTimeout(r, 300));
films.push({
@@ -824,7 +802,6 @@ async function fetchTmdbDetails(title, year) {
try {
const yearParam = year ? `&year=${year}` : '';
// Step 1 : Recherche du film pour obtenir son ID TMDB
const res = await fetch(
`https://api.themoviedb.org/3/search/movie?api_key=${key}&query=${encodeURIComponent(title)}&language=fr-FR${yearParam}`
);
@@ -836,7 +813,6 @@ async function fetchTmdbDetails(title, year) {
const posterPath = movie.poster_path ? `https://image.tmdb.org/t/p/w500${movie.poster_path}` : '';
let streamingInfo = streamingDefault;
// Step 2 : Requête sur l'endpoint "watch/providers" pour avoir le streaming en France (FR)
try {
const watchRes = await fetch(
`https://api.themoviedb.org/3/movie/${movieId}/watch/providers?api_key=${key}`
@@ -845,13 +821,10 @@ async function fetchTmdbDetails(title, year) {
if (watchData.results && watchData.results.FR) {
const frProviders = watchData.results.FR;
// On privilégie les plateformes incluses dans un abonnement (flatrate), sinon à la location/achat
const providers = frProviders.flatrate || frProviders.buy || frProviders.rent || [];
if (providers.length > 0) {
// Extrait les noms des plateformes trouvées (ex: Netflix, Canal+, Disney Plus)
const names = providers.map(p => p.provider_name);
// On supprime les doublons potentiels et on limite aux 3 premières pour l'affichage
const uniqueNames = [...new Set(names)].slice(0, 3);
streamingInfo = `Disponible sur : ${uniqueNames.join(', ')}`;
}