herohoroブログ

ブログでYouTubeを再生できるようにセルフサポートしたらblockの取得から表示までの流れが染み込むように理解できたよ_react-youtube



🔄   2022-08-09

CSSやp5.jsを学ぶようになってからYouTubeを見ながら手を動かす….

といったことが増えてきました。

以前の記事でもオススメ動画を紹介したものの

「記事内に埋め込んで再生できたらわざわざリンク飛んで見に行かなくても済むよな…」

と思うように。。。。

まだ本家でサポートされていないblockを

ブログに取り込んだ経験なんぞない私が

調べに調べて

弱音を吐きながらも

サンプルを動かしたり

そこから使えそうな部分を移植したり

エラーと格闘したりしながら

学べたことを解説していこうと思います。

😎
ここで学べたことを活用すれば他のブロックも使え、改造できるよなと自画自賛しております。


概要
  • サポート済みのコードから似たblockを探す
  • 他のプラットフォームにある情報を取得して動的に埋め込むには
  • 実装するためにやること


サポート済みのコードから似たblockを探す

YouTubeをNotion上で再生するにはVideo embedのブロックにリンクを入れますよね….。

画像が読み込まれない場合はページを更新してみてください。

👇

画像が読み込まれない場合はページを更新してみてください。

easy-notion-blogで既にサポートされているblockで

似た雰囲気のblockをNotion上で確認してみます。

画像が読み込まれない場合はページを更新してみてください。

画像が読み込まれない場合はページを更新してみてください。

なんとな〜く

  • Image
  • Web bookmark
  • code
  • Tweet

あたりが似てるのかな〜といった見通しが立ってきました。

怪しいblockからもっと似ているblockを探る

以下、3つの探り方で絞っていきます。

  • リンクの埋め込み方
  • 表示のされ方
  • JSONの類似具合

まずは、怪しそうな4つのblockを実際にNotion上で表示をさせて見比べてみます。

リンクの埋め込み方

  • Image
画像が読み込まれない場合はページを更新してみてください。

  • Web bookmark
画像が読み込まれない場合はページを更新してみてください。

  • Code
画像が読み込まれない場合はページを更新してみてください。

🤔
ん〜。なんかCode blockは違う雰囲気かも……

  • Tweet
画像が読み込まれない場合はページを更新してみてください。

怪しい4blockを見比べてみて、

リンクを入れて表示させるか否かの部分でCode blockは違うなということが分かりました。

  • Image
  • Web bookmark
  • Code
  • Tweet

残るは3blockです。

表示のされ方

YouTubeをNotion上で再生できるVideo embedを表示すると…

画像が読み込まれない場合はページを更新してみてください。

こんな感じになります。

Notion上で再生できるので、YouTubeを使っているような雰囲気になれます。

これに近いblockに狙いを定めたいのです。

怪しい3blockでは

リンクを埋め込んだ後Notion上ではどのように表示されているのかを見比べます。

  • Image
画像が読み込まれない場合はページを更新してみてください。

  • Web bookmark
画像が読み込まれない場合はページを更新してみてください。

  • Tweet
画像が読み込まれない場合はページを更新してみてください。

〇〇○をここで使っているかのような雰囲気。

Tweet blockがダントツです。

JSONの類似具合

実際にNotionからblockの情報を取得するには

Notion integration ドキュメントに明記されているJSONを参照しながらになります。

画像が読み込まれない場合はページを更新してみてください。

https://developers.notion.com/reference/block

Video blockのJSONは….

画像が読み込まれない場合はページを更新してみてください。

https://developers.notion.com/reference/block#video-blocks

File Objectを含むようです。

  • Tweet

先程表示のされ方が似ていたTweet blockを見てみると….

画像が読み込まれない場合はページを更新してみてください。

Notionアプリは、サードパーティのサービスであるEmbedlyを使用して、URLで指定されたembedのメタデータを検証して要求します。これは、NotionがURL情報の非同期リクエストを開始し、完了までに数秒またはそれ以上かかる可能性があり、Embedlyからの応答を受け取った後、UIでメタデータを含むブロックを更新できるため、Webアプリでうまく機能します。

なんか難しそうなことを言っています…..😵

URLだけでいいようです。

画像が読み込まれない場合はページを更新してみてください。

  • Web bookmark
画像が読み込まれない場合はページを更新してみてください。

https://developers.notion.com/reference/block#bookmark-blocks

Tweet blockにあったEmbed blockに似ています。

  • Image
画像が読み込まれない場合はページを更新してみてください。

https://developers.notion.com/reference/block#bookmark-blocks

🤩
Video blockにそっくり!!!!

JSON構造的にはImage Blockが似ているなということに気づけました⭐

怪しいblockまとめ

