diff --git a/ajax/load-more-videos.php b/ajax/load-more-videos.php new file mode 100644 index 0000000..b10dd84 --- /dev/null +++ b/ajax/load-more-videos.php @@ -0,0 +1,92 @@ + 'Accès non autorisé']); + exit; +} + +// Récupérer les paramètres +$type = isset($_GET['type']) ? $_GET['type'] : ''; +$page = isset($_GET['page']) ? intval($_GET['page']) : 1; + +// Vérifier que le type est valide +if (!in_array($type, ['recent', 'trending', 'independence'])) { + http_response_code(400); // Requête incorrecte + echo json_encode(['error' => 'Type de vidéos non valide']); + exit; +} + +// Récupérer les vidéos en fonction du type +$videos = []; +$offset = $page * LOAD_MORE_COUNT; + +switch ($type) { + case 'recent': + // Récupérer les vidéos récentes + $data = callPeerTubeApi('videos', [ + 'sort' => '-publishedAt', + 'count' => LOAD_MORE_COUNT, + 'start' => $offset, + 'isLocal' => true + ]); + $videos = formatVideosData($data['data'] ?? []); + break; + + case 'trending': + // Récupérer les vidéos tendances + $data = callPeerTubeApi('videos', [ + 'sort' => '-views', + 'count' => LOAD_MORE_COUNT, + 'start' => $offset, + 'isLocal' => true + ]); + $videos = formatVideosData($data['data'] ?? []); + break; + + case 'independence': + // Récupérer les vidéos sur l'indépendance + $data = callPeerTubeApi('videos', [ + 'tagsOneOf' => TAG_INDEPENDENCE, + 'count' => LOAD_MORE_COUNT, + 'start' => $offset, + 'isLocal' => true + ]); + $videos = formatVideosData($data['data'] ?? []); + break; +} + +// Préparer la réponse HTML +$html = ''; + +foreach ($videos as $video) { + $html .= '
'; + $html .= '
'; + $html .= ' ' . htmlspecialchars($video['title']) . ''; + $html .= '
'; + $html .= ' '; + $html .= '
'; + $html .= '
' . formatDuration($video['duration']) . '
'; + $html .= '
'; + $html .= '
'; + $html .= '

' . htmlspecialchars($video['title']) . '

