LXDコンテナでKonomiTV視聴環境構築(2026年6月版)

物理カードリーダー使用時の場合、バックアップがある場合、など、状況によって変わるので検証も難しいのですが。一応、現時点の内容を全部まとめた内容かなと。もっとも、メインで利用している環境は超安定しており困っていないのでそんなに細かくは検証していないということもあり。テレビ自体観る機会は減っているしPC用のTVチューナーが少なくなっているので、この界隈の新しいことは厳しいのがっげ
もし環境移行を予定しており、現在も無事動作しているのであれば、先にバックアップしておけば、新しい環境でのセットアップが楽になります。

チューナードライバインストール・LXDコンテナ作成(ホストで実行)

OS:Ubuntu 26.04
TVチューナー:DTV02A-1T1S-U

まずはホスト側にチューナードライバを適用します(サイト)。過去にダウンロードしたドライバがあればそれを利用するかの確認も表示されます。
Secure Boot環境の場合、ドライバインストール時に確認画面が表示されるので、こちらの記事を参考に設定。

mkdir -p /opt/lxd-data/konomitv-backup
cd /opt/lxd-data/konomitv-backup
sudo nano tuner-lxd.sh
sudo bash tuner-lxd.sh
#!/bin/bash
# =============================================================
# KonomiTV LXD セットアップスクリプト (ホスト側)
# TunerOK スナップショットまで
# =============================================================
set -euo pipefail

MOUNT_PATH="/opt/lxd-data"

# ============================================================
# ユーティリティ
# ============================================================
ask_yn() {
    local prompt="$1"
    local default="${2:-Y}"
    local hint
    if [ "$default" = "Y" ]; then
        hint="Y/n"
    else
        hint="y/N"
    fi
    while true; do
        read -rp "${prompt} [${hint}]: " ans
        ans="${ans:-$default}"
        case "${ans,,}" in
            y|yes) return 0 ;;
            n|no)  return 1 ;;
            *) echo "  y または n で答えてください。" ;;
        esac
    done
}

# ============================================================
# 1. チューナードライバのインストール
# ============================================================
echo "=========================================="
echo " KonomiTV LXD セットアップ"
echo "=========================================="
echo ""

