feat: UX improvements, folder picker, download state & NFO support

## Settings
- Folder picker pour les paths (navigation dossier côté serveur via GET /api/browse)
- Paths vides par défaut, démarrage à / dans le picker
- Picker accessible même sous Docker (volumes montés accessibles)
- Bouton "Analyser bibliothèque" pour relancer scanMediaPath manuellement (POST /api/scan)
- Toggle NFO support (téléchargement dossier GitLab Fankai Pack à l'organisation)
- nfoSupport ajouté dans Settings interface + defaults

## Catalogue
- Logo "Fankarr" cliquable → retour accueil
- Filtres séparés en 2 groupes : état série (orange) | état téléchargement (bleu)
- Groupe 1 : Toutes / Disponibles / Sans torrent
- Groupe 2 : DL complet / DL partiel
- Tooltip badge torrents → "X torrents téléchargeables"

## Page Série
- Anti-spam boutons download : Set au lieu de string, bouton bloqué après envoi
- Boutons bloqués si torrent déjà dans qBittorrent (cross-session via poll /api/downloads)
- Boutons bloqués si épisode/saison déjà organisé dans la bibliothèque
- Barre de progression sous épisode / saison / intégrale (poll 5s)
- Badge "✓ téléchargé" à 100% à la place de la barre
- Badge "dispo" en bleu, "✓ téléchargé" inline à côté de "dispo"

## Page Téléchargements
- Onglets EN COURS / TERMINÉS (défaut : EN COURS)
- EN COURS : downloading + paused + checking + error
- TERMINÉS : seeding uniquement
- Toggle "Masquer organisés" dans l'onglet TERMINÉS
- Message vide adapté au contexte

## Organizer
- seasonFolder : Saison N sans padding (pour matcher GitLab)
- Map SERIE_TITLE_GITLAB_MAP : 9 corrections titres API → GitLab
- downloadGitlabFolder : liste récursive + téléchargement NFO/images depuis fankai_pack
- Appelé au début de organizeTorrent si nfoSupport activé
- Paths vides par défaut dans readSettings du worker

## Backend
- POST /api/scan : déclenche scanMediaPath manuellement
- GET /api/browse?path= : liste les sous-dossiers d'un chemin
- dispatchList : utilise client.config.category en priorité sur la catégorie globale
- Catégorie configurable par client torrent (fix torrents non affichés page downloads)
This commit is contained in:
MasutaYunikon committed 2026-03-12 22:41:46 +01:00
1 parent f38a43d396
commit 693f8a96ae
9 files changed
+932 -319

No files matched your search

+35 -11
View File
@@ -4,9 +4,9 @@
<!-- Header -->
<header class="sticky top-0 z-50 bg-zinc-950/90 backdrop-blur border-b border-white/5">
<div class="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
<span class="text-2xl font-serif tracking-tight">
<router-link to="/" class="text-2xl font-serif tracking-tight hover:opacity-80 transition">
<span class="text-zinc-100">Fan</span><span class="text-orange-500">karr</span>
</span>
</router-link>
<div class="flex items-center gap-2">
<!-- Icône logs -->
<router-link to="/logs"
@@ -67,9 +67,10 @@
class="w-full bg-white/5 border border-white/[0.08] rounded-lg pl-9 pr-4 py-2 text-sm placeholder-zinc-600 outline-none focus:border-orange-500/50 transition"
/>
</div>
<div class="flex gap-1">
<div class="flex items-center gap-1">
<!-- Groupe 1 : état de la série -->
<button
v-for="f in filters"
v-for="f in filtersGroupe1"
:key="f.value"
class="px-3 py-1.5 rounded-lg text-xs font-medium border transition"
:class="activeFilter === f.value
@@ -77,6 +78,20 @@
: 'bg-transparent border-white/[0.08] text-zinc-500 hover:text-zinc-100 hover:border-white/20'"
@click="activeFilter = f.value"
>{{ f.label }}</button>
<!-- Séparateur -->
<span class="w-px h-5 bg-white/10 mx-1" />
<!-- Groupe 2 : état du téléchargement -->
<button
v-for="f in filtersGroupe2"
:key="f.value"
class="px-3 py-1.5 rounded-lg text-xs font-medium border transition"
:class="activeFilter === f.value
? 'bg-blue-500 border-blue-500 text-white'
: 'bg-transparent border-white/[0.08] text-zinc-500 hover:text-zinc-100 hover:border-white/20'"
@click="activeFilter = f.value"
>{{ f.label }}</button>
</div>
<span class="text-xs text-zinc-600 ml-auto">{{ filtered.length }} série{{ filtered.length > 1 ? 's' : '' }}</span>
</div>
@@ -133,8 +148,14 @@
</div>
<!-- Badge torrents dispo -->
<div v-if="serie.has_torrents" class="absolute top-2 right-2 bg-orange-500 text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full leading-none">
<div v-if="serie.has_torrents"
class="absolute top-2 right-2 bg-orange-500 text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full leading-none cursor-default group/badge">
{{ serie.torrent_count }}
<div class="absolute bottom-full right-0 mb-1.5 hidden group-hover/badge:block z-10">
<div class="bg-zinc-800 border border-white/10 text-white text-[10px] font-normal px-2 py-1 rounded whitespace-nowrap shadow-lg">
{{ serie.torrent_count }} torrent{{ serie.torrent_count > 1 ? 's' : '' }} téléchargeables
</div>
</div>
</div>
<!-- Badge état téléchargement -->
@@ -201,12 +222,15 @@ const search = ref('')
const activeFilter = ref('all')
const activeDownloads = ref(0)
const filters = [
{ label: 'Toutes', value: 'all' },
{ label: 'Disponibles', value: 'available' },
{ label: 'Téléchargés', value: 'complete' },
{ label: 'En cours', value: 'downloading' },
{ label: 'Sans torrent', value: 'unavailable' },
const filtersGroupe1 = [
{ label: 'Toutes', value: 'all' },
{ label: 'Disponibles', value: 'available' },
{ label: 'Sans torrent', value: 'unavailable' },
]
const filtersGroupe2 = [
{ label: 'DL complet', value: 'complete' },
{ label: 'DL partiel', value: 'downloading' },
]
const filtered = computed(() => {