Wed, 10 Dec 2003

Winamp で再生中の曲を Blog に

自分のローカル MP3 プレイヤーで聞いている曲を、Blog のサイドバーに表示させたら楽しいですよね。今回は Winamp の再生中の曲を HTTP で通知し、RSS ファイルを生成してみます。

DoSomething Plugin

MP3 Player で再生中の曲を知るには、そのプレイヤーソフトウェアの API や SDK(*1)を用いて(*2)、曲情報を取得する必要があります。Winamp や iTunes 等のメジャーなソフトであれば、こうした SDK が公開されているため、自作でプラグインなどを作成することによって、再生中の曲を Blog にアップロード、といった方法もとれそうです。

ですが、この方法は誰でもできる、というほど簡単なものではありません。今回は、Winamp の DoSomething Plugin というよりジェネリックなプラグインを使用します。このプラグインは、

A Winamp General Purpose Plugin which is geared toward Shoutcasters. Whenever a song changes (or predetermined time interval) this plugin is responsible for doing "something". The somethings that are supported are as follows :

Submit A URL
FTP A file
Generate an HTML Playlist (Just like MusicTicker)
Run a command
Gather XML Statistics (this uses the Shoutcast XML admin interface to gather statistics about your broadcast, you can then use tags to substitute this information into any of the above actions)

というもので、Shoutcast を利用しているユーザが、曲の切替時に実行すべきコマンドや、FTP/HTTP のアクセスなどを登録しておくことができるというものです。

今回は、このうち Submit A URL の機能を使用して、

  1. Winamp が曲が変わるたびに特定の CGI URL に送信
  2. CGI でデータを RSS 化して保存
という流れで実現してみます。

The Code

Winamp からの曲更新通知を受けとる CGI コードは List 1 のようになります。パラメータとして song に、曲情報を渡す仕様としています。

our $RssPath    = "now_playing.xml"; # path for RSS data
our $MaxEntries = 15;                # number of items to store
our $ClientEncoding = "Shift_JIS";   # DoSomething songinfo encoding
RSS のパス、RSS に保持する曲数、DoSomething プラグインがマルチバイトの曲情報を送信するときのエンコーディングを指定しています。今回 RSS のバージョンには 0.91 を使用しますが、このバージョンではエントリは 15 個までという規定があるため、15 としています。

my $q = CGI->new();
my $song = _param($q, 'song');
CGI オブジェクトを new し、song パラメータを取得します。

sub _param {
    my($q, $name) = @_;
    my $value = $q->param($name);
    return decode($ClientEncoding, $value);
}
CGI パラメータ取得用のラッパーメソッド _param では、CGI.pm の param メソッドで取得した値を、$ClientEncoding でデコードして Unicode 文字列として返します。実は DoSomething プラグインでは、マルチバイト文字列は URL エスケープせずに生バイト文字列として送信してしまう不具合がありますが、CGI.pm で扱う分にはとりあえず問題なく処理できるようです。

my $rss = XML::RSS->new(version => 0.91);

if (-e $RssPath) {
    eval {
        $rss->parsefile($RssPath);
        pop(@{$rss->{items}}) if @{$rss->{items}} >= $MaxEntries;
    };
}
XML::RSS オブジェクトを version 0.91 で生成し、RSS ファイルが存在するなら parsefile します。RSS ファイルをデータストレージとしても使用していることに注意してください。

$MaxEntries より数が増えている場合には、最後の1個のエントリを pop で削除しています(*3)

$rss->channel(
    title => "Now Playing",
    pubDate => time2str(time),
);
RSS の channel 要素を設定します。再生していない場合には、ずっとこの RSS ファイルは更新されないことになり、その情報が伝わりませんので、channel の pubDate 属性に、現在時刻を入れておきます。現在時刻は RFC822 形式で格納する必要があるため、HTTP::Date モジュールの time2str 関数を使用しました。

$rss->add_item(
    title => $song,
    mode  => "insert",
);
現在の曲情報を title に持つ item を追加します。通常なら item は最後尾に追加されますが、mode => "insert" とすることで、先頭に追加(*4)することができます。

$rss->save($RssPath);