if ask_yn "チューナードライバ (px4_drv) をインストールしますか?"; then
    echo ""
    echo "=== チューナードライバのインストール ==="
    mkdir -p /opt/lxd-data/konomitv-backup

    EXISTING_DEB=$(ls /opt/lxd-data/konomitv-backup/px4-drv-dkms_*.deb 2>/dev/null | head -n1 || true)

    if [ -n "$EXISTING_DEB" ]; then
        echo "  ダウンロード済みのドライバが見つかりました: $(basename "$EXISTING_DEB")"
        if ask_yn "ダウンロード済みの$(basename "$EXISTING_DEB")を使用しますか?"; then
            DRIVER_DEB="$EXISTING_DEB"
        else
            DRIVER_VERSION=$(curl -s https://api.github.com/repos/tsukumijima/px4_drv/releases/latest | grep '"tag_name"' | sed 's/.*"v\([^"]*\)".*/\1/')
            DRIVER_DEB="px4-drv-dkms_${DRIVER_VERSION}_all.deb"
            DRIVER_URL="https://github.com/tsukumijima/px4_drv/releases/download/v${DRIVER_VERSION}/${DRIVER_DEB}"
            curl -L -o /opt/lxd-data/konomitv-backup/"${DRIVER_DEB}" "${DRIVER_URL}"
            chmod 644 /opt/lxd-data/konomitv-backup/"${DRIVER_DEB}"
        fi
    else
        DRIVER_VERSION=$(curl -s https://api.github.com/repos/tsukumijima/px4_drv/releases/latest | grep '"tag_name"' | sed 's/.*"v\([^"]*\)".*/\1/')
        DRIVER_DEB="px4-drv-dkms_${DRIVER_VERSION}_all.deb"
        DRIVER_URL="https://github.com/tsukumijima/px4_drv/releases/download/v${DRIVER_VERSION}/${DRIVER_DEB}"
        curl -L -o /opt/lxd-data/konomitv-backup/"${DRIVER_DEB}" "${DRIVER_URL}"
        chmod 644 /opt/lxd-data/konomitv-backup/"${DRIVER_DEB}"
    fi

    sudo apt install -y "./${DRIVER_DEB}"
    sudo modprobe -r px4_drv 2>/dev/null || true
    sudo modprobe px4_drv
    echo "  ドライバ インストール完了"
else
    echo "  ドライバインストールをスキップします"
fi
echo ""

# ============================================================
# 2. コンテナ名の入力
# ============================================================
read -rp "作成するLXDコンテナ名を入力してください [konomitv]: " CONTAINER
CONTAINER="${CONTAINER:-konomitv}"
echo "  コンテナ名: ${CONTAINER}"
echo ""

# ============================================================
# 3. Tailscale authkey の入力
# ============================================================
if ask_yn "Tailscale の authkey がありますか?"; then
    read -rsp "authkey を入力してください(tskeyから入力。入力は非表示): " TS_AUTHKEY
    echo ""
    USE_TS_AUTHKEY=true
else
    USE_TS_AUTHKEY=false
fi
echo ""

# ============================================================
# 4. コンテナ作成・マウント・ID マッピング
# ============================================================
echo "=== コンテナ '${CONTAINER}' を作成 ==="
if lxc info "$CONTAINER" &>/dev/null; then
    echo "  コンテナは既に存在します"
else
    lxc launch ubuntu:26.04 "$CONTAINER"
fi

echo ""
echo "=== ${MOUNT_PATH} の確認・作成 ==="
if [ ! -d "$MOUNT_PATH" ]; then
    sudo mkdir -p "$MOUNT_PATH"
    echo "  ${MOUNT_PATH} を作成しました"
else
    echo "  ${MOUNT_PATH} は既に存在します"
fi

TV_DIR="${MOUNT_PATH}/tv"
if [ ! -d "$TV_DIR" ]; then
    sudo mkdir -p "$TV_DIR"
    echo "  ${TV_DIR} を作成しました"
else
    echo "  ${TV_DIR} は既に存在します"
fi
sudo chown 1000:1000 "$TV_DIR"
sudo chmod 755 "$TV_DIR"
echo "  録画フォルダ所有者: 1000:1000  パーミッション: 755"

echo ""
echo "=== ホストの ${MOUNT_PATH} をコンテナにマウント ==="
if lxc config device show "$CONTAINER" 2>/dev/null | grep -q "opt-lxd-data"; then
    echo "  opt-lxd-data は登録済み"
else
    lxc config device add "$CONTAINER" opt-lxd-data disk source="$MOUNT_PATH" path="$MOUNT_PATH"
fi

echo ""
echo "=== ID マッピング設定 ==="
lxc config set "$CONTAINER" raw.idmap "both 1000 1000"

echo ""
echo "=== コンテナを再起動 ==="
lxc restart "$CONTAINER"
sleep 3

# ============================================================
# 5. コンテナ内セットアップ (apt / Tailscale)
# ============================================================
echo ""
echo "=== コンテナ内セットアップ ==="
lxc exec "${CONTAINER}" -- bash -euo pipefail << 'INNER'
apt update
apt upgrade -y
apt install -y curl
curl -fsSL https://tailscale.com/install.sh | sh
INNER

# ============================================================
# 6. Tailscale 起動
# ============================================================
echo ""
echo "=== Tailscale を起動 ==="
if [ "$USE_TS_AUTHKEY" = true ]; then
    lxc exec "${CONTAINER}" -- tailscale up --authkey="${TS_AUTHKEY}"
else
    echo "  authkeyがないため、手動認証を行ってください。"
    lxc exec "${CONTAINER}" -- tailscale up || true
fi
TS_IP=$(lxc exec "${CONTAINER}" -- tailscale ip -4 2>/dev/null || echo "取得中...")
echo "  Tailscale IP: ${TS_IP}"

# ============================================================
# 7. TailscaleOK スナップショット
# ============================================================
echo ""
if ask_yn "スナップショット 'TailscaleOK' を作成しますか?"; then
    echo "=== コンテナを停止中 ==="
    lxc stop "${CONTAINER}"
    echo "=== スナップショット 'TailscaleOK' を作成中 ==="
    lxc snapshot "${CONTAINER}" TailscaleOK
    echo "  スナップショット 'TailscaleOK' を作成しました"
    echo "=== コンテナを起動中 ==="
    lxc start "${CONTAINER}"
    sleep 3
fi

# ============================================================
# 8. USB チューナーパススルー
# ============================================================
DO_TUNER_PASS=false
if ask_yn "USB チューナーをコンテナにパススルーしますか?"; then
    DO_TUNER_PASS=true

    echo ""
    echo "=== USB チューナーを検出中 ==="
    LSUSB_LINE=$(lsusb | grep -i "ISDBT2056" || true)
    if [ -z "$LSUSB_LINE" ]; then
        echo "  警告: ISDBT2056 デバイスが見つかりません。パススルーをスキップします。"
        DO_TUNER_PASS=false
    else
        echo "  検出: $LSUSB_LINE"
        IDS=$(echo "$LSUSB_LINE" | grep -oP 'ID \K[0-9a-fA-F]{4}:[0-9a-fA-F]{4}')
        VENDOR_ID=$(echo "$IDS" | cut -d: -f1)
        PRODUCT_ID=$(echo "$IDS" | cut -d: -f2)
        echo "  vendorid : $VENDOR_ID"
        echo "  productid: $PRODUCT_ID"

        if lxc config device show "$CONTAINER" 2>/dev/null | grep -q "usb-tuner"; then
            echo "  usb-tuner は登録済みのため上書きします"
            lxc config device remove "$CONTAINER" usb-tuner
        fi
        lxc config device add "$CONTAINER" usb-tuner usb \
            vendorid="$VENDOR_ID" \
            productid="$PRODUCT_ID"
        echo "  usb-tuner を追加しました"

        echo ""
        echo "=== isdb2056video デバイスを検出中 ==="
        ISDB_DEVS=$(ls /dev/isdb2056video* 2>/dev/null || true)
        if [ -z "$ISDB_DEVS" ]; then
            echo "  警告: /dev/isdb2056video* が見つかりません。スキップします。"
        else
            IDX=0
            for DEV in $ISDB_DEVS; do
                NAME="isdb2056-${IDX}"
                echo "  追加: $DEV ($NAME)"
                if lxc config device show "$CONTAINER" 2>/dev/null | grep -q "^${NAME}:"; then
                    lxc config device remove "$CONTAINER" "$NAME"
                fi
                lxc config device add "$CONTAINER" "$NAME" unix-char \
                    source="$DEV" path="$DEV"
                IDX=$(( IDX + 1 ))
            done
        fi
    fi
fi

# ============================================================
# 9. コンテナ内チューナー確認
# ============================================================
if [ "$DO_TUNER_PASS" = true ]; then
    echo ""
    echo "=== コンテナ内チューナー確認 ==="
    lxc exec "${CONTAINER}" -- bash -c '
        ISDB_DEVS=$(ls /dev/isdb2056video* 2>/dev/null || true)
        if [ -z "$ISDB_DEVS" ]; then
            echo "  NG: /dev/isdb2056video* が見つかりません"
        else
            for DEV in $ISDB_DEVS; do
                chmod 666 "$DEV" 2>/dev/null
                echo "  OK: $DEV"
            done
        fi
    '
fi

# ============================================================
# 10. TunerOK スナップショット
# ============================================================
echo ""
if ask_yn "スナップショット 'TunerOK' を作成しますか?"; then
    echo "=== コンテナを停止中 ==="
    lxc stop "${CONTAINER}"
    echo "=== スナップショット 'TunerOK' を作成中 ==="
    lxc snapshot "${CONTAINER}" TunerOK
    echo "  スナップショット 'TunerOK' を作成しました"
    echo "=== コンテナを起動中 ==="
    lxc start "${CONTAINER}"
    sleep 3
fi

# ============================================================
# 11. コンテナ内でシェルを開始
# ============================================================
echo ""
echo "コンテナ内でシェルを開始します。"
exec lxc exec "${CONTAINER}" -- bash

Mirakurun・EDCB・KonomiTVセットアップ(コンテナで実行)

そのままコンテナ内で下記を貼り付けてソフトをインストールします。もしバックアップがあれば、そちらを利用することも出来ます。

#!/bin/bash
# =============================================================
# KonomiTV インストールスクリプト (コンテナ内実行)
# TunerOK スナップショット取得後に実行
# 実行方法: bash konomitv-install10.sh
# =============================================================
set -e

if [ "$(id -u)" -ne 0 ]; then
    echo "エラー: root で実行してください"
    echo "  sudo bash $0"
    exit 1
fi

REAL_USER="${SUDO_USER:-$(logname 2>/dev/null || id -un)}"
[ -z "$REAL_USER" ] && REAL_USER="root"
REAL_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6)
[ -z "$REAL_HOME" ] && REAL_HOME="/root"
DTV_DIR="$REAL_HOME/dtv"
echo "作業ユーザー: $REAL_USER  ホーム: $REAL_HOME"

# ============================================================
# ユーティリティ
# ============================================================
ask_yn() {
    local prompt="$1"
    local default="${2:-Y}"
    local hint
    if [ "$default" = "Y" ]; then
        hint="Y/n"
    else
        hint="y/N"
    fi
    while true; do
        read -rp "${prompt} [${hint}]: " ans
        ans="${ans:-$default}"
        case "${ans,,}" in
            y|yes) return 0 ;;
            n|no)  return 1 ;;
            *) echo "  y または n で答えてください。" ;;
        esac
    done
}

