Пишем простой event-handler для ncmpcpp на bash

от
Linux

MPD (music player daemon) is an audio player that has a server-client architecture. It plays audio files, organizes playlists and maintains a music database all while using very few resources. In order to interface with it, a separate client is needed.(c) ArchWiki

В свою очередь, ncmpcpp — это удобный ncurses-клиент для mpd. Ко всему прочему, он же рализует некоторые другие возможности, не относящиеся к обязанностям mpd. Рассмотрим одну из них.

Запуск процесса по переключению трекаВ ncmpcpp(1) написано:
execute_on_song_change = COMMAND
        Shell command to execute on song change.
Предельно понятно. Следуя этой инструкции, редактируем конфиг и создаём собственный скрипт.

  1. execute_on_song_change = "~/.ncmpcpp/song-change.sh"
— Куда-нибудь в ~/.ncmpcpp/config

  1. $ cd .ncmpcpp
  2. $ touch song-change.sh
  3. $ chmod +x song-change.sh
— Собственно, скрипт

Выбор языка для скриптинга — вопрос больше эстетический. Для себя я обычно выбираю bash.

Реализация song-change.shЯ привык к подобной структуре проекта:
  1. #!/bin/bash
  2.  
  3. function config() {
  4.     set -o nounset
  5.     return
  6. }
  7.  
  8. funciton main() {
  9.     if config "$@"; then
  10.     fi
  11. }
  12.  
  13. main "$@"
  14. exit "$?"

Идея заключается в продолжении работы скрипта только в том случае, если инициализация (функция config) завершается с нулевым кодом возврата. Отдельно надо сказать о четвёртой строке — set -o nounset — это некоторая защита от использования неинициализированных переменных.

Хорошо. Теперь реализуем, например, логирование.
  1. TRACK="$(mpc current)"
  2. STATISTICS="$HOME/.ncmpcpp/log.txt"
  3. readonly TRACK STATISTICS
— Для функции config. mpc — ещё один mpd-клиент, но на этот раз c командным интерфейсом.

  1. function save_statistics() {
  2.     (
  3.         echo $(date +%a\ %d.%m.%Y\ %H:%M:%S | tr a-z A-Z)
  4.         echo "$TRACK"
  5.         echo
  6.     ) >> "$STATISTICS"
  7. }
— Сохранение статистики.

Теперь добавляем вызов функции save_statistics в main и тестируем. Если всё получилось правильно (как-то так), то результат не заставит себя ждать:
  1. $ cat ~/.ncmpcpp/log.txt | tail -3
  2. THU 31.12.2015 19:57:41
  3. Behemoth - The Seed Ov I

Все хорошо работает. Убедившись в этом, перейдём к чему-нибудь более интересному.

aNMusicScreenshot from 2015-12-31 21-28-11.pngСовсем недавно (спустя часа два относительно написания статьи) @aNNiMON обновил API для него.

Использование API сводится к корректно отправленному POST-запросу.
  - Целевой адрес: annimon.com/json/nowplay.php
  - При ошибке отдаётся json с полем error и описанием ошибки
  - Параметры запроса:
     - act = set
     - login = логин юзера
     - token = токен (взять тут http://annimon.com/str/nowplay.php)
     - artist = исполнитель
     - title = название
     - genre = жанр

Все параметры мы будем хранить прямо в скрипте, исключая токен. Здесь есть небольшая дилемма:
  - Хранить токен прямо в скрипте значением. Даже если учесть, что токен напрямую зависит от пароля (так и есть на самом деле), так делать не очень правильно;
  - Шифрование токена. Придётся иногда вводить пассофразу, но этим недостатком мы пренебрежём.

Теперь всё что остаётся сделать — это правильно дополнить наш скрипт. Приступим.

  1. # aNMusic specific
  2. ADDRESS="annimon.com/json/nowplay.php"
  3. LOGIN="kalter"
  4. TOKEN="$(gpg -qd "$HOME/.private/anmusic-token.gpg")"
  5. readonly ADDRESS LOGIN TOKEN
— в функцию config. Значение LOGIN меняем под себя, TOKEN расшифровываем из специального файла.

  1. function anmusic() {
  2.     local POST
  3.     POST="act=set&login=$LOGIN&token=$TOKEN&"
  4.     POST+="artist=$(mpc -f "%artist%" | head -1)&"
  5.     POST+="title=$(mpc -f "%title%" | head -1)"
  6.     curl -d "$POST" "annimon.com/json/nowplay.php"
  7. }
— собственно, реализация POST-запроса. Обратите внимание, параметр genre не задан.

  1. notify-send "$TRACK"
  2. save_statistics
  3. anmusic
— в функцию main. Нотификация, логирование и отправка трека.

Обработкой ошибок и анализом JSON-a мы пренебрегаем. Это можно реализовать отдельно, однако это уже другая история.

Результирующий скрипт: http://ix.io/n4Y
  • +4
  • views 4159