print $q->header, "Thanks, DoSomething!";
最後に RSS をファイルに保存し、適当な CGI レスポンスを出力して終了します。

Running the Hack

作成したスクリプトを CGI 実行可能な場所に配置します。ここでは URL を http://example.com/now_playing.cgi としましょう。

DoSomething Plugin をダウンロードして dosomethingv2_12.exe を実行します。とくにインストール自体は何の設定もなく終了するはずです。

次に DoSomething プラグインのアクションを設定します。Winamp を起動し、Options → Preference で設定画面を開き、Plug-ins → General Purpose から "DoSomething Plugin" を選択します。 DoSomething 設定 "Disable Plugin" のチェックをはずし、"Actions" から "Submit A URL" を選択、URL を

http://example.com/now_playing.cgi?song=%%CURRENTSONG%%

と設定し、Add をクリックすれば登録が完了です。もちろん、URL は今回作成した now_playing.cgi を設置した URL に置きかえてください。

Winamp で適当に曲を再生、また曲を変更してみましょう。プレイヤーの見た目はとくに変化しませんが、now_playing.cgi に対してリクエストが飛ばされているはずです。うまく動作すれば、$RssPath で設定したパスに RSS ファイル now_playing.xml が作成されています。サンプルの now_playing.xml は List 2 のようになります。

ここまでできれば、あとは出力された RSS ファイルを JavaScript 化して、Client Side Include で Blog のサイドバーなどに出力するとよいでしょう。

Hacking the Hack

今回は RSS 0.91 を使用しているため、各曲の演奏開始時刻を設定できていません。RSS 1.0 にすれば、実現可能でしょう。

RSS を使用していますが、それぞれの item に link 要素や description 要素が設定されていません。例えば AmazonWebServices を使用してアソシエイトへのリンクを link に設定したり、レビューを取得して description に設定したり、といったことができるとさらに面白いでしょう。

See Also

いずれも、専用の CGI ではなく、Trackback Ping (MovableType の Category Trackback や tb-standalone) を通知のプロトコルとして利用しています。

Listings

List 1: now_playing.cgi
#!/usr/local/bin/perl -w
# now_playing - Save winamp info to RSS

use strict;
use CGI;
use Encode;
use HTTP::Date;
use XML::RSS;

our $RssPath    = "now_playing.xml"; # path for RSS data
our $MaxEntries = 15;                # number of items to store
our $ClientEncoding = "Shift_JIS";   # DoSomething songinfo encoding

my $q = CGI->new();
my $song = _param($q, 'song');

my $rss = XML::RSS->new(version => 0.91);

if (-e $RssPath) {
    eval {
        $rss->parsefile($RssPath);
        pop(@{$rss->{items}}) if @{$rss->{items}} >= $MaxEntries;
    };
}

$rss->channel(
    title => "Now Playing",
    pubDate => time2str(time),
);

$rss->add_item(
    title => $song,
    mode  => "insert",
);

$rss->save($RssPath);

print $q->header, "Thanks, DoSomething!";

sub _param {
    my($q, $name) = @_;
    my $value = $q->param($name);
    return decode($ClientEncoding, $value);
}
List 2: now_playing.xml
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
            "http://my.netscape.com/publish/formats/rss-0.91.dtd">

<rss version="0.91">

<channel>
<title>Now Playing</title>
<link></link>
<description></description>
<pubDate>Tue, 09 Dec 2003 19:09:54 GMT</pubDate>

<item>
<title> Busted - Air Hostess </title>
<link></link>
</item>

<item>
<title> The Ataris - So long, Astoria </title>
<link></link>
</item>

<item>
<title> Crystal Kay - Can't be Stopped </title>
<link></link>
</item>

</channel>
</rss>
*1) Software Development Kit
*2) 公開されていれば、ですが
*3) この場合、RSS は新着順となるため、最も古いエントリが削除されることになります。
*4) Perl の配列でいえば、push ではなく unshift
Prev: Trackback スレッド化
Next: Blog Hacks 出版

About This Blog

Weblog まわりのテクノロジーをネタにして、プログラミングテクニックを Cookbook 形式で紹介しています。

Site Search


WWW This Site