0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EPGStationの状態をshでDisocrd WebHookに送る

Last updated at Posted at 2025-10-04

EPGStationの状態をDiscordのWebhookでDiscordに送るシェルスクリプトです。

必要なもの

EPGStationが導入済のLinux PC
Discordアカウント

環境

Ubuntu 24.04.2 LTS
EPGStation v2.10.0
Docker 28.4.0

docker-mirakurun-epgstationでepgstationを構築済み

DiscordのWebhookURLを作成

画像の通りに進めていってください。やり方が分かる方は読み飛ばしてください。

サーバーを作成

スクリーンショット 2025-10-05 044635.png

スクリーンショット 2025-10-05 045029.png

スクリーンショット 2025-10-05 045127.png

名前を決めます。どんな名前でも大丈夫です。
スクリーンショット 2025-10-05 045215.png

スクリーンショット 2025-10-05 045424.png

スクリーンショット 2025-10-05 045514.png

スクリーンショット 2025-10-05 045607.png

名前を決めてウェブフックURLをコピーをクリックします。
スクリーンショット 2025-10-05 045707.png

プログラムを保存

以下のシェルスクリプトをrun.shという名前でローカルに保存してください。

必ず「UnixLF」で保存してください。CRLFなどだと正しく動きません。

run.sh
#!/bin/bash
set -eu

#ここにWebHookURLを貼り付け

DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-https://discord.com/api/webhooks/XXXXXXXXXXXXXXXXX}"

########################################################################
# JSON エスケープ(簡易)
########################################################################
escape_json() {
  local s="$1"
  s="${s//\\/\\\\}"
  s="${s//\"/\\\"}"
  s="${s//$'\n'/\\n}"
  s="${s//$'\r'/\\r}"
  s="${s//$'\t'/\\t}"
  printf '%s' "$s"
}

########################################################################
# epoch -> JST 表示
########################################################################
epoch_to_jst() {
  local epoch_s="$1"
  if [ -z "$epoch_s" ] || [ "$epoch_s" = "0" ]; then
    printf '%s' "未設定"
    return
  fi
  local utc
  utc=$(date -u -d "@${epoch_s}" '+%Y-%m-%d %H:%M:%S')
  date -d "${utc} +9 hours" '+%m/%d(%a) %H:%M'
}

########################################################################
# main
########################################################################
if [ "$#" -ne 1 ]; then
  echo "Usage: $0 <reserve|update|delete|prestart|prepfailed|start|end|recfailed|encod_end>" >&2
  exit 1
fi

ret="$1"

CHANNELNAME="${CHANNELNAME:-放送局名なし}"
NAME="${NAME:-タイトル未設定}"
DESCRIPTION="${DESCRIPTION:-番組概要が指定されていません。}"
EXTENDED="${EXTENDED:-}"
STARTAT_MS="${STARTAT:-}"
ENDAT_MS="${ENDAT:-}"
DURATION_MS="${DURATION:-}"
ERROR_CNT="${ERROR_CNT:-N/A}"
DROP_CNT="${DROP_CNT:-N/A}"
SCRAMBLING_CNT="${SCRAMBLING_CNT:-N/A}"

start_epg_time=""
end_epg_time=""

if [ -n "$STARTAT_MS" ] && printf '%s' "$STARTAT_MS" | grep -qE '^[0-9]+$'; then
  start_epg_time=$(( STARTAT_MS / 1000 ))
fi
if [ -n "$ENDAT_MS" ] && printf '%s' "$ENDAT_MS" | grep -qE '^[0-9]+$'; then
  end_epg_time=$(( ENDAT_MS / 1000 ))
fi

startat="$(epoch_to_jst "${start_epg_time:-}")"
endat="$(epoch_to_jst "${end_epg_time:-}")"

if [ -n "$DURATION_MS" ] && printf '%s' "$DURATION_MS" | grep -qE '^[0-9]+$'; then
  duration_minutes=$(( DURATION_MS / 60000 ))