# ============================================================
# 1. バックアップの確認
# ============================================================
BACKUP_BASE=""
if [ -d "/opt/lxd-data/konomitv-backup" ]; then
    BACKUP_BASE="/opt/lxd-data/konomitv-backup"
fi

USE_BACKUP_KEY=false
USE_BACKUP_CHANNEL=false
USE_BACKUP_EPG=false
USE_BCAS_READER=false
BCAS_KEY_CONTENT=""

if [ -n "$BACKUP_BASE" ]; then
    echo ""
    echo "=== バックアップデータが見つかりました ==="
    echo "パス: $BACKUP_BASE"
    echo ""
    echo "バックアップ内容:"
    [ -f "$BACKUP_BASE/key/bcas_keys" ] && echo "  [key]      B-CAS キー"
    ls "$BACKUP_BASE/lib/"*.so 2>/dev/null | head -1 > /dev/null && echo "  [lib]      復号ライブラリ (SoftCAS)"
    [ -f "$BACKUP_BASE/recisdb/recisdb" ] && echo "  [recisdb]  recisdb バイナリ"
    [ -f "$BACKUP_BASE/channel/mirakurun/channels.yml" ] && echo "  [channel]  チャンネルデータ (Mirakurun + EDCB)"
    EPG_COUNT=$(ls "$BACKUP_BASE/epg/" 2>/dev/null | wc -l)
    [ "$EPG_COUNT" -gt 0 ] && echo "  [epg]      EPG データ ($EPG_COUNT ファイル)"
    echo ""

    # 質問: バックアップキーを使用するか
    if [ -f "$BACKUP_BASE/key/bcas_keys" ]; then
        if ask_yn "バックアップしたB-CASキーを使用しますか?"; then
            USE_BACKUP_KEY=true
            echo "  -> バックアップキーを使用します"
        else
            echo "  -> 新しくキーを入力します"
        fi
    fi

    # 質問: チャンネルデータを使用するか
    if [ -f "$BACKUP_BASE/channel/mirakurun/channels.yml" ]; then
        if ask_yn "バックアップしたチャンネルデータを使用しますか?"; then
            USE_BACKUP_CHANNEL=true
            echo "  -> バックアップのチャンネルデータを使用します"
        else
            echo "  -> 新しくチャンネルスキャンを実行します"
        fi
    fi

    # 質問: EPGデータを使用するか
    if [ "$EPG_COUNT" -gt 0 ]; then
        if ask_yn "バックアップしたEPGデータを使用しますか?"; then
            USE_BACKUP_EPG=true
            echo "  -> バックアップのEPGデータを使用します"
        else
            echo "  -> 新しくEPGを受信します"
        fi
    fi
else
    echo "バックアップが見つかりません。"
fi

# ============================================================
# 2. 物理B-CASカードリーダーの選択
# ============================================================
if [ "$USE_BACKUP_KEY" = false ]; then
    echo ""
    if ask_yn "物理B-CASカードリーダーを使用しますか?" "N"; then
        USE_BCAS_READER=true
        echo "  -> 物理B-CASカードリーダーを使用します (recisdb をダウンロード)"
    else
        echo "  -> SoftCAS (キー入力) を使用します"
        echo ""
        echo "キーの内容を貼り付けてください(入力後、Enter を 2 回押すと確定します):"
        BCAS_KEY_CONTENT=$(sed '/^$/q')
    fi
fi

# ============================================================
# 3. キーの設定
# ============================================================
echo ""
echo "=== 1/9: キー設定 ==="
mkdir -p /usr/local/etc

if [ "$USE_BACKUP_KEY" = true ]; then
    echo "バックアップキーを復元中..."
    cp "$BACKUP_BASE/key/bcas_keys" /usr/local/etc/bcas_keys
    echo "  -> /usr/local/etc/bcas_keys を復元しました"
