SweetPotato::Plagger このページをアンテナに追加 RSSフィード

2007-09-20

[] Subscription::Toranoana 大きい画像へのリンク付き修正版  Subscription::Toranoana 大きい画像へのリンク付き修正版 - SweetPotato::Plagger を含むブックマーク はてなブックマーク -  Subscription::Toranoana 大きい画像へのリンク付き修正版 - SweetPotato::Plagger  Subscription::Toranoana 大きい画像へのリンク付き修正版 - SweetPotato::Plagger のブックマークコメント

とらのあなの各店舗の出庫同人誌一覧を取得し,同人誌の小さいサンプル画像と通販ページにある個々の同人誌ページへのリンクを出力する,d:id:fubaさん作のSubscription::Toranoana

私はこのプラグインの常用者なんだけど,最近(というか大分前なのかな?)個々の同人誌ページに大きいサンプル画像へのリンクが追加されたようなので,プラグインの出力結果にも大きい画像へのリンクを含めるようにしてみた。ついでにリファクタリングをしたり,ログメッセージを詳しくしてみた。

Plagger/Plugin/Subscription/Toranoana.pm

本文に3枚のサンプル画像が表示されるのは以前と同じだが,各画像を選択すると大きい画像にジャンプする。「画像サイズが小さすぎてエロシーンが読めねえ!」とお嘆きの俺もこれで一安心。

package Plagger::Plugin::Subscription::Toranoana;
use base qw( Plagger::Plugin );
use strict;
use warnings;

use Plagger::Entry;
use Plagger::Feed;
use Plagger::Util;

use HTML::TreeBuilder::XPath;
use URI;

sub register {
    my ($self, $context) = @_;
    $context->register_hook(
        $self,
        'subscription.load' => \&load,
    );
}

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

    my $shops = $self->conf->{shop};
       $shops = [$shops] unless ref $shops;
    my $day = $self->conf->{day} || Plagger::Date->today->strftime('%m%d');

    for my $shop (@$shops) {
        my $feed = Plagger::Feed->new;
           $feed->aggregator(sub { $self->aggregate($context, $shop, $day) });
        $context->subscription->add($feed);
    }
}

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

    my $feed = Plagger::Feed->new;
       $feed->title('Toranoana@'."$shop $day");

    my $url = URI->new("http://www.toranoana.jp/shop/newka/$day/${shop}_all.html");
    my $tree = HTML::TreeBuilder::XPath->new;
       $tree->parse(Plagger::Util::load_uri($url));

    my $books = eval { $tree->findnodes('//body/div[3]/table') };
    if ($@) {
        $self->log(error => $@);
        return;
    }

    for my $book (@$books) {
        my $entry = $self->create_entry($context, $book) or next;
        $feed->add_entry($entry);
    }

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

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

    my $html = $book->as_HTML('<>&');

    my @record = ($html =~ m!<td[^>]*>([^<]*)</td>!g) or return;
    my ($id, $circle, $title, $genre) = @record;

    my $dir = join '/', ($id =~ /^(\d{2})(\d{4})(\d{2})(\d{2})/);
    my $link = "http://www.toranoana.jp/mailorder/article/$dir/$id.html";

    if ($self->conf->{yaoi_block} && ($html =~ /color: #FF99FF/)) {
        $context->log(info => "$title by $circle ($link) is yaoi");
        return;
    }

    my $body = join '', map {
          qq!<a href="http://img.toranoana.jp/popup_img/$dir/$id-${_}p.jpg">!
        . qq!<img src="http://img.toranoana.jp/img/$dir/$id-$_.gif" />!
        . qq!</a>!
    } (1..3);

    my $entry = Plagger::Entry->new;
       $entry->title($title);
       $entry->link($link);
       $entry->body($body);
       $entry->author($circle);
       $entry->tags([$genre]);

    return $entry;
}

1;
トラックバック - http://plagger.g.hatena.ne.jp/SweetPotato/20070920

2007-03-31

[] Subscription::WWWC  Subscription::WWWC - SweetPotato::Plagger を含むブックマーク はてなブックマーク -  Subscription::WWWC - SweetPotato::Plagger  Subscription::WWWC - SweetPotato::Plagger のブックマークコメント

WWWCのデータディレクトリを読み込み,アイテムごとにフィード化する。

WWWCバージョン1.0.4にて動作確認。それ以前のバージョンについては動作未確認。

Plagger/Plugin/Subscription/WWWC.pm

