2025-10-17 12:29:10 +04:00
2025-07-21 13:33:23 +04:00
2025-10-18 01:20:04 +04:00
2025-10-18 18:29:00 +04:00
2025-04-07 13:09:47 +04:00

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

Sommaire / Contents

🌍 Hub multimédia du podcast ANNU KUTE CED
🌍 Multimedia hub for the ANNU KUTE CED podcast

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


🇫🇷 Version française

📖 Description

ANNU KUTE CED est une interface web responsive qui fait office de hub multimédia pour le podcast du même nom. Elle 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) ;

  • 📡 sa timeline Mastodon (actualités et annonces).

La plateforme est légère, déployable sur un serveur mutualisé (PHP + serveur web, sans base de données) et optimisée pour mobile et desktop. Cest aussi une Progressive Web App installable, avec mode hors ligne.

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

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

É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

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 est configuré par défaut pour agréger 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

📡 Mastodon (timeline)

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

@cedric

🎵 Funkwhale (musique, optionnel, désactivé par défaut)

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

morceaux aléatoires de linstance

Toutes ces sources sont modifiables dans includes/config.local.php (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 recherche par hashtag (préfixe #)

  • Pagination AJAX « Voir plus » (protégée par jeton CSRF et vérification dorigine)

📺 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 (4:5, 1:1, 16:9)

  • Section hero daccueil configurable : live, vidéo unique, playlist (PeerTube / Funkwhale / Castopod) ou masquée

🎙️ Podcast et audio

  • Castopod : derniers épisodes via flux RSS, avec lecteur audio intégré (lecture/pause, avance automatique à l’épisode suivant, un seul flux à la fois)

  • Funkwhale (optionnel) : sélection aléatoire de morceaux de linstance, avec le même lecteur intégré

📡 Réseaux sociaux et contenus externes

  • Timeline Mastodon intégrée (bibliothèque vendored mastodon-embed-timeline v4.7.0, AGPLv3)

  • Compatibilité Pleroma : adaptateur qui convertit les réponses de lAPI Pleroma au format Mastodon attendu par la timeline

  • WordPress (optionnel) : derniers articles via lAPI REST, avec image à la une et auteur

📱 PWA et confort dutilisation

  • Progressive Web App installable (bouton dinstallation automatique)

  • Mode hors ligne : pages et ressources statiques en cache via Service Worker

  • Indicateur visuel de perte de connexion

  • Mode sombre (basé sur prefers-color-scheme, mémorisé en localStorage)

  • 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)

  • Compte à rebours : page de lancement/maintenance multi-fuseaux qui verrouille le site jusqu’à la date cible

  • 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

  • Balises Open Graph et Twitter Cards

  • sitemap.xml et robots.txt fournis en exemples

  • Prêt pour Plausible Analytics (voir Analytics) : désactivé par défaut, sans cookies et conforme RGPD

🛡️ Sécurité

  • En-têtes de sécurité complets : CSP dynamique avec nonce par requête, HSTS, X-Frame-Options, nosniff, Referrer-Policy, Permissions-Policy

  • Protection CSRF sur les requêtes AJAX, vérification de len-tête Origin

  • Validation anti-SSRF des URLs dinstances et liste blanche des endpoints de lAPI PeerTube

  • Échappement systématique des sorties (anti-XSS)

  • Exemples de configuration sécurisée pour Apache et Nginx dans conf/

🛠️ Technologies utilisées

  • 📄 HTML5, 🎨 CSS3 (Media Queries), JavaScript vanilla (sans framework, sans jQuery)

  • 🐘 PHP 7.4+ (aucune base de données requise)

  • 🔧 Service Worker (cache offline) et 📋 Web App Manifest (installation)

  • 📦 Bibliothèques :

    • 🎯 Font Awesome 7 (icônes, via CDN cdnjs)

    • 📡 mastodon-embed-timeline v4.7.0 (vendored dans js/, AGPLv3)

    • 📊 Plausible Analytics (pré-configuré, à activer manuellement)

📁 Structure du projet

├── ajax/
│   └── load-more-videos.php        # Endpoint AJAX « Voir plus » (CSRF + Origin)
├── conf/
│   ├── .htaccess.sample            # Configuration Apache sécurisée
│   └── nginx.conf.sample           # Configuration Nginx sécurisée
├── css/                            # Feuilles de style (styles, vidéo, catégories,
│   │                               #   recherche, dons, countdown, mastodon,
│   ├── castopod-podcasts.css       #   castopod, funkwhale, wordpress, grille…)
│   └── …
├── docs/
│   ├── wireframes/                 # Maquettes d'origine (PNG desktop + mobile)
│   ├── docinfo.html                # Styles d'impression (fallback emoji) pour l'export
│   └── generate-readme-pdf.sh      # Export de la doc : README.html / README.pdf
├── img/                            # Logo, favicons, icône de lecture
├── includes/
│   ├── config.php                  # Bootstrap + fonctions API (PeerTube, Castopod,
│   │                               #   Funkwhale) et formatage
│   ├── config.default.php          # Valeurs par défaut de toutes les constantes
│   ├── config.local.php.sample     # Modèle de configuration locale (à copier)
│   ├── security.php                # CSRF, CSP + nonce, en-têtes, validateurs
│   ├── simple-cache.php            # Cache fichier de l'API (cache/api/)
│   ├── wordpress.php               # Intégration API REST WordPress
│   ├── structured-data.php         # Générateurs JSON-LD
│   ├── header.php                  # Barre supérieure (recherche, réseaux, thème…)
│   ├── footer.php                  # Pied de page
│   ├── sidebar.php                 # Navigation latérale
│   ├── mobile-menu.php             # Menu mobile coulissant
│   ├── hero-section.php            # Bannière d'accueil (live/vidéo/playlist)
│   ├── featured-videos.php         # ⚠️ legacy, non utilisé par les pages actuelles
│   ├── recent-videos.php           # ⚠️ legacy, non utilisé par les pages actuelles
│   ├── pwa-init.php                # ⚠️ legacy, PWA désormais inline dans index.php
│   └── lib/
│       └── markdown.php            # Rendu Markdown (sous-ensemble PeerTube)
├── js/
│   ├── main.js                     # UI globale (thème, carrousel, « Voir plus »…)
│   ├── audio-player.js             # Lecteur audio Castopod / Funkwhale
│   ├── categories.js               # « Voir plus » de la page catégorie
│   ├── countdown.js                # Compte à rebours
│   ├── search.js                   # Cartes cliquables de la recherche
│   ├── mastodon-config.php         # Configuration JS de la timeline (servie en JS)
│   ├── mastodon-timeline.umd.js    # mastodon-embed-timeline v4.7.0 (vendored)
│   └── pleroma-adapter.js          # Adaptateur Pleroma → API Mastodon
├── uploads/                        # Images d'annonces (non versionné, hors .gitkeep)
├── index.php                       # Page d'accueil (agrégateur)
├── video.php                       # Page de lecture d'une vidéo
├── categories.php                  # Page d'une catégorie
├── recherche.php                   # Page de recherche
├── direct.php                      # Page du direct / annonce du prochain live
├── countdown.php                   # Page de compte à rebours (maintenance)
├── sw.js                           # Service Worker (PWA)
├── site.webmanifest.sample         # Exemple de manifest PWA
├── browserconfig.xml               # Tuiles Windows
├── sitemap.xml.sample              # Exemple de sitemap
├── robots.txt.sample               # Exemple de robots.txt
├── mentions-legales.php.sample     # Exemple de mentions légales
├── dons.php.sample                 # Exemple de page de dons
├── LICENSE                         # GNU AGPL v3
└── README.adoc
Note
Les fichiers marqués « legacy » sont conservés pour référence mais ne sont inclus par aucune page ; la page daccueil rend ces sections en interne.

📋 Prérequis

  • 🐘 PHP 7.4+ (recommandé : PHP 8.0+)

  • 📦 Extensions PHP requises :

    • curl — appels API (PeerTube, Castopod, Funkwhale, WordPress)

    • json — traitement des réponses

    • intl — dates internationales et fuseaux horaires

    • mbstring — chaînes multi-octets

    • xml (SimpleXML) — recommandée pour Castopod (un fallback regex existe)

  • 🌐 Serveur web : Apache ou Nginx

  • 🔒 HTTPS : requis pour les fonctionnalités PWA

Installation de lextension intl
# Ubuntu / Debian
sudo apt-get install php-intl

# Fedora / RHEL / CentOS
sudo dnf install php-intl

# Redémarrer le serveur web
sudo systemctl restart apache2        # Apache
sudo systemctl restart php8.3-fpm     # Nginx + php-fpm

# Vérifier
php -m | grep intl

🚀 Installation

  1. 📥 Clonez le dépôt :

    git clone git@labola.o-k-i.net:cedric/annu-kute-ced.git
    cd annu-kute-ced
  2. 📦 Vérifiez les prérequis PHP (voir ci-dessus).

  3. 🔧 Créez votre configuration locale et les fichiers dinstance :

    cp includes/config.local.php.sample includes/config.local.php
    cp site.webmanifest.sample site.webmanifest
    cp robots.txt.sample robots.txt
    cp sitemap.xml.sample sitemap.xml
    cp mentions-legales.php.sample mentions-legales.php
    cp dons.php.sample dons.php
  4. 🛡️ Installez la configuration serveur sécurisée depuis conf/ (voir Sécurité).

  5. ✍️ Rendez le dossier cache/ accessible en écriture par le serveur web (créé automatiquement au premier appel API).

  6. 🔒 Assurez-vous que HTTPS est actif (obligatoire pour la PWA).

  7. 🌐 Faites pointer votre domaine (annukuteced.buzz) vers la racine du projet.

⚙️ Configuration

Le système en trois fichiers

Fichier Rôle Versionné ?

includes/config.php

Bootstrap : charge les autres fichiers et contient toutes les fonctions métier

Oui

includes/config.default.php

Valeurs par défaut de chaque constante (définies seulement si absentes)

Oui

includes/config.local.php

Vos surcharges locales — prioritaires car chargées en premier

Non (.gitignore)

Pour personnaliser linstance, ne modifiez jamais config.default.php : définissez la constante dans config.local.php. Comme ce dernier est chargé avant, vos valeurs ont toujours la priorité.

cp includes/config.local.php.sample includes/config.local.php

Référence des constantes

Valeurs par défaut issues de includes/config.default.php.

Table 1. Application et organisation
Constante Défaut Description

APP_HOST_NAME

'annukuteced.buzz'

Nom de domaine affiché (mentions légales…)

ORGANIZATION_SHORT_NAME

'ANNU KUTE CED'

Nom court de lorganisation

ORGANIZATION_NAME

'ANNU KUTE CED'

Nom complet de lorganisation

Table 2. PeerTube
Constante Défaut Description

PEERTUBE_URL

'https://gade.o-k-i.net'

URL de linstance PeerTube (obligatoire)

PEERTUBE_DISPLAY_NAME

'gade.o-k-i.net'

Nom affiché de linstance

API_KEY

''

Clé API optionnelle (Authorization: ApiKey …)

SHOW_VIDEO_VIEWS

false

Afficher le nombre de vues sur les cartes vidéo

PRIORITY_CATEGORIES

[11, 15, 4, 9, 10]

Catégories mises en avant : [ID ⇒ 'Nom'], ordre = ordre daffichage (accueil, sidebar, footer, menu mobile)

DEFAULT_TIMEZONE

'America/Guadeloupe'

Fuseau horaire daffichage (liste PHP)

Table 3. Section hero (bannière daccueil)
Constante Défaut Description

HERO_TYPE

'live'

'live' · 'video' · 'playlist' · 'none'

LIVE_ACCOUNT_NAME

'annu_kute_ced'

Compte PeerTube surveillé pour les directs (hero + page /direct)

HERO_VIDEO_ID

'fDE6tvQMXRuBpE4eDDS7kj'

UUID de la vidéo (mode video), extrait de lURL /w/<uuid>

HERO_VIDEO_TITLE

'Vidéo de présentation'

Titre (accessibilité)

PLAYLIST_PLATFORM

'peertube'

'peertube' · 'funkwhale' · 'castopod'

PLAYLIST_INSTANCE_URL

''

URL de base de la plateforme de playlist

PLAYLIST_ID

''

ID de la playlist ; pour Castopod : '@slug'

PLAYLIST_TITLE / PLAYLIST_DESCRIPTION

'' / ''

Textes optionnels (sinon récupérés via API)

Table 4. Castopod (podcast)
Constante Défaut Description

CASTOPOD_ENABLED

true

Activer la section podcast de laccueil

CASTOPOD_URL

'https://kute.o-k-i.net'

URL de linstance Castopod

CASTOPOD_PODCAST_SLUGS

['annu_kute_cedric']

Slug(s) de podcast ; flux RSS = {URL}/@{slug}/feed. Plusieurs podcasts : épisodes fusionnés et triés par date

CASTOPOD_EPISODES_COUNT

5

Nombre total d’épisodes affichés

Warning
Castopod applique un rate limit strict (HTTP 429) sur les flux RSS. Le code patiente 5 s entre chaque podcast. Activez le cache (CACHE_ENABLED) pour éviter ces délais à chaque page.
Table 5. Funkwhale (musique, optionnel)
Constante Défaut Description

FUNKWHALE_ENABLED

false

Activer la section musique

FUNKWHALE_URL

'https://mizik.o-k-i.net'

URL de linstance Funkwhale

FUNKWHALE_TRACKS_COUNT

10

Morceaux aléatoires locaux affichés (50 mis en cache)

Table 6. Mastodon (timeline)
Constante Défaut Description

MASTODON_INSTANCE_URL

'https://bokante.o-k-i.net'

Instance Mastodon (ou Pleroma, via ladaptateur)

MASTODON_URL

'https://bokante.o-k-i.net/@cedric'

URL publique du compte (liens « Voir plus », footer)

MASTODON_DATE_FORMAT

'fr-FR'

Locale de date des posts

MASTODON_BTN_SEE_MORE / MASTODON_BTN_RELOAD

'Voir plus de post' / 'Rafraichir'

Libellés des boutons

MASTODON_MAX_POST_FETCH / MASTODON_MAX_POST_SHOW

'10' / '10'

Posts récupérés / affichés

MASTODON_S3_MEDIA_URL

(non défini)

Origine S3 des médias (ajoutée à la CSP media-src)

Note
La timeline na pas de flag dactivation : elle est toujours affichée sur laccueil.
Table 7. WordPress (optionnel)
Constante Défaut Description

WORDPRESS_ENABLED

false

Activer la section articles

WORDPRESS_URL

''

URL du site WordPress (sans / final)

WORDPRESS_POSTS_COUNT

6

Nombre darticles (API REST /wp-json/wp/v2/)

Table 8. Dons
Constante Défaut Description

DONATIONS_ENABLED

false

Activer le système (icône cœur, lien « Soutenir », page /dons.php)

LIBERAPAY_URL / KOFI_URL

'' / ''

URLs des plateformes externes

STRIPE_ENABLED

false

Activer linterface Stripe (onglets ponctuel / mensuel)

STRIPE_ONE_TIME_LINKS

[1, 5, 10, 20, 50, 'custom'] ⇒ ''

Liens de paiement Stripe par montant

STRIPE_MONTHLY_LINKS

[1, 5, 10, 20, 50] ⇒ ''

Liens dabonnement mensuel Stripe

DONATION_AMOUNTS

[1, 5, 10, 20, 50]

Montants suggérés

DONATION_CURRENCY

'EUR'

Devise

Note
Au moins une plateforme (LiberaPay, Ko-fi ou Stripe) doit être configurée pour que dons.php saffiche, sinon la page renvoie une erreur 500.
Table 9. Compte à rebours (maintenance / lancement)
Constante Défaut Description

COUNTDOWN_ENABLED

false

⚠️ Si true, toutes les pages redirigent vers countdown.php

COUNTDOWN_TARGET_DATE

'2025-10-11 00:00:00'

Date cible (Y-m-d H:i:s) ; à la fin, redirection vers /

COUNTDOWN_TIMEZONES

5 territoires

[Libellé ⇒ fuseau PHP] affichés sur la page

Table 10. Annonce du prochain live
Constante Défaut Description

NEXT_LIVE_ENABLED

false

Afficher lannonce quand aucun direct nest en cours

NEXT_LIVE_TITLE / NEXT_LIVE_DESCRIPTION

'Prochain live' / 'Rejoignez-nous…'

Textes (date et heure ajoutées automatiquement)

NEXT_LIVE_DATE

''

Date du live (Y-m-d H:i:s)

NEXT_LIVE_IMAGE

'uploads/next-live.jpg'

Image dannonce (utilisée seulement si le fichier existe)

Table 11. Site, réseaux sociaux et contact
Constante Défaut Description

SITE_NAME

'ANNU KUTE CED'

Nom du site (titres, meta)

SITE_DESCRIPTION

'Hub multimédia du podcast ANNU KUTE CED'

Description SEO

SITE_LOGO / SITE_FAVICON

'img/logo.png' / 'img/favicon.png'

Chemins des images

FACEBOOK_URL, X_URL, INSTAGRAM_URL, YOUTUBE_URL, TIKTOK_URL

'#'

'#' ou vide = icône masquée

CONTACT_EMAIL

'kontak@o-k-i.net'

E-mail de contact (footer)

Table 12. Mentions légales
Constante Défaut Description

LEGAL_COPYRIGHT

'ANNU KUTE CED'

Titulaire du copyright

LEGAL_WEBMASTER_NAME / LEGAL_WEBMASTER_EMAIL

'Cédric Famibelle-Pronzola' / 'contact@cedric-pronzola.dev'

Webmaster

LEGAL_HOST_NAME, LEGAL_HOST_COMPANY, LEGAL_HOST_RCS, LEGAL_HOST_ADDRESS

valeurs o2Switch

Coordonnées de lhébergeur

LEGAL_CONTACT_EMAIL

'kontak@o-k-i.net'

Contact juridique

LEGAL_LICENSE / LEGAL_LICENSE_URL

AGPL-V3 + URL GNU

Licence du code

LEGAL_SOURCE_CODE_URL

'https://labola.o-k-i.net/cedric/annu-kute-ced'

Dépôt du code source (exigé par lAGPL pour un service réseau)

LEGAL_SERVICE_DESCRIPTION

'est le hub multimédia du podcast ANNU KUTE CED.'

Phrase de description du service

Table 13. Tags et filtres
Constante Défaut Description

TAG_INDEPENDENCE

'indépendance'

Tag de la fonction getIndependenceVideos() (pas de section daccueil associée actuellement)

SHORTS_MAX_DURATION

180

Durée max dun short (s) ; ratio portrait (aspectRatio ≤ 1) aussi requis

IMPORTANT_TAGS

['ANNUKUTECED', 'podcast', …]

Hashtags de la sidebar, du footer et du menu mobile

POPULAR_TAGS

['podcast', 'annukuteced', …]

Hashtags affichés sur laccueil

Table 14. Pagination et volumes daffichage
Constante Défaut Description

VIDEOS_PER_PAGE

12

Vidéos par page (recherche)

COUNT_VIDEO_SEARCH

20

Résultats récupérés par recherche

RECENT_VIDEOS_COUNT / TRENDING_VIDEOS_COUNT / CATEGORY_VIDEOS_COUNT

6

Taille des sections daccueil

SHORTS_COUNT / SHORTS_COUNT_SEARCH

6 / 100

Shorts affichés / pool de recherche

LOAD_MORE_COUNT

6

Vidéos chargées par clic sur « Voir plus »

FEATURED_VIDEOS_COUNT / INDEPENDENCE_VIDEOS_COUNT

6

Réservés (sections non affichées actuellement)

Table 15. Cache
Constante Défaut Description

CACHE_ENABLED

false

Active le cache pour Castopod et Funkwhale (fortement recommandé)

CACHE_DURATION

3600

Durée de vie du cache (s)

Note
Le cache de lAPI PeerTube et de WordPress (includes/simple-cache.php) est toujours actif, indépendamment de CACHE_ENABLED, avec des TTL par endpoint (catégories 1 h, vidéos/recherche 10 min, articles WP 15 min, comptes 5 min). Les fichiers sont stockés dans cache/api/.
Table 16. Bloc « À propos » (pas de valeur par défaut — bloc masqué tant que non défini)
Constante Description

MOVEMENT_TITLE

Titre du bloc (défaut template : « À propos »)

MOVEMENT_DESCRIPTION

Premier paragraphe ; commenter cette ligne masque tout le bloc

MOVEMENT_DESCRIPTION_2

Second paragraphe (optionnel)

MOVEMENT_IMAGE / MOVEMENT_IMAGE_ALT / MOVEMENT_CAPTION

Image (img/movement_presentation.png, non versionnée), texte alternatif et légende (HTML simple accepté)

Exemple complet de config.local.php

<?php
define('APP_HOST_NAME', 'annukuteced.buzz');

// PeerTube (chaîne GADE du podcast)
define('PEERTUBE_URL', 'https://gade.o-k-i.net');
define('PEERTUBE_DISPLAY_NAME', 'gade.o-k-i.net');

// Hero : direct de la chaîne, sinon annonce du prochain live
define('HERO_TYPE', 'live');
define('LIVE_ACCOUNT_NAME', 'annu_kute_ced');
define('NEXT_LIVE_ENABLED', true);
define('NEXT_LIVE_TITLE', 'Prochain live');
define('NEXT_LIVE_DESCRIPTION', 'Enregistrement du prochain épisode !');
define('NEXT_LIVE_DATE', '2025-10-11 10:00:00');

// Castopod (compte KUTE du podcast)
define('CASTOPOD_ENABLED', true);
define('CASTOPOD_URL', 'https://kute.o-k-i.net');
define('CASTOPOD_PODCAST_SLUGS', ['annu_kute_cedric']);
define('CASTOPOD_EPISODES_COUNT', 5);

// Mastodon (compte BOKANTE)
define('MASTODON_INSTANCE_URL', 'https://bokante.o-k-i.net');
define('MASTODON_URL', 'https://bokante.o-k-i.net/@cedric');

// Cache : indispensable pour Castopod (rate limit RSS)
define('CACHE_ENABLED', true);
define('CACHE_DURATION', 3600);

🎨 Personnalisation de lapparence

  1. 🖼️ Remplacez img/logo.png par votre logo (et les favicons du dossier img/)

  2. 🎨 Modifiez les couleurs dans css/styles.css

  3. 📝 Textes : la plupart passent par les constantes de config.local.php (bloc « À propos », libellés Mastodon, etc.)

Fichiers dinstance à personnaliser

Les fichiers sitemap.xml, robots.txt, site.webmanifest, mentions-legales.php et dons.php sont ignorés par Git : copiez les .sample puis adaptez-les. Les exemples fournis sont pré-remplis pour annukuteced.buzz ; pour un autre domaine, remplacez simplement les occurrences du domaine. Dans mentions-legales.php, remplacez aussi le placeholder VOTRE-DATE-MAJ.

🧭 Ordre des sections sur la page daccueil

La page daccueil assemble les sections dans cet ordre, chacune conditionnée par sa configuration :

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

  2. Podcasts Castopod — si CASTOPOD_ENABLED et CASTOPOD_URL non vide

  3. Morceaux Funkwhale — si FUNKWHALE_ENABLED et FUNKWHALE_URL non vide

  4. Timeline Mastodon — toujours affichée

  5. Articles WordPress — si WORDPRESS_ENABLED et WORDPRESS_URL non vide

  6. Hashtags populaires (POPULAR_TAGS)

  7. Shorts — carrousel

  8. Dernières vidéos + bouton « Voir plus »

  9. Tendances + « Voir plus »

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

  11. Bloc « À propos » (si MOVEMENT_DESCRIPTION défini) + aside hashtags

Mise en page desktop : hero/Castopod/Funkwhale + Mastodon + WordPress sorganisent en colonnes (1fr 2fr, ou 1fr 2fr 1fr avec WordPress) ; empilés verticalement sur mobile.

🛡️ Sécurité

Ce que lapplication applique déjà

  • CSP dynamique : default-src 'self' avec nonce unique par requête pour les scripts/styles inline ; origines PeerTube/Mastodon/CDN ajoutées automatiquement ; assouplissements localhost pour le développement

  • En-têtes : X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, X-XSS-Protection, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy restrictive, HSTS (HTTPS uniquement), CORP/COOP same-origin

    Note
    Len-tête COEP a été volontairement retiré (commit 62d4d99) : require-corp bloquait les embeds cross-origin de PeerTube, Mastodon, Castopod et Funkwhale.
  • CSRF : jeton de session (random_bytes(32)) injecté en <meta>, vérifié par hash_equals sur lendpoint AJAX ; vérification de len-tête Origin

  • Anti-SSRF : validation des URLs dinstances (schéma http/https, blocage IP privées et localhost), liste blanche des endpoints de lAPI PeerTube, cURL sans redirections

  • Anti-XSS : échappement systématique des sorties ; validation des entrées (UUID vidéo, requête de recherche ≤ 200 car., numéros de page, ID de catégorie 120)

Configuration serveur (Apache)

cp conf/.htaccess.sample .htaccess

Protections incluses : blocage des fichiers de configuration et .sample, des dossiers /includes/, /cache/, /docs/, /conf/ ; pas de listing de répertoires ; fichiers cachés bloqués ; HTTPS forcé ; masquage de lextension .php (URLs propres /video au lieu de /video.php).

Warning
Cette configuration est essentielle : sans elle, vos fichiers de configuration sont exposés aux visiteurs.

Configuration serveur (Nginx)

# Adaptez les chemins dans conf/nginx.conf.sample puis :
sudo cp conf/nginx.conf.sample /etc/nginx/sites-available/annukuteced.buzz
sudo ln -s /etc/nginx/sites-available/annukuteced.buzz /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Warning
Le bloc location ~* \.(php|inc|conf|config|local)$ { deny all; } de lexemple correspond tel quel à tous les fichiers .php. Ajustez-le pour ne cibler que les chemins sensibles (par exemple ^/includes/), sous peine de rendre lapplication inaccessible.

📱 Progressive Web App (PWA)

Fonctionnalités

  • 📲 Installation native : bouton « Installer » dans le header à la première visite

  • 🌐 Mode hors ligne : cache des pages visitées et des ressources statiques

  • 📡 Détection de connexion : indicateur visuel en cas de perte réseau

  • 🔄 Mise à jour : proposition de rechargement quand une nouvelle version est détectée

Stratégies de cache du Service Worker (sw.js)

Type de requête Stratégie Détail

Ressources statiques (/css/, /js/, /img/, CDN cdnjs)

Cache First

Mise en cache à linstallation et au premier accès ; repli sur img/logo.png si une image échoue

Pages (/, *.php…)

Network First

Réseau dabord, cache en secours hors ligne

API et AJAX (/ajax/, mastodon-config.php…)

Network Only

Jamais de cache ; réponse JSON 503 synthétique hors ligne

Tip
Pour déployer une nouvelle version des assets, changez le suffixe de version des noms de cache dans sw.js (format date, ex. 08072026-0720) : les anciens caches sont supprimés à lactivation.

Installation par les visiteurs

  • 🌐 Chrome/Edge : menu → « Installer annukuteced.buzz »

  • 🍎 Safari iOS : Partager → « Ajouter à l’écran daccueil »

  • 🦊 Firefox Android : menu → « Installer »

📊 Analytics (Plausible)

Le site est pré-configuré pour Plausible Analytics (sans cookies, conforme RGPD, données anonymes), mais le script est désactivé par défaut.

Pour lactiver :

  1. Ouvrez index.php et décommentez le snippet Plausible (balise <script defer data-domain=… src="https://plausible.io/js/script…js"> et son shim window.plausible).

  2. Cest tout : la CSP autorise déjà https://plausible.io en script-src et connect-src, et le data-domain utilise automatiquement le domaine courant.

Données alors collectées (anonymes) : pages visitées, pays dorigine (IP non stockée), type dappareil, navigateur, temps passé.

🚀 Déploiement sur serveur mutualisé

  1. 🐘 Vérifiez que lhébergeur fournit PHP 7.4+ et les extensions requises

  2. 🔒 Activez HTTPS (obligatoire pour la PWA)

  3. 📤 Transférez les fichiers via FTP à la racine du site

  4. 🔧 Permissions : 644 pour les fichiers, 755 pour les dossiers ; cache/ accessible en écriture

  5. 📄 Créez les fichiers dinstance depuis les .sample (config, manifest, robots, sitemap, mentions légales, dons)

  6. 🛡️ Copiez conf/.htaccess.sample en .htaccess (Apache)

  7. 🌐 Faites pointer le domaine annukuteced.buzz vers le dossier dinstallation

  8. 🧪 Testez la PWA via les outils de développement du navigateur (onglet Application)

👨‍💻 Développement et contribution

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

  2. 💾 Committez avec des messages atomiques en anglais, au format conventionnel (< 70 caractères) :

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

  3. 📤 Poussez : git push origin ma-fonctionnalite

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

  • Ne versionnez jamais config.local.php ni les fichiers dérivés des .sample

  • Quelques constantes sont définies mais non utilisées par le code actuel (ENABLE_SEARCH, ENABLE_COMMENTS, ENABLE_USER_ACCOUNTS, TAG_SHORT) : ne vous en servez pas comme points dextension

📄 Exporter la documentation (HTML / PDF)

Ce README peut être exporté en HTML et en PDF en une seule commande :

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

Les fichiers sont produits à la racine du projet et ignorés par Git (.gitignore).

Prérequis : asciidoctor, chromium (rendu headless) et le paquet fonts-noto-color-emoji (émojis couleur dans le PDF). La feuille de style docs/docinfo.html (fallback emoji + règles dimpression) est injectée automatiquement via le mécanisme docinfo dAsciidoctor.

Note
asciidoctor-pdf (utilisé par lextension AsciiDoc de VSCodium) nest pas utilisable ici : son moteur Prawn ne sait pas embarquer les polices emoji couleur (CBDT/COLR), les émojis disparaissent du PDF. Chromium les gère nativement.

📜 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 a responsive web interface acting as a multimedia hub for 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);

  • 📡 its Mastodon timeline (news and announcements).

