Hatena::ブログ(Diary)

あまやん胸いっぱい このページをアンテナに追加 RSSフィード

2009-03-08

[tech][perl][plagger] Dropbox + Plaggerでお手軽Podcastもどき

大変世間に遅ればせながら、ストレージ共有サービスDropboxを使い始めました。

ユーザー登録後クライアントを設定したら、ローカルに設定した「Dropboxフォルダ」にファイルを放り込むだけ。あとは自動的に同期が行われて、クライアントさえ入っていればネット上のどこからでもローカルのファイルのようにこれを共有することができます。

便利なことにこの「Dropbox」はファイルの入出力情報をRSSで吐いてくれるので、これを使って面白いことができないかなと考えてみました。

たとえば・・・置かれたMp3ファイルのアップデート状況だけ抜き出せば、なんちゃってPodcastプラットフォームとしてDropboxを使えないかなと。

というわけでPlaggerを使い、DropboxのユーザーディレクトリにアップされたMp3の情報を抜き出してPodcastフィードを生成する仕組みを作ってみました。

Plagger::Plugin::CustomFeed::Dropbox::Mp3

Dropboxのユーザーフィードからmp3の追加部分を抜き出し、Podcast用のフィードとして構成しなおします。

package Plagger::Plugin::CustomFeed::Dropbox::Mp3;

use strict;
use warnings;

use base qw(Plagger::Plugin);
use Plagger::Date;
use Plagger::Feed;
use Plagger::Util;
use Plagger::Enclosure;

use XML::LibXML;
use HTML::TokeParser::Simple;
use DateTime::Format::DateParse;

sub register {
    my ($self, $context) = @_;

    # エントリーロード時のフックを設定
    $context->register_hook(
        $self,
        'subscription.load' => \&load,
    );
}

sub load {
    my ($self, $context) = @_;

    my $ym   = Plagger::Date->now()->strftime("%y%m");
    my $feed = Plagger::Feed->new();
    $feed->aggregator(sub { $self->aggregate($context, $ym); });
    $context->subscription->add($feed);
}

sub aggregate {
    my ($self, $context, $ym) = @_;

    my $feed = Plagger::Feed->new();
    my $renewaltime = Plagger::Date->now();
    $renewaltime->set_locale("ja_JP");

    $feed->link($self->conf->{url});
    $feed->title($self->conf->{title});
    $feed->description($self->conf->{desc});

    # Feedをパース
    my $items = $self->parse(
        Plagger::Util::load_uri(URI->new($self->conf->{url}))
    );

    # パースしたFeedのエントリたちをRSSに登録していく
    for my $item (@$items) {
        my $entry = Plagger::Entry->new();
        $entry->title($item->{title});
        $entry->link($item->{link});
        $entry->date($item->{datetime});

        my $enclosure = Plagger::Enclosure->new();
        $enclosure->url($item->{link});
        $enclosure->auto_set_type();
        $entry->add_enclosure($enclosure);

        $feed->add_entry($entry);
    }

    $context->update->add($feed);
}

sub parse {
    my ($self, $content) = @_;
    my $list = [];

    # XMLパース
    my $parser = XML::LibXML->new();
    my $dom = $parser->parse_string($content);

    my @nodes_item = $dom->findnodes('//item');

    # エントリーからHTMLタグ(Aタグ)のhref部分を抜き出してリストに追加
    foreach my $node_item (@nodes_item) {

        # エントリーのタイトルに「added」(ファイル追加)が含まれたものだけ対象
        my $action = $node_item->findvalue('title');
        next if ($action !~ /added/);

        my $content = $node_item->findvalue('description');

        # UTC形式で書かれたpubDateをJSTに変換(YYYYMMDDhhmmss)
        my $pubDate = $node_item->findvalue('pubDate');
        my $dt = DateTime::Format::DateParse->parse_datetime($pubDate);
        $dt->set_time_zone('Asia/Tokyo');
        my $datetime = sprintf("%04d%02d%02d%02d%02d%02d",
            $dt->year,
            $dt->month,
            $dt->day,
            $dt->hour,
            $dt->minute,
            $dt->second,
        );

        my $html_parser = HTML::TokeParser::Simple->new(
            string => $content
        );
        while(my $token = $html_parser->get_token) {
            if ($token->is_start_tag('a')) {
                my $title = $token->get_attr('title');
                next if ($title !~ /\.mp3$/);
                if ($title =~ /(.+)\.mp3$/) {
                    $title = $1;
                }

                my $href = $token->get_attr('href');

                push (@$list, {
                    datetime => $datetime,
                    title    => $title,
                    link     => $href,
                });
            }
        }
    }

    @$list = sort { $b->{datetime} cmp $a->{datetime} } @$list;
    $list;
}