elif [ "$USE_BCAS_READER" = true ]; then
    echo "物理B-CASカードリーダーを使用するため、キーの入力をスキップします"
else
    echo "$BCAS_KEY_CONTENT" > /usr/local/etc/bcas_keys
    echo "  -> /usr/local/etc/bcas_keys を保存しました"
fi

# bcas_keys の検証
if [ "$USE_BCAS_READER" = false ]; then
    KEY_LINES=$(grep -c '^Key\[' /usr/local/etc/bcas_keys 2>/dev/null || echo 0)
    HAS_BS_CS_KEY=$(grep -cE '^Key\[02\]|^Key\[03\]|^Key\[17\]' /usr/local/etc/bcas_keys 2>/dev/null || echo 0)
    if [ "$KEY_LINES" -lt 5 ]; then
        echo ""
        echo "警告: キーが少なすぎます ($KEY_LINES 個)。BS/CS視聴には不完全です。"
        echo "  完全なキーを再度入力してください。"
    elif [ "$HAS_BS_CS_KEY" -eq 0 ]; then
        echo ""
        echo "警告: キーが少ないのでBS/CSは視聴できません。"
    else
        echo "  bcas_keys: $KEY_LINES 個の鍵 (BS/CS鍵: $HAS_BS_CS_KEY 個)"
    fi
fi

# ============================================================
# 4. 依存パッケージのインストール
# ============================================================
echo "=== 2/9: 依存パッケージをインストール中 ==="
apt update
apt install -y \
    autoconf automake cmake libtool libpcsclite-dev \
    git build-essential pkg-config curl wget \
    libclang-dev libdvbv5-dev libudev-dev \
    nodejs npm ffmpeg liblua5.2-dev lua-zlib g++ make gcc

if [ "$USE_BCAS_READER" = true ]; then
    apt install -y pcscd pcsc-tools 2>/dev/null || true
fi

# Node.js 18 を nvm 経由でインストール
export NVM_DIR="/root/.nvm"
if [ ! -d "$NVM_DIR" ]; then
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
fi
source "$NVM_DIR/nvm.sh"
nvm install 18
nvm use 18
nvm alias default 18
NODE18_BIN=$(dirname "$(nvm which 18)")
export PATH="$NODE18_BIN:$PATH"
echo "Node.js バージョン確認: $(node -v)  npm: $(npm -v)"

npm install -g pm2 || true

# ============================================================
# 5. TVチューナードライバのセットアップ
# ============================================================
echo "=== 3/9: ドライバをセットアップ中 ==="
mkdir -p "$DTV_DIR" && cd "$DTV_DIR"

echo "デバイスパーミッションを設定中..."
chmod 666 /dev/isdb2056video* 2>/dev/null && echo "isdb2056: OK" || echo "isdb2056: デバイスなし"
chmod 666 /dev/px4video*      2>/dev/null && echo "px4video: OK"  || echo "px4video: デバイスなし"

# ============================================================
# 6. 復号ライブラリ (libyakisoba / libsobacas) のビルド
# ============================================================
echo "=== 4/9: 復号ライブラリのビルド ==="
if [ "$USE_BCAS_READER" = true ]; then
    echo "物理B-CASカードリーダーを使用するため、SoftCAS ライブラリのビルドをスキップします"
else
    echo "SoftCAS モード: 復号ライブラリをビルドします..."
    cd "$DTV_DIR"
    for repo in libyakisoba libsobacas; do
        [ ! -d "$repo" ] && git clone "https://github.com/tsunoda14/${repo}.git"
        cd "$repo"
        autoreconf -i
        mkdir -p build && cd build
        # libyakisoba のみ --sysconfdir を指定する(設定ファイルの配置先)
        [ "$repo" = "libyakisoba" ] && ../configure --sysconfdir=/usr/local/etc || ../configure
        make -j"$(nproc)"
        make install
        cd "$DTV_DIR"
    done
    # 共有ライブラリのキャッシュを更新
    ldconfig
    echo "  -> SoftCAS ライブラリ (libyakisoba, libsobacas) ビルド完了"
fi

# ============================================================
# 7. Rust / recisdb のビルド
# ============================================================
echo "=== 5/9: Rust と recisdb のビルド ==="
if [ "$USE_BCAS_READER" = true ]; then
    echo "物理B-CASカードリーダーを使用するため、recisdb をダウンロード中..."
    wget -q https://github.com/kazuki0824/recisdb-rs/releases/download/1.2.4/recisdb_1.2.4-1_amd64.deb -O /tmp/recisdb.deb
    apt install -y /tmp/recisdb.deb
    rm -f /tmp/recisdb.deb
    echo "  -> recisdb をダウンロードしてインストールしました (物理B-CAS用)"
else
    echo "SoftCAS モード: recisdb をソースからビルドします..."
    export CARGO_HOME="/root/.cargo"
    export RUSTUP_HOME="/root/.rustup"
    if ! command -v cargo &>/dev/null; then
        curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path
    fi
    source "$CARGO_HOME/env" 2>/dev/null || export PATH="$CARGO_HOME/bin:$PATH"

    # libsobacas の pkg-config ファイルを手動作成(Cargo ビルドで参照される)
    mkdir -p /usr/local/lib/pkgconfig
    tee /usr/local/lib/pkgconfig/libsobacas.pc > /dev/null <<'EOF'
