#show tech >>log

Raspberry Pi 3B で自宅のテレビをどこでも見る ── 改修編① 見えるようにする

>> はじめに - 動いた後に残ったもの

完結編 でテレビは映るようになった.8局並び,音が出て,番組表も出る.

ここから先は使ってみて初めて分かったことの記録になる.3本に分けた.

① 見えるようにする     ← この記事
② 壊れても映り続ける
③ 外へ出す

最初に困ったのは,何かがおかしい時に「見えない」としか分からないことだった.

>> リモコンを作った

チューナーが1本しかないので,誰かがチャンネルを変えれば全員の画面が変わる.この性質は欠点に見えて,実はリモコンとして筋が通っている.

映像を持たないページを別に作った.

/remote/   局・画質・番組表・ログ
           映像を持たんゆえ 16KB。タブレットを立てかけっぱなしでも負担にならん

局のボタンには今やっている番組名を出した.何をやっているか見て選べるのがリモコンとして自然だ.

>>> 掼した後の見せ方

チャンネルを変えると配信が再起動するので,数秒は何も見えない.黙っているともう一度掼される.

掼す → 「切替中」ボタンは全部押せなくなる(二度押し防止)
     → /state に反映されたら「切り替わりました」
     → 30秒で反映が無ければ「確認できませんでした」

5秒ごとに /state を見るので,他の端末で変えられても追いつく.画面を戻した瞬間にも合わせにいく.

>> 番組表 - 495KB を数KB に削る

mirakc の番組一覧は1局 495KB ある.ページから直に引かせるのは論外だ.

GET /guide?sid=&hours=30
   ★時刻と題名だけに削って返す → 1局 数KB
   8局まとめても数十KB。これなら一度に取ってよい

番組名の掃除もしている.[字] [解] [再] や全角スペース, 以降のサブタイトルを落とす.

>>> 縦に1局 → 横に全局

最初はタブで局を切り替えて縦に1局分を出していた.だが使ってみると,他の局を見るのにいちいちタブを掼すのが面倒だった.

横スクロールに作り替えた.

┌─────────────┬─────────────┬──────────┐
│ 041 中京 視聴中│ 051 CBC061 メ〜テレ│
│  8/30(日)    │  8/30(日)    │  8/30(日) │
│ 09:00 番組A09:00 番組D09:00 番組G │
│ 09:30 番組B ←今│ 09:25 番組E09:55 番組H │
│   視聴中       │▶このチャンネル│▶このチャ…│
└─────────────┴─────────────┴──────────┘
      ← 指で払って隣の局へ →

ここで印の意味を分ける必要が出た.

旧  タブの反転 = 視聴中の局      ← 横に払っても動かん
新  タブの反転 = ★今見ている列
    緑の点     = ★視聴中の局

同じ印に2つの意味を持たせていたのが誤りだった.番組表を眺めている時,「今どこを見ているか」と「今どれが映っているか」は別物だ.

>>> ページが跳ねる

タブを押すとページが上に戻るという苦情が来た.原因はすぐ分かった.

タブを押す → 一覧を「読み込み中…」に差し替え
           → 高さが 58vh → 数十px に縮む
           → ページ全体が短くなりブラウザがスクロール位置を上へ押し戻す

中身を消さず,高さを据え置いたまま薄くして待つ形にした.切替ボタンも hidden ではなく visibility で隠す——場所を空けたままにするためだ.

画面の中で位置を持つ物を差し替える時は,先に場所を確保してから中身を入れ替える.「消してから作る」は書きやすいが,その一瞬に高さがゼロになる.

>> 不調の理由を画面に出す

ある夜,外出先から見ようとして見られなかった.帰宅して調べると B-CAS カードリーダーが応答しておらず,配信が321回再起動を繰り返していた.

監視は鳴っていた.だが画面には「見えない」としか出ていなかった.

B-CAS カードエラー — カードリーダーを挿し直してください
⚠ チューナー使用中 — 番組表の収集中の可能性があります
⚠ 配信が起動していません(再起動 321 回)

/statetrouble を足し,配信が立っておらん時だけ journal を見に行って理由を返す.平時は叩かない——/state は5秒ごとに引かれるからだ.

>>> 「立ち上がり中」と「立たん」は別物

入れた直後,普通にチャンネルを変えただけで「配信が起動していません」と出るようになってしまった.

