diff --git a/includes/config.php b/includes/config.php index 9e4a6a9..2ec8a30 100644 --- a/includes/config.php +++ b/includes/config.php @@ -25,13 +25,13 @@ define('PEERTUBE_CATEGORIES', $peertube_categories); /** * Initialise et récupère les catégories depuis l'API PeerTube - * + * * @return array Liste des catégories */ function initCategories() { // Récupérer la liste des catégories depuis l'API $categories = callPeerTubeApi('videos/categories'); - + // Tableau de correspondance pour traduire les catégories en français $translations = [ 'Music' => 'Musique', @@ -53,7 +53,7 @@ function initCategories() { 'Kids' => 'Enfants', 'Food' => 'Cuisine', ]; - + // Si une constante PRIORITY_CATEGORIES est définie, utiliser ces traductions if (defined('PRIORITY_CATEGORIES')) { $priorityCategories = PRIORITY_CATEGORIES; @@ -65,20 +65,20 @@ function initCategories() { } } } - + $result = []; foreach ($categories as $key => $name) { // Utiliser la traduction si disponible, sinon garder le nom original $translatedName = isset($translations[$name]) ? $translations[$name] : $name; $result[$key] = $translatedName; } - + return $result; } /** * Fonction utilitaire pour appeler l'API PeerTube - * + * * @param string $endpoint Point de terminaison de l'API * @param array $params Paramètres optionnels pour la requête * @return array Données retournées par l'API @@ -89,21 +89,21 @@ function callPeerTubeApi($endpoint, $params = []) { error_log('SECURITY: Invalid PeerTube URL detected: ' . PEERTUBE_URL); return []; } - + // Nettoyer et valider l'endpoint $endpoint = ltrim($endpoint, '/'); if (!isValidApiEndpoint($endpoint)) { error_log('SECURITY: Invalid API endpoint detected: ' . $endpoint); return []; } - + $url = PEERTUBE_URL . '/api/v1/' . $endpoint; - + // Ajouter les paramètres à l'URL if (!empty($params)) { $url .= '?' . http_build_query($params); } - + // Initialiser cURL avec options de sécurité $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); @@ -115,40 +115,40 @@ function callPeerTubeApi($endpoint, $params = []) { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_USERAGENT, 'KaubuntuRe/1.0'); - + // Ajouter la clé API si définie if (defined('API_KEY') && !empty(API_KEY)) { curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: ApiKey ' . API_KEY ]); } - + // Exécuter la requête $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); - + // Traiter la réponse if ($response === false || !empty($error)) { error_log('PeerTube API error: ' . $error); return []; } - + if ($httpCode < 200 || $httpCode >= 300) { error_log('PeerTube API HTTP error: ' . $httpCode); return []; } - + // Décoder la réponse JSON $data = json_decode($response, true); - + return $data ?: []; } /** * Valide l'URL PeerTube pour prévenir les attaques SSRF - * + * * @param string $url URL à valider * @return bool True si l'URL est valide et sûre */ @@ -158,12 +158,12 @@ function isValidPeerTubeUrl($url) { if (!$parsed || !isset($parsed['scheme']) || !isset($parsed['host'])) { return false; } - + // Autoriser uniquement HTTPS (ou HTTP en développement) if (!in_array($parsed['scheme'], ['https', 'http'])) { return false; } - + // Bloquer les adresses IP privées et locales $host = $parsed['host']; if (filter_var($host, FILTER_VALIDATE_IP)) { @@ -171,19 +171,19 @@ function isValidPeerTubeUrl($url) { return false; } } - + // Bloquer localhost et autres domaines dangereux $blockedHosts = ['localhost', '127.0.0.1', '::1', '0.0.0.0', 'metadata.google.internal']; if (in_array(strtolower($host), $blockedHosts)) { return false; } - + return true; } /** * Valide l'endpoint API pour prévenir l'injection de chemins - * + * * @param string $endpoint Endpoint à valider * @return bool True si l'endpoint est valide */ @@ -192,12 +192,12 @@ function isValidApiEndpoint($endpoint) { if (strpos($endpoint, '..') !== false || strpos($endpoint, '//') !== false) { return false; } - + // Autoriser uniquement les caractères alphanumériques, tirets, underscores et slashes if (!preg_match('/^[a-zA-Z0-9\/_-]+$/', $endpoint)) { return false; } - + // Liste blanche des endpoints autorisés $allowedEndpoints = [ 'videos', @@ -208,7 +208,7 @@ function isValidApiEndpoint($endpoint) { 'accounts', 'accounts/.*/videos' // Pour les vidéos d'un compte spécifique ]; - + foreach ($allowedEndpoints as $pattern) { // Remplacer les .* par des marqueurs temporaires $tempPattern = str_replace('.*', '__WILDCARD__', $pattern); @@ -216,24 +216,24 @@ function isValidApiEndpoint($endpoint) { $escapedPattern = preg_quote($tempPattern, '/'); // Remettre les wildcards en place $regexPattern = str_replace('__WILDCARD__', '.*', $escapedPattern); - + if (preg_match('/^' . $regexPattern . '$/', $endpoint)) { return true; } } - + return false; } /** * Récupère les catégories depuis l'API PeerTube - * + * * @return array Liste des catégories */ function getCategories() { // Utiliser les catégories déjà récupérées $categories = PEERTUBE_CATEGORIES; - + $result = []; foreach ($categories as $key => $name) { $result[] = [ @@ -241,13 +241,13 @@ function getCategories() { 'name' => $name ]; } - + return $result; } /** * Récupère les vidéos récentes depuis l'API PeerTube - * + * * @param int $count Nombre de vidéos à récupérer * @return array Liste des vidéos récentes */ @@ -258,13 +258,13 @@ function getRecentVideos($count = RECENT_VIDEOS_COUNT) { 'count' => $count, 'isLocal' => true ]); - + return formatVideosData($data['data'] ?? []); } /** * Récupère les vidéos tendances depuis l'API PeerTube - * + * * @param int $count Nombre de vidéos à récupérer * @return array Liste des vidéos tendances */ @@ -275,13 +275,13 @@ function getTrendingVideos($count = TRENDING_VIDEOS_COUNT) { 'count' => $count, 'isLocal' => true ]); - + return formatVideosData($data['data'] ?? []); } /** * Récupère les vidéos avec un tag spécifique depuis l'API PeerTube - * + * * @param string $tag Tag à filtrer * @param int $count Nombre de vidéos à récupérer * @return array Liste des vidéos @@ -293,7 +293,7 @@ function getVideosByTag($tag, $count) { 'count' => $count, 'isLocal' => true ]); - + return formatVideosData($data['data'] ?? []); } @@ -311,10 +311,10 @@ function getShorts($count = SHORTS_COUNT) { 'count' => SHORTS_COUNT_SEARCH, 'isLocal' => true ]); - + // Formater les données $allVideos = formatVideosData($data['data'] ?? []); - + // Filtrer pour ne garder que les vidéos de moins de 2 minutes (120 secondes) et en mode portrait $shortVideos = array_filter($allVideos, function($video) { // Vérifier la durée (moins de 2 minutes) @@ -322,10 +322,10 @@ function getShorts($count = SHORTS_COUNT) { // Vérifier le ratio (mode portrait) $ratioOk = isset($video['aspectRatio']) && $video['aspectRatio'] <= 1; - + return $durationOk && $ratioOk; }); - + // Limiter au nombre demandé return array_slice($shortVideos, 0, $count); } @@ -343,7 +343,7 @@ function getIndependenceVideos($count = INDEPENDENCE_VIDEOS_COUNT) { /** * Vérifie s'il y a un direct en cours du compte LIVE_ACCOUNT_NAME sur l'instance PeerTube - * + * * @return array|null Informations sur le direct en cours ou null si aucun direct */ function getLiveStream() { @@ -355,44 +355,44 @@ function getLiveStream() { 'isLive' => true, // Filtrer uniquement les lives 'sort' => '-publishedAt' // Les plus récents en premier ]); - + // Vérifier si on a des résultats if (empty($data['data']) || count($data['data']) === 0) { return null; } - + // Formater les données du live $liveData = formatVideosData($data['data']); - + // Filtrer pour ne garder que les lives en cours $activeLives = array_filter($liveData, function($video) { return isset($video['isLive']) && $video['isLive'] === true; }); - + // Retourner le premier live trouvé return !empty($activeLives) ? reset($activeLives) : null; } /** * Formate les données brutes des vidéos venant de l'API - * + * * @param array $videosData Données brutes des vidéos * @return array Données formatées */ function formatVideosData($videosData) { $videos = []; - + foreach ($videosData as $video) { // Récupérer la vignette (thumbnail) - $thumbnail = isset($video['previewPath']) - ? PEERTUBE_URL . $video['previewPath'] + $thumbnail = isset($video['previewPath']) + ? PEERTUBE_URL . $video['previewPath'] : 'img/default-thumbnail.jpg'; - + // Récupérer l'avatar de la chaîne $channelAvatar = isset($video['channel']['avatars'][0]['path']) && isset($video['channel']['avatars'][0]['path']) ? PEERTUBE_URL . $video['channel']['avatars'][0]['path'] : 'img/default-avatar.png'; - + // Formater les données $videos[] = [ 'id' => $video['uuid'], @@ -409,7 +409,7 @@ function formatVideosData($videosData) { 'isLive' => isset($video['isLive']) ? $video['isLive'] : false ]; } - + return $videos; } @@ -418,7 +418,7 @@ function formatDuration($seconds) { $hours = floor($seconds / 3600); $minutes = floor(($seconds % 3600) / 60); $remainingSeconds = $seconds % 60; - + if ($hours > 0) { return sprintf('%d:%02d:%02d', $hours, $minutes, $remainingSeconds); } else { @@ -440,7 +440,7 @@ function formatDate($dateString) { $date = new DateTime($dateString); $now = new DateTime(); $interval = $now->diff($date); - + if ($interval->days == 0) { return 'Aujourd\'hui'; } elseif ($interval->days == 1) { @@ -474,7 +474,7 @@ function getVideosByCategory($categoryId, $count = CATEGORY_VIDEOS_COUNT) { 'sort' => '-publishedAt', // Les plus récentes d'abord 'isLocal' => true ]); - + return formatVideosData($data['data'] ?? []); } @@ -487,11 +487,11 @@ function getDisplayCategories() { $categories = []; $priorityCategories = PRIORITY_CATEGORIES; $allCategories = PEERTUBE_CATEGORIES; - + // Ajouter uniquement les catégories prioritaires dans l'ordre défini foreach ($priorityCategories as $catId => $categoryName) { $videos = getVideosByCategory($catId); - + // N'ajouter que les catégories qui ont des vidéos if (!empty($videos)) { $categories[] = [ @@ -501,7 +501,7 @@ function getDisplayCategories() { ]; } } - + return $categories; } @@ -513,11 +513,11 @@ function getDisplayCategories() { function getVideoComments($videoId) { $endpoint = "videos/{$videoId}/comment-threads"; $response = callPeerTubeApi($endpoint); - + if (!$response || !isset($response['data'])) { return []; } - + return $response['data']; } @@ -529,9 +529,9 @@ function getVideoComments($videoId) { function getVideoDownloadOptions($videoId) { // Récupérer les informations complètes de la vidéo $videoData = callPeerTubeApi('videos/' . $videoId); - + $downloadOptions = []; - + // Ajouter les fichiers directs s'ils existent if (isset($videoData['files']) && !empty($videoData['files'])) { foreach ($videoData['files'] as $file) { @@ -545,7 +545,7 @@ function getVideoDownloadOptions($videoId) { } } } - + // Ajouter les playlists de streaming s'ils existent if (isset($videoData['streamingPlaylists']) && !empty($videoData['streamingPlaylists'])) { foreach ($videoData['streamingPlaylists'] as $playlist) { @@ -563,7 +563,7 @@ function getVideoDownloadOptions($videoId) { } } } - + return $downloadOptions; } @@ -574,19 +574,19 @@ function getVideoDownloadOptions($videoId) { */ function formatFileSize($bytes) { $units = ['B', 'KB', 'MB', 'GB', 'TB']; - + $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); - + $bytes /= (1 << (10 * $pow)); - + return round($bytes, 2) . ' ' . $units[$pow]; } /** * Recherche des vidéos selon un critère - * + * * @param string $query Terme de recherche * @param int $count Nombre de vidéos à récupérer * @param int $start Index de départ pour la pagination @@ -596,13 +596,13 @@ function searchVideos($query, $count = COUNT_VIDEO_SEARCH, $start = 0) { if (empty($query)) { return []; } - + // Vérifier si la recherche concerne un tag (commence par #) $isTagSearch = false; if (substr($query, 0, 1) === '#') { $isTagSearch = true; $tag = substr($query, 1); // Enlever le # du début - + // Récupérer les vidéos avec ce tag via l'API $data = callPeerTubeApi('videos', [ 'tagsOneOf' => $tag, @@ -611,10 +611,10 @@ function searchVideos($query, $count = COUNT_VIDEO_SEARCH, $start = 0) { 'isLocal' => true, // Uniquement les vidéos locales 'sort' => '-publishedAt' // Les plus récentes d'abord ]); - + return formatVideosData($data['data'] ?? []); } - + // Recherche normale (pas un tag) $data = callPeerTubeApi('search/videos', [ 'search' => $query, @@ -623,7 +623,6 @@ function searchVideos($query, $count = COUNT_VIDEO_SEARCH, $start = 0) { 'isLocal' => true, // Uniquement les vidéos locales 'sort' => '-publishedAt' // Les plus récentes d'abord ]); - return formatVideosData($data['data'] ?? []); } -?> \ No newline at end of file +?> diff --git a/index.php b/index.php index 701264f..b26992b 100644 --- a/index.php +++ b/index.php @@ -14,7 +14,7 @@ setSecurityHeaders(); - + @@ -32,10 +32,10 @@ setSecurityHeaders();