rokkonet

PC・Androidソフトウェア・アプリの開発・使い方に関するメモ

Docker版EPGStationの録画ファイル保存場所の変更

2026 Sep. 22.

この記事の方法では、過去の録画番組について、それまでのストレージに保存されたままで削除されないが、EPGStationでは再生できなくなる。

手法
コンテナを停止する。

$ docker compose down


docker-compose.ymlのepgstationセクション内のvolumesセクションでの /app/recorded と /app/thumbnail のホストPCディレクトリ設定を新しいディレクトリに変更する。

services:
  epgstation:
    volumes:
      - /YOUR/NEW/VIDEO/DIR:/app/recorded
      - /YOUR/NEW/THUMBNAIL/DIR:/app/thumbnail


コンテナを起動する。

$ docker compose up -d


ubuntu系linuxインストール

2026 Sep. 22.
2026 May 03.
2026 Feb. 25.
2025 Nov. 03.
2025 Oct. 12.
2025 Sep. 15.
2025 Aug. 30.
2025 Feb. 08.
2024 Sep. 24.
2024 May. 01.
2024 Apr. 28.
2023 May 03.
2022 Sep. 10.
2022 Feb. 08.
2021 May 19.
2021 Apr. 24.
2021 Mar. 29.
2021 Feb. 27.
2021 Jan. 31.
2020 Oct. 17.
2020 Sep. 26.
2020 Aug. 15.
2019 Oct. 14.
2019 Jan. 04.
2018 Dec. 01.
2018 Nov. 04.
2018 Oct. 14.
2018 Aug. 13.
 
ストレージの準備
起動ディスクのパーティションを準備する。
 EFI System Partitionの準備 -> UEFI GPT HDDへのGRUBインストール - rokkonet
 
インストール先の/home内のユーザーディレクトリ名が既存ディレクトリ名と重ならないようにする。
 
インストール
DVDからlinuxディストリビューションをインストールする。
 
インストール時にセットするユーザーにはUIDを指定できないので、ユーザー名は仮のものとして適当なものにする。
 
 

インストールメディアをLegacy BIOSモードで読み込むべきか、UEFIモードで読み込むべきかを次の手順で決める。

(1)GParted等でLinuxをライブ起動する。
(2)インストール先ストレージがUEFIモードとなっているか、BIOSモードとなっているか、次のコマンドで確認する。
    $ sudo parted /dev/sda print
      「Partition Table: gpt」ならUEFIモード。
      「Partition Table: msdos」ならBIOSモード。
(3)ブートモードがUEFIモードとなっているか、BIOSモードとなっているか、次のコマンドで確認する。
    $ [ -d /sys/firmware/efi ] && echo "UEFIモード" || echo "BIOSモード"
(4)PCのBIOS設定/UEFI設定/ブートドライブ設定等で、ブートモードをインストール先ストレージのモードと一致させる。

 
 
(UEFIシステム・GPTドライブでのディスクパーティションについて)
300MB程度のEFI System Partition (ESP)がディスクに存在していなければ、vfatで作成・フォーマットする。
既にESPが存在していれば、再フォーマットせずそのまま残しておく。
システムが自動で処理するので、ESPはLinuxインストール作業時にファイルシステムへのマウント指定しない。
 
 
(video card対応)
nvidia video chipならば次のリンクを参照する。
  (nvidia-driver-390とか)
 
/etc/fstab のpass値の修正
各行の末尾のpass値について、/boot/efi は 0 に修正する。
その他の /(ルート)以外のパーティションは 2 に修正する。
 
ユーザを追加・設定
  最初のユーザーを登録し、サブグループはインストール時のユーザーと同じにする。
  所属グループ
   adm  cdrom  video  dip  plugdev  staff  lpadmin  lxd  sambashare  docker。
   sudoは必要に応じて。
 
 
ユーザーのパスワード設定
 
起動時NumLock設定
 
Xfce2 マウスフォーカス設定
 設定 -> ウィンドウマネージャー ->
 フォーカス -> フォーカスで前面に出す ->
 「ウィンドウがフォーカスを取得した場合自動的に前面に出す」のチェックを外す。
 
 設定 -> ウィンドウマネージャー ->
 フォーカス -> クリックで前面に出す ->
 「アプリケーションウィンドウ内でクリックした場合前面に出す」のチェックを外す。
 
 Firefoxは、「ツールバーをカスタマイズ」で「タイトルバーを表示する」設定にしないとXubuntuデスクトップ設定に従わない。
 
     
画面解像度変更
 
ユーザーのumask設定 umask 0022
セキュリティ性の高い"umask 0077"としたユーザーでビルドしたプログラムをシステムにインストールすると、システム上のディレクトリ・ファイルをrootユーザー以外が読み込めない恐れがあるので、"umask 0022"がよい。
~/.profileファイルに書き込んでおく。
 
 
ログイン時設定ファイル読み込みの仕組み確認
 
 
パス設定
 

固定IPアドレス設定
 
 
/etc/hostsを設定する。
 

(TCP Wrapper)
/etc/hosts.deny
ALL: ALL
 
/etc/hosts.allow
sshd: ALL
 
 
時刻合わせ
 
buildツールのインストール
 # apt update && apt install build-essential
 
FirefoxでのGeminiのチャットページ画面で上下スクロールバーが表示されるようにする。(Firefoxの「隠し設定」でスクロールバーを強制する)
  • FirefoxのURLバーに about:config と入力してEnterキーを押す。

  • 注意画面が出るが、「危険を承知で続行」を選択する。

  • 検索窓に widget.non-native-theme.scrollbar.style と入力する。

  • 右側の鉛筆アイコンをクリックして、値を 4 に書き換えてチェックマーク(保存)を押す。(0がデフォルト)

    • 4はシステム標準(Linux/GTK)のスタイルを強制する設定。

  • ページを再読み込み(F5)して確認する。

 
wake-on-lan設定
  
nfsクライアントインストール
 
nfsサーバーインストール
 参照先: nfs server 
 
fstab設定
 フィールド書式
  file-system  dir  type  options  dump  pass
 
 passフィールドについて
  起動時にfsckがチェックする順番。0,1または2を設定できる。
  1はチェックの優先度が一番高い。ルートファイルシステム(/)には1を指定する。
  2はその他のファイルシステムに指定すればよい。
  0のファイルシステムはチェックされない。
 
 NFSファイルシステムのマウント設定
 
 ntfsファイルシステムのマウント設定
 
 
sshサーバーインストール 鍵生成・配置
 
 ルーター設定 外部からのsshパケットをsshサーバーホストに転送させる
 

OpenVPNインストール
 参照先
  OpenVPNインストール ルーティング方式 Easy-RSA3利用、TLS認証あり版 - rokkonet

  OpenVPNのインストールとセットアップからインターネット接続までのガイドブック - Qiita

LAN外からのopenvpnクライアントパケットがホストに転送されるようにルーターを設定する。

 
sambaインストール
 
 
TV録画・視聴システム

 

TVストリーム分離ツール tssplitter_liteインストール(ffmpegでできるので不要)
  

pt2・recpt1インストール ) Mirakurun + EPGStationなら不要

epgrec_unaインストール  ) Mirakurun + EPGStationなら不要
 
 
( TV録画ファイル定期圧縮 epgrec版)
 
( TV録画番組内容ファイル定期作成 epgrec版)
 
 
データベースシステムをインストール
  ( 旧版  mysqlインストール )
  
 
 
rubyインストール
  
bitbucket利用対応
 gitインストール
  $ sudo apt update && sudo apt install git
 bitbucket設定
  リポジトリディレクトリを置くディレクトリの作成
   $ mkdir ~/bitbucket

  SSH認証設定
 
anti-virusソフトインストール
 
プリンター・スキャナー インストール

mailインストール
 
javaインストール
 
sinatraインストール・設定
 参照先: ruby sinatra - rokkonet
 
googleドライブ同期設定
 ・ローカルファイルとの同期

    grive2が扱いやすいが、google認証できない状況。
 
 ・googleドライブのマウント
 
mysqlデータ移行
 
mysqlデータベース定期バックアップ
 
他ファイルサーバーデータ移行
 
no-ipアップデートデーモンインストール
  
FM放送定期録音
サイマルラジオ定期録音
VPN Gateクライアント
ニュース定期録画
 
PT2テレビチューナーカード接続エラー定期メール通知
 
システムからのメールの保存量に制限を設定する
 
rootユーザーcron設定
一般ユーザーcron設定
 
( epgrecエラーログ定期メール通知 )
 

/etc/fstab の pass(起動時のファイルシステムチェック順序)設定値について

2026 Sep. 22.

/etc/fstab の各行の末尾にある数字はpass値と呼ばれ、起動時のfsck(ファイルシステムチェック)を行う順序を表す。

0 :
起動時に fsck によるファイルシステムのチェックを行わない。
チェックをスキップしたい場合や、リムーバブルメディア、特殊なファイルシステムなどで使われる。起動を少しでも早くしたい場合や、エラーチェックを手動で行いたい場合に指定する。

