Skip to content

feat(signage): broadcast-style会場サイネージ+チャイム(YouTube動画 / 50-10サイクル / 巡回インフォ)#37

Merged
ut42tech merged 36 commits into
developfrom
feat/signage-youtube
May 30, 2026
Merged

feat(signage): broadcast-style会場サイネージ+チャイム(YouTube動画 / 50-10サイクル / 巡回インフォ)#37
ut42tech merged 36 commits into
developfrom
feat/signage-youtube

Conversation

@ut42tech
Copy link
Copy Markdown
Owner

概要

会場サイネージ(壁掛けモニター向け・配信風 PWA)apps/signage を新規追加。YouTube プレイリストの動画を縮小パネルで流しつつ、50分活動 / 10分休憩のリズムを Web Audio チャイムと画面遷移で知らせ、右レーンに次チャイムまでのカウントダウン、下部に巡回インフォメーションを表示する。

主な内容

フロント(apps/signage — Next.js 16 / React 19, dev :3002)

  • YouTube IFrame Player API の自前キューENDED/onError で次 videoId へ・関連動画UIを実質抑止)
  • 50/10 サイクル+種別チャイム(再開/休憩/ターム終了)、ドリフトしない自己補正スケジューラ
  • 配信レイアウト:縮小動画パネル+右レーン(カウントダウンリング・次の予定・ターム進行タイムライン)+下部巡回ティッカー(来場・にぎわい・稼働状況・公式Instagram QR・前回の情報・動画タイトル)
  • 数字カウントダウンの桁ロール演出、ストーリーズ風進行バー、reduced-motion 全面尊重clamp() で 4K〜小型まで対応
  • メンター認証(共有アカウント運用・MeProvider)、キオスク(横向き/全画面/wake lock)、無音トグル+BGMは OS側 Spotify 前提
  • ?debug=1 プレビュー(擬似時計・倍速・稼働強制・手動チャイム・ティッカー手動送り)で実時刻を待たず全状態と各遷移を検証可能

API(apps/api — Hono on Cloudflare Workers / D1)

  • GET /api/signage/playlist(YouTube Data API・Worker キャッシュ)
  • GET /api/signage/previous-summary(前回開催の来場人数・平均滞在=集計のみ・PIIなし)
  • GET /api/signage/health(基盤システムの稼働確認)

共有(packages/shared

  • activity-cycle(50/10+チャイム時刻)/ attendance-level(にぎわい・想定上限25人)/ visit-summary(ユニーク来場集計)/ youtube(Data API ラッパ)+ vitest 31件

設定・運用メモ

  • 新規 env:YOUTUBE_API_KEY / YOUTUBE_PLAYLIST_ID(Worker シークレット)、TRUSTED_ORIGINS にサイネージ origin を追加、フロントは NEXT_PUBLIC_API_URL
  • 依存追加:qrcode.react(Instagram QR)、motion@types/youtube
  • DB スキーマ変更なし(migration 不要)。Google OAuth のリダイレクト URI 追加も不要(TRUSTED_ORIGINS への追加のみ)
  • 本番は API 再デプロイ(pnpm --filter @tecnova/api deploy)と signage の Vercel デプロイが必要

検証

  • biome クリーン(169 ファイル)/ 型チェック 7/7 / vitest 31件 / signage build 全ルート生成 / dev スモーク 200
  • 3観点のコードレビュー(バックエンド・ランタイム・UI)実施、Critical 0。指摘(16:00 同時チャイムの重なり防止・境界テスト追加・ドキュメント先行・a11y/メモ化)を反映

🤖 Generated with Claude Code

ut42tech and others added 30 commits May 30, 2026 13:09
Replace the self-host HTML5 <video> design with YouTube playback:
- video list managed in a YouTube playlist, fetched via a new
  GET /api/signage/playlist (Data API v3, server-side, API key, cached)
- self-managed queue (IFrame Player API, loadVideoById on ENDED) to
  suppress related/end-screen/up-next UI; rel=0/modestbranding noted as
  ineffective, so suppression depends on the queue swap
- ad reality: embedder cannot strip ads; ad-free only via YPP channel
  with monetization off (own uploads) or self-host (kept as fallback)
- audio: global mute toggle (default muted) + OS-level Spotify; app does
  not integrate Spotify (no SDK/OAuth)
Chime/cycle/auth/sessions-polling unchanged. Verified against current
YouTube/Spotify docs and adversarially reviewed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ll, login)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ke lock hook

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… toggle

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…+ mute)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- onReady now starts the first video if the playlist populated between player
  construction and onReady (the videoIds-change rescue effect bails while
  readyRef is still false, leaving the player invisible/stuck).
- Drop the unsafe `window.YT as typeof YT` cast in favor of a guard.
- Reuse shared SignagePlaylistResponse type in the API lib (drop dup interface).
- Remove pointless useCallback on isTermActive (dep is a fresh object each poll).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… chimes)

