Pular para conteúdo principal

Eterno Devir


Lazy loading de embeds do YouTube

Como estou adicionando muitos vídeos do YouTube à nova seção Web Gems, decidi implementar lazy loading para todos os embeds do YouTube no site. O conceito é simples: em vez de carregar o player completo do YouTube para cada vídeo em uma página, apenas a miniatura do vídeo e um pequeno trecho de CSS para imitar a interface do player são carregados. O player real só é solicitado e carregado se o leitor clicar em "Reproduzir". Isso faz com que as páginas carreguem significativamente mais rápido e economiza largura de banda. Nunca se sabe se a pessoa que está lendo vive em um veleiro onde cada bit de largura de banda importa.

Para isso, utilizei o paulirish/lite-youtube-embed. É um pequeno arquivo JavaScript que lida com a exibição da miniatura e a troca pelo iframe ao clicar em reproduzir, juntamente com o CSS necessário para a réplica do player. Além da simplicidade, o lite-youtube-embed usa youtube-nocookie.com por padrão, então nenhuma alteração extra voltada à privacidade foi necessária. Além do curl -O -L desses dois arquivos, foi apenas uma questão de escrever um shortcode para o Hugo e descobrir uma solução alternativa para suportar playlists.

Os vídeos do YouTube oferecem diferentes qualidades de miniatura, variando de default a maxres. Defini maxres como o valor padrão para poster-quality, já que é mais nítido e torna o uso do shortcode um pouco mais ergonômico. No entanto, um determinado vídeo pode não fornecer maxres, o que exige encontrar a maior qualidade disponível.

Para vídeos, a sintaxe final do shortcode é assim:

{{<
    lite-youtube
    src="https://www.youtube.com/watch?v=kTuLj0Nyhs4"
    poster-quality="sd"
>}}

Para playlists, onde a solução alternativa foi necessária, fica assim:

{{<
    lite-youtube
    src="https://www.youtube.com/playlist?list=PLdJRJcZwR07E0CsTwSAPK-4GdQyVLvyVN"
    poster-id="eY-eyZuW_Uk"
>}}

Embora os arquivos lite-yt-embed.js e lite-yt-embed.css sejam pequenos (3,63 kB e 1,88 kB), seria um desperdício carregá-los em páginas sem vídeos. Por isso, escrevi uma pequena partial para incluir esses dois recursos condicionalmente, apenas quando necessário. Ou seja, apenas páginas ou seções que usam o shortcode lite-youtube irão carregá-los.

<!-- layouts/_default/baseof.html -->
<head>
...
{{ $hasYoutube := partial "conditional-youtube.html" . }}
{{ if $hasYoutube }}
  {{ $ytCSS := resources.Get "lite-yt-embed.css" | minify | fingerprint }}
  {{ $ytJS := resources.Get "lite-yt-embed.js" | minify | fingerprint }}
  <link rel="stylesheet" href="{{ $ytCSS.RelPermalink }}" integrity="{{ $ytCSS.Data.Integrity }}" >
  <script src="{{ $ytJS.RelPermalink }}" defer integrity="{{ $ytJS.Data.Integrity }}"></script>
{{ end }}
...
</head>
<!-- layouts/partials/conditional-youtube.html -->
{{ $hasYoutube := false }}
{{ $listTypes := slice "logs" "web-gems" }}

{{ if .IsPage }}
  {{ $hasYoutube = .HasShortcode "lite-youtube" }}
{{ else if in $listTypes .Type }}
  {{ range .Paginator.Pages }}
    {{ if .HasShortcode "lite-youtube" }}
      {{ $hasYoutube = true }}
      {{ break }}
    {{ end }}
  {{ end }}
{{ end }}

{{ return $hasYoutube }}

Por fim, o shortcode em si. Como mencionei, as playlists exigiram uma pequena adaptação. Como o lite-youtube-embed espera um videoid, forneço um manualmente via poster-id para a miniatura, enquanto o shortcode cuida de passar o ID da playlist como um parâmetro para o iframe.

<!-- layouts/shortcodes/lite-youtube.html -->
{{ $src := .Get "src" }}
{{ $title := .Get "title" }}
{{ $posterQuality := .Get "poster-quality" | default "maxres" }}
{{ $customParams := .Get "params" }}

{{ $url := urls.Parse $src }}
{{ $v := $url.Query.Get "v" }}
{{ $list := $url.Query.Get "list" }}

{{ if not (or $v $list) }}
  {{ erroridf "lite-youtube-invalid-url" "%s: Invalid YouTube URL: %s" .Position $src }}
{{ end }}

{{ $posterId := .Get "poster-id" | default $v }}
{{ $posterUrl := printf "https://i.ytimg.com/vi/%s/%sdefault.jpg" $posterId $posterQuality }}

{{ $queryParams := dict }}
{{ with $v }}{{ $queryParams = merge $queryParams (dict "v" .) }}{{ end }}
{{ with $list }}{{ $queryParams = merge $queryParams (dict "list" .) }}{{ end }}

{{ $path := cond $list "playlist" "watch" }}
{{ $query := querify $queryParams }}
{{ $href := printf "https://www.youtube.com/%s?%s" $path $query }}

{{ $params := slice }}
{{ with $list }}{{ $params = $params | append (printf "list=%s" .) }}{{ end }}
{{ with $customParams }}{{ $params = $params | append . }}{{ end }}
{{ $params = delimit $params "&" }}

<div class="responsive-embed ratio-16-9">
  <lite-youtube
    videoid="{{ $v }}"
    title="{{ $title }}"
    style="background-image: url('{{ $posterUrl }}');"
    {{- with $params }}params="{{ . }}"{{- end }}>
    <a href="{{ $href }}"
       class="lyt-playbtn"
       title="{{ i18n "play" }} YouTube"
       aria-label="{{ i18n "play" }} YouTube {{ $title }}">
    </a>
  </lite-youtube>
</div>

{{ partial "nojs.html" $src }}

∗ ∗ ∗

registros   computação  

Comentar por e-mail ↪

Voltar ao topo ↑