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

Revision 790 (checked in by topia, 14 years ago)

discard error when isPermaLink attribute not found.

  • 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 $count = $self->{bloglines}->notify();
69     $context->log(info => "You have $count unread item(s) on Bloglines.");
70     if ($count) {
71         my $feed = Plagger::Feed->new;
72         $feed->aggregator(sub { $self->sync(@_) });
73         $context->subscription->add($feed);
74
75         if ($self->conf->{fetch_meta}) {
76             $self->{bloglines_meta} = $self->cache->get_callback(
77                 'listsubs_meta',
78                 sub { $self->fetch_meta($context) },
79                 '1 day',
80             );
81         }
82     }
83 }
84
85 sub fetch_meta {
86     my($self, $context) = @_;
87
88     $self->{folders} = {};
89     $context->log(info => "call Bloglines listsubs API to get folder structure");
90
91     my $subscription = $self->{bloglines}->listsubs();
92
93     my $meta;
94     for my $folder ($subscription->folders, 0) {
95         my $subid = ref $folder ? $folder->{BloglinesSubId} : 0;
96         my @feeds = $subscription->feeds_in_folder($subid);
97         for my $feed (@feeds) {
98             $meta->{$feed->{htmlUrl}} = {
99                 folder => $folder ? $folder->{title} : undef,
100                 xmlUrl => $feed->{xmlUrl},
101                 subid  => $feed->{BloglinesSubId},
102             };
103         }
104     }
105
106     $meta;
107 }
108
109 sub sync {
110     my($self, $context, $args) = @_;
111
112     my $mark_read = $self->conf->{mark_read};
113        $mark_read = 1 unless defined $mark_read;
114
115     my @updates;
116
117     # catch bad XML feed by Bloglines
118     eval {
119         @updates = $self->{bloglines}->getitems(0, $mark_read);
120     };
121
122     if ($@) {
123         $context->log(warn => "Bloglines Sync API returned bad XML. fallbacks to loop mode");
124         my @feeds = $self->{bloglines}->listsubs()->feeds;
125         for my $feed (@feeds) {
126             if ($feed->{BloglinesUnread}) {
127                 $context->log(debug => "Fetch $feed->{BloglinesSubId}");
128                 push @updates, eval { $self->{bloglines}->getitems($feed->{BloglinesSubId}, $mark_read) };
129             }
130         }
131     }
132
133     $context->log(info => scalar(@updates) . " feed(s) updated.");
134
135     for my $update (@updates) {
136         my $source = $update->feed;
137
138         my $feed = Plagger::Feed->new;
139         $feed->type('bloglines');
140         $feed->title($source->{title});
141         $feed->link($source->{link});
142         $feed->image($source->{image});
143         $feed->description($source->{description});
144         $feed->language($source->{language});
145         $feed->author($source->{webmaster});
146         $feed->meta->{bloglines_id} = $source->{bloglines}->{siteid};
147
148         # under fetch_pfolders option, set folder as tags to feeds
149         if (my $meta = $self->{bloglines_meta}->{$feed->link}) {
150             $feed->tags([ $meta->{folder} ]) if $meta->{folder};
151             $feed->url($meta->{xmlUrl});
152             $feed->meta->{bloglines_subid} = $meta->{subid};
153         }
154
155         $feed->source_xml($update->{_xml});
156
157         for my $item ( $update->items ) {
158             my $entry = Plagger::Entry->new;
159
160             $entry->title($item->{title});
161             $entry->author($item->{dc}->{creator});
162             $entry->tags([ $item->{dc}->{subject} ])
163                 if $item->{dc}->{subject};
164             $entry->date( Plagger::Date->parse('Mail', $item->{pubDate}) );
165             $entry->link($item->{link});
166
167             if ($item->{guid}) {
168                 my $is_permalink = eval { $item->{guid}->{isPermaLink}  } ||
169                     'false';
170                 my $guid_url     = "$item->{guid}"; # stringify MagicElement
171                 $entry->permalink($guid_url)
172                     if $guid_url =~ m!^https?://! && $is_permalink eq 'true';
173             }
174
175             $entry->feed_link($feed->link);
176             $entry->id($item->{guid});
177             $entry->body($item->{description});
178
179             if ($item->{enclosure}) {
180                 my $enclosure = Plagger::Enclosure->new;
181                 $enclosure->url( URI->new($item->{enclosure}->{url}) );
182                 $enclosure->length($item->{enclosure}->{length});
183                 $enclosure->auto_set_type($item->{enclosure}->{type});
184                 $entry->add_enclosure($enclosure);
185             }
186
187             $feed->add_entry($entry);
188         }
189
190         $context->update->add($feed);
191     }
192 }
193
194 1;
195
196 __END__
197
198 =head1 NAME
199
200 Plagger::Plugin::Subscription::Bloglines - Bloglines Subscription
201
202 =head1 SYNOPSIS
203
204   - module: Subscription::Bloglines
205     config:
206       username: your-email@account
207       password: your-password
208       mark_read: 1
209
210 =head1 DESCRIPTION
211
212 This plugin allows you to synchronize your subscription using
213 Bloglines Web Services sync API.
214
215 =head1 CONFIGURATION
216
217 =over 4
218
219 =item username, password
220
221 Your username & password to use with Bloglines API.
222
223 =item mark_read
224
225 C<mark_read> specifies whether this plugin I<marks as read> the items
226 you synchronize. With this option set to 0, you will get the
227 duplicated updates everytime you run Plagger, until you mark them
228 unread using Bloglines browser interface. Defaults to 1.
229
230 For people who uses Bloglines browser interface regularly, and use
231 Plagger as a tool to synchronize feed updates to mobile devices (like
232 PSP or iPod), I'd recommend set this option to 0.
233
234 Otherwise, especially for Publish::Gmail plugin users, I recommend set
235 to 1, the default.
236
237 =item fetch_meta
238
239 C<fetch_meta> specifies whether this plugin fetches I<folder>
240 strucuture using listsubs API. With this option on, all feeds under
241 I<Plagger> folder will have I<Plagger> as its tag.
242
243 You can use this tags information using Rules in later phase.
244
245 =back
246
247 =head1 AUTHOR
248
249 Tatsuhiko Miyagawa
250
251 =head1 SEE ALSO
252
253 L<Plagger>, L<WebService::Bloglines>, L<http://www.bloglines.com/>
254
255 =cut
256
Note: See TracBrowser for help on using the browser.