prefix=/usr/local
libdir=/usr/local/lib
includedir=/usr/include
Name: libsobacas
Description: PCSC compatible ECM decoder library
Version: 0.0.0
Libs: -L${libdir} -lsobacas
Cflags: -I${includedir}/PCSC
EOF

    cd "$DTV_DIR"
    [ ! -d recisdb-rs ] && git clone --recursive https://github.com/kazuki0824/recisdb-rs.git
    cd recisdb-rs
    # b25-sys の依存ライブラリを pcsclite から libsobacas に差し替える
    sed -i 's/pcsclite/sobacas/g' b25-sys/build.rs
    cargo build -F dvb --release
    cp target/release/recisdb /usr/local/bin/
    chmod +x /usr/local/bin/recisdb
    echo "  -> recisdb を SoftCAS 対応でビルドしました (sobacas リンク)"

    # B-CAS キーを所定のパスに再保存(1つ目のスクリプトと同じタイミング)
    mkdir -p /usr/local/etc
    if [ "$USE_BACKUP_KEY" = true ]; then
        cp "$BACKUP_BASE/key/bcas_keys" /usr/local/etc/bcas_keys
    else
        echo "$BCAS_KEY_CONTENT" | tee /usr/local/etc/bcas_keys > /dev/null
    fi
    echo "  -> /usr/local/etc/bcas_keys を再保存しました"
fi

# ============================================================
# 8. チャンネルスキャン・Mirakurun 設定
# ============================================================
echo "=== 6/9: チャンネルスキャンと Mirakurun 設定 ==="
wget -q https://github.com/tsukumijima/ISDBScanner/releases/download/v1.3.3/isdb-scanner \
    -O /usr/local/bin/isdb-scanner
chmod +x /usr/local/bin/isdb-scanner

export NVM_DIR="/root/.nvm"
source "$NVM_DIR/nvm.sh" 2>/dev/null
nvm use 18 2>/dev/null || true

npm install -g \
    --unsafe-perm \
    --foreground-scripts \
    --production \
    --ignore-engines \
    mirakurun@3.9.0-rc.4

mkdir -p /usr/local/etc/mirakurun

# 初回起動で設定ディレクトリ(server.yml, services.json 等)を生成させてから一旦停止する
mirakurun start || true
sleep 5
mirakurun stop  || true

if [ "$USE_BACKUP_CHANNEL" = true ] && [ -n "$BACKUP_BASE" ]; then
    echo "バックアップからチャンネルデータを復元中..."
    [ -f "$BACKUP_BASE/channel/mirakurun/channels.yml" ] && \
        cp "$BACKUP_BASE/channel/mirakurun/channels.yml" /usr/local/etc/mirakurun/channels.yml
    [ -f "$BACKUP_BASE/channel/mirakurun/tuners.yml" ] && \
        cp "$BACKUP_BASE/channel/mirakurun/tuners.yml" /usr/local/etc/mirakurun/tuners.yml
    chown -R root:root /usr/local/etc/mirakurun/

    mkdir -p /var/local/edcb/Setting
    [ -f "$BACKUP_BASE/channel/edcb/ChSet5.txt" ] && \
        cp "$BACKUP_BASE/channel/edcb/ChSet5.txt" /var/local/edcb/Setting/
    [ -f "$BACKUP_BASE/channel/edcb/BonDriver_LinuxMirakc(LinuxMirakc).ChSet4.txt" ] && \
        cp "$BACKUP_BASE/channel/edcb/BonDriver_LinuxMirakc(LinuxMirakc).ChSet4.txt" \
           '/var/local/edcb/Setting/BonDriver_LinuxMirakc(LinuxMirakc).ChSet4.txt'

    if [ -d "$BACKUP_BASE/channel/scanned" ]; then
        mkdir -p "$DTV_DIR/scanned"
        cp -a "$BACKUP_BASE/channel/scanned"/* "$DTV_DIR/scanned/" 2>/dev/null || true
    fi
    echo "  -> チャンネルデータを復元しました (スキャン skipped)"
else
    echo "チャンネルスキャンを実行します(ISDBScanner)..."
    mkdir -p "$DTV_DIR/scanned"
    if [ -z "$(ls -A "$DTV_DIR/scanned/" 2>/dev/null)" ]; then
        isdb-scanner "$DTV_DIR/scanned/"
    fi

    if [ ! -f "$DTV_DIR/scanned/Mirakurun/channels.yml" ]; then
        echo "エラー: チャンネルスキャンが完了しませんでした。"
        exit 1
    fi

    cp -a "$DTV_DIR/scanned/Mirakurun/channels.yml" /usr/local/etc/mirakurun/channels.yml
    cp -a "$DTV_DIR/scanned/Mirakurun/tuners.yml"   /usr/local/etc/mirakurun/tuners.yml
    chown -R root:root /usr/local/etc/mirakurun/
fi

# Mirakurun の起動時自動チャンネルスキャンを無効化
# (isdb-scanner で取得した channels.yml をそのまま使うため、
#  起動時の再スキャンでチューナーが占有され BS/CS が一時的に
#  受信不可になるのを防ぐ)
SERVER_YML="/usr/local/etc/mirakurun/server.yml"
if [ -f "$SERVER_YML" ]; then
    if grep -q '^scanServices:' "$SERVER_YML"; then
        sed -i 's/^scanServices:.*/scanServices: false/' "$SERVER_YML"
    else
        echo "scanServices: false" >> "$SERVER_YML"
    fi
else
    echo "scanServices: false" > "$SERVER_YML"
fi
chown root:root "$SERVER_YML"
echo "  -> Mirakurun の起動時自動チャンネルスキャンを無効化しました"

# Mirakurun はスキャン完了後に起動(チューナー独占を回避)
mirakurun start

# ============================================================
# 9. EDCB セットアップ
# ============================================================
echo "=== 7/9: EDCB セットアップ ==="
cd "$DTV_DIR"
[ ! -d EDCB ] && git clone https://github.com/xtne6f/EDCB
cd EDCB/Document/Unix
make -j"$(nproc)"
make install
make extra
make install_extra

mkdir -p /var/local/edcb
chown -R "$REAL_USER:$REAL_USER" /var/local/edcb
make setup_ini