The platform is lightweight, deployable on shared hosting (PHP + web server, no database), and optimized for both mobile and desktop. It is also an installable Progressive Web App with offline support.

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

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

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

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 is configured by default to aggregate the ANNU KUTE CED podcasts sources:

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

📡 Mastodon (timeline)

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

@cedric

🎵 Funkwhale (music, optional, disabled by default)

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

random tracks from the instance

All of these sources can be changed in includes/config.local.php (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)

  • AJAX "Load more" pagination (protected by CSRF token and Origin check)

📺 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 (4:5, 1:1, 16:9)

  • Configurable homepage hero section: live, single video, playlist (PeerTube / Funkwhale / Castopod) or hidden

🎙️ Podcast and audio

  • Castopod: latest episodes via RSS feed, with a built-in audio player (play/pause, auto-advance to the next episode, one stream at a time)

  • Funkwhale (optional): random track selection from the instance, using the same built-in player

📡 Social networks and external content

  • Embedded Mastodon timeline (vendored mastodon-embed-timeline v4.7.0 library, AGPLv3)

  • Pleroma compatibility: adapter converting Pleroma API responses to the Mastodon format expected by the timeline

  • WordPress (optional): latest posts via the REST API, with featured image and author

📱 PWA and user experience

  • Installable Progressive Web App (automatic install button)

  • Offline mode: visited pages and static assets cached via Service Worker

  • Visual indicator when the connection is lost

  • Dark mode (based on prefers-color-scheme, persisted in localStorage)

  • Fully responsive interface (mobile, tablet, desktop)

