msansm1/rss-podcast-dlpublic Fork
⑂ main
Commits
⬇ Clone ▾
git clone https://git.rickub.com/msansm1/rss-podcast-dl.git
git clone ssh://git@rickub.com/msansm1/rss-podcast-dl.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
UX and transcode updates b0e5d07 · on main · msansm1 · 2h ago
AGENTS.md · 65 lines · 5.7 KBmarkdown
Blame HistoryOpen raw

AGENTS.md — rss-podcast-dl-egui

Application desktop Rust/egui (egui + eframe 0.35) pour télécharger des podcasts depuis des flux RSS. MSRV 1.92.

Commandes

cargo build                # build debug
cargo run                  # lance l'app
cargo test                 # 98 tests (modèles, storage, parsing, downloader, extraction, UI)
cargo clippy --all-targets # doit passer sans aucun avertissement

Attention : ne pas lancer cargo fmt sur l'ensemble du dépôt — il n'est pas formaté à rustfmt et un fmt global crée un diff de plusieurs centaines de lignes. Suivre le style du fichier voisin ; formater uniquement les fichiers nouveaux ou entièrement réécrits (rustfmt --edition 2021 <fichier>).

Clippy sans warning est exigé : tout nouveau warning doit être corrigé, pas masqué par #[allow] (les seuls #![allow(dead_code)] existants marquent des fonctionnalités prévues par la spec mais pas encore branchées dans l'UI).

Architecture

src/
├── main.rs / app.rs   # point d'entrée ; RssPodcastApp (impl eframe::App), chargement initial
├── config/            # modèles + persistance (séparés de l'UI)
│   ├── app_state.rs   # AppState : tout l'état mutable de l'app (champs pub)
│   ├── feed.rs / episode.rs / playlist.rs   # modèles serde (IDs = UUID v4)
│   ├── storage.rs     # save/load JSON dans %APPDATA%/rss-podcast-dl (unix : ~/.config/rss-podcast-dl)
│   ├── settings.rs    # settings.json (thème), history.json
│   ├── history.rs     # historique des opérations, auto-persisté
│   └── sync_task.rs   # SyncTaskManager : threads + mpsc, statut + progression par flux
├── rss/               # I/O réseau et fichiers
│   ├── parser.rs / episode_extractor.rs  # récupération + parsing du flux (crate rss)
│   ├── async_sync.rs  # synchro asynchrone : émet SyncProgress épisode par épisode
│   ├── downloader.rs  # téléchargement atomique : .part puis rename, via reqwest blocking
│   ├── transcode.rs    # réencodage .m4a -> .mp3 via ffmpeg (fallback : original conservé)
│   └── audio_metadata.rs                 # tags ID3 (crate id3)
└── ui/                # rendu pur : fonctions draw_*(state: &mut AppState, ui)
    ├── feed_list.rs   # liste des flux ; barre de progression de synchro par ligne
    └── sync_status.rs # indicateur global + draw_progress_bar (réutilisé par feed_list)

Patterns à respecter

  • State centralisé : tout état mutable va dans AppState (struct plat, champs pub). Les composants UI sont des fonctions libres draw_...(&mut AppState, &mut egui::Ui) — pas de widgets à état interne.
  • Jamais de blocage du thread UI : les opérations réseau/downloads tournent dans des threads détachés et communiquent via mpsc (SyncMessage dans sync_task.rs). Les messages sont drainés par try_recv au fil des frames ; on ne fait pas de recv() bloquant dans la boucle egui.
  • Progression de synchro par flux : async_sync.rs émet SyncMessage::SyncProgress (0.0 → 1.0) au fil des épisodes ; SyncStatus.feed_sync_progress stocke la progression courante. feed_list.rs affiche une barre sur la ligne de chaque flux concerné. En synchro globale, start_global_sync marque tous les flux Pending (barre visible sur toutes les lignes) ; complete_global_sync vide feed_sync_progress pour faire disparaître les barres simultanément.
  • Téléchargements atomiques : toujours écrire via un fichier .part puis renommer. Ne jamais écrire directement le fichier final.
  • Persistance : sauvegarder le JSON après chaque mutation de feeds/playlists (storage::save_*_default). Historique auto-persisté via HistoryManager::set_save_path.
  • Couleurs : uniquement via la palette ui::theme::ThemeColors (apply_custom_theme). Pas de couleur en dur dans les composants — les deux thèmes (sombre/clair) doivent rester cohérents.
  • Feedback utilisateur : erreurs/succès via NotificationManager (notifications flottantes), jamais de panic! ni eprintln! pour une erreur runtime ; tout logging passe par la crate log.
  • Erreurs : types dédiés via thiserror (ex. DownloadError, StorageError), pas de String nues pour les erreurs internes.
  • Modèles serde : tout champ sérialisé doit être Serialize + Deserialize avec #[serde(default)] quand l'ancien format ne l'avait pas — rester tolérant aux configs existantes.
  • Suppressions : toujours passer par un dialogue de confirmation (confirm_dialog.rs), jamais en direct.