リンクの埋め込み表示のされ方JSON構造
Image
Web bookmark
Code --
Tweet

ImageとTweetの2blockにアンテナを張りながら実装していけば良さそうです。

他のプラットフォームにある情報を取得して動的に埋め込むには

Twetterのような表示はどうやって実装されているのか…..

本家のコードを確認してみます。

import { TwitterTweetEmbed } from 'react-twitter-embed'

import styles from '../../styles/notion-block.module.css'

const TweetEmbed = ({ url }) => {
  let matched
  try {
    matched = new URL(url).pathname.match(/\/(\d+)$/)
  } catch (error) {
    console.log(error)
    return null
  }

  if (!matched) {
    return null
  }

  return (
    <div className={styles.tweetEmbed}>
      <TwitterTweetEmbed tweetId={matched[1]} />
    </div>
  )
}

export default TweetEmbed
src/components/notionblocks/tweet-embed.tsxより

🤔
Twitterっぽい表示にしているのはreact-twitter-embed っていうライブラリのおかげ!?

ライブラリを深堀り

react-twitter-embedの本家リポジトリへ行ってみます。

READMEにはデモページへのリンクも公開されている….

画像が読み込まれない場合はページを更新してみてください。

https://saurabhnemade.github.io/react-twitter-embed/

😗
このライブラリーは多様なtwitter embedを扱っているんだね

YouTubeを埋め込むライブラリはどのようなものがあるのか……

動的に埋め込むライブラリ

検索画面で普通に【github react youtube 】と検索すると….

画像が読み込まれない場合はページを更新してみてください。

一番上のリポジトリをのぞいてみます。

画像が読み込まれない場合はページを更新してみてください。

https://github.com/tjallingt/react-youtube

Forkの件数やStarの数が多いので評判いいのかな〜というのが伝わってきます。

commitも最近まで行われているのでメンテナンスもされてるのかな〜といった安心感。

このライブラリを使ってみよう\(^o^)/

react-tweet-embedのようなデモページは無いので

code sandboxの力を借りて

セルフデモをしてみようと思います。

code sandbox でデモする方法

評判はいいのに実装した後に表示のされ方が残念だったらショックなので、

実際に動かして確認しようと思います。

画面右上のCreate SandboxからTypescriptのテンプレートを選びます。

画像が読み込まれない場合はページを更新してみてください。

react-youtubeリポジトリのREADMEにある記述をコピーし…..

画像が読み込まれない場合はページを更新してみてください。

Code sandboxに用意したテンプレートに貼り付けます

画像が読み込まれない場合はページを更新してみてください。

やいやい言われるので素直に受け入れて調整します。

調整したこと:

  • 画面左にあるAdd Dependencyからreact-youtubeライブラリをインストール
  • 関数名をテンプレートにあったexport default function App()にしておく

画像が読み込まれない場合はページを更新してみてください。

これでデモページを自分で用意することができました\(^o^)/

return にあるvideoIDを変更すると任意の動画を表示できるかを確認してみます。

画像が読み込まれない場合はページを更新してみてください。

👇

画像が読み込まれない場合はページを更新してみてください。

\(^o^)/

実装するためにやること

基本的な流れはBlog Learn06で行った部分と関連しています。

画像が読み込まれない場合はページを更新してみてください。

https://herohoro.com/blog/blog-learn_page-content_paragraph

今回の流れ:

  • ブロック情報を取得する
    • interfaces.ts
    • client.ts
  • 取得した情報を表示できる状態にする
    • notion-block.tsx
    • notion-blocks/video.tsx
  • 表示できる状態かを確認する
  • YouTubeリンクからIDを拾えるようにする
    • 正規表現

ブロック情報を取得する

Notionのblock情報を取得するにはJSON構造を把握しないといけません。

公式ドキュメントを参照して見比べた時、

Image blockにそっくりだということが分かっているので

画像が読み込まれない場合はページを更新してみてください。

Image blockのコードを参照しながら追記していこうと思います⭐

JSON構造は同じでもFile Object内で使いたい要素がどれくらいあるかによって取得の仕方がImaga blockと変わるので、その部分も整理しながら解説します。

interfaces.ts

Notion blockのJSON構造に直接向き合って拾ってくれるのがinterfaces.tsです。

export interface Image {
  Caption: RichText[]
  Type: string
  File?: File
  External?: External
  Width?: number
  Height?: number
}
lib/notion/interfaces.ts

Image blockではFileも取得しているようです。

画像が読み込まれない場合はページを更新してみてください。

🥱
Video blockはexternalだけでいいね。

Captionも必要ないかな….と思うのでカットすることにします。(widthやheightもいらないね)

必要最低限のblock情報となりました。

export interface Video{
  Type: string
  External?: External
}
lib/notion/interfaces.ts