💝 Donations, countdown and more

  • Donation page: LiberaPay, Ko-fi and Stripe (one-time and monthly donations, suggested amounts)

  • Countdown: multi-timezone launch/maintenance page that locks the whole site until the target date

  • Configurable "About" block (title, two paragraphs, captioned image)

🔍 SEO and structured data

  • Automatically generated JSON-LD: WebSite (with SearchAction), VideoObject, CollectionPage, BreadcrumbList

  • Open Graph and Twitter Cards meta tags

  • sitemap.xml and robots.txt provided as samples

  • Ready for Plausible Analytics (see Analytics): disabled by default, cookie-free and GDPR-compliant

🛡️ Security

  • Full security headers: dynamic CSP with a per-request nonce, HSTS, X-Frame-Options, nosniff, Referrer-Policy, Permissions-Policy

  • CSRF protection on AJAX requests, Origin header verification

  • Anti-SSRF validation of instance URLs and an allowlist of PeerTube API endpoints

  • Systematic output escaping (anti-XSS)

  • Hardened Apache and Nginx configuration samples in conf/

🛠️ Tech stack

  • 📄 HTML5, 🎨 CSS3 (Media Queries), vanilla JavaScript (no framework, no jQuery)

  • 🐘 PHP 7.4+ (no database required)

  • 🔧 Service Worker (offline cache) and 📋 Web App Manifest (installation)

  • 📦 Libraries:

    • 🎯 Font Awesome 7 (icons, via cdnjs CDN)

    • 📡 mastodon-embed-timeline v4.7.0 (vendored in js/, AGPLv3)

    • 📊 Plausible Analytics (pre-configured, to be enabled manually)

