Actualiser js/admin.js
This commit is contained in:
+9
-36
@@ -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.5–5 scale; convert to our 1–5 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(', ')}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user