# 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