Xの投稿ネタを探し、画像を作り、ときに動画まで。やることが増えるほど、別々のツールを立ち上げて行き来する時間も積み上がる。検索からメディア生成までを一本のパイプラインに通せば、運用のリズムは一気に軽くなる。
目次
このガイドで作るもの
xAI Grok を中核に、X検索・画像生成・音声生成(TTS)・動画生成・動画+音声合成までを一つの Streamlit アプリで回すメディア生成パイプライン。検索結果はインフォグラフィック化して保存し、生成したメディアはそのまま投稿素材に使える。
最初の関門:Hermes で Grok 認証を通す(ここが無いと1枚も作れない)
派手な生成機能の前に、必ず最初に越える壁がある。Grok への認証だ。このパイプラインは認証を xAI の API キーではなく、SuperGrok の OAuth トークンで通している。仕組みはシンプルで、Nous Research の CLI「Hermes Agent」で Grok にログインし、保存されたトークン(~/.hermes/auth.json)を generate_media.py がそのまま読んで https://api.x.ai に Bearer で投げるだけ。記事の元コードもこの方式だ。ここが未設定だと、画像も音声も動画も1枚たりとも生成できない。だから順番として一番先に通す。
用意するもの:X(旧Twitter)の SuperGrok(または Premium+)サブスクリプション。「自分の SuperGrok アカウントとして Grok を使う」OAuth なので、別途の xAI API キーは要らない。裏を返すと、無料アカウントではこの方式は通らない。
手順1:Hermes をインストールする
公式インストーラを使う。Python・Node・ffmpeg などの依存ごと自動で入るので、これ1本でいい。
# Windows(PowerShell)
iex (irm https://hermes-agent.nousresearch.com/install.ps1)
# macOS / Linux / WSL2
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
入ったら確認する。hermes doctor は足りない依存を名指しで教えてくれるので、最初に一度回しておくと後が楽だ。
hermes --version # 「Hermes Agent v0.14.0 ...」のように出れば OK
hermes doctor # 不足があればここで判明する
手順2:xAI(Grok) で OAuth ログインする
プロバイダ選択から「xAI Grok OAuth」を選ぶ。ブラウザが立ち上がるので、SuperGrok を契約している X アカウントでサインインして許可する。これだけだ。
# 対話ウィザードから選ぶ
hermes setup # → プロバイダで「xAI Grok OAuth」を選択
# xAI だけを直接認証したいなら
hermes login --provider xai-oauth
許可が終わると、トークンが ~/.hermes/auth.json に書き込まれる。この1ファイルが、アプリと Grok をつなぐ鍵になる。
手順3:通っているか確認する
hermes status # 「Provider: xAI Grok OAuth」が出ていれば認証済み
ここまで来れば、アプリ側(generate_media.py や Streamlit の app.py)は同じトークンを読むだけで動く。アプリに鍵を別途貼り付ける作業は一切ない。
トークンの寿命と、止まったときの戻し方
OAuth トークンには寿命がある。ここを知らないと、ある日突然「昨日まで動いていたのに全部エラー」で固まる。
- アクセストークンは約6時間で失効するが、通常は内部のリフレッシュトークンで自動更新されるので、普段は気にしなくていい。
- 問題はリフレッシュトークン自体が失効(revoked)したとき。自動更新も止まり、生成が一斉に落ちる。
~/.hermes/auth.jsonにinvalid_grant/relogin_requiredが残っていれば、これだ。 - 戻し方は入れ直すだけ。
hermes login --provider xai-oauth(またはhermes setup)でもう一度ログインすれば回復する。 - 鍵はコードに直書きしない。トークンの管理は
~/.hermes/auth.jsonに一元化し、アプリ側の.envや定数に貼り付けない。漏れたときの出どころを1か所に絞れる。
API キーをサッと貼るやり方に慣れていると遠回りに見えるかもしれない。ただ、すでに払っている SuperGrok のサブスクを、そのまま生成の認証に使い回せるのがこの方式の旨みだ。(どうしても API キーで始めたい場合は、generate_media.py の get_token() を API キー読み込みに差し替える必要がある。本記事のコードは Hermes トークン前提だ。)
規約についての注意。この OAuth トークンは xAI が SuperGrok 利用者向けに発行するもので、公式アプリ以外からの利用がどこまで許容されるかは xAI の利用規約の解釈と変更に依存する。規約の改定や認証方式の変更で、ある日使えなくなる可能性は常にある。継続的・業務的に使うなら、正規の xAI API キー(従量課金)での利用が確実で、本記事の方式を使う場合も各自で現行の利用規約を確認してほしい。
例えば、投稿素材1本はこう出来上がる
パイプライン全体を、実際の1本で通してみる。「今週のAIトピックで動画付きポストを作る」場面だ。①X検索でトピックの反応を集め、要点をインフォグラフィックPNGに起こす。②投稿文の読み上げをTTSで mp3 に生成する(voice_id は ara などの名前指定)。③Grok Video で短い映像素材を生成し、ジョブの request_id をポーリングして完成を待つ。④ffmpeg で映像と音声を合成し、final-*.mp4 が assets/日付/ に落ちる。ここまで全部、同じ generate_media.py のサブコマンドで完結する。
1本あたりの人間の仕事は、トピックを選ぶことと、出来た素材を投稿前に確かめることの2つ。生成の待ち時間はあるが、手が塞がる時間はほとんど無い。「別々のツールを行き来していた頃の疲れ」の正体は切り替えの摩擦だったと、一本化してみると分かる。

✨ 全部生成して合成を押すと ffmpeg 合成まで一気に走る。「投稿素材1本」を作る入口が、ここに集約されている。設計の起点:生成の各機能を1つのCLIに集約する
画像・音声・動画を別々のスクリプトで管理すると破綻する。generate_media.py に画像/TTS/動画/合成のサブコマンドを集約し、アプリからもCLIからも同じ口で叩けるようにした。生成の入口を一本化しておくと、新しいメディア種別を足すのが楽になる。

generate_media.py の口を叩く。作り方の要点
1. 検索結果を「見せる形」にする
X検索の生データは投稿に使いづらい。matplotlib で要点をインフォグラフィックに起こして保存する。レイアウトは GridSpec より add_axes の絶対座標配置の方が、セクションの重なりを避けやすい。

search_infographic.py)。2. 動画と音声は ffmpeg で合成する
動画生成と音声生成を別々に作ったら、最後は ffmpeg で1本に合成する。生成(Grok)と合成(ffmpeg)を分けておくと、片方だけ差し替えられる。
3. 外部APIの新機能は「いきなり本番投入しない」
これは痛い教訓だ。検証していない外部APIの新機能を全リクエストの既定経路に入れると、その機能が落ちた瞬間に通常動作まで全滅する。新機能はまず単発で疎通確認し、限定適用+実績のある経路へのフォールバックを用意してから組み込む。
Grok を実運用に乗せる「詰まりどころ」
ここからは、実際に動いている X_research の実装に沿って、生成パイプラインの作り方と、Grok API で実際に踏んだ落とし穴を全部出す。ドキュメントに書いていない所でこそ時間が溶けるからだ。
generate_media.py:1ファイル=1入口に集約する
画像・TTS・動画・合成を、generate_media.py のサブコマンドに集約する。アプリ(Streamlit app.py の6ページ)も、手動運用も、同じこの口を叩く。生成結果は日付で自動整理する。
assets/YYYY-MM-DD/
image/ image-*.png ← Grok Imagine
voice-ogg/ tts-*.mp3 ← Grok TTS
video-mp4/ video-*.mp4 ← Grok Video(素材)
final-*.mp4 ← ffmpeg 合成済み
「いつ・何を作ったか」がフォルダ構造だけで分かるので、後から投稿素材を探す手間が消える。

