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

14 KiB

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)

-- 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