Hugo: Improvements to internal YouTube shortcode

Created on 11 Jul 2017  路  16Comments  路  Source: gohugoio/hugo

The built-in Hugo shortcode for embedding YouTube videos obscures captions when the user is not interacting with the video. This is a simple CSS issue related to overflow: hidden; being included inline in the go template. I removed this CSS property in Chrome Dev Tools and everything works as expected.

overflow: hidden;

overflow-hidden

Removed overflow:hidden;

overflow-not-hidden

From GitHub:

<div {{ if .Get "class" }}class="{{ .Get "class" }}"{{ else }}style="position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; **overflow: hidden;**"{{ end }}>
  <iframe src="//www.youtube.com/embed/{{ .Get "id" }}?{{ with .Get "autoplay" }}{{ if eq . "true" }}autoplay=1{{ end }}{{ end }}" 
  {{ if not (.Get "class") }}style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" {{ end }}allowfullscreen frameborder="0"></iframe>
</div>

Enhancement

Most helpful comment

@rhewitt22 Here is the shortcode that will permit a start and stop timecode for the video. The shortcode shortcut requires that the parameters be in order and each previous one filled or else it won't work. It's not robust, but that part is a bit beyond my skill at the moment.

{{ if or (eq (index .Params 1) "widescreen") (not (index .Params 1) ) }}
    {{ $.Scratch.Add "ratio" 56.25 }}
{{ else if eq (index .Params 1) "standard" }}
    {{ $.Scratch.Add "ratio" 75 }}
{{ else }}
    {{ $nums := split (index .Params 1) ":" }}
    {{ $.Scratch.Add "ratio" (div (mul (int (index $nums 1)) 100.0) (int (index $nums 0))) }}
{{ end }}

{{ if (index .Params 2) }}
    {{ $.Scratch.Add "start" (add "?start=" (index .Params 2)) }}
{{ else }}
    {{ $.Scratch.Add "start" "" }}
{{ end }}

{{ if (index .Params 3) }}
    {{ $.Scratch.Add "end" (add "&end=" (index .Params 3)) }}
{{ else }}
    {{ $.Scratch.Add "end" "" }}
{{ end }}

<div style="position: relative; padding-bottom: {{ $.Scratch.Get "ratio" }}%; overflow: hidden;">
    <iframe style="position: absolute; width: 100%; height: 100%;"
        src="http://www.youtube.com/embed/{{ index .Params 0 }}{{ $.Scratch.Get "start" }}{{ $.Scratch.Get "end" }}" allowfullscreen frameborder="0">
    </iframe>
</div>

The entry would be thus:

{{< youtube B7zFRSVmN1w "16:9" 30 35 >}}

It works well.

All 16 comments

@rhewitt22 Out of curiosity, did you try this on multiple videos with different aspect ratios?

@rdwatters Everything our videographer handed off to me is 1920x1080 (16:9), which is pretty standard these days. If I were to use another resolution it would be 720p, which is also 16:9. I just tried this on a second video with the same resolution and had (unsurprisingly) the same result.

For now I'm just pasting in the shortcode html into my content and removing the inline overflow property.

For now I'm just pasting in the shortcode html into my content and removing the inline overflow property.

We will fix this eventually, but It would be more effectie for you to paste the Hugo youtube shortcode into your layouts/shortcodes and make your own correct version.

Thanks for the suggestion. That makes things much simpler.

@rhewitt22 @bep I was looking into this because I was experiencing issues with the container not being sized correctly. There is definitely something incorrect with the style parameters of the container and the iframe. After some research and help from Jonathan on the Hugo forum, I saw how Zurb Foundation was doing it, and saw how Hugo internal shortcode was doing it, and saw the difference. I took Zurb's style settings, applied them in the same way (inline) into a custom shortcode as Hugo does in its internal shortcode. The result works perfectly and as expected is completely responsive. The captions are correctly located also. The working code is below:

<div style="position: relative; padding-bottom: 56.25%; overflow: hidden;">
    <iframe style="position: absolute; width: 100%; height: 100%;"
        src="http://www.youtube.com/embed/{{ index .Params 0 }}" allowfullscreen frameborder="0">
    </iframe>
