For my needs I was creating it as shortcode with Scripts Organizer.

Replace with your channel ID:
$channel_id = ‘XXXXX’; // Replace with your channel ID
I was limiting it to 10 videos. Remove this if you want all.
if ( count( $videos ) >= 10 ) {
break;
}
I was using this as a slider with https://splidejs.com/ but you can make a grid as well
<?php
/*
Plugin Name: Video Slider
Plugin URI:
Description: A custom video slider plugin for WordPress
Version: 1.0.0
Author:
Author URI:
License: GPL v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: video-slider
*/
// If this file is called directly, abort.
if (!defined('ABSPATH')) {
die;
}
/**
* Fetches the latest YouTube videos using the channel's RSS feed.
*
* @param string $channel_id YouTube channel ID.
* @return array List of videos.
*/
function yt_fetch_videos_rss( $channel_id ) {
$rss_url = "https://www.youtube.com/feeds/videos.xml?channel_id={$channel_id}";
$response = wp_remote_get( $rss_url );
if ( is_wp_error( $response ) ) {
error_log( 'YouTube RSS request failed: ' . $response->get_error_message() );
return [];
}
$body = wp_remote_retrieve_body( $response );
$xml = simplexml_load_string( $body );
if ( ! $xml || ! isset( $xml->entry ) ) {
error_log( 'Invalid YouTube RSS response' );
return [];
}
$videos = [];
foreach ( $xml->entry as $entry ) {
$video_id = (string) $entry->children('yt', true)->videoId;
// Check for available thumbnail resolution
$thumbnail_url = yt_get_best_thumbnail($video_id);
$videos[] = [
'title' => (string) $entry->title,
'videoId' => $video_id,
'thumbnail' => $thumbnail_url
];
if ( count( $videos ) >= 10 ) {
break;
}
}
return $videos;
}
/**
* Gets the best available thumbnail for a YouTube video
*
* @param string $video_id YouTube video ID
* @return string URL of the best available thumbnail
*/
function yt_get_best_thumbnail($video_id) {
$resolutions = array(
'maxresdefault.jpg', // 1920x1080
'sddefault.jpg', // 640x480
'hqdefault.jpg', // 480x360
'mqdefault.jpg', // 320x180
'default.jpg' // 120x90
);
foreach ($resolutions as $resolution) {
$url = "https://img.youtube.com/vi/{$video_id}/{$resolution}";
$response = wp_remote_head($url);
// Check if the image exists and returns a 200 status code
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
return $url;
}
}
// If all else fails, return the default thumbnail
return "https://img.youtube.com/vi/{$video_id}/default.jpg";
}
/**
* Shortcode to display YouTube videos list.
*
* Usage: [videos_list]
*
* @param array $atts Shortcode attributes.
* @return string HTML output of the videos list.
*/
function yt_videos_list_shortcode( $atts ) {
$channel_id = 'XXXXX'; // Replace with your channel ID
$videos = yt_fetch_videos_rss( $channel_id );
if ( empty( $videos ) ) {
return '<p>No videos available.</p>';
}
// Initialize output with Splide structure
$output = '<div class="splide">';
$output .= '<div class="splide__track">';
$output .= '<ul class="splide__list">';
foreach ( $videos as $index => $video ) {
$output .= '<li class="splide__slide">';
$output .= '<div class="yt-video-thumbnail" data-video-id="' . esc_attr( $video['videoId'] ) . '">';
$output .= '<a href="https://www.youtube.com/watch?v=' . esc_attr( $video['videoId'] ) . '" target="_blank">';
$output .= '<img src="' . esc_url( $video['thumbnail'] ) . '" alt="' . esc_attr( $video['title'] ) . '"/>';
$output .= '</a>';
// $output .= '<h3>' . esc_html( $video['title'] ) . '</h3>';
$output .= '</div>';
$output .= '</li>';
}
$output .= '</ul>';
$output .= '</div>'; // Close splide__track
$output .= '</div>'; // Close splide
return $output;
}
add_shortcode( 'videos_list', 'yt_videos_list_shortcode' );
/**
* Enqueue scripts and styles for the video slider
*/
function yt_videos_enqueue_assets() {
// Enqueue Splide CSS
wp_enqueue_style(
'splide-css',
'https://cdn.jsdelivr.net/npm/@splidejs/splide@4.1.4/dist/css/splide.min.css',
array(),
'4.1.4'
);
// Enqueue Splide JavaScript
wp_enqueue_script(
'splide-js',
'https://cdn.jsdelivr.net/npm/@splidejs/splide@4.1.4/dist/js/splide.min.js',
array(),
'4.1.4',
true
);
}
add_action( 'wp_enqueue_scripts', 'yt_videos_enqueue_assets' );
$tablet: 768px;
$laptop: 1280px;
$desktop: 1400px;
@mixin tablet {
@media (min-width: #{$tablet}) {
@content;
}
}
@mixin laptop {
@media (min-width: #{$laptop}) {
@content;
}
}
@mixin desktop {
@media (min-width: #{$desktop}) {
@content;
}
}
.splide__arrows.splide__arrows--ltr {
position: relative;
z-index: 10;
top: 23vw;
height: 1px;
width: 80vw;
left: 10vw;
@include desktop {
top: 17vw;
width: 60vw;
left: 20vw;
}
}
.splide__arrow {
background-color: white;
border-radius: 0;
opacity: 1 !important;
min-width: 60px !important;
min-height: 60px !important;
@include tablet {
min-width: 80px !important;
min-height: 80px !important;
}
&--next {
right: 0;
left: unset;
}
&--prev {
left: 0;
}
&:disabled{
display: none !important;
}
}
.yt-video-thumbnail {
width: 100%;
position: relative;
img {
width: 100%;
height: auto;
display: block;
aspect-ratio: 16 / 9;
object-fit: cover;
}
}
.splide__slide {
&.is-active .yt-video-thumbnail {
transform: scale(1);
transform-origin: center;
transition: all 0.3s ease-in-out;
z-index: 2;
}
&.is-next .yt-video-thumbnail,
&.is-prev .yt-video-thumbnail, {
transform: scale(0.8);
transform-origin: left;
transition: all 0.3s ease-in-out;
opacity: 0.4;
z-index: 1;
pointer-events: none;
}
&.is-prev .yt-video-thumbnail {
transform-origin: right;
}
}
document.addEventListener('DOMContentLoaded', function () {
new Splide('.splide', {
type: 'slide',
perPage: 1,
perMove: 1,
focus: 'center',
padding: { left: '20vw', right: '20vw' }, // Use vw units
gap: '1rem',
updateOnMove: true,
breakpoints: {
1400: {
padding: { left: '10vw', right: '10vw' }, // Use vw units
},
768: {
padding: { left: '10vw', right: '10vw' }, // Use vw units
},
576: {
padding: { left: '5vw', right: '5vw' }, // Use vw units
}
},
pagination: false,
arrows: true
}).mount();
// Handle video thumbnail clicks
document.querySelectorAll('.yt-video-thumbnail').forEach(thumbnail => {
thumbnail.addEventListener('click', function () {
const videoId = this.dataset.videoId;
// Add your video player implementation here
console.log('Video clicked:', videoId);
});
});
});