SpeakerDeck を Web サイトに埋め込み時に意識する ID と screen_name の関係と oEmbed について

最近仕事で SpeakerDeck の埋め込みを実装する機会があり、 SpeakerDeck のスライド URL から埋め込みコードを生成することになった。

SpeakerDeck には埋め込みコードを取得できる機能があるが、利便性およびユーザーから提供される任意の HTML をそのまま表示したくないという理由から URL から自動的に生成するほうが都合が良いという結論に至り、実装を進めた。

結果的にはタイトルの通り SpeakerDeck が提供している oEmbed のフォーマットを利用することになったが、いくつか面白い仕様があったのでまとめておく。

SpeakerDeck の ID と screen_name について

普段あまり意識することはないが、SpeakerDeck は仕様として内部的に利用している数値 ID と URL 上で表示される screen_name 相当の ID の2つを持っている。

SpeakerDeck はタイトルを自動的にアルファベットに変換し、かつスペースをダッシュで区切った URL が自動的に生成されるが、これはあとからタイトルを変更した場合に URL もそれに連動して変わる。

そのため、内部的に URL 表示の ID(以後 screen_name) とは別の ID を持っている。なお、 SpeakerDeck で採用されている ID 形式は 32 桁の英数なので、恐らく uuid。

この辺りはユーザー側が定義できる screen_name があるアプリケーションでは基本的な実装であり、例えば Twitter なんかもこういった作りになっている(そのため、 screen_name を変えても Twitter アカウントは数値 ID でトラッキングできる)。

ここまでは至極一般的な話であるが、 SpeakerDeck は少々難儀な課題として、以下のような仕様となっている。

  • プレゼンテーションの URL に screen_name が利用される
    • 逆にいうと、プレゼンテーションの URL 以外では screen_name が利用されない
  • 例えば、以下のようなデータは全て ID を中心に処理される
    • OGP に設定されている1枚目のスライドのURL
    • 埋め込みで使う <script> にわたす data 属性
    • iframe の src に指定する player の URL

つまりは、URL を渡されただけでは埋め込みコードが発行できないという課題がある。ただ一方で、逆に言えば リクエストを飛ばして og:image を引っこ抜けば ID を取得できたりはするが、これについてはオフィシャルで提供されているわけではないので安定しないし、なによりスクレイピングまがいの処理が必要となる。

oEmbed での解決

そういった問題を解決するための手法として、SpeakerDeck では oEmbed 形式の埋め込みコード取得 URL を提供している。

マイナーなフォーマットではあるものの、一応共通規格として提唱されており、SpeakerDeck の他にもflickrや、同じくプレゼンテーション共有サービスのSlideShareもこのフォーマットに対応している。

今回はプレゼンテーションの埋め込み全般を取り扱いたかったので、 SpeakerDeck ならびに SlideShare 両方をまとめて取り扱える oEmbed を活用してみることとした。

ちなみにこの oEmbed はある程度マイナーな規格ではあるものの、WordPress が v4.4 にて提供を始めたことによって、それなりに広まっている規格でもある。

v4.4 の頃は WordPress を触っていたので、かなり懐かしい気持ちになった。

oEmbed にはそれなりに詳細な定義があるが、特徴として規格にて以下を要求している。

  • oEmbed 用に単一のエンドポイントを提供する
  • oEmbed API への GET リクエストでは url パラメータを要求する
    • url パラメータは、サービスの対象のコンテンツの URL を受け付ける
  • API はレスポンスとして、以下を満たすものを提供する
    • XML または JSON によるデータの返却
    • video や rich 形式のデータの場合、 html にてリソースを十分に表示できる HTML の返却

SpeakerDeck や SlideShare の場合は、 html に埋め込みコードが返ってくる JSON API として利用できる。

今回はこれを利用した。

SpeakerDeck での例

SpeakerDeck では https://speakerdeck.com/oembed.json にて oEmbed のエンドポイントを提供している。なお、これについては SpeakerDeck の公式 FAQ にしれっと書かれている

これに url パラメータをつけることで、該当のプレゼンテーションの埋め込みコードが発行されることとなる。

実際に https://speakerdeck.com/oembed.json?url=https://speakerdeck.com/potato4d/why-you-choose-spa へとアクセスを行った場合、以下のようなレスポンスが返却される。

typerich であり、かつ適切な html が返却されていることがわかる。

{
  "type": "rich",
  "version": 1.0,
  "provider_name": "Speaker Deck",
  "provider_url": "https://speakerdeck.com/",
  "title": "私たちはなぜ SPA で開発するのか / Why you choose SPA",
  "author_name": "Potato4d(Hanatani Takuma)",
  "author_url": "https://speakerdeck.com/potato4d",
  "html": "\u003ciframe id=\"talk_frame_570178\" src=\"//speakerdeck.com/player/3fbb749a3f8e4d80b9c6ca29d9f0737a\" width=\"710\" height=\"399\" style=\"border:0; padding:0; margin:0; background:transparent;\" frameborder=\"0\" allowtransparency=\"true\" allowfullscreen=\"allowfullscreen\" mozallowfullscreen=\"true\" webkitallowfullscreen=\"true\"\u003e\u003c/iframe\u003e\n",
  "width": 710,
  "height": 399
}

重要な点として、利用者が渡したのは screen_name ベースでの URL だが、返却される iframe は player/3fbb749a3f8e4d80b9c6ca29d9f0737a といった ID ベースとなっていること。

これによって当初の問題が解決され、円滑に埋め込むことができるようになった。

SpeakerDeck の oEmbed を利用する際の注意点について

注意点として、古い形式でもあるため、 SpeakerDeck の oEmbed は CORS に対応していない。これによって、フロントエンドから直接叩いて表示を行うことはできないことになる。

バックエンドでスクレイピングまがいのことをせずに ID を引いてくるための手段として検討している身としては苦い仕様ではあるが、利用するときは一度プロキシを通すなどで解決する必要がある。

幸い今回の自分のケースでは、 AppEngine で動いている SSR サーバーが存在したため、そこでプロキシ用のエンドポイントを用意することで解決できた。

終わりに

oEmbed はサイトに掲載されているライブラリ群を見てもわかるように、 PHP の界隈で使われてきた歴史が長いようで、あまり広く普及しているわけでもないフォーマットの様子。

一方で、使う場合の利便性はかなり高く、それぞれの API の形式を問わず、共通した規格のもとにリクエストを飛ばせることは実装コストや追従の観点でも大きなメリットがあるので、引き出しとして用意しておくのはよさそう。