MamieTV génère une page statique qui affiche ce qui passe en prime time (21 h – minuit) sur les chaînes de la TNT française.
Un seul script Python (lancé via uv) télécharge le guide XMLTV, choisit les
programmes, télécharge les logos et écrit un dossier dist/ prêt à publier —
sur GitHub Pages ou n'importe quel hébergeur statique. Aucun serveur applicatif.
uv run python build.py # écrit dist/
python3 -m http.server -d dist 8000 # aperçu local- Téléchargement (
utils/xmltv.py) : le guide XMLTV de XML TV Fr (xmltv_tnt.xml.gz, régénéré chaque jour). - Sélection : on ne garde que la fenêtre « ce soir » (21 h → minuit), et par chaîne les deux « vrais » programmes de la soirée (le prime et sa suite), façon Télé-Loisirs. Les interstitiels de moins de 25 min (météo, bandes-annonces) sont ignorés au moment de choisir. Un programme commencé avant 21 h est gardé s'il lui reste au moins 30 min (un match à 20 h 35), pas s'il se termine vers 21 h 25 (Quotidien, émissions d'avant-soirée).
- Logos (
utils/images.py) : téléchargés en 96×96 (~6 ko au lieu de 130 ko) dansdist/channels/, servis depuis notre origine (aucun hôte tiers). - Rendu (
utils/render.py) :dist/est recréé à partir destatic/(assets versionnés), puisdist/index.html(Jinja2 +minify-html) etdist/data/epg.jsonsont écrits de façon atomique, avecdist/data/version.json(justegenerated_at) que la page interroge toutes les 15 min pour savoir si un guide plus récent est publié.
dist/
├── index.html # page générée (URLs relatives)
├── data/epg.json # données du soir
├── data/version.json # generated_at seul, pour le rafraîchissement
├── channels/*.png # logos téléchargés
├── css/ js/ icons/ # assets copiés depuis static/
├── manifest.webmanifest # manifeste PWA
├── sw.js # service worker (hors ligne)
├── robots.txt
└── 404.html
Toutes les URLs sont relatives, donc le site fonctionne aussi bien à la
racine qu'en sous-chemin de projet (https://exemple.github.io/mamietv/).
mamietv/
├── build.py # point d'entrée : uv run python build.py
├── mamietv.toml # configuration (source, fenêtre, chaînes)
├── pyproject.toml # dépendances (uv), ruff, pytest
├── utils/
│ ├── config.py # vue typée de mamietv.toml
│ ├── xmltv.py # téléchargement + parsing + sélection
│ ├── images.py # téléchargement des logos
│ ├── render.py # copie des assets + rendu dans dist/
│ ├── build.py # orchestration
│ └── logs.py # structlog
├── templates/ # Jinja2 (index, head)
├── static/ # assets sources versionnés (css, js, icons, PWA…)
├── build_tools/
│ └── make_icons.py # génère les icônes PWA (Pillow, dev uniquement)
└── tests/ # pytest
Prérequis : Python 3.14, épinglé dans .python-version (minify-html n'a pas
encore de wheel pour Python 3.15), et uv.
uv sync
uv run python build.pyAvec go-task : task build, task preview,
task icons, task check.
[source]
url = "https://xmltvfr.fr/xmltv/xmltv_tnt.xml.gz"
[evening]
start_hour = 21
start_minute = 0
end_hour = 0
end_minute = 0
carry_over_minutes = 30 # un show commencé avant 21 h compte s'il reste ≥ 30 min
max_programs = 2 # nombre de shows retenus par chaîne (le 1er et le 2e)
min_duration_minutes = 25 # en-dessous, considéré comme un interstitiel
channels = [] # liste blanche ; vide = tous les programmes du guideVariables d'environnement : LOGLEVEL, et MAMIETV_STATIC_DIR,
MAMIETV_TEMPLATES_DIR, MAMIETV_CONFIG, MAMIETV_DIST_DIR pour déplacer les
chemins.
dist/ contient un manifeste et un service worker : le site est installable
(« Ajouter à l'écran d'accueil ») et reste consultable hors ligne (le dernier
programme est mis en cache ; le guide est servi en network-first).
Les icônes sont versionnées dans static/icons/. Pour les régénérer :
uv run python build_tools/make_icons.pyLe workflow .github/workflows/pages.yml :
- s'exécute toutes les 6 h (cron
17 */6 * * *), surworkflow_dispatch, et à chaque push surmaintouchant les sources ; - lance
uv run python build.pypuis publiedist/viaactions/deploy-pages.
Côté dépôt, une seule fois : Settings → Pages → Source: GitHub Actions.
Le cron GitHub peut être retardé (voire désactivé après 60 jours sans commit). En cas de besoin, relancer le workflow manuellement depuis l'onglet Actions.
task check
# équivalents directs
uv run ruff check .
uv run pytest -qVoir le fichier LICENSE.