Deux outils MCP pour qu'Hermes n'ait plus de scripts à écrire :
- hf_next_delivery() : prochaine box RÉELLEMENT sélectionnée (≈4 recettes,
pas le menu complet) + date/cutoff ; erreur stricte si introuvable
(jamais de repli propose). Saute les semaines PAUSED via next_delivery.
- hf_favorites() : recettes favorites du compte. Champ is_favorite ajouté
partout (hf_get_menu inclus).
Endpoints découverts (probe CDP) :
- sélection : GET /gw/my-deliveries/menu -> meals[].selection.quantity>0
- favoris : GET /gw/cfs/v2/favorites/recipe -> items[].object_id
(GET /gw/v1/carts/{week} renvoie 404 : pas la lecture de sélection.)
Images : URLs recettes CloudFront (502) réécrites vers
img.hellofresh.com/.../hellofresh_s3/... (hellofresh/images.py),
appliqué dans Recipe.summary() -> profite à tous les outils.
README : procédure de ré-auth CDP clarifiée (refresh tokens rotatifs,
backups inutiles, page /login, profil Chrome dédié).
Outils de re-découverte : tools/probe_selection.py, tools/probe_menu_capture.py
74 lines
2.9 KiB
Python
74 lines
2.9 KiB
Python
"""Normalisation des URLs d'images recettes pour un rendu direct (Telegram, etc.).
|
|
|
|
L'API recettes renvoie des URLs CloudFront du type
|
|
https://d3hvwccx09j84u.cloudfront.net/0,0/image/HF_....jpg
|
|
qui répondent en 502 au téléchargement direct (hotlink protégé). Le CDN servable est
|
|
https://img.hellofresh.com/<transfo>/hellofresh_s3/image/HF_....jpg
|
|
où le segment `0,0` (largeur,hauteur) de CloudFront est remplacé par une transformation
|
|
Cloudinary (`f_auto,fl_lossy,q_auto,w_1200`) et le chemin préfixé par `hellofresh_s3`.
|
|
|
|
Confirmé par probe (2026-06) : la variante img.hellofresh.com/.../hellofresh_s3/… renvoie 206 +
|
|
image/jpeg, alors que l'URL CloudFront brute renvoie 502. Hôte/transfo configurables via
|
|
config/endpoints.json (`image_cdn_host`, `image_cdn_transform`, `image_cdn_source_hosts`).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
from functools import lru_cache
|
|
from urllib.parse import urlsplit
|
|
|
|
from . import auth
|
|
|
|
_ENDPOINTS_PATH = auth.ROOT / "config" / "endpoints.json"
|
|
# Segment de dimensionnement CloudFront en tête de chemin, ex. "/0,0/" ou "/200,200/".
|
|
_SIZE_SEG = re.compile(r"^/\d+,\d+(?=/)")
|
|
|
|
_DEFAULTS = {
|
|
"image_cdn_host": "img.hellofresh.com",
|
|
"image_cdn_transform": "f_auto,fl_lossy,q_auto,w_1200",
|
|
"image_cdn_source_hosts": ["d3hvwccx09j84u.cloudfront.net"],
|
|
}
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def _cfg() -> tuple[str, str, tuple[str, ...]]:
|
|
"""(host cible, transfo, hôtes source) lus depuis endpoints.json, avec défauts."""
|
|
data: dict = {}
|
|
try:
|
|
data = json.loads(_ENDPOINTS_PATH.read_text(encoding="utf-8"))
|
|
except Exception:
|
|
pass
|
|
host = str(data.get("image_cdn_host") or _DEFAULTS["image_cdn_host"])
|
|
transform = str(data.get("image_cdn_transform") or _DEFAULTS["image_cdn_transform"])
|
|
sources = tuple(data.get("image_cdn_source_hosts") or _DEFAULTS["image_cdn_source_hosts"])
|
|
return host, transform, sources
|
|
|
|
|
|
def fix_image_url(url: str) -> str:
|
|
"""Réécrit une URL d'image CloudFront HelloFresh vers le CDN servable.
|
|
|
|
- URL vide → "".
|
|
- Déjà sur un hôte hellofresh.com → renvoyée telle quelle.
|
|
- Hôte CloudFront connu (ou *.cloudfront.net) → host remplacé, segment de taille retiré,
|
|
chemin préfixé par `hellofresh_s3` et la transformation insérée.
|
|
- Tout autre hôte → renvoyée telle quelle (on ne casse rien).
|
|
"""
|
|
if not url:
|
|
return ""
|
|
host, transform, sources = _cfg()
|
|
parts = urlsplit(url)
|
|
netloc = parts.netloc.lower()
|
|
|
|
if "hellofresh.com" in netloc: # déjà servable (img/media.hellofresh.com)
|
|
return url
|
|
if netloc not in sources and not netloc.endswith(".cloudfront.net"):
|
|
return url
|
|
|
|
path = _SIZE_SEG.sub("", parts.path) # "/0,0/image/X.jpg" -> "/image/X.jpg"
|
|
if not path.startswith("/"):
|
|
path = "/" + path
|
|
rest = path if path.startswith("/hellofresh_s3/") else "/hellofresh_s3" + path
|
|
return f"https://{host}/{transform}{rest}"
|