%

Hugo : Render Hook Image + Audio & Video

Comment utiliser le procédé de 'render hook' pour gérer à la fois l'inclusion d'image (ou de vidéo, ou d'audio) dans Hugo avec seulement la syntaxe image markdown.

Détails relatifs à l'article

Article publié, le
et modifié le
9 minutes de lecture

Cet article contient 1899 mots.

Identifiant de l'article : tag:doc.huc.fr.eu.org,2026-09-22:/fr/web/hugo/render-hook-image-video/


Source brute de l'article :
Commit version : aa02ae5


Cet article est aussi disponible sur le protocole Gemini :
gemini://gmi.it-log.fr.eu.org/fr/web/hugo/render-hook-image-video-audio.gmi


Description

Hugo gère la syntaxe Markdown des images, par le biais du moteur Goldmark.

Le format Markdown pour les images est connu :

![Tux](tux.jpg "Logo du Manchot")

Hugo permet de surcharger le comportement par le biais de “render hook” - qui se traduirait en français par ‘crochet de rendu’. Ceux-ci sont à écrire généralement dans un sous-répertoire nommé _markup du répertoire principal layouts.

Prérequis

Module

J’utilise historiquement celui créé par “Veriphor LLC”, déposé sous licence Apache 2.0… parce qu’à l’époque en 2023, cela m’était plus simple et je n’avais vraiment pas compris la mécanique derrière. Donc, pourquoi écrire ce que d’autres avaient certainement mieux fait que moi…

Ce render hook nécessite la modification de la configuration des modules à monter, tel que :

modules:
  mounts:
  - source: assets
    target: assets
  - source: static
    target: assets

Paramètres

Puis l’ajout de ces paramètres dans la configuration :

params:
(…)
  render_hooks:
    DontDlMedia:
      audio: true
      image: false
      video: true
    image:
    # ignore (default), warning, or error (fails the build)
      errorLevel: warning
(…)
  • ajout des paramètres DonotDlMedia : les valeurs boolènnes correspondantes aux medias permettent ou non le téléchargement du média, lors de la publication. Si la valeur boolènne est vraie, alors la méthode utilisée sera head au lieu de get. Ces paramètres n’interagissent que dans le contexte des ressources à distance, et non locales.
    Les medias en question peuvent avoir des tailles considérables ; dans le contexte de ressources partagées, cela peut avoir un impact certain (- exemple d’un service git-pages mutualisé, …).

À ce propos, j’ai aussi ajouté principalement une nouvelle option de paramètres :

params:
(…)
  render:
    video:
      size: "300x"
  • ce paramètre est utile pour fixer la taille du lecteur vidéo - pour rappel, la norme relative définit que la taille doit être en pixels et non pas pourcentage… Il vous faut les ajouter !

Sécurité

Deux aspects, liés à la sécurité des données gérées par Hugo, sont à mentionner…

⇒ Il est nécessaire d’autoriser la réception de données par les méthodes GET et HEAD.

Pour cela modifier la configuration security, pour ajouter/modifier les méthodes http, tel que :

security:
  http:
    (…)
    methods:
    - (?i)GET|HEAD

⇒ Il peut-être utile de gérer les mediatypes autorisés, toujours dans security, surtout si vous avez modifié la politique par défaut null.

Par exemple, en ajoutant les mediatypes audio et/ou vidéo concernés, tel que :

security:
  http:
    mediaTypes:
    - "^audio/mpeg$"
    - "^video/mp4$"

Très utile, quand Hugo refuse d’obtenir le contenu et que la console restitue le message sybillin suivant :

WARN  template: _markup/render-image.html:136:27: executing "_markup/render-image.html" at <resources.GetRemote>: error calling GetRemote: failed to resolve media type for remote resource "https://dn710802.ca.archive.org/0/items/tour_du_monde_en_80_jours_librivox/tour_monde_01_verne_64kb.mp3". See web/hugo/render-hook-image-video.md

Dans les faits, Hugo n’affichera pas le contenu distant… à retenir !

Modifications

Au courant de l’année 2026, j’ai commencé à triturer ce modèle pour essayer :

  • d’apporter un peu plus de sens, en ajoutant l’élément a et picture
  • de détecter si une taille d’affichage est demandée,
  • puis ajouter des sources pour l’affichage des formats webp, puis avif
  • de modifier le code pour gèrer les images au format svg
  • en date du 2 Oct., je me suis posé la question de gérer l’affichage de vidéo.
  • en date du 5 Oct, j’ai ajouté la gestion de l’affichage d’un lecteur audio.

Nativement, Markdown ne supporte ni l’affichage audio, ni vidéo.

Je veux pouvoir utiliser la syntaxe pour les images et que le crochet fasse la détection d’une image, d’une audio ou d’une vidéo et affiche le code adéquat.


Après le gros du travail effectué par le code originel, j’ai initialisé quelques variables utiles pour le traitement futur d’un média image ou vidéo, avant l’initialisation du dictionnaire d’attributs $attrs :

(…)
    {{- /* Initialize variables. */}}

    {{- $alt := $.PlainText -}}
    {{- $content := "" -}}
    {{- $dest := $.Destination | safeURL -}}
    {{- $ext := .MediaType.SubType -}}
    {{- $height := "" -}}
    {{- $media := .MediaType.MainType -}}
    {{- $size := len .Content -}}
    {{- $src := .RelPermalink -}}
    {{- $srcset := dict }}
    {{- $title := $.Title | safeHTML -}}
    {{- $type := .MediaType -}}
    {{- $width := "" -}}
(…)

$r étant la variable ressource…

J’ai de fait modifié l’initialisation du dictionnaire $attrs, tel que :

(…)
    {{- /* Initialize attributes. */}}
    {{- $attrs := merge $.Attributes (dict) }}

    {{- if eq $media "image" }}

        {{- $img := $r -}}

        {{/* - with $r */}}

        {{- if eq $ext "svg" }}

            (…)

        {{- else }}

            {{- $attrs = merge $attrs (dict "id" $id "alt" $alt "title" $title "src" $src) }}
(…)

En effet, le besoin de gérer les attributs d’image n’est sensible que pour les images autres que le format SVG, du moins actuellement.

Mediatype

Une des choses que fait très bien Hugo est la détection du type de media. Il est ainsi possible grâce à l’utilisation de l’objet .MediaType d’une ressource de savoir si c’est une image ou autre chose, telle une vidéo.
.MainType est la méthode d’objet qui retourne le type principal de la ressource, initialisée dans la variable $media.

Il suffit ensuite d’utiliser la variable $media pour alimenter du code spécifique :

(…)

    {{- if eq $media "image" }}
    (…)

    {{- else if eq $media "audio" }}
    (…)

    {{- else if eq $media "video" }}
    (…)

    {{- end }}
(…)

Image

Déterminer la taille d’image

Pour déterminer si une taille d’affichage de l’image est demandée, je rajoute dans la syntaxe d’image l’argument suivant w et précise la taille désirée, tel que ?w=250 par exemple.

Pour gérer cette particularité dans le render hook, j’ai dû le modifier pour déterminer si l’ajout était fait dans l’url de l’image :

{{- /* Determine if 'w=' on $u */ -}}
{{ $w := "" }}
{{- if findRE "w=" $u }}
    {{ $w = replaceRE ".*w=([^ ]*).*" "$1" $u }}
{{- end }}

Puis il faut gérer la retouche de la taille d’image, dans le bloc de code du media image :

(…)
    {{- $img := $r -}}

    {{- with $r }}

        (…)

            {{- if $w }}
                {{- $width = $w -}}
                {{- $img = $r.Resize (printf "%sx" $w) -}}
                {{- with $img }}{{- $height = .Height -}}{{- end -}}

            {{ else }}
                {{- $height = .Height -}}
                {{- $width = .Width -}}

            {{- end }}

            {{- $attrs = merge $attrs (dict "height" (string $height) "width" (string $width)) }}
(…)

L’image est retravaillée s’il y a présence de l’argument $w ; le code détecte la largeur de l’image créée, sinon si l’argument n’est pas présent, il récupère la taille de l’image ressource.
Ainsi les paramètres height et width ont toujours une valeur adéquate, à restituer dans le code source HTML lié.

Sources avif, webp

/!\ Depuis la version 0.162.0, Hugo est enfin capable de gèrer le format Avif !
J’ai donc retouché le code…

Pour ajouter les sources webp, et avif, cela se fait en deux temps :

  • Une première fois pour retoucher l’image aux formats avif et webp :
(…)
            {{/* Hugo.version need to: 0.162.0, to support Avif */}}
            {{- if site.Params.enable.img.avif }}
                {{- with .Resize (printf "%dx%d avif" ($height | int) ($width | int)) }}
                    {{ $srcset = merge $srcset (dict "avif" (dict "srcset" .RelPermalink "type" .MediaType)) }}
                {{- end }}
            {{- end }}

            {{- if site.Params.enable.img.webp }}
                {{- with .Resize (printf "%dx%d webp" ($height | int) ($width | int)) }}
                    {{ $srcset = merge $srcset (dict "webp" (dict "srcset" .RelPermalink "type" .MediaType)) }}
                {{- end }}
            {{- end }}
(…)

PS : Comme vous pouvez le remarquer, j’ai fait le choix d’appeler des booléens depuis la configuration des paramètres, pour activer ou non le support desdits formats.

  • Une deuxième fois, dans le code HTML restitué :
(…)
        <picture>
            {{- with $srcset }}
                {{ range $srcset }}
                    {{ with . }}
                    <source srcset="{{ .srcset }}" type="{{ .type }}">
                    {{- end }}
                {{- end }}
            {{- end }}
            <img (…) >
        </picture>
(…)

Déterminer le format svg

Pour déterminer le format svg, l’utilisation de la méthode .SubType de l’objet ressource est viable.

Au sein du code déterminant le média image, la condition si le sous-type du média est svg alors il récupère le contenu du svg, puisque SVG est un contenu textuel.

(…)
{{- if eq $ext "svg" }}

    {{- $content = .Content | safeHTML -}}

{{- else }}
    (…) ; traitement des autres formats d'image

{{- end }}
(…)

Puis dans un deuxième temps, l’affichage du code HTML, tel que :

(…)
<figure role="figure" {{- with $title }}aria-label="{{ . }}"{{- end }}>
    <a href="{{ $src }}" {{- with $title }}title="{{ $title }}"{{- end }}>
        {{- with $content }}
            {{ . }}
        {{- else }}
(…)
  • je réfléchis à l’ajout du paramètre w dans l’url de l’image svg - mais pour l’instant rien ne me satisfait…

Audio

Aucun traitement d’image n’est ni fait ni à faire - sur de l’audio…

Le code appel directement le HTML nécessaire :

(…)
{{- else if eq $media "audio" }}
<figure role="figure" {{- with $title }}aria-label="{{ . }}"{{- end }}>
    <audio id="{{ $id }}" controls controlslist="nodownload" loading="lazy" preload="metadata" src="{{ $dest }}" >
    <p>
        <a href="{{ $src }}" {{- with $title }}title="{{ . }}"{{- end }}>
        {{ T "txtDlAudio" }}
        </a>
    </p>
    </audio>
    {{- with $title }}<figcaption>{{ . }}</figcaption>{{- end }}
</figure>
(…)
  • faites attention au texte linguistique…

Vidéo

Là encore, aucun traitement d’image n’est ni fait ni à faire - sur la vidéo…

Le code appel directement le HTML nécessaire :

(…)
{{- else if eq $media "video" }}
<video id="{{ $id }}" controls controlslist="nodownload" height="" loading="lazy" playsinline preload="metadata" src="{{ $dest }}" width="300px">
    <source src="{{ $dest }}" type="{{ $type }}">
    <p>
        <em>{{ T "txtNoSupportVideo" }}</em><br>
        <a href="{{ $dest }}" title="{{ $title }}">{{ $dest }}</a>
    </p>
</video>
(…)
  • faites attention au texte linguistique…

Utilisation

Comment je l’utilise ?

Très simplement :

Image

⇒ une image, hébergée localement, tel mon logo, au format PNG :

![Logo au format PNG](/images/Logo_full_192px.png "Mon logo au format PNG")

Logo au format PNG
Mon logo au format PNG

![Logo au format PNG, taille demandée de 128 px](/images/Logo_full_192px.png?w=128 "Mon logo au format PNG, retaillé à 128px")

Logo au format PNG, taille demandée de 128 px
Mon logo au format PNG, retaillé à 128px

⇒ une image, hébergée localement, tel mon logo, au format SVG :

![Logo au format SVG](/svg/Logo_final.svg "Mon logo au format SVG")

Logo Emblème Stéphane HUC Logo Emblème Stéphane HUC
Mon logo au format SVG

⇒ une image, à distance, tel mon logo, au format SVG :

![Logo au format SVG](https://img.huc.fr.eu.org/svg/Logo_final.svg "Mon logo au format SVG depuis l'URL FQDN adéquate")

⇒ ou, tel cette image :

⇒ ou la même, mais avec une taille demandé de 64 px

Logo au format PNG, taille demandée de 64 px
Mon logo au format PNG depuis l'URL FQDN adéquate, retaillé à 64px

Audio

⇒ un fichier audio, par exemple un livre nommé “Le tour du monde en quatre-vingts jours” (Collection LibriVox, hébergé sur archive.org, domaine public)

![Audio du livre "Le tour du monde en quatre-vingts jours"](https://www.archive.org/download/tour_du_monde_en_80_jours_librivox/tour_monde_01_verne.mp3 "Le tour du monde en quatre-vingts jours, de Jules Vernes, au format mp3")

Vidéo

⇒ une vidéo, au hasard celle de “Big Buck Bunny” (sous Licence CC 3.0, par la fondation Blender, hébergé sur archive.org)

![Vidéo de "Big Buck Bunny"](https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4 "Big Buck Bunny")

⇒ La même enregistrée localement :

![Vidéo de "Big Buck Bunny", servie localement](/video/big_buck_bunny_720p_surround.mp4 "Big Buck Bunny")

Le navigateur utilisé ne prend pas en charge les vidéos intégrées. Voici le lien pour pouvoir télécharger la vidéo :
/video/big_buck_bunny_720p_surround.mp4

TL;DR

Pour les impatients, allez donc télécharger :

Pour les détails, lisez l’article ! ;)

Remerciements


Voilà !


Enjoy-ID!
Enjoy-IT!