📁 Project structure

├── ajax/
│   └── load-more-videos.php        # "Load more" AJAX endpoint (CSRF + Origin)
├── conf/
│   ├── .htaccess.sample            # Hardened Apache configuration
│   └── nginx.conf.sample           # Hardened Nginx configuration
├── css/                            # Stylesheets (styles, video, categories,
│   │                               #   search, donations, countdown, mastodon,
│   ├── castopod-podcasts.css       #   castopod, funkwhale, wordpress, grid…)
│   └── …
├── docs/
│   ├── wireframes/                 # Original mockups (desktop + mobile PNGs)
│   ├── docinfo.html                # Print styles (emoji fallback) for the export
│   └── generate-readme-pdf.sh      # Docs export: README.html / README.pdf
├── img/                            # Logo, favicons, play icon
├── includes/
│   ├── config.php                  # Bootstrap + API functions (PeerTube, Castopod,
│   │                               #   Funkwhale) and formatting helpers
│   ├── config.default.php          # Default values for every constant
│   ├── config.local.php.sample     # Local configuration template (to copy)
│   ├── security.php                # CSRF, CSP + nonce, headers, validators
│   ├── simple-cache.php            # File-based API cache (cache/api/)
│   ├── wordpress.php               # WordPress REST API integration
│   ├── structured-data.php         # JSON-LD generators
│   ├── header.php                  # Top bar (search, socials, theme…)
│   ├── footer.php                  # Footer
│   ├── sidebar.php                 # Side navigation
│   ├── mobile-menu.php             # Slide-in mobile menu
│   ├── hero-section.php            # Homepage banner (live/video/playlist)
│   ├── featured-videos.php         # ⚠️ legacy, unused by current pages
│   ├── recent-videos.php           # ⚠️ legacy, unused by current pages
│   ├── pwa-init.php                # ⚠️ legacy, PWA now inlined in index.php
│   └── lib/
│       └── markdown.php            # Markdown renderer (PeerTube-flavored subset)
├── js/
│   ├── main.js                     # Global UI (theme, carousel, "Load more"…)
│   ├── audio-player.js             # Castopod / Funkwhale audio player
│   ├── categories.js               # Category page "Load more"
│   ├── countdown.js                # Countdown timer
│   ├── search.js                   # Clickable search result cards
│   ├── mastodon-config.php         # Timeline JS config (served as JavaScript)
│   ├── mastodon-timeline.umd.js    # mastodon-embed-timeline v4.7.0 (vendored)
│   └── pleroma-adapter.js          # Pleroma → Mastodon API adapter
├── uploads/                        # Announcement images (not versioned, except .gitkeep)
├── index.php                       # Homepage (aggregator)
├── video.php                       # Video playback page
├── categories.php                  # Category page
├── recherche.php                   # Search page
├── direct.php                      # Live stream / next-live announcement page
├── countdown.php                   # Countdown page (maintenance)
├── sw.js                           # Service Worker (PWA)
├── site.webmanifest.sample         # PWA manifest sample
├── browserconfig.xml               # Windows tiles
├── sitemap.xml.sample              # Sitemap sample
├── robots.txt.sample               # robots.txt sample
├── mentions-legales.php.sample     # Legal notice sample
├── dons.php.sample                 # Donation page sample
├── LICENSE                         # GNU AGPL v3
└── README.adoc
Note
Files marked "legacy" are kept for reference but are not included by any page; the homepage renders those sections internally.