A gated debug control bar lets a developer verify every state/transition/chime
without waiting for the real wall clock. Zero production impact: with no
?debug=1, debugEnabled stays false and every new branch short-circuits to the
prior behavior.

- now.ts: module-singleton debug store co-located with getNow. getNow priority
  is debug pseudo-clock (anchor + speed + play/pause, continuity-preserving
  reanchor) -> ?now anchor -> real clock. useSyncExternalStore snapshot.
- use-chime-scheduler.ts: jumpEpoch suppresses catch-up replay on forward jumps
  so a -5s landing fires exactly one boundary chime; backward re-jump replays it.
- debug-panel.tsx: ?debug=1-gated bottom bar — term select, force-active,
  preset '5s-before' jumps, x1/x30/x120 speed, play/pause, manual chimes,
  reset-to-real-clock, live readout (readout mirrors the screen's active gate).
- page.tsx wires the store (forced-active OR, jumpEpoch, conditional panel).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
来場者数を quiet/steady/lively/crowded に分類する純粋ロジック。
サイネージのにぎわい・混雑表示に使う。しきい値は会場規模に合わせて
調整可能な暫定値。境界値のテストを vitest で追加。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…player

usePlaylist が videoId だけでなくタイトル付き SignagePlaylistItem[] を返すように。
タイトルは API(YouTube Data API)由来で既にレスポンスに含まれていたがクライアントで
捨てていた。useYoutubePlayer に onVideoChange コールバックを追加し、いま再生中の
トラックを購読側へ通知できるようにする(ブロードキャスト下部インフォの動画タイトル表示用)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
配信(ライブ配信)風レイアウトに刷新。checkin と同じ世界観(sky→white の地・
LINE Seed JP・emerald のライブ表現・motion + reduced-motion 尊重)で統一する。

- 動画を縮小したパネルに収め、休憩/待機はパネル上のスライドへクロスフェード
- 右レーン=チャイムの役割を持つゾーン(ターム・次チャイムまでのカウントダウンリング・
  サイクル進捗、idle 時は本日のスケジュール)
- 下部 lower-third に巡回インフォメーション(動画タイトル・来場・にぎわい・主催/共催・
  tec-nova / tecnova-platform 紹介)。?debug=1 で手動送り
- ヘッダにワードマーク・ステータスバッジ・大きな JST 時計
- 起動ゲート・音声トグルも同じ世界観へ。旧 InfoBar/BreakScreen/IdleScreen を置換

長崎市ロゴはリポジトリに無いため、共催クレジットはテキスト表記(実ロゴ差し替え可)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ログイン画面・MeProvider のローディング/権限エラー・body・manifest/themeColor を
配信レイアウトの明るい地(sky→white)に統一。ログインにロゴと入場アニメを追加。
signage/CLAUDE.md と設計 spec にブロードキャスト UI への刷新を追記(doc 先行の原則)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
一番混んでいる状態を同時滞在25人と定義(ATTENDANCE_CAPACITY)。にぎわいレベルの
しきい値を 7/13/19 に再設定し、25人で crowded(満員)に到達。メーター用の
occupancyRatio(0..1・上限クランプ)を追加。境界値・クランプのテストを更新。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
数字のカウントダウンを桁単位の縦ロール(odometer)でアニメーション。
新規 <AnimatedTime> を countdown-ring 中央と break-overlay 巨大表示にドロップイン。
毎秒は一の位だけ静かに動き、reduced-motion / 値なしは素の文字で副作用ゼロ。
休憩スライドの文言も子ども向けに(さんかちゅう→あそびちゅう)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lides)

巡回の時間源を単一 AnimationFrame に一本化。進行バーはストーリーズ風(通過済み満タン/
active が満ちる)にし、満ち=送りに同期。スライドはクリップ内スライドアップ+ラベル→値
スタッガー、アイコンは key 切替時のみ spring。進行バーの満ちは MotionValue 駆動で本体を
毎フレーム再レンダしない(StoryProgress は animate 時のみマウント=reduced/単一時は RAF 不要)。

