The code below works with multiple channels.
First, we need to loop through all channels and get all videos.
function get_channel_videos( $channel_ids, $api_key ) {
$api_url = 'https://www.googleapis.com/youtube/v3/search';
$api_key = 'your-api-key-here';
$max_results = 100;
$videos = [];
foreach ( $channel_ids as $channel_id ) {
$url = "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=$channel_id&maxResults=$max_results&order=date&key=$api_key";
$response = file_get_contents( $url );
$data = json_decode( $response );
foreach ( $data->items as $item ) {
if ( $item->id->kind === 'youtube#video' ) {
$video_id = $item->id->videoId;
$title = $item->snippet->title;
$thumbnail = '';
if ( isset( $item->snippet->thumbnails->maxres ) ) {
$thumbnail = $item->snippet->thumbnails->maxres->url;
} elseif ( isset( $item->snippet->thumbnails->standard ) ) {
$thumbnail = $item->snippet->thumbnails->standard->url;
//} elseif ( isset( $item->snippet->thumbnails->high ) ) {
// $thumbnail = $item->snippet->thumbnails->high->url;
} elseif ( isset( $item->snippet->thumbnails->medium ) ) {
$thumbnail = $item->snippet->thumbnails->medium->url;
}
$videos[] = [
'videoId' => $video_id,
'title' => $title,
'thumbnail' => $thumbnail,
];
}
}
}
return $videos;
}
The code is pretty easy to understand. Note how I commented out the high resolution thumbnail, as it has a letterbox.
The black lines (letterbox) appearing at the top and bottom of the “high” resolution thumbnail can occur due to the aspect ratio difference between the original video and the thumbnail size. YouTube generates thumbnails with specific dimensions, and if the video’s aspect ratio doesn’t match the thumbnail dimensions perfectly, black bars may be added to fill the empty space.
The next step is to iterate through the array and display all videos. As a bonus, I have added a JavaScript lightbox, so that videos open on the same page, without leaving the site.
See the code below, first wrapped as a WordPress plugin (one file) and second as custom PHP. The WordPress plugin can be extended with options and various settings.
WordPress
<?php
/**
* Plugin Name: Saturn - YouTube Feeds
* Plugin URI: https://getbutterfly.com/
* Description: YouTube feed fetcher
* Version: 1.0.0
* Author: Ciprian Popescu
* Author URI: https://getbutterfly.com/
* License: GNU General Public License v3 or later
* License URI: https://www.gnu.org/licenses/gpl-3.0.html
*/
function saturn_get_channel_videos( $channel_ids, $api_key ) {
$api_url = 'https://www.googleapis.com/youtube/v3/search';
$api_key = 'your-api-key-here';
$max_results = 100;
$videos = [];
foreach ( $channel_ids as $channel_id ) {
$url = "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=$channel_id&maxResults=$max_results&order=date&key=$api_key";
$response = file_get_contents( $url );
$data = json_decode( $response );
foreach ( $data->items as $item ) {
if ( $item->id->kind === 'youtube#video' ) {
$video_id = $item->id->videoId;
$title = $item->snippet->title;
$thumbnail = '';
if ( isset( $item->snippet->thumbnails->maxres ) ) {
$thumbnail = $item->snippet->thumbnails->maxres->url;
} elseif ( isset( $item->snippet->thumbnails->standard ) ) {
$thumbnail = $item->snippet->thumbnails->standard->url;
//} elseif ( isset( $item->snippet->thumbnails->high ) ) {
// $thumbnail = $item->snippet->thumbnails->high->url;
} elseif ( isset( $item->snippet->thumbnails->medium ) ) {
$thumbnail = $item->snippet->thumbnails->medium->url;
}
$videos[] = [
'videoId' => $video_id,
'title' => $title,
'thumbnail' => $thumbnail,
];
}
}
}
return $videos;
}
function saturn_feed_youtube() {
$api_url = 'https://www.googleapis.com/youtube/v3/search';
$api_key = 'your-api-key-here';
$max_results = 100;
$channel_ids = [
'channel-id-1',
'channel-id-2',
'channel-id-3',
'channel-id-4',
];
$out = '<div id="lightbox-container">
<div id="lightbox-video"></div>
<button id="lightbox-close-btn">X</button>
</div>
<div class="saturn-feed--wrapper">';
$videos = saturn_get_channel_videos( $channel_ids, $api_key );
foreach ( $videos as $video ) {
$video_id = $video['videoId'];
$title = $video['title'];
$thumbnail = $video['thumbnail'];
$out .= "<a href='#' class='video-thumbnail' data-video-id='$video_id'>
<img src='$thumbnail' alt='$title'>
</a>";
}
$out .= '</div>
<script>
const thumbnails = document.querySelectorAll(".video-thumbnail");
thumbnails.forEach((thumbnail) => {
thumbnail.addEventListener("click", function(event) {
event.preventDefault();
const videoId = this.dataset.videoId;
const videoUrl = `https://www.youtube.com/embed/${videoId}`;
const lightboxContainer = document.getElementById("lightbox-container");
const lightboxVideo = document.getElementById("lightbox-video");
lightboxVideo.innerHTML = `<iframe width="560" height="315" src="${videoUrl}" frameborder="0" allowfullscreen></iframe>`;
lightboxContainer.style.display = "flex";
});
});
const lightboxContainer = document.getElementById("lightbox-container");
lightboxContainer.addEventListener("click", function(event) {
// Check if the clicked element is the lightbox container or its descendant elements
if (
event.target === lightboxContainer ||
event.target === lightboxCloseBtn ||
lightboxContainer.contains(event.target)
) {
lightboxContainer.style.display = "none";
const lightboxVideo = document.getElementById("lightbox-video");
lightboxVideo.innerHTML = "";
}
});
const lightboxCloseBtn = document.getElementById("lightbox-close-btn");
lightboxCloseBtn.addEventListener("click", function() {
lightboxContainer.style.display = "none";
const lightboxVideo = document.getElementById("lightbox-video");
lightboxVideo.innerHTML = "";
});
</script>';
return $out;
}
add_shortcode( 'saturn-feed-youtube', 'saturn_feed_youtube' );
Custom PHP + JavaScript
If you prefer the custom PHP version, just remove the header comments and the last function – add_shortcode()
– and you are good to go. Use the saturn_feed_youtube()
function to return the videos wherever you need them.
CSS
I have added the CSS code separately, as it can be added to your main stylesheet.
#lightbox-container {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 70%;
height: auto;
max-width: 800px;
background-color: rgba(0, 0, 0, 0.8);
display: none;
z-index: 9999;
}
#lightbox-video {
position: relative;
width: 100%;
padding-bottom: 56.25%;
}
#lightbox-video iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#lightbox-close-btn {
position: absolute;
top: 10px;
right: 10px;
font-size: 20px;
color: #fff;
background-color: transparent;
border: none;
cursor: pointer;
z-index: 1;
}
.saturn-feed--wrapper {
display: flex;
flex-wrap: wrap;
gap: 2em;
}
.saturn-feed--wrapper > a {
width: 31.33%;
}
.saturn-feed--wrapper > a img {
max-width: 100%;
height: 100%;
object-fit: cover;
}