root/trunk/plagger/lib/Plagger/Plugin/CustomFeed/iTunesRecentPlay.pm

Revision 652 (checked in by mizzy, 15 years ago)

Add genre to tags. (Thanks to otsune-san!)

Line 
1 package Plagger::Plugin::CustomFeed::iTunesRecentPlay;
2 use strict;
3 use warnings;
4 use base qw( Plagger::Plugin );
5 use File::Spec;
6 use Encode;
7 use DateTime::Format::W3CDTF;
8 use HTML::Entities;
9 use Plagger::UserAgent;
10 use Net::Amazon;
11 use Net::Amazon::Request::Keyword;
12
13 sub register {
14     my($self, $context) = @_;
15     $context->register_hook(
16         $self,
17         'subscription.load' => \&load,
18     );
19 }
20
21 sub load {
22     my($self, $context) = @_;
23
24     my $feed = Plagger::Feed->new;
25     $feed->aggregator(sub { $self->aggregate(@_) });
26     $context->subscription->add($feed);
27 }
28
29 sub aggregate {
30     my($self, $context, $args) = @_;
31
32     my $file = $self->conf->{library_path};
33     unless ($file) {
34         if ($^O eq 'MSWin32') {
35             require File::HomeDir::Windows;
36             my $mymusic = File::HomeDir::Windows->my_win32_folder('My Music');
37             $file = File::Spec->catfile($mymusic, 'iTunes', 'iTunes Music Library.xml');
38         } elsif ($^O eq 'darwin') {
39             $file = File::Spec->catfile($ENV{HOME}, 'Music', 'iTunes', 'iTunes Music Library.xml');
40         } else {
41             $context->log(error => "I can't guess library.xml path using your OS name $^O.");
42             return;
43         }
44     }
45
46     my $uri = URI->new($file);
47     if ($uri->scheme) {
48         $file = $self->cache->path_to('iTunes Music Library.xml');
49
50         my $ua = Plagger::UserAgent->new;
51         my $response = $ua->mirror($uri => $file);
52         if ($response->is_error) {
53             $context->log(error => "GET $uri failed: " . $response->status_line);
54             return;
55         }
56
57         $context->log(info => "Downloaded $uri to $file");
58     }
59
60     open my $fh, "<:encoding(utf-8)", $file
61         or return $context->log(error => "$file: $!");
62
63     my $feed = Plagger::Feed->new;
64     $feed->type('itunesrecentplay');
65     $feed->title("iTunes Recent Play");
66
67     my $data;
68     while (<$fh>) {
69         m!<key>Name</key><string>(.*?)</string>!
70             and $data->{track} = HTML::Entities::decode($1);
71         m!<key>Artist</key><string>(.*?)</string>!
72             and $data->{artist} = HTML::Entities::decode($1);
73         m!<key>Album</key><string>(.*?)</string>!
74             and $data->{album} = HTML::Entities::decode($1);
75         m!<key>Genre</key><string>(.*?)</string>!
76             and $data->{genre} = HTML::Entities::decode($1);
77         m!<key>Total Time</key><integer>(.*?)</integer>!
78             and $data->{duration} = HTML::Entities::decode($1);
79         m!<key>Play Date UTC</key><date>(.*?)</date>!
80             and $data->{date} = HTML::Entities::decode($1);
81         m!</dict>!
82             and do {
83                 if( $data->{date} and $data->{artist} ){
84                     my $dt = DateTime::Format::W3CDTF->parse_datetime($data->{date});
85                     unless ($dt) {
86                         $context->log( warn => "Can't parse $data->{date}");
87                         next;
88                     }
89                     if( !defined $self->conf->{duration} or $dt->epoch > time - $self->conf->{duration} * 60 ){
90                         my $entry = Plagger::Entry->new;
91                         $entry->date(Plagger::Date->from_epoch($dt->epoch));
92
93                         # author
94                         $entry->author($data->{artist});
95
96                         # Genre to tags
97                         $entry->add_tag($data->{genre});
98
99                         # title
100                         my $title = $self->conf->{title_format};
101                         $title = '%track - %artist' unless $title;
102                         $title =~ s/%artist/$data->{artist}/;
103                         $title =~ s/%album/$data->{album}/;
104                         $title =~ s/%track/$data->{track}/;
105                         $entry->title($title);
106
107                         # search aws
108                         if($self->conf->{aws_developer_token}){
109                             my $item = $self->search_aws($context, $data->{artist}, $data->{album});
110                             if($item){
111                                 $entry->link($item->url);
112                                 $entry->icon({ url => $item->ImageUrlSmall });
113                                 $entry->body($item->ProductDescription);
114                                 $entry->summary($item->ProductDescription);
115                             }
116                         }
117
118                         for my $key (keys %$data){
119                             $entry->meta->{$key} = $data->{$key};
120                         }
121
122                         $context->log( debug => $data->{artist} . ' ' . $data->{track});
123
124                         $feed->add_entry($entry);
125                     }
126                 }
127                 $data = {};
128             };
129     }
130
131     $context->update->add($feed);
132 }
133
134 sub search_aws {
135     my($self, $context, $artist, $album) = @_;
136     $context->log( info => "Searching $artist - $album on Amazon...");
137     my $attr;
138     $attr->{token}  = $self->conf->{aws_developer_token};
139     $attr->{locale} = $self->conf->{aws_locale};
140     $attr->{affiliate_id} = $self->conf->{aws_associate_id};
141
142     my $ua = Net::Amazon->new(%$attr);
143
144     my $keyword = encode("UTF-8", "$artist $album");
145     my $req = Net::Amazon::Request::Keyword->new(
146         keyword => $keyword,
147         mode    => 'music'. $self->conf->{aws_locale},
148     );
149
150     my $response = $ua->request($req);
151     my $item = ($response->properties())[0];
152     return $item;
153 }
154
155 1;
156 __END__
157
158 =head1 NAME
159
160 Plagger::Plugin::CustomFeed::iTunesRecentPlay - iTunes Recent Play custom feed
161
162 =head1 SYNOPSIS
163
164   # entries updated within 120 minutes
165   - module: CustomFeed::iTunesRecentPlay
166     config:
167       library_path: /path/to/iTunes Music Library.xml
168       duration: 120
169       title_format: %track - %artist
170       aws_developer_token: XXXXXXXXXXXXXXXXXXXX
171       aws_associate_id: xxxxxxxxxx-22
172       aws_locale: jp
173
174 =head1 DESCRIPTION
175
176 This plugin fetches the data of musics you played with iTunes or iPod recently.
177
178 =head1 CONFIG
179
180 =over 4
181
182 =item library_path
183
184 A path name of iTunes Music Libary.xml.If you omit this parameter,
185 this plugin try to find it automatically.
186
187 =item duration
188
189 This plugin find a music played recently if last played time is within
190 this parameter.It's good to define this parameter same as execution
191 period of plagger with cron to reduce memory usage.
192
193 =item title_format
194
195 Set a title format of an entry.You can use %track, %artist and %album.
196
197 =item aws_developer_token
198
199 If you set this parameter, this plugin get information about a track from the Amazon web service.
200
201 =item aws_associate_id
202
203 Your Amazon associate ID.
204
205 =item aws_locale
206
207 Set a web service locale.
208
209 =back
210
211 =head1 AUTHOR
212
213 Gosuke Miyashita, E<lt>gosukenator@gmail.comE<gt>
214
215 Tatsuhiko Miyagawa
216
217 =head1 SEE ALSO
218
219 L<Plagger>
220
221 =cut
Note: See TracBrowser for help on using the browser.