スライド内容を更新:にぎわいは上限25人のメーター、現在人数は『いま会場にいる人』。
コンセプト/主催・共催スライドを廃し、公式 Instagram と『前回のテクノバ』(config 設定時)を追加。
プラットフォーム紹介は GitHub 表記をやめ『テクノバを支える基盤システム。チーフメンターの
たくやが開発・運用しています。』に。静的スライドは src/config/info-slides.ts で運用者が編集可。
動画タイトルは末尾に置きインデックスずれを防止。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
右レーンの間延びを解消:リング→次の予定(UpNext・次2件のチャイム)→ターム進行タイムライン
(3サイクル×活動/休憩の6セグメント+現在位置マーカー)の4階層に再構成し、冗長な
『サイクルN/3+3ドット』を撤去。低い縦解像度向けに overflow ガードとリング下限を調整。
動画パネルの shadow-xl を微細リング+拡散の弱い影+トップハイライトに差し替え(安っぽさ解消)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ユーザー指定の文言修正:放送を連想する『オンアエア』を避け活動中ステータスは『活動中』に、
公開面の人数表現は『在館』を避け『あそびちゅう』系に。配信フレームに staggered な入場
アニメ(Reveal)を追加。にぎわいメーター移行で不要になった ATTENDANCE_META.fill を削除。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
来場セッションから「ユニーク来場者数」と「1人あたり平均滞在(分)」を集計する純粋ロジック。
sessions は (participantId, eventId) に一意制約が無く、退館→再入館で同一人物が複数行に
なる(週末の昼休み運用)。人数はユニーク参加者で数え、平均滞在は同一人物の複数区間を
合算してから平均する。境界値を vitest でカバー。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GET /api/signage/previous-summary:JST の今日より前で最新の開催日を辞書順で引き、
そのセッションを集計して来場人数・平均滞在を返す(集計のみ・PII なし)。
GET /api/signage/health:基盤システムの稼働確認(D1 への軽量クエリ)。公開 /health は
CORS 対象外で別オリジンのサイネージから叩けないため、CORS 済みの /api 配下に置く。
DB スキーマ変更なし(migration 不要)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…a QR call-out

下部インフォを更新:
- 「前回のテクノバ」= 前回開催の来場人数・平均滞在(/api/signage/previous-summary)
- プラットフォーム枠を“紹介”から“稼働状況”へ(/api/signage/health を ping し 稼働中/障害表示)
- 公式 Instagram は QR を細い帯に小さく載せず、スライド中だけ白カードでティッカー上へ
  大きくせり出すコールアウトに(qrcode.react 追加)
- 値の font-black をやめ font-bold に(Black 過多の見た目を緩和)
- ティッカーを固定高さ+中央寄せにし、スライド切替で高さが動かないように
静的設定(Instagram ハンドル)は src/config/info-slides.ts。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ut42tech and others added 6 commits May 30, 2026 20:09
ヘッダのステータスバッジの脈動(ソナー)アニメは描画が安定せず途切れて見えるため廃止し、
静的な点+ラベルにする。未使用になった AIR_STATUS_META.pulse も削除。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…atar

QR コールアウトを Instagram ブランドグラデのフォローカードに。アバター(円形)+ハンドル+
『フォローしてね』のヘッダ+白地の QR。実プロフィール画像は public に置いて config の
INSTAGRAM_AVATAR にパス設定で差し替え可。未設定時はブランドアイコンで代用。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
カードが狭く @tecnovanagasaki がはみ出して切れていたのを修正。幅を広げ、ハンドルを
nowrap+tracking-tight で少し詰めて収め、QR も大きく、余白・角丸・影を整える。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Instagram ブランド装飾(グラデヘッダ+アバター)とカード幅広げをやめ、従来のシンプルな
白カード(QR +『フォローしてね @handle』)に戻す。INSTAGRAM_AVATAR 設定も撤去。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- チャイムスケジューラ: 同一インスタントに重なるイベント(隣接タームの term-end@16:00 と
  resume@16:00 が両方稼働中など)を稼働中1件だけ鳴らし、音の重なりを防止(通常運用は排他で従来同値)
- visit-summary: 一部のみ退館・同一人物の開/閉混在の境界テストを追加
- /api/signage/health: count() に冗長な limit を除去
- info-ticker: デバッグ送りボタンと QR にアクセシブル名(aria-label/title)を付与
- page: videoIds を useMemo で安定化(プレーヤー救済 effect の毎レンダ実行を回避)
- spec に previous-summary/health エンドポイントと visit-summary を追記(ドキュメント先行)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tecnova-platform-admin Ready Ready Preview, Comment May 30, 2026 11:36am
tecnova-platform-checkin Ready Ready Preview, Comment May 30, 2026 11:36am

@ut42tech ut42tech merged commit efda9bb into develop May 30, 2026
4 checks passed
@ut42tech ut42tech deleted the feat/signage-youtube branch May 30, 2026 11:37
ut42tech added a commit that referenced this pull request May 30, 2026
Reflect the shipped signage app (PR #37) in the human-facing docs:

- README: app roster, feature table, architecture/deploy diagrams, repo
  tree, dev-port tip, deploy targets
- requirements: Phase 1.5 entry, new §9.4 screen spec, tech-stack table,
  monorepo tree, appendix B (signage design-decision log)
- mvp: new §6.4 /api/signage/* endpoint spec, setup/env/deploy updates
- handoff: shipped-to-develop status, endpoints, remaining work, gotchas

Light-touch and additive; endpoint shapes verified against
packages/shared/src/schemas/signage.ts. No secrets or production domains.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant