fresh digitable

めんどくさかったなってことを振り返ったり振り返らなかったりするための記録

Jetpack ComposeでYouTubeのライブ配信リストを作っている

手短に

github.com

動機付け

  • vtuberの配信をよく見るようになった。調子に乗ってチャンネル登録してたら登録数が200をこえました。ななしいんく箱推しです。
  • YouTubeの「登録チャンネル」ページ(PC)は、配信中のものが上の方に出てきて、それ以降は配信ページの公開順に並ぶ。動画やShortsやもちろん配信終了したアーカイブも混ざっている。開始して間もない配信は配信中グループの末尾に追加されるので、ゴールデンタイムには2スクロールぐらいしないと見たい配信にたどり着けない。
  • プレミアムじゃないので通知が来てから配信ページに行ってもすぐ見れない。だから開始前に開いておきたいんだけど「登録チャンネル」から探そうとしてもだいたい見つけられない。
  • これからの時間の配信予定をみて自分の予定を立てたい。
  • 「今やってる配信」や「これからの時間の配信予定」がすぐにわかる何かが欲しかったので、勉強のついでにJetpack ComposeでAndroidアプリをつくることにした。

YouTube Data APIの動きを見よう

  • YouTube Data APIの動きを見たかったので Android Quickstart  |  YouTube Data API  |  Google for Developers を見ながらセットアップしてアプリから呼んでみる。
  • 今回はG様謹製のライブラリを使うので、OAuth2の対応にはDeveloper Consoleの設定(↑のセットアップでやる)が必要だがアプリ側ではruntime permissionで連絡先取得の許可がとれればよい。エラーからの復帰をユーザーに促す処理でDialogFragmentを表示するものなどComposableを完全にフォローしていない部分があり、このあと頭を悩ませることになる。
  • UIはこれまでの方法(XMLのやつ)で一旦簡単に作った。マイグレーションを経験する的な側面もあるかもしれない。
  • 配信中、配信予定のものだけを取得するAPIが無い。紆余曲折を経て、最終的な実装では次のようにして配信リストを作成することにした。
    1. Subscriptionエンドポイントから登録しているチャンネルのリストを取得して、各チャンネル情報から、すべての動画(と配信予定)が含まれるプレイリストのIDを取り出す*1
    2. PlaylistItemsエンドポイントからプレイリストアイテムのリストを取得して、各プレイリストアイテムから動画IDを取り出す*2
    3. Videosエンドポイントに動画IDのリストを渡して動画のデータを取得する。ライブ配信は配信開始時間の情報が含まれているので、これを見て配信/動画とか配信中/配信予定とかを仕分けてリストにする。
  • あとは関係ありそうなAPIの動きをチェックしたかったので動画詳細とかチャンネル詳細の画面を作っておく。
  • 以下、試行錯誤の雑多なまとめ。
    • 当初はActionsエンドポイントから動画アップロード(配信ならいわゆる待機所の作成)のイベントをとってきていた。Actionsエンドポイントにはbefore/sinceを指定できるクエリがあるので、キャッシュに残っている最新のイベントの日付より新しいものをリクエストするといういかにもモバイルアプリ的な実装ができたと思ってご満悦だったのだが、動画の中には非公開でアップロードされるものもあり、その後に公開状態でアップロードしたイベントが来てしまうと非公開の動画アップロードイベントを取得できなくなってしまう。事前に非公開でアップロードされる配信というのがよりによってコラボ配信だったり記念日配信だったりして、取りこぼしたくないので却下した。
    • ChannelSectionsといういかにもWeb版のチャンネルのホームタブのページを作れそうなエンドポイントがあるが、いまのところ「ライブ配信中」セクションも「今後のライブ配信」セクションも空の配列が返ってくる。
    • Searchエンドポイントなら配信予定の枠だけ取得できるが、1回のリクエストでQuotaを100使ってしまい(他のは1)、すぐ上限(10,000/day)に到達してしまうので今回の用途には合わなさそうだった。

Twitch APIの動きを見よう

  • YouTubeでもTwitchでも配信するという人が増えたのでこちらもちゃんとカバーすることにした。実際の作業はComposableに移行した後にやった。
  • とりあえず Get Started | Twitch Developers から始める。
  • OAuth2はWebブラウザに飛ばしてログインしてもらってまたアプリに帰ってくるパターン。launchMode=SingleTopでもSingleTaskでもActivity#onCreate()がどうしても呼ばれてしまうのでよくわからん実装になった。
  • Twitch APIにはライブ配信中のリストやチャンネルの配信予定のリストを取得できるAPIがあるので最終的なリストを作るのは簡単だった。
    • 配信予定は、例えば「毎週月水金の19:00~」みたいに設定できるらしいのだが、配信予定をリクエストすると日付のリストで直近の日付から指定した件数だけ返ってくる。期間の指定ができないので際限なく取得できてしまう(1敗)。
    • フォローとかサブスクライブとか、YouTube側との用語の差異があってクラス名とかをどうすべきかまあまあ悩んだ。
  • APIリファレンス内で用語が統一されていないように見受けられるので文章やcurlの例をよく読む必要がある。
  • twitch4jといういかにもなライブラリがあって使ってみたのだが、Androidアプリであまり使われない依存関係がいくつか含まれており案の定動かすのが面倒くさそうだったので今回は採用しなかった。

