diff --git a/categories.php b/categories.php
index 8d3e4ef..fc9b57e 100644
--- a/categories.php
+++ b/categories.php
@@ -1,6 +1,8 @@
- kaubuntu.re">
+
+
+ 'Accueil', 'url' => getBaseUrl()],
+ ['name' => 'Catégories', 'url' => getBaseUrl() . '/#categories'],
+ ['name' => $categoryName, 'url' => $categoryUrl]
+ ];
+ $breadcrumbJsonLd = generateBreadcrumbJsonLd($breadcrumbs);
+ outputJsonLd($breadcrumbJsonLd);
+ ?>
diff --git a/direct.php b/direct.php
index 0650baa..1e81c23 100644
--- a/direct.php
+++ b/direct.php
@@ -1,3 +1,14 @@
+
@@ -18,8 +29,8 @@
-
-
+
+
@@ -28,15 +39,44 @@
-
+
+
+
+
+
+ 'Accueil', 'url' => getBaseUrl()],
+ ['name' => 'Direct', 'url' => getBaseUrl() . '/direct.php']
+ ];
+ $breadcrumbJsonLd = generateBreadcrumbJsonLd($breadcrumbs);
+ outputJsonLd($breadcrumbJsonLd);
+ ?>
-
diff --git a/includes/structured-data.php b/includes/structured-data.php
new file mode 100644
index 0000000..fc1d7d8
--- /dev/null
+++ b/includes/structured-data.php
@@ -0,0 +1,319 @@
+ "https://schema.org",
+ "@type" => "WebSite",
+ "name" => "kaubuntu.re",
+ "description" => "Plateforme multimédia indépendante du parti panifricaniste et indépendantiste réunionnais Ka-Ubuntu",
+ "url" => $baseUrl,
+ "potentialAction" => [
+ "@type" => "SearchAction",
+ "target" => [
+ "@type" => "EntryPoint",
+ "urlTemplate" => $baseUrl . "/recherche.php?q={search_term_string}"
+ ],
+ "query-input" => "required name=search_term_string"
+ ],
+ "publisher" => [
+ "@type" => "Organization",
+ "name" => "Ka-Ubuntu",
+ "url" => $baseUrl,
+ "logo" => [
+ "@type" => "ImageObject",
+ "url" => $baseUrl . "/img/logo.png"
+ ]
+ ]
+ ];
+
+ return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+}
+
+/**
+ * Génère le JSON-LD pour un objet VideoObject
+ *
+ * @param array $videoData Données de la vidéo depuis l'API PeerTube
+ * @param array $video Données formatées de la vidéo
+ * @return string JSON-LD pour la vidéo
+ */
+function generateVideoObjectJsonLd($videoData, $video) {
+ $baseUrl = getBaseUrl();
+ $videoUrl = $baseUrl . "/video.php?id=" . $video['id'];
+
+ // Construire l'URL de la vignette
+ $thumbnailUrl = isset($videoData['thumbnailPath'])
+ ? PEERTUBE_URL . $videoData['thumbnailPath']
+ : $baseUrl . "/img/default-thumbnail.jpg";
+
+ // Formater la durée en format ISO 8601 (PT1H30M pour 1h30min)
+ $duration = formatDurationISO8601($video['duration'] ?? 0);
+
+ // Construire les données de base
+ $data = [
+ "@context" => "https://schema.org",
+ "@type" => "VideoObject",
+ "name" => $video['title'],
+ "description" => !empty($video['description'])
+ ? truncateText(strip_tags($video['description']), 300)
+ : "Regardez cette vidéo sur kaubuntu.re",
+ "url" => $videoUrl,
+ "thumbnailUrl" => $thumbnailUrl,
+ "uploadDate" => formatDateISO8601($video['date']),
+ "duration" => $duration,
+ "publisher" => [
+ "@type" => "Organization",
+ "name" => "kaubuntu.re",
+ "url" => $baseUrl,
+ "logo" => [
+ "@type" => "ImageObject",
+ "url" => $baseUrl . "/img/logo.png"
+ ]
+ ]
+ ];
+
+ // Ajouter les informations de la chaîne/créateur
+ if (!empty($video['channel'])) {
+ $data["creator"] = [
+ "@type" => "Person",
+ "name" => $video['channel'],
+ "url" => PEERTUBE_URL . "/c/" . ($videoData['channel']['name'] ?? '')
+ ];
+ }
+
+ // Ajouter les statistiques d'interaction
+ if (isset($video['views']) && $video['views'] > 0) {
+ $data["interactionStatistic"] = [
+ "@type" => "InteractionCounter",
+ "interactionType" => [
+ "@type" => "WatchAction"
+ ],
+ "userInteractionCount" => $video['views']
+ ];
+ }
+
+ // Ajouter les likes si disponibles
+ if (isset($video['likes']) && $video['likes'] > 0) {
+ if (!isset($data["interactionStatistic"])) {
+ $data["interactionStatistic"] = [];
+ } else {
+ // Convertir en array si c'était un seul élément
+ if (isset($data["interactionStatistic"]["@type"])) {
+ $data["interactionStatistic"] = [$data["interactionStatistic"]];
+ }
+ }
+
+ $data["interactionStatistic"][] = [
+ "@type" => "InteractionCounter",
+ "interactionType" => [
+ "@type" => "LikeAction"
+ ],
+ "userInteractionCount" => $video['likes']
+ ];
+ }
+
+ // Ajouter les tags/mots-clés si disponibles
+ if (!empty($video['tags'])) {
+ $data["keywords"] = implode(", ", $video['tags']);
+ }
+
+ // Ajouter les informations de licence si disponibles
+ if (isset($videoData['licence']) && !empty($videoData['licence'])) {
+ $licenceId = $videoData['licence']['id'];
+ $licenceMapping = [
+ 1 => "https://creativecommons.org/licenses/by/4.0/",
+ 2 => "https://creativecommons.org/licenses/by-sa/4.0/",
+ 3 => "https://creativecommons.org/licenses/by-nd/4.0/",
+ 4 => "https://creativecommons.org/licenses/by-nc/4.0/",
+ 5 => "https://creativecommons.org/licenses/by-nc-sa/4.0/",
+ 6 => "https://creativecommons.org/licenses/by-nc-nd/4.0/",
+ 7 => "https://creativecommons.org/publicdomain/zero/1.0/"
+ ];
+
+ if (isset($licenceMapping[$licenceId])) {
+ $data["license"] = $licenceMapping[$licenceId];
+ }
+ }
+
+ // Ajouter la catégorie si disponible
+ if (isset($videoData['category']) && !empty($videoData['category'])) {
+ $data["genre"] = $videoData['category']['label'] ?? 'Vidéo';
+ }
+
+ // Ajouter les dimensions si c'est un short (format portrait)
+ if (isset($video['aspectRatio']) && $video['aspectRatio'] <= 1) {
+ $data["videoFrameSize"] = "Portrait";
+ }
+
+ return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+}
+
+/**
+ * Génère le JSON-LD pour les fils d'Ariane (BreadcrumbList)
+ *
+ * @param array $breadcrumbs Tableau des fils d'Ariane [['name' => 'Nom', 'url' => 'URL']]
+ * @return string JSON-LD pour les fils d'Ariane
+ */
+function generateBreadcrumbJsonLd($breadcrumbs) {
+ $baseUrl = getBaseUrl();
+
+ $listItems = [];
+ foreach ($breadcrumbs as $index => $crumb) {
+ $listItems[] = [
+ "@type" => "ListItem",
+ "position" => $index + 1,
+ "name" => $crumb['name'],
+ "item" => $crumb['url']
+ ];
+ }
+
+ $data = [
+ "@context" => "https://schema.org",
+ "@type" => "BreadcrumbList",
+ "itemListElement" => $listItems
+ ];
+
+ return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+}
+
+/**
+ * Génère le JSON-LD pour une page de collection de vidéos
+ *
+ * @param string $name Nom de la collection
+ * @param string $description Description de la collection
+ * @param array $videos Tableau des vidéos
+ * @param string $url URL de la page de collection
+ * @return string JSON-LD pour la collection
+ */
+function generateVideoCollectionJsonLd($name, $description, $videos, $url) {
+ $baseUrl = getBaseUrl();
+
+ $videoItems = [];
+ foreach ($videos as $video) {
+ $videoItems[] = [
+ "@type" => "VideoObject",
+ "name" => $video['title'],
+ "url" => $baseUrl . "/video.php?id=" . $video['id'],
+ "thumbnailUrl" => $video['thumbnail'],
+ "uploadDate" => formatDateISO8601($video['date']),
+ "duration" => formatDurationISO8601($video['duration'] ?? 0)
+ ];
+ }
+
+ $data = [
+ "@context" => "https://schema.org",
+ "@type" => "CollectionPage",
+ "name" => $name,
+ "description" => $description,
+ "url" => $url,
+ "mainEntity" => [
+ "@type" => "ItemList",
+ "itemListElement" => $videoItems,
+ "numberOfItems" => count($videos)
+ ],
+ "publisher" => [
+ "@type" => "Organization",
+ "name" => "kaubuntu.re",
+ "url" => $baseUrl
+ ]
+ ];
+
+ return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+}
+
+/**
+ * Formate une durée en secondes au format ISO 8601 (PTnHnMnS)
+ *
+ * @param int $seconds Durée en secondes
+ * @return string Durée au format ISO 8601
+ */
+function formatDurationISO8601($seconds) {
+ $hours = floor($seconds / 3600);
+ $minutes = floor(($seconds % 3600) / 60);
+ $remainingSeconds = $seconds % 60;
+
+ $duration = 'PT';
+
+ if ($hours > 0) {
+ $duration .= $hours . 'H';
+ }
+ if ($minutes > 0) {
+ $duration .= $minutes . 'M';
+ }
+ if ($remainingSeconds > 0 || ($hours === 0 && $minutes === 0)) {
+ $duration .= $remainingSeconds . 'S';
+ }
+
+ return $duration;
+}
+
+/**
+ * Formate une date au format ISO 8601
+ *
+ * @param string $dateString Date à formater
+ * @return string Date au format ISO 8601
+ */
+function formatDateISO8601($dateString) {
+ try {
+ $date = new DateTime($dateString);
+ return $date->format('c'); // Format ISO 8601
+ } catch (Exception $e) {
+ // En cas d'erreur, retourner la date actuelle
+ return (new DateTime())->format('c');
+ }
+}
+
+/**
+ * Tronque un texte à une longueur donnée
+ *
+ * @param string $text Texte à tronquer
+ * @param int $length Longueur maximale
+ * @return string Texte tronqué
+ */
+function truncateText($text, $length = 200) {
+ if (strlen($text) <= $length) {
+ return $text;
+ }
+
+ $truncated = substr($text, 0, $length);
+ $lastSpace = strrpos($truncated, ' ');
+
+ if ($lastSpace !== false) {
+ $truncated = substr($truncated, 0, $lastSpace);
+ }
+
+ return $truncated . '...';
+}
+
+/**
+ * Obtient l'URL de base du site
+ *
+ * @return string URL de base
+ */
+function getBaseUrl() {
+ $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http';
+ $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
+ return $scheme . '://' . $host;
+}
+
+/**
+ * Génère et affiche un script JSON-LD
+ *
+ * @param string $jsonLd Données JSON-LD
+ */
+function outputJsonLd($jsonLd) {
+ echo '' . "\n";
+}
+
+?>
diff --git a/index.php b/index.php
index 6c0d74c..b27ccd5 100644
--- a/index.php
+++ b/index.php
@@ -1,6 +1,8 @@
@@ -46,6 +48,19 @@ setSecurityHeaders();
+
+
+ 'Accueil', 'url' => getBaseUrl()]
+ ];
+ $breadcrumbJsonLd = generateBreadcrumbJsonLd($breadcrumbs);
+ outputJsonLd($breadcrumbJsonLd);
+ ?>
diff --git a/recherche.php b/recherche.php
index 26c389e..127b2ec 100644
--- a/recherche.php
+++ b/recherche.php
@@ -1,6 +1,8 @@
0) {
">
+
+
+
+ 'Accueil', 'url' => getBaseUrl()],
+ ['name' => 'Recherche', 'url' => getBaseUrl() . '/recherche.php'],
+ ['name' => $searchTitle, 'url' => $searchUrl]
+ ];
+ $breadcrumbJsonLd = generateBreadcrumbJsonLd($breadcrumbs);
+ outputJsonLd($breadcrumbJsonLd);
+ ?>
+
diff --git a/video.php b/video.php
index 4912249..6fcd0c3 100644
--- a/video.php
+++ b/video.php
@@ -3,6 +3,8 @@
require_once 'includes/config.php';
// Inclure le convertisseur Markdown
require_once 'includes/lib/markdown.php';
+// Inclure les fonctions de données structurées
+require_once 'includes/structured-data.php';
// Appliquer les en-têtes de sécurité
setSecurityHeaders();
@@ -139,6 +141,22 @@ if (empty($videoData) || isset($videoData['error'])) {
+
+
+
+ 'Accueil', 'url' => getBaseUrl()],
+ ['name' => $video['title'], 'url' => getBaseUrl() . '/video.php?id=' . $video['id']]
+ ];
+ $breadcrumbJsonLd = generateBreadcrumbJsonLd($breadcrumbs);
+ outputJsonLd($breadcrumbJsonLd);
+ ?>
+