feat: auto-téléchargement par catégorie avec souscription persistante
Docker / docker (push) Has been cancelled
Docker / docker (push) Has been cancelled
- Bouton ⬇ sur chaque pill de catégorie pour activer/désactiver l'auto-DL
- Souscriptions sauvegardées en SQLite (table auto_dl_categories)
- Boucle background toutes les AUTO_DL_INTERVAL secondes (défaut 1h)
- Déduplication via already_enqueued() (évite re-queue si déjà queued/done)
- POST /api/auto-dl/check pour déclencher un check immédiat
- GET/POST/DELETE /api/auto-dl/{category} pour gérer les souscriptions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+32
-2
@@ -50,18 +50,48 @@ function debounce(fn, ms) {
|
||||
// ── Categories ───────────────────────────────────────────────────────────────
|
||||
async function loadCategories() {
|
||||
try {
|
||||
const cats = await fetch('/api/categories').then(r => r.json());
|
||||
const [cats, watched] = await Promise.all([
|
||||
fetch('/api/categories').then(r => r.json()),
|
||||
fetch('/api/auto-dl').then(r => r.json()),
|
||||
]);
|
||||
const watchedSet = new Set(watched);
|
||||
cats.forEach(cat => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'cat-pill';
|
||||
btn.dataset.cat = cat;
|
||||
btn.textContent = cat;
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.textContent = cat;
|
||||
btn.appendChild(label);
|
||||
|
||||
const icon = document.createElement('span');
|
||||
icon.className = 'auto-icon' + (watchedSet.has(cat) ? ' active' : '');
|
||||
icon.title = watchedSet.has(cat) ? 'Auto-DL actif — cliquer pour désactiver' : 'Activer le téléchargement automatique';
|
||||
icon.dataset.cat = cat;
|
||||
icon.textContent = '⬇';
|
||||
btn.appendChild(icon);
|
||||
|
||||
catBar.appendChild(btn);
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function toggleAutoDl(cat, iconEl) {
|
||||
const isActive = iconEl.classList.contains('active');
|
||||
try {
|
||||
await fetch(`/api/auto-dl/${encodeURIComponent(cat)}`, { method: isActive ? 'DELETE' : 'POST' });
|
||||
iconEl.classList.toggle('active', !isActive);
|
||||
iconEl.title = !isActive ? 'Auto-DL actif — cliquer pour désactiver' : 'Activer le téléchargement automatique';
|
||||
} catch {}
|
||||
}
|
||||
|
||||
catBar.addEventListener('click', e => {
|
||||
const icon = e.target.closest('.auto-icon');
|
||||
if (icon) {
|
||||
e.stopPropagation();
|
||||
toggleAutoDl(icon.dataset.cat, icon);
|
||||
return;
|
||||
}
|
||||
const pill = e.target.closest('.cat-pill');
|
||||
if (!pill) return;
|
||||
catBar.querySelectorAll('.cat-pill').forEach(p => p.classList.remove('active'));
|
||||
|
||||
+49
-2
@@ -216,10 +216,13 @@ body {
|
||||
cursor: pointer;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 13px;
|
||||
padding: 6px 16px;
|
||||
padding: 6px 6px 6px 14px;
|
||||
white-space: nowrap;
|
||||
transition: color var(--transition), border-color var(--transition), background var(--transition);
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.cat-pill:hover {
|
||||
@@ -234,6 +237,37 @@ body {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.auto-icon {
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
opacity: 0.3;
|
||||
padding: 2px 5px;
|
||||
border-radius: 10px;
|
||||
transition: opacity var(--transition), background var(--transition), color var(--transition);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.auto-icon:hover {
|
||||
opacity: 0.8;
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.auto-icon.active {
|
||||
opacity: 1;
|
||||
color: var(--gold);
|
||||
}
|
||||
|
||||
.cat-pill.active .auto-icon {
|
||||
opacity: 0.5;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.cat-pill.active .auto-icon.active {
|
||||
opacity: 1;
|
||||
background: rgba(0,0,0,0.18);
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* ══ GRID ══════════════════════════════════════════════════════════════════ */
|
||||
.grid {
|
||||
display: grid;
|
||||
@@ -252,7 +286,7 @@ body {
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
|
||||
transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition), opacity var(--transition), filter var(--transition);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -262,6 +296,19 @@ body {
|
||||
border-color: var(--gold-dim);
|
||||
}
|
||||
|
||||
.card.downloaded {
|
||||
opacity: 0.45;
|
||||
filter: grayscale(0.6);
|
||||
}
|
||||
|
||||
.card.downloaded:hover {
|
||||
opacity: 0.65;
|
||||
filter: grayscale(0.4);
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
.card-thumb-wrap {
|
||||
position: relative;
|
||||
aspect-ratio: 16/9;
|
||||
|
||||
Reference in New Issue
Block a user