テレビを録って観るためのもの。チューナーエージェント(選局) と denpa(番組表・予約・録画・エンコード・配信・ライブ視聴) の2つだけで、 メディアサーバは置きません。
チューナー ── エージェント ── denpa ── 録画(mkv) ─┬─→ ブラウザでそのまま観る
└─→ Nova (WebDAV) / 落として好きなプレイヤーで
エージェントはチャンネルを掴んで素のTSを流すだけです。番組表を読むのも、 局を選り分けるのも、CMを見つけるのも denpa がやります。
- チューナー — Linux DVB (PT2/PT3、PX-S1UD など)。ドライバはホスト側に入れておく
- B-CASカード と PC/SC 対応のリーダー
- Docker か Kubernetes
イメージは公開してあるので、リポジトリを持ってくる必要はありません。
mkdir denpa && cd denpa
curl -Lo compose.yml https://raw.githubusercontent.com/danything/denpa/main/compose.prod.yml
docker compose up -d
# 入るためのパスワード。初回の起動で作られ、ここに1度だけ出ます
# (まだ出ていなければ、起動を待ってもう一度)
docker compose logs denpa | grep ベーシック認証- 開く — http://localhost:3000。ユーザー名は
denpa、パスワードは上のログ - チューナーを確かめる — 「チューナー」に、見つかったものが並んでいます。 本数と種別 (地上波 / 衛星) が合っていれば、そのまま次へ。 違っていたら 同じ画面で直します (LNB や「1本だけ止める」もここ)
- スキャンする — 同じ画面から。チャンネルは空で出荷しているので、これをやるまで 番組表も空です。地上波の総当たりで十数分
- 待つ — 終われば自分で番組表を集めに行きます。空いているチューナーの数だけ 並べて回るので数分
- 予約する — 番組表から選ぶか、「ルール」にキーワードを登録して自動で
設定ファイルを書く必要はありません。 チューナーはエージェントが /dev/dvb/* を
開いて、地上波か衛星かまで自分で判別します。直すところがあっても「チューナー」から
書けます。
うまくいかないときも「チューナー」を見てください。 エージェントとカードリーダーの 状態、スキャンの1チャンネルごとの結果、番組表がどこまで集まったかが出ます。 カードリーダーが NG のまま録ると、成功したように見えて中身が全部スクランブル されたままになります。
指しているのは latest で、これはリリースを作ったときだけ動きます。 main へ
入ったぶんは develop に積み上がるので、作業中のものが勝手に降ってくることは
ありません。版を固定したいなら 0.1.0 のように書けます (0.1 と書くと
その系列の最新に付いてきます) — docs/architecture.md。
curl -L https://github.com/danything/denpa/archive/refs/heads/main.tar.gz | tar xz --strip=1 denpa-main/k3s
kubectl apply -f k3s/k3s/ は自分のクラスタ向けの例です。そのままでは使えないので、
namespace・StorageClass・Ingress のホスト名を書き換えてから当ててください。
必要なものは docs/architecture.md。
「ライブ」を開くと、放送中のものがそのまま観られます。 左に映像、右にチャンネル (番組表と同じ並び)。前回見ていた局から開くので、テレビを点けたときと同じです。
- 遅延は 0.5〜0.7 秒(焼くのに 0.24〜0.49 秒、貯めに 0.2 秒〜)。宅外から見て 止まるようなら、自分で貯めを伸ばして戻します
- 止めた所から見られます。 止めている間も受け取り続けるので、電話に出て 戻ってきても追いつけます (5分ぶん)。追いかけるときは速さを選べて (最大2倍)、 追いついたら自分でライブに戻ります
- 音声を選べます。 二カ国語の主音声/副音声も、解説放送のような音声1/音声2 も
- 焼き方を見ながら選べます。 H.264 はどの端末でも出るほう、AV1 は軽いほう (宅外向け)。AV1 は放送の今から1秒ぶん離れます。出せない端末では H.264 に戻して、 戻した理由を出します
- 字幕を出せます。 放送どおりの絵で出るので、録画で見る字幕と同じです (外字も崩れません)。字幕を持っている番組でだけボタンが出ます
- データ放送を出せます (d ボタン)。テレビと同じ BML がそのまま動くので、 ニュース・気象情報・番組連動のページが出ます。指で押せるリモコン (十字・決定・色・数字) が右に並びます。天気や地域のニュースを地元のものに するには、設定に郵便番号を入れてください (docs/stream.md)
- Hybridcast は、載っている番組で行き先だけ出します。 別のタブで開くだけで、 denpa は動かしません — 受信機の API をアプリの中に用意できず、放送局の サーバも認証された受信機を期待するためです (docs/stream.md)
録画一覧の行を押すと、その場で再生が始まります。 別のアプリは要りません。 番組の中身は左に並んで出ます (狭い画面では映像の下)。
- スマホ・タブレットは開いた時点で全画面、PC は左に番組の中身・右に映像
- どこを押しても再生と一時停止 (指のときは YouTube と同じで、押すと操作が出ます)
- 左右の端を素早く2回で 10秒 戻す/送る。続けて押せばそのぶん重なります
- CM 飛ばし。 CM はチャプターとして入っているので、送りのボタンで飛ばせます
- 観終わったその場で消せます。 末尾はたいてい CM なので、流したまま押せます
焼き上がるまでは観られません。 放送そのままの TS は MPEG-2 で、ブラウザに 復号器が無いためです。その間と、エンコードに失敗した録画は、詳細から落として お手元のプレイヤーで観てください。
- 字幕が出ます。 放送どおりの絵で出るので、テレビで見る字幕と同じです (ルビも外字も崩れません)。持っている録画でだけボタンが出ます
- 倍速で観られます (1〜2倍)。選んだ速さは覚えています
- 切り抜けます。 いまの場面を字幕ごとクリップボードへ
- 続きから始まります。 途中で止めたところを覚えていて、別の端末で開いても続きます
Nova Video Player から観るときは /dav を WebDAV サーバーとして追加します
(Android TV・Fire TV も同じ)。字幕はそのまま出ます — 入れ物の中に PGS で
入っています (docs/library.md)。
何も設定しなくても、掛かった状態で上がります。 初回の起動で、ベーシック認証が 無ければその場で作ります。以前は設定するまで録画も WebDAV も誰でも取れる状態で、 しかも掛け忘れに気付く手立てがありませんでした。
ユーザー名は denpa で固定。パスワードは24文字を自動で作り、起動のログに
1度だけ出します。
[boot] ベーシック認証を作りました: denpa / abYHnrdeniq7npvdZNkakKQV
設定画面から見直せます。プレイヤー (Nova) にも同じものを入れてください
以降は設定画面のベーシック認証の欄にそのまま出ています (目のボタンで表示、隣でコピー)。 Nova にも同じものを入れます。
遠くのサーバに入れて、起動のログを流してしまったときです。画面を開くにも
そのパスワードが要るので、DBから直に読みます (像に bun が入っています)。
docker compose exec denpa \
bun -e 'import {Database} from "bun:sqlite"; const db = new Database(process.env.DENPA_DB ?? "/app/data/denpa.db", {readonly: true}); console.log(db.query("SELECT value FROM settings WHERE key = ?").get("basicAuthPassword")?.value)'kubectl -n denpa exec deploy/denpa -- \
bun -e 'import {Database} from "bun:sqlite"; const db = new Database(process.env.DENPA_DB ?? "/app/data/denpa.db", {readonly: true}); console.log(db.query("SELECT value FROM settings WHERE key = ?").get("basicAuthPassword")?.value)'作り直すのは最後の手段です — 登録済みのプレイヤーが全部つながらなくなります。
家の中の端末に毎回パスワードを入れさせたくないときは、通す網を書けます。
TRUSTED_NETWORKS=10.10.0.0/16
ADDRESS_HEADER=x-forwarded-for # 前段にリバースプロキシを置いているときここから来た相手にはベーシック認証も OIDC も掛かりません。プレイヤー (Nova) に資格情報を入れずに使わせるためのものです。CIDR の カンマ区切りで、いくつでも並べられます。
ADDRESS_HEADERを忘れると誰も当たりません。 プロキシ越しだと、接続元として プロキシの住所が見えるためです。逆に、denpa へ直に届く経路が残っていると ヘッダを詐称できます — 前段を通してしか触れないことが前提です。
ベーシック認証だけでインターネットに晒さないでください。 資格情報が1つきりで、 誰が入ったのか分からず、切りたいときはパスワードごと替えるしかありません (=登録済みのプレイヤーが全部つながらなくなります)。
3つ渡すと、画面のほうは OIDC でのログインに替わります。
OIDC_ISSUER=https://login.microsoftonline.com/<tenant>/v2.0
OIDC_CLIENT_ID=...
OIDC_CLIENT_SECRET=...
OIDC_GROUP=... # このグループに居る人だけ通す (省くと入れた人は全員)ファイルを取りに来る口 (/api/recordings/<id>/file と /dav) はベーシック認証の
ままです。 プレイヤーがログイン画面へのリダイレクトを扱えないためで、
だから「画面は OIDC・プレイヤーはベーシック認証」の2本立てになります。
入れ方は docs/auth.md に。
- docs/architecture.md — なぜこの形なのか (決めたこと・踏んだ落とし穴)
- docs/app.md — どこに何があるか (ファイル・環境変数・画面・状態遷移)
- docs/data.md — エージェントに都度聞くもの / denpa が持つもの
- docs/development.md — 手を入れるとき (開発環境・テスト)
- docs/player.md — ホーム画面に置く、LAN でも https で開く
- docs/agent.md — チューナーを掴むところ (エージェント・取り合い・B-CAS)
- docs/encode.md — CM とエンコード (字幕・AV1・CM検出)
- docs/auth.md — 誰を通すか (OIDC でのログイン・ベーシック認証)
- docs/migrate.md — EPGStation からの引き継ぎ
- docs/roadmap.md — これから入れるもの
- docs/stream.md — ライブ視聴 (放送中のものを観る)
AGPL-3.0-or-later (LICENSE)。
denpa は自分の家に置いて、外から使うものです。ネットワーク越しに使わせる形で 配るなら、そのときの中身も同じ条件で渡せるようにしてほしい、という選び方です。
抱えているものは出どころのままです:
| src/lib/vendor/web-bml | MIT (LICENSE)。データ放送を描くところ |
| patches/ | ffmpeg に当てている直し。上流に投げるつもりのものだけ (LGPL/GPL) |
| 像に入れるもの | ffmpeg (GPL)・libaribcaption (MIT)・join_logo_scp 一式・rounded-mplus-1m-arib (SIL OFL)。焼き方は Dockerfile に |