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

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

Subscriptoin::Bloglines: when it encounters bad XML, falls back to loop mode. Fixes #100

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