切替では配信が再起動し,live.m3u8 が一度消える.「30秒以上新しくない」だけを見ていたので,正常な途中経過を異常と断じていた.

① こちらが起こした切替に 35秒の猫予 を置く
② こちらが起こしたのではない再起動も拾う
     systemd に起動からの経過を聞き,35秒未満なら黙る
>>> ✕ で閉じられるようにした

もう一つ,自動で消えない場合の逃げ道を要求された.調べるとこれもバグだった.

# 旧 —— tv-stream が «終わる» 時にしか消していない
finish() {[ "$MODE" = "copy" ] || rm -f "$REASON"; }
trap finish EXIT

動き続けている限り永久に消えない.「立ち上がる時に消す」のが正しかった.

そのうえで ✕ も付けた.ただし閉じても「直った」ことにはせん.同じ文言の間だけ黙り,問題が直って消えたら閉じた記憶も捨てる——再発すればまた出る.

>> ログを両方に

リモコンにもログを付けた.最初は事が起きた時だけ書く作りにしてしまい,開いても「まだ何もありません」のままだった.

空のログは壊れているようにしか見えない.開いた時点の状態も書くようにした.

08:52:01  SYS   いま 041 中京テレビ / low /808:52:00  SYS   登録済み: msair
08:51:59  SYS   リモコンを開いた

さらにサーバ側のログも見たいと言われた./log を作り,タブで切り替える形にした.

[ この端末 ][ サーバ ]

この端末  リモコン自身の動き(切替を頒んだ・別端末で変更)
サーバ    ★Pi の中(配信の再起動・監視の検知・mirakc)

雑音はサーバ側で落とす.dwc_otg だけで毎時880行あるので,送ってから捨てるのは無駄だ.systemd の定型行(Starting / Finished / Consumed)も落とす——胝心の行が押し出される.

>> この日踏んだ UI の落とし穴

技術ではなく,小さな判断が使い心地を左右するという話が多かった.

やってしまったこと なぜ悪いか
同じ印に2つの意味(タブの反転=視聴中+見ている列) どちらとしても読めない.分けるべき
中身を消してから作る その一瞬に高さがゼロになりページが跳ねる
hidden でボタンを消す 場所ごと消えてレイアウトが動く.visibility なら動かん
事が起きた時だけログを書く 開いた人には「何も動いていない」ように見える
22px のボタン 指で狙うには小さすぎる.34px あれば外さない

どれも使ってみて初めて分かったことだ.机上では「タブで切り替えればよい」と思っていたし,「ボタンは小さくても押せる」と思っていた.

>> もう一つ - エラーの出し方で遠回りした

番組表を作り替えた後,リモコンが「tv-switch に接続できません」と言い出した.

サーバを調べると全部正常だった(/state/guide/log も 200).原因はこれだ.

poll() の中で
  paint();
  paintTabs();   ← ★消した関数を呼んでいた
  buildGuide();

ReferenceError が .then() の中で起き,.catch() が「接続できません」として拾っていた.通信の失敗と自分のバグを区別していなかった.

>>> 検算を「思い出し」から「機械」へ

置換の度に「消したはずの名前」を列挙して数えていたが,思い出せなかった1つで転んだ.

# script 全体から «呼んでいる関数» と «定義している関数» を両方拾い,差を取る
defined = set(re.findall(r'function\s+(\w+)\s*\(', js))
called  = set(re.findall(r'(?<![.\w$])([a-z]\w*)\s*\(', js))
print(sorted(called - defined - builtin))
#   → 🔴 paintTabs を呼んでいるが定義が無い(script 内 234行目付近)

構文が通ることと,辻祺が合うことは別物だ.node --check は通っていた.消した名前が残っていないかを見るのが,いちばん安い確かめ方だった.

>> まとめ

  • 同じ印に2つの意味を持たせない.「見ている列」と「映っている局」は別物
  • 差し替える時は先に場所を確保する.消してから作ると高さがゼロになる
  • 空のログは壊れて見える.開いた時点の状態も書く
  • 雑音は送る前に落とす.端末へ送ってから捨てるのは無駄
  • .catch は通信の失敗と自分のバグを区別しない.遠回りの元になる
  • 消した名前が残っていないかを機械に洗わせる

次は改修編② 壊れても映り続ける.B-CAS カードが死んだ日の話だ.