cd "$DTV_DIR"
[ ! -d EDCB_Material_WebUI ] && git clone https://github.com/EMWUI/EDCB_Material_WebUI
cp -r EDCB_Material_WebUI/HttpPublic /var/local/edcb/
cp -r EDCB_Material_WebUI/Setting    /var/local/edcb/
chown -R "$REAL_USER:$REAL_USER" /var/local/edcb

[ ! -d BonDriver_LinuxMirakc ] && \
    git clone https://github.com/matching/BonDriver_LinuxMirakc.git --recurse-submodules
cd BonDriver_LinuxMirakc
make -j"$(nproc)"
cp BonDriver_LinuxMirakc.so     /usr/local/lib/edcb/
cp BonDriver_LinuxMirakc.so.ini_sample /usr/local/lib/edcb/BonDriver_LinuxMirakc.so.ini

if [ "$USE_BACKUP_CHANNEL" = false ]; then
    [ -f "$DTV_DIR/scanned/EDCB-Wine/ChSet5.txt" ] && \
        cp "$DTV_DIR/scanned/EDCB-Wine/ChSet5.txt" /var/local/edcb/Setting/
    [ -f "$DTV_DIR/scanned/EDCB-Wine/BonDriver_mirakc(BonDriver_mirakc).ChSet4.txt" ] && \
        cp "$DTV_DIR/scanned/EDCB-Wine/BonDriver_mirakc(BonDriver_mirakc).ChSet4.txt" \
           '/var/local/edcb/Setting/BonDriver_LinuxMirakc(LinuxMirakc).ChSet4.txt'
fi

sed -i 's/^ALLOW_SETTING=.*/ALLOW_SETTING=true/' /var/local/edcb/HttpPublic/legacy/util.lua

# 録画フォルダのセットアップ
RECORD_SRC="/opt/lxd-data/tv"
RECORD_DST="/var/local/edcb/HttpPublic/video"
echo "録画フォルダをセットアップ中: $RECORD_SRC -> $RECORD_DST"
mkdir -p "$RECORD_DST"

if [ -d "$RECORD_SRC" ]; then
    if grep -q "$RECORD_DST" /etc/fstab; then
        echo "fstab: $RECORD_DST は既に登録済み"
    else
        echo "$RECORD_SRC $RECORD_DST none bind 0 0" >> /etc/fstab
        echo "fstab: $RECORD_DST を登録しました"
    fi
    if mountpoint -q "$RECORD_DST"; then
        echo "バインドマウント: $RECORD_DST は既にマウント済み"
    else
        mount --bind "$RECORD_SRC" $RECORD_DST
        echo "バインドマウント: $RECORD_DST をマウントしました"
    fi
else
    echo "警告: $RECORD_SRC が存在しないためバインドマウントをスキップします。"
fi
chown -R "$REAL_USER:$REAL_USER" "$RECORD_DST" 2>/dev/null || true

# チューナー数の検出
ISDB_DEV_COUNT=$(ls /dev/isdb2056video* 2>/dev/null | wc -l)
PX4_DEV_COUNT=$(ls /dev/px4video*       2>/dev/null | wc -l)
if [ "$ISDB_DEV_COUNT" -gt 0 ]; then
    TUNER_COUNT=$ISDB_DEV_COUNT
elif [ "$PX4_DEV_COUNT" -gt 0 ]; then
    TUNER_COUNT=$(( PX4_DEV_COUNT / 4 ))
    [ "$TUNER_COUNT" -eq 0 ] && TUNER_COUNT=1
else
    TUNER_COUNT=$(grep -c '^ *- name:' /usr/local/etc/mirakurun/tuners.yml 2>/dev/null || echo 1)
    [ "$TUNER_COUNT" -eq 0 ] && TUNER_COUNT=1
fi
echo "検出チューナー数: ${TUNER_COUNT}"

tee /var/local/edcb/EpgTimerSrv.ini > /dev/null <<EOT
[SET]
EnableHttpSrv=1
EnableTCPSrv=1
RecEndMode=0
Data=1
HttpAccessControlList=+127.0.0.0/8,+10.0.0.0/8,+172.16.0.0/12,+192.168.0.0/16,+169.254.0.0/16,+100.64.0.0/10
[TunerNum]
BonDriver_LinuxMirakc(LinuxMirakc).so=${TUNER_COUNT}
[BonDriver_LinuxMirakc.so]
Count=${TUNER_COUNT}
GetEpg=1
EPGCount=0
Priority=0
EOT

tee /var/local/edcb/Common.ini > /dev/null <<'EOT'
[SET]
RecFolderPath0=/var/local/edcb/HttpPublic/video
RecFolderNum=1
EOT

tee /etc/systemd/system/edcb.service > /dev/null <<EOT
[Unit]
Description=EpgTimerSrv
After=network-online.target
[Service]
Type=simple
User=$REAL_USER
ExecStart=/usr/local/bin/EpgTimerSrv
Restart=always
[Install]
WantedBy=default.target
EOT
systemctl daemon-reload
systemctl enable edcb
systemctl start edcb

# ============================================================
# 10. EPG 受信開始(EDCB起動後すぐに開始)
# ============================================================
if [ "$USE_BACKUP_EPG" = false ]; then
    echo ""
    echo "=== EPG 受信を開始します ==="
    (
        echo "EDCBの起動を待機中..."
        for i in $(seq 1 60); do
            if curl -sf "http://127.0.0.1:5510/legacy/index.html" -o /dev/null 2>/dev/null; then
                echo "EDCB: 起動確認 (${i}秒)"
                break
            fi
            sleep 1
        done
        sleep 5

        echo "EPG取得リクエストを送信中..."
        CTOK=$(curl -s http://127.0.0.1:5510/legacy/index.html \
            | grep -o 'name="ctok" value="[^"]*"' | head -1 \
            | grep -o 'value="[^"]*"' | cut -d'"' -f2)
        RESULT=$(curl -s -X POST "http://127.0.0.1:5510/legacy/index.html" \
            -H "Content-Type: application/x-www-form-urlencoded" \
            -d "ctok=${CTOK}&epgcap=y" \
            | grep -o 'id="result">[^<]*')
        if echo "$RESULT" | grep -q "開始しました"; then
            echo "EPG受信: 開始しました"
        else
            echo "警告: EPG取得リクエストの応答: $RESULT"
        fi
    ) >> /var/log/dtv_epg_start.log 2>&1 &
    echo "EPG受信をバックグラウンドで開始しました(KonomiTVインストール中に受信進行中)"
    echo "進捗ログ: tail -f /var/log/dtv_epg_start.log"
