herohoroブログ

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



🔄   2023-03-14

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

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

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

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

と思うように。。。。

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

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

調べに調べて

弱音を吐きながらも

サンプルを動かしたり

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

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

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

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


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


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

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

image block

👇

image block

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

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

image block

image block

なんとな〜く

  • Image
  • Web bookmark
  • code
  • Tweet

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

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

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

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

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

リンクの埋め込み方

  • Image
image block

  • Web bookmark
image block

  • Code
image block

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

  • Tweet
image block

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

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

  • Image
  • Web bookmark
  • Code
  • Tweet

残るは3blockです。

表示のされ方

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

image block

こんな感じになります。

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

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

怪しい3blockでは

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

  • Image
image block

  • Web bookmark
image block

  • Tweet
image block

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

Tweet blockがダントツです。

JSONの類似具合

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

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

image block

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

Video blockのJSONは….

image block

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

File Objectを含むようです。

  • Tweet

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

image block

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

APIはUIよりも高速に返す必要があり、またEmbedlyからの応答によってブロックの種類が実際に変更される可能性があるため、APIで埋め込みブロックを作成する際にはEmbedlyを呼び出さないことにしました。これは、レスポンスにあるブロックがリクエストで送られたブロックと一致しないため、遅くなり、混乱を招く可能性があります。

その結果、API経由で作成されたエンベッドブロックは、Notionアプリで作成された対応するブロックとまったく同じようには見えないかもしれません。

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

URLだけでいいようです。

image block

  • Web bookmark
image block

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

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

  • Image
image block

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にはデモページへのリンクも公開されている….

image block

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

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

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

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

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

image block

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

image block

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

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

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

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

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

code sandboxの力を借りて

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

code sandbox でデモする方法

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

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

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

image block

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

image block

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

image block

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

調整したこと:

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

image block

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

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

image block

👇

image block

\(^o^)/

実装するためにやること

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

image block

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

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も取得しているようです。

image block

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

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

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

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

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

image block
lib/notion/interfaces.ts

client.ts

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

image block
pages/blog/[slug].tsx

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

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

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

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

image block
lib/notion/clinent.ts

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

image block
lib/notion/client.ts
🧐
interfaces.tsは型定義だったのか…..

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

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

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

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

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

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

image block
components/notion-blocks.tsx

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

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

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

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

image block
components/notion-block.tsx

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

image block

🤔
今回は長くなりそうだから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に追加できるようになったので入れます。

image block

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

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

image block

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

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

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

やること:

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

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

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

image block

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

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を埋め込み….

image block

これを表示させると….

image block

成功\(^o^)/

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

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

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

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

image block

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

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

正規表現について

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

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

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

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

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

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

image block

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の情報が表示されます。

image block

欲しいのは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

👇

image block

とれてるじゃーーーーん\(^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

👇

image block

完成\(^o^)/

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

まとめ

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

めでたしめでたし

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

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

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

Buy Me A Coffee

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


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

https://herohoro.com/atom

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

Twitter Timeline


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