1;

config部分

プラグインを経由して整形したフィードをPublish::Feed経由でRSS2.0に書き出します。podcast.yamlとか適当に名前をつけるよろし。

フィードの書き出し先はwebサーバの公開ディレクトリにします。

plugins:
  - module: CustomFeed::Dropbox::Mp3
    config:
      url: https://www.getdropbox.com/****/events.xml # チェックするDropboxのフィードURL
      title: Amayan Dropbox Mp3 updates               # Podcastのタイトル
      desc: Dropboxでポッドキャストなのだっ           # Podcastの説明文
  - module: Publish::Feed
    config:
      format: RSS
      dir: /home/amayan/dir/ # フィードの書き出し先(ローカルの絶対パス)
      filename: podcast.xml  # 書き出しするファイル名

実際の動かし方

ローカルのDropboxフォルダにmp3ファイルを置く

番組のタイトルをファイル名にして保存するだけ。

f:id:amayan:20090308144933p:image

Plaggerを動作させる

実行するplaggerコマンドの直下にあるlibディレクトリ内にプラグインを入れていることを前提にしています。

$ ./plagger -c podcast.yaml

実際に運用する場合は、これをcronで回したりするといい感じだと思います。

フィードを生成する
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <generator>Plagger/0.7.17</generator>
    <link>https://www.getdropbox.com/****/events.xml</link>
    <description>Dropboxでポッドキャストなのだっ</description>
    <title>Amayan Dropbox Mp3 updates</title>
    <pubDate>Sun, 08 Mar 2009 14:48:14 +0900</pubDate>
    <item>
      <author>nobody@example.com</author>
      <dc:creator>nobody@example.com</dc:creator>
      <link>https://dl.getdropbox.com/u/645112/%E9%9F%B3%E5%A3%B0%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%281KHz%29.mp3</link>
      <enclosure length="" url="https://dl.getdropbox.com/u/645112/%E9%9F%B3%E5%A3%B0%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%281KHz%29.mp3" type="audio/mpeg">https://dl.getdropbox.com/u/645112/%E9%9F%B3%E5%A3%B0%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%281KHz%29.mp3audio/mpeg</enclosure>
      <title>音声サンプル(1KHz)</title>
      <guid isPermaLink="false">tag:vps00462,2006:https://dl.getdropbox.com/u/645112/%E9%9F%B3%E5%A3%B0%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%281KHz%29.mp3</guid>
    </item>
  </channel>
</rss>
(iTunesで見てもらいやすくするために) 公開する

iTunesインストールされているクライアントの場合、itpc://で始まるURIを開くと自動的にiTunesが立ち上がってインポートを行ってくれる*1ので、下記のような「クリック一発登録」のリンクを作成します。

サンプルはこんな感じです。http://akabane.amayan.jp/podcast.html

<html>
  <head>
    <title>Podcast登録</title>
  </head>
  <body>
    <a href="itpc://akabane.amayan.jp/podcast.xml">クリックして登録</a>
  </body>
</html>

f:id:amayan:20090308154406p:image

このリンクをクリックすると、例えばWindows Vistaの場合は以下のようなダイアログが出るので「アプリケーションの起動」をクリックします。

f:id:amayan:20090308153737p:image

その後はiTunes側で自動的にインポートしてくれて、ミッション完了!

f:id:amayan:20090308154650p:image

素敵なPodcastライフを!

まとめ&余談

わざわざ外部のサービスを利用したりするほど積極的には更新/公開するつもりはないけれど、特定の友人間で気軽な交換日記的に音声を上げあってチェックできるような仕組みがほしいなーというときになかなか重宝しそうです。

あとは擬似ライブ放送のような形で時差的にファイルをどんどんアップしていきたいときなど、普段配信しているPodcastとは別に「特別チャンネル」のような形で立ち上げて見るのも楽しそう。

せっかく作ったプラグインなので、ゆーすけべーさんが以前やっていた*2ようにCodeReposへ登録してみようかとも思うのですが・・・いったいどこまでファイルを上げればよいものやら。

現状はローカルにチェックアウトしたSixapartのPlaggerリポジトリにファイルを足して(コミットはしていない)使っている状況なのですが、この場合はトップレベルからまるまるCodeReposインポートしてしまう形でいいのでしょうか。。教えてエロい人!

2009/07/13追記: githubに上げました http://github.com/amayan/Plagger--Plugin--CustomFeed--Dropbox--Mp3/tree/master

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/amayan/20090308/1236495643