package Plagger::Plugin::Subscription::WWWC;
use strict;
use base qw( Plagger::Plugin );

use Clone qw( clone );
use Config::Simple;
use Encode;
use File::Spec;
use IO::File;

sub register {
    my ($self, $context) = @_;
    $context->register_hook(
        $self,
        'subscription.load' => \&load,
    );
}

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

    return unless $self->conf->{dir};
    my @leaves = $self->leaves($self->branch($context, $self->conf->{dir}));

    for my $leaf (@leaves) {
        my $feed = Plagger::Feed->new;
        $feed->url($leaf->{check_url});
        $feed->title($leaf->{title});
        $feed->link($leaf->{view_url} || $leaf->{check_url});
        $feed->meta($leaf->{meta});

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

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

    # meta
    my $ini = Config::Simple->new(File::Spec->catfile($dir, 'Folder.ini'));
    my $meta = {
        wwwc_check_st => $ini->param('CHECK.CheckSt'),
    };
    undef $ini;

    # dirs
    opendir DIR, $dir or $context->error("can't open directory: $dir");
    my @dirs = map  { $self->branch($context, $_) }
               grep { -d $_ }
               map  { File::Spec->catfile($dir, $_) }
               grep { $_ ne '.' and $_ ne '..' }
               readdir DIR;
    closedir DIR;

    # items
    my $io = IO::File->new(File::Spec->catfile($dir, 'Item.dat'));
    my @items = map { $self->item($_) } $io->getlines;
    $io->close;
    undef $io;

    return {
        meta  => $meta,
        dirs  => \@dirs,
        items => \@items,
    };
}

sub item {
    my ($self, $record) = @_;

    chomp $record;
    my @fields = split /\t/, $record;

    return {
        title     => Encode::decode('cp932', $fields[0]),
        check_url => $fields[1],
        view_url  => $fields[8],
        meta => {
            wwwc_check_st => $fields[12],
        },
    };
}

sub leaves {
    my ($self, $branch) = @_;

    my @leaves = ();
    push @leaves, $self->leaves($_) for @{$branch->{dirs}};
    push @leaves, map {
        my $leaf = clone($_);
        $leaf->{meta}->{wwwc_check_st} ||= $branch->{meta}->{wwwc_check_st};
        $leaf;
    } @{$branch->{items}};

    @leaves;
}

1;

WWWCにおける階層構造は無視される。

各フィードにはメタ情報 wwwc_check_st がセットされる。対応するアイテムがWWWCにおいてチェックされる場合(アイテムそれ自身,およびそれが含まれるディレクトリが,ともに「チェックする」状態の場合)は0が,そうでない場合は1がセットされる。0と1,それぞれが表す状態が直感と異なるのは,WWWCの実装に合わせた結果である。

config.wwwc.yaml

plugins:
  - module: Subscription::WWWC
    config:
      dir: C:\Program Files\WWWC\username\newssite
  - module: Filter::Rule
    rule:
#     expression: !$args->{feed}->meta->{wwwc_check_st}
      expression: $args->{feed}->meta->{wwwc_check_st} == 0

dir にはWWWCのデータディレクトリへのパスを指定する。dir 以下(サブディレクトリも)に含まれる全てのアイテムがフィード化される。

dir には必ずしもルートに相当するデータディレクトリ(上記例では C:\Program Files\WWWC\username がルート)を指定しなくてもよい。

dir に日本語を含むパスを指定する場合のテストは不十分である。自分の環境(Win2k,ActivePerl 5.8.8 Build 819,Plagger 0.7.16)では,configファイルをUTF-8ではなくシフトJISで保存するとうまくいくようである。

WWWCにおいてはチェックされないアイテムに対応するフィード(のエントリ)を除外したい場合は,Filter::Ruleを上記のように使用する。

expressionがうまく動かなかったので書き替えた。

TODO

WWWCのデータディレクトリ解析部だけ別モジュールにして再利用できそう。WWWC2XBELも作りたいなあ。

あとWWWC関連だと,WWWCのフィルタを読み込んでPlaggerで利用するモジュールもあるといいかも。Filter::WWWCかな。

さらにFilter::Diffも合わせれば,既存のデータを移行することなく,しかもWWWCのフィルタ機能を利用した似非はてなアンテナが作れそう。うわー夢が広がるわー。

トラックバック - http://plagger.g.hatena.ne.jp/SweetPotato/20070331