File Coverage

blib/lib/Lyrics/Fetcher.pm
Criterion Covered Total %
statement 27 88 30.6
branch 5 32 15.6
condition 0 18 0.0
subroutine 6 11 54.5
pod 2 2 100.0
total 40 151 26.4


line stmt bran cond sub pod time code
1             package Lyrics::Fetcher;
2              
3 1     1   883 use strict;
  1         2  
  1         38  
4 1     1   6 use warnings;
  1         2  
  1         30  
5 1     1   544 use Lyrics::Fetcher::Cache;
  1         2  
  1         36  
6              
7             # Lyrics Fetcher
8             #
9             # Copyright (C) 2007-10 David Precious (CPAN: BIGPRESH)
10             #
11             # Originally authored by and copyright (C) 2003 Sir Reflog
12             # who kindly passed maintainership on to David Precious in Feb 2007
13             #
14             # Original idea:
15             # Copyright (C) 2003 Zachary P. Landau
16             # All rights reserved.
17             #
18             # This program is free software; you can redistribute it and/or modify
19             # it under the terms of the GNU General Public License as published by
20             # the Free Software Foundation; either version 2 of the License, or
21             # (at your option) any later version.
22             #
23             # This program is distributed in the hope that it will be useful,
24             # but WITHOUT ANY WARRANTY; without even the implied warranty of
25             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26             # GNU General Public License for more details.
27             #
28             # You should have received a copy of the GNU General Public License
29             # along with this program; if not, write to the Free Software
30             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31              
32             # $Id$
33              
34 1     1   5 use vars qw($VERSION $Error @FETCHERS $Fetcher $debug);
  1         1  
  1         71  
35              
36             $VERSION = '0.5.2';
37             $Error = 'OK'; #return status string
38              
39             $debug = 0; # If you want debug messages, set debug to a true value, and
40             # messages will be output with warn.
41              
42 1     1   3 use strict;
  1         2  
  1         233  