client.tsxで使えるようにBlock関数にVideoを追加しておきます。

画像が読み込まれない場合はページを更新してみてください。
lib/notion/interfaces.ts

client.ts

記事を表示させるpages/blog/[slug].tsxと深い関係があるのがclient.tsです。

画像が読み込まれない場合はページを更新してみてください。
pages/blog/[slug].tsx

[slug].tsxに記述されているgetStaticProps関数では

記事ごとに本文のblockを取得するgetAllBlocksByBlocksId があります。

これはclinent.tsで記述されています。

interfaces.tsで用意したvideo要素もclient.tsxに追加して….

画像が読み込まれない場合はページを更新してみてください。
lib/notion/clinent.ts

Image blockの記述を参考にgetAllBlocksByBlocksId関数にVideoを追加します。

画像が読み込まれない場合はページを更新してみてください。
lib/notion/client.ts
🧐
interfaces.tsは型定義だったのか…..

狙いは定まった!!!!!\(^o^)/

取得した情報を表示できる状態にする

client.tsで[slug].tsxから記事毎のblockを取得できるようになりました。

取得した内容を表示できるようにしているのがcomponentsディレクトリです。

どのように表示しているのかを

ざっくり確認するためにnotion-block.tsxを開きます。

画像が読み込まれない場合はページを更新してみてください。
components/notion-blocks.tsx

[slug].tsxでblock変数として引数に入れられた時に

該当したらこんなコードにして表示してね

って感じの指示をしているのが分かります。

👇末尾のコードでは【該当したらコンポネントしちゃう】っていうのが如実に明記されています。

画像が読み込まれない場合はページを更新してみてください。
components/notion-block.tsx

表示するためにいろいろ処理が必要な場合にはdynamicしてつなげてもいいようです。

画像が読み込まれない場合はページを更新してみてください。

🤔
今回は長くなりそうだからdynamicかな〜

見通しが立ったので実装してみます\(^o^)/

やること:

  • components/notion-blocksにvideo.tsxを用意する
  • components/notion-block.tsxにdynamic追加する
  • components/notion-blocks.tsxの末尾NotionBlock関数にvideoコンポネントを追加する

video.tsxを用意する

code sandboxでセルフデモしたコードをそのまま使います。

import styles from '../../styles/notion-block.module.css'
import React from 'react';
import YouTube, {YouTubeProps } from 'react-youtube';


const Video=({block})=> {
  
  const onPlayerReady: YouTubeProps['onReady'] = (event) => {
    event.target.pauseVideo();
  }
  const opts: YouTubeProps['opts'] = {
    height: '390',
    width: '640',
    playerVars: {
      autoplay: 1,
    },
  };

  return (
    <div className={styles.Video}>
      <YouTube videoId="V7v3z09m07s" opts={opts} onReady={onPlayerReady} className={styles.youtube}/>
    </div>
  );
}


export default Video;
components/notion-blocks/video.tsx

このままではわざわざ引数にblockを入れている意味がないのですが、

後から修正すればいいかなって構えることにします。

notion-block.tsxにdynamic追加する

video.tsxを用意したことでパスをnotion-block.tsxに追加できるようになったので入れます。

画像が読み込まれない場合はページを更新してみてください。

末尾NotionBlock関数にvideoコンポネントを追加する

notion-block.tsxの末尾にある条件分岐部分も追加します。

画像が読み込まれない場合はページを更新してみてください。

つながった\(^o^)/

表示できる状態かを確認する

セルフデモのコードをコピペしただけのvideo.tsxを修正していきます。

やること:

  • YouTubeリンクを読み込む
  • 読み込んだリンクを表示する

🔥
[slug].tsxから飛んでくるblock情報からYouTubeリンクを表示できるようにしたい。

JSON構造が似ているImage blockのnotion-block.tsxの記述の仕方を参照して追加します。

画像が読み込まれない場合はページを更新してみてください。

読み込めているのかをひとまず表示させてみることにします。

returnの部分を細工します。

return (
    // <div className={styles.Video}>
    //   <YouTube videoId="V7v3z09m07s" opts={opts} onReady={onPlayerReady} className={styles.youtube}/>
    // </div>
    <>
      <p> 
        これはNotion blockから取得したYouTubリンクだよ:{url}
      </p>
    </>
  );
}
components/notion-blocks/video.tsx

Notionにvideo blockを埋め込み….

画像が読み込まれない場合はページを更新してみてください。

これを表示させると….

画像が読み込まれない場合はページを更新してみてください。

成功\(^o^)/

YouTubeリンクからIDを拾えるようにする

YouTubeリンクは無事取得できていることが分かったので、

今度はそのリンクにあるvideoIDを拾えるようにします。