📋 Requirements

  • 🐘 PHP 7.4+ (recommended: PHP 8.0+)

  • 📦 Required PHP extensions:

    • curl — API calls (PeerTube, Castopod, Funkwhale, WordPress)

    • json — response handling

    • intl — international dates and timezones

    • mbstring — multi-byte strings

    • xml (SimpleXML) — recommended for Castopod (a regex fallback exists)

  • 🌐 Web server: Apache or Nginx

  • 🔒 HTTPS: required for PWA features

Installing the intl extension
# Ubuntu / Debian
sudo apt-get install php-intl

# Fedora / RHEL / CentOS
sudo dnf install php-intl

# Restart the web server
sudo systemctl restart apache2        # Apache
sudo systemctl restart php8.3-fpm     # Nginx + php-fpm

# Check
php -m | grep intl

🚀 Installation

  1. 📥 Clone the repository:

    git clone git@labola.o-k-i.net:cedric/annu-kute-ced.git
    cd annu-kute-ced
  2. 📦 Check the PHP requirements (see above).

  3. 🔧 Create your local configuration and instance files:

    cp includes/config.local.php.sample includes/config.local.php
    cp site.webmanifest.sample site.webmanifest
    cp robots.txt.sample robots.txt
    cp sitemap.xml.sample sitemap.xml
    cp mentions-legales.php.sample mentions-legales.php
    cp dons.php.sample dons.php
  4. 🛡️ Install the hardened server configuration from conf/ (see Security).

  5. ✍️ Make the cache/ directory writable by the web server (created automatically on the first API call).

  6. 🔒 Make sure HTTPS is enabled (mandatory for the PWA).

  7. 🌐 Point your domain (annukuteced.buzz) to the project root.