1 :
最優先(プライオリティ 1)で fsck によるファイルシステムのチェックを行う。
通常はルートファイルシステム(/)専用の設定。
システムが正しく動作するために最も重要であるため、最初にチェックされる。/ 以外の通常のデータ用パーティションに 1 を設定するのは不適切。

2 :
プライオリティ 1のチェックが完了した後に、順番に fsck によるファイルシステムのチェックを行う。
ルートファイルシステム以外の通常のパーティション(/home や追加パーティションなど)の標準的な設定。

/ (ルート)は 1 にする。
/boot/efi は 0 にする。
/homeなど、 / 以外のパーティションは 2 にする。

Debian 13 Xfce版をUEFI PCにインストール

2026 Sep. 22.
2026 Sep. 08.

パーティション操作のため、別のlinux PCのbalena-EtcherでGpartedのisoファイルをUSBメモリに入れた。
debian 13 amd64 Xfce版のisoファイルを、別のlinux PCのbalena-EtcherでGpartedとは別のUSBメモリに入れた。

PCの基本設定機構がレガシーBIOSかUEFIかを確認する。
BIOS画面で確認したら、「AMIUEFI. Aptio Setup Utility - Copyright (C) 2014 American Megatrends, Inc.」とあり、UEFIであった。

GpartedのUSBメモリはレガシーMBRだったので、PCのbootデバイスをUSB Hard Diskに指定した。

搭載されているストレージがレガシーMBRかGPTかを確認する。
GPartedで確認したら、2つのストレージはどちらもGPTであった。

Gpartedでパーティションを操作した。
EFIシステムパーティション(esp)として、300MBから500MB程度のvfat(fat 32 LBA)パーティションを確保。(Windowsなど既存OSが利用しているespパーティションがあれば、そのパーティションを初期化せずにそのまま利用する)
50GB程度の / (ルート)パーティションを確保。
メモリの2倍のサイズのswapパーティションを確保。
自家プログラムや設定ファイルバックアップのために500MB程度の /usr/local/my パーティションを確保。 /home パーティションを確保。

debianインストーラのUSBメモリはUEFIだったので、PCのbootデバイスをUEFI USB Hard Diskに指定して起動した。

debianインストール中のパーティション設定での留意点。
地域・言語・キーボードについての質問にはすべて日本で回答したらロケールが日本に設定された。
/(ルート)パーティションにブートフラグをつけない。

ESPパーティション設定の留意点
Windowsなど既存OSが利用しているespパーティションがあれば、そのパーティションを初期化せずにそのまま利用する。
300MBから500MB程度のvfat(fat 32 LBA)パーティションを確保する。
「利用方法:」欄に「EFI システム パーティション(EFI system partition)」 を選ぶ。
「起動フラグ」を設定できるならば bootフラグを設定する。
ESPパーティションにespフラグをセットできるならばセットするが、指定できなければ指定しなくてよい。
ESPパーティションにマウントポイントを指定できるならば /boot/efi を指定するが、指定できなければ指定しなくても自動的にセットされる。

エディタvimをインストールした。

$ su -
# apt update && apt install vim


/etc/fstab のpass値の修正
各行の末尾のpass値について、/boot/efi は 0 に修正する。
その他の /(ルート)以外のパーティションは 2 に修正する。

debianインストール直後は日本語入力できなかったので、日本語入力を可能にした。

$ su -
# apt update &&  apt install fcitx5-mozc fcitx5-frontend-gtk3 fcitx5-frontend-qt5
# exit
$ im-config -n fcitx5
$ echo "export GTK_IM_MODULE=fcitx" >> ~/.xprofile
$ echo "export QT_IM_MODULE=fcitx" >> ~/.xprofile
$ echo "export XMODIFIERS=@im=fcitx" >> ~/.xprofile


PCを再起動すると日本語入力が可能になった。
他のユーザーでも設定無しで日本語入力が可能になった。

ロケール設定
debian 13インストール直後に次の設定となっており、何もする必要がなかった。

$ locale 
LANG=ja_JP.UTF-8
LANGUAGE=
LC_CTYPE="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_PAPER="ja_JP.UTF-8"
LC_NAME="ja_JP.UTF-8"
LC_ADDRESS="ja_JP.UTF-8"
LC_TELEPHONE="ja_JP.UTF-8"
LC_MEASUREMENT="ja_JP.UTF-8"
LC_IDENTIFICATION="ja_JP.UTF-8"
LC_ALL=


/opt ディレクトリが無かったので、作成した。

$ su -
# mkdir /opt


システムに一般ユーザーを追加した。

$ su -
# useradd -d /home/USERDIR -m -s /bin/bash -u USERNUMBER USERNAME
# passwd USERNAME

/etc/group を編集し、システムインストール時に設定した一般ユーザーと同じ権限にした。
デスクトップからログアウトし再度ログインするか、PCを再起動して設定をシステムに反映させる。

デフォルトのエディタをvimにした。

$ su -
# select-editor
/usr/bin/vim.basic を選択した。


visudoコマンドでの編集エディタをvimに設定した。

$ su -
# EDITOR=/usr/bin/vim.basic visudo
開かれた設定ファイルの先頭に次の1行を記述した。
Defaults editor=/usr/bin/vim.basic


一般ユーザーがsudoコマンドを利用できるように設定した。

$ su -
# visudo
root ALL=(ALL:ALL) ALL が書かれている行の下に
"USER ALL=(ALL:ALL) ALL"を記述した。


ファイアウォール設定ソフト ufw をインストールした。

$ sudo apt update && sudo apt install ufw


PATH設定
/etc/profile.d/hoge.sh ファイルでの設定はrootユーザーにのみ反映される。
一般ユーザーのPATH設定は ~/.bashrc ファイルに書き込む。

Xfce2 マウスフォーカス設定
 設定 -> ウィンドウマネージャー ->
 フォーカス -> フォーカスで前面に出す ->
 「ウィンドウがフォーカスを取得した場合自動的に前面に出す」のチェックを外す。

 設定 -> ウィンドウマネージャー ->
 フォーカス -> クリックで前面に出す ->
 「アプリケーションウィンドウ内でクリックした場合前面に出す」のチェックを外す。

 Firefoxは、「ツールバーをカスタマイズ」で「タイトルバーを表示する」設定にしないとXubuntuデスクトップ設定に従わない。

「デスクトップの設定 -> 高度なネットワーク設定」でIPアドレスを固定化した。

画面解像度を下記のリンク記事にしたがって21.5型ワイドディスプレイに適したものに変更した。
Linux CUIでの画面解像度変更 - rokkonet

PCにスピーカーをつなげていないので、wifi経由でAndroid端末から音声を出力するAudioRelayを下記のリンクページにしたがってインストールした。
Xubuntu 24.04 (Linux)の音声をAndroid端末に出力する : AudioRelay - rokkonet

VLCメディアプレーヤーをインストールした。

$ sudo apt update && sudo apt install vlc


IPアドレスとそのエイリアス名の対応を自分の環境に合わせて /etc/hosts に設定した。

どのアドレスからのTCP Wrappersに対応したネットワークサービスも拒否するように /etc/hosts.deny を設定した。
ALL: ALL

どのアドレスからのSSHアクセスも受け付けるように /etc/hosts.allow を許可にした。
sshd: ALL

時刻のズレが自動的に修正されるようにした。
現在の時刻同期の状態を確認する(NTPサービスが有効になっているか確認する)。

$ timedatectl status
               Local time: 金 2026-09-11 19:43:18 JST
           Universal time: 金 2026-09-11 10:43:18 UTC
                 RTC time: 金 2026-09-11 19:43:18
                Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: no
              NTP service: n/a
          RTC in local TZ: yes

出力結果の中に "NTP service: active" "System clock synchronized: yes"があれば自動同期が有効となっている。
そのようになっていなかったのでNTPによる自動同期の有効化を行った。

$ sudo timedatectl set-ntp true
Failed to set ntp: NTP not supported 
システムクロックを同期するためのバックエンドサービス(systemd-timesyncd など)がシステムにインストールされていないか、有効化されていない。

必要なパッケージをインストールし、サービスを有効化する。

$ sudo apt update
$ sudo apt install systemd-timesyncd

自動同期サービスを有効にして起動する。

$ sudo systemctl enable --now systemd-timesyncd


systemd-timesyncdがactive (running) になっていることを確認する。

$ sudo systemctl status systemd-timesyncd


timedatectl を使って同期を有効にする。

$ sudo timedatectl set-ntp true

設定が正しく反映されたかを以下のコマンドで確認する。

$ timedatectl status
Local time: 金 2026-09-11 19:55:12 JST
Universal time: 金 2026-09-11 10:55:12 UTC
RTC time: 金 2026-09-11 19:47:47
Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: yes
NTP service: active
RTC in local TZ: yes 

System clock synchronized: yes および NTP service: active となっており、自動時刻同期が正常に機能していた。
一方、"RTC in local TZ: yes" となっているのでハードウェア時計(RTC)をローカル時間(日本時間)として扱っている。
LinuxシステムではRTCをUTC(協定世界時)に設定することが標準的で、ここが yes のままだと、将来的にOS間のデュアルブートなどで時刻のズレが生じる原因になることがある。
標準的な UTC に修正する。

