>> はじめに - 動いた後に残ったもの
完結編 でテレビは映るようになった.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 CBC │ 061 メ〜テレ│
│ 8/30(日) │ 8/30(日) │ 8/30(日) │
│ 09:00 番組A │ 09:00 番組D │ 09:00 番組G │
│ 09:30 番組B ←今│ 09:25 番組E │ 09:55 番組H │
│ 視聴中 │▶このチャンネル│▶このチャ…│
└─────────────┴─────────────┴──────────┘
← 指で払って隣の局へ → ここで印の意味を分ける必要が出た.
旧 タブの反転 = 視聴中の局 ← 横に払っても動かん
新 タブの反転 = ★今見ている列
緑の点 = ★視聴中の局 同じ印に2つの意味を持たせていたのが誤りだった.番組表を眺めている時,「今どこを見ているか」と「今どれが映っているか」は別物だ.
>>> ページが跳ねる
タブを押すとページが上に戻るという苦情が来た.原因はすぐ分かった.
タブを押す → 一覧を「読み込み中…」に差し替え
→ 高さが 58vh → 数十px に縮む
→ ページ全体が短くなりブラウザがスクロール位置を上へ押し戻す 中身を消さず,高さを据え置いたまま薄くして待つ形にした.切替ボタンも hidden ではなく visibility で隠す——場所を空けたままにするためだ.
画面の中で位置を持つ物を差し替える時は,先に場所を確保してから中身を入れ替える.「消してから作る」は書きやすいが,その一瞬に高さがゼロになる.
>> 不調の理由を画面に出す
ある夜,外出先から見ようとして見られなかった.帰宅して調べると B-CAS カードリーダーが応答しておらず,配信が321回再起動を繰り返していた.
監視は鳴っていた.だが画面には「見えない」としか出ていなかった.
⚠ B-CAS カードエラー — カードリーダーを挿し直してください
⚠ チューナー使用中 — 番組表の収集中の可能性があります
⚠ 配信が起動していません(再起動 321 回) /state に trouble を足し,配信が立っておらん時だけ 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 / 局 8個
08: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 カードが死んだ日の話だ.