feat: permit to play Castopod files

This commit is contained in:
2025-10-17 21:28:14 +04:00
parent b3ee9d9837
commit 6ac195b089
4 changed files with 237 additions and 148 deletions
+61 -4
View File
@@ -63,22 +63,28 @@
display: flex; display: flex;
padding: 8px 12px; padding: 8px 12px;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
text-decoration: none;
color: inherit; color: inherit;
transition: background-color 0.2s ease; transition: background-color 0.2s ease;
gap: 8px; gap: 8px;
position: relative;
} }
.castopod-episode-item:hover { .castopod-episode-item:hover {
background: var(--hover-bg); background: var(--hover-bg);
text-decoration: none;
color: inherit;
} }
.castopod-episode-item:last-child { .castopod-episode-item:last-child {
border-bottom: none; border-bottom: none;
} }
.castopod-episode-item.playing {
background: var(--hover-bg);
}
.castopod-episode-item.playing .castopod-episode-title {
color: var(--primary-red);
}
.castopod-episode-thumb { .castopod-episode-thumb {
width: 60px; width: 60px;
height: 60px; height: 60px;
@@ -91,6 +97,14 @@
justify-content: center; justify-content: center;
color: var(--text-secondary); color: var(--text-secondary);
position: relative; position: relative;
border: none;
padding: 0;
cursor: pointer;
transition: transform 0.2s ease;
}
.castopod-episode-thumb:hover {
transform: scale(1.05);
} }
.castopod-episode-thumb img { .castopod-episode-thumb img {
@@ -99,7 +113,8 @@
object-fit: cover; object-fit: cover;
} }
.castopod-play-icon { .castopod-play-icon,
.castopod-pause-icon {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
@@ -116,6 +131,18 @@
opacity: 1; opacity: 1;
} }
.castopod-episode-item.playing .castopod-play-icon {
opacity: 0;
}
.castopod-episode-item.playing .castopod-pause-icon {
opacity: 1;
}
.castopod-episode-item.playing:hover .castopod-pause-icon {
opacity: 1;
}
.castopod-episode-info { .castopod-episode-info {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
@@ -171,6 +198,36 @@
font-size: 0.9rem; font-size: 0.9rem;
} }
/* Lien externe vers Castopod */
.castopod-external-link {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
flex-shrink: 0;
color: var(--text-secondary);
text-decoration: none;
border-radius: 50%;
transition: all 0.2s ease;
margin-left: auto;
}
.castopod-external-link:hover {
background: var(--tag-bg);
color: var(--primary-red);
transform: scale(1.1);
}
.castopod-external-link i {
font-size: 0.85rem;
}
/* Masquer l'élément audio */
.castopod-episode-item audio {
display: none;
}
/* Ajustement du layout selon les contenus présents */ /* Ajustement du layout selon les contenus présents */
/* Ordre dans le HTML : 1. Castopod, 2. Mastodon, 3. WordPress */ /* Ordre dans le HTML : 1. Castopod, 2. Mastodon, 3. WordPress */
+28 -11
View File
@@ -135,15 +135,15 @@ setSecurityHeaders();
if (empty($castopodEpisodes)) { if (empty($castopodEpisodes)) {
echo '<div class="castopod-no-episodes">Aucun épisode disponible</div>'; echo '<div class="castopod-no-episodes">Aucun épisode disponible</div>';
} else { } else {
foreach ($castopodEpisodes as $episode): foreach ($castopodEpisodes as $index => $episode):
?> ?>
<a href="<?php echo htmlspecialchars($episode['link']); ?>" <div class="castopod-episode-item"
class="castopod-episode-item" data-episode-id="<?php echo $index; ?>"
target="_blank" data-audio-url="<?php echo htmlspecialchars($episode['audioUrl']); ?>">
rel="noopener noreferrer"
title="<?php echo htmlspecialchars($episode['title']); ?>">
<div class="castopod-episode-thumb"> <button class="castopod-episode-thumb castopod-play-btn"
type="button"
aria-label="Lire <?php echo htmlspecialchars($episode['title']); ?>">
<?php if (!empty($episode['image'])): ?> <?php if (!empty($episode['image'])): ?>
<img src="<?php echo htmlspecialchars($episode['image']); ?>" <img src="<?php echo htmlspecialchars($episode['image']); ?>"
alt="<?php echo htmlspecialchars($episode['title']); ?>" alt="<?php echo htmlspecialchars($episode['title']); ?>"
@@ -151,10 +151,13 @@ setSecurityHeaders();
<div class="castopod-play-icon"> <div class="castopod-play-icon">
<i class="fas fa-play-circle"></i> <i class="fas fa-play-circle"></i>
</div> </div>
<div class="castopod-pause-icon">
<i class="fas fa-pause-circle"></i>
</div>
<?php else: ?> <?php else: ?>
<i class="fas fa-podcast"></i> <i class="fas fa-podcast"></i>
<?php endif; ?> <?php endif; ?>
</div> </button>
<div class="castopod-episode-info"> <div class="castopod-episode-info">
<h3 class="castopod-episode-title"> <h3 class="castopod-episode-title">
@@ -175,7 +178,21 @@ setSecurityHeaders();
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
<a href="<?php echo htmlspecialchars($episode['link']); ?>"
class="castopod-external-link"
target="_blank"
rel="noopener noreferrer"
title="Voir sur Castopod"
aria-label="Voir <?php echo htmlspecialchars($episode['title']); ?> sur Castopod">
<i class="fas fa-external-link-alt"></i>
</a> </a>
<!-- Élément audio caché -->
<audio preload="none">
<source src="<?php echo htmlspecialchars($episode['audioUrl']); ?>" type="audio/mpeg">
</audio>
</div>
<?php <?php
endforeach; endforeach;
} }
@@ -722,9 +739,9 @@ setSecurityHeaders();
}); });
</script> </script>
<?php if (defined('FUNKWHALE_ENABLED') && FUNKWHALE_ENABLED): ?> <?php if ((defined('FUNKWHALE_ENABLED') && FUNKWHALE_ENABLED) || (defined('CASTOPOD_ENABLED') && CASTOPOD_ENABLED)): ?>
<!-- Lecteur audio Funkwhale --> <!-- Lecteur audio unifié pour Funkwhale et Castopod -->
<script src="js/funkwhale-player.js?v=<?php echo filemtime('js/funkwhale-player.js'); ?>"></script> <script src="js/audio-player.js?v=<?php echo filemtime('js/audio-player.js'); ?>"></script>
<?php endif; ?> <?php endif; ?>
</body> </body>
</html> </html>
+147
View File
@@ -0,0 +1,147 @@
/**
* Lecteur audio intégré universel pour Funkwhale et Castopod
* Permet de lire les morceaux/épisodes directement dans la page
*/
(function() {
'use strict';
let currentlyPlayingItem = null;
let currentAudioElement = null;
/**
* Arrête l'élément en cours de lecture
*/
function stopCurrentItem() {
if (currentAudioElement) {
currentAudioElement.pause();
currentAudioElement.currentTime = 0;
}
if (currentlyPlayingItem) {
currentlyPlayingItem.classList.remove('playing');
currentlyPlayingItem = null;
}
}
/**
* Démarre la lecture d'un élément
*/
function playItem(item, audioElement) {
// Arrêter l'élément actuel s'il y en a un
if (currentlyPlayingItem && currentlyPlayingItem !== item) {
stopCurrentItem();
}
currentlyPlayingItem = item;
currentAudioElement = audioElement;
// Marquer comme en cours de lecture
item.classList.add('playing');
// Démarrer la lecture
audioElement.play().catch(function(error) {
console.error('Erreur lors de la lecture:', error);
stopCurrentItem();
});
}
/**
* Met en pause l'élément
*/
function pauseItem(item, audioElement) {
audioElement.pause();
item.classList.remove('playing');
}
/**
* Initialise les contrôles du lecteur pour un type d'élément
*/
function initPlayerForType(itemSelector, buttonSelector) {
const items = document.querySelectorAll(itemSelector);
items.forEach(function(item) {
const playButton = item.querySelector(buttonSelector);
const audioElement = item.querySelector('audio');
if (!playButton || !audioElement) {
return;
}
// Gestion du clic sur le bouton play/pause
playButton.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
if (item.classList.contains('playing')) {
pauseItem(item, audioElement);
} else {
playItem(item, audioElement);
}
});
// Gérer la fin de la lecture
audioElement.addEventListener('ended', function() {
item.classList.remove('playing');
// Passer automatiquement à l'élément suivant
const nextItem = item.nextElementSibling;
if (nextItem && (nextItem.classList.contains('funkwhale-track-item') ||
nextItem.classList.contains('castopod-episode-item'))) {
const nextAudio = nextItem.querySelector('audio');
if (nextAudio) {
playItem(nextItem, nextAudio);
}
} else {
// Réinitialiser si c'était le dernier élément
currentlyPlayingItem = null;
currentAudioElement = null;
}
});
// Gérer les erreurs de chargement
audioElement.addEventListener('error', function(e) {
console.error('Erreur de chargement audio:', e);
item.classList.remove('playing');
// Afficher un message d'erreur temporaire
const titleSelector = item.classList.contains('funkwhale-track-item') ?
'.funkwhale-track-title' : '.castopod-episode-title';
const title = item.querySelector(titleSelector);
if (title) {
const originalText = title.textContent;
title.textContent = 'Erreur de lecture';
title.style.color = '#ff0000';
setTimeout(function() {
title.textContent = originalText;
title.style.color = '';
}, 3000);
}
});
});
}
/**
* Initialise tous les lecteurs
*/
function initPlayer() {
// Initialiser le lecteur Funkwhale
initPlayerForType('.funkwhale-track-item', '.funkwhale-play-btn');
// Initialiser le lecteur Castopod
initPlayerForType('.castopod-episode-item', '.castopod-play-btn');
}
// Initialiser le lecteur quand le DOM est prêt
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initPlayer);
} else {
initPlayer();
}
// Nettoyer à la fermeture de la page
window.addEventListener('beforeunload', function() {
stopCurrentItem();
});
})();
-132
View File
@@ -1,132 +0,0 @@
/**
* Lecteur audio intégré pour Funkwhale
* Permet de lire les morceaux directement dans la page
*/
(function() {
'use strict';
let currentlyPlayingTrack = null;
let currentAudioElement = null;
/**
* Arrête le morceau en cours de lecture
*/
function stopCurrentTrack() {
if (currentAudioElement) {
currentAudioElement.pause();
currentAudioElement.currentTime = 0;
}
if (currentlyPlayingTrack) {
currentlyPlayingTrack.classList.remove('playing');
currentlyPlayingTrack = null;
}
}
/**
* Démarre la lecture d'un morceau
*/
function playTrack(trackItem, audioElement) {
// Arrêter le morceau actuel s'il y en a un
if (currentlyPlayingTrack && currentlyPlayingTrack !== trackItem) {
stopCurrentTrack();
}
currentlyPlayingTrack = trackItem;
currentAudioElement = audioElement;
// Marquer comme en cours de lecture
trackItem.classList.add('playing');
// Démarrer la lecture
audioElement.play().catch(function(error) {
console.error('Erreur lors de la lecture:', error);
stopCurrentTrack();
});
}
/**
* Met en pause le morceau
*/
function pauseTrack(trackItem, audioElement) {
audioElement.pause();
trackItem.classList.remove('playing');
}
/**
* Initialise les contrôles du lecteur
*/
function initPlayer() {
const trackItems = document.querySelectorAll('.funkwhale-track-item');
trackItems.forEach(function(trackItem) {
const playButton = trackItem.querySelector('.funkwhale-play-btn');
const audioElement = trackItem.querySelector('audio');
if (!playButton || !audioElement) {
return;
}
// Gestion du clic sur le bouton play/pause
playButton.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
if (trackItem.classList.contains('playing')) {
pauseTrack(trackItem, audioElement);
} else {
playTrack(trackItem, audioElement);
}
});
// Gérer la fin de la lecture
audioElement.addEventListener('ended', function() {
trackItem.classList.remove('playing');
// Passer automatiquement au morceau suivant
const nextTrack = trackItem.nextElementSibling;
if (nextTrack && nextTrack.classList.contains('funkwhale-track-item')) {
const nextAudio = nextTrack.querySelector('audio');
if (nextAudio) {
playTrack(nextTrack, nextAudio);
}
} else {
// Réinitialiser si c'était le dernier morceau
currentlyPlayingTrack = null;
currentAudioElement = null;
}
});
// Gérer les erreurs de chargement
audioElement.addEventListener('error', function(e) {
console.error('Erreur de chargement audio:', e);
trackItem.classList.remove('playing');
// Afficher un message d'erreur temporaire
const trackTitle = trackItem.querySelector('.funkwhale-track-title');
if (trackTitle) {
const originalText = trackTitle.textContent;
trackTitle.textContent = 'Erreur de lecture';
trackTitle.style.color = '#ff0000';
setTimeout(function() {
trackTitle.textContent = originalText;
trackTitle.style.color = '';
}, 3000);
}
});
});
}
// Initialiser le lecteur quand le DOM est prêt
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initPlayer);
} else {
initPlayer();
}
// Nettoyer à la fermeture de la page
window.addEventListener('beforeunload', function() {
stopCurrentTrack();
});
})();