【Rails】YoutubeとTwitterをAPIを使わずに記事に埋め込む
実装したいこと
記事投稿アプリで、YoutubeやTwitterを記事に埋め込みたい。
URLを入力したら、埋め込み用に変換して、記事に反映させたい。
Slim記法が苦手で、HTMLに反映させるのに手こずってしまいました。。便利ツールを使ってようやくできた。(後述)
データの流れ
- ユーザーがYoutubeかTwitterを選択し、入力フォームにURLを入力する。
- ユーザーの入力した値を受け取り、保存する。
- ユーザーが入力した値を記事に埋め込める形式に変換する。
- Viewに反映される。
実装の流れ
- ユーザーの入力値を受け取るカラムを用意する。
- 入力フォームを用意する。
- 埋め込みに対応してくれるhtmlを返すviewを用意する。
- 受け取ったURLを変換するメソッドを準備する。
カラム準備
ユーザーが入力した値を保存するためのカラムを用意します。
embed_type
でYoutubeかTwitterを選択します。
identifier
でURLを保存します。
class Embed < ApplicationRecord enum embed_type: { youtube: 0, twitter: 1 } validates :identifier, length: { maximum: 200 } end
入力フォーム
YoutubeかTwitterかを選択し、identifier
を入力できるように設定します。
(gem 'simple_form'
を使っています。)
(略) .box-body = f.input :embed_type, collect: Embed.embed_types_i18n.invert, include_blank: false = f.input :identifier (略)
View
Youtube 公式
動画と再生リストを埋め込む - YouTube ヘルプ
Twitter公式
タイムラインを埋め込む方法
Overview | Docs | Twitter Developer
Twitter Publish
HTMLをSlimに変換できる
HTML2slim
Youtubeのケース
上記のYoutube公式の動画にも丁寧に解説がありますが、埋め込みたいYoutube動画ページで、「共有」→「<> 埋め込む」を選択すると、埋め込み用のHTMLを用意してくれます。
<iframe width="560" height="315" src="https://www.youtube.com/embed/ojdbDYahiCQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
私はSlimを使っているので、このHTMLをSlim記法へ変換します。
ただし、とても苦手なので上記のサイトを使っています…
YoutubeのURLを切り取る
Youtubeの埋め込み用URLは以下の構成になっています。
https://www.youtube.com/embed/動画一意のID
最後の/
までは固定値として設定し、ユーザーの入力したURLのうち、最後の/
以下だけを受け取り、反映させていきます。
最初私は、embed.identifier.last(11)
で末尾の11字だけ切り取って実装していたのですが、これだと開始位置を指定したURLを貼り付けられた場合に動かなくなってしまいます。
なので、以下のように、/
以下を取得するメソッドを作っておきます。
split
メソッドで、/
で区切りをつけた配列を返して、そのlast
末尾部分を取得しています。
(略) def split_id_from_youtube_url # YoutubeならIDのみ抽出 identifier.split('/').last if youtube? end
ruby: embed = local_assigns[:embed] width = local_assigns[:width] || 853 height = local_assigns[:height] || 480 .embed-youtube = content_tag 'iframe', nil, width: width, height: height, src: "https://www.youtube.com/embed/#{embed.split_id_from_youtube_url}", \ frameborder: 0, gesture: 'media', allow: 'encrypted-media', allowfullscreen: true / Youtube公式に乗っている埋め込み用のHTMLを出力する形にする。 / #{embed.identifier.last(11)}でIDを切り取ると、開始秒指定されたときにOUT
Twitterのケース
上記の公式に乗っているように進めていきます。
貼り付け用に変換してくれる公式ツールがあるので、ここにツイートを貼り付けて埋め込み用HTMLを生成します。
Twitter Publish
<blockquote class="twitter-tweet"> <p lang="en" dir="ltr">A huge thank you to all of the bosses who are making voting a priority for their employees and giving people the time they need to vote. I’m doing that for my team 😘😘 Please share your voting plans with me!</p>— Lady Gaga (@ladygaga) <a href="https://twitter.com/ladygaga/status/1317133489829937152?ref_src=twsrc%5Etfw">October 16, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
これの無駄な部分を省いて、この部分があれば良さそう
<blockquote class="twitter-tweet"> <a href="https://twitter.com/ladygaga/status/1317133489829937152?ref_src=twsrc%5Etfw"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
これを上記サイトでSlimへ変換して整えればOK
.embed-twitter blockquote.twitter-tweet a href="#{embed.identifier}" script async="" charset="utf-8" src="https://platform.twitter.com/widgets.js"
泥臭くやってしまったけど、もっとスマートな方法があるかもしれないです。。
RSpecメモ
# 更新ボタンが同じページにいくつかあるとき… page.all('.box-footer')[0].click_button('更新する') # Youtubeの埋め込みを確認したいとき expect(page).to have_selector("iframe[src='https://www.youtube.com/embed/ojdbDYahiCQ']")