The other day, I was asked to import YouTube videos from a channel on a website I maintain for a small local organization. My idea was to import the ID
s for every videos on that channel so I could built an iframe
element with the right src
attribute. I didn't want to import data from YouTube with every site build and since I'm managing the YouTube channel for the client, I was happy with running a script with every updates (which are not frequent).
Note: in order to use the YouTube API you'll need to setup with Google first. Here's an introduction on how to do that.
I usually find Google API confusing. The documentation is straight forward and thorough enough. It's just that there's so many parameters to go through. In the end, all I need is to get the data and then use it in a template for prerendering. My static site generator (SSG) of choice right now is Eleventy (11ty) which runs on Node. For this project, the JamStack architecture really fits well!
11ty is so flexible and simple. I really love working with this tool!
The code
I could have used a library like Axios, Request (now deprecated) or node-fetch, but my requirement was so simple that I decided to go without dependencies! Node's standard library has a https
module which gives me access to a get
method. Making a get
request with the https
module is quite simple. Here's the code using the stream.pipeline()
method:
youtube-refresh.js
const https = require('https');
const fs = require('fs');
const { pipeline } = require('stream');
//Creating Writable stream to write on _data/youtube.json.
//If the file doesn't exist, it will be created.
const OUTPUT = fs.createWriteStream('./_data/youtube.json');
https.get(`https://www.googleapis.com/youtube/v3/search?...`, res => {
//Piping res (IncomingMessage: Readable) to OUTPUT (Writable).
//The pipeline method wilL manage stream flow and errors!
pipeline(res, OUTPUT, error => {
if(error) return console.error(error.stack);
console.log('Transfering YouTube data is done!');
});
}).on('error', error => console.error(error.stack));
To come up with this, I had to read the documentation and experiment a bit with the Node's Stream
module (v14.5.0 docs). Since the res
returned by the get method extends the stream.Readable
, it felt really natural to use a stream method (in this case the pipeline
method) to manage the response.
Note: unlike the
pipe
method,pipeline
will destroy every streams automatically onerror
!
Finally, when I need to, I would run this script in the project's root directory like this before I build for production with the new data:
command line interface
node youtube-refresh.js
By running this command, I get a new (or refreshed) youtude.json
file in the _data
directory which in 11ty
exposes data globally through the youtube
object! Then, I can use the data to build pages. Here's an example of what I did with the 11ty
built-in pagination
constructor:
videos.njk
---
layout: base-layout.njk
pagination:
data: youtube.items
size: 1
alias: video
addAllPagesToCollections: true
tags: videos
eleventyComputed:
title: Formations {{ video.snippet.title }}
description: "{{ video.snippet.description }}"
publishedat: "{{ video.snippet.publishedAt }}"
permalink: formations/{{ video.snippet.title | lower | slug }}/
---
<main class="post-layout default-blog-theme">
<article>
<header class="article-header">
<h1>{{ video.snippet.title }}</h1>
<p><small>{{ video.snippet.publishedAt | timeFormat }}</small></p>
</header>
<div class="youtube">
<iframe src="https://www.youtube.com/embed/{{ video.id.videoId }}?rel=0" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p>{{ video.snippet.description }}</p>
</article>
</main>
There's a lot going on here. First, I paginate the data in youtube.items
, which is the data contained in _data/youtube.json
, in order to create one page per item. Then, I use addAllPagesToCollections
(new in v0.8.0) to create a collection available through collections.videos
which I will use on a listing page (see below)! I also use eleventyComputed
(new in v0.11.0) and permalink
in order to generate specific title, description, date and url for each page! The timeFormat filter makes date human readable and french canadian locale (only works with Node v13 and up). 😉
Here's the listing page:
formations.njk
---
layout: formations-layout
title: Formations
description: Les formations offertent par l'ADTILCSL!
date: 2019-07-16
tags: pages
pagination:
data: collections.videos
size: 9
alias: videos
reverse: true
permalink: formations/{% if pagination.pageNumber > 0 %}{{ pagination.pageNumber }}/{% endif %}index.html
---
<section class="listing-section">
{% for video in videos %}
{% card
defaultClass = 'listing-card',
title = video.data.title,
detail = video.data.publishedat | timeFormat,
text = video.data.description,
link = video.url | url,
linkText = 'participe à la formation!'
%}
{% endfor %}
</section>
My stack
Software | Version |
---|---|
OS | Linux Mint 18.2 Sonya |
Node | v14.x |
11ty | 0.11.0 |
The end
Thanks for reading! 😀
Top comments (0)