$ sudo timedatectl set-local-rtc 0

確認。

 $ timedatectl status
Local time: 金 2026-09-11 19:56:54 JST
Universal time: 金 2026-09-11 10:56:54 UTC
RTC time: 金 2026-09-11 10:56:55
Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no 

"RTC in local TZ: no" に変更され、RTC time が Universal time と一致する状態になった。

buildツールをインストールした。

$ sudo apt update && sudo apt install build-essential


FirefoxでのGeminiのチャットページ画面で上下スクロールバーが表示されるようにした。(Firefoxの「隠し設定」でスクロールバーを強制する)
* FirefoxのURLバーに about:config と入力してEnterキーを押す。
* 注意画面が出るが、「危険を承知で続行」を選択する。
* 検索窓に widget.non-native-theme.scrollbar.style と入力する。
* 右側の鉛筆アイコンをクリックして、値を 4 に書き換えてチェックマーク(保存)を押す。(0がデフォルト)
  4はシステム標準(Linux/GTK)のスタイルを強制する設定。
* ページを再読み込み(F5 キー)して確認する。

nfsクライアントを下記ページの記事にしたがってインストールをした。
NFSクライアント設定 - rokkonet

nfsサーバーを下記ページの記事にしたがってインストールした。
nfsサーバーインストール - rokkonet

sshサーバーを下記リンク記事にしたがってインストールした。
OpenSSH SSHサーバー、SSHクライアント インストール、鍵生成・配置 - rokkonet

OpenVPNサーバーを下記リンク記事にしたがってインストールした。
OpenVPNインストール ルーティング方式 Easy-RSA3利用、TLS認証あり版 - rokkonet

sambaサーバーを下記リンク記事にしたがってインストールした。
sambaインストール - rokkonet

PT 2テレビチューナーとMirekurun、EPGStationによるテレビ受信・録画・視聴システムを下記リンク記事にしたがってインストールした。
TV録画システム構築:PT2 TVチューナー & Docker版Mirakurun & Docker版MySQL & Docker版EPGStation (Xubuntu 24.04 , Debian 13 ) - rokkonet

TV録画システム構築:PT2 TVチューナー & Docker版Mirakurun & Docker版MySQL & Docker版EPGStation (Xubuntu 24.04 , Debian 13 )

2026 Sep. 19.
2026 May 12.
2026 Apr. 02.
2026 Mar. 30.
2025 Nov. 07.
2025 Oct. 10.
2025 Sep. 28.
2025 Aug. 24.
2025 Jul. 21.
2025 May 18.
2025 Feb. 24.
2024 Sep. 15.
2024 May 01.

(手順)

PCにPT2チューナー、B-CAS ICカードリーダ、録画ファイル保存用ストレージを接続する。

この記事では、録画ファイル保存ストレージのパーティションを /dev/sdb1 とし、マウントポイントを /var/bigdata/02 とし、録画ファイル保存ディレクトリを /var/bigdata/02/video とする。

$ sudo mkdir -p /var/bigdata/02


録画ファイル保存ストレージをマウントする設定を /etc/fstab に記述し、システムを再起動する。

/dev/sdb1 /var/bigdata/02 ext4 defaults 0 2


録画ファイル保存ディレクトリを作成する。

$ sudo mkdir /var/bigdata/02/video
$ sudo chmod 777 /var/bigdata02/video


PT2アンテナ端子配置

  S1 BS (PCケース上蓋側)
  T1 地上
  S2 BS
  T2 地上 (マザーボード側)

ロケール、タイムゾーンを設定する

$ sudo apt update
$ sudo update-locale LANG=ja_JP.UTF-8
$ sudo timedatectl set-timezone Asia/Tokyo


時刻サーバーに同期させる


debian 13、ubuntu 24..04
次のリンク記事にしたがって設定する。
https://rasumus.hatenablog.com/entry/2026/09/14/072340

パッケージがインストールされていたら下記でもよい。

# /etc/systemd/timesyncd.conf に次の設定を記述する
NTP=ntp.nict.jp
FallbackNTP=ntp1.jst.mfeed.ad.jp ntp2.jst.mfeed.ad.jp ntp3.jst.mfeed.ad.jp

# サービス再起動
$ sudo systemctl restart systemd-timesyncd

# 状態確認
$ systemctl status systemd-timesyncd


ビルドツール、インストールツールをインストール

$ sudo apt update && sudo apt install build-essential git unzip dkms pkg-config automake cmake curl libssl-dev libtool libboost-all-dev yasm


(オプション)swapを無効化する

$ sudo swapoff --all
$ sudo reboot


(オプション)/tmpをtmpfsにする

/etc/fstabに次の行を書き込み、システムを再起動する。
tmpfs /tmp tmpfs defaults,noatime 0 0


(オプション)ログをRAMディスクに書き込むようにする

$ echo "deb http://packages.azlux.fr/debian/ buster main" | sudo tee /etc/apt/sources.list.d/azlux.list
$ wget -qO - https://azlux.fr/repo.gpg.key | sudo apt-key add -
$ sudo apt update
$ sudo apt install -y log2ram

/etc/log2ram.confのSIZEとUSE_RSYNCの設定を次のようにする。
SIZE=256MB
USE_RSYNC=true

システムを再起動する。

$ sudo reboot

稼働確認

$ df -h | grep "log2ram"
log2ram         256M   60M  197M  24% /var/log


チューナー PT2がPCに接続されていることを確認

$ lspci
03:00.0 Multimedia controller: Xilinx Corporation Device 222a (rev 01)


PT2デバイスを確認

PT2をPCに装着してLinuxを起動すると自動的に/dev/dvb/以下にデバイスノードが作成される。

$ ls /dev/dvb/
adapter0  adapter1  adapter2  adapter3

/dev/dvb/adapterN が作成されない時は次のリンク記事にしたがって作成する。

Xubuntu 24.04 : TVチューナーカードPT2のデバイスノード/(dev/dvb/adapterN)が自動作成されない時 - rokkonet

次の記事を参考に、B-CASカードリーダをXubuntuにインストールする

ICカードリーダライタSCR3310-NTTComをxubuntuにインストール - rokkonet


pcscdを停止する

Mirakurunでは pcscdが稼働していてはいけない。

$ sudo systemctl stop pcscd.socket
$ sudo systemctl disable pcscd.socket
$ pcsc_scan
 次の出力があれば成功
  SCardEstablishContext: Service not available.


dvb-tools(TV受信コマンドdvbv5-zap)をインストール

$ sudo apt install dvb-tools


b25デコードツールはmirakurunに含まれているのでインストール不要



Docker、docker-composeをインストール

docker-composeをインストールすると「distutilsファイルがない」とのエラーになるのでdocker-compose-v2をインストールする。
$ sudo apt update && sudo apt install docker.io docker-compose-v2

Dockerがインストールされたことを確認する。
$ docker version

Dockerの自動起動設定
$ sudo systemctl enable docker.service
$ systemctl status docker.service

dockerが稼働していなければ
$ sudo systemctl start docker.service


Docker版のMirakurun、MySQL、EPGStationを運用する任意の一般ユーザーを決める(ここではtvユーザーとする)

/etc/group ファイルを編集して、tvユーザーを他の一般ユーザーと同じように各グループに所属させ、さらに dockerグループに所属させる。


"$ sudo visudo"コマンドで、"tv ALL=(ALL:ALL) ALL"という1行を記述する。


システムからログアウトし、tvユーザーで再ログインする。


tvユーザーのホームディレクトリ内にdockerディレクトリを作成する

$ su - tv
$ mkdir ~/docker


Docker版mirakurun-epgstationをインストールする

GitHub - l3tnun/docker-mirakurun-epgstation: Mirakurun + EPGStation on Docker · GitHub ]のsetup.sh
docker-mirakurun-epgstation/setup.sh at v2 · l3tnun/docker-mirakurun-epgstation · GitHub
のとおりだが、次の変更を行ったので、下記の「docker-compose-v2でのDocker版mirakurun-epgstationのインストール」を実行する。

  • docker-composeコマンドをroot権限で実行しないようにした。
  • docker-compose-v2ではsetup.sh内のdocker-composeコマンドが通らず、"docker compose"にする必要があるので、"curl -sf https://raw.githubusercontent.com/l3tnun/docker-mirakurun-epgstation/v2/setup.sh | sh -s cd docker-mirakurun-epgstation"を実行せず、下記手順を進める。

    docker-compose-v2でのDocker版mirakurun-epgstationのインストール
$ cd ~/docker
$ git clone https://github.com/l3tnun/docker-mirakurun-epgstation.git
$ cd ~/docker/docker-mirakurun-epgstation
$ cp docker-compose-sample.yml docker-compose.yml && \
  cp epgstation/config/enc.js.template epgstation/config/enc.js && \
  cp epgstation/config/config.yml.template epgstation/config/config.yml && \
  cp epgstation/config/operatorLogConfig.sample.yml epgstation/config/operatorLogConfig.yml && \
  cp epgstation/config/epgUpdaterLogConfig.sample.yml epgstation/config/epgUpdaterLogConfig.yml && \
  cp epgstation/config/serviceLogConfig.sample.yml epgstation/config/serviceLogConfig.yml