⚙️ Configuration

The three-file system

File Role Versioned?

includes/config.php

Bootstrap: loads the other files and holds all business functions

Yes

includes/config.default.php

Default values for every constant (defined only if missing)

Yes

includes/config.local.php

Your local overrides — take precedence as they load first

No (.gitignore)

To customize the instance, never edit config.default.php: define the constant in config.local.php. Since it loads first, your values always win.

cp includes/config.local.php.sample includes/config.local.php

Constants reference

Default values from includes/config.default.php.

Table 17. Application and organization
Constant Default Description

APP_HOST_NAME

'annukuteced.buzz'

Displayed domain name (legal notice…)

ORGANIZATION_SHORT_NAME

'ANNU KUTE CED'

Short organization name

ORGANIZATION_NAME

'ANNU KUTE CED'

Full organization name

Table 18. PeerTube
Constant Default Description

PEERTUBE_URL

'https://gade.o-k-i.net'

PeerTube instance URL (required)

PEERTUBE_DISPLAY_NAME

'gade.o-k-i.net'

Displayed instance name

API_KEY

''

Optional API key (Authorization: ApiKey …)

SHOW_VIDEO_VIEWS

false

Show view counts on video cards

PRIORITY_CATEGORIES

[11, 15, 4, 9, 10]

Featured categories: [ID ⇒ 'Name'], order = display order (homepage, sidebar, footer, mobile menu)

DEFAULT_TIMEZONE

'America/Guadeloupe'

Display timezone (PHP list)

Table 19. Hero section (homepage banner)
Constant Default Description

HERO_TYPE

'live'

'live' · 'video' · 'playlist' · 'none'

LIVE_ACCOUNT_NAME

'annu_kute_ced'

PeerTube account watched for live streams (hero + /direct page)

HERO_VIDEO_ID

'fDE6tvQMXRuBpE4eDDS7kj'

Video UUID (video mode), extracted from the /w/<uuid> URL

HERO_VIDEO_TITLE

'Vidéo de présentation'

Title (accessibility)

PLAYLIST_PLATFORM

'peertube'

'peertube' · 'funkwhale' · 'castopod'

PLAYLIST_INSTANCE_URL

''

Base URL of the playlist platform

PLAYLIST_ID

''

Playlist ID; for Castopod: '@slug'

PLAYLIST_TITLE / PLAYLIST_DESCRIPTION

'' / ''

Optional texts (otherwise fetched via API)

Table 20. Castopod (podcast)
Constant Default Description

CASTOPOD_ENABLED

true

Enable the homepage podcast section

CASTOPOD_URL

'https://kute.o-k-i.net'

Castopod instance URL

CASTOPOD_PODCAST_SLUGS

['annu_kute_cedric']

Podcast slug(s); RSS feed = {URL}/@{slug}/feed. Multiple podcasts: episodes merged and date-sorted

CASTOPOD_EPISODES_COUNT

5

Total number of episodes displayed

Warning
Castopod enforces a strict rate limit (HTTP 429) on RSS feeds. The code waits 5 s between podcasts. Enable the cache (CACHE_ENABLED) to avoid these delays on every page.
Table 21. Funkwhale (music, optional)
Constant Default Description

FUNKWHALE_ENABLED

false

Enable the music section

FUNKWHALE_URL

'https://mizik.o-k-i.net'

Funkwhale instance URL

FUNKWHALE_TRACKS_COUNT

10

Random local tracks displayed (50 cached)

Table 22. Mastodon (timeline)
Constant Default Description

MASTODON_INSTANCE_URL

'https://bokante.o-k-i.net'

Mastodon instance (or Pleroma, via the adapter)

MASTODON_URL

'https://bokante.o-k-i.net/@cedric'

Public account URL ("See more" links, footer)

MASTODON_DATE_FORMAT

'fr-FR'

Post date locale

MASTODON_BTN_SEE_MORE / MASTODON_BTN_RELOAD

'Voir plus de post' / 'Rafraichir'

Button labels

MASTODON_MAX_POST_FETCH / MASTODON_MAX_POST_SHOW

'10' / '10'

Posts fetched / displayed

MASTODON_S3_MEDIA_URL

(undefined)

S3 media origin (added to the CSP media-src)

