YouTube Data APIで登録チャンネルのリストを取得する

## はじめに
ポートフォリオにて、YouTubeで自分が登録しているチャンネル一覧を取得する機能を実装しようとした際に、参考になる記事に出会えず、なかなか苦労したので本記事を作成しました。

本記事では登録チャンネルの一覧取得のみを取り上げていますが、応用することで[YouTube Data API](https://developers.google.cn/youtube/v3/docs?hl=ja)のリファレンスで**このパラメータは、適切に承認されたリクエストでのみ使用できます。**という記述のあるフィルタなどを利用することができるかと思います！

## 環境
Ruby:3.0.2
Rails:6.0.4

##

## 1.Google APIでプロジェクトを作成したりする
### プロジェクトの作成
①[こちら](https://console.cloud.google.com/apis/dashboard)からAPIのダッシュボードに移動します。
②「プロジェクトを作成」から、プロジェクトの作成します。
③「プロジェクト」に任意のプロジェクト名を入力し、「作成」。

### OAuth同意画面の編集 
①左側のメニューから「OAuth同意画面」を開き、「外部」を選択して「作成」。
②「① OAuth認証画面」では、必須項目にそれぞれ入力後、「保存して次へ」。
③「② スコープ」では、まず「スコープを追加または削除」から、範囲`.../auth/userinfo.email`にチェックをつけて「更新」し、「保存して次へ」。
④「③ テストユーザー」では、[+ADD USERS]で自身のメールアドレスを入力して[追加]し、[保存して次へ]。
⑤「④ 概要」はこれまでの内容が表示されるだけなので、これにてOAuth同意画面の編集は完了です。

### OAuthクライアントIDの作成
①画面上部の[+ 認証情報を作成]から、[OAuthクライアントID]を選択します。
②[アプリケーションの種類]でウェブアプリケーションを選択する。[名前]は任意のものでOK。
③続けて、「承認済みのリダイレクトURI」の[+ URIを追加]で`http://localhost:3000/auth/google_oauth2/callback`と入力します。
④[作成]を押すと、クライアントIDとクライアントシークレットが表示されるので、メモ帳などにコピペしておきます。 

### ライブラリの有効化
左側のメニューから「ライブラリ」を開き、以下のライブラリを検索して、それぞれ有効化しておきます。

* Google+ API
* YouTube Data API v3

## 2.必要なgemをインストールする
`rails new`でアプリケーションを作成したら、以下のgemをGemfileに追加して、`bundle install`します。

```ruby:Gemfile
#環境変数用
gem 'dotenv-rails'
#Googleログイン用
gem 'omniauth-google-oauth2'
gem 'omniauth-rails_csrf_protection'
#YouTubeデータ取得用
gem 'google-apis-youtube_v3'
```
また、`bundle exec rails db:create`でDBを作成しておきましょう。

### 3.Googleログインの実装

①`.env`を作成し、環境変数を記述

```ruby:.env
GOOGLE_CLIENT_ID='OAuthクライアントID作成時に表示されたクライアントID'
GOOGLE_CLIENT_SECRET='OAuthクライアントID作成時に表示されたクライアントシークレット'
```
②認証用の設定ファイルを作成
`/config/initializers`内に、`omniauth.rb`を作成し、以下のように記述します。

```ruby:omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2,
            ENV['GOOGLE_CLIENT_ID'],
            ENV['GOOGLE_CLIENT_SECRET']
end
```

③Userモデルを生成

```
$ bundle exec rails g model User name email avatar token refresh_token
$ bundle exec rails db:migrate
```

Userモデルの各カラムはstring型としているので、型指定を省いています。

④ログイン周りの処理を実装

```
bundle exec rails g controller sessions
```

```ruby:sessions_controller.rb
class SessionsController < ApplicationController
  skip_before_action :check_logged_in, only: :create

  def create
    if user = User.find_or_create_from_auth_hash(auth_hash)
      log_in user
    end
    redirect_to root_path
  end

  def destroy
    log_out
    redirect_to root_path
  end

  private

  def auth_hash
    request.env['omniauth.auth']
  end
end

```

```ruby:sessions_helper.rb
module SessionsHelper
  def current_user
    return unless (user_id = session[:user_id])

    @current_user ||= User.find_by(id: user_id)
  end

  def log_in(user)
    session[:user_id] = user.id
  end

  def log_out
    session.delete(:user_id)
    @current_user = nil
  end
end
```
```ruby:application_cotroller.rb
class ApplicationController < ActionController::Base
  include SessionsHelper
  before_action :check_logged_in

  def check_logged_in
    return if current_user

    redirect_to root_path
  end
end
```

⑤Home画面周りの実装

```
bundle exec rails g controller home index
```

```ruby:home_controller.rb
class HomeController < ApplicationController
  skip_before_action :check_logged_in, only: :index

  def index; end
end
```

⑥ルーティングを設定

```ruby:routes.rb
Rails.application.routes.draw do
  root 'home#index'

  get 'auth/:provider/callback', to: 'sessions#create'
  get 'auth/failure', to: redirect('/')
  get 'log_out', to: 'sessions#destroy', as: 'log_out'

  resources :sessions, only: %i[create destroy]
end
```

⑦Userモデルにメソッドを記述

```ruby:user.rb
class User < ApplicationRecord
  #クラスメソッドとして定義
  class << self
    def find_or_create_from_auth_hash(auth_hash)
      user_params = user_params_from_auth_hash(auth_hash)
      #emailをキーとして検索し、重複するものがあればユーザーを作成・更新しない
      find_or_create_by(email: user_params[:email]) do |user|
        user.update(user_params)
      end
    end

    private

    #googleログイン時に渡されるauth_hashから、必要な情報を各カラムに格納
    def user_params_from_auth_hash(auth_hash)
      {
        name: auth_hash.info.name,
        email: auth_hash.info.email,
        avatar: auth_hash.info.image,
        token: auth_hash.credentials.token,
        refresh_token: auth_hash.credentials.refresh_token
      }
    end
  end
```

`token`属性には、登録チャンネルのリストを取得する際に必要な**アクセストークン**が格納されます。
`refresh_token`属性には、上記のアクセストークンを更新する際に必要な**リフレッシュトークン**が格納されるのですが、本記事では扱いません…
（アクセストークンの有効期限が1時間なので、時限(Rakeタスクなど？)でリフレッシュトークンを用いた更新をしてあげると良いかと思います…）

⑧ビューにログイン・ログアウト用のリンクを作成

```ruby:home/index.html.erb
<% if current_user %>
  <%= image_tag current_user.avatar %>
  <%= current_user.name %>さん
  <%= link_to "ログアウト", log_out_path %>
<% else  %>
  <%= link_to "Googleでログイン", "/auth/google_oauth2", method: :post %>
<% end %>
```

ここまでで、Googleログインの実装は完了です。
なお、ここまでの実装については、[こちらの記事](https://zenn.dev/batacon/articles/e9b4a88ede2889)を参考にさせていただきました！

### 4.登録チャンネルのリストを取得する
①モデルを作成する

```
bundle exec rails g model Subscription
```

```ruby:subscription.rb
class Subscription < ApplicationRecord
  require 'google/apis/youtube_v3'

  def channels(user)
    service = Google::Apis::YoutubeV3::YouTubeService.new
    service.authorization =
      Signet::OAuth2::Client.new(
        access_token: user.token
      )

    service.list_subscriptions(:snippet, mine: true, max_results: 10)
  end

end
```

`list_subscriptions`メソッドに利用できる引数は[こちら](https://github.com/googleapis/google-api-ruby-client/blob/6f4030d4c883d167ce739d748699c9238e4b84f9/generated/google-apis-youtube_v3/lib/google/apis/youtube_v3/service.rb)で確認できます。

②コントローラーを作成する

```
bundle exec rails g controller subscriptions index
```

```ruby:subscriptions_controller.rb
class SubscriptionsController < ApplicationController

  def index
    @subscription = Subscription.new
    @user = current_user
    @subscription_channels = @subscription.channels(@user)
  end

end

```

③ルーティングを追記する

```ruby:routes.rb
resources :subscriptions, only: %i[index]
```

④ビューを編集する

```ruby:views/subscriptions/index.html.erb
<div>
  <% if current_user %>
    <%= image_tag current_user.image %>
    <%= current_user.name %>さん
    <%= link_to "ログアウト", log_out_path %>
  <% end %>
<div>

<div>
  <% @subscription_channels.items.each do |item| %>
    <% channel_icon = item.snippet.thumbnails.medium.url %>
    <img src='<%= channel_icon %>'>
    <p><%= item.snippet.title %></p>
  <% end %>
<div>
```
インスタンス変数(@subscription_channels)に格納された属性から、必要なものを引っ張り出して表示しています。
なお、ここでは、以下の内容を表示させています。
・チャンネルのアイコン(`item.snippet.thumbnails.medium.url`)
・チャンネルのタイトル(`item.snippet.title`)

⑤seesionsコントローラーのcreateアクションを編集する

```ruby:sessions_controller.rb
def create
  if user = User.find_or_create_from_auth_hash(auth_hash)
    user.update(token: auth_hash.credentials.token, refresh_token: auth_hash.credentials.refresh_token) # 追記
    log_in user
  end
    redirect_to subscriptions_path #リダイレクト先を変更
end

```

なお、`localhost:3000/subscriptions`で表示される画面は以下のような感じになります。
実際には、`channels`メソッド内で`list_subscriptions`に引数として`max_results: 10`を渡しているので、あと8件ほど(計10件)の登録チャンネルが表示されています。
![localhost:3000/subscriptions](https://i.gyazo.com/24a1f736e98a8e7595fc7b387c1b35c7.png)

## 終わりに
最後まで目を通していただき、ありがとうございました。
ポートフォリオに組み込む前にお試しでやってみたもので、色々といけてない部分も目立つかと思いますが、参考になる部分があれば幸いです。