c46a2e268b
Design and implementation plan for Tower auto-push: registration- triggered, weekly, podcasts only. Ignore __pycache__ and *.real editor backups.
6.1 KiB
6.1 KiB
Spec A — Tower pousse les podcasts automatiquement (design)
Date: 2026-06-15
Statut: validé (brainstorming), prêt pour le plan.
Projet: Lisael Box (ESP32-S3-BOX-3) + Tower (clems@192.168.0.120).
But
Garder les podcasts de la box à jour automatiquement, sans MSC ni
intervention : Tower (toujours allumé) rafraîchit les derniers épisodes Radio
France chaque semaine et les pousse vers la box dès qu'elle se signale en
ligne. La box ne fait jamais de téléchargement lourd (elle reçoit en HTTP simple
via son file-server :8080 déjà en place).
Décisions (validées)
- Auto-refresh : podcasts seulement (épisodes + pochettes + manifeste).
- Découverte/déclenchement : enregistrement — la box s'annonce à Tower, qui pousse alors le delta (fichiers que la box n'a pas).
- Fréquence du refresh Tower : hebdomadaire.
- Port serveur Tower : :9555 ; id box fixe :
lisael-box.
Architecture
Tower (192.168.0.120) Box (Les cils, DHCP)
┌─────────────────────────────┐ ┌────────────────────────┐
│ lisael-refresh.timer (hebdo)│ │ net_task @ WiFi connect │
│ → lisael_refresh.py │ │ lit /sdcard/podcasts │
│ RSS RF → épisodes+cover │ │ POST /register ───────┼──┐
│ → /home/clems/lisael- │ └────────────────────────┘ │
│ content/podcasts/ (stg)│ │
│ │ ◄──── POST /register {id,ip,have} ───────────┘
│ lisael-content.service :9555 │
│ /register → diff(stg,have) │ ──── POST :8080/put?name=podcasts/<f> ───► file-server
│ → push delta + manifest │ (épisodes manquants + manifest.json) → /sdcard/podcasts
└─────────────────────────────┘
Composants
Tower — lisael_refresh.py (timer hebdo)
- Pour chaque show (Oli
rss_19721, Les Odysséesrss_20108, Salut l'inforss_20689, Bestioles proxycast) : fetch RSS, prendre les N derniers épisodes (N=3 configurable), télécharger l'enclosure (HTTPS), récupérer<itunes:image>→ redimensionner 160×160 →.binLVGL RGB565 viaLVGLImage.py. Noms ASCIIkey_NN.<ext>/key.bin. - Reconstruire
manifest.jsonv2 =[{show,cover,episodes:[{file,title}]}]. - Écrire le tout dans le staging
/home/clems/lisael-content/podcasts/(atomique : écrire dans un tmp puis renommer). Reprend la logique éprouvée detools/gen_covers.py. Idempotent (skip si déjà présent).
Tower — lisael_server.py (service :9555, toujours actif)
POST /registercorps JSON{ "id": "lisael-box", "have": ["oli_01.m4a", ...] }. L'IP de la box = l'IP source de la requête (client_address), pas besoin que la box l'envoie.- À la réception : calcule le delta = (fichiers du staging) − (
have). Pour chaque fichier manquant (audio +.bindont la pochette),POST http://<ip>:8080/put?name=podcasts/<f>(corps = octets du fichier). Puis pousse toujoursmanifest.jsonen dernier (il reflète le staging courant). - Journalise (
/var/logou journald) : ip, nb fichiers poussés, erreurs. - Stdlib only (
http.server+urllibpour le push) — pas de dépendance.
Box — main/net/box_register.c / .h
void lisael_box_register(void);: lit/sdcard/podcasts/*.m4a|*.mp3(basenames) → construit un JSON{id, have:[...]}→POST http://192.168.0.120:9555/registerviaesp_http_client(HTTP simple, court timeout, non bloquant pour l'UI). Pas besoin d'envoyer l'IP (Tower lit la source). Échec → silencieux (best-effort).- Appelé depuis
app_main.c net_task, dans le chemin connecté (aprèslisael_file_server_start()), une fois.
Données
- Register (box→Tower) :
{id:string, have:[string]}(l'IP = source de la requête). - Staging Tower :
/home/clems/lisael-content/podcasts/{<key>_<NN>.<ext>, <key>.bin, manifest.json}— miroir exact de ce que la box doit avoir sous/sdcard/podcasts/. - Push (Tower→box) :
POST :8080/put?name=podcasts/<basename>(handler existant, crée/sdcard/podcasts/au besoin).
Gestion d'erreurs / robustesse
- Box éteinte → pas de
/register→ pas de push ; rattrapé au prochain boot. - RSS/HTTPS RF en échec pendant le refresh → garder l'ancien staging (écriture atomique), réessayer la semaine suivante.
- Push interrompu (box se déconnecte) → état partiel sur la box ; le prochain
/registerrenverra unhaveréduit → Tower repousse le manquant (delta auto-réparant). - Tower indisponible au boot box →
/registeréchoue silencieusement, la box fonctionne avec son contenu actuel.
Sécurité
LAN domestique : pas d'auth (comme le file-server :8080). Le serveur :9555
n'accepte que /register ; le push ne touche que /sdcard/podcasts/.
Hors-scope v1
- Rafraîchir l'écran Histoires sans reboot (le manifeste est lu à l'ouverture ; les nouveaux épisodes apparaissent au prochain boot).
- Multi-box (l'id permet d'évoluer), auth, contenu Lire/webradios auto.
Tests (acceptation)
lisael_refresh.pyà la main sur Tower → staging peuplé (épisodes +.bin+ manifest v2 cohérent).lisael-content.serviceup ;curl -XPOST :9555/register -d '{...,"have":[]}'depuis le Mac → Tower pousse tout vers une box de test (ou un faux file-server).- Box réelle : reboot → elle s'enregistre (log Tower montre son ip + un delta) →
après le push,
/sdcard/podcasts/contient les nouveaux épisodes (vérif via le prochain boot : ils apparaissent dans Histoires). - Re-boot sans nouvel épisode →
havecomplet → delta vide → aucun push. - Couper Tower → boot box → pas d'erreur, contenu inchangé.