I’ve got 150+ videos on @SupeRails Youtube channel, and now I want to list them on my own superails.com website.

I could copypaste the information manually, but that would take ages.

I could use web scraping, but what’s the point, if there is an easy to use Youtube API?

Youtube API is accessible in Ruby via the gem ‘google-api-client’

1. Generate a Youtube API key

First, access Google Cloud Console. Search for YouTube Data API v3:

youtube-api-find-marketplace

Click to enable the API:

youtube-api-enable-marketplace

Create an API key:

youtube-api-create-key

View Youtube API docs

2. Find a youtube channel ID

To make the API call to list videos, you need to provide a channel ID. Here’s how you can access any channel ID:

youtube-api-find-channel-id

3. API call to list all videos on a channel

Install the gem:

# Gemfile
# gem 'google-api-client' # https://github.com/googleapis/google-api-ruby-client
gem 'google-apis-youtube_v3'

Make a list_searches API call to get up to 50 videos per page form the selected channel.

# rails c
require 'google/apis/youtube_v3'
CHANNEL_ID = 'UCyr6ZTmztFW3FB4qG_97FoA'
YOUTUBE_KEY = 'MySecretKey'
youtube = Google::Apis::YoutubeV3::YouTubeService.new
youtube.key = YOUTUBE_KEY
# list videos and playlists
response = youtube.list_searches('snippet', channel_id: CHANNEL_ID, max_results: 50)
# list only videos
response = youtube.list_searches('snippet', channel_id: CHANNEL_ID, max_results: 5, type: 'video')

This will provide you basic information about up to 50 videos per page.

4. API call to show details of one video

If you want to have more detailed info about a specific video, you should make a list_videos API call while passing the video_id.

# rails c
require 'google/apis/youtube_v3'
CHANNEL_ID = 'UCyr6ZTmztFW3FB4qG_97FoA'
YOUTUBE_KEY = 'MySecretKey'
youtube = Google::Apis::YoutubeV3::YouTubeService.new
youtube.key = YOUTUBE_KEY
video_id = "07XQY8nRvd0"
video_response = youtube.list_videos('snippet', id: video_id)

5. Import Youtube videos into a Rails app

Genarate a Post model, and jobs to make async API calls:

rails g model Post video_id title description:text tags:text published_at:datetime cover_image_url
rails g job Youtube::ListVideosJob
rails g job Youtube::CreateVideoJob

The API call will give us tags as an array, so let’s store them as such:

# app/models/post.rb
  serialize :tags, Array

Job to LIST and Paginate all the videos on a Youtube channel:

# app/jobs/youtube/list_videos_job.rb
require 'google/apis/youtube_v3'
# Youtube::ListVideosJob.perform_later
class Youtube::ListVideosJob < ApplicationJob
  queue_as :default

  CHANNEL_ID = Rails.application.credentials.dig(:youtube, :channel_id)
  YOUTUBE_KEY = Rails.application.credentials.dig(:youtube, :youtube_key)

  def perform
    youtube = Google::Apis::YoutubeV3::YouTubeService.new
    youtube.key = YOUTUBE_KEY

    video_ids = fetch_all_videos(youtube)
    process_videos(video_ids)
  end

  private

  def fetch_all_videos(youtube)
    video_ids = []
    next_page_token = nil

    loop do
      response = youtube.list_searches('snippet', channel_id: CHANNEL_ID, max_results: 50, page_token: next_page_token)

      response_ids = response.items.map { |item| item.id.video_id }.compact
      video_ids += response_ids

      next_page_token = response.next_page_token

      break if next_page_token.nil?
    end

    video_ids
  end

  def process_videos(video_ids)
    video_ids.each do |video_id|
      Youtube::CreateVideoJob.perform_later(video_id)
    end
  end
end

Job to get detailed info for a specific youtube video and create a local record:

# app/jobs/youtube/create_video_job.rb
require 'google/apis/youtube_v3'
# Youtube::CreateVideoJob.perform_later
class Youtube::CreateVideoJob < ApplicationJob
  queue_as :default

  CHANNEL_ID = Rails.application.credentials.dig(:youtube, :channel_id)
  YOUTUBE_KEY = Rails.application.credentials.dig(:youtube, :youtube_key)

  def perform(video_id)
    youtube = Google::Apis::YoutubeV3::YouTubeService.new
    youtube.key = YOUTUBE_KEY

    video_response = youtube.list_videos('snippet', id: video_id)

    video = video_response.items.first.snippet
    video_hash = {
      video_id: video_id,
      title: video.title,
      description: video.description,
      tags: video.tags,
      published_at: video.published_at,
      cover_image_url: video.thumbnails.maxres.url
    }

    Post.find_or_create_by(video_id: video_hash[:video_id]).update(video_hash)
  end
end

6. Display videos in a view:

<% @posts.each do |post| %>
  <h3><%= post.title %></h3>
  <p><%= post.description %></p>
  <iframe width="560" height="315" src="https://www.youtube.com/embed/<%= post.video_id %>" frameborder="0" allowfullscreen></iframe>
  <hr>
<% end %>

Example fixture of a stored video:

one:
  youtube_video_id: "Ubrr9mqE94o"
  title: "Ruby on Rails #27 Gem Letter Opener - best way to preview emails in development"
  description: "gem letter_opener:\nhttps://github.com/ryanb/letter_opener\nSource Code for the Post:\nhttps://github.com/corsego/26-action-mailer/commit/24fb10065fb5c4502b15ea75d651aec8e61413e0\n\nTo fix Launchy error - run these commands in console:\nexport BROWSER=/dev/null\nexport LAUNCHY_DRY_RUN=true"
  tags: ["ruby", "rails", "ruby on rails", "tutorial", "programming"]
  published_at: "2021-05-19T13:00:15Z"
  cover_image_url: "https://i.ytimg.com/vi/Ubrr9mqE94o/maxresdefault.jpg"