diff --git a/assets/video/BACKGROUND_VIDEO_MISSION_REVERSE_opt.mp4 b/assets/video/BACKGROUND_VIDEO_MISSION_REVERSE_opt.mp4 new file mode 100644 index 0000000..661ea5d Binary files /dev/null and b/assets/video/BACKGROUND_VIDEO_MISSION_REVERSE_opt.mp4 differ diff --git a/assets/video/BACKGROUND_VIDEO_MISSION_opt.mp4 b/assets/video/BACKGROUND_VIDEO_MISSION_opt.mp4 new file mode 100644 index 0000000..136aa4d Binary files /dev/null and b/assets/video/BACKGROUND_VIDEO_MISSION_opt.mp4 differ diff --git a/assets/video/Website_version_opt.mp4 b/assets/video/Website_version_opt.mp4 new file mode 100644 index 0000000..cd47f4b Binary files /dev/null and b/assets/video/Website_version_opt.mp4 differ diff --git a/assets/video/mobile_version_opt.mp4 b/assets/video/mobile_version_opt.mp4 new file mode 100644 index 0000000..e7c0e67 Binary files /dev/null and b/assets/video/mobile_version_opt.mp4 differ diff --git a/docs/ffmpeg.md b/docs/ffmpeg.md new file mode 100644 index 0000000..8628f91 --- /dev/null +++ b/docs/ffmpeg.md @@ -0,0 +1,48 @@ +# Commandes FFmpeg + +## Qu'est-ce que FFmpeg ? + +FFmpeg est un outil en ligne de commande open source pour traiter des fichiers audio et vidéo : conversion de formats, compression, extraction de frames, redimensionnement, etc. C'est la référence dans le domaine, utilisée par VLC, YouTube et la plupart des plateformes vidéo en coulisses. + +### Installation + +**macOS** (via Homebrew) : +```bash +brew install ffmpeg +``` + +**Linux (Debian/Ubuntu)** : +```bash +sudo apt install ffmpeg +``` + +**Windows** : télécharger le build depuis [ffmpeg.org/download.html](https://ffmpeg.org/download.html) et ajouter le dossier `bin/` au PATH. + +Vérifier l'installation : +```bash +ffmpeg -version +``` + +--- + +## Compression vidéo (page d'accueil) + +Compresse sans perte de résolution ni de framerate. CRF 23 = qualité par défaut libx264. `faststart` place l'atome `moov` en tête du fichier pour un démarrage immédiat en streaming. + +```bash +# Desktop (1920x1080, 30fps) — 22MB → 5.8MB +ffmpeg -i Website_version.mp4 \ + -c:v libx264 -preset slow -crf 23 -an -movflags +faststart \ + Website_version_opt.mp4 + +# Mobile (1080x1920, 30fps) — 22MB → 4.8MB +ffmpeg -i mobile_version_texte_fixe.mp4 \ + -c:v libx264 -preset slow -crf 23 -an -movflags +faststart \ + mobile_version_opt.mp4 +``` + +## Notes + +- `-an` : supprime la piste audio (inutile pour des vidéos `muted` en fond) +- `-preset slow` : meilleure compression à qualité égale (encode plus lentement) +- `crf` : Constant Rate Factor — plus bas = meilleure qualité. 23 = défaut libx264, 18 = quasi-lossless diff --git a/site/blueprints/pages/expertise.yml b/site/blueprints/pages/expertise.yml index 1226e6a..8fc7b85 100644 --- a/site/blueprints/pages/expertise.yml +++ b/site/blueprints/pages/expertise.yml @@ -53,3 +53,37 @@ tabs: - heading headings: - 3 + + media: + label: Médias + icon: image + columns: + - width: 1/2 + sections: + mediaFields: + type: fields + fields: + backgroundVideo: + label: Vidéo d'arrière-plan (sens normal) + type: files + layout: cards + max: 1 + accept: video/* + translate: false + image: + ratio: 16/9 + cover: true + uploads: + template: video + backgroundVideoReverse: + label: Vidéo d'arrière-plan (sens inverse) + type: files + layout: cards + max: 1 + accept: video/* + translate: false + image: + ratio: 16/9 + cover: true + uploads: + template: video diff --git a/site/blueprints/pages/home.yml b/site/blueprints/pages/home.yml index 04c61f3..3c7802d 100644 --- a/site/blueprints/pages/home.yml +++ b/site/blueprints/pages/home.yml @@ -41,9 +41,8 @@ tabs: type: fields fields: backgroundVideo: - label: Vidéo d'arrière-plan + label: Vidéo d'arrière-plan (desktop) type: files - layout: cards max: 1 accept: video/* translate: false @@ -52,3 +51,36 @@ tabs: cover: true uploads: template: video + backgroundVideoPoster: + label: Poster vidéo desktop + type: files + layout: cards + max: 1 + accept: image/* + translate: false + image: + ratio: 16/9 + cover: true + help: Image affichée au besoin pendant le chargement de la vidéo + backgroundVideoMobile: + label: Vidéo d'arrière-plan (mobile) + type: files + max: 1 + accept: video/* + translate: false + image: + ratio: 9/16 + cover: true + uploads: + template: video + backgroundVideoMobilePoster: + label: Poster vidéo mobile + type: files + layout: cards + max: 1 + accept: image/* + translate: false + image: + ratio: 9/16 + cover: true + help: Image affichée au besoin pendant le chargement de la vidéo diff --git a/site/templates/expertise.json.php b/site/templates/expertise.json.php index 895206e..105e16b 100644 --- a/site/templates/expertise.json.php +++ b/site/templates/expertise.json.php @@ -1,8 +1,10 @@ $page->writer()->value(), - 'items' => $page->body()->toBlocks()->filter(fn($b) => $b->type() === 'text')->map(fn($block) => [ + 'pageTitle' => $page->writer()->value(), + 'backgroundVideo' => $page->backgroundVideo()->toFile()?->url(), + 'backgroundVideoReverse' => $page->backgroundVideoReverse()->toFile()?->url(), + 'items' => $page->body()->toBlocks()->filter(fn($b) => $b->type() === 'text')->map(fn($block) => [ 'text' => $block->text()->value() ])->values() ]; diff --git a/site/templates/home.json.php b/site/templates/home.json.php index 3f6f227..a7d53c2 100644 --- a/site/templates/home.json.php +++ b/site/templates/home.json.php @@ -9,7 +9,10 @@ $specificData = [ 'ctaPath' => $page->ctaLink()->toPage()?->id() ?? '#', 'image' => $page->heroImage()->toFile()?->url() ], - 'backgroundVideo' => $page->backgroundVideo()->toFile()?->url(), + 'backgroundVideo' => $page->backgroundVideo()->toFile()?->url(), + 'backgroundVideoPoster' => $page->backgroundVideoPoster()->toFile()?->url(), + 'backgroundVideoMobile' => $page->backgroundVideoMobile()->toFile()?->url(), + 'backgroundVideoMobilePoster' => $page->backgroundVideoMobilePoster()->toFile()?->url(), 'floatingBubbles' => $page->floatingBubbles()->toStructure()->map(function($bubble) { return [ 'text' => $bubble->text()->value(), diff --git a/src/views/Expertise.svelte b/src/views/Expertise.svelte index 2dedd53..32f92f2 100644 --- a/src/views/Expertise.svelte +++ b/src/views/Expertise.svelte @@ -232,7 +232,7 @@ playsinline preload="auto" > - + diff --git a/src/views/Home.svelte b/src/views/Home.svelte index 602ac02..866422f 100644 --- a/src/views/Home.svelte +++ b/src/views/Home.svelte @@ -7,36 +7,34 @@ const currentLang = $derived(locale.current) - onMount(() => { - const playVideo = async (videoId) => { - const video = document.getElementById(videoId) - if (video) { - try { - video.muted = true - video.playsInline = true - const playPromise = video.play() - if (playPromise !== undefined) { - await playPromise - } - } catch (error) { - console.log(`Autoplay failed for ${videoId}:`, error) - const playOnInteraction = () => { - video.play().catch(e => console.log('Fallback play failed:', e)) - document.removeEventListener('click', playOnInteraction) - document.removeEventListener('touchstart', playOnInteraction) - } - document.addEventListener('click', playOnInteraction) - document.addEventListener('touchstart', playOnInteraction) + function playWhenReady(video) { + if (!video) return + + const attempt = () => { + video.muted = true + video.playsInline = true + video.play().catch(() => { + // Autoplay bloqué : on attend une interaction + const onInteraction = () => { + video.play().catch(() => {}) + document.removeEventListener('click', onInteraction) + document.removeEventListener('touchstart', onInteraction) } - } + document.addEventListener('click', onInteraction) + document.addEventListener('touchstart', onInteraction) + }) } - const timer = setTimeout(() => { - playVideo('home-video') - playVideo('home-video-mobile') - }, 100) + if (video.readyState >= 3) { + attempt() + } else { + video.addEventListener('canplay', attempt, { once: true }) + } + } - return () => clearTimeout(timer) + onMount(() => { + playWhenReady(document.getElementById('home-video')) + playWhenReady(document.getElementById('home-video-mobile')) }) function handleExplore() { @@ -49,27 +47,25 @@