Conventions de code

  • Édition 2021, Rust 2021 idiomatique ; commentaires de doc /// et en-têtes de module //! obligatoires.
  • Docs/UI en français (README, textes), identifiants et doc-comments en anglais — suivre le fichier voisin.
  • Tests unitaires dans chaque module via #[cfg(test)] mod tests ; utiliser tempfile pour tout fichier temporaire, ne jamais toucher au vrai répertoire de config de l'utilisateur dans un test.
  • Pas de nouvelle dépendance sans justification (Cargo.toml est volontairement minimal, reqwest en mode blocking uniquement).

Notes

  • spec/ contient le plan initial et les tâches — la roadmap du README fait foi pour ce qui est fait/reste à faire.
  • Les émojis dans les libellés de boutons existent dans l'UI ; ne pas en ajouter dans le code, les commits ou les commentaires.
  • Fins de ligne : l'arbre de travail est en LF mais core.autocrlf=true (gitconfig système Git for Windows). Ne pas faire git checkout -- <fichiers> pour annuler des changements : les fichiers repasseraient en CRLF et casseraient les outils d'édition. Annuler via git -c core.autocrlf=false checkout -- <fichiers> puis normaliser en LF si besoin.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# AGENTS.md — rss-podcast-dl-egui

Application desktop Rust/egui (egui + eframe 0.35) pour télécharger des podcasts depuis des flux RSS. MSRV 1.92.

## Commandes

```bash
cargo build                # build debug
cargo run                  # lance l'app
cargo test                 # 98 tests (modèles, storage, parsing, downloader, extraction, UI)
cargo clippy --all-targets # doit passer sans aucun avertissement
```

Attention : ne pas lancer `cargo fmt` sur l'ensemble du dépôt — il n'est pas formaté à rustfmt et un fmt global crée un diff de plusieurs centaines de lignes. Suivre le style du fichier voisin ; formater uniquement les fichiers nouveaux ou entièrement réécrits (`rustfmt --edition 2021 <fichier>`).