else
  duration_minutes="未設定"
fi

# ----- ここから printf -v を使わない組み立て -----
case "$ret" in
  reserve)
    content=$(printf '%s\n%s\n%s\n%s ~ %s    %s分\n%s\n%s' \
      "✅ 予約追加" "$NAME" "$CHANNELNAME" "$startat" "$endat" "$duration_minutes" "$DESCRIPTION" "$EXTENDED")
    ;;
  delete)
    content=$(printf '%s\n%s\n%s' "💨 予約削除" "$NAME" "$CHANNELNAME")
    ;;
  update)
    content=$(printf '%s\n%s\n%s\n%s ~ %s    %s分' \
      "🔁 予約更新" "$NAME" "$CHANNELNAME" "$startat" "$endat" "$duration_minutes")
    ;;
  prestart)
    content=$(printf '%s\n%s\n%s' "🔷 録画準備開始" "$NAME" "$CHANNELNAME")
    ;;
  prepfailed)
    content=$(printf '%s\n%s\n%s' "💥 録画準備失敗" "$NAME" "$CHANNELNAME")
    ;;
  start)
    content=$(printf '%s\n%s\n%s\n%s' "⏺ 録画開始" "$NAME" "$CHANNELNAME" "$startat")
    ;;
  encod_end)
    content=$(printf '%s\n%s\n%s' "⏹ エンコード終了" "$NAME" "$CHANNELNAME")
    ;;
  end)
    content=$(printf '%s\n%s\n%s\n%s ~ %s    %s分\n%s' \
      "⏹ 録画終了" "$NAME" "$CHANNELNAME" "$startat" "$endat" "...ラー: ${ERROR_CNT}, ドロップ: ${DROP_CNT}, スクランブル: ${SCRAMBLING_CNT}")
    ;;
  recfailed)
    content=$(printf '%s\n%s\n%s' "❌ 録画失敗" "$NAME" "$CHANNELNAME")
    ;;
  *)
    echo "未知のコマンド: $ret" >&2
    exit 1
    ;;
esac
# ----- 終わり -----

# Discord の webhook はメッセージ本文を JSON の content に入れます
escaped_text=$(escape_json "$content")

# Discord の制限(1 メッセージあたり約 2000 文字)を考慮して簡単に切り詰める
max_len=1900
if [ "${#escaped_text}" -gt "$max_len" ]; then
  # bash の部分文字列で切り詰め
  trimmed="${escaped_text:0:1896}..."
else
  trimmed="${escaped_text}"
fi

json="{\"content\":\"${trimmed}\"}"

curl -sS -X POST "${DISCORD_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d "${json}" || {
    echo "Discord webhook post failed" >&2
    exit 1
  }

exit 0

WebHookURLを設定

先程コピーしたWebHookURLをrun.shに貼り付けます。

DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-https://discord.com/api/webhooks/XXXXXXXXXXXXXXXXX}"
↓
DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-https://discord.com/api/webhooks/12561567152671/bxtyfTDRiftfftfeftfTDTDRYSvpppp}"
#このURLは適当です。正しいURLに置き換えてください。

スクリプトの設置と設定

docker-mirakurun-epgstationで構築している場合はこの先の手順を行っても正しく動作しない場合があります。
docker-mirakurun-epgstationで構築している場合は
https://qiita.com/saturi/items/b32c384600fa2f79b020#docker-mirakurun-epgstationで構築している場合
も参考にしてください。

run.shをEPGStationのルートディレクトリ(%ROOT%)の下のconfigディレクトリに保存
(例: /path/to/epgstation/config/run.sh)

必ず「UnixLF」で保存してください。CRLFなどだと正しく動きません。

実行権限を付与

chmod +x /path/to/epgstation/config/run.sh

EPGStationでのConfig設定

EPGStationのconfigファイルに以下を追記します。