BS15_2 channelの設定を修正する(dvbv5_channels_isdbs.confを修正する)

Mirakurun/config at master · Chinachu/Mirakurun · GitHub を開く。
"dvbconf-for-isdb"で始まるディレクトリを開く。
"conf"ディレクトリを開く。
dvbv5_channels_isdbs.confファイルの[BS15_2]セクションのSTREAM_IDが18675であれば修正の必要なし。

[BS15_2]
    DELIVERY_SYSTEM = ISDBS
    FREQUENCY = 1318000
    STREAM_ID = 18675

修正作業

Mirakurun/config at master · Chinachu/Mirakurun · GitHub を開く。
"dvbconf-for-isdb @ XXXXXXX"ディレクトリのURLの最後の/(スラッシュ)の後の文字列(dvbconf-for-isdb @ XXXXXXXのXXXXXXXから始まる文字列)をメモする。ここでは"e337342d140f24d8d22e1ab75d72a475192dd6ba"とする。

任意の作業ディレクトリに移る。
$ cd /WORK/DIR/

$ git clone https://github.com/Chinachu/dvbconf-for-isdb.git
$ cd dvbconf-for-isdb
$ git checkout e337342d140f24d8d22e1ab75d72a475192dd6ba

dvbv5_channels_isdbs.confをローカルホストのmirakurunコンテナイメージのソースリポジトリに配置する。
$ cp conf/dvbv5_channels_isdbs.conf ~/docker/docker-mirakurun-epgstation/mirakurun/conf/

~/docker/docker-mirakurun-epgstation/mirakurun/conf/dvbv5_channels_isdbs.confの [BS15_2]のセクションのSTREAM_ID設定を18675にする。

[BS15_2]
    DELIVERY_SYSTEM = ISDBS
    FREQUENCY = 1318000
    STREAM_ID = 18675


mirakurunのDockerfileの作成

Docker版MirakurunのDockerfile - rokkonet にしたがって、"~/docker/docker-mirakurun-epgstation/mirakurun/container-init.sh"と"~/docker/docker-mirakurun-epgstation/mirakurun/my.Dockerfile"を作成する。

MySQLのDockerfileの作成

Dockerfileが読み込むentrypointスクリプト作成

$ mkdir ~/docker/docker-mirakurun-epgstation/mysql


~/docker/docker-mirakurun-epgstation/mysql/entrypoint.sh

#!/bin/bash

# cron をバックグラウンドで起動
# cron
service cron start

# cron のログファイルが存在しない場合は作成
touch /var/log/cron.log

# MySQL をフォアグラウンドで起動
exec docker-entrypoint.sh mysqld

実行属性を付ける。

$ chmod +x ~/docker/docker-mirakurun-epgstation/mysql/entrypoint.sh

~/docker/docker-mirakurun-epgstation/mysql/my.Dockerfile を次の内容で作成

FROM mariadb:10.5