Note
The timeline has no enable flag: it is always displayed on the homepage.
Table 23. WordPress (optional)
Constant Default Description

WORDPRESS_ENABLED

false

Enable the posts section

WORDPRESS_URL

''

WordPress site URL (no trailing /)

WORDPRESS_POSTS_COUNT

6

Number of posts (REST API /wp-json/wp/v2/)

Table 24. Donations
Constant Default Description

DONATIONS_ENABLED

false

Enable the system (heart icon, "Support" link, /dons.php page)

LIBERAPAY_URL / KOFI_URL

'' / ''

External platform URLs

STRIPE_ENABLED

false

Enable the Stripe interface (one-time / monthly tabs)

STRIPE_ONE_TIME_LINKS

[1, 5, 10, 20, 50, 'custom'] ⇒ ''

Stripe payment links per amount

STRIPE_MONTHLY_LINKS

[1, 5, 10, 20, 50] ⇒ ''

Stripe monthly subscription links

DONATION_AMOUNTS

[1, 5, 10, 20, 50]

Suggested amounts

DONATION_CURRENCY

'EUR'

Currency

Note
At least one platform (LiberaPay, Ko-fi or Stripe) must be configured for dons.php to render, otherwise the page returns a 500 error.
Table 25. Countdown (maintenance / launch)
Constant Default Description

COUNTDOWN_ENABLED

false

⚠️ When true, every page redirects to countdown.php

COUNTDOWN_TARGET_DATE

'2025-10-11 00:00:00'

Target date (Y-m-d H:i:s); on completion, redirects to /

COUNTDOWN_TIMEZONES

5 territories

[Label ⇒ PHP timezone] shown on the page

Table 26. Next-live announcement
Constant Default Description

NEXT_LIVE_ENABLED

false

Show the announcement when no live stream is running

NEXT_LIVE_TITLE / NEXT_LIVE_DESCRIPTION

'Prochain live' / 'Rejoignez-nous…'

Texts (date and time appended automatically)

NEXT_LIVE_DATE

''

Live date (Y-m-d H:i:s)

NEXT_LIVE_IMAGE

'uploads/next-live.jpg'

Announcement image (used only if the file exists)

Table 27. Site, social networks and contact
Constant Default Description

SITE_NAME

'ANNU KUTE CED'

Site name (titles, meta)

SITE_DESCRIPTION

'Hub multimédia du podcast ANNU KUTE CED'

SEO description

SITE_LOGO / SITE_FAVICON

'img/logo.png' / 'img/favicon.png'

Image paths

FACEBOOK_URL, X_URL, INSTAGRAM_URL, YOUTUBE_URL, TIKTOK_URL

'#'

'#' or empty = icon hidden

CONTACT_EMAIL

'kontak@o-k-i.net'

Contact e-mail (footer)

Table 28. Legal notice
Constant Default Description

LEGAL_COPYRIGHT

'ANNU KUTE CED'

Copyright holder

LEGAL_WEBMASTER_NAME / LEGAL_WEBMASTER_EMAIL

'Cédric Famibelle-Pronzola' / 'contact@cedric-pronzola.dev'

Webmaster

LEGAL_HOST_NAME, LEGAL_HOST_COMPANY, LEGAL_HOST_RCS, LEGAL_HOST_ADDRESS

o2Switch values

Hosting provider details

LEGAL_CONTACT_EMAIL

'kontak@o-k-i.net'

Legal contact

LEGAL_LICENSE / LEGAL_LICENSE_URL

AGPL-V3 + GNU URL

Code license

LEGAL_SOURCE_CODE_URL

'https://labola.o-k-i.net/cedric/annu-kute-ced'

Source code repository (required by the AGPL for a network service)

LEGAL_SERVICE_DESCRIPTION

'est le hub multimédia du podcast ANNU KUTE CED.'

Service description sentence

Table 29. Tags and filters
Constant Default Description

TAG_INDEPENDENCE

'indépendance'

Tag used by getIndependenceVideos() (no associated homepage section currently)

SHORTS_MAX_DURATION

180

Max short duration (s); portrait ratio (aspectRatio ≤ 1) also required

IMPORTANT_TAGS

['ANNUKUTECED', 'podcast', …]

Hashtags in the sidebar, footer and mobile menu

POPULAR_TAGS

['podcast', 'annukuteced', …]

Hashtags displayed on the homepage

Table 30. Pagination and display volumes
Constant Default Description

VIDEOS_PER_PAGE

12

Videos per page (search)

COUNT_VIDEO_SEARCH

20

Results fetched per search

RECENT_VIDEOS_COUNT / TRENDING_VIDEOS_COUNT / CATEGORY_VIDEOS_COUNT

6

Homepage section sizes

SHORTS_COUNT / SHORTS_COUNT_SEARCH

6 / 100

Shorts displayed / search pool

LOAD_MORE_COUNT

6

Videos loaded per "Load more" click

FEATURED_VIDEOS_COUNT / INDEPENDENCE_VIDEOS_COUNT

6

Reserved (sections not currently displayed)

Table 31. Cache
Constant Default Description

CACHE_ENABLED

false

Enable caching for Castopod and Funkwhale (strongly recommended)

CACHE_DURATION

3600

Cache lifetime (s)

Note
The PeerTube API and WordPress cache (includes/simple-cache.php) is always on, regardless of CACHE_ENABLED, with per-endpoint TTLs (categories 1 h, videos/search 10 min, WP posts 15 min, accounts 5 min). Files are stored in cache/api/.
Table 32. "About" block (no default values — block hidden until defined)
Constant Description

MOVEMENT_TITLE

Block title (template fallback: "À propos")

MOVEMENT_DESCRIPTION

First paragraph; commenting this line hides the whole block

MOVEMENT_DESCRIPTION_2

Second paragraph (optional)

MOVEMENT_IMAGE / MOVEMENT_IMAGE_ALT / MOVEMENT_CAPTION

Image (img/movement_presentation.png, not versioned), alt text and caption (simple HTML allowed)

Complete config.local.php example

<?php
define('APP_HOST_NAME', 'annukuteced.buzz');

// PeerTube (the podcast's GADE channel)
define('PEERTUBE_URL', 'https://gade.o-k-i.net');
define('PEERTUBE_DISPLAY_NAME', 'gade.o-k-i.net');

// Hero: channel live stream, otherwise next-live announcement
define('HERO_TYPE', 'live');
define('LIVE_ACCOUNT_NAME', 'annu_kute_ced');
define('NEXT_LIVE_ENABLED', true);
define('NEXT_LIVE_TITLE', 'Prochain live');
define('NEXT_LIVE_DESCRIPTION', 'Recording the next episode!');
define('NEXT_LIVE_DATE', '2025-10-11 10:00:00');

// Castopod (the podcast's KUTE account)
define('CASTOPOD_ENABLED', true);
define('CASTOPOD_URL', 'https://kute.o-k-i.net');
define('CASTOPOD_PODCAST_SLUGS', ['annu_kute_cedric']);
define('CASTOPOD_EPISODES_COUNT', 5);

// Mastodon (BOKANTE account)
define('MASTODON_INSTANCE_URL', 'https://bokante.o-k-i.net');
define('MASTODON_URL', 'https://bokante.o-k-i.net/@cedric');