tweet-embed.tsxではどのように拾っているかを確認してみると….

画像が読み込まれない場合はページを更新してみてください。

謎の記号をmatchメソッドに入れているようです…..。

調べたところこれは【正規表現】という新キャラなようです 🦠

正規表現について

ネットで正規表現ネタを調べて

なんとな〜く謎の記号の意味が分かってきました。

YouTubeリンクからIDを拾うには…..

と難しく考えず単純に検索バーに「正規表現 youtube videoID」と入力するといろいろヒットします。

しっくり来ない場合は正規表現を英語Regexにして検索するとフィットする確率大です。

ドンピシャの情報を発見!!!

画像が読み込まれない場合はページを更新してみてください。

https://www.web-dev-qa-db-ja.com/ja/javascript/urlからyoutubeビデオidを取得する方法/1067339059/

この記事によると….

/(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/)

といった正規表現で解決するようです。

ポイントごとに解説もあり一人で感動していました。

正規表現でvideoIDを取得する

YouTubeリンクからvideoIDを拾うためにVIDEOSという変数を追加します。

const Video=({block})=> {
  const url = block.Video.External.Url
  const VIDEOS =url.match(/(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/);
  
//以下同文
components/notion-blocks/video.tsx

このまま表示しても何も変わらないので

console.logで中身を確認することにします。

const Video=({block})=> {
  const url = block.Video.External.Url
  const VIDEOS =url.match(/(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/);
  
  const onPlayerReady: YouTubeProps['onReady'] = (event) => {
    event.target.pauseVideo();
  }
  const opts: YouTubeProps['opts'] = {
    height: '390',
    width: '640',
    playerVars: {
      autoplay: 1,
    },
  };
  console.log(VIDEOS)//👈 追加

  return (

//以下同文
components/notion-blocks/video.tsx

するとターミナルにVIDEOSの情報が表示されます。

画像が読み込まれない場合はページを更新してみてください。

欲しいのは2つ目の部分。

表示させてみます。

return (
    // <div className={styles.Video}>
    //   <YouTube videoId={videoID} opts={opts} onReady={onPlayerReady} className={styles.youtube}/>
    // </div>
    <>
    <p> 
      これはNotion blockから取得したYouTubリンクだよ:{url}
      videoIDがとれてるかな?{VIDEOS[1]} {/* 👈 追加 */}
    </p>
  </>

  );
}
components/notion-blocks/video.tsx

👇

画像が読み込まれない場合はページを更新してみてください。

とれてるじゃーーーーん\(^o^)/

無事videoIDを取得できたので、

本来の記述に戻して様子を見ます。

return (
    <div className={styles.Video}>
      <YouTube videoId={VIDEOS[1]} opts={opts} onReady={onPlayerReady} className={styles.youtube}/>
    </div>
  //   <>
  //   <p> 
  //     これはNotion blockから取得したYouTubリンクだよ:{url}<br/>
  //     videoIDがとれてるかな?{VIDEOS[1]}
  //   </p>
  // </>

  );
components/notion-blocks/video.tsx

👇

画像が読み込まれない場合はページを更新してみてください。

完成\(^o^)/

再生してみてみて〜〜〜〜〜(*´∀`*)(*´∀`*)♥

まとめ

本家でサポートされていないblockでも既にあるコードを手がかりにすれば

なんだかんだでセルフサポートできるということが分かりました。

実際1週間くらい格闘する中で身についてきた術があります。

いろいろ追記しないといけないことはあるんだけど、

優先順位を付けて「今これやる!」「ここまでやれたら次これやる!」

みたいな切り替えができるようになってきたという術です。

分からない部分を検索するにしても

キーワードの候補も上げられず右往左往してばかりだった状態から

なんとな〜く候補ワードを入力できるようになってきたかなと思います。

まだまだイメージする力が弱いので

進捗状況を表示して確認する工程は必要だなと感じ、

途中謎の<p>タグの記述やconsole.logをはさみながら進めました 😋

何事も見える化って大切だなと感じたvideo embedの実装でした\(^o^)/

作業中にぶつぶつツイートしたスレッドはこちら(*´ω`*)(*´ω`*)www


本家にPRしてみました。(ドキドキ)

いろいろ仕上げを手伝っていただきながら本家へ正式Mergeを果たしましたとさ。

めでたしめでたし

Twitterでは更新のお知らせを随時行っています

興味ある方はLet'sフォロー★

▼ この記事に興味があったら同じタグから関連記事をのぞいてみてね

新着記事を通知したい??


RSSリーダーにatomのリンクを登録すると通知が行くよ🐌

https://herohoro.com/atom

やってみてね(*´ω`*)(*´ω`*)

Twitter Timeline


フォロー大歓迎\(^o^)/