fi

# ============================================================
# 11. KonomiTV インストール
# ============================================================
echo "=== 8/9: KonomiTV インストール ==="
cd "$DTV_DIR"
curl -LO https://github.com/tsukumijima/KonomiTV/releases/download/v0.13.0/KonomiTV-Installer.elf
chmod +x KonomiTV-Installer.elf

apt install -y expect

expect << 'EXPECT_EOF'
set timeout 300
log_file /tmp/konomi_install.log

spawn ./KonomiTV-Installer.elf

expect -re {\[1/2/3/4/5\].*:} { send "1\r" }
expect -re {フォルダのパス:} { send "/opt/KonomiTV\r" }
expect -re {\[EDCB/Mirakurun\].*:} { send "\r" }
expect -re {TCP API の URL:} { send "tcp://127.0.0.1:4510/\r" }
expect -re {\[FFmpeg.*\].*:} { send "FFmpeg\r" }
expect -re {録画フォルダのパス:} { send "/var/local/edcb/HttpPublic/video\r" }
expect -re {録画フォルダのパス:} { send "\r" }
expect -re {キャプチャ画像の保存先フォルダのパス:} { send "/var/local/edcb/HttpPublic/video\r" }
expect -re {キャプチャ画像の保存先フォルダのパス:} { send "\r" }

expect eof
EXPECT_EOF

KONOMI_CONFIG=$(find /opt/KonomiTV -name "config.yaml" 2>/dev/null | head -1)
if [ -z "$KONOMI_CONFIG" ]; then
    echo "警告: KonomiTV の config.yaml が見つかりませんでした。"
else
    echo "KonomiTV config.yaml を更新中: $KONOMI_CONFIG"
    sed -i 's/always_receive_tv_from_mirakurun: false/always_receive_tv_from_mirakurun: true/' "$KONOMI_CONFIG"
fi

# KonomiTV を起動
export NVM_DIR="/root/.nvm"
source "$NVM_DIR/nvm.sh" 2>/dev/null
nvm use 18 2>/dev/null || true

if [ "$USE_BCAS_READER" = true ]; then
    # 物理B-CASモード: 既存プロセスを停止してから手動起動 + pm2登録
    killall -9 akebi-https-server.elf python 2>/dev/null || true
    sleep 2

    cd /opt/KonomiTV/server
    source .venv/bin/activate
    nohup python KonomiTV.py > /tmp/konomitv.log 2>&1 &
    sleep 3

    pm2 start /opt/KonomiTV/server/.venv/bin/python --name KonomiTV -- KonomiTV.py 2>/dev/null || true
    pm2 save 2>/dev/null || true
else
    # SoftCASモード: 1つ目のスクリプトと同様、インストーラが起動したプロセスを pm2 restart のみで反映
    pm2 restart KonomiTV 2>/dev/null || true
fi

# ============================================================
# 12. EPG データ復元 (バックアップのみ)
# ============================================================
echo ""
echo "=== 9/9: 全セットアップ完了 ==="

