rokkonet

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

"Docker版Mirakurun + 非Docker版MySQL + 非Docker版EPGStation"をPLEX PX-W3U4チューナーのXubuntu 22.04にインストール

2024 Feb. 25.
2024 Jan. 06.
2023 Dec. 31.

非Docker版MySQLとすることで、MySQLを一般ユーザーが利用しやすいメリットあり。

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

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

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

/etc/systemd/timesyncd.conf の "NTP="行と"FallbackNTP="行を設定する。

(例)

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

(オプション)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=256M
USE_RSYNC=true

システムを再起動する。

$ sudo reboot

稼働確認

$ df -h | grep "log2ram"
log2ram         256M   63M  194M  25% /var/log

次の状況となったら、/etc/log2ram.conf のSIZE値を大きくする。

$ journalctl -xeu log2ram.service
log2ram.service: Failed with result 'exit-code'.
ERROR: RAM disk for "/var/hdd.log/" too small. Can't sync.

PLEXチューナードライバをインストールする

次のサイトにしたがってインストールする。
PLEX PX-W3U4 TVチューナー ドライバをxubuntu 22.04にインストール - rokkonet

B-CASカードリーダーをインストールする

次のサイトにしたがってインストールする。
ICカードリーダライタSCR3310-NTTComをxubuntuにインストール - rokkonet

Docker (version 18.06.0 and higher), docker-compose (version 1.22.0 and higher)をインストールする

次のサイトにしたがってインストールする。
Docker、docker-composeをubuntuにインストール - rokkonet

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

tvユーザーをdockerグループ、sudoグループ、videoグループに所属させ、ログインしなおす

・所属グループ追加

$ sudo usermod -aG docker,sudo,video tv

・いったんログアウトし、tvユーザーで再度ログインする

dockerディレクトリを作成する
$ su - tv
$ mkdir ~/docker

Docker版Mirakurunをインストールする

