Nostr タイムライン再生型クライアント

Nostr タイムライン再生型クライアント 要件書

1. 基本コンセプト

  • タイムラインを配信の追っかけ再生のように操作できる
  • 取得・表示の単位は件数ではなく時間による
  • 画面全体がYouTubeライブのコメント欄のようなタイムライン表示域になる
  • スクロール位置=再生ヘッド。動画画面は存在しない

2. 画面構成

┌─────────────────────────────────────────┐
│ ⚡ NostrPlayer    [追従中]  1.5x  🔊   │
├─────────────────────────────────────────┤
│                                         │
│  ┌─────────────────────────────────┐   │
│  │ 08:14:55  npub1abc...           │   │
│  │ ほげほげほげ                     │   │
│  └─────────────────────────────────┘   │
│                                         │
│  ┌─────────────────────────────────┐   │
│  │ 08:15:02  npub1def...           │   │
│  │ ふがふがふがふが  ◀ 再生ヘッド  │   │
│  │ ▶▶▶▶▶▶▶▶▶▶ 読み上げ中...       │   │
│  └─────────────────────────────────┘   │
│                                         │
│  ┌─────────────────────────────────┐   │
│  │ 08:15:10  npub1ghi...           │   │
│  │ ぴよぴよぴよ                    │   │
│  └─────────────────────────────────┘   │
│                                         │
├─────────────────────────────────────────┤
│ 08:15:02 ○━━━━━━━━━━━━━━━ 09:07:00      │
│ [<<] [⏸] [>>] [1.5x] [🔊] [● 追従]    │
├─────────────────────────────────────────┤
│ [🙂] なにかコメントする...        [投稿] │
│ ⚙️ 投稿時刻: [再生位置 / 現在時刻]      │
└─────────────────────────────────────────┘

3. 再生操作

操作 動作
開始時刻指定 過去の任意の日時を指定して、その時点から再生開始
追っかけ再生 現在時刻(LIVE)まで自動で追いかける。追いつくとリアルタイム表示になる
一時停止 自動スクロール・音声を停止。ユーザは自由に過去をスクロールして読める
倍速再生 1.0x〜2.0x。自動スクロール速度と音声読み上げ速度が連動する
シーク(スクロール) 手動で上下スクロール=時間軸を移動。指を離すとそこから再生再開
LIVEボタン 現在時刻に瞬間移動し、追っかけ再生を開始する

4. シークUI・時間スケール

4.1 表示ウィンドウ(View Window)方式

シークバーは**「今見ている時間幅」**を表す。右端・左端は状況で変わる。

モード 右端 左端
LIVE追っかけ 現在時刻 現在-表示幅
過去再生 表示範囲の終了時刻 表示範囲の開始時刻

4.2 ズームレベル

  • 1時間ビュー:直近の流れを追っかける。LIVEモードの基本
  • 1日ビュー:1日のTLを概観
  • 1週間ビュー:週単位で傾向を見る
  • 1年ビュー:1年分を1画面に。1日が数ピクセル。細かい操作は不可

4.3 具体例

LIVE追っかけ(1時間ビュー)

08:06  08:15  08:30  08:45  09:00 09:07
  |      |      |      |      |      |
  ├─────┼─────┼─────┼─────┼─────┤
  ▲                              ○
左端(1h前)                    右端=現在
              ● 08:45:10 ← 再生ヘッド

過去再生(1日前、1日ビュー)

00:00  06:00  12:00  18:00  24:00
  |      |      |      |      |
  ├─────┼─────┼─────┼─────┤
  ▲                        ▲
左端=0時               右端=24時
       ● 14:30 ← 再生ヘッド

ズームアウト(1年ビュー)

5月 6月 7月 8月 9月 10月 11月 12月 1月 2月 3月 4月 5月
 |   |   |   |   |   |    |    |    |   |   |   |   |
 ├───┼───┼───┼───┼───┼────┼────┼────┼───┼───┼───┼───┤
 ▲                                                      ▲
1年前                                               現在
       ▲ 再生ヘッド(2025/05/30)

4.4 遠過去へのアクセス

  • 日付ピッカーで直接ジャンプ → 表示幅が自動で1日に切り替わる
  • ズームアウトして1年ビューで概観を見てから、該当日にズームイン
  • 右端が「現在」になるのはLIVEモード時のみ。過去再生時は右端は「その日の24:00」など

5. 音声読み上げ(TTS)

5.1 基本動作

  • テキストを音声で読み上げながらタイムラインを進める
  • 音声の現在位置=テキストの再生ヘッド
  • 読み上げ中の投稿にはインジケータ表示

5.2 音声ON時の挙動

操作 音声動作
再生開始 先頭から順にキューに入れて読み上げ
倍速 TTS速度も連動(1.5x、2.0x)
追っかけ中 新着投稿をキューに自動追加
一時停止 音声停止。再開で続きから
手動スクロール(シーク) その投稿の先頭から読み上げ再開
音声OFF 通常のテキストスクロール再生に戻る

