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 }}∗ ∗ ∗