// Cache: essential for Castopod (RSS rate limit)
define('CACHE_ENABLED', true);
define('CACHE_DURATION', 3600);

🎨 Appearance customization

  1. 🖼️ Replace img/logo.png with your logo (and the favicons in img/)

  2. 🎨 Edit the colors in css/styles.css

  3. 📝 Most texts are set through config.local.php constants ("About" block, Mastodon labels, etc.)

Instance files to customize

The sitemap.xml, robots.txt, site.webmanifest, mentions-legales.php and dons.php files are Git-ignored: copy the .sample files then adapt them. The provided samples are pre-filled for annukuteced.buzz; for another domain, simply replace the domain occurrences. In mentions-legales.php, also replace the VOTRE-DATE-MAJ placeholder.

🧭 Homepage section order

The homepage assembles sections in this order, each gated by its configuration:

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

  2. Castopod podcasts — if CASTOPOD_ENABLED and CASTOPOD_URL is not empty

  3. Funkwhale tracks — if FUNKWHALE_ENABLED and FUNKWHALE_URL is not empty

  4. Mastodon timeline — always displayed

  5. WordPress posts — if WORDPRESS_ENABLED and WORDPRESS_URL is not empty

  6. Popular hashtags (POPULAR_TAGS)

  7. Shorts — carousel

  8. Latest videos + "Load more" button

  9. Trending + "Load more"

  10. One section per priority category (PRIORITY_CATEGORIES, only when it has videos) + "Load more"

  11. "About" block (if MOVEMENT_DESCRIPTION is defined) + hashtags aside

Desktop layout: hero/Castopod/Funkwhale + Mastodon + WordPress are arranged in columns (1fr 2fr, or 1fr 2fr 1fr with WordPress); stacked vertically on mobile.

🛡️ Security

What the application already enforces

  • Dynamic CSP: default-src 'self' with a unique per-request nonce for inline scripts/styles; PeerTube/Mastodon/CDN origins added automatically; localhost relaxations for development

  • Headers: X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, X-XSS-Protection, Referrer-Policy: strict-origin-when-cross-origin, restrictive Permissions-Policy, HSTS (HTTPS only), CORP/COOP same-origin

    Note
    The COEP header was deliberately removed (commit 62d4d99): require-corp broke cross-origin embeds from PeerTube, Mastodon, Castopod and Funkwhale.
  • CSRF: session token (random_bytes(32)) injected as a <meta> tag, verified with hash_equals on the AJAX endpoint; Origin header verification

  • Anti-SSRF: instance URL validation (http/https scheme, blocking private IPs and localhost), allowlist of PeerTube API endpoints, cURL without redirects

  • Anti-XSS: systematic output escaping; input validation (video UUID, search query ≤ 200 chars, page numbers, category ID 120)

Server configuration (Apache)

cp conf/.htaccess.sample .htaccess

Included protections: blocking of configuration and .sample files, of the /includes/, /cache/, /docs/, /conf/ directories; no directory listing; hidden files blocked; HTTPS enforced; .php extension masking (clean URLs /video instead of /video.php).

Warning
This configuration is essential: without it, your configuration files are exposed to visitors.

Server configuration (Nginx)

# Adjust the paths in conf/nginx.conf.sample, then:
sudo cp conf/nginx.conf.sample /etc/nginx/sites-available/annukuteced.buzz
sudo ln -s /etc/nginx/sites-available/annukuteced.buzz /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Warning
The samples location ~* \.(php|inc|conf|config|local)$ { deny all; } block as written matches all .php files. Adjust it to target only sensitive paths (e.g. ^/includes/), or the whole application will become unreachable.

📱 Progressive Web App (PWA)

Features

  • 📲 Native installation: an "Install" button in the header on first visit

  • 🌐 Offline mode: visited pages and static assets cached

  • 📡 Connection detection: visual indicator when the network is lost

  • 🔄 Updates: reload prompt when a new version is detected

Service Worker caching strategies (sw.js)

Request type Strategy Detail

Static assets (/css/, /js/, /img/, cdnjs CDN)

Cache First

Cached at install time and on first access; falls back to img/logo.png if an image fails

Pages (/, *.php…)

Network First

Network first, cache fallback when offline

API and AJAX (/ajax/, mastodon-config.php…)

Network Only

Never cached; synthetic 503 JSON response when offline

Tip
To deploy a new version of the assets, change the version suffix of the cache names in sw.js (date format, e.g. 08072026-0720): old caches are deleted on activation.

Installation by visitors

  • 🌐 Chrome/Edge: menu → "Install annukuteced.buzz"

  • 🍎 Safari iOS: Share → "Add to Home Screen"

  • 🦊 Firefox Android: menu → "Install"

📊 Analytics (Plausible)

The site is pre-configured for Plausible Analytics (cookie-free, GDPR-compliant, anonymous data), but the script is disabled by default.

To enable it:

  1. Open index.php and uncomment the Plausible snippet (the <script defer data-domain=… src="https://plausible.io/js/script…js"> tag and its window.plausible shim).

  2. Thats it: the CSP already allows https://plausible.io in script-src and connect-src, and the data-domain automatically uses the current domain.

Data then collected (anonymous): visited pages, country of origin (IP not stored), device type, browser, time spent.

🚀 Deploying to shared hosting

  1. 🐘 Check that the host provides PHP 7.4+ and the required extensions

  2. 🔒 Enable HTTPS (mandatory for the PWA)

  3. 📤 Upload the files via FTP to the site root

  4. 🔧 Permissions: 644 for files, 755 for directories; cache/ must be writable

  5. 📄 Create the instance files from the .sample files (config, manifest, robots, sitemap, legal notice, donations)

  6. 🛡️ Copy conf/.htaccess.sample to .htaccess (Apache)

  7. 🌐 Point the annukuteced.buzz domain to the installation directory

  8. 🧪 Test the PWA via the browser developer tools (Application tab)

👨‍💻 Development and contributing

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

  2. 💾 Commit with atomic, English messages, in conventional format (< 70 characters):

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

  3. 📤 Push: git push origin my-feature

  4. 🔀 Open a pull request

Repository best practices:

  • Bug reports and suggestions are welcome on the forks repository; generic improvements can be proposed upstream (FEDIVERSE OKI)

  • Never version config.local.php or files derived from the .sample files

  • A few constants are defined but unused by the current code (ENABLE_SEARCH, ENABLE_COMMENTS, ENABLE_USER_ACCOUNTS, TAG_SHORT): do not rely on them as extension points

📄 Exporting the documentation (HTML / PDF)

This README can be exported to HTML and PDF with a single command:

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

Files are produced in the project root and are Git-ignored (.gitignore).

Requirements: asciidoctor, chromium (headless rendering) and the fonts-noto-color-emoji package (color emoji in the PDF). The docs/docinfo.html stylesheet (emoji fallback + print rules) is automatically injected through Asciidoctors docinfo mechanism.

Note
asciidoctor-pdf (used by VSCodiums AsciiDoc extension) cannot be used here: its Prawn engine cannot embed color-emoji fonts (CBDT/COLR), so emoji disappear from the PDF. Chromium handles them natively.

📜 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%