Orangepi PCでInternet Radioを構築する

MPDからの情報取り出し


OrangePi PCでインターネットラジオを構築する手順を紹介します。
インターネットラジオとしては以下の機能を持っています。
・ラジオステーションの選択やスピーカボリュームの変更を赤外線リモコンを使って行う
・再生中のアーティスト名と曲名をリアルタイムにLCDに表示する

今回は、再生中のアーティスト名と曲名をリアルタイムに取り出す手順を紹介します。
MPDのpythonクライアントライブラリを使用します。
このライブラリを使うと色々なことができるようになります。



【python mpd2のインストール】

python mpd2ライブラリをインストールします。
python mpd2ライブラリは、python mpdライブラリを元に開発されたライブラリで、こちらで公開されています。
python mpd2ライブラリは以下の手順でインストールします。
$ sudo pip install python-mpd2

【pythonコード】

以下のコード(radio1.py)で簡単な動作テストを行います。
idle()はよくできた関数で、何も変化が無いとブロックされ、何か変化が有ると抜けてきます。
currentsong()で取り出したtitleからアーティスト名と曲名を分割します。
アーティスト名と曲名の中に「-」が含まれることが有るので、分割時のセパレータは[ - ]で行います。
#!/usr/bin/python
#-*- encoding: utf-8 -*-
import sys
import subprocess
import time
from mpd import MPDClient # sudo pip install python-mpd2

if __name__ == '__main__':
        _DEBUG_ = 1
        argv = sys.argv         # コマンドライン引数を格納したリストの取得
        argc = len(argv)        # 引数の個数

        client = MPDClient()                            # create client object
        client.connect("localhost", 6600)       # connect to localhost:6600
        if _DEBUG_ == 1: print "connect"

        while True:
                client.idle()
                if _DEBUG_ == 1: print(client.status()["state"])
                if client.status()["state"] == "play":
                        if _DEBUG_ == 1: print(client.currentsong())
                        name="name" in client.currentsong()
                        file="file" in client.currentsong()
                        title="title" in client.currentsong()
                        if name:
                                if _DEBUG_ == 1: print('name:[%s]' % client.currentsong()["name"])
                        if file:
                                if _DEBUG_ == 1: print('file:[%s]' % client.currentsong()["file"])
                        if title:
                                titleString = client.currentsong()["title"]
                                artist, song = titleString.split(' - ')
                                artist = artist.strip()
                                song = song.strip()
                                if _DEBUG_ == 1: print('artist:[%s]' % artist)
                                if _DEBUG_ == 1: print('song:[%s]' % song)

        # never reach here
        client.close()                  # send the close command
        client.disconnect()             # disconnect from the server

上のスクリプトを実行して、別の端末で以下のコマンドを実行すると、再生中のアーティスト名と曲名を表示します。
$ mpc play
$ mpc stop
$ mpc next;mpc play
$ mpc prev;mpc play