</div>

I have this as a custom YouTube shortcode for now but of course it should be fixed in the internal shortcode. Replacing the current style parameters with these will do that. Should I submit a PR?

Furthermore, Zurb's CSS had a setting whereby, without specifying the aspect ratio of the video, it would default to standard (4:3) but if you set the parameter of widescreen it would change appropriately. The switch was in the padding-bottom: parameter, where standard was 75% and widescreen was 56.25%. This may be a worthwhile parameter to include in the internal shortcode... something like:

{{< youtube Pt9MjE70Fd8 standard >}}

but would default to widescreen if standard was not given. @rdwatters this would address your comment about different aspect ratios, but what I've laid out here only pertains to 16:9 and 4:3. If there were another aspect ratio (which I have to imagine is probably pretty rare, I guess there is anamorphic and some other types but not common) then maybe some logic for an input ratio and the shortcode takes that, does the math, and determines what the padding-bottom should be.

Also, there is the option of including another parameter to give the start timecode position of the video. Not sure how best to handle that, but I just wanted to throw that in there for another useful option. Maybe the 1st .Params would be the video link code, 2nd would be the ratio (standard or widescreen), 3rd would be the start-time in seconds:

{{< youtube Pt9MjE70Fd8 widescreen 45 >}}

EDIT 1
I've been hacking away and I've got a solution that will allow the user to use any video display ratio they want, given a standard set of inputs.

The code is here:

{{ if or (eq (index .Params 1) "widescreen") (not (index .Params 1) ) }}
    {{ $.Scratch.Add "ratio" 56.25 }}
{{ else if eq (index .Params 1) "standard" }}
    {{ $.Scratch.Add "ratio" 75 }}
{{ else }}
    {{ $nums := split (index .Params 1) ":" }}
    {{ $.Scratch.Add "ratio" (div (mul (int (index $nums 1)) 100.0) (int (index $nums 0))) }}
{{ end }}
<div style="position: relative; padding-bottom: {{ $.Scratch.Get "ratio" }}%; overflow: hidden;">
    <iframe style="position: absolute; width: 100%; height: 100%;"
        src="http://www.youtube.com/embed/{{ index .Params 0 }}" allowfullscreen frameborder="0">
    </iframe>
</div>

This allows the user to leave .Param 1 blank to give a 16:9 format (basically default). If the user puts widescreen then it will be 16:9. If the user puts standard it will be 4:3. If the user puts _anything_ else, it MUST be in the format of "X:Y" where X and Y are integers (not floating point numbers). So the user could put "16:9" to get a 16:9 ratio, or "4:3" to get the standard ratio, or "235:100" to get 2.35:1 ratio. The numbers have to be integers (just multiply both sides equally by orders of magnitude until they are both integers) and they have to be separated by a colon, and they have to be in double-quotes. Given those constraints, they can render a YouTube video in any ratio they want.

If there were some way to take a string made of a floating point value and convert it directly into a floating point value, that'd be simpler, but I found no such function in Hugo.

EDIT 2
HA! This even works with vertical videos using a ratio of "9:16"

{{< youtube 17uHCHfgs60 "9:16" >}}

I'm super embarrassed to admit I had a stray CSS selector that was causing the issue with the youtube captions. That said with those rules removed there is still the issue of black bars at the top and bottom of the YouTube embedded video. Given the other suggestions from @gaetawoo perhaps this issue should be renamed? Maybe Improvements to internal YouTube shortcode?

Sorry for the mixup.

@rhewitt22 Here is the shortcode that will permit a start and stop timecode for the video. The shortcode shortcut requires that the parameters be in order and each previous one filled or else it won't work. It's not robust, but that part is a bit beyond my skill at the moment.

{{ if or (eq (index .Params 1) "widescreen") (not (index .Params 1) ) }}
    {{ $.Scratch.Add "ratio" 56.25 }}
{{ else if eq (index .Params 1) "standard" }}
    {{ $.Scratch.Add "ratio" 75 }}
{{ else }}
    {{ $nums := split (index .Params 1) ":" }}
    {{ $.Scratch.Add "ratio" (div (mul (int (index $nums 1)) 100.0) (int (index $nums 0))) }}
{{ end }}

