root/trunk/plagger/lib/Plagger/Plugin/Subscription/Bloglines.pm

Revision 430 (checked in by miyagawa, 14 years ago)

Reorganized CustomFeed? API:

  • Added RSS/Atom auto-discovery in Aggregator::Simple. Fixes #20
  • Fixes CustomFeed? bugs with multiple instances. Fixes #56
  • Fixes Aggregator::Simple bug it ignores title in Config. Fixes #110.
  • Automatically loads Aggregator::Simple where no Aggregator is installed. Refs #125
  • Warns if no aggregator corresponds with the feed. Fixes #125
  • Added $context->is_loaded($plugin). Fixes #132
  • Property svn:keywords set to Id Revision
Line 
1 package Plagger::Plugin::Subscription::Bloglines;
2 use strict;
3 use base qw( Plagger::Plugin );
4
5 our $VERSION = '0.10';
6 use WebService::Bloglines;
7
8 sub register {
9     my($self, $context) = @_;
10
11     $self->init_bloglines();
12
13     if ($self->conf->{no_sync_api}) {
14         $context->register_hook(
15             $self,
16             'subscription.load' => \&getsubs,
17         );
18     } else {
19         $context->register_hook(
20             $self,
21             'subscription.load' => \&notifier,
22         );
23     }
24 }
25
26 sub getsubs {
27     my($self, $context) = @_;
28     my $subscription = $self->{bloglines}->listsubs();
29
30     for my $folder ($subscription->folders, 0) {
31         my $subid = $folder ? $folder->{BloglinesSubId} : 0;
32         my $title = $folder ? $folder->{title} : undef;
33         $self->add_subscription($subscription, $subid, $title);
34     }
35 }
36
37 sub add_subscription {
38     my($self, $subscription, $subid, $title) = @_;
39
40     my @feeds = $subscription->feeds_in_folder($subid);
41     for my $source (@feeds) {
42         my $feed = Plagger::Feed->new;
43         $feed->title($source->{title});
44         $feed->link($source->{htmlUrl});
45         $feed->url($source->{xmlUrl} );
46         $feed->tags([ $title ]) if $title;
47         Plagger->context->subscription->add($feed);
48     }
49 }
50
51 sub init_bloglines {
52     my $self = shift;
53     $self->{bloglines} = WebService::Bloglines->new(
54         username => $self->conf->{username},
55         password => $self->conf->{password},
56         use_liberal => 1,
57     );
58 }
59
60 sub notifier {
61     my($self, $context) = @_;
62
63     my $count = $self->{bloglines}->notify();
64     $context->log(info => "You have $count unread item(s) on Bloglines.");
65     if ($count) {
66         my $feed = Plagger::Feed->new;
67         $feed->aggregator(sub { $self->sync(@_) });
68         $context->subscription->add($feed);
69
70         if ($self->conf->{fetch_meta}) {
71             $self->{bloglines_meta} = $self->cache->get_callback(
72                 'listsubs_meta',
73                 sub { $self->fetch_meta($context) },
74                 '1 day',
75             );
76         }
77     }
78 }
79
80 sub fetch_meta {
81     my($self, $context) = @_;
82
83     $self->{folders} = {};
84     $context->log(info => "call Bloglines listsubs API to get folder structure");
85
86     my $subscription = $self->{bloglines}->listsubs();
87
88     my $meta;
89     for my $folder ($subscription->folders, 0) {
90         my $subid = ref $folder ? $folder->{BloglinesSubId} : 0;
91         my @feeds = $subscription->feeds_in_folder($subid);
92         for my $feed (@feeds) {
93             $meta->{$feed->{htmlUrl}} = {
94                 folder => $folder ? $folder->{title} : undef,
95                 xmlUrl => $feed->{xmlUrl},
96                 subid  => $feed->{BloglinesSubId},
97             };
98         }
99     }
100
101     $meta;
102 }
103
104 sub sync {
105     my($self, $context, $args) = @_;
106
107     my $mark_read = $self->conf->{mark_read};
108        $mark_read = 1 unless defined $mark_read;
109
110     my @updates;
111
112     # catch bad XML feed by Bloglines
113     eval {
114         @updates = $self->{bloglines}->getitems(0, $mark_read);
115     };
116
117     if ($@) {
118         $context->log(warn => "Bloglines Sync API returned bad XML. fallbacks to loop mode");
119         my @feeds = $self->{bloglines}->listsubs()->feeds;
120         for my $feed (@feeds) {
121             if ($feed->{BloglinesUnread}) {
122                 $context->log(debug => "Fetch $feed->{BloglinesSubId}");
123                 push @updates, eval { $self->{bloglines}->getitems($feed->{BloglinesSubId}, $mark_read) };
124             }
125         }
126     }
127
128     $context->log(info => scalar(@updates) . " feed(s) updated.");
129
130     for my $update (@updates) {
131         my $source = $update->feed;
132
133         my $feed = Plagger::Feed->new;
134         $feed->type('bloglines');
135         $feed->title($source->{title});
136         $feed->link($source->{link});
137         $feed->image($source->{image});
138         $feed->description($source->{description});
139         $feed->language($source->{language});
140         $feed->author($source->{webmaster});
141         $feed->meta->{bloglines_id} = $source->{bloglines}->{siteid};
142
143         # under fetch_pfolders option, set folder as tags to feeds
144         if (my $meta = $self->{bloglines_meta}->{$feed->link}) {
145             $feed->tags([ $meta->{folder} ]) if $meta->{folder};
146             $feed->url($meta->{xmlUrl});
147             $feed->meta->{bloglines_subid} = $meta->{subid};
148         }
149
150         $feed->source_xml($update->{_xml});
151
152         for my $item ( $update->items ) {
153             my $entry = Plagger::Entry->new;
154
155             $entry->title($item->{title});
156             $entry->author($item->{dc}->{creator});
157             $entry->tags([ $item->{dc}->{subject} ])
158                 if $item->{dc}->{subject};
159             $entry->date( Plagger::Date->parse('Mail', $item->{pubDate}) );
160             $entry->link($item->{link});
161             $entry->feed_link($feed->link);
162             $entry->id($item->{guid});
163
164             $entry->body($item->{description});
165
166             $feed->add_entry($entry);
167         }
168
169         $context->update->add($feed);
170     }
171 }
172
173 1;
174
175 __END__
176
177 =head1 NAME
178
179 Plagger::Plugin::Subscription::Bloglines - Bloglines Subscription
180
181 =head1 SYNOPSIS
182
183   - module: Subscription::Bloglines
184     config:
185       username: your-email@account
186       password: your-password
187       mark_read: 1
188
189 =head1 DESCRIPTION
190
191 This plugin allows you to synchronize your subscription using
192 Bloglines Web Services sync API.
193
194 =head1 CONFIGURATION
195
196 =over 4
197
198 =item username, password
199
200 Your username & password to use with Bloglines API.
201
202 =item mark_read
203
204 C<mark_read> specifies whether this plugin I<marks as read> the items
205 you synchronize. With this option set to 0, you will get the
206 duplicated updates everytime you run Plagger, until you mark them
207 unread using Bloglines browser interface. Defaults to 1.
208
209 For people who uses Bloglines browser interface regularly, and use
210 Plagger as a tool to synchronize feed updates to mobile devices (like
211 PSP or iPod), I'd recommend set this option to 0.
212
213 Otherwise, especially for Publish::Gmail plugin users, I recommend set
214 to 1, the default.
215
216 =item fetch_meta
217
218 C<fetch_meta> specifies whether this plugin fetches I<folder>
219 strucuture using listsubs API. With this option on, all feeds under
220 I<Plagger> folder will have I<Plagger> as its tag.
221
222 You can use this tags information using Rules in later phase.
223
224 =back
225
226 =head1 AUTHOR
227
228 Tatsuhiko Miyagawa
229
230 =head1 SEE ALSO
231
232 L<Plagger>, L<WebService::Bloglines>, L<http://www.bloglines.com/>
233
234 =cut
235
Note: See TracBrowser for help on using the browser.