Rapberry Pi4 kodi-send、dialogコマンド

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254888/7ec29982-ecfe-af04-c294-83310947af5f.png" width=80%>

# 序文
以下の投稿で今更`kodi-send`を知る。
[Kodiの操作コマンド - Qiita](https://qiita.com/koba_ninkigumi/items/3e470c4ed8b1b3f45c50 "Kodiの操作コマンド - Qiita")

面白そうだったので試してみた。

# インストール
標準パッケージなので以下。
```shell-session
$ sudo apt install kodi-eventclients-kodi-send -y
```
# 使えそうなコマンド

```shell-session
# 戻る
$ kodi-send -a "Action(Back)"

# メニューカーソル上移動
$ kodi-send -a "Action(Up)"

# メニューカーソル下移動
$ kodi-send -a "Action(Down)"

# メニューカーソル左移動
$ kodi-send -a "Action(Left)"

# メニューカーソル右移動
$ kodi-send -a "Action(Right)"

# メニュー選択
$ kodi-send -a "Action(Select)"

# 動画一時停止／再開
$ kodi-send -a "Action(PlayPause)"

# 動画停止
$ kodi-send -a "Action(Stop)"
$ kodi-send -a "PlayerControl(Stop)"

# 再生音量を上げる
$ kodi-send -a "Action(VolumeUp)"

# 再生音量を下げる
$ kodi-send -a "Action(VolumeDown)"

# 再生音量パーセント指定
$ kodi-send -a "SetVolume(90, showvolumebar)"

# ミュート／解除
$ kodi-send -a "Action(Mute)"
$ kodi-send -a "Mute"

# kodi終了
$ kodi-send -a Quit

# スクリーンショット
$ kodi-send -a "TakeScreenshot"

# 次の動画
$ kodi-send -a "Action(SkipNext)"

# 前の動画
$ kodi-send -a "Action(SkipPrevious)"

# ダイアログ閉じる
$ kodi-send -a "Action(Close)"

# お気に入り画面表示
$ kodi-send -a "ActivateWindow(favourites)"
```
お気に入り画面表示、スクリーンショットが地味に便利かも。但し最新の Kodi on Raspberry Pi の動画再生はオーバーレイ表示なので原理的にスクリーンショットが撮れないけど。何とかならんかねぇ。

# 動画再生
対象がローカル環境の場合はフルパス指定で以下。NAS 等に保存してあるお気に入りの動画等を一発で再生できるのは便利かも。
```shell-session
# 組み込み WAVEファイル再生
$ kodi-send -a "PlayMedia(/usr/share/gui-pkinst/instcompr.wav)"
```

インターネット上の動画ファイル再生も同様に URL 指定で。
```shell-session
# インターネット上の動画ファイル再生
$ kodi-send -a "PlayMedia(https://mazwai.com/videvo_files/video/free/2019-12/small_watermarked/190915_B_01_Timelapse%20Danang_02_preview.webm)"
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254888/a18bc8d7-357f-8d38-6667-0037b1d865bb.png" width=80%>

※ [フリー動画サイト](https://mazwai.com/ "Free Stock Video Footage HD Royalty-Free Videos Download")より引用

# YouTube動画再生
YouTube動画となると URL指定ではダメ。以下の構文で。`video_id=`で動画IDを指定れば良い。
サンプルは一時期日本語圏でも話題になったライブ配信。日本人が殺到し YouTubeコメントで日本語（英語以外）のコメントが問答無用で削除対象になったのは記憶に新しい。最近は落ち着いたのかな？
```shell-session
# Namibia: Live stream in the Namib Desert
$ kodi-send -a "PlayMedia(plugin://plugin.video.youtube/play/?video_id=ydYDqZQpim8)"
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254888/9ec3a11d-cce4-7525-d6ab-4a2b2ae45297.png" width=80%>

プレイリスト再生の場合は以下の構文で。「超」FM音源ですなぁ。[X68k生誕35周年記念ということで。](https://akiba-pc.watch.impress.co.jp/docs/column/retrohard/1395502.html "生誕35周年の「シャープ X68000」、ツインタワーが特徴的なパーソナルワークステーション - AKIBA PC Hotline!")
```shell-session
# Cho Ren Sha 68K Original Soundtracks Complete
$ kodi-send -a "PlayMedia(plugin://plugin.video.youtube/play/?playlist_id=PL6PHQCxAqpJTA3R5hgkqVJChfIuecx2gh&play=1&order=default)"
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254888/b95eb638-eac4-e4a6-2bd0-47c042d4b18b.png" width=80%>

こちらは`playlist_id=`でプレイリストID を指定する形。`play=1`でプレイリストの先頭からの再生を指定しているけど、2 以降を指定すると再生できない。謎。`order=default`を省略すると、再生順番を確認するダイアログが表示されてしまうので指定するが吉。

以上は YouTube Add-on経由ということになる。詳細は以下参照。
[Add-on_YouTube - Official Kodi Wiki](https://kodi.wiki/view/Add-on:YouTube "Add-on_YouTube - Official Kodi Wiki")

因みに変なタイミングで再生すると、動画の手前にメニューが表示されてしまう場合がある。その時は前述`kodi-send -a "Action(Back)"`実行でメニューをどかすこと。

# シェルスクリプト1
コマンド入力は一通り分かったけど、やはり面倒。そもそも本来はリモコン操作でできるものだし。
そこでせめてシェルスクリプト化。まずは以下。

```shell-session:ksid.sh
#!/bin/bash

# 引数未指定の場合
if [ $# -ne 1 ]; then
  echo "使用法: $0 [動画ID | プレイリストID]" 1>&2
  echo "        YouTube 動画ID、またはプレイリストID を指定してください。" 1>&2
  exit 1
fi

if [ ${#1} = 11 ]; then
  kodi-send -a "PlayMedia(plugin://plugin.video.youtube/play/?video_id=$1,resume)" > /dev/null
else
  kodi-send -a "PlayMedia(plugin://plugin.video.youtube/play/?playlist_id=$1&play=1&order=default)" > /dev/null
fi
```

`github`にアップしたので、そこから取得する場合は以下。
```shell-session
$ wget https://github.com/god1964/RaspberryPi/raw/main/kodi-send/ksid.sh
$ chmod 755 ./ksid.sh
```

実行例は以下。YouTube動画ID、またはプレイリストID を引数で与え再生。
```shell-session
$ ./ksid.sh PL6PHQCxAqpJTA3R5hgkqVJChfIuecx2gh
```
シェルスクリプトを見てもらえば分かる通り、動画IDかプレイリストID かの判定は単純に文字列の長さ。（`if [ ${#1} = 11 ]; then`の箇所）
エラー判定等は行っていない。存在しない ID を与えてもエラー終了するだけ。
`PlayMedia`の引数に`resume`を指定しているのは、前回再生を中断している動画の場合、中断箇所からの再生か先頭から再生するかの確認ダイアログが表示されてしまうので、その対策。

# シェルスクリプト2
どうせならお気に入りの動画一覧を表示し、選択指定し再生したい。ついでの制御コマンド類もショートカットで指定したい。ということで以下。

<details><summary>クリックで開閉</summary><div>

```shell-session:ksyt.sh
#/bin/bash

function menu {
  # メニューカーソルの位置
  choice=0
  # メニュー配列
  menu=(
    "ydYDqZQpim8 Namibia: Live stream in the Namib Desert"
    "DAJYk1jOhzk Let It Go - Frozen - Alex Boyé (Africanized Tribal Cover) Ft. One Voice Children's Choir"
    "StLHSkvz3Rk 【鬼滅の刃 Demon Slayer: Kimetsu no Yaiba】和楽 紅蓮華【LiSA/紅蓮華 Gurenge】"
    "UvouZBYuijM JOKER | Don't Stop Me Now"
    "elJc256ekw4 【BGMギターカバー】沙羅曼蛇(1986KONAMI/AC))全曲メドレー"
    "PL6PHQCxAqpJTA3R5hgkqVJChfIuecx2gh Cho Ren Sha 68K Original Soundtracks Complete"
  )
  tail=$((${#menu[@]} - 1)) # メニューの末尾番号
  printf "\e[32mq:メニュー終了, Q:kodi終了, c:ダイアログ閉じる, e:選択, b:戻る, u:カーソル上移動, d:カーソル下移動, l:カーソル左移動, r:カーソル右移動, p:一時停止／再開, s:停止, n:次の動画, f:前の動画, o:お気に入りを開く, -:音量下げる, +:音量挙げる, t:スクショ, m:ミュート／解除, k:kodi起動\e[m\n"  >&2
  for _ in $(seq 0 $tail);do echo "";done

  # 無限ループ
  while true
  do
    printf "\e[${#menu[@]}A\e[m" >&2

    for i in $(seq 0 $tail);do
      if [ $choice = $i ]; then
        printf "\e[1;31m>\e[m \e[1;4m" >&2
      else # メニューが選択中でなければ
        printf "  " >&2
      fi
      title=`echo ${menu[$i]} | sed -e "s/^[^ ]*[ ]//"`
      printf "$title\e[m\n" >&2
    done

    read -sn1 input

    # ショートカット処理
    case $input in
      "q") return ;;
      "Q") kodi-send -a Quit > /dev/null ;;
      "c") kodi-send -a "Action(Close)" > /dev/null ;;
      "e") kodi-send -a "Action(Select)" > /dev/null ;;
      "b") kodi-send -a "Action(Back)" > /dev/null ;;
      "u") kodi-send -a "Action(Up)" > /dev/null ;;
      "d") kodi-send -a "Action(Down)" > /dev/null ;; 
      "l") kodi-send -a "Action(Left)" > /dev/null ;;
      "r") kodi-send -a "Action(Right)" > /dev/null ;;
      "p") kodi-send -a "Action(PlayPause)" > /dev/null ;; 
      "s") kodi-send -a "Action(Stop)" > /dev/null ;;
      "n") kodi-send -a "Action(SkipNext)" > /dev/null ;;
      "f") kodi-send -a "Action(SkipPrevious)" > /dev/null ;;
      "o") kodi-send -a "ActivateWindow(favourites)" > /dev/null ;;
      "-") kodi-send -a "Action(VolumeDown)" > /dev/null ;;
      "+") kodi-send -a "Action(VolumeUp)" > /dev/null ;;
      "t") kodi-send -a "TakeScreenshot" > /dev/null ;;
      "m") kodi-send -a "Mute" > /dev/null ;;
      "k")
        ps=`ps aux | grep "/bin/sh /usr/bin/kodi" | grep -v grep | wc -l`
        if [ $ps -eq 0 ]; then
          /bin/sh /usr/bin/kodi & > /dev/null
        fi
        ;;
    esac

    # メニュー制御
    if [ "$input" = "^[" ]; then # (^[はctrl+V ctrl+[とかで入力してね)
      read -sn2 input
    fi
    case $input in
      "j"|"[B") choice=$((choice + 1)); if [ $choice -gt $tail ]; then choice=0; fi ;;
      "k"|"[A") choice=$((choice - 1)); if [ $choice -lt 0 ]; then choice=$tail; fi ;;
      "")
        # 改行で決定
        ./ksid.sh `echo ${menu[$choice]} | awk '{print $1}'`
        ;;
    esac
  done
}

menu
```
</div></details>

こちらも`github`にアップしたのでそこから取得する場合は以下。
```shell-session
$ wget https://github.com/god1964/RaspberryPi/raw/main/kodi-send/ksyt.sh
$ chmod 755 ./ksyt.sh
```

実行例は以下。
```shell-session
$ ./ksyt.sh
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254888/bc2ff013-441c-6798-f21b-9b2b71d7f82c.png" width=80%>

以下を参考にさせていただいた。感謝。
[menu.sh](https://gist.github.com/matsub/8454ddda75bbe1039dc3af8d6638be60 "menu.sh")

矢印上下キーで対象動画を選択、Enterキー、またはSpaceキー押下で再生。
緑色文字の説明にあるように、基本的なコマンドもショートカットキーに割り当ててある。

画面に表示しているメニュー定義はスクリプト冒頭で定義している以下の配列。
```shell-session
  menu=(
    "ydYDqZQpim8 Namibia: Live stream in the Namib Desert"
```

以下のように、`<動画ID又はプレイリストリストID>`と`<動画タイトル>`を半角スペース区切りで記述する。
```shell-session
    "<動画ID又はプレイリストリストID> <動画タイトル>"`
```
スクリプトの作りとして、最初の半角までを`ID`、それ以降を`動画タイトル`として解釈するので、`動画タイトル`に半角スペースが含まれていても問題ない。
お気に入りの物に差し替えてもらえば相応に動作する筈。

但し問題がある。単純な文字列出力でメニューを構成しているので、まともに動くのは10個程度。多すぎると画面表示が乱れる。勿論一画面に収まらない量を定義したらまともに表示されない。程々に。
ベースとさせて頂いたものは、大量のメニューを想定していないものであり、本来の使用方法を逸脱しているので止む無し。

# シェルスクリプト3
もう少し何とかならないものか。かといって`ncurses`とかでフルスクラッチで作るのは本末転倒だし。お手軽な範囲で何とかならないかとぐぐって辿り着いたので`dialog`コマンド。
これも標準パッケージなので以下でインストール。
```shell-session
$ sudo apt install dialog -y
```

`dialog`コマンドを応用して作成したシェルスクリプトが以下。
<details><summary>クリックで開閉</summary><div>

```shell-session:ksdlg.sh
#!/bin/bash

menu_file=./ksdlg_menu.txt
DATA=`cat $menu_file`

while true; do
  str="dialog --menu \"一覧:\" 0 0 0 "
  i=1
  while read line
  do
    str+="$i "
    title=`echo $line | sed -e "s/^[^ ]*[ ]//"`
    str+="\"${title}\" "
    i=$((i + 1))
  done << FILE
$DATA
FILE

  str+="2>temp"
  eval $str

  # OK が押されたら
  if [ "$?" = "0" ]; then
    _return=$(cat temp)
    i=1
    while read line
    do
      if [ $_return -eq $i ]; then
        break
      fi
      i=$((i + 1))
    done << FILE
$DATA
FILE

    ./ksid.sh `echo $line | awk '{print $1}'`
  else
    break
  fi

  rm -f temp
done
```
```shell-session:ksdlg_menu.txt
ydYDqZQpim8 Namibia: Live stream in the Namib Desert
DAJYk1jOhzk Let It Go - Frozen - Alex Boye (Africanized Tribal Cover) Ft. One Voice Children's Choir
StLHSkvz3Rk 【鬼滅の刃 Demon Slayer: Kimetsu no Yaiba】和楽 紅蓮華【LiSA/紅蓮華 Gurenge】
UvouZBYuijM JOKER | Don't Stop Me Now
PLvlum7YWrP-WtAH0PS6_WfoWs0XALtNJ_ グラディウスII-GRADIUS II [Guitar Cover]
PL6PHQCxAqpJTA3R5hgkqVJChfIuecx2gh Cho Ren Sha 68K Original Soundtracks Complete
```
</div></details>

こちらも`github`にアップしたのでそこから取得する場合は以下。
```shell-session
$ wget https://github.com/god1964/RaspberryPi/raw/main/kodi-send/ksdlg.sh
$ wget https://github.com/god1964/RaspberryPi/raw/main/kodi-send/ksdlg_menu.txt
$ chmod 755 ./ksdlg.sh
```

実行例は以下。
```shell-session
$ ./ksdlg.sh
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254888/65822a56-4ed7-0c3f-015e-b4c976ed3a7b.png" width=80%>

こちらはメニューを分離した。`ksdlg_menu.txt`を編集しお気に入りの動画に差し替える想定。
操作方法は前出のスクリプト同様。大分まともになったけどやはり問題が。
まずメニュー選択位置が保持されない。
`dialog`コマンドは`<了解>`選択で終了し、メニュー選択位置（インデックス）を返す仕様であり、それを無限ループで回していて、再度`dialog`コマンドが実行されるので、毎回先頭メニューが選択される動きになってしまう。
こちらも本来の使用方法を逸脱しているので止む無し。それと`screen`コマンドとの相性が最悪で表示が崩れる。
それでなくても選択肢が多過ぎるとやはり表示が一部乱れる。ここら辺は出来合いの物で間に合わせている以上対応策はないっぽい。それとこちらのスクリプトにはショートカットキーは組み込むことができない。うーむ。

# 既知の問題
`ksyt.sh` `ksdlg.sh`スクリプトは`LibreELEC`環境では動かない。当初原因が分からず困惑したけど、シェルが`busybox`であることが原因。
```shell-session
LibreELEC:~ # ./ksyt.sh
./ksyt.sh: line 6: syntax error: unexpected "(" (expecting "}")
LibreELEC:~ # ./ksdlg.sh
./ksdlg.sh: line 3: syntax error: unexpected "("
LibreELEC:~ #
LibreELEC:~ # ls -l /bin/sh
lrwxrwxrwx    1 root     root             7 Aug 10  2021 /bin/sh -> busybox
LibreELEC:~ # ls -l /bin/bash
lrwxrwxrwx    1 root     root             7 Aug 10  2021 /bin/bash -> busybox
LibreELEC:~ #
```
`busybox`は配列に対応していないので、結果上記のシェルスクリプトは動かないという。うーむ。何だかなぁ。
それと`kodi-send`コマンドを連射すると、簡単に`kodi`が落ちたりするので注意。

# その他
ブラウザ上で一覧を表示して、という方向性をも考えたけどまとまらなかった。今日日はセキュリティ対策が厳しくブラウザ（JavaScript）上でコマンドを発行すのは地味に面倒。

標準で用意されている Webアプリは`kodi-webinterface.js`を使用しているようで、それを利用して何とかできないかなとも思ったけど……。Web系は苦手なんだよねぇ。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254888/5223ba17-765c-617e-5489-f8a38a989a88.png" width=80%>

あるとしたら、勉強がてら`ncurses`で作る、という方向性かなぁ。

# 参考URL
[kodi-send(1) — kodi-eventclients-kodi-send — Debian bullseye — Debian Manpages](https://manpages.debian.org/bullseye/kodi-eventclients-kodi-send/kodi-send.1.en.html "kodi-send(1) — kodi-eventclients-kodi-send — Debian bullseye — Debian Manpages")
[Debian -- bullseye の kodi-eventclients-kodi-send パッケージに関する詳細](https://packages.debian.org/bullseye/kodi-eventclients-kodi-send "Debian -- bullseye の kodi-eventclients-kodi-send パッケージに関する詳細")

[List of built-in functions - Official Kodi Wiki](https://kodi.wiki/view/List_of_built-in_functions "List of built-in functions - Official Kodi Wiki")
[Action IDs - Official Kodi Wiki](https://kodi.wiki/view/Action_IDs "Action IDs - Official Kodi Wiki")
[Window IDs - Official Kodi Wiki](https://kodi.wiki/view/Window_IDs "Window IDs - Official Kodi Wiki")
[Add-on_YouTube - Official Kodi Wiki](https://kodi.wiki/view/Add-on:YouTube "Add-on_YouTube - Official Kodi Wiki")

[Dialog_ How to create menus in your scripts - Applications and Systems](https://aplicacionesysistemas.com/en/dialog-crear-menus-tus-scripts/ "Dialog_ How to create menus in your scripts - Applications and Systems")
[A menu box - Linux Shell Scripting Wiki](https://bash.cyberciti.biz/guide/A_menu_box "A menu box - Linux Shell Scripting Wiki")
[dialog(1) manページ](https://nxmnpg.lemoda.net/ja/1/dialog "dialog(1) manページ")
[Shell _シェルスクリプトを組もう! Dialog 編 _ 221B Baker Street](http://baker-street.jugem.jp/?eid=150 "Shell _シェルスクリプトを組もう! Dialog 編 _ 221B Baker Street")

### 2022/03/29 追記
`ksdlg.sh`修正。
メニュー分離。
github上のフォルダ移動。