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
2025-04-07 13:09:47 +04:00

🎙️ ANNU KUTE CED — Hub multimédia du podcast (SvelteKit)

Sommaire / Contents

🌍 Hub multimédia du podcast ANNU KUTE CED — version SvelteKit statique
🌍 Multimedia hub for the ANNU KUTE CED podcast — static SvelteKit version

Choisissez votre langue / Choose your language :
🇫🇷 Version française · 🇬🇧 English version


🇫🇷 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 nimporte quel hébergement statique — y compris un mutualisé comme o2switch — sans PHP, sans Node, sans base de données. Cest aussi une Progressive Web App installable, avec mode hors ligne, en français et en anglais.

🎯 Mission : offrir un point dentré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 FEDIVERSE OKI, développée par lORGANISATION KA INTERNATIONALE (OKI), elle-même issue du projet kaubuntu.re du mouvement Ka-Ubuntu. La licence dorigine (GNU AGPL v3) est conservée et respectée. La refonte SvelteKit applique la charte OKI (tokens panafricains, thème sombre par défaut, iconographie SVG kréyòl, cadences de motion « gwoka »).

É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

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 :

Service Instance Compte / chaîne

🎥 PeerTube (vidéos, lives)

GADE — https://gade.o-k-i.net

annu_kute_ced

🎙️ Castopod (épisodes audio)

KUTE — https://kute.o-k-i.net

@annu_kute_cedric

🎵 Funkwhale (musique)

MIZIK — https://mizik.o-k-i.net

morceaux aléatoires de linstance

📡 Mastodon (timeline)

BOKANTE — https://bokante.o-k-i.net

@cedric

Toutes ces sources sont modifiables dans src/lib/config.ts (voir 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 dinté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 sil est en cours, avec autoplay

  • Annonce du prochain live : date et heure converties automatiquement pour 5 territoires (Maohi Nui, Martinique/Guadeloupe, Guyane, France, Kanaky), image personnalisable

  • Section hero daccueil 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 linstance, 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 dutilisation

  • Progressive Web App installable (bouton dinstallation 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 laccueil ≈ 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

├── 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 dImageMagick
# Ubuntu / Debian
sudo apt-get install imagemagick

# Fedora / RHEL / CentOS
sudo dnf install ImageMagick

🚀 Installation et développement

  1. 📥 Clonez le dépôt et basculez sur la branche :

    git clone git@labola.o-k-i.net:cedric/annu-kute-ced.git
    cd annu-kute-ced
    git checkout svelte
  2. 📦 Installez les dépendances :

    npm ci
  3. 🔧 Lancez le serveur de développement :

    npm run dev
  4. 🏗️ Buildez le site statique :

    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 nimporte 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.

⚙️ Configuration

Toute la configuration dinstance vit dans un seul fichier : src/lib/config.ts (versionné). Cest l’équivalent des anciennes constantes PHP — modifiez-le puis rebuildez.

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 linstance, chaîne, compte live, durée max des shorts, volumes par section, tag « indépendance »

hero

Bannière daccueil : 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 daffichage), noms FR/EN, pictos du sprite

importantTags / popularTags

Hashtags de la sidebar/footer et de laccueil

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 dinterface

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 daccueil

  1. Hero — si hero.type'none' (live, vidéo ou playlist)

  2. Derniers épisodes (Castopod) — si activé et épisodes disponibles

  3. Morceaux (Funkwhale) — si activé et morceaux disponibles

  4. Actualités (timeline Mastodon) — si posts disponibles

  5. Shorts — carrousel

  6. Vidéos récentes

  7. Tendances

  8. Une section par catégorie prioritaire (seulement si elle contient des vidéos) + lien « Voir plus »

  9. Bloc « À propos » (si activé) + aside hashtags populaires

🛡️ 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)

  • Lenregistrement 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 dun épisode)

👨‍💻 Développement et contribution

  1. 🌿 Créez une branche : git checkout -b ma-fonctionnalite

  2. 💾 Committez avec des messages atomiques, au format conventionnel :

    • feat: add …, fix: prevent …, docs: update …, refactor: …

  3. Avant tout commit : npm run check (0 erreur / 0 warning) et npm run build vert

  4. 📤 Poussez : git push origin ma-fonctionnalite

  5. 🔀 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)

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 dAsciidoctor.

📜 Licence

Copyright © 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 lespoir quil sera utile, mais SANS AUCUNE GARANTIE ; sans même la garantie implicite de COMMERCIALISATION ou dADAPTATION À UN USAGE PARTICULIER. Voir la 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 : kontak@o-k-i.net


🇬🇧 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 podcasts 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 FEDIVERSE OKI, developed by the ORGANISATION KA INTERNATIONALE (OKI), itself derived from the kaubuntu.re project by the 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).

Item Detail

Upstream repository

https://labola.o-k-i.net/ORGANISATION-KA-INTERNATIONALE/FEDIVERSE-OKI

This forks 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

GNU Affero General Public License v3 (AGPL-V3) or later

🔗 Aggregated sources and instances

The hub aggregates the ANNU KUTE CED podcasts sources at build time:

Service Instance Account / channel

🎥 PeerTube (videos, lives)

GADE — https://gade.o-k-i.net

annu_kute_ced

🎙️ Castopod (audio episodes)

KUTE — https://kute.o-k-i.net

@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

@cedric

All of these sources can be changed in src/lib/config.ts (see 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 (Maohi 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

├── 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
# Ubuntu / Debian
sudo apt-get install imagemagick

# Fedora / RHEL / CentOS
sudo dnf install ImageMagick

🚀 Installation and development

  1. 📥 Clone the repository and switch to the branch:

    git clone git@labola.o-k-i.net:cedric/annu-kute-ced.git
    cd annu-kute-ced
    git checkout svelte
  2. 📦 Install dependencies:

    npm ci
  3. 🔧 Start the development server:

    npm run dev
  4. 🏗️ Build the static site:

    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.

⚙️ 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.

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

Note
Interface texts
FR/EN labels live in src/lib/i18n/fr.json and en.json (not in config.ts).

🧭 Homepage section order

  1. Hero — if hero.type'none' (live, video or playlist)

  2. Latest episodes (Castopod) — if enabled and episodes available

  3. Tracks (Funkwhale) — if enabled and tracks available

  4. News (Mastodon timeline) — if posts available

  5. Shorts — carousel

  6. Recent videos

  7. Trending

  8. One section per priority category (only when it has videos) + "See more" link

  9. "About" block (if enabled) + popular hashtags aside

🛡️ 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 pages 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

  1. 🌿 Create a branch: git checkout -b my-feature

  2. 💾 Commit with atomic messages, conventional format:

    • feat: add …, fix: prevent …, docs: update …, refactor: …

  3. Before any commit: npm run check (0 errors / 0 warnings) and a green npm run build

  4. 📤 Push: git push origin my-feature

  5. 🔀 Open a pull request

Repository best practices:

  • Bug reports and proposals are welcome on the forks 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)

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 Asciidoctors docinfo mechanism.

📜 License

Copyright © 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 GNU Affero General Public License for more details (full text in LICENSE).

📞 Contact

For any question or suggestion about this platform: kontak@o-k-i.net

S
Description
Interface web responsive pour plateforme multimédia PeerTube, intégrant une timeline Mastodon.
https://annukuteced.buzz
Readme AGPL-3.0 9.5 MiB
Languages
PHP 59.5%
CSS 30.4%
JavaScript 10.1%