Files
ohm_streaming/.sisyphus/plans/watchlist-refonte.md
2026-02-28 09:22:57 +00:00

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