if [ "$USE_BACKUP_EPG" = true ] && [ -n "$BACKUP_BASE" ]; then
    echo "バックアップから EPG データを復元中..."
    EPG_DST="/var/local/edcb/Setting/EpgData"
    mkdir -p "$EPG_DST"
    if [ -d "$BACKUP_BASE/epg" ] && [ -n "$(ls -A "$BACKUP_BASE/epg" 2>/dev/null)" ]; then
        cp -a "$BACKUP_BASE/epg"/* "$EPG_DST/"
        EPG_RESTORED=$(ls "$EPG_DST" | wc -l)
        echo "  -> EPG データを復元しました ($EPG_RESTORED ファイル)"
    else
        echo "  警告: バックアップに EPG データがありません"
    fi
fi

CONTAINER_NAME=$(hostname 2>/dev/null || echo "localhost")

echo ""
echo "============================================================"
echo " セットアップ完了"
echo "============================================================"
echo " EDCB Web:     http://${CONTAINER_NAME}:5510/"
echo " KonomiTV:     https://$(hostname -I 2>/dev/null | awk '{print $1}' | tr '.' '-').local.konomi.tv:7000/"
echo " EPG確認:       ls /var/local/edcb/Setting/EpgData"
echo " EPGログ:       tail -f /var/log/dtv_epg_start.log"
echo " EDCB再起動:    systemctl restart edcb"
echo "============================================================"
echo ""
echo "セットアップが完了しました。コンテナ内で作業を続けてください。"
bash

インストール後のEPG取得状況確認など(コンテナ内)

番組表データを取得中なので、下記でEPG取得状況を確認。tmpファイルが無くなればEPGの取得完了。

ls /var/local/edcb/Setting/EpgData

1チューナー環境で先にKonomiTVの動作確認を行いたいなら、チューナーがEPG受信中なので下記でEDCBを再起動してからKonomiTVで動作確認を。LXDコンテナを再起動してもよいかも。
Mirakurun関係で視聴出来ない場合も再起動するか、しばらく待てば見られるはず。一応、ホスト側で「http://コンテナのIPアドレス:40772」にアクセスすればMirakurunの状況は確認出来ます。

# EDCB再起動
systemctl restart edcb

# KonomiTV再起動
pm2 restart KonomiTV

# Mirakurun再起動
pm2 restart mirakurun-server 

コンテナ内でバックアップを実行

コンテナ内で下記を実行してバックアップします。

mkdir -p /opt/lxd-data/konomitv-backup
cd /opt/lxd-data/konomitv-backup
nano konomitv-backup.sh
bash konomitv-backup.sh
#!/bin/bash
# =============================================================
# KonomiTV バックアップスクリプト
# 再インストール時に必要なファイルを /opt/lxd-data/konomitv-backup/ に保存する
# 実行方法: sudo bash konomitv-backup.sh
# =============================================================
set -e

BACKUP_BASE="/opt/lxd-data/konomitv-backup"
REAL_USER="${SUDO_USER:-$(logname 2>/dev/null || id -un)}"
DTV_DIR="$(getent passwd "$REAL_USER" | cut -d: -f6)/dtv"

echo "=== KonomiTV バックアップ開始 ==="
echo "バックアップ先: $BACKUP_BASE"

# ------------------------------------------------------------
# 1. B-CAS キー
# ------------------------------------------------------------
echo "[1/5] B-CAS キーをバックアップ中..."
mkdir -p "$BACKUP_BASE/key"
if [ -f /usr/local/etc/bcas_keys ]; then
    cp /usr/local/etc/bcas_keys "$BACKUP_BASE/key/bcas_keys"
    echo "  -> $BACKUP_BASE/key/bcas_keys"
else
    echo "  警告: /usr/local/etc/bcas_keys が見つかりません"
fi

# ------------------------------------------------------------
# 2. 復号ライブラリ (libyakisoba / libsobacas)
# ------------------------------------------------------------
echo "[2/5] 復号ライブラリをバックアップ中..."
mkdir -p "$BACKUP_BASE/lib"

# 共有ライブラリ
for lib in libyakisoba libsobacas; do
    for f in /usr/local/lib/${lib}.*; do
        [ -e "$f" ] && cp -a "$f" "$BACKUP_BASE/lib/" && echo "  -> $f"
    done
done

# pkg-config ファイル
if [ -f /usr/local/lib/pkgconfig/libsobacas.pc ]; then
    mkdir -p "$BACKUP_BASE/lib/pkgconfig"
    cp /usr/local/lib/pkgconfig/libsobacas.pc "$BACKUP_BASE/lib/pkgconfig/"
    echo "  -> libsobacas.pc"
fi

# ------------------------------------------------------------
# 3. recisdb
# ------------------------------------------------------------
echo "[3/5] recisdb をバックアップ中..."
mkdir -p "$BACKUP_BASE/recisdb"
if [ -f /usr/local/bin/recisdb ]; then
    cp /usr/local/bin/recisdb "$BACKUP_BASE/recisdb/"
    echo "  -> $BACKUP_BASE/recisdb/recisdb"
else
    echo "  警告: /usr/local/bin/recisdb が見つかりません"
fi

# ------------------------------------------------------------
# 4. チャンネルデータ (Mirakurun + EDCB)
# ------------------------------------------------------------
echo "[4/5] チャンネルデータをバックアップ中..."
mkdir -p "$BACKUP_BASE/channel/mirakurun"
mkdir -p "$BACKUP_BASE/channel/edcb"

# Mirakurun チャンネル設定
for f in channels.yml tuners.yml; do
    if [ -f "/usr/local/etc/mirakurun/$f" ]; then
        cp "/usr/local/etc/mirakurun/$f" "$BACKUP_BASE/channel/mirakurun/"
        echo "  -> mirakurun/$f"
    fi
done

# EDCB チャンネル設定
for f in ChSet5.txt "BonDriver_LinuxMirakc(LinuxMirakc).ChSet4.txt"; do
    if [ -f "/var/local/edcb/Setting/$f" ]; then
        cp "/var/local/edcb/Setting/$f" "$BACKUP_BASE/channel/edcb/"
        echo "  -> edcb/$f"
    fi
done

# isdb-scanner のスキャン結果(存在する場合)
if [ -d "$DTV_DIR/scanned" ] && [ -n "$(ls -A "$DTV_DIR/scanned" 2>/dev/null)" ]; then
    cp -a "$DTV_DIR/scanned" "$BACKUP_BASE/channel/scanned"
    echo "  -> scanned/"
fi

# ------------------------------------------------------------
# 5. EPG データ
# ------------------------------------------------------------
echo "[5/5] EPG データをバックアップ中..."
mkdir -p "$BACKUP_BASE/epg"
EPG_DIR="/var/local/edcb/Setting/EpgData"
if [ -d "$EPG_DIR" ] && [ -n "$(ls -A "$EPG_DIR" 2>/dev/null)" ]; then
    cp -a "$EPG_DIR"/* "$BACKUP_BASE/epg/"
    EPG_COUNT=$(ls "$BACKUP_BASE/epg/" | wc -l)
    echo "  -> $EPG_COUNT ファイル"
else
    echo "  警告: EPG データがまだ取得されていないか、ディレクトリが空です"
fi

# ------------------------------------------------------------
# 完了
# ------------------------------------------------------------
echo ""
echo "=== バックアップ完了 ==="
echo "内容:"
echo "  key/      : B-CAS キー"
echo "  lib/      : 復号ライブラリ (libyakisoba / libsobacas)"
echo "  recisdb/  : recisdb バイナリ"
echo "  channel/  : チャンネル設定 (Mirakurun + EDCB)"
echo "  epg/      : EPG データ"
echo ""
du -sh "$BACKUP_BASE"/* 2>/dev/null

GPUパススルーする場合

セットアップ完了後、GPUをパスするーするならこちらの記事を参考に。

タイトルとURLをコピーしました