同じ内容が何回か表示されますが、ステータスの変更/ステーションの変更/再生中のアーティスト名と曲名の変更など、
何かが変わると必ずidle関数から抜けてきます。
play
{'id': '1', 'pos': '0', 'name': 'Left Coast 70s: Mellow album rock from the Seventies. Yacht friendly. [SomaFM]', 'file': 'http://ice2.somafm.com/seventies-128-mp3', 'title': 'Toto - You Are The Flower'}
name:[Left Coast 70s: Mellow album rock from the Seventies. Yacht friendly. [SomaFM]]
file:[http://ice2.somafm.com/seventies-128-mp3]
artist:[Toto]
song:[You Are The Flower]

あとは以下のように前回紹介したLCDに表示するプログラムを呼び出せば完成です。
subprocessの呼び出しでエラーとなったときはsyslogに残す様にしています。
限られたラジオステーションしかテストしていないので、不具合が有るかもしれません。
#!/usr/bin/python
#-*- encoding: utf-8 -*-
import os
import sys
import subprocess
import time
from mpd import MPDClient # sudo pip install python-mpd2
import logging
import logging.handlers

if __name__ == '__main__':
        _DEBUG_ = 0
        argv = sys.argv         # コマンドライン引数を格納したリストの取得
        argc = len(argv)        # 引数の個数

        # ユーザ判定
        curr_user = os.geteuid()
        if _DEBUG_ == 1: print "curr_user=",curr_user
        if curr_user != 0:
                print("Must be root")
                sys.exit()

        # LDC表示プログラムの絶対パス
        lcdPath = os.path.join(os.path.dirname(os.path.abspath(__file__)),'lcd-gpio3')
        #lcdPath = os.path.join(os.path.dirname(os.path.abspath(__file__)),'lcd-dummy')
        if _DEBUG_ == 1: print "lcdPath=",lcdPath

        # start syslog
        my_logger = logging.getLogger('MyLogger')
        my_logger.setLevel(logging.DEBUG)
        handler = logging.handlers.SysLogHandler(address = '/dev/log')
        my_logger.addHandler(handler)

        # connect mpd server
        client = MPDClient()                            # create client object
        client.connect("localhost", 6600)       # connect to localhost:6600
        if _DEBUG_ == 1: print "connect"

        while True:
                client.idle()
                if _DEBUG_ == 1: print(client.status()["state"])
                if client.status()["state"] == "play":
                        if _DEBUG_ == 1: print(client.currentsong())
                        name="name" in client.currentsong()
                        file="file" in client.currentsong()
                        title="title" in client.currentsong()
                        if name:
                                if _DEBUG_ == 1: print('name:[%s]' % client.currentsong()["name"])
                        if file:
                                if _DEBUG_ == 1: print('file:[%s]' % client.currentsong()["file"])
                        if title:
                                #if _DEBUG_ == 1: print('title:%s' % client.currentsong()["title"])
                                titleString = client.currentsong()["title"]
                                artist, song = titleString.split(' - ')
                                artist = artist.strip()
                                song = song.strip()
                                if _DEBUG_ == 1: print('artist:[%s]' % artist)
                                if _DEBUG_ == 1: print('song:[%s]' % song)

                                # Display on LCD
                                try:
                                        subprocess.call([lcdPath, artist, song])
                                except:
                                        print('radio:subprocess.check_call() failed')
                                        my_logger.critical('radio:subprocess.check_call() failed')

                if client.status()["state"] == "stop":
                        # Clear on LCD & turn off Backlight
                        try:
                                subprocess.call([lcdPath])
                        except:
                                print('radio:subprocess.check_call() failed')
                                my_logger.critical('radio:subprocess.check_call() failed')

        # never reach here
        client.close()                  # send the close command
        client.disconnect()             # disconnect from the server

なお、LCDに表示するプログラムはルート権限が必要になります。
そこで、このスクリプト(radio2.py)はルート権限で実行します。
$ sudo python ./radio2.py

【crontabへの登録】

システム起動と同時にプログラムを起動するやり方はいくつかありますが、今回はcrontabに任せることにします。
いきなり「sudo python /home/orangepi/radio/radio2.py」を起動してもいいですが、
起動/停止のログをsyslogに残すために、以下の内容で「/home /orangepi/radio/radio.sh」を作って、このシェルをcrontabに登録します。
#!/bin/bash
logger "internet radio start"
sudo /usr/bin/python /home/orangepi/radio/radio2.py
logger "internet radio stop"

crontabの末尾に以下の1行を追加します。
@reboot sleep 15 && /home/orangepi/radio/radio.sh

これで起動と同時に、radio.shのスクリプト経由でradio2.pyが動きます。
syslogにスクリプトの起動/停止が記録されます。
$ sudo cat /var/log/syslog | grep "internet radio"
Nov  9 10:23:23 localhost orangepi: internet radio start
Nov  9 10:23:28 localhost orangepi: internet radio stop

【リモート実行】

mpdはクラサバのシステムです。
ネットワークに繋がっているマシンならば、リモートでmpcを実行することができます。
以下の1行を変えるだけで、別のマシンでこのスクリプトを実行することができます。

client.connect("localhost", 6600)       # connect to localhost:6600

client.connect("mpdが動いているサーバのIPアドレス", 6600)       # connect to remote:6600

続く...