Files
annu-kute-ced/README.adoc
T
sucupira 3c0b094e92 Réécrit le README pour la version SvelteKit
Remplace la documentation PHP par celle de la branche svelte (FR/EN) :
architecture statique et bake au build, fonctionnalités portées (Funkwhale
inclus), référence de src/lib/config.ts, modÚle CSP par page, PWA, structure
du projet, prérequis Node + ImageMagick, mesures Lighthouse, déploiement
2026-07-23 08:04:41 -04:00

821 lines
40 KiB
Plaintext

= đŸŽ™ïž ANNU KUTE CED — Hub multimĂ©dia du podcast (SvelteKit)
:toc: left
:toc-title: Sommaire / Contents
:toclevels: 3
[[top]]
🌍 *Hub multimĂ©dia du podcast ANNU KUTE CED — version SvelteKit statique* +
🌍 *Multimedia hub for the ANNU KUTE CED podcast — static SvelteKit version*
[.text-center]
*Choisissez votre langue / Choose your language :* +
<<fr,đŸ‡«đŸ‡· Version française>> · <<en,🇬🇧 English version>>
'''
[[fr]]
== đŸ‡«đŸ‡· Version française
=== 📖 Description
*ANNU KUTE CED* est le *hub multimĂ©dia* du podcast du mĂȘme nom. Il regroupe en un seul lieu :
- đŸŽ„ la chaĂźne *PeerTube* du podcast (vidĂ©os, shorts, directs) ;
- đŸŽ™ïž son compte *Castopod* (derniers Ă©pisodes, lecture audio intĂ©grĂ©e) ;
- đŸŽ” une sĂ©lection alĂ©atoire de morceaux *Funkwhale* (mĂȘme lecteur intĂ©grĂ©) ;
- 📡 sa timeline *Mastodon* (actualitĂ©s et annonces).
Cette version est une *application SvelteKit 100 % statique* (Svelte 5 + TypeScript, `adapter-static`, prerender intĂ©gral) : toutes les donnĂ©es des instances sont *rĂ©cupĂ©rĂ©es au moment du build* (« bake ») et le site gĂ©nĂ©rĂ© peut ĂȘtre servi depuis n'importe quel hĂ©bergement statique — y compris un mutualisĂ© comme o2switch — *sans PHP, sans Node, sans base de donnĂ©es*. C'est aussi une *Progressive Web App* installable, avec mode hors ligne, en français et en anglais.
🎯 *Mission* : offrir un point d'entrĂ©e unique, libre et dĂ©centralisĂ©, pour dĂ©couvrir, Ă©couter et suivre le podcast ANNU KUTE CED sans dĂ©pendre des grandes plateformes propriĂ©taires.
NOTE : La version historique PHP est conservée sur la branche `main`. Cette documentation décrit la branche `svelte`.
=== 🌳 Origine du projet
Cette application est un *fork* de https://labola.o-k-i.net/ORGANISATION-KA-INTERNATIONALE/FEDIVERSE-OKI[*FEDIVERSE OKI*^], dĂ©veloppĂ©e par l'https://o-k-i.net[ORGANISATION KA INTERNATIONALE (OKI)^], elle-mĂȘme issue du projet https://codeberg.org/Ka-Ubuntu/kaubuntu.re[kaubuntu.re^] du mouvement https://kaubuntu.com[Ka-Ubuntu^]. La licence d'origine (GNU AGPL v3) est conservĂ©e et respectĂ©e. La refonte SvelteKit applique la https://o-k-i.net[charte OKI] (tokens panafricains, thĂšme sombre par dĂ©faut, iconographie SVG krĂ©yĂČl, cadences de motion « gwoka »).
[cols="1,3",options="header"]
|===
| ÉlĂ©ment | DĂ©tail
| DépÎt upstream
| https://labola.o-k-i.net/ORGANISATION-KA-INTERNATIONALE/FEDIVERSE-OKI
| DépÎt de ce fork
| `git@labola.o-k-i.net:cedric/annu-kute-ced.git` (branche `svelte`)
| Domaine de production
| https://annukuteced.buzz
| Gouvernance
| Application maintenue par OKI ; fork porté par le propriétaire du dépÎt *cedric* (Cédric Famibelle-Pronzola)
| Licence
| https://www.gnu.org/licenses/agpl-3.0.html[GNU Affero General Public License v3^] (AGPL-V3) ou ultérieure
|===
=== 🔗 Sources et instances utilisĂ©es
Le hub agrĂšge au build les sources du podcast ANNU KUTE CED :
[cols="1,2,3",options="header"]
|===
| Service | Instance | Compte / chaĂźne
| đŸŽ„ PeerTube (vidĂ©os, lives)
| GADE — `https://gade.o-k-i.net`
| https://gade.o-k-i.net/c/annu_kute_ced/videos[`annu_kute_ced`^]
| đŸŽ™ïž Castopod (Ă©pisodes audio)
| KUTE — `https://kute.o-k-i.net`
| https://kute.o-k-i.net/@annu_kute_cedric[`@annu_kute_cedric`^]
| đŸŽ” Funkwhale (musique)
| MIZIK — `https://mizik.o-k-i.net`
| morceaux aléatoires de l'instance
| 📡 Mastodon (timeline)
| BOKANTE — `https://bokante.o-k-i.net`
| https://bokante.o-k-i.net/@cedric[`@cedric`^]
|===
Toutes ces sources sont modifiables dans `src/lib/config.ts` (voir <<fr-configuration,Configuration>>).
=== ✹ FonctionnalitĂ©s
==== đŸŽ„ VidĂ©os PeerTube
- Vidéos récentes, tendances et par catégories (configurables)
- *Shorts* : carrousel dédié aux vidéos portrait de moins de 3 minutes (défilement tactile)
- Page vidéo complÚte : lecteur PeerTube embarqué, description Markdown, badge de licence Creative Commons, commentaires (lecture seule), vidéos suggérées
- *Téléchargement* : fichiers directs et playlists HLS, avec résolution et taille
- *Partage* : copie de lien, code d'intégration, e-mail, Facebook, X, WhatsApp, LinkedIn, Telegram
- Recherche plein texte et par hashtag (préfixe `#`) sur un index JSON généré au build
==== đŸ“ș Directs et annonces
- Page *Direct* (`/direct`) : embed du live PeerTube s'il est en cours, avec autoplay
- *Annonce du prochain live* : date et heure converties automatiquement pour 5 territoires (Ma'ohi Nui, Martinique/Guadeloupe, Guyane, France, Kanaky), image personnalisable
- Section *hero* d'accueil configurable : live, vidéo unique, playlist ou masquée
==== đŸŽ™ïž Podcast et musique
- *Castopod* : derniers épisodes via flux RSS, avec *lecteur audio intégré* (lecture/pause, avance automatique à l'épisode suivant, Media Session API)
- *Funkwhale* : sĂ©lection alĂ©atoire de morceaux de l'instance, avec le mĂȘme lecteur intĂ©grĂ©
- *Un seul flux à la fois* entre les deux lecteurs (bus audio partagé)
==== 📡 RĂ©seaux sociaux
- *Timeline Mastodon* : les 10 derniers posts récupérés au build et rendus en HTML sémantique (plus de bibliothÚque JS embarquée)
==== 🌍 Internationalisation
- Interface en *français* (`/`) et en *anglais* (`/en/`) : routage par paramÚtre optionnel, bundles JSON, `hreflang`, attribut `lang` par route
- KrĂ©yĂČl en signature : page 404 (« Paj la pa la »), page hors-ligne (« Pa gen koneksyon »), verrou maintenance (« An nou tann ! »)
==== 🎹 Charte OKI
- *ThÚme sombre par défaut* (identité de marque), thÚme clair en opt-in persisté en `localStorage`, appliqué sans FOUC
- Tokens panafricains (noir/or/vert/rouge), *flag-bar* de signature, typographies *Archivo* et *Inter* self-hébergées en woff2
- *ZĂ©ro emoji en interface* : sprite SVG maison (pictos krĂ©yĂČl `ka`, `lambi`, `zetwal`
 + icĂŽnes UI), zĂ©ro Font Awesome, zĂ©ro CDN
- Motion « gwoka » : KineticText au scroll (`animation-timeline: view()`), barre de progression, reveal syncopĂ© (3+3+2), View Transitions — gate unique `prefers-reduced-motion`
==== đŸ“± PWA et confort d'utilisation
- *Progressive Web App* installable (bouton d'installation automatique)
- Mode hors ligne : shell et pages en cache via Service Worker (Workbox), page `/offline` dédiée
- *Indicateur visuel* de perte de connexion
- Interface entiĂšrement responsive (mobile, tablette, desktop)
==== 💝 Dons, compte à rebours et divers
- *Page de dons* : LiberaPay, Ko-fi et Stripe (dons ponctuels et mensuels, montants suggérés)
- *Verrou maintenance* : compte à rebours multi-fuseaux affiché à la place de tout le site quand activé au build, redirection automatique à l'échéance
- Bloc « À propos » configurable (titre, deux paragraphes, image lĂ©gendĂ©e)
==== 🔍 SEO et donnĂ©es structurĂ©es
- *JSON-LD* généré automatiquement : `WebSite` (avec `SearchAction`), `VideoObject`, `CollectionPage`, `BreadcrumbList`, `Organization`
- Balises *Open Graph* complĂštes et *Twitter Cards*, canonical, `hreflang` fr/en/x-default
- `sitemap.xml` *généré au build* (toutes les pages, vidéos comprises) et `robots.txt`
==== đŸ›Ąïž SĂ©curitĂ©
- *CSP stricte* : politique globale en en-tĂȘte (`_headers` Cloudflare / `.htaccess` Apache) + `script-src` dĂ©livrĂ© *par page* en balise `<meta>` avec les empreintes SHA-256 des scripts inline (aucun `'unsafe-inline'` pour les scripts)
- *ZĂ©ro requĂȘte tierce* au chargement hors instances first-party de l'Ă©cosystĂšme (GADE, KUTE, BOKANTE, MIZIK) : ni Google Fonts, ni CDN, ni analytics
- HTML distant *sanitisé* au build (posts Mastodon, commentaires) ; descriptions Markdown échappées
- HSTS, `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, `Referrer-Policy`, `Permissions-Policy` restrictive
==== ⚡ Performance (mesurĂ©e, Lighthouse mobile)
- *97 / 100 / 100 / 100* (performance, accessibilité, bonnes pratiques, SEO)
- JS initial ≈ 66 Ko gzip ; poids total de l'accueil ≈ 900 Ko ; LCP 2,4 s ; TBT 50 ms ; CLS 0
- Images distantes *optimisées au build* (WebP via ImageMagick) : vignettes PeerTube, avatars, médias Mastodon, pochettes Castopod
=== đŸ› ïž Technologies utilisĂ©es
- ⚡ *SvelteKit 2* + *Svelte 5* (runes) + *TypeScript* strict
- 📩 `@sveltejs/adapter-static` (prerender intĂ©gral, `trailingSlash: 'always'`)
- đŸ“± `vite-plugin-pwa` (Workbox, generateSW)
- đŸ–Œïž *ImageMagick* (optimisation des images au prebuild)
- 🎹 CSS vanilla : tokens OKI + styles scopĂ©s — pas de framework CSS
- đŸ”€ `@fontsource/archivo` + `@fontsource/inter` (woff2 copiĂ©s dans `static/fonts/`)
=== 📁 Structure du projet
[source]
----
├── docs/
│ ├── wireframes/ # Maquettes d'origine (PNG desktop + mobile)
│ ├── DEPLOIEMENT-SVELTEKIT.md # Guide build + dĂ©ploiement (o2switch, Cloudflare)
│ └── generate-readme-pdf.sh # Export de la doc : README.html / README.pdf
├── scripts/
│ ├── fetch-podcast-cover.mjs # Prebuild : pochettes Castopod optimisĂ©es (WebP)
│ ├── optimize-remote-images.mjs # Prebuild : vignettes/avatars/mĂ©dias optimisĂ©s
│ └── postbuild-csp.mjs # Postbuild : CSP SHA-256 par page (balise meta)
├── src/
│ ├── app.html # %lang%, fonts, theme.js, registerSW.js
│ ├── hooks.server.ts # lang fr/en via transformPageChunk
│ ├── params/locale.ts # Matcher du paramùtre [[locale]]
│ ├── lib/
│ │ ├── config.ts # ⭐ Toute la configuration d'instance
│ │ ├── types.ts # Types partagĂ©s (VideoSummary, Episode, Post
)
│ │ ├── audio-bus.svelte.ts # Un seul flux audio à la fois
│ │ ├── styles/ # oki-tokens.css (charte) + base.css (primitives)
│ │ ├── i18n/ # fr.json, en.json + helpers (locale, hrefFor
)
│ │ ├── server/ # RĂ©cupĂ©ration des donnĂ©es au build :
│ │ │ ├── http.ts # fetch throttlĂ© (429 → retry), sanitisation
│ │ │ ├── peertube.ts # API GADE (vidĂ©os, shorts, live, commentaires)
│ │ │ ├── castopod.ts # Flux RSS KUTE (Ă©pisodes)
│ │ │ ├── funkwhale.ts # API MIZIK (morceaux alĂ©atoires locaux)
│ │ │ ├── mastodon.ts # API BOKANTE (timeline)
│ │ │ ├── image-map.ts # Mapping images optimisĂ©es (mapImage)
│ │ │ └── image-map.json # (gĂ©nĂ©rĂ© au prebuild)
│ │ ├── motion/ # tokens gwoka, action use:reveal
│ │ └── components/ # Seo, Nav, Sidebar, Footer, VideoCard,
│ │ # AudioPlayer, MastodonTimeline, HeroSection,
│ │ # CountdownLock, OfflineIndicator, motion/

│ └── routes/
│ ├── +layout.ts # prerender = true, trailingSlash
│ ├── +error.svelte # 404 krĂ©yĂČl (« Paj la pa la »)
│ ├── [[locale=locale]]/ # Toutes les pages, FR (/) et EN (/en/) :
│ │ ├── +page.svelte # accueil (agrĂ©gateur)
│ │ ├── video/[uuid]/ # page vidĂ©o (embed, tĂ©lĂ©chargements, partage)
│ │ ├── categories/[id]/ # page catĂ©gorie
│ │ ├── recherche/ # recherche (index JSON + filtrage client)
│ │ ├── direct/ # live + annonce du prochain live
│ │ ├── dons/ # dons (LiberaPay, Ko-fi, Stripe)
│ │ ├── mentions-legales/ # mentions lĂ©gales
│ │ └── offline/ # page hors-ligne (fallback du SW)
│ ├── search-index.json/ # Index de recherche (bake)
│ └── sitemap.xml/ # Sitemap (bake)
├── static/
│ ├── fonts/ # Archivo/Inter woff2 + fonts.css
│ ├── images/ # logo, favicons, pochettes, images optimisĂ©es
│ ├── icons.svg # Sprite SVG (pictos krĂ©yĂČl + UI)
│ ├── theme.js # Anti-FOUC (thĂšme sombre dĂ©faut, clair opt-in)
│ ├── registerSW.js # Enregistrement du Service Worker
│ ├── manifest.webmanifest # Manifest PWA
│ ├── _headers / .htaccess # Headers de sĂ©curitĂ© (Cloudflare / Apache)
│ ├── 404.html # 404 statique autonome
│ ├── robots.txt / browserconfig.xml
├── uploads/ # Images d'annonces (non versionnĂ©, hors .gitkeep)
├── package.json / vite.config.ts # Adapter, PWA, paths.relative: false
└── README.adoc
----
=== 📋 PrĂ©requis
- 🟱 *Node.js 20+* (recommandĂ© : 22+)
- đŸ–Œïž *ImageMagick* (`convert`) — optimisation des images au prebuild
- 🌐 Pour le dĂ©veloppement : un accĂšs rĂ©seau aux instances (les donnĂ©es sont fetchĂ©es au build)
.Installation d'ImageMagick
[source,bash]
----
# Ubuntu / Debian
sudo apt-get install imagemagick
# Fedora / RHEL / CentOS
sudo dnf install ImageMagick
----
=== 🚀 Installation et dĂ©veloppement
. đŸ“„ Clonez le dĂ©pĂŽt et basculez sur la branche :
+
[source,bash]
----
git clone git@labola.o-k-i.net:cedric/annu-kute-ced.git
cd annu-kute-ced
git checkout svelte
----
. 📩 Installez les dĂ©pendances :
+
[source,bash]
----
npm ci
----
. 🔧 Lancez le serveur de dĂ©veloppement :
+
[source,bash]
----
npm run dev
----
. đŸ—ïž Buildez le site statique :
+
[source,bash]
----
npm run build # prebuild (images) → prerender (donnĂ©es) → postbuild (CSP)
npm run preview # vérification locale du build
----
Le dossier `build/` contient le site complet, prĂȘt Ă  dĂ©poser sur n'importe quel hĂ©bergement statique.
✅ *VĂ©rifications qualitĂ©* : `npm run check` (svelte-check, 0 erreur / 0 warning exigĂ©).
🔄 *RafraĂźchissement du contenu* : les donnĂ©es Ă©tant bakĂ©es au build, un nouveau build est nĂ©cessaire pour afficher les derniĂšres vidĂ©os/Ă©pisodes/posts — voir `docs/DEPLOIEMENT-SVELTEKIT.md` pour un exemple de cron + rsync.
[[fr-configuration]]
=== ⚙ Configuration
Toute la configuration d'instance vit dans *un seul fichier* : `src/lib/config.ts` (versionnĂ©). C'est l'Ă©quivalent des anciennes constantes PHP — modifiez-le puis rebuildez.
[cols="1,3",options="header"]
|===
| Export | RĂŽle
| `site`
| Nom, domaine (`baseUrl`), description, e-mail de contact, URLs licence/source, fuseau par défaut
| `legal`
| Coordonnées des mentions légales (webmaster, hébergeur o2Switch)
| `peertube`
| URL de l'instance, chaßne, compte live, durée max des shorts, volumes par section, tag « indépendance »
| `hero`
| BanniÚre d'accueil : `type` (`'live'`, `'video'`, `'playlist'`, `'none'`), vidéo/playlist associée
| `castopod`
| Activation, instance, slug(s) de podcast, nombre d'épisodes
| `funkwhale`
| Activation, instance MIZIK, nombre de morceaux (pool de 50, sélection aléatoire)
| `mastodon`
| Instance, compte, nombre de posts de la timeline
| `social`
| URLs des réseaux sociaux du header/footer (vide = icÎne masquée)
| `donations`
| Activation, URLs LiberaPay/Ko-fi, liens Stripe (ponctuels/mensuels), montants, devise
| `nextLive`
| Annonce du prochain live : activation, titre, description, date, image, 5 fuseaux horaires
| `categories` / `categoryNames` / `categoryIcons`
| Catégories prioritaires (ordre d'affichage), noms FR/EN, pictos du sprite
| `importantTags` / `popularTags`
| Hashtags de la sidebar/footer et de l'accueil
| `about`
| Bloc « À propos » : activation, titre, deux paragraphes, image + lĂ©gende (masquĂ© tant que `enabled: false`)
| `countdown`
| *Verrou maintenance* : quand `enabled: true`, tout le site buildé affiche le compte à rebours multi-fuseaux + date cible
|===
.Les textes d'interface
NOTE : Les libellés FR/EN vivent dans `src/lib/i18n/fr.json` et `en.json` (pas dans `config.ts`).
=== 🧭 Ordre des sections sur la page d'accueil
. *Hero* — si `hero.type` ≠ `'none'` (live, vidĂ©o ou playlist)
. *Derniers Ă©pisodes* (Castopod) — si activĂ© et Ă©pisodes disponibles
. *Morceaux* (Funkwhale) — si activĂ© et morceaux disponibles
. *ActualitĂ©s* (timeline Mastodon) — si posts disponibles
. *Shorts* — carrousel
. *Vidéos récentes*
. *Tendances*
. *Une section par catégorie prioritaire* (seulement si elle contient des vidéos) + lien « Voir plus »
. *Bloc « À propos »* (si activĂ©) + aside hashtags populaires
[[fr-securite]]
=== đŸ›Ąïž SĂ©curitĂ©
==== ModĂšle CSP
- *En-tĂȘte global* (`static/_headers` pour Cloudflare Pages, `static/.htaccess` pour Apache/o2switch) : `style-src`, `img-src`, `font-src 'self'`, `connect-src 'self'`, `media-src`, `frame-src https://gade.o-k-i.net`, `frame-ancestors 'none'`, `base-uri 'self'`, `form-action 'self'`, `object-src 'none'`. Seules origines externes : les instances de l'Ă©cosystĂšme.
- *`script-src` par page* : `scripts/postbuild-csp.mjs` calcule les empreintes SHA-256 des scripts inline de chaque page (script de démarrage SvelteKit + JSON-LD) et les injecte dans une balise `<meta http-equiv="Content-Security-Policy">` complÚte. Aucun `'unsafe-inline'` pour les scripts.
- Anti-FOUC, redirection de langue et enregistrement du SW en *fichiers externes* (`theme.js`, `registerSW.js`) : aucun script inline non hashé.
==== Redirections des anciennes URLs (Apache)
Le `.htaccess` redirige les URLs historiques PHP : `index.php` → `/`, `video.php?uuid=X` → `/video/X/`, `categories.php?id=N` → `/categories/N/`, `recherche.php?q=
` → `/recherche/?q=
`, `direct.php` → `/direct/`, `dons.php` → `/dons/`, `mentions-legales.php` → `/mentions-legales/`. Il force aussi HTTPS, sert `404.html` et met en cache immutable `/_app/` et `/fonts/`.
=== đŸ“± Progressive Web App (PWA)
- đŸ“Č *Installation native* : bouton « Installer » dans le header (Ă©vĂ©nement `beforeinstallprompt`)
- 🌐 *Mode hors ligne* : prĂ©cache du shell (Workbox `generateSW`), `navigateFallback` vers `/offline/`
- 📡 *DĂ©tection de connexion* : indicateur visuel en cas de perte rĂ©seau
- 🔄 *Mise à jour* : `autoUpdate` + `skipWaiting` (mise à jour silencieuse au prochain chargement)
- L'enregistrement du SW se fait par le fichier statique `registerSW.js` (chemins absolus), jamais par injection du plugin
NOTE : Les flux audio/vidĂ©o des instances ne sont *pas* interceptĂ©s par le SW (requĂȘtes `Range`) — seuls le shell et les assets locaux sont prĂ©cachĂ©s.
=== 🚀 DĂ©ploiement
Voir le guide complet : *`docs/DEPLOIEMENT-SVELTEKIT.md`*.
- *o2switch (Apache)* : déposer `build/` à la racine web ; le `.htaccess` inclus gÚre HTTPS, headers, redirections des anciennes URLs et 404
- *Cloudflare Pages* : build command `npm run build`, output `build` ; le `_headers` inclus applique la sécurité et le cache
- *Rafraßchissement* : rebuild planifié (cron + rsync, ou CI avec webhook aprÚs publication d'un épisode)
=== đŸ‘šâ€đŸ’» DĂ©veloppement et contribution
. 🌿 CrĂ©ez une branche : `git checkout -b ma-fonctionnalite`
. đŸ’Ÿ Committez avec des *messages atomiques*, au format conventionnel :
* `feat: add 
`, `fix: prevent 
`, `docs: update 
`, `refactor: 
`
. ✅ Avant tout commit : `npm run check` (0 erreur / 0 warning) et `npm run build` vert
. đŸ“€ Poussez : `git push origin ma-fonctionnalite`
. 🔀 Ouvrez une pull request
Bonnes pratiques du dépÎt :
- Les remontĂ©es de bugs et propositions sont les bienvenues sur le dĂ©pĂŽt du fork ; les amĂ©liorations gĂ©nĂ©riques peuvent ĂȘtre proposĂ©es en upstream (FEDIVERSE OKI)
- `src/lib/server/image-map.json` et `static/images/remote/` sont régénérés au prebuild : pas d'édition manuelle
- Conventions Svelte 5 : runes uniquement, `{#each}` à clé unique, `onclick` (jamais `on:click`), durées lues depuis les tokens CSS
==== 📄 Exporter la documentation (HTML / PDF)
[source,bash]
----
docs/generate-readme-pdf.sh # README.html + README.pdf
docs/generate-readme-pdf.sh --html # HTML seulement
docs/generate-readme-pdf.sh --pdf # PDF seulement
----
Prérequis : `asciidoctor`, `chromium` et le paquet `fonts-noto-color-emoji`. La feuille de style `docs/docinfo.html` est injectée automatiquement via le mécanisme `docinfo` d'Asciidoctor.
=== 📜 Licence
Copyright (C) 2025 Cédric Famibelle-Pronzola & *ORGANISATION KA INTERNATIONALE*
Ce programme est un logiciel libre : vous pouvez le redistribuer et/ou le modifier selon les termes de la licence publique générale GNU Affero publiée par la Free Software Foundation, soit la version 3 de la licence, soit (à votre choix) toute version ultérieure.
Ce programme est distribuĂ© dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE ; sans mĂȘme la garantie implicite de COMMERCIALISATION ou d'ADAPTATION À UN USAGE PARTICULIER. Voir la https://www.gnu.org/licenses/agpl-3.0.html[licence publique gĂ©nĂ©rale GNU Affero^] pour plus de dĂ©tails (texte intĂ©gral dans `LICENSE`).
=== 📞 Contact
Pour toute question ou suggestion concernant cette plateforme : mailto:kontak@o-k-i.net[kontak@o-k-i.net]
<<top,⬆ Retour au sommaire>>
'''
[[en]]
== 🇬🇧 English version
=== 📖 Description
*ANNU KUTE CED* is the *multimedia hub* of the podcast of the same name. It brings together in a single place:
- đŸŽ„ the podcast's *PeerTube* channel (videos, shorts, live streams);
- đŸŽ™ïž its *Castopod* account (latest episodes, built-in audio playback);
- đŸŽ” a random selection of *Funkwhale* tracks (same built-in player);
- 📡 its *Mastodon* timeline (news and announcements).
This version is a *100% static SvelteKit application* (Svelte 5 + TypeScript, `adapter-static`, full prerender): all instance data is *fetched at build time* ("baked") and the generated site can be served from any static hosting — including shared hosting like o2switch — *with no PHP, no Node, no database*. It is also an installable *Progressive Web App* with offline support, in French and English.
🎯 *Mission*: provide a single, free and decentralized entry point to discover, listen to and follow the ANNU KUTE CED podcast without relying on Big Tech platforms.
NOTE: The historical PHP version is kept on the `main` branch. This documentation covers the `svelte` branch.
=== 🌳 Project origin
This application is a *fork* of https://labola.o-k-i.net/ORGANISATION-KA-INTERNATIONALE/FEDIVERSE-OKI[*FEDIVERSE OKI*^], developed by the https://o-k-i.net[ORGANISATION KA INTERNATIONALE (OKI)^], itself derived from the https://codeberg.org/Ka-Ubuntu/kaubuntu.re[kaubuntu.re^] project by the https://kaubuntu.com[Ka-Ubuntu^] movement. The original license (GNU AGPL v3) is preserved and respected. The SvelteKit rewrite applies the OKI design system (panafrican tokens, dark theme by default, KrĂ©yĂČl SVG iconography, "gwoka" motion cadences).
[cols="1,3",options="header"]
|===
| Item | Detail
| Upstream repository
| https://labola.o-k-i.net/ORGANISATION-KA-INTERNATIONALE/FEDIVERSE-OKI
| This fork's repository
| `git@labola.o-k-i.net:cedric/annu-kute-ced.git` (`svelte` branch)
| Production domain
| https://annukuteced.buzz
| Governance
| Application maintained by OKI; fork carried by the repository owner *cedric* (Cédric Famibelle-Pronzola)
| License
| https://www.gnu.org/licenses/agpl-3.0.html[GNU Affero General Public License v3^] (AGPL-V3) or later
|===
=== 🔗 Aggregated sources and instances
The hub aggregates the ANNU KUTE CED podcast's sources at build time:
[cols="1,2,3",options="header"]
|===
| Service | Instance | Account / channel
| đŸŽ„ PeerTube (videos, lives)
| GADE — `https://gade.o-k-i.net`
| https://gade.o-k-i.net/c/annu_kute_ced/videos[`annu_kute_ced`^]
| đŸŽ™ïž Castopod (audio episodes)
| KUTE — `https://kute.o-k-i.net`
| https://kute.o-k-i.net/@annu_kute_cedric[`@annu_kute_cedric`^]
| đŸŽ” Funkwhale (music)
| MIZIK — `https://mizik.o-k-i.net`
| random tracks from the instance
| 📡 Mastodon (timeline)
| BOKANTE — `https://bokante.o-k-i.net`
| https://bokante.o-k-i.net/@cedric[`@cedric`^]
|===
All of these sources can be changed in `src/lib/config.ts` (see <<en-configuration,Configuration>>).
=== ✹ Features
==== đŸŽ„ PeerTube videos
- Recent, trending and per-category videos (configurable)
- *Shorts*: dedicated carousel for portrait videos under 3 minutes (touch scrolling)
- Full video page: embedded PeerTube player, Markdown description, Creative Commons licence badge, comments (read-only), suggested videos
- *Downloads*: direct files and HLS playlists, with resolution and size
- *Sharing*: copy link, embed code, e-mail, Facebook, X, WhatsApp, LinkedIn, Telegram
- Full-text search and hashtag search (`#` prefix) over a build-generated JSON index
==== đŸ“ș Live streams and announcements
- *Live page* (`/direct`): embeds the PeerTube live stream when one is running, with autoplay
- *Next live announcement*: date and time automatically converted for 5 territories (Ma'ohi Nui, Martinique/Guadeloupe, French Guiana, France, Kanaky), custom image
- Configurable homepage *hero section*: live, single video, playlist or hidden
==== đŸŽ™ïž Podcast and music
- *Castopod*: latest episodes via RSS feed, with a *built-in audio player* (play/pause, auto-advance to the next episode, Media Session API)
- *Funkwhale*: random track selection from the instance, using the same built-in player
- *One stream at a time* across both players (shared audio bus)
==== 📡 Social networks
- *Mastodon timeline*: the 10 latest posts fetched at build time and rendered as semantic HTML (no embedded JS library anymore)
==== 🌍 Internationalization
- Interface in *French* (`/`) and *English* (`/en/`): optional-parameter routing, JSON bundles, `hreflang`, per-route `lang` attribute
- KrĂ©yĂČl as a signature: 404 page ("Paj la pa la"), offline page ("Pa gen koneksyon"), maintenance lock ("An nou tann !")
==== 🎹 OKI design system
- *Dark theme by default* (brand identity), light theme as an opt-in persisted in `localStorage`, applied without FOUC
- Panafrican tokens (black/gold/green/red), signature *flag-bar*, self-hosted *Archivo* and *Inter* woff2 fonts
- *Zero emoji in the interface*: custom SVG sprite (KrĂ©yĂČl pictograms `ka`, `lambi`, `zetwal`
 + UI icons), no Font Awesome, no CDN
- "Gwoka" motion: scroll-driven KineticText (`animation-timeline: view()`), progress bar, syncopated reveal (3+3+2), View Transitions — single `prefers-reduced-motion` gate
==== đŸ“± PWA and user experience
- Installable *Progressive Web App* (automatic install button)
- Offline mode: shell and pages cached via Service Worker (Workbox), dedicated `/offline` page
- *Visual indicator* when the connection is lost
- Fully responsive interface (mobile, tablet, desktop)
==== 💝 Donations, countdown and more
- *Donation page*: LiberaPay, Ko-fi and Stripe (one-time and monthly donations, suggested amounts)
- *Maintenance lock*: multi-timezone countdown displayed instead of the whole site when enabled at build time, automatic redirect when it ends
- Configurable "About" block (title, two paragraphs, captioned image)
==== 🔍 SEO and structured data
- Automatically generated *JSON-LD*: `WebSite` (with `SearchAction`), `VideoObject`, `CollectionPage`, `BreadcrumbList`, `Organization`
- Full *Open Graph* and *Twitter Cards* meta tags, canonical, `hreflang` fr/en/x-default
- Build-generated `sitemap.xml` (every page, including videos) and `robots.txt`
==== đŸ›Ąïž Security
- *Strict CSP*: global policy in headers (`_headers` for Cloudflare / `.htaccess` for Apache) + per-page `script-src` delivered in a `<meta>` tag with SHA-256 hashes of inline scripts (no `'unsafe-inline'` for scripts)
- *Zero third-party requests* at load time outside the first-party ecosystem instances (GADE, KUTE, BOKANTE, MIZIK): no Google Fonts, no CDN, no analytics
- Remote HTML *sanitized* at build time (Mastodon posts, comments); Markdown descriptions escaped
- HSTS, `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, `Referrer-Policy`, restrictive `Permissions-Policy`
==== ⚡ Performance (measured, mobile Lighthouse)
- *97 / 100 / 100 / 100* (performance, accessibility, best practices, SEO)
- Initial JS ≈ 66 KB gzip; homepage total weight ≈ 900 KB; LCP 2.4 s; TBT 50 ms; CLS 0
- Remote images *optimized at build time* (WebP via ImageMagick): PeerTube thumbnails, avatars, Mastodon media, Castopod covers
=== đŸ› ïž Tech stack
- ⚡ *SvelteKit 2* + *Svelte 5* (runes) + strict *TypeScript*
- 📩 `@sveltejs/adapter-static` (full prerender, `trailingSlash: 'always'`)
- đŸ“± `vite-plugin-pwa` (Workbox, generateSW)
- đŸ–Œïž *ImageMagick* (build-time image optimization)
- 🎹 Vanilla CSS: OKI tokens + scoped styles — no CSS framework
- đŸ”€ `@fontsource/archivo` + `@fontsource/inter` (woff2 copied to `static/fonts/`)
=== 📁 Project structure
[source]
----
├── docs/
│ ├── wireframes/ # Original mockups (desktop + mobile PNGs)
│ ├── DEPLOIEMENT-SVELTEKIT.md # Build + deployment guide (o2switch, Cloudflare)
│ └── generate-readme-pdf.sh # Docs export: README.html / README.pdf
├── scripts/
│ ├── fetch-podcast-cover.mjs # Prebuild: optimized Castopod covers (WebP)
│ ├── optimize-remote-images.mjs # Prebuild: optimized thumbnails/avatars/media
│ └── postbuild-csp.mjs # Postbuild: per-page SHA-256 CSP (meta tag)
├── src/
│ ├── app.html # %lang%, fonts, theme.js, registerSW.js
│ ├── hooks.server.ts # fr/en lang via transformPageChunk
│ ├── params/locale.ts # [[locale]] param matcher
│ ├── lib/
│ │ ├── config.ts # ⭐ All instance configuration
│ │ ├── types.ts # Shared types (VideoSummary, Episode, Post
)
│ │ ├── audio-bus.svelte.ts # One audio stream at a time
│ │ ├── styles/ # oki-tokens.css (brand) + base.css (primitives)
│ │ ├── i18n/ # fr.json, en.json + helpers (locale, hrefFor
)
│ │ ├── server/ # Build-time data fetching:
│ │ │ ├── http.ts # throttled fetch (429 → retry), sanitizing
│ │ │ ├── peertube.ts # GADE API (videos, shorts, live, comments)
│ │ │ ├── castopod.ts # KUTE RSS feed (episodes)
│ │ │ ├── funkwhale.ts # MIZIK API (random local tracks)
│ │ │ ├── mastodon.ts # BOKANTE API (timeline)
│ │ │ ├── image-map.ts # Optimized images mapping (mapImage)
│ │ │ └── image-map.json # (generated at prebuild)
│ │ ├── motion/ # gwoka tokens, use:reveal action
│ │ └── components/ # Seo, Nav, Sidebar, Footer, VideoCard,
│ │ # AudioPlayer, MastodonTimeline, HeroSection,
│ │ # CountdownLock, OfflineIndicator, motion/

│ └── routes/
│ ├── +layout.ts # prerender = true, trailingSlash
│ ├── +error.svelte # KrĂ©yĂČl 404 ("Paj la pa la")
│ ├── [[locale=locale]]/ # Every page, FR (/) and EN (/en/):
│ │ ├── +page.svelte # home (aggregator)
│ │ ├── video/[uuid]/ # video page (embed, downloads, sharing)
│ │ ├── categories/[id]/ # category page
│ │ ├── recherche/ # search (JSON index + client filtering)
│ │ ├── direct/ # live + next-live announcement
│ │ ├── dons/ # donations (LiberaPay, Ko-fi, Stripe)
│ │ ├── mentions-legales/ # legal notice
│ │ └── offline/ # offline page (SW fallback)
│ ├── search-index.json/ # Search index (baked)
│ └── sitemap.xml/ # Sitemap (baked)
├── static/
│ ├── fonts/ # Archivo/Inter woff2 + fonts.css
│ ├── images/ # logo, favicons, covers, optimized images
│ ├── icons.svg # SVG sprite (KrĂ©yĂČl pictograms + UI)
│ ├── theme.js # Anti-FOUC (dark default, light opt-in)
│ ├── registerSW.js # Service Worker registration
│ ├── manifest.webmanifest # PWA manifest
│ ├── _headers / .htaccess # Security headers (Cloudflare / Apache)
│ ├── 404.html # Self-contained static 404
│ ├── robots.txt / browserconfig.xml
├── uploads/ # Announcement images (not versioned, except .gitkeep)
├── package.json / vite.config.ts # Adapter, PWA, paths.relative: false
└── README.adoc
----
=== 📋 Requirements
- 🟱 *Node.js 20+* (recommended: 22+)
- đŸ–Œïž *ImageMagick* (`convert`) — build-time image optimization
- 🌐 For development: network access to the instances (data is fetched at build time)
.Installing ImageMagick
[source,bash]
----
# Ubuntu / Debian
sudo apt-get install imagemagick
# Fedora / RHEL / CentOS
sudo dnf install ImageMagick
----
=== 🚀 Installation and development
. đŸ“„ Clone the repository and switch to the branch:
+
[source,bash]
----
git clone git@labola.o-k-i.net:cedric/annu-kute-ced.git
cd annu-kute-ced
git checkout svelte
----
. 📩 Install dependencies:
+
[source,bash]
----
npm ci
----
. 🔧 Start the development server:
+
[source,bash]
----
npm run dev
----
. đŸ—ïž Build the static site:
+
[source,bash]
----
npm run build # prebuild (images) → prerender (data) → postbuild (CSP)
npm run preview # local check of the build
----
The `build/` directory holds the complete site, ready to drop on any static hosting.
✅ *Quality gates*: `npm run check` (svelte-check, 0 errors / 0 warnings required).
🔄 *Content refresh*: since data is baked at build time, a new build is required to display the latest videos/episodes/posts — see `docs/DEPLOIEMENT-SVELTEKIT.md` for a cron + rsync example.
[[en-configuration]]
=== ⚙ Configuration
All instance configuration lives in *a single file*: `src/lib/config.ts` (versioned). It is the equivalent of the old PHP constants — edit it, then rebuild.
[cols="1,3",options="header"]
|===
| Export | Role
| `site`
| Name, domain (`baseUrl`), description, contact e-mail, license/source URLs, default timezone
| `legal`
| Legal notice details (webmaster, o2Switch host)
| `peertube`
| Instance URL, channel, live account, max shorts duration, per-section volumes, "independence" tag
| `hero`
| Homepage banner: `type` (`'live'`, `'video'`, `'playlist'`, `'none'`), associated video/playlist
| `castopod`
| Toggle, instance, podcast slug(s), episode count
| `funkwhale`
| Toggle, MIZIK instance, track count (pool of 50, random selection)
| `mastodon`
| Instance, account, timeline post count
| `social`
| Header/footer social network URLs (empty = icon hidden)
| `donations`
| Toggle, LiberaPay/Ko-fi URLs, Stripe links (one-time/monthly), amounts, currency
| `nextLive`
| Next live announcement: toggle, title, description, date, image, 5 timezones
| `categories` / `categoryNames` / `categoryIcons`
| Priority categories (display order), FR/EN names, sprite pictograms
| `importantTags` / `popularTags`
| Sidebar/footer and homepage hashtags
| `about`
| "About" block: toggle, title, two paragraphs, image + caption (hidden while `enabled: false`)
| `countdown`
| *Maintenance lock*: when `enabled: true`, the whole built site shows the multi-timezone countdown + target date
|===
.Interface texts
NOTE: FR/EN labels live in `src/lib/i18n/fr.json` and `en.json` (not in `config.ts`).
=== 🧭 Homepage section order
. *Hero* — if `hero.type` ≠ `'none'` (live, video or playlist)
. *Latest episodes* (Castopod) — if enabled and episodes available
. *Tracks* (Funkwhale) — if enabled and tracks available
. *News* (Mastodon timeline) — if posts available
. *Shorts* — carousel
. *Recent videos*
. *Trending*
. *One section per priority category* (only when it has videos) + "See more" link
. *"About" block* (if enabled) + popular hashtags aside
[[en-securite]]
=== đŸ›Ąïž Security
==== CSP model
- *Global header* (`static/_headers` for Cloudflare Pages, `static/.htaccess` for Apache/o2switch): `style-src`, `img-src`, `font-src 'self'`, `connect-src 'self'`, `media-src`, `frame-src https://gade.o-k-i.net`, `frame-ancestors 'none'`, `base-uri 'self'`, `form-action 'self'`, `object-src 'none'`. Only external origins: the ecosystem instances.
- *Per-page `script-src`*: `scripts/postbuild-csp.mjs` computes SHA-256 hashes of each page's inline scripts (SvelteKit start script + JSON-LD) and injects them into a full `<meta http-equiv="Content-Security-Policy">` tag. No `'unsafe-inline'` for scripts.
- Anti-FOUC, language redirect and SW registration as *external files* (`theme.js`, `registerSW.js`): no unhashed inline script.
==== Legacy URL redirects (Apache)
The `.htaccess` redirects historical PHP URLs: `index.php` → `/`, `video.php?uuid=X` → `/video/X/`, `categories.php?id=N` → `/categories/N/`, `recherche.php?q=
` → `/recherche/?q=
`, `direct.php` → `/direct/`, `dons.php` → `/dons/`, `mentions-legales.php` → `/mentions-legales/`. It also forces HTTPS, serves `404.html` and sets immutable caching for `/_app/` and `/fonts/`.
=== đŸ“± Progressive Web App (PWA)
- đŸ“Č *Native installation*: "Install" button in the header (`beforeinstallprompt` event)
- 🌐 *Offline mode*: shell precache (Workbox `generateSW`), `navigateFallback` to `/offline/`
- 📡 *Connection detection*: visual indicator when the network is lost
- 🔄 *Updates*: `autoUpdate` + `skipWaiting` (silent update on next load)
- SW registration is done by the static `registerSW.js` file (absolute paths), never via plugin injection
NOTE: Instance audio/video streams are *not* intercepted by the SW (`Range` requests) — only the shell and local assets are precached.
=== 🚀 Deployment
See the full guide: *`docs/DEPLOIEMENT-SVELTEKIT.md`*.
- *o2switch (Apache)*: drop `build/` at the web root; the bundled `.htaccess` handles HTTPS, headers, legacy URL redirects and 404
- *Cloudflare Pages*: build command `npm run build`, output `build`; the bundled `_headers` applies security and caching
- *Refresh*: scheduled rebuild (cron + rsync, or CI with a webhook after an episode is published)
=== đŸ‘šâ€đŸ’» Development and contribution
. 🌿 Create a branch: `git checkout -b my-feature`
. đŸ’Ÿ Commit with *atomic messages*, conventional format:
* `feat: add 
`, `fix: prevent 
`, `docs: update 
`, `refactor: 
`
. ✅ Before any commit: `npm run check` (0 errors / 0 warnings) and a green `npm run build`
. đŸ“€ Push: `git push origin my-feature`
. 🔀 Open a pull request
Repository best practices:
- Bug reports and proposals are welcome on the fork's repository; generic improvements can be proposed upstream (FEDIVERSE OKI)
- `src/lib/server/image-map.json` and `static/images/remote/` are regenerated at prebuild: no manual editing
- Svelte 5 conventions: runes only, uniquely keyed `{#each}`, `onclick` (never `on:click`), durations read from CSS tokens
==== 📄 Exporting the documentation (HTML / PDF)
[source,bash]
----
docs/generate-readme-pdf.sh # README.html + README.pdf
docs/generate-readme-pdf.sh --html # HTML only
docs/generate-readme-pdf.sh --pdf # PDF only
----
Requirements: `asciidoctor`, `chromium` and the `fonts-noto-color-emoji` package. The `docs/docinfo.html` stylesheet is automatically injected via Asciidoctor's `docinfo` mechanism.
=== 📜 License
Copyright (C) 2025 Cédric Famibelle-Pronzola & *ORGANISATION KA INTERNATIONALE*
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the https://www.gnu.org/licenses/agpl-3.0.html[GNU Affero General Public License^] for more details (full text in `LICENSE`).
=== 📞 Contact
For any question or suggestion about this platform: mailto:kontak@o-k-i.net[kontak@o-k-i.net]
<<top,⬆ Back to top>>