feat: intégration TMDB — poster, backdrop, lien fiche
Docker / docker (push) Successful in 1m21s

- tmdb.py : recherche TMDB par title+subtitle, matching fuzzy,
  cache SQLite 30 jours (table tmdb_cache dans arte_dl.db)
- arte_api.py : enrichissement concurrent (5 workers) après résolution
  des IDs ; ajoute tmdb_id, tmdb_poster, tmdb_backdrop au concert
- app.js : backdrop TMDB utilisé comme thumbnail de carte quand dispo ;
  subtitle affiché sous le titre de carte ; poster dans la modal ;
  lien direct vers la fiche TMDB
- docker-compose.yml : passage de TMDB_API_KEY au container

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dev
2026-04-26 13:15:27 +02:00
parent 16736e2e7a
commit 9a5e356238
6 changed files with 195 additions and 7 deletions
+25 -3
View File
@@ -102,12 +102,14 @@ function renderConcerts(data) {
statusText.textContent = `${total} concert${total > 1 ? 's' : ''} · page ${state.page} / ${pages}`;
grid.innerHTML = concerts.map(c => {
const thumb = c.thumbnail
? `<img class="card-thumb" src="${c.thumbnail}" alt="" loading="lazy" />`
const imgSrc = c.tmdb_backdrop || c.thumbnail;
const thumb = imgSrc
? `<img class="card-thumb" src="${imgSrc}" alt="" loading="lazy" />`
: `<div class="card-thumb" style="background:#1a1a1a"></div>`;
const dur = c.duration ? `<span class="card-duration">${fmtDuration(c.duration)}</span>` : '';
const dl = state.downloadedUrls.has(c.url) ? `<span class="card-downloaded">✓ Téléchargé</span>` : '';
const date = fmtDate(c.upload_date);
const sub = c.subtitle ? `<div class="card-subtitle">${c.subtitle}</div>` : '';
return `
<div class="card" data-id="${c.id}" tabindex="0" role="button" aria-label="${c.title}">
@@ -116,6 +118,7 @@ function renderConcerts(data) {
</div>
<div class="card-info">
<div class="card-title">${c.title}</div>
${sub}
${date ? `<div class="card-date">${date}</div>` : ''}
</div>
</div>`;
@@ -175,9 +178,20 @@ pagination.addEventListener('click', e => {
function openModal(concert) {
state.current = concert;
$('modal-thumb').src = concert.thumbnail || '';
const modalThumbEl = $('modal-thumb');
modalThumbEl.src = concert.tmdb_backdrop || concert.thumbnail || '';
const posterEl = $('modal-poster');
if (concert.tmdb_poster) {
posterEl.src = concert.tmdb_poster;
posterEl.hidden = false;
} else {
posterEl.hidden = true;
}
$('modal-title').textContent = concert.title;
$('modal-meta').textContent = [
concert.subtitle || '',
concert.duration ? fmtDuration(concert.duration) : '',
concert.upload_date ? fmtDate(concert.upload_date) : '',
].filter(Boolean).join(' · ');
@@ -185,6 +199,14 @@ function openModal(concert) {
$('modal-dur-badge').textContent = concert.duration ? fmtDuration(concert.duration) : '';
$('btn-watch').href = concert.url || '#';
const btnTmdb = $('btn-tmdb');
if (concert.tmdb_id) {
btnTmdb.href = `https://www.themoviedb.org/movie/${concert.tmdb_id}`;
btnTmdb.hidden = false;
} else {
btnTmdb.hidden = true;
}
const btnDl = $('btn-download');
const alreadyDone = state.downloadedUrls.has(concert.url);
btnDl.textContent = alreadyDone ? '✓ Déjà téléchargé' : 'Télécharger';