config.yml
reserveNewAddtionCommand: '/bin/sh %ROOT%/config/run.sh reserve'
reserveUpdateCommand: '/bin/sh %ROOT%/config/run.sh update'
reservedeletedCommand: '/bin/sh %ROOT%/config/run.sh delete'
recordingPreStartCommand: '/bin/sh %ROOT%/config/run.sh prestart'
recordingPrepRecFailedCommand: '/bin/sh %ROOT%/config/run.sh prepfailed'
recordingStartCommand: '/bin/sh %ROOT%/config/run.sh start'
recordingFinishCommand: '/bin/sh %ROOT%/config/run.sh end'
recordingFailedCommand: '/bin/sh %ROOT%/config/run.sh recfailed'
encodingFinishCommand: '/bin/sh %ROOT%/config/run.sh encod_end'
設定項目名 イベント発生タイミング
reserveNewAddtionCommand 新しい予約が追加されたとき
reserveUpdateCommand 既存の予約が更新されたとき
reservedeletedCommand 予約が削除されたとき
recordingPreStartCommand 録画開始直前(チューナー準備中)
recordingPrepRecFailedCommand 録画準備に失敗したとき
recordingStartCommand 録画が開始されたとき
recordingFinishCommand 録画が完了したとき
recordingFailedCommand 録画中に失敗したとき
encodingFinishCommand エンコードが完了したとき

EPGStationを再起動して適用

停止

cd ~/git/docker-mirakurun-epgstation  (Pathは自分の環境に合わせて変えてください。)
docker compose down

起動

cd ~/git/docker-mirakurun-epgstation  (Pathは自分の環境に合わせて変えてください。)
docker compose up -d

テスト

プログラムが正しく動くかテストします。
以下コマンドを実行し、Discordに正しくメッセージが来るか確認してください。

cd /path/to/epgstation/config  (Pathは自分の環境に合わせて変えてください。)
bash run.sh reserve

スクリーンショット 2025-10-05 051107.png

あとはEPGStationのWebUIから録画予約などをしてみて正しくメッセージが届けば成功です。

docker-mirakurun-epgstationで構築している場合

dockerコンテナ内にEPGStationがある場合、上記の方法ではEPGStation側からrun.shを呼び出せません。
(つまり手動でrun.shを打つと動くのにEPGStationでrun.shを呼び出そうとしてもrun.shが呼び出されないという問題が起こります。)

docker内からrun.shを呼び出す方法

docker psを実行してコンテナ名を確認

docker psを実行してコンテナ名を確認します。

euser@SV-REC01:~$ docker  ps
CONTAINER ID   IMAGE                                    COMMAND                   CREATED       STATUS       PORTS                                                                                          NAMES
697917e7b531   docker-mirakurun-epgstation-epgstation   "npm start"               4 hours ago   Up 4 hours   0.0.0.0:8888-8889->8888-8889/tcp, [::]:8888-8889->8888-8889/tcp                                epgstation-v2
a99c725ceacb   mariadb:10.5                             "docker-entrypoint.s…"   4 hours ago   Up 4 hours   3306/tcp                                                                                       mysql-epgstation-v2
61b0245f5ec9   chinachu/mirakurun                       "docker-entrypoint.s…"   4 hours ago   Up 4 hours   0.0.0.0:9229->9229/tcp, [::]:9229->9229/tcp, 0.0.0.0:40772->40772/tcp, [::]:40772->40772/tcp   mirakurun

一番右側のNAMESの部分にある名前がコンテナ名です。(この場合は[epgstation-v2]です。)

docker cpでrun.shをコピー(お手軽)

この方法は簡単ですが、dockerの再起動 (もしくはPC自体の再起動)でrun.shが消えてしまいます。
永続化したい場合はこの次の方法をおすすめします。

ホストPCからコンテナ内にコピー

docker cp ./run.sh epgstation-v2:/app/config/run.sh

コンテナ内で実行権限を付与

docker exec -it epgstation-v2 chmod +x /app/config/run.sh

これで EPGStation から呼び出せるようになります。

