536 lines
14 KiB
Markdown
536 lines
14 KiB
Markdown
# Plan : Refonte du Système Watchlist
|
|
|
|
## TL;DR
|
|
|
|
> **Objectif** : Refaire le système de watchlist avec auto-téléchargement, notifications et stockage SQLite
|
|
>
|
|
> **Deliverables** :
|
|
> - Base de données SQLite pour la watchlist
|
|
> - API REST pour gérer les animes suivis
|
|
> - Système d'auto-téléchargement (vérification automatique des nouveaux épisodes)
|
|
> - Système de notifications (in-app)
|
|
> - Interface frontend (page séparée, même style que le reste)
|
|
>
|
|
> **Effort** : XL
|
|
> **Exécution** : En waves parallèles
|
|
|
|
---
|
|
|
|
## Contexte
|
|
|
|
### Système Actuel
|
|
- Stockage JSON (`config/watchlist.json`)
|
|
- Pas de SQLite
|
|
- Auto-download basique via scheduler
|
|
- Pas de système de notifications
|
|
- Interface intégrée à la page principale
|
|
|
|
### Besoins Utilisateur
|
|
- Auto-téléchargement des nouveaux épisodes ✅
|
|
- Notifications quand un nouvel épisode est dispo ✅
|
|
- Stockage SQLite ✅
|
|
- Même style que le reste du site ✅
|
|
- Page séparée ✅
|
|
|
|
---
|
|
|
|
## Work Objectives
|
|
|
|
### Objectif Principal
|
|
Créer un système de watchlist complet permettant de :
|
|
1. Suivre des animes (ajout via recherche)
|
|
2. Détecter automatiquement les nouveaux épisodes
|
|
3. Télécharger automatiquement les nouveaux épisodes
|
|
4. Notifier l'utilisateur quand un nouvel épisode est disponible
|
|
|
|
### Deliverables Concrets
|
|
- [ ] Base de données SQLite (`config/watchlist.db`)
|
|
- [ ] Modèles Pydantic pour la watchlist
|
|
- [ ] API endpoints (CRUD + actions)
|
|
- [ ] Service d'auto-check (scheduler)
|
|
- [ ] Service de notifications
|
|
- [ ] Page frontend dédiée
|
|
- [ ] Intégration avec le système de download existant
|
|
|
|
### Définition de Terminé
|
|
- [ ] Un anime peut être ajouté à la watchlist
|
|
- [ ] La watchlist affiche tous les animes suivis
|
|
- [ ] Les épisodes peuvent être téléchargés manuellement
|
|
- [ ] Le scheduler vérifie automatiquement les nouveaux épisodes
|
|
- [ ] Les nouveaux épisodes sont téléchargés automatiquement
|
|
- [ ] Une notification apparaît quand un nouvel épisode est dispo
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
### Structure des Fichiers
|
|
|
|
```
|
|
app/
|
|
├── watchlist/
|
|
│ ├── __init__.py
|
|
│ ├── models.py # Modèles Pydantic
|
|
│ ├── database.py # Connexion SQLite
|
|
│ ├── service.py # Logique métier
|
|
│ ├── scheduler.py # Auto-check
|
|
│ └── notifications.py # Notifications
|
|
├── routes/
|
|
│ └── watchlist.py # API endpoints
|
|
static/
|
|
└── js/
|
|
└── watchlist/ # Frontend
|
|
├── index.js
|
|
├── components/
|
|
└── style.css
|
|
```
|
|
|
|
### Schéma Base de Données (SQLite)
|
|
|
|
```sql
|
|
-- Table principale : watchlist items
|
|
CREATE TABLE watchlist_items (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL,
|
|
anime_title TEXT NOT NULL,
|
|
anime_url TEXT NOT NULL,
|
|
provider_id TEXT NOT NULL,
|
|
lang TEXT DEFAULT 'vostfr',
|
|
poster_image TEXT,
|
|
cover_image TEXT,
|
|
synopsis TEXT,
|
|
genres TEXT, -- JSON array
|
|
|
|
-- Tracking
|
|
status TEXT DEFAULT 'active', -- active, paused, completed
|
|
auto_download INTEGER DEFAULT 1,
|
|
quality_preference TEXT DEFAULT 'auto',
|
|
last_episode_downloaded INTEGER DEFAULT 0,
|
|
total_episodes INTEGER,
|
|
last_checked_at TEXT,
|
|
|
|
-- Metadata
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
-- Table : Episodes téléchargés
|
|
CREATE TABLE downloaded_episodes (
|
|
id TEXT PRIMARY KEY,
|
|
watchlist_item_id TEXT NOT NULL,
|
|
episode_number INTEGER NOT NULL,
|
|
filename TEXT NOT NULL,
|
|
file_path TEXT,
|
|
file_size INTEGER,
|
|
downloaded_at TEXT NOT NULL,
|
|
FOREIGN KEY (watchlist_item_id) REFERENCES watchlist_items(id)
|
|
);
|
|
|
|
-- Table : Notifications
|
|
CREATE TABLE notifications (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL,
|
|
watchlist_item_id TEXT,
|
|
type TEXT NOT NULL, -- new_episode, download_complete, error
|
|
title TEXT NOT NULL,
|
|
message TEXT,
|
|
read INTEGER DEFAULT 0,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY (watchlist_item_id) REFERENCES watchlist_items(id)
|
|
);
|
|
|
|
-- Table : Settings
|
|
CREATE TABLE watchlist_settings (
|
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
check_interval_hours INTEGER DEFAULT 6,
|
|
auto_download_enabled INTEGER DEFAULT 1,
|
|
max_concurrent_downloads INTEGER DEFAULT 2,
|
|
notifications_enabled INTEGER DEFAULT 1
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Execution Strategy
|
|
|
|
### Wave 1 (Fondations)
|
|
```
|
|
Tâches :
|
|
├── 1. Créer structure du module watchlist/
|
|
├── 2. Créer database.py (connexion SQLite, migrations)
|
|
├── 3. Créer models.py (Pydantic models)
|
|
├── 4. Créer service.py (CRUD operations)
|
|
└── 5. Mettre à jour models/__init__.py
|
|
|
|
Dépendances :Aucune (start immediate)
|
|
```
|
|
|
|
### Wave 2 (API + Scheduler)
|
|
```
|
|
Tâches (dépendent de Wave 1) :
|
|
├── 6. Créer routes/watchlist.py (API endpoints)
|
|
├── 7. Créer scheduler.py (auto-check)
|
|
├── 8. Intégrer scheduler dans main.py
|
|
└── 9. Créer notifications.py
|
|
|
|
Bloqué par : 1-5
|
|
```
|
|
|
|
### Wave 3 (Frontend)
|
|
```
|
|
Tâches (dépendent de Wave 2) :
|
|
├── 10. Créer page HTML watchlist.html
|
|
├── 11. Créer watchlist-ui.js (logique)
|
|
├── 12. Ajouter CSS pour la page
|
|
└── 13. Ajouter routes pour servir la page
|
|
|
|
Bloqué par : 6-9
|
|
```
|
|
|
|
### Wave 4 (Intégration + Tests)
|
|
```
|
|
Tâches :
|
|
├── 14. Tester l'ajout d'un anime
|
|
├── 15. Tester le téléchargement manuel
|
|
├── 16. Tester l'auto-download
|
|
├── 17. Tester les notifications
|
|
└── 18. Nettoyer l'ancien code
|
|
|
|
Bloqué par : 10-13
|
|
```
|
|
|
|
---
|
|
|
|
## TODOs
|
|
|
|
- [ ] 1. **Créer la structure du module watchlist/**
|
|
|
|
**Quoi faire** :
|
|
- Créer le répertoire `app/watchlist/`
|
|
- Créer `__init__.py` avec exports
|
|
|
|
**Pas faire** :
|
|
- Toucher aux autres modules
|
|
|
|
**Agent recommandé** : `quick`
|
|
|
|
**QA Scenarios** :
|
|
```
|
|
Scenario: Le répertoire existe
|
|
Tool: Bash
|
|
Command: ls -la app/watchlist/
|
|
Expected: Le répertoire existe avec __init__.py
|
|
```
|
|
|
|
- [ ] 2. **Créer database.py (connexion SQLite)**
|
|
|
|
**Quoi faire** :
|
|
- Créer `app/watchlist/database.py`
|
|
- Implémenter connexion SQLite avec `sqlite3`
|
|
- Implémenter fonctions : `init_db()`, `get_connection()`, `migrate()`
|
|
- Créer les tables définies dans le schéma
|
|
|
|
**Pas faire** :
|
|
- Toucher aux autres fichiers
|
|
|
|
**Agent recommandé** : `quick`
|
|
|
|
**QA Scenarios** :
|
|
```
|
|
Scenario: La base de données est créée
|
|
Tool: Bash
|
|
Command: python3 -c "from app.watchlist.database import init_db; init_db(); import os; print(os.path.exists('config/watchlist.db'))"
|
|
Expected: True
|
|
|
|
Scenario: Les tables existent
|
|
Tool: Bash
|
|
Command: sqlite3 config/watchlist.db ".tables"
|
|
Expected: watchlist_items downloaded_episodes notifications watchlist_settings
|
|
```
|
|
|
|
- [ ] 3. **Créer models.py**
|
|
|
|
**Quoi faire** :
|
|
- Créer `app/watchlist/models.py`
|
|
- Définir les modèles Pydantic :
|
|
- WatchlistItem, WatchlistItemCreate, WatchlistItemUpdate
|
|
- DownloadedEpisode
|
|
- Notification, NotificationCreate
|
|
- WatchlistSettings
|
|
- Utiliser les types existants de `app/models/`
|
|
|
|
**Pas faire** :
|
|
- Dupliquer les types existants
|
|
|
|
**Agent recommandé** : `quick`
|
|
|
|
**QA Scenarios** :
|
|
```
|
|
Scenario: Les modèles peuvent être importés
|
|
Tool: Bash
|
|
Command: python3 -c "from app.watchlist.models import WatchlistItem, Notification; print('OK')"
|
|
Expected: OK (no error)
|
|
```
|
|
|
|
- [ ] 4. **Créer service.py**
|
|
|
|
**Quoi faire** :
|
|
- Créer `app/watchlist/service.py`
|
|
- Implémenter `WatchlistService` avec :
|
|
- `add_item()`, `get_items()`, `get_item()`, `update_item()`, `delete_item()`
|
|
- `mark_episode_downloaded()`, `get_downloaded_episodes()`
|
|
- `create_notification()`, `get_notifications()`, `mark_notification_read()`
|
|
- `get_settings()`, `update_settings()`
|
|
- `get_items_due_for_check()`
|
|
- Utiliser SQLite directement (pas d'ORM)
|
|
|
|
**Pas faire** :
|
|
- Toucher au frontend
|
|
|
|
**Agent recommandé** : `unspecified-high`
|
|
|
|
**QA Scenarios** :
|
|
```
|
|
Scenario: Ajouter un item à la watchlist
|
|
Tool: Bash
|
|
Command: python3 -c "
|
|
from app.watchlist.service import WatchlistService
|
|
svc = WatchlistService()
|
|
item = svc.add_item(user_id='test', anime_title='Test Anime', anime_url='https://example.com', provider_id='anime-sama')
|
|
print(f'Created: {item.id}')
|
|
"
|
|
Expected: Un UUID est retourné
|
|
|
|
Scenario: Récupérer les items
|
|
Tool: Bash
|
|
Command: python3 -c "
|
|
from app.watchlist.service import WatchlistService
|
|
svc = WatchlistService()
|
|
items = svc.get_items()
|
|
print(f'Count: {len(items)}')
|
|
"
|
|
Expected: Count: 1
|
|
```
|
|
|
|
- [ ] 5. **Mettre à jour models/__init__.py**
|
|
|
|
**Quoi faire** :
|
|
- Ajouter export des nouveaux modèles si besoin
|
|
|
|
**Agent recommandé** : `quick`
|
|
|
|
- [ ] 6. **Créer routes/watchlist.py**
|
|
|
|
**Quoi faire** :
|
|
- Créer `app/routes/watchlist.py`
|
|
- Définir les endpoints :
|
|
- `GET /api/watchlist` - Liste des items
|
|
- `POST /api/watchlist` - Ajouter un item
|
|
- `GET /api/watchlist/{id}` - Détail d'un item
|
|
- `PUT /api/watchlist/{id}` - Modifier un item
|
|
- `DELETE /api/watchlist/{id}` - Supprimer un item
|
|
- `POST /api/watchlist/{id}/download/{episode}` - Télécharger un épisode
|
|
- `GET /api/watchlist/{id}/episodes` - Épisodes téléchargés
|
|
- `GET /api/watchlist/notifications` - Liste des notifications
|
|
- `PUT /api/watchlist/notifications/{id}/read` - Marquer comme lu
|
|
- `GET /api/watchlist/settings` - Settings
|
|
- `PUT /api/watchlist/settings` - Mettre à jour settings
|
|
- Ajouter auth (Bearer token)
|
|
- Intégrer avec `download_manager` pour les téléchargements
|
|
|
|
**Agent recommandé** : `unspecified-high`
|
|
|
|
**QA Scenarios** :
|
|
```
|
|
Scenario: L'API répond
|
|
Tool: Bash
|
|
Command: curl -s http://127.0.0.1:3000/api/watchlist
|
|
Expected: {"items": [...], "count": N}
|
|
```
|
|
|
|
- [ ] 7. **Créer scheduler.py**
|
|
|
|
**Quoi faire** :
|
|
- Créer `app/watchlist/scheduler.py`
|
|
- Implémenter `WatchlistScheduler` :
|
|
- `start()`, `stop()`
|
|
- `_check_loop()` - Boucle principale
|
|
- `check_item(item)` - Vérifier un anime
|
|
- `download_new_episodes(item, new_episodes)` - Télécharger
|
|
- Utiliser `APScheduler` (déjà dans requirements)
|
|
- Intervalle configurable (défaut: 6h)
|
|
|
|
**Agent recommandé** : `unspecified-high`
|
|
|
|
- [ ] 8. **Intégrer scheduler dans main.py**
|
|
|
|
**Quoi faire** :
|
|
- Importer et initialiser le scheduler
|
|
- Ajouter au startup event
|
|
- Ajouter au shutdown event
|
|
|
|
**Agent recommandé** : `quick`
|
|
|
|
- [ ] 9. **Créer notifications.py**
|
|
|
|
**Quoi faire** :
|
|
- Créer `app/watchlist/notifications.py`
|
|
- Implémenter `NotificationService`
|
|
- Types de notifications :
|
|
- `new_episode` - Nouvel épisode détecté
|
|
- `download_started` - Téléchargement commencé
|
|
- `download_complete` - Téléchargement terminé
|
|
- `download_error` - Erreur de téléchargement
|
|
- Stocker dans SQLite
|
|
- Retourner via API pour affichage
|
|
|
|
**Agent recommandé** : `quick`
|
|
|
|
- [ ] 10. **Créer page HTML watchlist.html**
|
|
|
|
**Quoi faire** :
|
|
- Créer `templates/watchlist.html`
|
|
- Même structure que `index.html`
|
|
- Sections :
|
|
- Header avec stats
|
|
- Liste des animes (cards)
|
|
- Zone de notifications
|
|
- Modal pour les détails
|
|
|
|
**Agent recommandé** : `visual-engineering`
|
|
|
|
**QA Scenarios** :
|
|
```
|
|
Scenario: La page se charge
|
|
Tool: playwright
|
|
Navigate: http://127.0.0.1:3000/watchlist
|
|
Expected: Titre "Ma Watchlist" affiché
|
|
```
|
|
|
|
- [ ] 11. **Créer watchlist-ui.js**
|
|
|
|
**Quoi faire** :
|
|
- Créer `static/js/watchlist/main.js`
|
|
- Fonctions :
|
|
- `loadWatchlist()` - Charger la liste
|
|
- `renderWatchlist(items)` - Afficher les cards
|
|
- `addAnime(animeData)` - Ajouter un anime
|
|
- `removeAnime(id)` - Retirer
|
|
- `downloadEpisode(itemId, episode)` - Télécharger
|
|
- `loadNotifications()` - Charger les notifs
|
|
- `renderNotifications(notifs)` - Afficher
|
|
- `markAsRead(id)` - Marquer lu
|
|
- Appels API vers les endpoints créés
|
|
|
|
**Agent recommandé** : `visual-engineering`
|
|
|
|
- [ ] 12. **Ajouter CSS**
|
|
|
|
**Quoi faire** :
|
|
- Créer `static/css/watchlist.css`
|
|
- Style cohérent avec `style.css` existant
|
|
- Cards, badges, buttons, notifications
|
|
|
|
**Agent recommandé** : `visual-engineering`
|
|
|
|
- [ ] 13. **Ajouter routes pour servir la page**
|
|
|
|
**Quoi faire** :
|
|
- Ajouter route `GET /watchlist` dans main.py
|
|
- Servir le template
|
|
|
|
**Agent recommandé** : `quick`
|
|
|
|
- [ ] 14-17. **Tests d'intégration**
|
|
|
|
**Quoi faire** :
|
|
- Tester le flux complet :
|
|
1. Ajouter un anime via API
|
|
2. Voir dans la liste
|
|
3. Télécharger un épisode manuellement
|
|
4. Recevoir une notification
|
|
- Tester l'auto-download (simuler un nouvel épisode)
|
|
|
|
**Agent recommandé** : `unspecified-high`
|
|
|
|
- [ ] 18. **Nettoyer l'ancien code**
|
|
|
|
**Quoi faire** :
|
|
- Supprimer `app/watchlist.py` (l'ancien)
|
|
- Supprimer les fichiers JSON `config/watchlist*.json`
|
|
- Mettre à jour les imports
|
|
|
|
**Agent recommandé** : `quick`
|
|
|
|
---
|
|
|
|
## Stratégie de Vérification
|
|
|
|
### Test Manual (Agent QA)
|
|
|
|
**Scenario: Ajout d'un anime**
|
|
```
|
|
1. Ouvrir /watchlist
|
|
2. Cliquer "Ajouter un anime"
|
|
3. Rechercher "Frieren"
|
|
4. Sélectionner un résultat
|
|
5. Cliquer "Suivre"
|
|
Expected: L'anime apparaît dans la liste
|
|
```
|
|
|
|
**Scenario: Téléchargement manuel**
|
|
```
|
|
1. Dans la watchlist, cliquer sur un anime
|
|
2. Voir la liste des épisodes
|
|
3. Cliquer "Télécharger" sur épisode 1
|
|
4. Vérifier dans /downloads
|
|
Expected: Le téléchargement commence
|
|
```
|
|
|
|
**Scenario: Auto-download**
|
|
```
|
|
1. Ajouter un anime avec auto-download activé
|
|
2. Simuler l'apparition d'un nouvel épisode (via scheduler)
|
|
3. Vérifier dans les downloads
|
|
Expected: L'épisode est téléchargé automatiquement
|
|
```
|
|
|
|
**Scenario: Notification**
|
|
```
|
|
1. Un nouvel épisode est détecté
|
|
2. Une notification apparaît
|
|
3. Cliquer sur la notification
|
|
Expected: Redirection vers l'épisode
|
|
```
|
|
|
|
---
|
|
|
|
## Critères de Succès
|
|
|
|
- [ ] La base SQLite est créée et fonctionnelle
|
|
- [ ] Les animes peuvent être ajoutés/retirés de la watchlist
|
|
- [ ] Les épisodes peuvent être téléchargés manuellement
|
|
- [ ] Le scheduler vérifie automatiquement les nouveaux épisodes
|
|
- [ ] L'auto-téléchargement fonctionne
|
|
- [ ] Les notifications sont créées et affichées
|
|
- [ ] L'interface est cohérente avec le reste du site
|
|
- [ ] L'ancien code est nettoyé
|
|
|
|
---
|
|
|
|
## Commit Strategy
|
|
|
|
- Wave 1: `feat(watchlist): add SQLite database and models`
|
|
- Wave 2: `feat(watchlist): add API routes and scheduler`
|
|
- Wave 3: `feat(watchlist): add frontend UI`
|
|
- Wave 4: `feat(watchlist): integrate and test`
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- Le système de download existant (`download_manager`) est réutilisé
|
|
- Les providers existants (anime-sama, vostfree, etc.) sont réutilisés
|
|
- Le système de notification est simple (in-app) pour éviter les dépendances supplémentaires
|
|
- Le scheduler utilise APScheduler déjà présent dans le projet
|