自作CGIを改造してポッドキャスト対応にする

表題の通り。以前趣味で作成した3gpやらそれ以外のCODECでエンコードされた動画を
別の形式に変換してダウンロードさせるスクリプトを作成したのだが、
今回はそれをポッドキャスト対応にすべく色々と試行錯誤を繰り返すことにした。

方法としては幾つかあるが、今回はPlaggerを使ってみることにした。
折角Perlをある程度使いこなせるようになったし、業務にも応用する機会が来れば良いなあという
淡い期待を胸に抱きながらPlagger自宅サーバに導入。

Podcastってのは要はRSSなので、別にPlaggerに頼らなくてもXML::RSSモジュールを使えば
直接Podcastを配信することは可能なのだが、今回はPlaggerの試用も兼ねているので
Plaggerのサンプルレシピの中にあった「RSSPodcastに変換する」設定を使ってみることに。
つまりPodcast化する前のRSS自体をXML::RSSで作成して、それをPlaggerPodcast化するわけだ。
ま……まどろっこしい!


というわけで、まずはRSSの生成・更新から。
CPANにあった解説に基づいて以下のようなサンプルコードを作成してみた。

#!/usr/bin/perl

use XML::RSS;
use Jcode;

my $GETDIR = "/var/html/data/video";
my $PREFIX_URL = "http://www.example.jp/data/video";

my $rss;
my %item;
my @files;
my @sorted_files;
my @sorted_keys;
my @reversed_keys;
my $i;

$rss = new XML::RSS;

if (-f "/var/html/tmp_video/feeds/rss.xml") {
    goto update_rss;
}

$rss->channel (
    title => "test rss",
    link => "http://www.example.jp/",
    language => "ja",
    description => "Summary for test rss",
);

opendir DIR, "$GETDIR" or die;
@files = grep { $_ =~ /.*\.3gp$/ } readdir DIR;
closedir DIR;

@sorted_files = sort @files;

for ($i = 0; defined $sorted_files[$i]; $i++) {
    my $disptitle = "テストコンテンツ$i";
    $item{"$disptitle"} = "$PREFIX_URL/$sorted_files[$i]";
}

@sorted_keys = sort keys %item;
@reversed_keys = reverse @sorted_keys;

foreach (@reversed_keys) {
    my $key = $_;
    $dispkey = jcode($key)->utf8;
    $rss->add_item(
        title => "$dispkey",
        link => "$item{$key}",
    );
}

open FILE, "> /var/html/tmp_video/feeds/rss.xml" or die;
print FILE $rss->as_string();
close FILE;

update_rss:
    $rss->parsefile("/var/html/tmp_video/feeds/rss.xml");
    $rss->add_item(
	title => "テストコンテンツ(追加)",
	link => "http://www.example.jp/data/video/test4.3gp",
	mode => "insert",
    );

    $rss->save("/var/html/tmp_video/feeds/rss.xml");

exit 0;


RSSの生成は実は今回が初なのだけれど、
こうやって高々60行かそこらのスクリプトで十分通用するものが作れるところがPerlのいいところだと思う。
Perl Hacksが実質モジュール解説の書籍になっているのも頷ける話だ……

閑話休題。あとはこのスクリプトで作成したRSSPlaggerを使ってPodcast化すればよいわけだ。
Plagger側のconfig.yamlも載せようかどうか迷ったけど、
サンプルのpodcast.yamlを微調整しただけなのであえて載せないことにする。

あとはこれを動画変換スクリプトに組み込めば完成……だがそこで一つ問題が発生した。
RSSの更新時に新しく登録した項目のタイトルが文字化けを起こしてしまうのだ。

散々調べまくって資料を確認した結果、どうやらXML::RSSの関連モジュールであるXML::Parserが
読み込んだRSSのエントリを自動的にUTF8で読み込むためらしい。
さらに困ったことにJcodeモジュールで日本語をUTF8にエンコードしても文字化けが解消されることは無かった。

……で、色々調べてみたところ(個人的には)意外な事実が発覚した。
詳細なところはこちらにあるが、
つまりこういうことらしい。

Perl 5.8.x では、内部的には、文字列は Unicode で扱われます。 内部的に扱われている Unicode 文字列には、 UTF8フラグというものがつくみたいです。
(中略)
外部から読み込んだ文字列や、ソースコードに書いた"ほげ"とかいう文字列は、 UTF8フラグがついていません(use encoding "..." しない場合)。

http://www.rwds.net/kuroita/program/Perl_unicode.html#flg


なんということだ!そんなこと聞いてないぞ!
……ということは、つまり外部から入ってきた文字列にUTF8フラグを付けてやればXML::Parserが正しく解釈してくれるということだろう。
というわけで早速上記の引用元の記事を参考にさっきのコードに手を入れてみる。

update_rss:
    use Encode;
    $rss->parsefile("/var/html/tmp_video/feeds/rss.xml");
    ## utf8フラグを立てるためにEncode::decodeを使う。
    #
    my $titlename = Encode::decode("euc-jp","テストコンテンツ(追加)");
    $rss->add_item(
	title => $titlename,
	link => "http://www.example.jp/data/video/test4.3gp",
	mode => "insert",
    );

    open FILE, "> /var/html/tmp_video/feeds/rss.xml" or die;
    binmode(STDOUT, "utf8");
    print FILE jcode($rss->as_string)->utf8;
    close FILE;

exit 0;


こんな感じ。これで文字化けすることなくRSSを更新することが出来た。