5.3 オプション設定

  • 声の割り振り:npubのハッシュで異なる声を割り当て、誰が話しているか判別しやすくする
  • 読み上げ対象:本文のみ / リプライ先も読む / 絵文字・メンションは無視
  • バックグラウンド再生:画面を閉じても音声は続行(Podcast感)

6. 投稿(kind1)

  • 常時下部に入力欄を表示。YouTubeライブのコメント欄にコメントするような感じでサクッと投稿できる
  • リプライは対象投稿を選択してリプライモードに切り替え

6.1 投稿時刻の基準(設定で切り替え)

設定 動作
現在時刻にしたがう(デフォルト) いつでも投稿した瞬間の現在時刻が created_at になる。過去再生中でも「今」の投稿として流れる
再生位置にもとづく 再生ヘッドの位置の時刻が created_at になる。過去のタイムラインを見ているとき、その時刻に「当時の投稿」として挿入される感じになる

6.2 投稿設定UIイメージ

┌─────────────────────────────────────────┐
│ ⚙️ 設定                                 │
├─────────────────────────────────────────┤
│  [投稿]                                 │
│  ┌─────────────────────────────────┐   │
│  │ 投稿時刻の基準                  │   │
│  │                                 │   │
│  │ ○ 現在時刻にしたがう            │   │
│  │    いつでも「今」の投稿として    │   │
│  │    公開される                   │   │
│  │                                 │   │
│  │ ● 再生位置にもとづく            │   │
│  │    再生ヘッドの時刻で投稿する。  │   │
│  │    過去のタイムラインに対して    │   │
│  │    当時のコメントを残せる        │   │
│  └─────────────────────────────────┘   │
│                                         │
│  [音声]                                 │
│  ┌─────────────────────────────────┐   │
│  │ 読み上げ速度: 1.5x              │   │
│  │ バックグラウンド再生: ON        │   │
│  │ npubごとに異なる声: ON          │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

7. 利用シナリオ

シナリオ 状況
A. LIVE追っかけ 今現在のTLを見たい。右端=現在、左端=1時間前。自動スクロールで新着を追う。音声ONにすれば新着を読み上げてくれる
B. 数時間前から追っかけ 3時間前のTLを指定して再生開始。1.5xで追っかけ再生。現在時刻に追いつくまで自動で進む。追いついたらLIVEモードに移行
C. 昨日のTLを1日分再生 日付ピッカーで「昨日」を指定。表示幅が1日に切り替わる。00:00から再生開始。音声ONで「昨日のTLを聴く」
D. 1年前の特定日を振り返る ズームアウト(1年ビュー)で概観を見る。日付ピッカーで2025/05/30にジャンプ。表示幅が1日に。一時停止しながら自由に読み漁る
E. 音声バックグラウンド(Podcastモード) 画面を見ずにタイムラインを聴く。倍速でダイジェスト再生。新着が入れば自動でキューに追加。通勤中・作業中に「Nostrを聴く」
F. 一時停止して過去を読む 再生中に気になる投稿を発見。一時停止 → 自由にスクロールして過去を遡る。未来の投稿はまだ表示されない(時間軸で区切られる)。読み終わったら再生再開

8. データ取得・キャッシュ

8.1 10分チャンク方式

  • タイムラインを10分単位の時間チャンクに分割して管理
  • 各チャンクは since=xx:00 / until=xx:10 の範囲指定で1回のREQで取得
  • キャッシュキーは relay + フィルタ条件 + 10分のタイムスロット
チャンク例:
[09:00:00〜09:10:00] [09:10:00〜09:20:00] [09:20:00〜09:30:00] ...

8.2 過去再生時の取得

  • 指定時刻の10分チャンクをREQ
  • 1回のREQで返ってくる件数がmax_limitに到達した場合(イベントが多い場合)、最後に取得したイベントの時刻を新しいuntilとして同じ10分範囲内で追加REQを行う
  • これは内部的な取得処理の分割であり、UIに「ページ」概念は出てこない
  • 全イベント取得完了後、その時刻からシームレスに再生開始

8.3 LIVE追っかけ時のリアルタイム取得

  • 購問型REQ(CLOSEしない)で常時受信
  • イベントは届いた瞬間に随時表示される
  • 同時に created_at で振り分け、後から10分チャンクに格納(キャッシュ化)
  • 表示はチャンク確定を待たない

8.4 リアルタイムデータのチャンク化

リアルタイム購問で流れてくるイベントを、「届いた時刻」ではなく「created_atの時刻」で振り分けて10分チャンクに詰めていく。

購問中(常時受信中)
    ↓
イベント到着
    ↓