メモ:
/app は epgstation の %ROOT% にあたるディレクトリです

docker-compose.yml にマウント設定を入れる(永続化・おすすめ)

この方法であれば再起動しても消えないのでこの方法がおすすめです。

docker-compose.ymlにマウント設定をする

書き方はホストPCのPath:Docker内のPathです。

docker-compose.yml
   epgstation:
        container_name: epgstation-v2
        build:
            context: "./epgstation"
            dockerfile: "debian.Dockerfile"
        volumes:
            #この下の行を追加 (Pathは環境に合わせて変えてください。) (相対パスでも動くはずですが、トラブルを避けるために絶対パスにしています。)
            - /home/euser/git/docker-mirakurun-epgstation/epgstation/config/run.sh:/app/config/run.sh  # ← ☆追加
            - /etc/localtime:/etc/localtime:ro
            - ./epgstation/config:/app/config
            - ./epgstation/data:/app/data
            - ./epgstation/thumbnail:/app/thumbnail
            - ./epgstation/logs:/app/logs
            - /mnt/data1:/app/recorded

こうすると、ホスト側の ./config フォルダに置いた run.sh が 自動でコンテナの /app/config と同期されます。
→ 以降はホストで run.sh を編集するだけでコンテナ内に反映されます。

実行権限を付与

chmod +x /path/to/epgstation/config/run.sh

ホスト側で実行権限をつけるとDockerにも反映されます。

Docker再起動して適用

停止

cd ~/git/docker-mirakurun-epgstation  (Pathは自分の環境に合わせて変えてください。)
docker compose down

起動

cd ~/git/docker-mirakurun-epgstation  (Pathは自分の環境に合わせて変えてください。)
docker compose up -d

送信テスト

以下を実行し正しくメッセージが届くか確認してください。

docker exec -it epgstation-v2 /bin/bash /app/config/run.sh reserve

届く場合は成功です。EPGStationのWebUI側から操作して正しく動くか確認してください。
届かない場合はrun.shで使われているcURLがDockerコンテナにインストールされていない可能性があります。以下コマンドでcURLをインストールしてください。

#Dockerコンテナへ入る
docker exec -it epgstation-v2 /bin/bash
#パッケージリストの更新
apt update
#cURLのインストール
apt install curl

再度以下を実行し正しくメッセージが届くか確認してください。

docker exec -it epgstation-v2 /bin/bash /app/config/run.sh reserve

メッセージが届けば成功です。EPGStationのWebUI側から操作して正しく動くか確認してください。

スクリーンショット 2025-10-05 051107.png

この方法ではDockerコンテナ・PCの再起動でcURLが消えてしまい、run.shを動かすには再度インストールが必要です。永続化したい場合は下記の手順を参考にDockerファイルにapt-get install curlを追加し、ビルドしてください。

DockerコンテナのcURLのインストールと再ビルド

Dockerコンテナ・PCの再起動後もcURLが使えるようにDockerfileを書き換えてからDockerコンテナを再ビルドします。

再ビルドではデータは消えないはずですが、バックアップを取っておくことをおすすめします。

EPGStationを停止

cd ~/git/docker-mirakurun-epgstation  (Pathは自分の環境に合わせて変えてください。)
docker compose down

Dockerfileは以下のようになっていると仮定して進めます。この部分は環境によって変えてください。
DockerfileのPath/home/euser/git/docker-mirakurun-epgstation/epgstation/debian.Dockerfile

Dockerfile
FROM l3tnun/epgstation:master-debian

ENV DEV="make gcc git g++ automake curl wget autoconf build-essential libass-dev libfreetype6-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texinfo zlib1g-dev"
ENV FFMPEG_VERSION=7.0

RUN apt-get update && \
    apt-get -y install $DEV && \
    apt-get -y install yasm libx264-dev libmp3lame-dev libopus-dev libvpx-dev && \
    apt-get -y install libx265-dev libnuma-dev && \
    apt-get -y install libasound2 libass9 libvdpau1 libva-x11-2 libva-drm2 libxcb-shm0 libxcb-xfixes0 libxcb-shape0 libvorbisenc2 libtheora0 libaribb24-dev && \
