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

Revision 1852 (checked in by miyagawa, 13 years ago)

fixed typoes

  • 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 plugin_id {
9     my $self = shift;
10     $self->class_id . '-' . $self->conf->{username};
11 }
12
13 sub register {
14     my($self, $context) = @_;
15
16     $self->init_bloglines();
17
18     if ($self->conf->{no_sync_api}) {
19         $context->register_hook(
20             $self,
21             'subscription.load' => \&getsubs,
22         );
23     } else {
24         $context->register_hook(
25             $self,
26             'subscription.load' => \&notifier,
27         );
28     }
29 }
30
31 sub getsubs {
32     my($self, $context) = @_;
33     my $subscription = $self->{bloglines}->listsubs();
34
35     for my $folder ($subscription->folders, 0) {
36         my $subid = $folder ? $folder->{BloglinesSubId} : 0;
37         my $title = $folder ? $folder->{title} : undef;
38         $self->add_subscription($subscription, $subid, $title);
39     }
40 }
41
42 sub add_subscription {
43     my($self, $subscription, $subid, $title) = @_;
44
45     my @feeds = $subscription->feeds_in_folder($subid);
46     for my $source (@feeds) {
47         my $feed = Plagger::Feed->new;
48         $feed->title($source->{title});
49         $feed->link($source->{htmlUrl});
50         $feed->url($source->{xmlUrl} );
51         $feed->tags([ $title ]) if $title;
52         Plagger->context->subscription->add($feed);
53     }
54 }
55
56 sub init_bloglines {
57     my $self = shift;
58     $self->{bloglines} = WebService::Bloglines->new(
59         username => $self->conf->{username},
60         password => $self->conf->{password},
61         use_liberal => 1,
62     );
63 }
64
65 sub notifier {
66     my($self, $context) = @_;
67
68     my $to_fetch = $self->conf->{dont_use_notifier_api};
69     unless ($to_fetch) {
70         my $count = $self->{bloglines}->notify();
71         $context->log(info => "You have $count unread item(s) on Bloglines.");
72         $to_fetch = $count;
73     }
74
75     if ($to_fetch) {
76         my $feed = Plagger::Feed->new;
77         $feed->aggregator(sub { $self->sync(@_) });
78         $context->subscription->add($feed);
79
80         if ($self->conf->{fetch_meta}) {
81             $self->{bloglines_meta} = $self->cache->get_callback(
82                 'listsubs_meta',
83                 sub { $self->fetch_meta($context) },
84                 '1 day',
85             );
86         }
87     }
88 }
89
90 sub fetch_meta {
91     my($self, $context) = @_;
92
93     $self->{folders} = {};
94     $context->log(info => "call Bloglines listsubs API to get folder structure");
95
96     my $subscription = $self->{bloglines}->listsubs();
97
98     my $meta;
99     for my $folder ($subscription->folders, 0) {
100         my $subid = ref $folder ? $folder->{BloglinesSubId} : 0;
101         my @feeds = $subscription->feeds_in_folder($subid);
102         for my $feed (@feeds) {
103             $meta->{$feed->{htmlUrl}} = {
104                 folder => $folder ? $folder->{title} : undef,
105                 xmlUrl => $feed->{xmlUrl},
106                 subid  => $feed->{BloglinesSubId},
107             };
108         }
109     }
110
111     $meta;
112 }
113
114 sub sync {
115     my($self, $context, $args) = @_;
116
117     my $mark_read = $self->conf->{mark_read};
118        $mark_read = 1 unless defined $mark_read;
119
120     my @updates;
121
122     # catch bad XML feed by Bloglines
123     eval {
124         @updates = $self->{bloglines}->getitems(0, 0);
125     };
126
127     if ($@) {
128         $context->log(warn => "Bloglines Sync API returned bad XML. fallbacks to loop mode: $@");
129         my @feeds = $self->{bloglines}->listsubs()->feeds;
130         for my $feed (@feeds) {
131             if ($feed->{BloglinesUnread}) {
132                 $context->log(debug => "Fetch $feed->{BloglinesSubId}");
133                 push @updates, eval { $self->{bloglines}->getitems($feed->{BloglinesSubId}, $mark_read) };
134             }
135         }
136     } elsif ($mark_read) {
137         # no error found with XML ... call the API again to mark read
138         eval {
139             @updates = $self->{bloglines}->getitems(0, $mark_read);
140         };
141     }
142
143     $context->log(info => scalar(@updates) . " feed(s) updated.");
144
145     for my $update (@updates) {
146         my $source = $update->feed;
147
148         my $feed = Plagger::Feed->new;
149         $feed->type('bloglines');
150         $feed->title($source->{title});
151         $feed->link($source->{link});
152         $feed->image($source->{image});
153         $feed->description($source->{description});
154         $feed->language($source->{language});
155         $feed->author($source->{webmaster});
156         $feed->meta->{bloglines_id} = $source->{bloglines}->{siteid};
157
158         # under fetch_pfolders option, set folder as tags to feeds
159         if (my $meta = $self->{bloglines_meta}->{$feed->link}) {
160             $feed->tags([ $meta->{folder} ]) if $meta->{folder};
161             $feed->url($meta->{xmlUrl});
162             $feed->meta->{bloglines_subid} = $meta->{subid};
163         }
164
165         $feed->source_xml($update->{_xml});
166
167         for my $item ( $update->items ) {
168             my $entry = Plagger::Entry->new;
169
170             $entry->title($item->{title});
171             $entry->author($item->{dc}->{creator});
172             $entry->tags([ $item->{dc}->{subject} ])
173                 if $item->{dc}->{subject};
174             $entry->date( Plagger::Date->parse('Mail', $item->{pubDate}) );
175             $entry->link($item->{link});
176
177             if ($item->{guid}) {
178                 my $is_permalink = eval { $item->{guid}->{isPermaLink}  } ||
179                     'false';
180                 my $guid_url     = "$item->{guid}"; # stringify MagicElement
181                 $entry->permalink($guid_url)
182                     if $guid_url =~ m!^https?://! && $is_permalink eq 'true';
183             }
184
185             $entry->feed_link($feed->link);
186             $entry->id($item->{guid});
187             $entry->body($item->{description});
188
189             if ($item->{enclosure}) {
190                 my $enclosure = Plagger::Enclosure->new;
191                 $enclosure->url( URI->new($item->{enclosure}->{url}) );
192                 $enclosure->length($item->{enclosure}->{length});
193                 $enclosure->auto_set_type($item->{enclosure}->{type});
194                 $entry->add_enclosure($enclosure);
195             }
196
197             $feed->add_entry($entry);
198         }
199
200         $context->update->add($feed);
201     }
202 }
203
204 1;
205
206 __END__
207
208 =head1 NAME
209
210 Plagger::Plugin::Subscription::Bloglines - Bloglines Subscription
211
212 =head1 SYNOPSIS
213
214   - module: Subscription::Bloglines
215     config:
216       username: your-email@account
217       password: your-password
218       mark_read: 1
219
220 =head1 DESCRIPTION
221
222 This plugin allows you to synchronize your subscription using
223 Bloglines Web Services sync API.
224
225 =head1 CONFIGURATION
226
227 =over 4
228
229 =item username, password
230
231 Your username & password to use with Bloglines API.
232
233 =item mark_read
234
235 C<mark_read> specifies whether this plugin I<marks as read> the items
236 you synchronize. With this option set to 0, you will get the
237 duplicated updates every time you run Plagger, until you mark them
238 unread using Bloglines browser interface. Defaults to 1.
239
240 For people who uses Bloglines browser interface regularly, and use
241 Plagger as a tool to synchronize feed updates to mobile devices (like
242 PSP or iPod), I'd recommend set this option to 0.
243
244 Otherwise, especially for Publish::Gmail plugin users, I recommend set
245 to 1, the default.
246
247 =item fetch_meta
248
249 C<fetch_meta> specifies whether this plugin fetches I<folder>
250 structure using listsubs API. With this option on, all feeds under
251 I<Plagger> folder will have I<Plagger> as its tag.
252
253 You can use this tags information using Rules in later phase.
254
255 =item dont_use_notifier_api
256
257 Turn it on when you want to skip Notifier API, which could sometimes
258 be broken and always returns 0 instead of the actual unread count.
259
260 =back
261
262 =head1 AUTHOR
263
264 Tatsuhiko Miyagawa
265
266 =head1 SEE ALSO
267
268 L<Plagger>, L<WebService::Bloglines>, L<http://www.bloglines.com/>
269
270 =cut
271
Note: See TracBrowser for help on using the browser.