はじめに
色々と忙しくてNext14のキャッチアップができていなかったのでそれを兼ねてちょっとしたアプリを作ってみようと作りました。
最初にハッカソンで原型を作ったためアーキテクチャはこうしたほうがよかったななどの思い入れもありますがその辺りはご愛嬌。
サイト: https://scp-log.vercel.app/
GitHub: https://github.com/yoshi-non/scp-log
アプリ概要
Youtubeの動画を検索してその動画をプレイリストに追加して再生できる一般的な再生アプリ。
オフライン環境への音声ダウンロードも可。
※プレイリストのデータは全てローカルストレージに保存されます。
環境
Next.js: 14.1.0
Node: 18.17.0
フォルダ構成
├ api // Next.js環境で作成できなかったapi
├ src
│ ├ app // Next.jsのルーティング
│ ├ components // コンポーネント
│ │ ├ features // 特定の機能を実現するコンポーネント
│ │ │ ├ sample // サンプルコンポーネント
│ │ │ │ ├ hooks // サンプルコンポーネントのみで使用するhook(不要な場合もある)
│ │ │ │ ├ logics // サンプルコンポーネントのみで使用するロジック(不要な場合もある)
│ │ │ │ ├ tests // テスト専用
│ │ │ │ ├ index.tsx // エントリーポイント
│ │ ├ functions // UIとして表示されないコンポーネント
│ │ ├ ui // UIコンポーネント,shadcn/uiで使用
│ ├ constants // 全体に共通の定数ファイルを配置する
│ ├ libs // ライブラリのラッパーや使いまわしやすいようにする
│ ├ types // 全体に共通の型定義ファイルを配置する
│ ├ utils // 使い回すロジックなど
理想としてはsrc配下にapi層のapiフォルダを作成したかったのだがデプロイするvercelの環境と使用するapiの性質からsrcと同じディレクトリに存在する。
app下のAPI Routeのフォルダ名をapiにしてしまうとvercelで使用するapiと名前が重複しまい、API Routeは動かなくなります。ルートのapiフォルダはフォルダ名をapi以外にするとvercelで動かないです。
ここではAPI Routeをapisというフォルダ名にして回避しています。
※api(apis)フォルダが複数存在するかの詳細は関連動画検索で説明
shadcn/ui
shadcn/uiは使ってみたかったので導入した。
MUIみたいな「らしさ」がないのとカラーテーマの設定や作るのが面倒なResizableを簡単に実装できたりするところには感動した。ライト・ダークではデフォルトでローカルストレージにデータを保存までやってくれるのでかなり助かる。
一方でDialogは若干コードが煩雑になってしまうところだったり最近できたDrawerは部分的な幅で使えるものがあったらいいなと感じた。
今回話すこと
Google Analytics
基本的に上記のサイトを使えば簡単に導入できたが開発当初はNext14.0.4であったが14.1.0にバージョンを上げる時にビルドエラーでuseSearchParams() should be wrapped in a suspense boundary
のように出てしまうので以下のようにGoogleAnalyticsタグをSuspenseタグで囲えばエラーは消える。
<head>
<Suspense>
<GoogleAnalytics />
</Suspense>
</head>
導入も手軽で以下の画像のように初心者でもわかりやすくデータが見える。
ドラッグアンドドロップ
React 18に対応していることと現在伸びてることを加味して@dnd-kit/core
を採用
細かい使い方は省くが簡単に言えばDndContextWrapperでドラッグ&ドロップを反映させたい箱を用意してSortableItemWrapperで移動させたいものをラッピングして使用する。
関連動画検索
動画を検索して追加する分には簡単なのだがYoutube Data APIには関連動画の検索APIなるものは存在しないので自作する必要があった。
処理の流れ
- プレイリストに存在する動画タイトルを形態素解析して頻繁に使われている単語を検索する
- 頻繁に使われている単語を繋げて検索APIにかける
日本語の形態素解析には代表的にMeCab
とKuromoji
があるが今回はNext.jsで作っている観点からNodeの資料が多いKuromoji
を採用。
ぶちあたった問題
APIを作る段階でNext.jsのAPI Routeで./node_modules/kuromoji/dict
というパスがvercelでは認識されないというエラーに遭遇した。調べてみるとvercelはNext.jsを用いたgzipファイルをサポートしていないということらしいのでsrcと同ディレクトリにapiフォルダを作成して無理やり動かすことにした。
ただしこれはvercel環境でしか動かないためローカル環境は別で作る必要があるためapiファイルを2つ作る必要がある。
npm run dev
or npm run build
で動くapiファイル
vercel dev
or 本番環境で動くapiファイル
オフラインダウンロード
やっぱりオフラインでも使えたらいいなという思いがあったのでオフラインダウンロードにも挑戦。
しかしながら以下の流れで音声だけのダウンロードしかできなかった。
- YoutubeのダウンロードAPIには高画質(1080p)の動画と音声が合成しているファイルをダウンロードするものが存在せず動画と音声を別々でダウンロードして合成する必要がある
- 合成にはffmpegというライブラリがあるのだが、vercel環境では
errorType":"Error","errorMessage":"spawn /var/task/node_modules/ffmpeg-static/ffmpeg ENOENT","code"
というエラー、わかりやすくいえばそんなファイル見つかんないよ。と言われる※ローカル環境は動く - 画質を下げればffmpegをしようせずともいけると考えたがvercelの無料枠のServerless Functionは10sでタイムアウトを起こすためそもそも無理っぽい
- 高画質(1080p)動画ダウンロードは関連動画のようにapiフォルダに記載して実行すれば本番環境でも動くようになりましたが3のタイムアウトでサービスとしては機能してない
vercel dev
で動くapiファイル
おわりに
普段ハッカソンはその場で作って終わることが多いですが今回は継続開発含めいろいろと学ぶことも多くありNext.jsの理解を深めながら触ったことのない新しいライブラリや処理を模索できて楽しかったです。ぜひ参考になったら嬉しいです。
※テストコードを書く用の設計をしましたがめんどくて書いてないです。懺悔...