Clippy sans warning est exigé : tout nouveau warning doit être corrigé, pas masqué par `#[allow]` (les seuls `#![allow(dead_code)]` existants marquent des fonctionnalités prévues par la spec mais pas encore branchées dans l'UI).

## Architecture

```
src/
├── main.rs / app.rs   # point d'entrée ; RssPodcastApp (impl eframe::App), chargement initial
├── config/            # modèles + persistance (séparés de l'UI)
│   ├── app_state.rs   # AppState : tout l'état mutable de l'app (champs pub)
│   ├── feed.rs / episode.rs / playlist.rs   # modèles serde (IDs = UUID v4)
│   ├── storage.rs     # save/load JSON dans %APPDATA%/rss-podcast-dl (unix : ~/.config/rss-podcast-dl)
│   ├── settings.rs    # settings.json (thème), history.json
│   ├── history.rs     # historique des opérations, auto-persisté
│   └── sync_task.rs   # SyncTaskManager : threads + mpsc, statut + progression par flux
├── rss/               # I/O réseau et fichiers
│   ├── parser.rs / episode_extractor.rs  # récupération + parsing du flux (crate rss)
│   ├── async_sync.rs  # synchro asynchrone : émet SyncProgress épisode par épisode
│   ├── downloader.rs  # téléchargement atomique : .part puis rename, via reqwest blocking
│   ├── transcode.rs    # réencodage .m4a -> .mp3 via ffmpeg (fallback : original conservé)
│   └── audio_metadata.rs                 # tags ID3 (crate id3)
└── ui/                # rendu pur : fonctions draw_*(state: &mut AppState, ui)
    ├── feed_list.rs   # liste des flux ; barre de progression de synchro par ligne
    └── sync_status.rs # indicateur global + draw_progress_bar (réutilisé par feed_list)
```

## Patterns à respecter

- **State centralisé** : tout état mutable va dans `AppState` (struct plat, champs `pub`). Les composants UI sont des fonctions libres `draw_...(&mut AppState, &mut egui::Ui)` — pas de widgets à état interne.
- **Jamais de blocage du thread UI** : les opérations réseau/downloads tournent dans des threads détachés et communiquent via `mpsc` (`SyncMessage` dans `sync_task.rs`). Les messages sont drainés par `try_recv` au fil des frames ; on ne fait pas de `recv()` bloquant dans la boucle egui.
- **Progression de synchro par flux** : `async_sync.rs` émet `SyncMessage::SyncProgress` (0.0 → 1.0) au fil des épisodes ; `SyncStatus.feed_sync_progress` stocke la progression courante. `feed_list.rs` affiche une barre sur la ligne de chaque flux concerné. En synchro globale, `start_global_sync` marque tous les flux `Pending` (barre visible sur toutes les lignes) ; `complete_global_sync` vide `feed_sync_progress` pour faire disparaître les barres simultanément.
- **Téléchargements atomiques** : toujours écrire via un fichier `.part` puis renommer. Ne jamais écrire directement le fichier final.
- **Persistance** : sauvegarder le JSON après chaque mutation de feeds/playlists (`storage::save_*_default`). Historique auto-persisté via `HistoryManager::set_save_path`.
- **Couleurs** : uniquement via la palette `ui::theme::ThemeColors` (`apply_custom_theme`). Pas de couleur en dur dans les composants — les deux thèmes (sombre/clair) doivent rester cohérents.
- **Feedback utilisateur** : erreurs/succès via `NotificationManager` (notifications flottantes), jamais de `panic!` ni `eprintln!` pour une erreur runtime ; tout logging passe par la crate `log`.
- **Erreurs** : types dédiés via `thiserror` (ex. `DownloadError`, `StorageError`), pas de `String` nues pour les erreurs internes.
- **Modèles serde** : tout champ sérialisé doit être `Serialize + Deserialize` avec `#[serde(default)]` quand l'ancien format ne l'avait pas — rester tolérant aux configs existantes.
- **Suppressions** : toujours passer par un dialogue de confirmation (`confirm_dialog.rs`), jamais en direct.

## Conventions de code

- Édition 2021, Rust 2021 idiomatique ; commentaires de doc `///` et en-têtes de module `//!` obligatoires.
- Docs/UI en français (README, textes), identifiants et doc-comments en anglais — suivre le fichier voisin.
- Tests unitaires dans chaque module via `#[cfg(test)] mod tests` ; utiliser `tempfile` pour tout fichier temporaire, ne jamais toucher au vrai répertoire de config de l'utilisateur dans un test.
- Pas de nouvelle dépendance sans justification (Cargo.toml est volontairement minimal, `reqwest` en mode blocking uniquement).

## Notes

- `spec/` contient le plan initial et les tâches — la roadmap du README fait foi pour ce qui est fait/reste à faire.
- Les émojis dans les libellés de boutons existent dans l'UI ; ne pas en ajouter dans le code, les commits ou les commentaires.
- Fins de ligne : l'arbre de travail est en LF mais `core.autocrlf=true` (gitconfig système Git for Windows). Ne pas faire `git checkout -- <fichiers>` pour annuler des changements : les fichiers repasseraient en CRLF et casseraient les outils d'édition. Annuler via `git -c core.autocrlf=false checkout -- <fichiers>` puis normaliser en LF si besoin.