'; + $html .= '
' . htmlspecialchars($video['channel']) . '
'; + $html .= ' '; + $html .= '
'; + $html .= '
'; +} + +// Retourner la réponse +echo json_encode([ + 'success' => true, + 'html' => $html, + 'page' => $page, + 'hasMore' => count($videos) >= LOAD_MORE_COUNT +]); +?> \ No newline at end of file diff --git a/css/styles.css b/css/styles.css index f4fbdb7..128d58c 100644 --- a/css/styles.css +++ b/css/styles.css @@ -325,11 +325,12 @@ img { /* Video Sections */ .video-section { margin-bottom: 40px; + position: relative; } .video-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + grid-template-columns: repeat(3, 1fr); gap: 20px; } @@ -340,6 +341,9 @@ img { box-shadow: var(--card-shadow); transition: transform 0.3s; cursor: pointer; + height: 100%; + display: flex; + flex-direction: column; } .video-card:hover { @@ -349,6 +353,7 @@ img { .video-thumbnail { position: relative; padding-top: 56.25%; /* 16:9 aspect ratio */ + overflow: hidden; } .video-thumbnail img { @@ -377,6 +382,9 @@ img { .video-info { padding: 15px; + flex-grow: 1; + display: flex; + flex-direction: column; } .video-title { @@ -388,6 +396,16 @@ img { -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; + flex-grow: 1; +} + +.video-channel { + font-size: 14px; + color: #555; + margin-bottom: 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .video-metadata { @@ -396,6 +414,31 @@ img { justify-content: space-between; font-size: 12px; color: #666; + margin-top: auto; +} + +.video-metadata i { + margin-right: 4px; + font-size: 13px; + color: var(--primary-red); +} + +.video-views, .video-date { + display: flex; + align-items: center; + font-size: 12px; +} + +.video-duration { + position: absolute; + bottom: 10px; + right: 10px; + background-color: rgba(0, 0, 0, 0.7); + color: white; + padding: 3px 6px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; } .video-tag { @@ -407,24 +450,23 @@ img { margin-right: 5px; } -.video-date, .video-views { - font-size: 12px; -} - /* Carousel */ .carousel { position: relative; overflow: hidden; + margin: 15px 0 30px; + padding: 10px 0; } .carousel-container { display: flex; transition: transform 0.5s ease; + padding-bottom: 10px; } .carousel-item { flex: 0 0 auto; - width: 280px; + width: 220px; margin-right: 20px; } @@ -441,28 +483,43 @@ img { background-color: #ccc; margin: 0 5px; cursor: pointer; + transition: all 0.2s ease; } .carousel-dot.active { background-color: var(--primary-red); + transform: scale(1.2); } /* View More Button */ .view-more { display: block; width: max-content; - margin: 20px auto; - padding: 10px 25px; - background-color: var(--search-bg); + margin: 25px auto 5px; + padding: 12px 30px; + background-color: var(--primary-red); + color: white; border: none; border-radius: 4px; font-size: 16px; + font-weight: 500; cursor: pointer; - transition: background-color 0.2s; + transition: all 0.2s; + text-transform: uppercase; + letter-spacing: 1px; } .view-more:hover { - background-color: #d0d0d0; + background-color: #cc0000; + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0,0,0,0.1); +} + +.view-more:disabled { + background-color: #999; + cursor: not-allowed; + transform: none; + box-shadow: none; } /* Info Section */ @@ -689,13 +746,19 @@ img { @media (max-width: 992px) { .video-grid { - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + grid-template-columns: repeat(2, 1fr); + gap: 15px; + } + + .video-title { + font-size: 15px; } } @media (max-width: 576px) { .video-grid { grid-template-columns: 1fr; + gap: 15px; } .section-title { @@ -714,4 +777,8 @@ img { .play-button i { font-size: 24px; } + + .video-title { + font-size: 16px; + } } \ No newline at end of file diff --git a/css/video-page.css b/css/video-page.css index 8387765..025edaa 100644 --- a/css/video-page.css +++ b/css/video-page.css @@ -10,6 +10,9 @@ .video-player-container { grid-column: 1 / -1; grid-row: 1; + max-width: 900px; + margin: 0 auto; + width: 100%; } .video-player { @@ -54,6 +57,7 @@ .video-info { display: flex; + flex-direction: row; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; @@ -66,6 +70,8 @@ flex-direction: row; align-items: center; gap: 1rem; + margin-bottom: 0; + flex-wrap: nowrap; } .video-views, .video-date { @@ -74,29 +80,41 @@ display: flex; align-items: center; gap: 5px; + height: 36px; + white-space: nowrap; } .video-views i, .video-date i { font-size: 0.9375rem; - color: #555; + color: var(--primary-red); + display: flex; + align-items: center; + justify-content: center; + width: 20px; } .video-actions { display: flex; + flex-direction: row; gap: 1.5rem; + align-items: center; + flex-wrap: nowrap; } .action-button { display: flex; align-items: center; + justify-content: center; background: none; border: none; cursor: pointer; gap: 0.5rem; font-size: 0.875rem; - padding: 0.5rem; + padding: 0.5rem 0.75rem; border-radius: 4px; transition: background-color 0.2s; + height: 36px; + min-width: 80px; } .action-button:hover { @@ -108,6 +126,12 @@ color: #444; } +.action-button span { + display: inline-flex; + align-items: center; + height: 100%; +} + .video-secondary-info { margin-bottom: 30px; } @@ -468,13 +492,15 @@ grid-template-columns: repeat(2, 1fr); gap: 15px; } + + .video-player-container { + max-width: 100%; + } } @media (max-width: 768px) { .video-player-container { - margin-left: -20px; - margin-right: -20px; - width: calc(100% + 40px); + margin-bottom: 15px; } .video-player { @@ -487,13 +513,23 @@ .video-info { flex-direction: column; - gap: 1rem; align-items: flex-start; } - + + .video-metadata { + width: 100%; + margin-bottom: 10px; + } + .video-actions { width: 100%; justify-content: space-between; + flex-wrap: wrap; + } + + .action-button { + padding: 0.5rem; + min-width: 70px; } .suggestion-list { @@ -506,30 +542,36 @@ display: flex; flex-direction: column; gap: 20px; + margin-top: 10px; } .video-player-container { - width: 100vw; - margin-left: -20px; - margin-right: -20px; + order: 1; + margin-bottom: 0; } .video-content, .video-suggestions { + order: 2; width: 100%; } .video-title { - font-size: 1.2rem; + font-size: 1.25rem; } .video-actions { - flex-wrap: wrap; - gap: 0.5rem; + justify-content: space-around; } .action-button { - padding: 0.3rem; + font-size: 0.75rem; + min-width: 60px; + padding: 0.5rem 0.25rem; + } + + .video-views, .video-date { + font-size: 0.8125rem; } .comment { @@ -537,29 +579,20 @@ } .comment-avatar { - display: none; + margin-bottom: 10px; } .suggested-video { - display: flex; - flex-direction: row; + grid-template-columns: 120px 1fr; } .suggested-video-thumbnail { width: 120px; - min-width: 120px; - height: 68px; - padding-top: 0; - margin-right: 10px; - } - - .suggested-video-info { - padding: 5px; - width: calc(100% - 120px); + height: 67px; } .suggested-video-title { - -webkit-line-clamp: 2; + font-size: 0.875rem; } } diff --git a/includes/config.php b/includes/config.php index a26c971..f371059 100644 --- a/includes/config.php +++ b/includes/config.php @@ -14,11 +14,12 @@ define('API_KEY', ''); // Laissez vide si pas nécessaire // Pagination et affichage define('VIDEOS_PER_PAGE', 12); -define('FEATURED_VIDEOS_COUNT', 5); -define('RECENT_VIDEOS_COUNT', 5); -define('SHORTS_COUNT', 5); -define('TRENDING_VIDEOS_COUNT', 5); -define('INDEPENDENCE_VIDEOS_COUNT', 5); +define('FEATURED_VIDEOS_COUNT',6); +define('RECENT_VIDEOS_COUNT', 6); +define('SHORTS_COUNT', 6); +define('TRENDING_VIDEOS_COUNT', 6); +define('INDEPENDENCE_VIDEOS_COUNT', 6); +define('LOAD_MORE_COUNT', 6); // Informations du site define('SITE_NAME', 'Kaubuntu.re'); diff --git a/index.php b/index.php index cfd5eca..7e90cfe 100644 --- a/index.php +++ b/index.php @@ -205,8 +205,8 @@

