セーブポイント

特にジャンルの決まってない雑記です。

Heardleのクローンを作りたい - 技術日記 10月2日号

イントロクイズっておもろいんだよなあ、と思ったのでHeardleのクローンアプリを作ろうとしてみたけど難しくて頓挫したので、それまでの流れの話をする。

まずなにが必要か

Heardleみたいなサービスを作るにあたって必要な機能をまず考えてみると、

  1. 各種動画サイトなどの動画を自分のサイト上で再生する
  2. 動画の音声のみを再生する
  3. 再生する時間(開始時間、終了時間)がコントロールできる
  4. 解答入力フォームに入力補完機能をつける

みたいな感じになると思う。4.に関してはautoComplete.js(Heardleにも使われている)を使えば勝手にいい感じになってくれると思うので、考えるべきは主に音声関係の部分ということになる。

ニコニコ動画を再生したい

1.の機能をどうやって実現するかの話。

サイト上で流したい曲の音源データを別で用意してしまえばWebサービス上の処理は簡単になると思うんだけど、まあ明らかにスマートではないし。
できたらYouTubeニコニコ動画の動画を直接再生できたら嬉しいよね、ということでまずはこれを考えた。

YouTubeの動画を自分のサイトで流す手法については、jquery.mb.YTPlayerというライブラリがあるのでこれを使うとできる。

で、問題はニコニコ動画を使う場合どうするのかということ。簡単にググってみてもそれらしきライブラリを誰かが作っている気配がない。

ライブラリ無いなら無いで自分でなんとかできるかな、と軽く思っていたら普通にここが沼ポイントだった。

自分が考えたのは、ニコニコのサーバー上に保存されているであろう動画データに直接アクセスできたらいけそう、ということ。

実際にニコニコ動画にアクセスして調べてみる。

ブラウザの検証ツールなどで動画のページ内のvideoタグを探すと、上のようなものがヒットする。
普通ならここからsrc属性にセットされているURLを引っ張ってくればいいのだけど、今回はそれではうまくいかない。なぜなら、URLの前にblob:というのがあるからである。

先頭のblob:をとれば普通のURLとしてアクセスできるかというとこれも誤りで、難しいポイントである。

blobって何

自分もこれについてまだよく分かっていないんだけど、端的に言うとストリーミング再生を実現するための機能である(らしい)。Webページをロードする際に、動画のようなサイズの大きいコンテンツを全て読み込んでから再生を始めるのでは時間がかかってしまうので、blobという単位にデータを小さく細切れにしてクライアントに送信しているようである。細切れにされたデータをどこまで受信したかは、シークバーを見ると分かるようになっている。なるほどね。

今回はニコニコ動画について調べたけど、多分YouTubeも同じことをやっているはずである。動画配信サービス以外でも、例えばSpotifyみたいな音声を配信するサービスは音声を細切れにして送信していると思う。

ところで、blobにされる前のデータにアクセスする方法はないの?という話だけど一応できる場合もあるらしい。

taroosg.io

ただ、ニコニコ動画でネットワークの送受信を追ってみたけどよくわからなかった。

上のようなmp2tという形式で送られてきているのが動画の本体なんだけど、先ほどのブログで書かれていたようなクエリパラメータで開始位置と終了位置を設定している方式ではなさそう。

とまあ、ニコニコ動画に関してはこういうことがあったので、Heardleクローンで扱うのはYouTubeに投稿されている動画に限ることにした。

補足: Heardleはどうやって音声を再生しているのか?

本家のHeardleとかその亜種はどうやって音声流してるんじゃい、という疑問が当然湧く。

本家のHeardleはSpotify上に存在する曲を参照していて、亜種はSoundCloudを使っているものが多いので今回やりたいことと全く同じというわけではないけれども、参考くらいにはなりそう。

そもそもの元ネタであるWordleなどもそうだけど、多くのWordle亜種はGlitchというWebアプリを制作できるサービスを利用して作られている。Glitchで作られたアプリは他の人がソースコードを見たりすることができて、これにより亜種アプリの制作もしやすい。

例えば、以下のプロジェクトを見てみる。

glitch.com

ここのmain.jsを参考にすれば今回の問題は解決しそうなのだけど……

見てもらえば分かる通りminify(?)された状態のjavascriptが公開されており、一番知りたいところであるHeardleの主要な機能がどのように実装されているのかよく分からない、という状態。

ソースコードを公開している意味の無さよ…

動画を取得して音声のみを流す

YouTubeに関しては先ほど紹介したようにライブラリがあるので使う。ただそれだけの話なんだけど、ここでもちょっと困った。

もともと今回のHeardleクローンはNext.jsで開発していたんだけども、先ほど紹介したjquery.mb.YTPlayerは名前からも分かる通りjQueryが動作の上で必要となる。

別にjQueryを使いたいだけならnpm i jqueryしてimport $ from "jquery";すればいいのだけど、普通に自分がReactに慣れていないせいでコード上でReactとjQueryを混ぜて書くやり方がよく分からなかった。

React、普通に独学辛すぎる。
だから僕はReactを辞めた。

再生時間のコントロールをする

とりあえず動くものを作ろうと思ったので、Reactというモダンなものは捨ててHTML直書きに帰ってきた。

プロジェクトのソースコードはだいぶ不格好になるけど、結局これが書きやすいわけだ。技術の進歩についていけなくて悲しくなってきた。

で、結局この段階にも問題があって、ライブラリ側に用意されてる関数があまり豊富ではない。
いやまあ、音楽プレイヤーとして使うことは想定されてないんだろうけども。これは自分の使い方が悪い。

具体的に何が難しいのかというと、元の参照する動画によっては動画の先頭にロゴアニメーションなどが含まれる場合があって、こういうものはその部分を飛ばして再生したいわけである。今回の場合は、曲ごとに動画の先頭から何秒経ったところから再生を始めるのかの数値を持たせている。

これに関しては、ライブラリ側に指定した時間に動画をシークする関数が用意されているので、これを再生時に読んであげればその位置から再生が開始できる。問題は、再生を止めるときである。

Heardleを作りたいわけだから、再生を始めたあと1秒後などに自動で音声の再生をストップさせたいのだが、この処理が少し面倒。再生の開始位置だけでなくて、終了位置も指定できる関数が用意されていればよかったのだが、終了位置の指定はHTML上の属性の値からしかできない。

とここまで記事を書いていて思ったのだけど、HTMLの要素にはsetAttributeという関数があるらしい。これはなんとかなるかもしれない?

ただ、ここまで挫折ポイント多いと流石に普通にaudioエレメントとか使ったほうがいいよな、と思っている。

これから愚直に先頭のみ切り取った音源を再生する方式で作り直してみる予定です。狂いそう