Jetpack Composeに移行しよう

  • 画面は配信中と予定タブに分かれていて、それぞれ動画リストを持っている。リストから詳細画面に飛べる。ほかには登録チャンネル一覧とかアカウントの連携画面があって、そこに飛ぶためのNavigation Drawerがある。
  • まずはシンプルにXMLリソース(View, Navigation)をComposableに置き換えた。そのあと、できるかやってみようと思ってActivityの中身もComposableに押し込んだりした。
    • 何回か無限コンポーズ編に突入したりもしたけどStateをProviderで包んで渡すやつをやったら何とかなった。
    • ViewModelとかNavigationControllerをComposableにじかに渡してたけどこれもStateのProviderとかCallbackとかで包んで渡したらなんかすっきりした。
    • YouTubeのOAuth周りの実装をComposableに移してくるのが難しかった。もっとうまくやる方法がありそう。
  • カスタムViewの実装とかActivity/Fragmentのライフサイクルのこととかを考えなくてもよくなったのはうれしい。
  • Modifierにapplyでclickableを足そうとしたらできなかった(applyの中でif文で足すかどうか決めたかった)。本当はenabledを使うところだし今となってはなぜそんなことをしたかったのかも思い出せないけど、不思議な体験をしたことだけは覚えている。
  • Navigationのビューワーなくなって地味にしょんぼり。
  • NavigationのURIむずかしい。
    • PathパラメータならNonNull、QueryパラメータならNullableというようにしたかったが、NonNull/Nullableは型によって予め決められているようだった。
    • startDestinationが引数を受けられないの不便かなって思う。初期値を使って乗り切るみたいな方法がよく紹介されているけど値がNullableでなければならないので実質的にString一択になるんじゃないか。引数を受けたい画面はナビゲーションツリーの根になれない。
    • 引数名とかリファレンスの中のrouteっていうのがuser/{userId}のことなのかuser/1234のことなのかわからんことある。
    • パスやパラメータの名前を定数で何度も書かなきゃいけないのがだるいしtypoするかもなと思ったので結局オレオレフレームワークみたいなものを作った。面倒くささと引き換えに型で守られている安心感のようなものを得られて満足している。
  • AppBarのハンバーガーアイコンとUpボタンの切り替わりの便利なクラスとかアニメーションないなった。
  • rememberどこで使ったらいいかいまいちわからない。

まとめとか雑多な感想とか

  • YouTubeは動画配信プラットフォーム、Twitchはライブストリーミングプラットフォーム。
  • 最初はYouTubeだけだったので公式が便利になったらこのアプリは必要なくなるんだけど、Twitchと合わせることで情報を一か所に集めることができるからあらためて作る意味ができてよかった。
  • YouTubeの方は今の方法でも取得できない配信があるので要調査。サムネの更新も取れたり取れなかったりする。
  • Twitchの方はスケジュールと配信との対応のつけ方がわからないので要調査。
  • フリーチャットを別のタブに分けられたらいいな。どうやって分けるかが課題。今の実装では枠が作成された時期によっては取得できないのもなやみ。
  • マルチアカウント対応って必要?(YouTubeにログインするアカウントが複数あってそれぞれで登録してる人が異なるケース)
  • あらかじめわかっているチャンネルIDとかをアプリで持ってればSubscriptionsのリスト取得が必要ないし箱公式のアプリみたいなものが作れるかも?なおQuota上限*3
  • PCで見ることの方が多いので本音を言うとWebでやりたい*4GDCの設定が別途必要になるけどfor Webもやってみればいいだろうか。Androidアプリもそれはそれで需要ある。
  • GDCのセットアップとかソースコードのビルドとかができる人は自己責任で使ってみてください(アプリのパッケージIDを変えなきゃいけないかも?)。環境構築のサポートはできませんがソースコードの問題はissueやpull reqにて受け付けます。

*1:卒業をされるとこのプレイリストが非公開になる様子。こんな形で知りたくなかった…

*2:プレイリストには別のチャンネル(例えばサブチャンネルのような)に上がっている動画も含まれていることがあるので、ローカルキャッシュのDBでここら辺のIDに対して参照制約をつける場合は配慮が必要

*3:今の実装だと私一人で3,000/dayぐらい使っている。減らしたいけど登録数に比例するのでどうかなって感じ。

*4:Chrome Extensionとかの方が簡単に早くできたりしますか?