RUN apt-get update && apt-get install -y --no-install-recommends \
        vim \
        less \
        cron \
        locales \
        tzdata \
        procps \
    && echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen \
    && locale-gen \
    && update-locale LANG=ja_JP.UTF-8 \
    && ln -snf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
    && echo Asia/Tokyo > /etc/timezone \
    && dpkg-reconfigure -f noninteractive tzdata \
    && rm -rf /var/lib/apt/lists/* \
    && touch /var/log/cron.log

# スクリプトをコンテナにコピー
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

# ENTRYPOINT を変更
ENTRYPOINT ["/entrypoint.sh"]


EPGStationのDockerfileの作成

~/docker/docker-mirakurun-epgstation/epgstation/entrypoint.sh を作成する。


entrypoint.sh

#!/usr/bin/env bash
set -euo pipefail

# ----------------------------------------
# entrypoint.sh for Docker版EPGStation
# - cron をデーモンとして起動
# - EPGStation をフォアグラウンドで起動
# ----------------------------------------

# ログファイルがなければ作成
: > /var/log/cron.log

# cron デーモンをバックグラウンドで起動
echo ">>> Starting cron daemon..."
# cron
service cron start

# (オプション)cron のログをコンテナ stdout に流す
# tail -F /var/log/cron.log &

# EPGStation の起動
echo ">>> Launching EPGStation..."
exec npm run start

実行属性を付ける
$ chmod +x ~/docker/docker-mirakurun-epgstation/epgstation/entrypoint.sh

~/docker/docker-mirakurun-epgstation/epgstation/my.Dockerfile を次の内容で作成する。
########################################
# 1. Build Stage: FFmpeg をコンパイル
########################################
FROM debian:bookworm-slim AS builder

ARG DEV="make gcc git g++ automake curl wget autoconf build-essential pkg-config \
           texinfo zlib1g-dev"
ARG DEVFFMPEG="yasm libx264-dev libmp3lame-dev libopus-dev libvpx-dev libass-dev \
               libfreetype6-dev libsdl1.2-dev libtheora-dev libtool libva-dev \
               libvdpau-dev libvorbis-dev libx265-dev libnuma-dev libaribb24-dev"

ARG FFMPEGVERSION=7.0.1
ENV FFMPEGVERSION=${FFMPEGVERSION}

RUN set -eux; \
    apt-get update; \
    apt-get install -y --no-install-recommends \
      $DEV \
      $DEVFFMPEG \
      ca-certificates; \
    mkdir -p /tmp/ffmpeg_sources; \
    cd /tmp/ffmpeg_sources; \
    curl -fsSL "http://ffmpeg.org/releases/ffmpeg-${FFMPEGVERSION}.tar.bz2" \
      | tar -xj --strip-components=1; \
    ./configure \
      --prefix=/usr/local \
      --disable-shared \
      --pkg-config-flags="--static" \
      --enable-gpl --enable-version3 --enable-nonfree \
      --enable-libass --enable-libfreetype \
      --enable-libmp3lame --enable-libopus --enable-libtheora \
      --enable-libvorbis --enable-libvpx \
      --enable-libx264 --enable-libx265 --enable-libaribb24 \
      --disable-debug --disable-doc; \
    make -j"$(nproc)"; \
    make install; \
    apt-get purge -y --auto-remove $DEV $DEVFFMPEG; \
    rm -rf /var/lib/apt/lists/* /tmp/ffmpeg_sources

##################################
# 2. Runtime Stage: 必要最小限の共有ライブラリのみ
##################################
FROM l3tnun/epgstation:master-debian

# ビルド成果物をコピー
COPY --from=builder /usr/local /usr/local

ENV LANG=ja_JP.UTF-8 \
    TZ=Asia/Tokyo \
    DEBIAN_FRONTEND=noninteractive

RUN set -eux; \
    apt-get update; \
    apt-get install -y --no-install-recommends \
      cron locales tzdata procps vim less \
      libasound2 libass9 libvdpau1 \
      libva-drm2 libva-x11-2 \
      libvorbisenc2 libtheora0 \
      libvpx7 libopus0 libmp3lame0 libaribb24-0 \
      libx264-164 libx265-199; \
    echo "/usr/local/lib" > /etc/ld.so.conf.d/local-ffmpeg.conf; \
    ldconfig; \
    echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen; \
    locale-gen; \
    update-locale LANG=ja_JP.UTF-8; \
    ln -snf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime; \
    echo "Asia/Tokyo" > /etc/timezone; \
    dpkg-reconfigure -f noninteractive tzdata; \
    touch /var/log/cron.log; \
    chmod 0644 /var/log/cron.log; \
    apt-get clean; \
    rm -rf /var/lib/apt/lists/*

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]


ホストのtvユーザーで、

$ mkdir ~/docker/docker-mirakurun-epgstation/epgstation/crontab
$ chmod 755 ~/docker/docker-mirakurun-epgstation/epgstation/crontab


ホストに共有ディレクトリを作成する。

Mirakurun
$ mkdir ~/docker/docker-mirakurun-epgstation/mirakurun/crontab  # コンテナのcron設定を保管する場所
$ chmod 755 ~/docker/docker-mirakurun-epgstation/mirakurun/crontab
$ sudo mkdir /var/bigdata/02/video/mirakurun  # EPGStationを使わずMirakurunの機能だけで録画したファイルの保管場所
$ sudo chmod 777 /var/bigdata/02/video/mirakurun
MySQL
$ mkdir -p ~/docker/docker-mirakurun-epgstation/mysql/crontab # コンテナのcron設定を保管する場所
$ chmod 755 ~/docker/docker-mirakurun-epgstation/mysql/crontab
$ mkdir ~/docker/docker-mirakurun-epgstation/mysql/my.cnf # コンテナのMySQL設定を保管する場所
$ sudo chown 999:999 ~/docker/docker-mirakurun-epgstation/mysql/my.cnf  # mysqlコンテナ内のmysqlユーザーID999に合わせる
$ sudo chmod 700 ~/docker/docker-mirakurun-epgstation/mysql/my.cnf
$ mkdir ~/mysql-share # mysqlコンテナとのデータ共有場所
$ chmod 777 ~/mysql-share
EPGStation
これは上記までに作成済み -> $ mkdir ~/docker/docker-mirakurun-epgstation/epgstation/crontab # コンテナのcron設定を保管する場所
これは上記までに設定済み -> $ chmod 755 ~/docker/docker-mirakurun-epgstation/epgstation/crontab
$ sudo mkdir -p /var/bigdata/02/video/epgstation250803/video # 録画ファイル保管場所
$ sudo chmod 777 /var/bigdata/02/video/epgstation250803/video
$ sudo mkdir /var/bigdata/02/video/epgstation250803/thumbnail # 録画ファイルサムネイル保管場所
$ sudo chmod 777 /var/bigdata/02/video/epgstation250803/thumbnail


docker-compose.ymlの作成


元ファイルのバックアップ

$ cp ~/docker/docker-mirakurun-epgstation/docker-compose.yml ~/docker/docker-mirakurun-epgstation/docker-compose.yml.original


~/docker/docker-mirakurun-epgstation/docker-compose.yml を以下の記述に沿って 修正する


各コンテナ内のディレクトリをホストと共有できるようにdocker-compose.ymlの各サービスのvolumes:セクションを修正する


(書式)
 - ホスト側の(docker-compose.ymlから見た相対)パス:コンテナの絶対パス

mirakurun

mirakurunのvolumes:セクションにMirakurun録画ファイルを保管するディレクトリを設定する。
 - /var/bigdata/02/video/mirakurun:/app-recorded

cron設定保管ディレクトリ設定
 - ./mirakurun/crontab:/var/spool/cron/crontabs

EPGStation

録画ファイルをコンテナ外で保存するように、epgstationのvolumes:セクションを修正する。

録画サムネイルファイル保存用と録画ファイル保存用のホスト側ディレクトリは以下とした。
* コンテナ内のthumbnailに結びつけるホスト側ディレクトリ:/var/bigdata/02/video/epgstation240501/thumbnail
* コンテナ内のrecordedに結びつけるホスト側ディレクトリ:/var/bigdata/02/video/epgstation240501/video
* コンテナ内のcrontabに結びつけるホスト側ディレクトリ:~/docker/docker-mirakurun-epgstation/epgstation/crontab

volume設定
* 52行目あたりの"- ./epgstation/thumbnail:/app/thumbnail"の:(コロン)の前は録画サムネイルファイル保存ディレクトリのパスにする。
* 54行目あたりの"- ./recorded:/app/recorded"の:(コロン)の前は録画ファイル保存ディレクトリのパスにする。

MySQL

mysql:のvolumes:セクションに次を記述する。

mysql:
        volumes:
            - mysql-db:/var/lib/mysql
            - /home/tv-xu2404/mysql-share:/tmp # ホストとのデータ共有
            - ./mysql/crontab:/var/spool/cron/crontabs # ホストとのcron設定の共有
            - ./mysql/my.cnf:/etc/mysql/conf.d # ホストとのmysql設定の共有
docker-compose.yml のvolumesへの記述例
 [mirakurunのvolumes設定]
    volumes:
        - ./mirakurun/conf:/app-config
        - ./mirakurun/data:/app-data
        - /var/bigdata/02/video/mirakurun:/app-recorded
        - ./mirakurun/crontab:/var/spool/cron/crontabs

 [mysqlのvolumes設定]
    volumes:
        - mysql-db:/var/lib/mysql
        - /home/tv-xubuntu2404/mysql-share:/tmp
        - ./mysql/crontab:/var/spool/cron/crontabs
        - ./mysql/my.cnf:/etc/mysql/conf.d

 [epgstationのvolumes設定]
    volumes:
        - ./epgstation/config:/app/config
        - ./epgstation/data:/app/data
        - ./epgstation/logs:/app/logs
        - ./epgstation/crontab:/var/spool/cron/crontabs
        - /var/bigdata/02/video/epgstation250222/video:/app/recorded
        - /var/bigdata/02/video/epgstation250222/thumbnail:/app/thumbnail

MySQLのパスワード等設定

  • mysqlのenvironment:セクションにデータベース名・ユーザー名・パスワードを設定する。
  • MYSQL_ROOT_PASSWORD変数にMySQLのrootユーザーのパスワードを設定する。(デフォルトではepgstationになっている)
  • ユーザー名、データベース名、ユーザーパスワードはepgstationとしておくのが無難。

    各コンテナにタイムゾーン・ロケールを設定する

services:
    mirakurun:
        environment:
            TZ: "Asia/Tokyo"
            LANG: ja_JP.UTF-8
            LC_ALL: ja_JP.UTF-8

    mysql:
        environment:
            TZ: "Asia/Tokyo"
            LANG: ja_JP.UTF-8
            LC_ALL: ja_JP.UTF-8

    epgstation:
        environment:
            TZ: "Asia/Tokyo"
            LANG: ja_JP.UTF-8
            LC_ALL: ja_JP.UTF-8

Mirakurun、MYSQL、EPGStationの各コンテナの確実な連携

Docker Compose環境でのホスト名について

Docker Compose環境では、同じDockerネットワーク内に存在するコンテナ同士は、コンテナサービス名をホスト名として名前解決できるようになる。
そのため、コンテナ間の通信にはサービス名を使用する。
EPGStationのコンテナから見て、Mirakurunのコンテナは mirakurun というサービス名で稼働している。
Dockerコンテナにおける localhost はコンテナ自身を指す。
もし "http://localhost:40772" と指定してしまうと、EPGStationコンテナ自身の中でポート 40772 を探してしまうため、Mirakurunと通信ができずエラーになってしまう。

epgstation:
  environment:
    TZ: "Asia/Tokyo"
    LANG: ja_JP.UTF-8
    LC_ALL: ja_JP.UTF-8
    HOST: "0.0.0.0"
    PORT: "8888"
    REMOTE_MIRAKURUN_URI: "http://mirakurun:40772"
    DB_HOST: "mysql"
    DB_USER: "epgstation"
    DB_PASSWORD: "epgstation"
    DB_NAME: "epgstation"


その他の各種設定

・docker-compose.ymlファイルの先頭に"version:"行があれば、不要なので削除する。

・各コンテナはDockerfileでビルドするので、各コンテナサービスの build セクションにmy.Dockerfileを指定する。
・各サービスの"image:"行は削除する。

#version: '3.7'
services:
    mirakurun:
        # image: chinachu/mirakurun
        build:
            context: "./mirakurun"
            dockerfile: "my.Dockerfile"

    mysql:
        # image: mariadb:10.5
        build:
            context: "./mysql"
            dockerfile: "my.Dockerfile"

    epgstation:
        build:
            context: "./epgstation"
            dockerfile: "my.Dockerfile"


作成した docker-compose.yml の全体


~/docker/docker-mirakurun-epgstation/docker-compose.yml

services:
    mirakurun:
        build:
            context: "./mirakurun"
            dockerfile: "my.Dockerfile"

        cap_add:
            - SYS_ADMIN
            - SYS_NICE

        ports:
            - "40772:40772"
            - "9229:9229"

        volumes:
            - ./mirakurun/conf:/app-config
            - ./mirakurun/data:/app-data
            - ./mirakurun/crontab:/var/spool/cron/crontabs
            - /var/usbhdd01/video/mirakurun:/app-recorded

        environment:
            TZ: "Asia/Tokyo"
            LANG: ja_JP.UTF-8
            LC_ALL: ja_JP.UTF-8

        devices:
            - /dev/bus:/dev/bus
            - /dev/dvb:/dev/dvb

        restart: always

        logging:
            driver: json-file
            options:
                max-file: "1"
                max-size: 10m

    mysql:
        build:
            context: "./mysql"
            dockerfile: "my.Dockerfile"

        volumes:
            - mysql-db:/var/lib/mysql
            - /home/tv-xu2404/mysql-share:/tmp
            - ./mysql/crontab:/var/spool/cron/crontabs
            - ./mysql/my.cnf:/etc/mysql/conf.d

        environment:
            MYSQL_USER: epgstation
            MYSQL_PASSWORD: epgstation
            MYSQL_ROOT_PASSWORD: MYSQLROOTPW  # 任意のmysql rootパスワード
            MYSQL_DATABASE: epgstation
            TZ: "Asia/Tokyo"
            LANG: ja_JP.UTF-8
            LC_ALL: ja_JP.UTF-8

        command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --performance-schema=false --expire_logs_days=1
        restart: always
        logging:
            options:
                max-size: "10m"
                max-file: "3"

    epgstation:
        build:
            context: "./epgstation"
            dockerfile: "my.Dockerfile"
        volumes:
            - ./epgstation/config:/app/config
            - ./epgstation/data:/app/data
            - ./epgstation/logs:/app/logs
            - ./epgstation/crontab:/var/spool/cron/crontabs
            - /var/usbhdd01/video/epgstation250928/video:/app/recorded
            - /var/usbhdd01/video/epgstation250928/thumbnail:/app/thumbnail

        environment:
            TZ: "Asia/Tokyo"
            LANG: ja_JP.UTF-8
            LC_ALL: ja_JP.UTF-8
            HOST: "0.0.0.0"
            PORT: "8888"
            REMOTE_MIRAKURUN_URI: "http://mirakurun:40772"
            DB_HOST: "mysql"
            DB_USER: "epgstation"
            DB_PASSWORD: "epgstation"
            DB_NAME: "epgstation"

        depends_on:
            - mirakurun
            - mysql
        ports:
            - "8888:8888"
            - "8889:8889"
        restart: always

volumes:
    mysql-db:
        driver: local


mirakurunをSETUPする。

$ cd ~/docker/docker-mirakurun-epgstation
$ docker compose run --rm -e SETUP=true mirakurun


Mirakurunの tuners.yml は ~/docker/docker-mirakurun-epgstation/mirakurun/conf/ に自動作成されるので、作業不要。



Mirakurun用チャンネル情報ファイルを作成する

$ cd ~/docker/docker-mirakurun-epgstation/mirakurun/conf
$ sudo cp channels.yml channels.yml.original
手動でチャンネル設定する場合

参考 mirakurun BSCSチャンネル設定(2025/09/01現在) · GitHub

下記の"神戸チャンネル フォーマット"の内容で ~/docker/docker-mirakurun-epgstation/mirakurun/conf/channels.yml を作成する。

~/docker/docker-mirakurun-epgstation/mirakurun/conf/ 内のファイルのオーナーはrootユーザーになっているので、root権限で編集する。

神戸チャンネル フォーマット 広島チャンネル フォーマット
参照先:Mirakurun : チャンネル設定データ - rokkonet

自動チャンネル設定する場合

地デジ チャンネルスキャン

(mirakurunコンテナが稼働した状態で、Dockerコンテナ内ではなくホスト上で)
$ curl -X PUT "http://localhost:40772/api/config/channels/scan?type=GR&setDisabledOnAdd=false&refresh=true"

BS チャンネルスキャン

(mirakurunコンテナが稼働した状態で、Dockerコンテナ内ではなくホスト上で)
$ curl -X PUT "http://localhost:40772/api/config/channels/scan?type=BS&setDisabledOnAdd=false&refresh=true"


BSを自動チャンネル設定したら1日経過しても番組表が歯抜け状態となったが、mirakurunのGUIのconfigでrestartしたら直った。

EPGStationの設定を行う

最低限の[オリジナルの]config.ymlでTV録画システム起動を確認する


$ cp ~/docker/docker-mirakurun-epgstation/epgstation/config/config.yml ~/docker/docker-mirakurun-epgstation/epgstation/config/config.yml.original

最低限のconfig.yml: ~/docker/docker-mirakurun-epgstation/epgstation/config/config.yml

port: 8888
mirakurunPath: http://mirakurun:40772/

dbtype: mysql
mysql:
    host: mysql
    port: 3306
    user: epgstation
    password: epgstation
    database: epgstation

epgUpdateIntervalTime: 10

conflictPriority: 1
recPriority: 2

recordedFormat: '%YEAR%年%MONTH%月%DAY%日%HOUR%時%MIN%分%SEC%秒-%TITLE%'
recordedFileExtension: .m2ts
recorded:
    - name: recorded
      path: '%ROOT%/recorded'

thumbnail: '%ROOT%/thumbnail'
thumbnailSize: 480x270
thumbnailPosition: 5

ffmpeg: /usr/local/bin/ffmpeg
ffprobe: /usr/local/bin/ffprobe

encodeProcessNum: 4
concurrentEncodeNum: 1
encode:
    - name: H.264
      cmd: '%NODE% %ROOT%/config/enc.js'
      suffix: .mp4
      rate: 4.0

urlscheme:
    m2ts:
        ios: vlc-x-callback://x-callback-url/stream?url=PROTOCOL%3A%2F%2FADDRESS
        android: intent://ADDRESS#Intent;action=android.intent.action.VIEW;type=video/*;scheme=PROTOCOL;end
    video:
        ios: infuse://x-callback-url/play?url=PROTOCOL://ADDRESS
        android: intent://ADDRESS#Intent;action=android.intent.action.VIEW;type=video/*;scheme=PROTOCOL;end
    download:
        ios: vlc-x-callback://x-callback-url/download?url=PROTOCOL%3A%2F%2FADDRESS&filename=FILENAME

stream:
    live:
        ts:
            m2ts:
                - name: 720p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2
                      -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -y -f mpegts pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2
                      -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -y -f mpegts pipe:1'
                - name: 無変換

            m2tsll:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -f mpegts -analyzeduration 500000 -i pipe:0 -map 0 -c:s copy -c:d
                      copy -ignore_unknown -fflags nobuffer -flags low_delay -max_delay 250000 -max_interleave_delta 1
                      -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -flags +cgop -vf yadif,scale=-2:720
                      -b:v 3000k -preset veryfast -y -f mpegts pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -f mpegts -analyzeduration 500000 -i pipe:0 -map 0 -c:s copy -c:d
                      copy -ignore_unknown -fflags nobuffer -flags low_delay -max_delay 250000 -max_interleave_delta 1
                      -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -flags +cgop -vf yadif,scale=-2:480
                      -b:v 1500k -preset veryfast -y -f mpegts pipe:1'

            webm:
                - name: 720p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac
                      2 -c:v libvpx-vp9 -vf yadif,scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f
                      webm pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 2 -c:a libvorbis -ar 48000 -b:a 128k -ac
                      2 -c:v libvpx-vp9 -vf yadif,scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f
                      webm pipe:1'

            mp4:
                - name: 720p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2
                      -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2
                      -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'

            hls:
                - name: 720p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast
                      -flags +loop-global_header %OUTPUT%'
                - name: 480p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast
                      -flags +loop-global_header %OUTPUT%'
    recorded:
        ts:
            webm:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac 2
                      -c:v libvpx-vp9 -vf yadif,scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f
                      webm pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 128k -ac 2
                      -c:v libvpx-vp9 -vf yadif,scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f
                      webm pipe:1'

            mp4:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v
                      libx264 -vf yadif,scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v
                      libx264 -vf yadif,scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'

            hls:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast
                      -flags +loop-global_header %OUTPUT%'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast
                      -flags +loop-global_header %OUTPUT%'

        encoded:
            webm:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 3 -c:a libvorbis -ar 48000 -b:a
                      192k -ac 2 -c:v libvpx-vp9 -vf scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y
                      -f webm pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 3 -c:a libvorbis -ar 48000 -b:a
                      128k -ac 2 -c:v libvpx-vp9 -vf scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y
                      -f webm pipe:1'

            mp4:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac
                      2 -c:v libx264 -vf scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac
                      2 -c:v libx264 -vf scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'

            hls:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf scale=-2:720 -b:v 3000k -preset veryfast -flags
                      +loop-global_header %OUTPUT%'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf scale=-2:480 -b:v 3000k -preset veryfast -flags
                      +loop-global_header %OUTPUT%'

Dockerコンテナを起動する。

$ cd ~/docker/docker-mirakurun-epgstation
$ docker compose build --no-cache --pull
$ docker compose up

ブラウザで"http://localhost:40772"にアクセスしてMirakurunサーバーへの接続を確認する。

ブラウザで"http://localhost:8888"にアクセスしてEPGStationサーバーへの接続を確認する。

コマンドラインの出力を確認する。

ffmpegの稼働を確認する。
 "docker compose up"コマンドを入力したコマンドライン端末はそのままにして、別のシェルコマンドライン端末を起動する。
 次のコマンドを実行する。

$ cd ~/docker/docker-mirakurun-epgstation
$ docker exec -it $(docker ps | grep docker-mirakurun-epgstation-epgstation | grep -o '^[^ ]*') bash  # epgstationコンテナ内に入る
# which ffmpeg  # ffmpegコマンドの有無とコマンドパスを確認する
# ffmpeg -version  # ffmpegコマンドを実行できることを確認する
# exit  # epgstationコンテナから出る
$ exit  # シェルコマンドライン端末を終了する


MirakurunでNHK BS101チャンネルを視聴する。
 VLCメディアプレーヤーで再生するなら、「ネットワークストリームを開く」メニューに次のURLを指定する。
 http://MIRAKURUN-SERVER:40772/api/channels/BS/BS15_0/services/101/stream

ブラウザでEPGStationサーバーに接続し、放映中画面でライブ視聴ができることを確認する。

"docker compose up"コマンドを実行中のコマンドライン端末でキーボードから"Ctrl-c"を入力し、dockerコンテナを終了する。


dockerコンテナをデーモン起動する。

$ cd ~/docker/docker-mirakurun-epgstation
$ docker compose up -d


EPGStationで録画する。
 番組表が取得されるまでしばらく待機する。
 EPGStationで"TS"形式の録画と"H.264"形式の録画を予約し、録画結果を確認する。

config.ymlをカスタマイズする

~/docker/docker-mirakurun-epgstation/epgstation/config/config.yml に下記の修正(mysqlの文字コード指定、録画ファイル名フォーマット)を行う。

$ diff -u config.yml.original config.yml
--- config.yml.original 2024-05-03 11:40:10.068198955 +0900
+++ config.yml  2024-05-03 11:52:43.442341934 +0900
@@ -14,13 +14,14 @@
     user: epgstation
     password: epgstation
     database: epgstation
+    charset: utf8mb4
 
 epgUpdateIntervalTime: 10
 
 conflictPriority: 1
 recPriority: 2
 
-recordedFormat: '%YEAR%年%MONTH%月%DAY%日%HOUR%時%MIN%分%SEC%秒-%TITLE%'
+recordedFormat: '%SHORTYEAR%%MONTH%%DAY%%HOUR%%MIN%%TYPE%%CH%-%SID%-%CHNAME%-%TITLE%'
 recordedFileExtension: .m2ts
 recorded:
     - name: recorded


H.264エンコード設定増設

次の3モードを追加する。

  • 音声欠落を防ぐため、冒頭4秒間をカットして H.264にエンコードする設定
  • NHK BSドキュメンタリー H.264エンコード設定
  • NHK19時ニュース H.264エンコード設定

次の参照先にしたがって、設定する。
 参照先 EPGStation : エンコードモード追加 - rokkonet

作成したconfig.yml
port: 8888
mirakurunPath: http://mirakurun:40772/

# dbtype: sqlite
# sqlite:
#   extensions:
#     - '/hoge/regexp.dylib'
#   regexp: true

dbtype: mysql
mysql:
    host: mysql
    port: 3306
    user: epgstation
    password: epgstation
    database: epgstation
    charset: utf8mb4

epgUpdateIntervalTime: 10

conflictPriority: 1
recPriority: 2

#recordedFormat: '%YEAR%年%MONTH%月%DAY%日%HOUR%時%MIN%分%SEC%秒-%TITLE%'
recordedFormat: '%SHORTYEAR%%MONTH%%DAY%%HOUR%%MIN%%TYPE%%CH%-%SID%-%CHNAME%-%TITLE%'
recordedFileExtension: .m2ts
recorded:
    - name: recorded
      path: '%ROOT%/recorded'

thumbnail: '%ROOT%/thumbnail'
thumbnailSize: 480x270
thumbnailPosition: 5

ffmpeg: /usr/local/bin/ffmpeg
ffprobe: /usr/local/bin/ffprobe

encodeProcessNum: 4
concurrentEncodeNum: 1
encode:
    - name: H.264-cut4secs
      cmd: '%NODE% %ROOT%/config/enc-cut4secs.js'
      suffix: .mp4
      rate: 4.0

    - name: H.264-nhk_bs_docu-cut4secs
      cmd: '%NODE% %ROOT%/config/enc-bs_docu-cut4secs.js'
      suffix: .mp4
      rate: 4.0

    - name: H.264-nhk_news-cut4secs
      cmd: '%NODE% %ROOT%/config/enc-nhk_news-cut4secs.js'
      suffix: .mp4
      rate: 4.0

    - name: H.264
      cmd: '%NODE% %ROOT%/config/enc.js'
      suffix: .mp4
      rate: 4.0

urlscheme:
    m2ts:
        ios: vlc-x-callback://x-callback-url/stream?url=PROTOCOL%3A%2F%2FADDRESS
        android: intent://ADDRESS#Intent;action=android.intent.action.VIEW;type=video/*;scheme=PROTOCOL;end
    video:
        ios: infuse://x-callback-url/play?url=PROTOCOL://ADDRESS
        android: intent://ADDRESS#Intent;action=android.intent.action.VIEW;type=video/*;scheme=PROTOCOL;end
    download:
        ios: vlc-x-callback://x-callback-url/download?url=PROTOCOL%3A%2F%2FADDRESS&filename=FILENAME

stream:
    live:
        ts:
            m2ts:
                - name: 720p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2
                      -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -y -f mpegts pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2
                      -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -y -f mpegts pipe:1'
                - name: 無変換

            m2tsll:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -f mpegts -analyzeduration 500000 -i pipe:0 -map 0 -c:s copy -c:d
                      copy -ignore_unknown -fflags nobuffer -flags low_delay -max_delay 250000 -max_interleave_delta 1
                      -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -flags +cgop -vf yadif,scale=-2:720
                      -b:v 3000k -preset veryfast -y -f mpegts pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -f mpegts -analyzeduration 500000 -i pipe:0 -map 0 -c:s copy -c:d
                      copy -ignore_unknown -fflags nobuffer -flags low_delay -max_delay 250000 -max_interleave_delta 1
                      -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -flags +cgop -vf yadif,scale=-2:480
                      -b:v 1500k -preset veryfast -y -f mpegts pipe:1'

            webm:
                - name: 720p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac
                      2 -c:v libvpx-vp9 -vf yadif,scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f
                      webm pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 2 -c:a libvorbis -ar 48000 -b:a 128k -ac
                      2 -c:v libvpx-vp9 -vf yadif,scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f
                      webm pipe:1'

            mp4:
                - name: 720p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2
                      -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2
                      -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'

            hls:
                - name: 720p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast
                      -flags +loop-global_header %OUTPUT%'
                - name: 480p
                  cmd:
                      '%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast
                      -flags +loop-global_header %OUTPUT%'
    recorded:
        ts:
            webm:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac 2
                      -c:v libvpx-vp9 -vf yadif,scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f
                      webm pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 128k -ac 2
                      -c:v libvpx-vp9 -vf yadif,scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f
                      webm pipe:1'

            mp4:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v
                      libx264 -vf yadif,scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v
                      libx264 -vf yadif,scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'

            hls:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast
                      -flags +loop-global_header %OUTPUT%'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast
                      -flags +loop-global_header %OUTPUT%'

        encoded:
            webm:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 3 -c:a libvorbis -ar 48000 -b:a
                      192k -ac 2 -c:v libvpx-vp9 -vf scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y
                      -f webm pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 3 -c:a libvorbis -ar 48000 -b:a
                      128k -ac 2 -c:v libvpx-vp9 -vf scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y
                      -f webm pipe:1'

            mp4:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac
                      2 -c:v libx264 -vf scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac
                      2 -c:v libx264 -vf scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune
                      fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4
                      pipe:1'

            hls:
                - name: 720p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf scale=-2:720 -b:v 3000k -preset veryfast -flags
                      +loop-global_header %OUTPUT%'
                - name: 480p
                  cmd:
                      '%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -ignore_unknown
                      -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1
                      -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a
                      aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf scale=-2:480 -b:v 3000k -preset veryfast -flags
                      +loop-global_header %OUTPUT%'


TV録画システム起動

稼働中の TV録画システムを終了する。
$ cd ~/docker/docker-mirakurun-epgstation
$ docker compose down

TV録画システムを再ビルドする
 $ docker compose build --no-cache
  ホスト共有ディレクトリ以外のコンテナ内のデータは、失われる。

TV録画システムを起動する。
・ブラウザの設定でキャッシュを削除する。
・TV録画システムを起動する。
  $ docker compose up -d
・TVシステム起動後、ブラウザでMirakurun・EPGStationのページを表示し、"Ctrl + Shift + R"でハードリロードする。
(設定を変更した時は、ブラウザのキャッシュを削除し、ブラウザのページを"Ctrl + Shift + R"でハードリロードする)

Mirakurun、EPGStation各コンテナ内ディレクトリとホストのディレクトリの共用設定反映を確認する。

Mirakurunコンテナ内のcron自動起動設定

次のページにしたがって、cron自動起動を設定する。
Docker版mirakurun:vim・ less・cron・ps組み込み、locale・タイムゾーン設定とcronの自動起動設定 - rokkonet

mirakutunコンテナ内で"crontab -e"を実行し、コマンド記述部の上部に次の変数設定を記述する。

PATH=/app-data/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin  # 適当に検索パスを増やす
LANG=ja_JP.UTF-8
LC_ALL=ja_JP.UTF-8

cronで適当なコマンドを実行してcronの稼働、時間設定(JSTかUTCか)を確認する。
 例)下記cron設定で、cronの稼働、時間設定、共有ディレクトリの確認ができる。

39 21 * * * /usr/bin/touch /app-recorded/"$(date)"

MySQL設定ファイル作成

MySQL接続のパスワードを不要にする。
次の内容の"~/docker/docker-mirakurun-epgstation/mysql/my.cnf/50-client.cnf"ファイルを作成する。
(ファイル名は、拡張子が.cnfであれば何でもよい)

[client]
user=epgstation
password=epgstation
host=localhost

ファイル属性を設定する

$ sudo chown 999:999 ~/docker/docker-mirakurun-epgstation/mysql/my.cnf/50-client.cnf  # mysqlコンテナ内のmysqlユーザーID999に合わせる
$ sudo chmod 600 ~/docker/docker-mirakurun-epgstation/mysql/my.cnf/50-client.cnf


MySQLコンテナのcronのロケール設定

MySQLコンテナ内で"crontab -e"を実行し、コマンド記述部の上部に次の変数設定を記述する。

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin  # 適当に検索パスを増やす
LANG=ja_JP.UTF-8
LC_ALL=ja_JP.UTF-8


EPGStationの録画データのテキストファイル保存

MySQLコンテナ内のcron用のシェルスクリプトを作成する

ホストにて次のコマンドでMySQLコンテナに入る。
$ docker exec -it $(docker ps | grep 'mysql' | awk '{print $1}') bash

/usr/local/bin/saveEpgstationDataAsText.sh を次の内容で作成する。
スクリプト中のSHAREはホストと共有のディレクトリとする。この記事では"/tmp"ディレクトリ。
スクリプト中のUSERNAMEとDATABASENAMEは、この記事ではepgstation。

参考: EPGStationの録画データをテキストファイルに保存する - rokkonet

#!/usr/bin/env sh
# save epgstation-recorded-data in mysql to text-file.

if [ -f  SHARE/epgstation-recorded-information.txt ]; then
   mv SHARE/epgstation-recorded-information.txt  SHARE/epgstation-recorded-information-prev.txt
fi

mysql -u USERNAME DATABASENAME -B -e "SELECT video_file.id, video_file.parentDirectoryName, video_file.filePath, video_file.type, video_file.name, video_file.size, video_file.recordedId, recorded.startAt, recorded.endAt, recorded.name, recorded.description, recorded.extended FROM video_file LEFT OUTER JOIN recorded ON video_file.recordedId = recorded.id;" > SHARE/epgstation-recorded-information.txt

exit 0

ファイルに実行属性を与える。
# chmod 744 /usr/local/bin/saveEpgstationDataAsText.sh

cron設定する

ホストにて次のコマンドでMySQLコンテナに入る。
$ docker exec -it $(docker ps | grep 'mysql' | awk '{print $1}') bash

"crontab -e"コマンドで次の設定を記述する。

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LANG=ja_JP.UTF-8
LC_ALL=ja_JP.UTF-8

# m h  dom mon dow   command
1   3   *   *   *   /usr/local/bin/saveEpgstationDataAsText.sh


トラブル等でのログの確認

$ su - tv
$ docker logs docker-mirakurun-epgstation-epgstation-1  # docker-mirakurun、、、の途中でTabキーを押せば補完される
$ docker compose logs -f mirakurun
$ docker compose logs -f epgstation
$ docker compose logs -f mysql


稼働しているTV録画システムの終了と再起動

録画ファイル・サムネイルファイル・mysql内の情報(予約情報・録画情報等)は削除されない。

$ cd ~/docker/docker-mirakurun-epgstation

## 稼働状況確認
$ docker ps

## コンテナ内データが削除されない終了
$ docker compose down

## コンテナ内データが削除される終了
##   mysql内の情報(予約情報・録画情報等)等、コンテナ内のデータは削除される
##   録画ファイル・サムネイルファイルはホストとの共有ディレクトリに保存していない限り削除される
$ docker compose down -v

## 既存コンテナの起動
$ docker compose up -d
Docker内ソフトの設定を変更した時は、ブラウザのキャッシュを削除し、ブラウザのページを"Ctrl + Shift + R"でハードリロードする。

## キャッシュを利用せずにホスト内の既存コンテナイメージからビルド
$ docker compose build --no-cache

## キャッシュを効かせてホスト内の既存コンテナイメージからビルド
$ docker compose build

## コンテナをビルドして起動
$ docker compose up --build -d
上記コマンド実行後、ブラウザのキャッシュを削除し、ブラウザのページを"Ctrl + Shift + R"でハードリロードする。

## コンテナイメージをダウンロードしキャッシュを使わずビルド
$ docker compose build --no-cache --pull

## コンテナイメージをダウンロードしキャッシュを効かせてビルド
$ docker compose build --pull


"docker compose down"コマンドについて

docker compose down

Composeファイルで定義されたサービスを停止し、関連するコンテナを削除するが、ボリューム(データ)は削除されない。
つまり、ボリュームデータは残る。

docker compose down -v

Composeファイルで定義されたサービスを停止し、関連するコンテナを削除するだけでなく、ボリューム(データ)も削除する。
つまり、ボリュームデータも一緒に削除される。

ボリュームデータを保持して再利用したい場合は、"docker compose down"コマンドを使用し、ボリュームデータも削除したい場合は、"docker compose down -v"コマンドを使用する。
ホストとの共有ディレクトリ内のデータは削除されない。

設定変更手順

Dockerコンテナ内のソフトの設定変更の反映

(1)Dockerコンテナを停止する
  $ docker compose down

(2)Dockerコンテナを起動する
  $ docker compose up -d

(3)ブラウザのキャッシュを削除する

(4)操作するブラウザのページを"Ctrl + Shift + R"でハードリロードする

その他

 参考ページ Docker Composeにおける各種ファイルの変更時の反映 #docker-compose - Qiita

docker-compose.ymlを修正する場合

要注意

mysql内の情報(予約情報・録画情報等)等、コンテナ内のデータは削除される。
録画ファイル・サムネイルファイルはホスト共有ディレクトリに保存していない限り削除される。

手順
  • コンテナを終了・ボリュームを削除する。
    $ cd ~/docker/docker-mirakurun-epgstation
    $ docker compose down -v

  • 設定変更作業を行う。

  • コンテナを起動する。
    $ docker compose up -d

    ブラウザのキャッシュを削除し、ブラウザのページを"Ctrl + Shift + R"でハードリロードする。

    Dockerfileを修正する場合

    コンテナを再構築して、再起動する。

    要注意
    mysql内の情報(予約情報・録画情報等)等、コンテナ内のデータは削除される。
    録画ファイル・サムネイルファイルはコンテナ外に保存していない限り削除される。

    手順
  • コンテナを終了・ボリュームを削除する。
    $ cd ~/docker/docker-mirakurun-epgstation
    $ docker compose down -v

  • 設定変更作業を行う。

  • イメージ取得・ビルド・コンテナ起動を行う。
    $ docker compose build --pull
    $ docker compose up -d
    ブラウザのキャッシュを削除し、ブラウザのページを"Ctrl + Shift + R"でハードリロードする。

"--pull"オプションを使用すると、Composeがビルドプロセスの一部としてイメージをプルするように指示する。つまり、ローカルにそのイメージが存在しない場合でも、常に最新のイメージを取得してビルドする。これにより、常に最新の状態でアプリケーションをビルドおよび実行できる。

EPGStationコンテナの停止

$ cd ~/docker/docker-mirakurun-epgstation
$ docker compose kill epgstation
$ docker compose rm -f epgstation

"docker compose kill"コマンドは、Composeで定義されたサービスのコンテナを強制的に停止するが、ボリュームデータを削除しない。このコマンドは単にコンテナを停止するだけであり、ボリュームデータには影響を与えない。

"docker compose rm -f"コマンドは、Composeファイルで定義されたサービスのコンテナを強制的に削除する。このコマンドを実行すると、実行中のコンテナを強制的に停止し、その後削除する。ボリュームデータは削除されない。つまり、関連するコンテナが削除されるが、ボリュームデータは残る。

epgstation以外でのmysqlの利用

tvユーザでmysqlコンテナに入ってmysqlを利用する。

(tvユーザでログインする)
$ su - tv

(mysqlコンテナIDを確認する)
$ docker ps

(mysqlコンテナに入る)
$ docker exec -it MYSQLCONTAINERID /bin/bash

(mysqlクライアントを起動する)
# mysql -u USER -p DATABASE

コンテナ外とのデータ共有

docker-compose.ymlに"- /home/tv-USER/mysql-share:/tmp"を記述したので、コンテナ内の"/tmp"ディレクトリがホストの"/home/tv-USER/mysql-share"と共有される。

mirakurunのログ取得(コンテナのログ収録)

$ docker logs MIRAKURUN-CONTAINER-ID


TV番組録画、ストリーミング再生

https://rasumus.hatenablog.com/entry/2025/01/11/234619

ICカードリーダライタSCR3310-NTTComをxubuntuにインストール

2026 Sep. 15.
2026 Mar. 01.
2024 Jan. 03.
2023 Dec. 31.

カードリーダー用ソフトをインストール

参考ページ 録画サーバーOS更新

$ sudo apt update && sudo apt install pcscd pcsc-tools libpcsclite-dev
$ systemctl status pcscd.socket

(期待される出力例)
● pcscd.socket - PC/SC Smart Card Daemon Activation Socket
     Loaded: loaded (/lib/systemd/system/pcscd.socket; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2023-10-29 10:49:13 JST; 18s ago
   Triggers: ● pcscd.service
     Listen: /run/pcscd/pcscd.comm (Stream)
     CGroup: /system.slice/pcscd.socket

 (pcscdが動作していなかったら)
  $ sudo systemctl restart pcscd.socket

カードリーダーにカードの裏面を上にしてB-CASカードを挿入し、動作確認する。
$ pcsc_scan
 次の出力があれば成功
  Japanese Chijou Digital B-CAS Card (pay TV)

pcsc_scanコマンド実行に失敗し、次のエラーとなったら

$ pcsc_scan
PC/SC device scanner
V 1.7.3 (c) 2001-2024, Ludovic Rousseau <ludovic.rousseau@free.fr>
SCardEstablishContext: Access denied.

/etc/default/pcscd ファイルに次の1行を記述し、pcscd.serviceを再起動する。
PCSCD_ARGS="--disable-polkit"

サービス再起動と確認

$ sudo systemctl restart pcscd.service
$ systemctl status pcscd.service
$ pcsc_scan