fix(video-player): fix the video player to permit the nagivation through the video (it was because de fast api server refused range request)
Some checks failed
Deploy to Development / build-and-deploy (push) Failing after 2m15s

This commit is contained in:
EvanChal
2026-01-25 18:08:38 +01:00
parent 3fbf372dae
commit 0020c13bfd
17 changed files with 393 additions and 212 deletions

View File

@@ -2,29 +2,37 @@
<div class="video-player-container">
<div class="relative">
<!-- Video.js Player -->
<video
ref="videoPlayer"
class="video-js vjs-default-skin vjs-big-play-centered w-full rounded-lg"
controls
preload="auto"
:poster="posterUrl"
data-setup="{}"
>
<source :src="videoUrl" type="video/mp4" />
<p class="vjs-no-js">
Pour voir cette vidéo, activez JavaScript et considérez passer à un navigateur web qui
<a href="https://videojs.com/html5-video-support/" target="_blank">supporte la vidéo HTML5</a>.
</p>
</video>
<div data-vjs-player>
<video
ref="videoPlayer"
class="video-js vjs-default-skin vjs-big-play-centered w-full rounded-lg"
controls
preload="auto"
:poster="posterUrl"
playsinline
>
<source :src="videoUrl" />
<p class="vjs-no-js">
Pour voir cette vidéo, activez JavaScript et considérez passer à un navigateur web qui
<a href="https://videojs.com/html5-video-support/" target="_blank">supporte la vidéo HTML5</a>.
</p>
</video>
</div>
</div>
<!-- Video Stats -->
<div class="mt-4 flex items-center justify-between text-sm text-gray-600">
<div class="flex items-center space-x-4">
<span class="flex items-center">
<Eye class="w-4 h-4 mr-1" />
{{ viewsCount }} vue{{ viewsCount > 1 ? 's' : '' }}
</span>
<div class="flex items-center space-x-3">
<span class="flex items-center" title="Vues uniques">
<Eye class="w-4 h-4 mr-1" />
{{ viewsCount }}
</span>
<span class="flex items-center" title="Replays">
<RotateCcw class="w-4 h-4 mr-1" />
{{ replaysCount }}
</span>
</div>
<span class="flex items-center">
<Clock class="w-4 h-4 mr-1" />
{{ formatDuration(duration) }}
@@ -57,7 +65,7 @@
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue'
import videojs from 'video.js'
import 'video.js/dist/video-js.css'
import { Eye, Clock, Heart, MessageSquare } from 'lucide-vue-next'
import { Eye, Clock, Heart, MessageSquare, RotateCcw } from 'lucide-vue-next'
import { getMediaUrl } from '@/utils/axios'
const props = defineProps({
@@ -85,6 +93,10 @@ const props = defineProps({
type: Number,
default: 0
},
replaysCount: {
type: Number,
default: 0
},
likesCount: {
type: Number,
default: 0
@@ -103,6 +115,7 @@ const emit = defineEmits(['like', 'toggle-comments'])
const videoPlayer = ref(null)
const player = ref(null)
const currentVideoSrc = ref(null) // Track la source actuelle pour éviter les rechargements inutiles
// Computed properties pour les URLs
const videoUrl = computed(() => getMediaUrl(props.src))
@@ -124,16 +137,62 @@ function toggleComments() {
emit('toggle-comments')
}
// Fonction pour gérer les raccourcis clavier manuellement
function handleKeydown(e) {
if (!player.value) return;
// Ignorer si l'utilisateur tape dans un input
if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) return;
switch(e.key) {
case 'ArrowLeft':
e.preventDefault();
player.value.currentTime(Math.max(0, player.value.currentTime() - 10));
break;
case 'ArrowRight':
e.preventDefault();
player.value.currentTime(Math.min(player.value.duration(), player.value.currentTime() + 10));
break;
case ' ':
case 'Space': // Espace pour pause/play
e.preventDefault();
if (player.value.paused()) {
player.value.play();
} else {
player.value.pause();
}
break;
}
}
onMounted(() => {
if (videoPlayer.value) {
player.value = videojs(videoPlayer.value, {
// Options de base pour Video.js
const options = {
controls: true,
fluid: true,
responsive: true,
playbackRates: [0.5, 0.75, 1, 1.25, 1.5, 2],
// Désactiver les hotkeys natifs qui causent l'erreur passive listener
userActions: {
hotkeys: false
},
html5: {
vhs: {
overrideNative: true
},
nativeAudioTracks: false,
nativeVideoTracks: false
},
controlBar: {
skipButtons: {
forward: 10,
backward: 10
},
children: [
'playToggle',
'skipBackward',
'skipForward',
'volumePanel',
'currentTimeDisplay',
'timeDivider',
@@ -143,12 +202,23 @@ onMounted(() => {
'fullscreenToggle'
]
}
})
};
player.value = videojs(videoPlayer.value, options);
// Définir la source initiale après l'initialisation
if (videoUrl.value) {
player.value.src({ src: videoUrl.value, type: 'video/mp4' })
currentVideoSrc.value = videoUrl.value
}
// Error handling
player.value.on('error', (error) => {
console.error('Video.js error:', error)
})
// Ajouter l'écouteur d'événements clavier global
document.addEventListener('keydown', handleKeydown)
}
})
@@ -156,20 +226,39 @@ onBeforeUnmount(() => {
if (player.value) {
player.value.dispose()
}
// Retirer l'écouteur
document.removeEventListener('keydown', handleKeydown)
})
// Watch for src changes to reload video
watch(() => props.src, () => {
if (player.value && videoUrl.value) {
player.value.src({ src: videoUrl.value, type: 'video/mp4' })
// Watch for src changes to reload video - amélioré pour éviter les rechargements inutiles
watch(() => videoUrl.value, (newUrl, oldUrl) => {
// Ne recharger que si l'URL a vraiment changé et que le player est prêt
if (player.value && newUrl && newUrl !== currentVideoSrc.value) {
const wasPlaying = !player.value.paused()
const currentTime = player.value.currentTime()
player.value.src({ src: newUrl, type: 'video/mp4' })
player.value.load()
currentVideoSrc.value = newUrl
// Restaurer la position si possible (optionnel)
player.value.ready(() => {
if (currentTime > 0 && currentTime < player.value.duration()) {
player.value.currentTime(currentTime)
}
if (wasPlaying) {
player.value.play().catch(() => {})
}
})
}
})
}, { immediate: false })
</script>
<style scoped>
.video-js {
aspect-ratio: 16/9;
/* Fix pour l'erreur "passive event listener" sur certains navigateurs */
touch-action: manipulation;
}
.video-js .vjs-big-play-button {