created_at を見て 09:07:15 なら → [09:00〜09:10] チャンクに格納
created_at を見て 09:12:30 なら → [09:10〜09:20] チャンクに格納
    ↓
現在時刻が 09:10:00 を超えたら
[09:00〜09:10] チャンクは「確定」
→ メモリキャッシュに昇格 or ストレージに書き込み

8.5 遅延到着への対処

リレーによっては 09:08 のイベントが 09:11 に届く こともある。

状態 扱い
未確定チャンク(現在時刻がまだ区切りを超えてない) 随時追加OK
確定直後(区切りを超えてから数分以内) 遅延イベントとして受け入れ、チャンクに追加
十分経過後 そのチャンクはロック。遅延イベントは無視 or 手動リロード時に再取得
例:
09:10:00 を超えても、[09:00〜09:10] は「受け入れ猶予期間」として
あと2〜3分程度はイベントを受け入れる。
09:13:00 以降に 09:08 のイベントが来たら、基本的に無視

8.6 接続断・バックグラウンド復帰時の扱い(rx-nostr前提)

通常の接続断

  • rx-nostrが自動的に再接続処理を行う
  • アプリ側で特別な再接続ロジックは不要
  • 一時的な切断中はキャッシュ済み分の再生を継続し、復帰を待つ

ブラウザバックグラウンドからの復帰(全リレー切断状態)

ブラウザをバックグラウンドにしたり、スリープから復帰したりした場合、すべてのWebSocket接続が切断されていることがある。

この場合、rx-nostrの自動再接続後、以下の処理を行う:

  1. 最後に受信したイベントの時刻を確認
  2. 現在時刻までのギャップを計算
  3. Catch-up REQ:その間の未受信イベントを一括取得
    • ギャップ分の10分チャンクをまとめてREQ
    • 取得完了後、リアルタイム購問を再開
  4. 追っかけ再生の再開:取得した分を一気に追いつく、または倍速で追いつく
復帰時の流れ:

[バックグラウンド中:全リレー切断]
    ↓
[フォアグラウンド復帰]
    ↓
[rx-nostrが自動再接続]
    ↓
[最後の受信時刻確認:09:15:00]
[現在時刻:09:35:00]
    ↓
[Catch-up REQ] 09:15:00〜09:35:00 のチャンクを一括取得
    ↓
[取得完了] → 追っかけ再生再開

バックグラウンド時の挙動

状態 接続 再生
フォアグラウンド 購問型REQで常時受信 通常通り追っかけ再生
バックグラウンド(短時間) rx-nostrが維持を試みる。切れたら復帰時にcatch-up 音声はキャッシュ済み分を読み上げ続ける。新着は途切れる
バックグラウンド(長時間/スリープ) 全リレー切断。復帰時にrx-nostrが自動再接続後、catch-up REQで一気に取得 音声停止 or キャッシュ分で再生継続

再生への影響

  • 接続断中:キャッシュ済みのチャンクは再生を継続。新着ストリームは一時停止
  • 追っかけ再生中:接続断中は再生ヘッドが「LIVE」から遅れていく(実際の現在時刻との差が開く)。復帰後、取得した分を一気に追いつく or 倍速で追いつく
  • 音声:キャッシュ分は読み上げ継続。新着が途切れたらその区間で音声停止 or 待機

UI表示

  • 復帰時に全リレー切断が検知された場合、ステータスバーに 「🔄 同期中」 を表示
  • Catch-up REQの進捗を表示(取得中のチャンク数など)
  • 同期完了後、通常の 「● 追従中」 に戻す

8.7 キャッシュのライフサイクル

[リアルタイム受信]
    ↓
[created_at で振り分け] ──→ [未来の未確定チャンク](メモリ上のバッファ)
    ↓ 10分経過+猶予
[確定チャンク] ──→ [メモリキャッシュ(24h)] ──→ [ストレージ(1週間)]

8.8 キャッシュ管理

  • メモリキャッシュ:直近24時間分のチャンクを保持
  • ストレージ(IndexedDB等):直近1週間分を保持
  • 破棄方針:LRU(最後にアクセスしたものから破棄)で管理
  • 手動リロード:特定のチャンクを強制再取得する機能あり

9. 技術スタック・開発方針

  • フレームワーク: Tauri v2(デスクトップ + Android対応)
  • UI: Svelte 5(Runesによるリアクティブ設計)
  • Nostr通信: rx-nostr(Observableベースのストリーミング)
  • 音声: Web Speech API(ブラウザ標準TTS)
  • キャッシュ: IndexedDB(メモリ)+ Tauri FS(永続化)
  • ビルドターゲット: Windows / macOS / Linux / Android

注意点

  • Android版はバックグラウンド音声再生・接続維持に制約があるため、必要に応じてTauriネイティブプラグインを検討
  • 同じコードベースでデスクトップとAndroidを共通化する
Write a comment
No comments yet.