assets/日付/ 単位で生成物を一覧する。画像・インフォグラフィック・音声・動画が日付フォルダに整理され、後から投稿素材を探しやすい。Grok API の地雷:ドキュメントの行間で落ちる
ここが本ガイドで一番価値のある部分だ。xAI の API は、ちょっとした表記揺れで黙って落ちる。実際に踏んで直したものを並べる。
- TTS の
voice_idは名前。数字は不可。"4"を渡すと Voice not found。ara/eve/rex/sal/leoのような名前で指定する。 - TTS の codec に
oggは通らない。422 エラーになる。mp3/wav/pcmのいずれかを使う。 - 動画エンドポイントは複数形。
/v1/video/generationsは 404。正しくは/v1/videos/generations。 - 動画ジョブの ID は
request_id。job["id"]はNoneが返る。 - 完了動画の URL は
status["video"]["url"]。status["output"]["url"]は KeyError。
どれも単体では些細だが、組み合わさると「なぜか動かない」に化ける。API クライアントは薄いラッパにして、これらを1か所に閉じ込めておくと事故が減る。

ara のような名前で指定する(数字の voice_id は通らない)。言語とサンプルレートもこの画面で選ぶ。新機能を本番に入れて全滅させた話(そして対策)
前半の「いきなり投入しない」の実例だ。Grok の Live Search(search_parameters)を全応答の既定経路に常時有効化したところ、その機能が廃止済みで全リクエストが HTTP 410 になり、それまで動いていた通常チャットまで巻き添えで全滅した。教訓は2つ。①新機能はまず単発で疎通確認してから既定経路に入れる。②意図検出などで限定適用し、ダメなら実績のある経路へフォールバックする。「全部に効かせる」前に「壊れても通常動作は守る」設計を先に置く。
インフォグラフィックの実装メモ
X検索→可視化は search_infographic.py。matplotlib は GridSpec+hspace だとセクションが重なりやすいので、fig.add_axes([left, bottom, width, height]) の絶対座標配置で組む。保存時の bbox_inches に "off" を渡すと例外になる(省略するか "tight")。地味だが、ここで何度もレイアウトをやり直すと時間を食う。
正直なまとめ
このパイプラインの肝は、「入口を1ファイルに集約し、API の地雷を1か所に閉じ込め、新機能は限定適用+フォールバックで守る」運用設計にある。外部APIは仕様変更で挙動が変わる。だからこそ、壊れ方を穏やかにする作りが効く。
👉 関連: 開発実例:X Media Studio ・ ほかのアプリの作り方