\
#ffmpeg build
    mkdir /tmp/ffmpeg_sources && \
    cd /tmp/ffmpeg_sources && \
    curl -fsSL http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2 | tar -xj --strip-components=1 && \
    ./configure \
      --prefix=/usr/local \
      --disable-shared \
      --pkg-config-flags=--static \
      --enable-gpl \
      --enable-libass \
      --enable-libfreetype \
      --enable-libmp3lame \
      --enable-libopus \
      --enable-libtheora \
      --enable-libvorbis \
      --enable-libvpx \
      --enable-libx264 \
      --enable-libx265 \
      --enable-version3 \
      --enable-libaribb24 \
      --enable-nonfree \
      --disable-debug \
      --disable-doc \
    && \
    make -j$(nproc) && \
    make install && \
\
# 不要なパッケージを削除
    apt-get -y remove $DEV && \
    apt-get autoremove -y && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* && \
    rm -rf /tmp/*

これを以下のように変更します。

Dockerfile
FROM l3tnun/epgstation:master-debian

ENV DEV="make gcc git g++ automake curl wget autoconf build-essential libass-dev libfreetype6-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texinfo zlib1g-dev"
ENV FFMPEG_VERSION=7.0

RUN apt-get update && \
    apt-get -y install $DEV && \
    apt-get -y install yasm libx264-dev libmp3lame-dev libopus-dev libvpx-dev && \
    apt-get -y install libx265-dev libnuma-dev && \
    apt-get -y install libasound2 libass9 libvdpau1 libva-x11-2 libva-drm2 libxcb-shm0 libxcb-xfixes0 libxcb-shape0 libvorbisenc2 libtheora0 libaribb24-dev && \
\
#ffmpeg build
    mkdir /tmp/ffmpeg_sources && \
    cd /tmp/ffmpeg_sources && \
    curl -fsSL http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2 | tar -xj --strip-components=1 && \
    ./configure \
      --prefix=/usr/local \
      --disable-shared \
      --pkg-config-flags=--static \
      --enable-gpl \
      --enable-libass \
      --enable-libfreetype \
      --enable-libmp3lame \
      --enable-libopus \
      --enable-libtheora \
      --enable-libvorbis \
      --enable-libvpx \
      --enable-libx264 \
      --enable-libx265 \
      --enable-version3 \
      --enable-libaribb24 \
      --enable-nonfree \
      --disable-debug \
      --disable-doc \
    && \
    make -j$(nproc) && \
    make install && \
\
# 不要なパッケージを削除
    apt-get -y remove $DEV && \
    apt-get autoremove -y && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* && \
    rm -rf /tmp/* && \ #☆この行から変更あり
\
# --- 最終イメージに curl と httpsを使うための ca-certificates を残す  ---
    apt-get update && \
    apt-get -y install --no-install-recommends curl ca-certificates && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

プロジェクトルートフォルダに移動し、ビルド実行

cd ~/git/docker-mirakurun-epgstation  (Pathは自分の環境に合わせて変えてください。)

docker compose build --no-cache epgstation

コマンドは「epgstation」です。「epgstation-v2」ではありません。
実行できない場合はdocker-compose.ymlにかかれている名前を使用してください。

services:
    epgstation: # ←この部分
        container_name: epgstation-v2
        ...
全文
docker-compose.yml
services:
    mirakurun:
        container_name: mirakurun
        build:
          context: Mirakurun
          dockerfile: docker/Dockerfile
        image: chinachu/mirakurun
        cap_add:
            - SYS_ADMIN
            - SYS_NICE
        ports:
            - "40772:40772"
            - "9229:9229"
        volumes:
            - /etc/localtime:/etc/localtime:ro
            - ./Mirakurun/config/:/app-config/
            - ./Mirakurun/data/:/app-data/
        environment:
            TZ: "Asia/Tokyo"
        devices:
            - /dev/px4video0:/dev/px4video0
            - /dev/px4video1:/dev/px4video1
            - /dev/px4video2:/dev/px4video2
            - /dev/px4video3:/dev/px4video3
            - /dev/bus:/dev/bus
        restart: always
        logging:
            driver: json-file
            options:
                max-file: "1"
                max-size: 10m

    mysql:
        container_name: mysql-epgstation-v2
        image: mariadb:10.5
        volumes:
            - mysql-db:/var/lib/mysql
        environment:
            MYSQL_USER: epgstation
            MYSQL_PASSWORD: epgstation
            MYSQL_ROOT_PASSWORD: epgstation
            MYSQL_DATABASE: epgstation
            TZ: "Asia/Tokyo"
        command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --performance-schema=false --expire_logs_days=1 # for mariadb
        restart: always
        logging:
            options:
                max-size: "10m"
                max-file: "3"

    epgstation: # ← !!!この部分!!!
        container_name: epgstation-v2
        build:
            context: "./epgstation"
            dockerfile: "debian.Dockerfile"
        volumes:
            - /home/euser/git/docker-mirakurun-epgstation/epgstation/config/run.sh:/app/config/run.sh
            - /etc/localtime:/etc/localtime:ro
            - ./epgstation/config:/app/config
            - ./epgstation/data:/app/data
            - ./epgstation/thumbnail:/app/thumbnail
            - ./epgstation/logs:/app/logs
            - /mnt/data1:/app/recorded
        environment:
            TZ: "Asia/Tokyo"
        depends_on:
            - mirakurun
            - mysql
        ports:
            - "8888:8888"
            - "8889:8889"
        #user: "1000:1000"
        restart: always

volumes:
    mysql-db:
        driver: local

必ず「--no-cache」をつけてください。つけないと古いレイヤーが使われ変更が反映されない場合があります。

ビルド実行
スクリーンショット 2025-10-05 024446.png

ビルドが終わったらEPGStationを立ち上げます。

cd ~/git/docker-mirakurun-epgstation  (Pathは自分の環境に合わせて変えry)
docker compose up -d 

Dockerコンテナ内でcURLが使えるか確認

docker exec -it epgstation-v2 bash
curl --version

トラブルシューティング

ありがちなトラブルです。

無効なオプションです。と表示される

bash run.sh
: 無効なオプションです set: 使用法: set [-abefhkmnptuvxBCEHPT] [-o option-name] [--] [-] [arg ...] run.sh: 行 5: $'\r': コマンドが見つかりません run.sh: 行 12: $'\r': コマンドが見つかりません run.sh: 行 18: 予期しないトークン $'{\r'' 周辺に構文エラーがあります 'un.sh: 行 18: escape_json() {

このエラーの原因は基本的にCRLFで保存したことによる「改行コードが Windows(CRLF) のまま(\r が残っている)」または「ファイル先頭に UTF-8 BOM が混入している」です。

対処法 → UnixLFで保存し直してください。

EPGStationから呼び出せない。かつEPGStationのSystemログに以下のようなものが残っている。

[2025-10-04T14:12:50.630] [INFO] system - execute cmd: /bin/bash /app/config/run.sh delete 
[2025-10-04T14:12:50.664] [ERROR] system - failed: /bin/bash /app/config/run.sh delete. exit: 1

exit: 1は「EPGStation がスクリプトを呼べているがスクリプトが途中で失敗した」ことを意味します。
このエラーはcURLがインストールされていない環境で起きやすいです。cURLがインストールされているか確認し、されていなければインストールしてください。
インストール方法

#Dockerコンテナへ入る(dockerを使っていない場合はスキップ)
docker exec -it epgstation-v2 /bin/bash
#パッケージリストの更新
apt update
#cURLのインストール
apt install curl
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?