43              
44             BEGIN {
45 1     1   3 @FETCHERS = ();
46 1         2 my $myname = __PACKAGE__;
47 1         1 my $me = $myname;
48 1         5 $me =~ s/\:\:/\//g;
49 1         2 foreach my $d (@INC) {
50 11         14 chomp $d;
51 11 100       956 if ( -d "$d/$me/" ) {
52 3         6 local (*F_DIR);
53 3         90 opendir( *F_DIR, "$d/$me/" );
54 3         53 while ( my $b = readdir(*F_DIR) ) {
55 9 100       63 if (my($fetcher) = $b =~ /^(.*)\.pm$/) {
56 3 50       12 next if $fetcher eq 'Cache';
57 0           push @FETCHERS, $fetcher;
58             }
59             }
60             }
61             }
62             }
63              
64              
65             =head1 NAME
66              
67             Lyrics::Fetcher - Perl extension to manage fetchers of song lyrics.
68              
69             =head1 SYNOPSIS
70              
71             use Lyrics::Fetcher;
72            
73             # using a specific fetcher:
74             print Lyrics::Fetcher->fetch('Pink Floyd','Echoes','LyricWiki');
75            
76             # if you omit the fetcher, automatically tries all available fetchers:
77             print Lyrics::Fetcher->fetch('Oasis', 'Cast No Shadow');
78            
79             # or you can pass an arrayref of fetchers you want used:
80             print Lyrics::Fetcher->fetch('Oasis', 'Whatever', [qw(LyricWiki Google)]);
81              
82             # To find out which fetchers are available:
83             my @fetchers = Lyrics::Fetcher->available_fetchers;
84              
85              
86             =head1 DESCRIPTION
87              
88             This module is a fetcher manager. It searches for modules in the
89             Lyrics::Fetcher::* name space and registers them as available fetchers.
90              
91             The fetcher modules are called by Lyrics::Fetcher and they return song's lyrics
92             in plain text form.
93              
94             This module calls the respective Fetcher->fetch($$) method and returns the
95             result.
96              
97             In case of module error the Fetchers must return undef with the error
98             description in $@.
99              
100             In case of problems with lyrics' fetching, the error will be returned in the
101             $Lyrics::Fetcher::Error string. If all goes well, it will have 'OK' in it.
102              
103             The fetcher selection is made by the "method" parameter passed to the fetch()
104             of this module. You can also omit this parameter, in which case all available
105             fetchers will be tried, or you can supply an arrayref of fetchers you'd like
106             to try (in order of preference).
107              
108             The value of the "method" parameter must be a * part of the Lyrics::Fetcher::*
109             fetcher package name.
110              
111             =head1 INTERFACE
112              
113             =over 4
114              
115             =item available_fetchers
116              
117             Returns a list of available fetcher modules.
118              
119             say "Fetchers available: " . join ',', Lyrics::Fetcher->available_fetchers;
120              
121              
122             =cut
123              
124             sub available_fetchers {
125 0 0   0 1   return wantarray ? @FETCHERS : \@FETCHERS;
126             }
127              
128              
129             =item fetch($artist, $title [, $fetcher])
130              
131             Attempt to fetch the lyrics for the given artist and title.
132              
133             If you want to control which fetcher(s) will be used, you can supply a scalar
134             containing the name of the fetcher you want to use, or an arrayref of fetchers
135             you want to try (in the order you want them tried). By default, each fetcher
136             module which is installed will be tried.
137              
138             if (my $lyrics = Lyrics::Fetcher->fetch('Oasis', 'Whatever')) {
139             say $lyrics;
140             } else {
141             warn "Failed to fetch lyrics - error was: " . $Lyrics::Fetcher::ERROR;
142             }
143              
144              
145             =cut
146              
147             sub fetch {
148 0     0 1   my ( $self, $artist, $title, $fetcherspec ) = @_;
149            
150             # first, see if we've got it cached:
151 0 0         if (defined(my $cached = Lyrics::Fetcher::Cache::get($artist, $title))) {
152             # found in the cache; it could either be the lyrics, or 0 (meaning
153             # we didn't find the lyrics last time, but we cached that fact so
154             # that we don't try again. If it's 0, return undef rather than the
155             # 0.
156 0 0         return $cached ? $cached : undef;
157             }
158              
159 0           my @tryfetchers;
160 0 0 0       if ( $fetcherspec && !ref $fetcherspec && $fetcherspec ne 'auto') {
    0 0        
161             # we've been given a specific fetcher to use:
162 0 0         if (grep /$fetcherspec/, @FETCHERS) {
163 0           push @tryfetchers, $fetcherspec;
164             } else {
165 0           warn "$fetcherspec isn't a valid fetcher";
166 0           $Error = "Fetcher $fetcherspec isn't installed or is invalid";
167 0           return;
168             }
169             } elsif (ref $fetcherspec eq 'ARRAY') {
170             # we've got an arrayref of fetchers to use:
171 0           for my $fetcher (@$fetcherspec) {
172 0 0         if (grep /$fetcher/, @FETCHERS) {
173 0           push @tryfetchers, $fetcher;
174             } else {
175 0           warn "$fetcher isn't a valid fetcher, ignoring";
176             }
177             }
178             } else {
179             # OK, try all available fetchers.
180 0           push @tryfetchers, @FETCHERS;
181             }
182              
183 0           return _fetch( $artist, $title, \@tryfetchers );
184              
185             } # end of sub fetch
186              
187              
188             # actual implementation method - takes params $artist, $title, and an
189             # arrayref of fetchers to try. Returns the result from the first fetcher
190             # that succeeded, or undef if all fail.
191             sub _fetch {
192              
193 0     0     my ( $artist, $title, $fetchers ) = @_;
194              
195 0 0 0       if ( !$artist || !$title || ref $artist || ref $title ) {
      0        
      0        
196 0           warn "_fetch called incorrectly";
197 0           return;
198             }
199              
200 0 0 0       if ( !$fetchers || ref $fetchers ne 'ARRAY' ) {
201 0           warn "_fetch not given arrayref of fetchers to try";
202 0           return;
203             }
204              
205            
206             fetcher:
207 0           for my $fetcher (@$fetchers) {
208            
209 0           _debug("Trying fetcher $fetcher for artist:$artist title:$title");
210            
211 0           my $fetcherpkg = __PACKAGE__ . "::$fetcher";
212 0           eval "require $fetcherpkg";
213 0 0         if ($@) {
214 0           warn "Failed to require $fetcherpkg ($@)";
215 0           next fetcher;
216             }
217            
218             # OK, we require()d this fetcher, try using it:
219 0           $Error = 'OK';
220 0           _debug("Fetcher $fetcher loaded OK");
221 0 0         if (!$fetcherpkg->can('fetch')) {
222 0           _debug("Fetcher $fetcher can't ->fetch()");
223 0           next fetcher;
224             }
225            
226 0           _debug("Trying to fetch with $fetcher");
227 0           my $f = $fetcherpkg->fetch( $artist, $title );
228 0 0         if ( $Error eq 'OK' ) {
229 0           $Fetcher = $fetcher;
230 0           _debug("Fetcher $fetcher returned lyrics");
231 0           my $lyrics = _html2text($f);
232 0           Lyrics::Fetcher::Cache::set($artist, $title, $lyrics);
233 0           return $lyrics;
234             }
235             else {
236 0           next fetcher;
237             }
238             }
239              
240             # if we get here, we tried all fetchers we were asked to try, and none
241             # of them worked.
242 0           $Error = 'All fetchers failed to fetch lyrics';
243            
244             # if we're caled again for the same artist and title, there's no point
245             # trying all the fetchers again, so cache the failure:
246 0           Lyrics::Fetcher::Cache::set($artist, $title, 0);
247            
248 0           return undef;
249             } # end of sub _fetch
250              
251             # nasty way to strip out HTML
252             sub _html2text {
253 0     0     my $str = shift;
254              
255 0           $str =~ s/\r/\n/g;
256 0           $str =~ s//\n/g;
257 0           $str =~ s/>/>/g;
258 0           $str =~ s/</
259 0           $str =~ s/&/&/g;
260 0           $str =~ s/"/\"/g;
261 0           $str =~ s/<.*?>//g;
262 0           $str =~ s/\n\n/\n/g;
263 0           return $str;
264             }
265              
266              
267             sub _debug {
268              
269 0     0     my $msg = shift;
270            
271 0 0         warn $msg if $debug;
272              
273             }
274              
275             1;
276              
277             =back
278              
279             =head1 ADDING FETCHERS
280              
281             If there's a lyrics site you'd like to see supported, raise a request as a
282             wishlist item on http://rt.cpan.org/NoAuth/Bugs.html?Dist=Lyrics-Fetcher or
283             mail me direct: davidp@preshweb.co.uk and, if I have time, I'll whip up a
284             fetcher. Or, feel free to code it up yourself and send it to me (or upload
285             it to CPAN yourself) if you want to be really helpful ;)
286              
287              
288             =head1 CONTACT AND COPYRIGHT
289              
290             Copyright 2007-2010 David Precious (CPAN Id: BIGPRESH)
291              
292             All comments / suggestions / bug reports gratefully received (ideally use the
293             RT installation at http://rt.cpan.org/ but mail me direct if you prefer)
294              
295             Developed on Github at http://github.com/bigpresh/Lyrics-Fetcher
296              
297              
298             Previously:
299             Copyright 2003 Sir Reflog .
300             Copyright 2003 Zachary P. Landau
301              
302              
303             =head1 LICENSE
304              
305             All rights reserved. This program is free software; you can redistribute it
306             and/or modify it under the same terms as Perl itself.
307              
308             =cut