Mirakurunと共有するホスト側ディレクトリを作成する
$ sudo mkdir /opt/mirakurun
$ cd /opt/mirakurun
$ sudo mkdir run opt config data
$ sudo chmod 755 /opt/mirakurun /opt/mirakurun/*
Docker版Mirakurunをダウンロードする

PLEXチューナー・recpt1コマンドを利用できるgithub.com/Chinachu/Mirakurunを利用する。

参考
【家】録画鯖のリプレース(その他 その他)by @由貴 - みんカラ
PLEX PX-Q3PE4 で docker-mirakurun-epgstation を使いたい #Docker - Qiita
【完全解説】LinuxとPX-W3U4でEPGStation v2を構築する方法(PX-Q3U4/W3PE4/Q3PE4) | ページ 4 | 小茂根の暮らし

https://github.com/Chinachu/Mirakurun にアクセスし、Codeタブ画面でブランチ・ドロップダウンをクリックしTagsタブをクリックし、最新バージョンもしくは最新rc版を確認する。現時点では 3.9.0-rc.4 もしくは 3.8.0。
git clone コマンドの"-b"オプションに最新タグを指定する。

$ su - tv
$ cd ~/docker
$ git clone https://github.com/Chinachu/Mirakurun -b 3.9.0-rc.4
MirakurunのDockerfileを作成する

参考
【家】録画鯖のリプレース(その他 その他)by @由貴 - みんカラ
PLEX PX-Q3PE4 で docker-mirakurun-epgstation を使いたい #Docker - Qiita
【完全解説】LinuxとPX-W3U4でEPGStation v2を構築する方法(PX-Q3U4/W3PE4/Q3PE4) | ページ 4 | 小茂根の暮らし

$ su - tv
$ mv ~/docker/Mirakurun/docker/Dockerfile ~/docker/Mirakurun/docker/Dockerfile.original

~/docker/Mirakurun/docker/Dockerfile ファイルを次の内容にする。

(参考)マルチステートビルドDockerfile
Dockerのマルチステージビルドでimageを軽量化する

下記のDockerfileは recpt1とarib25をインストールするようになっている。

~/docker/Mirakurun/docker/Dockerfile

ARG ARCH=
FROM ${ARCH}node:18.15.0-buster AS build
WORKDIR /app
ENV DOCKER=YES NODE_ENV=production
ADD . .
RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y --no-install-recommends build-essential && \
    npm install --production=false && \
    npm run build && \
    npm install -g --unsafe-perm --production

FROM ${ARCH}node:18.15.0-buster-slim
WORKDIR /app
ENV DEV="build-essential libtool autoconf git automake"
RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y --no-install-recommends \
        ca-certificates \
        make \
        gcc \
        g++ \
        pkg-config \
        pcscd \
        libpcsclite-dev \
        libccid \
        pcsc-tools \
        $DEV && \
\
# arib25
\
apt install -y --no-install-recommends \
    autoconf \
    build-essential \
    cmake \
    curl \
    git \
    libssl-dev \
    libtool \
    libboost-all-dev \
    pkg-config \
    yasm && \
git clone https://github.com/stz2012/libarib25.git && \
cd libarib25 && \
cmake . && \
make && \
make install && \
ldconfig && \
\
# recpt1
\
git clone https://github.com/stz2012/recpt1 /tmp/recpt1 && \
cd /tmp/recpt1/recpt1 && \
./autogen.sh && \
./configure --enable-b25 && \
make && \
make install && \
rm -rf /tmp/recpt1 && \
\
apt-get -y remove $DEV && \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

COPY --from=build /usr/local/lib/node_modules/mirakurun /app
CMD ["./docker/container-init.sh"]
EXPOSE 40772 9229

docker-compose.ymlを作成する

参考
【家】録画鯖のリプレース(その他 その他)by @由貴 - みんカラ
PLEX PX-Q3PE4 で docker-mirakurun-epgstation を使いたい #Docker - Qiita

$ su - tv
$ cd ~/docker/Mirakurun/docker
$ mv docker-compose.yml docker-compose.yml.original

次の内容で ~/docker/Mirakurun/docker/docker-compose.yml を作成する。
imageには、先に"git clone https://github.com/Chinachu/Mirakurun -b 3.9.0-rc.4"コマンドの -bオプションで指定した"3.9.0-rc.4"に合ったものを https://hub.docker.com/r/chinachu/mirakurun/tags の中で見つけて記述する。

~/docker/Mirakurun/docker/docker-compose.yml

services:
    mirakurun:
        container_name: mirakurun-3.9.0-rc.4
        build:
            # context: Mirakurun
            context: ../
            dockerfile: docker/Dockerfile
        # image: chinachu/mirakurun:3.9.0-rc.4
        image: chinachu/mirakurun:amd64-3.9.0-rc.4
        cap_add:
            - SYS_ADMIN
            - SYS_NICE
        ports:
            - "40772:40772"
            - "9229:9229"
        environment:
            TZ: "Asia/Tokyo"
            # DOCKER_NETWORK: host
        # network_mode: host
        devices:
            - /dev/px4video0:/dev/px4video0
            - /dev/px4video1:/dev/px4video1
            - /dev/px4video2:/dev/px4video2
            - /dev/px4video3:/dev/px4video3
            - /dev/bus:/dev/bus
        volumes:
          # ** CAUTION **
          # (default) `/usr/local/mirakurun/*` has been moved to `/opt/mirakurun/*`
          # try:
          #   sudo mv -vf /usr/local/mirakurun /opt/mirakurun
          #   sudo mkdir -p /opt/mirakurun/run /opt/mirakurun/opt /opt/mirakurun/config /opt/mirakurun/data
          # you can change to any location if required.
          - type: bind
            source: /opt/mirakurun/run/
            target: /var/run/
          - type: bind
            source: /opt/mirakurun/opt/
            target: /opt/
          - type: bind
            source: /opt/mirakurun/config/
            target: /app-config/
          - type: bind
            source: /opt/mirakurun/data/
            target: /app-data/
        restart: always
        logging:
            driver: json-file
            options:
                max-file: "1"
                max-size: 10m

tuners.yml, channels.ymlを作成する

$ su - tv
$ cd ~/docker/Mirakurun/config
$ mv tuners.yml tuners.yml.original
$ mv channels.yml channels.yml.original
チューナー情報ファイル作成

次の内容で ~/docker/Mirakurun/config/tuners.yml を作成する。
参考 【完全解説】LinuxとPX-W3U4でEPGStation v2を構築する方法(PX-Q3U4/W3PE4/Q3PE4) | ページ 5 | 小茂根の暮らし

~/docker/Mirakurun/config/tuners.yml

- name: PX-W3U4_S1
  types:
    - BS
    - CS
  command: recpt1 --device /dev/px4video0 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false
 
- name: PX-W3U4_S2
  types:
    - BS
    - CS
  command: recpt1 --device /dev/px4video1 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false
 
- name: PX-W3U4_T1
  types:
    - GR
  command: recpt1 --device /dev/px4video2 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false
 
- name: PX-W3U4_T2
  types:
    - GR
  command: recpt1 --device /dev/px4video3 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false
チャンネル情報ファイル作成

参考 mirakurun BSCSチャンネル設定(2023/04/19現在) · GitHub

次の内容で ~/docker/Mirakurun/config/channels.yml を作成する。

神戸チャンネル フォーマット
(見ないチャンネルには "isDisabled: true"行を書き込む)

~/docker/Mirakurun/config/channels.yml

- name: NHK総合
  type: GR
  channel: '22'
  isDisabled: false
- name: NHKEテレ
  type: GR
  channel: '13'
  isDisabled: false
- name: サンテレビ
  type: GR
  channel: '26'
  isDisabled: false
- name: MBS毎日放送
  type: GR
  channel: '16'
  isDisabled: false
- name: ABCテレビ
  type: GR
  channel: '15'
  isDisabled: false
- name: テレビ大阪
  type: GR
  channel: '18'
  isDisabled: false
- name: 関西テレビ
  type: GR
  channel: '17'
  isDisabled: false
- name: 読売テレビ
  type: GR
  channel: '14'
  isDisabled: false
- name: J:COMテレビ
  type: GR
  channel: '19'
  isDisabled: false
- name: J:COMチャンネル
  type: GR
  channel: '24'
  isDisabled: false
- name: NHK BS1
  type: BS
  channel: BS15_0
  serviceId: 101
- name: NHK BSプレミアム
  type: BS
  channel: BS03_1
  serviceId: 103
- name: BS日テレ
  type: BS
  channel: BS13_0
  serviceId: 141
- name: BS朝日
  type: BS
  channel: BS01_0
  serviceId: 151
- name: BS-TBS
  type: BS
  channel: BS01_1
  serviceId: 161
- name: BSテレ東
  type: BS
  channel: BS01_2
  serviceId: 171
- name: BSフジ
  type: BS
  channel: BS13_1
  serviceId: 181
- name: WOWOWプライム
  type: BS
  channel: BS03_0
  serviceId: 191
  isDisabled: true
- name: WOWOWライブ
  type: BS
  channel: BS05_0
  serviceId: 192
  isDisabled: true
- name: WOWOWシネマ
  type: BS
  channel: BS05_1
  serviceId: 193
  isDisabled: true
- name: スター・チャンネル1
  type: BS
  channel: BS09_1
  serviceId: 200
  isDisabled: true
- name: スター・チャンネル2
  type: BS
  channel: BS15_1
  serviceId: 201
  isDisabled: true
- name: スター・チャンネル3
  type: BS
  channel: BS15_1
  serviceId: 202
  isDisabled: true
- name: BS11
  type: BS
  channel: BS09_0
  serviceId: 211
- name: TwellV
  type: BS
  channel: BS09_2
  serviceId: 222
- name: 放送大学 BS1
  type: BS
  channel: BS11_0
  serviceId: 231
- name: 放送大学 BS2
  type: BS
  channel: BS11_0
  serviceId: 232
- name: グリーンチャンネル
  type: BS
  channel: BS21_2
  serviceId: 234
  isDisabled: true
- name: BSアニマックス
  type: BS
  channel: BS13_2
  serviceId: 236
  isDisabled: true
- name: J SPORTS 1
  type: BS
  channel: BS19_1
  serviceId: 242
  isDisabled: true
- name: J SPORTS 2
  type: BS
  channel: BS19_2
  serviceId: 243
  isDisabled: true
- name: J SPORTS 3
  type: BS
  channel: BS19_3
  serviceId: 244
  isDisabled: true
- name: J SPORTS 4
  type: BS
  channel: BS19_0
  serviceId: 245
  isDisabled: true
- name: BS釣りビジョン
  type: BS
  channel: BS11_1
  serviceId: 251
  isDisabled: true
- name: WOWOWプラス
  type: BS
  channel: BS21_0
  serviceId: 252
  isDisabled: true
- name: 日本映画専門チャンネル
  type: BS
  channel: BS21_1
  serviceId: 255
  isDisabled: true
- name: ディズニー・チャンネル
  type: BS
  channel: BS23_0
  serviceId: 256
  isDisabled: true
- name: BS松竹東急
  type: BS
  channel: BS23_3
  serviceId: 260
- name: BSJapanext
  type: BS
  channel: BS23_2
  serviceId: 263
- name: BSよしもと
  type: BS
  channel: BS23_1
  serviceId: 265

dockerデーモンの稼働を確認する

$ systemctl status docker.service

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

pcscdを停止させる

Mirakurunではpcscdは稼働させない

$ sudo systemctl stop pcscd.socket
$ sudo systemctl disable pcscd.socket

(確認)
$ systemctl status pcscd.socket

Mirakurunコンテナを作成する

$ su - tv
$ cd ~/docker/Mirakurun/docker
$ docker-compose -f ./docker-compose.yml pull
$ docker-compose -f ./docker-compose.yml build --no-cache
かなり時間がかかる

初回セットアップを行う

$ su - tv
$ cd ~/docker/Mirakurun/docker
$ docker-compose -f ./docker-compose.yml run --rm -e SETUP=true mirakurun

チューナー情報ファイルを確認する

~/docker/Mirakurun/config/tuners.yml が先に作成した内容であることを確認する。

チャンネル設定ファイルを確認する


~/docker/Mirakurun/config/channels.yml が先に作成した内容であることを確認する。

Mirakurunコンテナを起動する

$ su - tv
$ cd ~/docker/Mirakurun/docker
$ docker-compose -f ./docker-compose.yml up -d

Mirakurunの稼働を確認する

$ curl -o - http://<MirakurunURL>:<Port>/api/version
  通常は curl -o - http://localhost:40772/api/version
 => {"current":"xxxx","latest":"xxxx"}

あるいは、ブラウザから http://MY/MIRAKURUN/IP/ADDRESS:40772 にアクセスする。

PC起動時のMirakurun起動について

docker-compose.ymlに"restart: always"を記述しているので、PC起動時にMirakurunが自動起動する。

(参考)Mirakurunでの手動録画

$ su - tv

(コンテナID確認)
$ docker ps

(シェルモードでMirakurunコンテナに入る)
$ docker exec -it MIRAKURUNcontainerID /bin/bash

(ホスト(Xubuntu)と共有のテンポラリディレクトリ作成)
# mkdir /app-data/tmp

(地デジ22チャンネルを15秒間録画しホストの/opt/mirakurun/data/に保存)
# recpt1 --b25 --strip --sid hd 22 15 /app-data/tmp/tmp22ch.ts

(NHK BS1を15秒間録画しホストの/opt/mirakurun/data/に保存)
# recpt1 --b25 --strip --sid hd BS15_0 15 /app-data/tmp/tmpBS1ch.ts

(Mirakurunコンテナから出る)
# exit

(録画ファイル確認)
$ ls /opt/mirakurun/data/tmp/*.ts
/opt/mirakurun/data/tmp/tmp22ch.ts  /opt/mirakurun/data/tmp/tmpBS1ch.ts
ポート開放

40772番ポートをLAN内に開放する

(例)

アドレス確認
$ ip a | grep '192.168.'
    inet 192.168.6.3/24 brd 192.168.6.255 scope global noprefixroute ens33

$ sudo ufw enable
$ sudo ufw allow proto tcp from 192.168.6.0/24 to any port 40772
$ sudo ufw allow proto udp from 192.168.6.0/24 to any port 40772

openVPNでLAN外からのアクセスを許可する場合
$ sudo ufw allow proto tcp from 10.8.0.0/24 to any port 40772
$ sudo ufw allow proto udp from 10.8.0.0/24 to any port 40772

設定読み込み
$ sudo ufw reload

(参考)Mirakurunの停止

稼働しているコンテナを停止する。

$ su - tv
$ cd ~/docker/Mirakurun/docker
$ docker-compose -f ./docker-compose.yml down


MySQLをインストールする

mariaDBをインストールする

次のサイトにしたがってインストールする。
MariaDBをubuntuにインストール - rokkonet

mariaDBをEPGStation向けに設定する

出典 https://github.com/y-hashida/Memo_Mirakurun-and-EPGStation

データベースとユーザーを作成し、大量発生するバイナリログの保管期間を1日にする

$ sudo mysql
> CREATE DATABASE epgstation CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
> GRANT ALL ON epgstation.* TO epgstation@localhost IDENTIFIED BY 'epgstation';
> GRANT ALL ON epgstation.* TO epgstation@127.0.0.1 IDENTIFIED BY 'epgstation';
> GRANT ALL ON epgstation.* TO epgstation@MY.EPGSTATION.IP.ADDRESS IDENTIFIED BY 'epgstation';
> SET GLOBAL expire_logs_days = 1;
> exit

EPGStationをインストールする

gccのインストール
$ gcc --version

(インストールされていなければ)
$ sudo apt update && sudo apt install build-essential
Node.jsのインストール
$ node --version

インストールされていなければ次のサイトにしたがってインストールする。
  github.com/l3tnun/EPGStationでの動作環境記載
   Node.js : ^14.6.0 || ^16.6.1 || ^18.16.1

  バージョン20でEPGStationが稼働した。

 インストールガイド -> 最新のNode.jsのubuntuへのインストール - rokkonet

FFMPEGのインストール
$ ffmpeg -version

(インストールされていなければ)
$ sudo apt update && sudo apt install ffmpeg
pythonのインストール
$ python --version

インストールされていなければ、EPGStationを稼働させるユーザー(tvとした)でまずpyenvをインストールし、その後、pythonをインストールする。
出典 ubuntu 20.04 / 18.04 に pyenv をインストール

$ su - tv
$ sudo apt update && sudo apt install \
  build-essential libssl-dev zlib1g-dev \
  libbz2-dev libreadline-dev libsqlite3-dev curl llvm \
  libncursesw5-dev tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev \
  libopencv-dev git

$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv

~/.profile に下記を記述する。

export PYENV_ROOT="${HOME}/.pyenv"
export PATH="${PYENV_ROOT}/bin:${PATH}"
eval "$(pyenv init --path)"

~/.profile を読み込む。

$ source ~/.profile

pyenvがインストールされていることを確認する

$ pyenv -v

インストールできるpythonの一覧を出力する

$ pyenv install --list

適当なバージョンのpythonをインストールする

$ pyenv install 3.9.4

(確認)
$ pyenv versions

グローバル空間での利用に設定する
(インストールしたユーザー空間でのグローバル。他ユーザーからは利用できない)

$ pyenv global 3.9.4

(確認)
$ python -V
Mirakurunの稼働確認
$ curl -o - http://<MirakurunURL>:<Port>/api/version
  通常は curl -o - http://localhost:40772/api/version
 => {"current":"xxxx","latest":"xxxx"}

稼働していなければ
$ cd ~/docker/Mirakurun/docker
$ docker-compose -f ./docker-compose.yml up -d
MySQLの稼働確認
$ systemctl status mysql.service

稼働していなければ
$ sudo systemctl start mysql.service
録画ファイル保管場所と録画サムネイル画像ファイル保管場所を作成する

EPGStationをWindows上のVMware Workstation PlayerのXubuntuにインストールし、録画ファイル保管場所をWindows上のストレージとする時は、Windows上の録画ファイル保管ストレージをVMwareとの共有フォルダとする。

$ sudo mkdir /MY/RECORDED/FILE/DIR
$ sudo mkdir /MY/RECORDED/FILE/DIR/thums
$ sudo chmod 777 /MY/RECORDED/FILE/DIR /MY/RECORDED/FILE/DIR/thums
EPGStationをインストールする

出典 EPGStation/doc/linux-setup.md at master · l3tnun/EPGStation · GitHub

$ su - tv
$ cd ~/
$ git clone https://github.com/l3tnun/EPGStation.git
$ cd EPGStation
$ npm run all-install
$ npm run build

$ cd config
$ cp config.yml.template config.yml
$ cp operatorLogConfig.sample.yml operatorLogConfig.yml
$ cp epgUpdaterLogConfig.sample.yml epgUpdaterLogConfig.yml
$ cp serviceLogConfig.sample.yml serviceLogConfig.yml
$ cp enc.js.template enc.js
~/EPGStation/config/config.ymlを修正する

(修正概要)
unix socket接続できなかったので、
 unix socket接続設定を削除
 40772ポートIP接続を設定
mysql利用に変更
録画ファイル名規則を変更
録画ファイル保存ディレクトリを変更
録画サムネイル画像保存ディレクトリを変更
マニュアル EPGStation/doc/conf-manual.md at master · l3tnun/EPGStation · GitHub

~/EPGStation/config/config.yml

# mirakurunPath: http+unix://%2Fvar%2Frun%2Fmirakurun.sock/
mirakurunPath: http://localhost:40772

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

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

recordedFormat: '%SHORTYEAR%%MONTH%%DAY%%HOUR%%MIN%%TYPE%%CH%-%SID%-%CHNAME%-%TITLE%'

recorded:
    - name: recorded    ## ブラウザ上に表示される保存場所名
      path: '/MY/RECORDED/FILE/DIR'    ## 保存ディレクトリのフルパス
thumbnail: '/MY/RECORDED/FILE/DIR/thums'

#ffmpeg: /usr/local/bin/ffmpeg
ffmpeg: /usr/bin/ffmpeg
#ffprobe: /usr/local/bin/ffprobe
ffprobe: /usr/bin/ffprobe
H.264エンコード設定増設
音声欠落を防ぐため、冒頭4秒間をカットして H.264エンコードする設定を追加する

参考 副音声付きTV録画tsファイルのmp4へのトランスコード - rokkonet

(1) エンコードスクリプトを作成する
次の内容で ${HOME}/EPGStation/config/enc-cut4secs.js をファイル属性644で作成する。

const spawn = require('child_process').spawn;
const ffmpeg = process.env.FFMPEG;

const input = process.env.INPUT;
const output = process.env.OUTPUT;
const analyzedurationSize = '10M'; // Mirakurun の設定に応じて変更すること
const probesizeSize = '32M'; // Mirakurun の設定に応じて変更すること
const maxMuxingQueueSize = 1024;
const dualMonoMode = 'main';
const videoHeight = parseInt(process.env.VIDEORESOLUTION, 10);
const isDualMono = parseInt(process.env.AUDIOCOMPONENTTYPE, 10) == 2;
const audioBitrate = videoHeight > 720 ? '192k' : '128k';
const preset = 'veryfast';
const codec = 'libx264';
const crf = 23;

const args = ['-ss', '4', '-y', '-analyzeduration', analyzedurationSize, '-probesize', probesizeSize];

// dual mono 設定
if (isDualMono) {
    Array.prototype.push.apply(args, ['-dual_mono_mode', dualMonoMode]);
}

// input 設定
Array.prototype.push.apply(args,['-i', input]);

// メタ情報を先頭に置く
Array.prototype.push.apply(args,['-movflags', 'faststart']);

// 字幕データを含めたストリームをすべてマップ
// Array.prototype.push.apply(args, ['-map', '0', '-ignore_unknown', '-max_muxing_queue_size', maxMuxingQueueSize, '-sn']);

// video filter 設定
let videoFilter = 'yadif';
if (videoHeight > 720) {
    videoFilter += ',scale=-2:720'
}
Array.prototype.push.apply(args, ['-vf', videoFilter]);

// その他設定
Array.prototype.push.apply(args,[
    '-preset', preset,
    '-aspect', '16:9',
    '-c:v', codec,
    '-crf', crf,
    '-f', 'mp4',
    '-c:a', 'aac',
    '-ar', '48000',
    '-ab', audioBitrate,
    '-ac', '2',
    output
]);

let str = '';
for (let i of args) {
    str += ` ${ i }`
}
console.error(str);

const child = spawn(ffmpeg, args);

child.stderr.on('data', (data) => { console.error(String(data)); });

child.on('error', (err) => {
    console.error(err);
    throw new Error(err);
});

process.on('SIGINT', () => {
    child.kill('SIGINT');
});

(2) EPGStationに enc-cut4secs.js によるエンコード設定を組み込む
${HOME}/EPGStation/config/config.yml のencodeブロックに設定を記述する。
"- name:"で始め、変換コマンドjsファイル・拡張子・録画後タイムアウト時間(秒)を指定する。

設定例

encode:
   #### デフォルトの設定 ####
    - name: H.264
      cmd: '%NODE% %ROOT%/config/enc.js'
      suffix: .mp4
      rate: 4.0

   #### enc-cut4secs.js によるエンコード設定 ####
    - name: H.264-cut4secs
      cmd: '%NODE% %ROOT%/config/enc-cut4secs.js'
      suffix: .mp4
      rate: 4.0


(参考)デフォルトの ${HOME}/EPGStation/config/enc.js'

const spawn = require('child_process').spawn;
const ffmpeg = process.env.FFMPEG;

const input = process.env.INPUT;
const output = process.env.OUTPUT;
const analyzedurationSize = '10M'; // Mirakurun の設定に応じて変更すること
const probesizeSize = '32M'; // Mirakurun の設定に応じて変更すること
const maxMuxingQueueSize = 1024;
const dualMonoMode = 'main';
const videoHeight = parseInt(process.env.VIDEORESOLUTION, 10);
const isDualMono = parseInt(process.env.AUDIOCOMPONENTTYPE, 10) == 2;
const audioBitrate = videoHeight > 720 ? '192k' : '128k';
const preset = 'veryfast';
const codec = 'libx264';
const crf = 23;

const args = ['-y', '-analyzeduration', analyzedurationSize, '-probesize', probesizeSize];

// dual mono 設定
if (isDualMono) {
    Array.prototype.push.apply(args, ['-dual_mono_mode', dualMonoMode]);
}

// input 設定
Array.prototype.push.apply(args,['-i', input]);

// メタ情報を先頭に置く
Array.prototype.push.apply(args,['-movflags', 'faststart']);

// 字幕データを含めたストリームをすべてマップ
// Array.prototype.push.apply(args, ['-map', '0', '-ignore_unknown', '-max_muxing_queue_size', maxMuxingQueueSize, '-sn']);

// video filter 設定
let videoFilter = 'yadif';
if (videoHeight > 720) {
    videoFilter += ',scale=-2:720'
}
Array.prototype.push.apply(args, ['-vf', videoFilter]);

// その他設定
Array.prototype.push.apply(args,[
    '-preset', preset,
    '-aspect', '16:9',
    '-c:v', codec,
    '-crf', crf,
    '-f', 'mp4',
    '-c:a', 'aac',
    '-ar', '48000',
    '-ab', audioBitrate,
    '-ac', '2',
    output
]);

let str = '';
for (let i of args) {
    str += ` ${ i }`
}
console.error(str);

const child = spawn(ffmpeg, args);

child.stderr.on('data', (data) => { console.error(String(data)); });

child.on('error', (err) => {
    console.error(err);
    throw new Error(err);
});

process.on('SIGINT', () => {
    child.kill('SIGINT');
});
ポート開放

8888番ポートをLAN内に開放する

(例)

$ sudo ufw enable
$ sudo ufw allow proto tcp from MY.NET.ADDRESS..0/24 to any port 8888
$ sudo ufw allow proto udp from MY.NET.ADDRESS..0/24 to any port 8888

openVPNでLAN外からのアクセスを許可する場合
$ sudo ufw allow proto tcp from 10.8.0.0/24 to any port 8888
$ sudo ufw allow proto udp from 10.8.0.0/24 to any port 8888

設定読み込み
$ sudo ufw reload
初回起動
$ cd ~/EPGStation
$ sudo npm install pm2 -g
$ sudo pm2 startup
(次の出力が出る)
##### ここから #####
[PM2] Freeze a process list on reboot via:
$ pm2 save

[PM2] Remove init script via:
$ pm2 unstartup systemd
##### ここまで #####

$ pm2 start dist/index.js --name "epgstation"
$ pm2 save

上記コマンド実行時の画面出力内容
+++ ここから +++

$ sudo npm install pm2 -g
/usr/local/bin/pm2 -> /usr/local/lib/node_modules/pm2/bin/pm2
/usr/local/bin/pm2-dev -> /usr/local/lib/node_modules/pm2/bin/pm2-dev
/usr/local/bin/pm2-docker -> /usr/local/lib/node_modules/pm2/bin/pm2-docker
/usr/local/bin/pm2-runtime -> /usr/local/lib/node_modules/pm2/bin/pm2-runtime
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.3.1 (node_modules/pm2/node_modules/chokidar/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

+ pm2@4.5.6
added 175 packages from 194 contributors in 15.996s


$ sudo pm2 startup

                          Runtime Edition

        PM2 is a Production Process Manager for Node.js applications
                     with a built-in Load Balancer.

                Start and Daemonize any application:
                $ pm2 start app.js

                Load Balance 4 instances of api.js:
                $ pm2 start api.js -i 4

                Monitor in production:
                $ pm2 monitor

                Make pm2 auto-boot at server restart:
                $ pm2 startup

                To go further checkout:
                http://pm2.io/


                        -------------

[PM2] Init System found: systemd
-----------------------------------------------------------
 PM2 detected systemd but you precised ubuntu
 Please verify that your choice is indeed your init system
 If you arent sure, just run : pm2 startup
-----------------------------------------------------------
Platform ubuntu
Template
[Unit]
Description=PM2 process manager
Documentation=https://pm2.keymetrics.io/
After=network.target

[Service]
Type=forking
User=root
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Environment=PM2_HOME=/root/.pm2
PIDFile=/root/.pm2/pm2.pid
Restart=on-failure

ExecStart=/usr/local/lib/node_modules/pm2/bin/pm2 resurrect
ExecReload=/usr/local/lib/node_modules/pm2/bin/pm2 reload all
ExecStop=/usr/local/lib/node_modules/pm2/bin/pm2 kill

[Install]
WantedBy=multi-user.target

Target path
/etc/systemd/system/pm2-root.service
Command list
[ 'systemctl enable pm2-root' ]
[PM2] Writing init configuration in /etc/systemd/system/pm2-root.service
[PM2] Making script booting at startup...
[PM2] [-] Executing: systemctl enable pm2-root...
Created symlink /etc/systemd/system/multi-user.target.wants/pm2-root.service → /etc/systemd/system/pm2-root.service.
[PM2] [v] Command successfully executed.
+---------------------------------------+
[PM2] Freeze a process list on reboot via:
$ pm2 save

[PM2] Remove init script via:
$ pm2 unstartup systemd


$ pm2 start dist/index.js --name "epgstation"

                          Runtime Edition

        PM2 is a Production Process Manager for Node.js applications
                     with a built-in Load Balancer.

                Start and Daemonize any application:
                $ pm2 start app.js

                Load Balance 4 instances of api.js:
                $ pm2 start api.js -i 4

                Monitor in production:
                $ pm2 monitor

                Make pm2 auto-boot at server restart:
                $ pm2 startup

                To go further checkout:
                http://pm2.io/


                        -------------

[PM2] Spawning PM2 daemon with pm2_home=/HOME/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /HOME/EPGStation/dist/index.js in fork_mode (1 instance)
[PM2] Done.
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ epgstation         │ fork     │ 0    │ online    │ 0%       │ 21.8mb   │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘


$  pm2 save
[PM2] Saving current process list...
[PM2] Successfully saved in /HOME/.pm2/dump.pm2

+++ ここまで +++


EPGStationの稼働を http://EPGSTATION.HOST.IP.ADDRESS:8888 を開いて確認する。
あるいは、"curl -o - http://EPGSTATION.HOST.IP.ADDRESS:8888"コマンドで出力があることを確認する。
異常があれば、 ${HOME}/.pm2/logs/epgstation-error.log ファイルを確認する。

システム起動時に、Mirakurun、MySQLの起動後にEPGStationが自動起動するように設定する

/etc/systemd/system/epgstation.serviceを作成する。

/etc/systemd/system/epgstation.service

[Unit]
Description=EPGStation
Requires=docker.service
After=docker.service mysql.service
Wants=mysql.service

[Service]
Type=simple
User=tv
Group=tv
WorkingDirectory=/home/tv/EPGStation
ExecStart=npm start

[Install]
WantedBy=multi-user.target


systemdにepgstation.serviceをロードする。

$ sudo systemctl daemon-reload


EPGStation自動起動を設定する。

$ sudo systemctl enable epgstation.service


pm2で起動し稼働しているEPGStationを終了する

$ su - tv
$ pm2 stop epgstation


"pm2 save"コマンドでセットされたEPGStation自動起動を解除する

$ su - tv
$ pm2 unstartup


システムを再起動してEPGStationの自動起動を確認する

$ sudo reboot


Mirakurun、MySQLの起動に無関係にEPGStationを起動する方法(非推奨)
出典 https://qiita.com/ikemura23/items/68fb61b16c6752daa7e8

$ su - tv
$ cd ~/EPGStation
$ pm2 stop epgstation
$ pm2 start dist/index.js --name "epgstation" --update-env
$ pm2 startup
    ## 画面出力された指示にしたがって、画面のコマンドをコピーする

上記でコピーしたコマンドを実行する
$ sudo env PATH=$PATH:/usr/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup systemd -u tv --hp ${HOME}

"pm2 save"コマンドを実行すると、次回システム起動時にEPGStationが自動起動する
$ pm2 save

システムを再起動してEPGStationの自動起動を確認
$ sudo reboot 


稼働確認

ブラウザで http://EPGSTATION.HOST.IP.ADDRESS:8888 を開いて確認する。
あるいは、"curl -o - http://EPGSTATION.HOST.IP.ADDRESS:8888"コマンドで出力があることを確認する。

・pm2コマンド起動している場合のpm2コマンドでの確認

$ su - tv
$ pm2 status

mysqlのデータベーステーブルを確認

$ mysql -u epgstation -p epgstation
> show tables;
> select * from channel;
> select * from program;
> exit
(参考)ステータス表示
$ su - tv
$ pm2 show epgstation
(参考)systemdでMirakurun、MySQLの起動後に自動起動したEPGStation の終了
$ sudo systemctl stop epgstation.service
(参考)pm2コマンドで起動した EPGStation の終了
$ su - tv
$ pm2 stop epgstation
(参考)"pm2 save"コマンドで設定したEPGStation自動起動の解除
$ su - tv
$ pm2 unstartup
(参考)npmコマンドによるEPGStation起動(次回システム起動時に自動起動はしない)
$ su - tv
$ npm start
(参考)npmコマンドで起動したEPGStationの終了
$ su - tv
$ npm stop


ログ保管場所

${HOME}/EPGStation/logs/
${HOME}/.pm2/logs/epgstation-out.log
${HOME}/.pm2/logs/epgstation-error.log

ログファイルについて
GitHub - l3tnun/EPGStation: Mirakurun を使用した録画管理ソフト

config.yml詳細マニュアル

EPGStation/doc/conf-manual.md at master · l3tnun/EPGStation · GitHub

(参考)EPGStationのアンインストール

出典 EPGStationのインストール - にゃののん日記

$ su - tv
$ pm2 delete epgstation    ## PM2に登録されているEPGStationを削除
$ pm2 save    ## PM2の現在の状態を保存
$ pm2 kill    ## PM2を停止
$ sudo systemctl stop pm2-xxxxxxxx    ## 現在のユーザ(xxxxxxxxをユーザ名に置き換える)でのPM2の自動起動を停止
$ sudo systemctl disable pm2-xxxxxxxx  ## 現在のユーザでのPM2の無効化
$ sudo rm -f /etc/systemd/system/pm2-xxxxxxxx.service    ## 設定を削除
$ rm -rf ~/.pm2     ## PM2の設定保存ディレクトリを削除


(参考)mysqlのテーブル構造
> show tables;
+----------------------------+
| Tables_in_epgstation       |
+----------------------------+
| channel                    |
| drop_log_file              |
| migrations                 |
| program                    |
| recorded                   |
| recorded_history           |
| recorded_tag               |
| recorded_tags_recorded_tag |
| reserve                    |
| rule                       |
| thumbnail                  |
| video_file                 |
+----------------------------+
> show columns from program;
+----------------------+--------------+------+-----+---------+-------+
| Field                | Type         | Null | Key | Default | Extra |
+----------------------+--------------+------+-----+---------+-------+
| id                   | bigint(20)   | NO   | PRI | NULL    |       |
| updateTime           | bigint(20)   | NO   |     | NULL    |       |
| channelId            | bigint(20)   | NO   |     | NULL    |       |
| eventId              | bigint(20)   | NO   |     | NULL    |       |
| serviceId            | int(11)      | NO   |     | NULL    |       |
| networkId            | int(11)      | NO   |     | NULL    |       |
| startAt              | bigint(20)   | NO   |     | NULL    |       |
| endAt                | bigint(20)   | NO   |     | NULL    |       |
| startHour            | int(11)      | NO   |     | NULL    |       |
| week                 | int(11)      | NO   |     | NULL    |       |
| duration             | int(11)      | NO   |     | NULL    |       |
| isFree               | tinyint(4)   | NO   |     | NULL    |       |
| name                 | text         | NO   |     | NULL    |       |
| halfWidthName        | text         | NO   |     | NULL    |       |
| shortName            | text         | NO   |     | NULL    |       |
| description          | text         | YES  |     | NULL    |       |
| halfWidthDescription | text         | YES  |     | NULL    |       |
| extended             | text         | YES  |     | NULL    |       |
| halfWidthExtended    | text         | YES  |     | NULL    |       |
| genre1               | int(11)      | YES  |     | NULL    |       |
| subGenre1            | int(11)      | YES  |     | NULL    |       |
| genre2               | int(11)      | YES  |     | NULL    |       |
| subGenre2            | int(11)      | YES  |     | NULL    |       |
| genre3               | int(11)      | YES  |     | NULL    |       |
| subGenre3            | int(11)      | YES  |     | NULL    |       |
| channelType          | varchar(255) | NO   |     | NULL    |       |
| channel              | varchar(255) | NO   |     | NULL    |       |
| videoType            | text         | YES  |     | NULL    |       |
| videoResolution      | text         | YES  |     | NULL    |       |
| videoStreamContent   | int(11)      | YES  |     | NULL    |       |
| videoComponentType   | int(11)      | YES  |     | NULL    |       |
| audioSamplingRate    | int(11)      | YES  |     | NULL    |       |
| audioComponentType   | int(11)      | YES  |     | NULL    |       |
+----------------------+--------------+------+-----+---------+-------+
> show columns from recorded;
+----------------------+------------+------+-----+---------+----------------+
| Field                | Type       | Null | Key | Default | Extra          |
+----------------------+------------+------+-----+---------+----------------+
| id                   | int(11)    | NO   | PRI | NULL    | auto_increment |
| reserveId            | int(11)    | YES  |     | NULL    |                |
| ruleId               | int(11)    | YES  |     | NULL    |                |
| programId            | bigint(20) | YES  |     | NULL    |                |
| channelId            | bigint(20) | NO   |     | NULL    |                |
| isProtected          | tinyint(4) | NO   |     | 0       |                |
| startAt              | bigint(20) | NO   |     | NULL    |                |
| endAt                | bigint(20) | NO   |     | NULL    |                |
| duration             | int(11)    | NO   |     | NULL    |                |
| name                 | text       | NO   |     | NULL    |                |
| halfWidthName        | text       | NO   |     | NULL    |                |
| description          | text       | YES  |     | NULL    |                |
| halfWidthDescription | text       | YES  |     | NULL    |                |
| extended             | text       | YES  |     | NULL    |                |
| halfWidthExtended    | text       | YES  |     | NULL    |                |
| genre1               | int(11)    | YES  |     | NULL    |                |
| subGenre1            | int(11)    | YES  |     | NULL    |                |
| genre2               | int(11)    | YES  |     | NULL    |                |
| subGenre2            | int(11)    | YES  |     | NULL    |                |
| genre3               | int(11)    | YES  |     | NULL    |                |
| subGenre3            | int(11)    | YES  |     | NULL    |                |
| videoType            | text       | YES  |     | NULL    |                |
| videoResolution      | text       | YES  |     | NULL    |                |
| videoStreamContent   | int(11)    | YES  |     | NULL    |                |
| videoComponentType   | int(11)    | YES  |     | NULL    |                |
| audioSamplingRate    | int(11)    | YES  |     | NULL    |                |
| audioComponentType   | int(11)    | YES  |     | NULL    |                |
| isRecording          | tinyint(4) | NO   |     | NULL    |                |
| dropLogFileId        | int(11)    | YES  | UNI | NULL    |                |
+----------------------+------------+------+-----+---------+----------------+
id: video_fileテーブルのrecordedIdと同じ値
startAt, endAt: 単位がミリ秒のunix time値
NHK総合神戸のchannelIdは"3208043008"
> show columns from reserve;
+-----------------------------+------------+------+-----+---------+----------------+
| Field                       | Type       | Null | Key | Default | Extra          |
+-----------------------------+------------+------+-----+---------+----------------+
| id                          | int(11)    | NO   | PRI | NULL    | auto_increment |
| updateTime                  | bigint(20) | NO   |     | NULL    |                |
| ruleId                      | int(11)    | YES  |     | NULL    |                |
| ruleUpdateCnt               | int(11)    | YES  |     | NULL    |                |
| isSkip                      | tinyint(4) | NO   |     | 0       |                |
| isConflict                  | tinyint(4) | NO   |     | 0       |                |
| allowEndLack                | tinyint(4) | NO   |     | 0       |                |
| tags                        | text       | YES  |     | NULL    |                |
| isOverlap                   | tinyint(4) | NO   |     | 0       |                |
| isIgnoreOverlap             | tinyint(4) | NO   |     | 0       |                |
| isTimeSpecified             | tinyint(4) | NO   |     | 0       |                |
| parentDirectoryName         | text       | YES  |     | NULL    |                |
| directory                   | text       | YES  |     | NULL    |                |
| recordedFormat              | text       | YES  |     | NULL    |                |
| encodeMode1                 | text       | YES  |     | NULL    |                |
| encodeParentDirectoryName1  | text       | YES  |     | NULL    |                |
| encodeDirectory1            | text       | YES  |     | NULL    |                |
| encodeMode2                 | text       | YES  |     | NULL    |                |
| encodeParentDirectoryName2  | text       | YES  |     | NULL    |                |
| encodeDirectory2            | text       | YES  |     | NULL    |                |
| encodeMode3                 | text       | YES  |     | NULL    |                |
| encodeParentDirectoryName3  | text       | YES  |     | NULL    |                |
| encodeDirectory3            | text       | YES  |     | NULL    |                |
| isDeleteOriginalAfterEncode | tinyint(4) | NO   |     | 0       |                |
| programId                   | bigint(20) | YES  |     | NULL    |                |
| programUpdateTime           | bigint(20) | YES  |     | NULL    |                |
| channelId                   | bigint(20) | NO   |     | NULL    |                |
| channel                     | text       | NO   |     | NULL    |                |
| channelType                 | text       | NO   |     | NULL    |                |
| startAt                     | bigint(20) | NO   |     | NULL    |                |
| endAt                       | bigint(20) | NO   |     | NULL    |                |
| name                        | text       | YES  |     | NULL    |                |
| halfWidthName               | text       | YES  |     | NULL    |                |
| shortName                   | text       | YES  |     | NULL    |                |
| description                 | text       | YES  |     | NULL    |                |
| halfWidthDescription        | text       | YES  |     | NULL    |                |
| extended                    | text       | YES  |     | NULL    |                |
| halfWidthExtended           | text       | YES  |     | NULL    |                |
| genre1                      | int(11)    | YES  |     | NULL    |                |
| subGenre1                   | int(11)    | YES  |     | NULL    |                |
| genre2                      | int(11)    | YES  |     | NULL    |                |
| subGenre2                   | int(11)    | YES  |     | NULL    |                |
| genre3                      | int(11)    | YES  |     | NULL    |                |
| subGenre3                   | int(11)    | YES  |     | NULL    |                |
| videoType                   | text       | YES  |     | NULL    |                |
| videoResolution             | text       | YES  |     | NULL    |                |
| videoStreamContent          | int(11)    | YES  |     | NULL    |                |
| videoComponentType          | int(11)    | YES  |     | NULL    |                |
| audioSamplingRate           | int(11)    | YES  |     | NULL    |                |
| audioComponentType          | int(11)    | YES  |     | NULL    |                |
+-----------------------------+------------+------+-----+---------+----------------+
> show columns from video_file;
+---------------------+------------+------+-----+---------+----------------+
| Field               | Type       | Null | Key | Default | Extra          |
+---------------------+------------+------+-----+---------+----------------+
| id                  | int(11)    | NO   | PRI | NULL    | auto_increment |
| parentDirectoryName | text       | NO   |     | NULL    |                |
| filePath            | text       | NO   |     | NULL    |                |
| type                | text       | NO   |     | NULL    |                |
| name                | text       | NO   |     | NULL    |                |
| size                | bigint(20) | NO   |     | 0       |                |
| recordedId          | int(11)    | NO   | MUL | NULL    |                |
+---------------------+------------+------+-----+---------+----------------+

recordedId:recordedテーブルのidと同じ値
(参考)録画ファイル情報取得例

(例1)mysqlのデータから抽出

mysql > 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 ;

(例2)config.ymlからの録画ファイル保存ディレクトリ取得 & mysqlデータ抽出

# visudo
epgstationUser ALL=(root) NOPASSWD: /usr/bin/mysql

$ su - epgstationUser
$ ConfigFile="${HOME}/EPGStation/config/config.yml"
$ ParentDir=$(cat "$ConfigFile" | grep -P '^[ \t]*path')
$ ParentDir=${ParentDir#*\'}
$ ParentDir=${ParentDir%\'*}
$ ParentDir=${ParentDir%/}
$ DestDir="DIR/TO/SAVE/RECORDED-FILE-LIST"
$ DestFileName="FILE-NAME-OF-RECORDED-FILE-LIST"
$ DbName=DATABE-NAME-OF-EPGSTATION-IN-MYSQL
$ sed "s:${ParentDir}\t*:${ParentDir}/:" > "${DestDir}/${DestFileName}" <(echo "select video_file.id, '${ParentDir}' as parentDirPath, 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" | sudo /usr/bin/mysql -D ${DbName})

(例3)指定するchannelIdとstartAtに合致するレコードをrecordedテーブルとvideo_fileテーブルから削除するsql

DELETE recorded, video_file
FROM recorded
INNER JOIN video_file ON recorded.id = video_file.recordedId
WHERE recorded.channelId = 指定するchannelId
AND recorded.startAt = 指定するstartAt;
(参考)録画ファイル保存ディレクト

録画ファイル保存ディレクトリは、mysqlデータベースには"recorded"と記録されており、パスを取得できない。
録画ファイル保存ディレクトリパスは ~/EPGStation/config/config.yml内のrecorded: path: 'PARENT/DIR'を参照する。

(参考)録画ファイル情報テキストファイル保存スクリプト

スクリプト保管場所
https://bitbucket.org/arsmus/shell-script-public/src/master/audio-video/save_record-file-information-epgstation.sh

(参考)録画ファイル保存ディレクトリの変更について