```
✨ feat(sitemap): Améliore le sitemap avec images, vidéos et pages statiques
```
This commit is contained in:
BIN
public/provider/video/video.jpg
Normal file
BIN
public/provider/video/video.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
80
src/Command/GenerateVideoThumbsCommand.php
Normal file
80
src/Command/GenerateVideoThumbsCommand.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:generate-video-thumbs',
|
||||
description: 'Génère une miniature JPG à partir d’une liste de vidéos.',
|
||||
)]
|
||||
class GenerateVideoThumbsCommand extends Command
|
||||
{
|
||||
// Liste des vidéos (relatif à /public)
|
||||
private const VIDEOS_TO_THUMB = [
|
||||
'provider/video/video.mp4',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private readonly ParameterBagInterface $parameterBag
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$publicPath = $this->parameterBag->get('kernel.project_dir') . '/public/';
|
||||
|
||||
$io->title('Extraction des miniatures Vidéo');
|
||||
|
||||
foreach (self::VIDEOS_TO_THUMB as $relativeVideoPath) {
|
||||
$videoFile = $publicPath . $relativeVideoPath;
|
||||
|
||||
if (!file_exists($videoFile)) {
|
||||
$io->warning("Vidéo introuvable : $videoFile");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Définition du nom de sortie (ex: video.mp4 -> video.jpg)
|
||||
$pathInfo = pathinfo($videoFile);
|
||||
$thumbName = $pathInfo['filename'] . '.jpg';
|
||||
$thumbPath = $pathInfo['dirname'] . '/' . $thumbName;
|
||||
|
||||
$io->writeln("Traitement de : " . $pathInfo['basename']);
|
||||
|
||||
// Commande FFmpeg :
|
||||
// -i : fichier d'entrée
|
||||
// -ss : position (00:00:01 = 1ère seconde pour éviter l'écran noir du début)
|
||||
// -vframes 1 : extraire une seule image
|
||||
// -q:v 2 : qualité de l'image (2 à 5 est bon)
|
||||
$command = [
|
||||
'ffmpeg',
|
||||
'-y', // Écraser si le fichier existe
|
||||
'-i', $videoFile,
|
||||
'-ss', '00:00:01.000', // Capture à 1 seconde
|
||||
'-vframes', '1',
|
||||
$thumbPath
|
||||
];
|
||||
|
||||
$process = new Process($command);
|
||||
$process->run();
|
||||
|
||||
if ($process->isSuccessful()) {
|
||||
$io->writeln("<info>[OK]</info> Miniature générée : $thumbName");
|
||||
} else {
|
||||
$io->error("Erreur FFmpeg : " . $process->getErrorOutput());
|
||||
}
|
||||
}
|
||||
|
||||
$io->success('Toutes les vidéos ont été traitées.');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -2,31 +2,29 @@
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use App\Entity\Product;
|
||||
use App\Repository\ProductRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Presta\SitemapBundle\Event\SitemapPopulateEvent;
|
||||
use Presta\SitemapBundle\Sitemap\Url\GoogleImage;
|
||||
use Presta\SitemapBundle\Sitemap\Url\GoogleImageUrlDecorator;
|
||||
use Presta\SitemapBundle\Sitemap\Url\GoogleVideo;
|
||||
use Presta\SitemapBundle\Sitemap\Url\GoogleVideoUrlDecorator;
|
||||
use Presta\SitemapBundle\Sitemap\Url\UrlConcrete;
|
||||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Twig\Environment;
|
||||
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
|
||||
|
||||
class SiteMapListener implements EventSubscriberInterface
|
||||
{
|
||||
|
||||
public function __construct(private readonly ProductRepository $productRepository)
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UploaderHelper $uploaderHelper,
|
||||
private readonly ProductRepository $productRepository,
|
||||
private readonly EntityManagerInterface $entityManager
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
SitemapPopulateEvent::class => 'populate',
|
||||
@@ -38,26 +36,91 @@ class SiteMapListener implements EventSubscriberInterface
|
||||
$urlGenerator = $event->getUrlGenerator();
|
||||
$urlContainer = $event->getUrlContainer();
|
||||
|
||||
$t = new \DateTime();
|
||||
$rv = new UrlConcrete($urlGenerator->generate('reservation',[], UrlGeneratorInterface::ABSOLUTE_URL),$t,UrlConcrete::CHANGEFREQ_DAILY,1);
|
||||
$urlContainer->addUrl($rv,'reservation');
|
||||
$rv = new UrlConcrete($urlGenerator->generate('reservation_contact',[], UrlGeneratorInterface::ABSOLUTE_URL),$t,UrlConcrete::CHANGEFREQ_DAILY,1);
|
||||
$urlContainer->addUrl($rv,'reservation');
|
||||
$rv = new UrlConcrete($urlGenerator->generate('reservation_cookies',[], UrlGeneratorInterface::ABSOLUTE_URL),$t,UrlConcrete::CHANGEFREQ_WEEKLY,0.5);
|
||||
$urlContainer->addUrl($rv,'reservation');
|
||||
$rv = new UrlConcrete($urlGenerator->generate('reservation_mentions-legal',[], UrlGeneratorInterface::ABSOLUTE_URL),$t,UrlConcrete::CHANGEFREQ_WEEKLY,0.5);
|
||||
$urlContainer->addUrl($rv,'reservation');
|
||||
$rv = new UrlConcrete($urlGenerator->generate('reservation_rgpd',[], UrlGeneratorInterface::ABSOLUTE_URL),$t,UrlConcrete::CHANGEFREQ_WEEKLY,0.5);
|
||||
$urlContainer->addUrl($rv,'reservation');
|
||||
$rv = new UrlConcrete($urlGenerator->generate('reservation_hosting',[], UrlGeneratorInterface::ABSOLUTE_URL),$t,UrlConcrete::CHANGEFREQ_WEEKLY,0.5);
|
||||
$urlContainer->addUrl($rv,'reservation');
|
||||
$rv = new UrlConcrete($urlGenerator->generate('reservation_cgv',[], UrlGeneratorInterface::ABSOLUTE_URL),$t,UrlConcrete::CHANGEFREQ_WEEKLY,0.5);
|
||||
$urlContainer->addUrl($rv,'reservation');
|
||||
// Base URL pour les médias
|
||||
$baseUrl = $urlGenerator->generate('reservation', [], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$rootUrl = rtrim($baseUrl, '/');
|
||||
|
||||
$t = new \DateTime();
|
||||
|
||||
// --- 1. PAGES STATIQUES & VIDÉO ---
|
||||
$reservationUrl = $urlGenerator->generate('reservation', [], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$homeUrl = new UrlConcrete($reservationUrl, $t, UrlConcrete::CHANGEFREQ_DAILY, 1);
|
||||
|
||||
// Décorateur Vidéo pour la page d'accueil
|
||||
$videoUrl = $rootUrl . '/provider/video/video.mp4';
|
||||
$videoTrumbs = $rootUrl . '/provider/video/video.jpg';
|
||||
$homeWithVideo = new GoogleVideoUrlDecorator($homeUrl);
|
||||
|
||||
$video = new GoogleVideo(
|
||||
$videoTrumbs,
|
||||
"Découvrez nos produits en vidéo",
|
||||
"Découvrez nos produits en vidéo",
|
||||
['content_location' => $videoUrl]
|
||||
);
|
||||
$homeWithVideo->addVideo($video);
|
||||
$urlContainer->addUrl($homeWithVideo, 'reservation');
|
||||
|
||||
// Autres routes statiques
|
||||
$staticRoutes = [
|
||||
'reservation_contact' => UrlConcrete::CHANGEFREQ_DAILY,
|
||||
'reservation_cookies' => UrlConcrete::CHANGEFREQ_WEEKLY,
|
||||
'reservation_mentions-legal' => UrlConcrete::CHANGEFREQ_WEEKLY,
|
||||
'reservation_rgpd' => UrlConcrete::CHANGEFREQ_WEEKLY,
|
||||
'reservation_hosting' => UrlConcrete::CHANGEFREQ_WEEKLY,
|
||||
'reservation_cgv' => UrlConcrete::CHANGEFREQ_WEEKLY,
|
||||
];
|
||||
|
||||
foreach ($staticRoutes as $route => $freq) {
|
||||
$url = $urlGenerator->generate($route, [], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$urlContainer->addUrl(new UrlConcrete($url, $t, $freq, 0.5), 'reservation');
|
||||
}
|
||||
|
||||
// --- 2. PRODUITS & IMAGES ---
|
||||
foreach ($this->productRepository->findAll() as $product) {
|
||||
$rv = new UrlConcrete($urlGenerator->generate('reservation_product_show',['id'=>$product->slug()], UrlGeneratorInterface::ABSOLUTE_URL),$t,UrlConcrete::CHANGEFREQ_WEEKLY,0.5);
|
||||
$urlContainer->addUrl($rv,'reservation_product');
|
||||
$productUrl = $urlGenerator->generate(
|
||||
'reservation_product_show',
|
||||
['id' => $product->slug()],
|
||||
UrlGeneratorInterface::ABSOLUTE_URL
|
||||
);
|
||||
|
||||
$decoratedUrl = new GoogleImageUrlDecorator(
|
||||
new UrlConcrete($productUrl, $t, UrlConcrete::CHANGEFREQ_WEEKLY, 0.5)
|
||||
);
|
||||
|
||||
$imagePath = $this->uploaderHelper->asset($product, 'imageFile');
|
||||
|
||||
if ($imagePath) {
|
||||
$usedList = [];
|
||||
// Appel de votre fonction avec l'objet product
|
||||
$this->addImageToSitemap($decoratedUrl, $usedList, $rootUrl, $imagePath, $product);
|
||||
}
|
||||
|
||||
$urlContainer->addUrl($decoratedUrl, 'reservation_product');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Votre fonction personnalisée avec gestion Base64 et Titre dynamique
|
||||
*/
|
||||
private function addImageToSitemap(GoogleImageUrlDecorator $pathMenu, array &$usedList, string $rootUrl, string $image, object $object): void
|
||||
{
|
||||
// 1. Ignorer les images en base64
|
||||
if (str_starts_with($image, 'data:image/')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Éviter les doublons
|
||||
if (!isset($usedList[$image])) {
|
||||
$imageUrl = str_starts_with($image, 'http') ? $image : $rootUrl . $image;
|
||||
$gImage = new GoogleImage($imageUrl);
|
||||
|
||||
if ($object instanceof Product) {
|
||||
// Utilisation du nom de l'objet pour le SEO de l'image
|
||||
$gImage->setTitle("Image du produit - " . $object->getName());
|
||||
}
|
||||
|
||||
$pathMenu->addImage($gImage);
|
||||
$usedList[$image] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user