- vues - + vues +
@@ -251,8 +251,8 @@

- vues - + vues +
@@ -297,8 +297,8 @@

- vues - + vues +
diff --git a/js/main.js b/js/main.js index cdbb4da..fe52795 100644 --- a/js/main.js +++ b/js/main.js @@ -65,58 +65,134 @@ document.addEventListener('DOMContentLoaded', function() { }); // Gestion des clics sur les vidéos - const videoCards = document.querySelectorAll('.video-card'); - - videoCards.forEach(card => { - card.addEventListener('click', function() { - const videoId = this.dataset.videoId; - if (videoId) { - window.location.href = 'video.php?id=' + videoId; - } - }); - }); - - // Lazy loading des images - if ('IntersectionObserver' in window) { - const imageObserver = new IntersectionObserver((entries, observer) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - const img = entry.target; - const src = img.getAttribute('data-src'); - if (src) { - img.src = src; - img.removeAttribute('data-src'); - } - observer.unobserve(img); - } - }); - }); + function initVideoCardClicks() { + const videoCards = document.querySelectorAll('.video-card'); - document.querySelectorAll('img[data-src]').forEach(img => { - imageObserver.observe(img); - }); - } else { - // Fallback pour les navigateurs qui ne supportent pas IntersectionObserver - document.querySelectorAll('img[data-src]').forEach(img => { - const src = img.getAttribute('data-src'); - if (src) { - img.src = src; - img.removeAttribute('data-src'); + videoCards.forEach(card => { + if (!card.hasAttribute('data-click-initialized')) { + card.setAttribute('data-click-initialized', 'true'); + card.addEventListener('click', function() { + const videoId = this.dataset.videoId; + if (videoId) { + window.location.href = 'video.php?id=' + videoId; + } + }); } }); } + // Initialiser les clics sur les vidéos existantes + initVideoCardClicks(); + + // Lazy loading des images + function initLazyLoading() { + if ('IntersectionObserver' in window) { + const imageObserver = new IntersectionObserver((entries, observer) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const img = entry.target; + const src = img.getAttribute('data-src'); + if (src) { + img.src = src; + img.removeAttribute('data-src'); + } + observer.unobserve(img); + } + }); + }); + + document.querySelectorAll('img[data-src]').forEach(img => { + imageObserver.observe(img); + }); + } else { + // Fallback pour les navigateurs qui ne supportent pas IntersectionObserver + document.querySelectorAll('img[data-src]').forEach(img => { + const src = img.getAttribute('data-src'); + if (src) { + img.src = src; + img.removeAttribute('data-src'); + } + }); + } + } + + // Initialiser le lazy loading + initLazyLoading(); + // Gestion des boutons "Voir plus" const viewMoreButtons = document.querySelectorAll('.view-more'); viewMoreButtons.forEach(button => { + // Déterminer le type de vidéos à charger + const section = button.closest('.video-section'); + const sectionTitle = section.querySelector('.section-title').textContent.trim().toLowerCase(); + const videoGrid = section.querySelector('.video-grid'); + let videoType = ''; + + if (sectionTitle.includes('dernières')) { + videoType = 'recent'; + } else if (sectionTitle.includes('tendances')) { + videoType = 'trending'; + } else if (sectionTitle.includes('indépendance')) { + videoType = 'independence'; + } + + // Stocker le numéro de page actuel + button.dataset.page = '1'; + button.addEventListener('click', function() { - // Dans un vrai projet, cela chargerait plus de contenu via AJAX - // Pour cet exemple, on simule juste un clic - button.innerText = 'Chargement...'; - setTimeout(() => { - button.innerText = 'Voir plus'; - }, 1000); + const page = parseInt(this.dataset.page); + + // Changer le texte du bouton pendant le chargement + button.textContent = 'Chargement...'; + button.disabled = true; + + // Faire la requête AJAX + fetch(`ajax/load-more-videos.php?type=${videoType}&page=${page}`, { + headers: { + 'X-Requested-With': 'XMLHttpRequest' + } + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + // Ajouter les nouvelles vidéos à la grille + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = data.html; + + // Ajouter chaque vidéo à la grille + while (tempDiv.firstChild) { + videoGrid.appendChild(tempDiv.firstChild); + } + + // Mettre à jour le numéro de page + this.dataset.page = data.page + 1; + + // Réinitialiser le texte du bouton + button.textContent = 'Voir plus'; + button.disabled = false; + + // Si plus de vidéos à charger, masquer le bouton + if (!data.hasMore) { + button.style.display = 'none'; + } + + // Initialiser les clics sur les nouvelles vidéos + initVideoCardClicks(); + // Initialiser le lazy loading pour les nouvelles images + initLazyLoading(); + } else { + // En cas d'erreur, afficher un message et réactiver le bouton + console.error('Erreur lors du chargement des vidéos:', data.error); + button.textContent = 'Voir plus'; + button.disabled = false; + } + }) + .catch(error => { + console.error('Erreur lors de la requête AJAX:', error); + button.textContent = 'Voir plus'; + button.disabled = false; + }); }); }); }); \ No newline at end of file