{{ if (index .Params 2) }}
    {{ $.Scratch.Add "start" (add "?start=" (index .Params 2)) }}
{{ else }}
    {{ $.Scratch.Add "start" "" }}
{{ end }}

{{ if (index .Params 3) }}
    {{ $.Scratch.Add "end" (add "&end=" (index .Params 3)) }}
{{ else }}
    {{ $.Scratch.Add "end" "" }}
{{ end }}

<div style="position: relative; padding-bottom: {{ $.Scratch.Get "ratio" }}%; overflow: hidden;">
    <iframe style="position: absolute; width: 100%; height: 100%;"
        src="http://www.youtube.com/embed/{{ index .Params 0 }}{{ $.Scratch.Get "start" }}{{ $.Scratch.Get "end" }}" allowfullscreen frameborder="0">
    </iframe>
</div>

The entry would be thus:

{{< youtube B7zFRSVmN1w "16:9" 30 35 >}}

It works well.

Embeds now support some other useful features, such as hiding the video title, payer controls and (my favorite) an enhanced privacy mode, shown here checked:

screen shot 2017-10-11 at 5 17 28 pm

Also, here's a link to the iFrame Player API in case you want to play around with the different settings and see which events are fired: https://developers.google.com/youtube/youtube_player_demo

Regarding responsive iframe embeds, it would be good to tackle Vimeo while this is being addressed and tee up a generic iframe for things like coub. Advise not getting too complex in Hugo as the solve for this long-standing issue is at the CSS spec level.

Here's an elegant solution for 16:9:

<style>
.wrapper {
    position: relative;
    padding-bottom: 56.25%; /* 16:9 */
    padding-top: 25px;
    height: 0;
}
.wrapper iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
</style>
<div class="wrapper">
  <iframe src="//coub.com/embed/bwdx6?muted=false&autostart=false&originalSize=true&startWithHD=true" allowfullscreen="true" frameborder="0"></iframe>
</div>

Source: Making Embedded Content Work In A Responsive iFrame

The time has to be seconds. The user shouldn't have to worry about that.

{{$mins := (.Get "m")}}
{{$secs := (.Get "s")}}
{{if and $mins $secs}}
    {{$time := add (mul (int $mins) 60) (int $secs)}}
    {{$.Scratch.Add "start" (add "?start=" (string $time))}}
{{else}}
    {{$.Scratch.Add "start" ""}}
{{end}}

@p-groarke I think it's more straight-forward to input with seconds, especially for Hugo users, it's not really much of an ask to convert minutes in to seconds and add. But one could always make a shortcode that takes an argument like "4:31" and parses that as 4 minutes and 31 seconds and does what you do in your code to turn it into seconds. I think the built-in youtube shortcode probably shouldn't be comprehensive and make usable every option for a video player. That's what custom shortcodes are for since most people are going to want the basic functions. My 2cents anyway.

This issue has been automatically marked as stale because it has not had recent activity. The resources of the Hugo team are limited, and so we are asking for your help.
If this is a bug and you can still reproduce this error on the master branch, please reply with all of the information you have about it in order to keep the issue open.
If this is a feature request, and you feel that it is still relevant and valuable, please tell us why.
This issue will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.

Just an FYI, I am excited to see a robust youtube short code added to Hugo, as I recently ran into some problems with the default. I'm still chugging along at the moment, but will switch back to the default when it becomes available/robust.

Another enhancement which I think would be worthwile would be the ability to embed the video as a thumbnail linked to the player, not the player itself. This is something which has been requested more and more by clients on CMS projects, and I would like to have this for my Hugo sites too.

It would be super awesome to be able to specify a start and end time for a video

@niemyjski
There's an open PR to specify a start and end time for a video. See https://github.com/gohugoio/hugo/pull/7365.

Was this page helpful?
0 / 5 - 0 ratings