[SOLVED] Conditional logic for data-driven content that returns no result

Hello,

I’m trying to construct a data-driven partial template which can serve any kind of embedded video, regardless of the provider, with only a url supplied in front matter. I’m starting with only YouTube and Vimeo, but it would be ideal to add functionality for many of the less prominent video providers as well. So, for example, in front matter a user could paste in any of the following URLs:

and the referenced video will embed with the proper respective player. Before someone immediately replies, “Why not just paste in the video ID?”, please understand i’m going for the lowest common denominator here with respect to end-user behavior. From a usability standpoint, at least for the clients who will be using the sites i’m building, pasting in an entire URL, regardless of the provider, is more feasible than correctly identifying a video ID and then pasting only that into a front matter field.

Using Vimeo’s oEmbed tool, this is pretty straight forward, except for a few strange surprises (more on those some other time). YouTube’s oEmbed tool is a little less robust, but it providers similar functionality.

Here’s how i’ve got things setup so far:

Front matter

video:
  url: "//vimeo.com/10655199"
  description: ""
  title: ""
  another_param: ""
  another_param_you_get_the_idea: ""

In whatever template i want a video

{{ partial "video.html" (dict "url" .Params.video.url ) }}

Partial template

{{ $vimeo := getJSON "https://vimeo.com/api/oembed.json?url=" .url }}
{{ with $vimeo.html }}
  {{ . | safeHTML }}
{{ end }}
{{ $youtube := getJSON "https://www.youtube.com/oembed?url=" .url }}
{{ with $youtube.html }}
  {{ . | safeHTML }}
{{ end }}

By the way, it’s easy and helpful to take a look at the data made available via oEmbed. In my example, one could do so this way:

{{ printf "%#v" $vimeo }}
{{ printf "%#v" $youtube }}

There’s a lot of flexibility and customization that could be achieved with this data, but i’ll save that for future post when i’ve actually got it working!

The problem

I need to write some conditional logic that will not return a build-error when no file is found. If the user pastes in a Vimeo url, then obviously the YouTube request will return file-not-found. And if s/he pastes in a YouTube url, the Vimeo request will return file not found. As i hopefully add additional players, this will only increase. This is the expected behavior since, again, i’m trying to create a one-size-fits-all solution wherein the user only interacts with one front matter param: “video”.

I’ve tried with, if, if ne (to a length of 0 characters), if ne (to nil), etc., but no matter what i get the same can’t-build-the-site error:

error calling getJSON: Failed to retrieve remote file: Not Found

The goal is to cycle through every provider until the url returns a result.

Any thoughts are most appreciated! And here’s an emoji since i’m new here and i want that emoji badge real bad. :burrito:

I think I would use urls.Parse to determine which partial to render.

Thanks @maiki!

This gets me closer, but the fundamental issue is not yet solved. Here’s how my partial template looks now:

{{ $videoUrl := urls.Parse .url }}
{{ if ( findRE "(youtu)" $videoUrl.Host 1 ) }}
  {{ $youtube := getJSON "https://www.youtube.com/oembed?url=" .url }}
  {{ with $youtube.html }}
    {{ . | safeHTML }}
  {{ end }}
{{ else if ( findRE "(vimeo)" $videoUrl.Host 1 ) }}
  {{ $vimeo := getJSON "https://vimeo.com/api/oembed.json?url=" .url }}
  {{ with $vimeo.html }}
    {{ . | safeHTML }}
  {{ end }}
{{ end }}

Where this is successful: Assuming the user entered a correct url (for either a Vimeo or a YouTube video) then the error i described is gone! Hooray!

Where this is still weak:

  1. Regular expressions are, for me, still akin to gnostic magic. I’m confident a much stronger regex could and should be constructed than the ones seen above.
  2. The problem of a site-breaking error is still an issue if the user enters an incorrect url. For example, in the front matter field, if the user enters https://www.youtube.com/watch?v=asxzkUjTMcE, it works as expected. But if s/he enters, say, youtube.com/watch?v=asxzkUjTMcE (omitting the “https://www.”) it again throws up error calling getJSON: Failed to retrieve remote file: Not Found.

Because i can’t rely on users to get the URL correct all the time, i’d still like some kind of conditional logical which can account for user errors without breaking the site build. In this example, i’m attempting to do so using the with function before the getJSON call, but it makes no difference.

{{ $videoUrl := urls.Parse .url }}
{{ if ( findRE "(youtu)" $videoUrl.Host 1 ) }}
  {{ with $youtube := getJSON "https://www.youtube.com/oembed?url=" .url }}
    {{ with $youtube.html }}
      {{ . | safeHTML }}
    {{ end }}
  {{ end }}
{{ else if ( findRE "(vimeo)" $videoUrl.Host 1 ) }}
  {{ with $vimeo := getJSON "https://vimeo.com/api/oembed.json?url=" .url }}
    {{ with $vimeo.html }}
      {{ . | safeHTML }}
    {{ end }}
  {{ end }}
{{ end }}

Thanks again for helping me think through this!

I stumbled on this post after i made this support request:

What i’ll probably end up using is:

since it seems like the most comprehensive solution. But i think i’m always partial to things being done on the generation side, rather than the client side. It feels cleaner. It’d be cool to see this functionality pulled into Hugo some day, if there were sufficient interest in it.

1 Like