Get YouTube Channel videos

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

Screenshot 2025 02 22 At 11.49.50

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);
        });
    });
});