File Coverage

blib/lib/WWW/Bleep.pm
Criterion Covered Total %
statement 71 144 49.3
branch 12 46 26.0
condition 7 46 15.2
subroutine 12 12 100.0
pod 5 5 100.0
total 107 253 42.2


line stmt bran cond sub pod time code
1             package WWW::Bleep;
2              
3 1     1   40658 use 5.008;
  1         5  
  1         46  
4 1     1   5 use strict;
  1         2  
  1         36  
5 1     1   7 use warnings;
  1         5  
  1         37  
6 1     1   4513 use LWP::UserAgent;
  1         129523  
  1         31  
7 1     1   840 use HTML::TokeParser;
  1         14123  
  1         40  
8              
9             require Exporter;
10 1     1   8 use vars qw( @ISA @EXPORT @EXPORT_OK $VERSION );
  1         2  
  1         1910  
11              
12             @EXPORT = qw(error album artists tracks);
13             @EXPORT_OK = qw();
14             @ISA = qw(Exporter);
15             $VERSION = '0.92';
16              
17             sub new($);
18             sub album($);
19             sub artists($$);
20             sub tracks($$);
21             sub error($);
22             sub _cleanurldata($);
23              
24             =head1 NAME
25              
26             WWW::Bleep - Perl interface to Bleep.com
27              
28             =head1 VERSION
29              
30             Version 0.92
31              
32             =head1 SYNOPSIS
33              
34             use WWW::Bleep;
35              
36             my $bleep = WWW::Bleep->new();
37              
38             my @tracks = WWW::Bleep->tracks( artist => 'Aphex Twin' );
39              
40             =head1 DESCRIPTION
41              
42              
43             A Perl interface to Bleep.com. Specfically for searching artist,
44             album, and label data. Current purpose is the help with cataloging
45             your personal (physical) album collection.
46              
47             This has no shopping cart capability and it isn't planned.
48             (Less there is some dire need for it.)
49              
50              
51             =head1 FUNCTIONS
52              
53             =head2 new
54              
55             Create a new WWW::Bleep object.
56              
57             Currently has no arguments.
58              
59             =cut
60              
61             sub new($) {
62 1     1 1 12 my $class = shift;
63 1         10 my $self = bless {
64             _base_url => 'http://www.bleep.com/',
65             _ua => LWP::UserAgent->new(),
66             _parser => '',
67             _response => '',
68             _error => '',
69             }, $class;
70 1         4449 $self->{'_ua'}->agent('');#"WWW::Bleep v$VERSION");
71 1         55 return $self;
72             }
73              
74              
75             =head2 error
76              
77             Returns an error description or ''.
78              
79             Doesn't take or require any arguments.
80              
81             For the sake of your own sanity, check this ne '' after every call.
82             The most you'd generally get from other routines as far as errors are
83             concerned would be a null response.
84              
85             =cut
86              
87             sub error($) {
88 3     3 1 8 my $self = shift;
89 3         18 return $self->{_error};
90             }
91              
92              
93             =head2 album
94              
95             Gathers album data based on the arguments given.
96              
97             Requires one of the following arguments:
98              
99             cat
100             (eventually title)
101              
102             Returns a hash containing relevant album and track data.
103             Specifically artist, date, label, title, and tracks. artist,
104             date, label, and title are all scalars. date may always be
105             returned.
106              
107             tracks contains the following array
108              
109             track_number => {
110             'time' => length_in_standard_time,
111             'title' => track_title,
112             'valid' => 1_=_downloadable__0_=_not
113             }
114             next_track_number => {
115             'time' => length_in_standard_time,
116             'title' => track_title,
117             'valid' => 1_=_downloadable__0_=_not
118             }
119             ...
120              
121             =cut
122              
123             sub album($) {
124 1     1 1 4 my $self = shift;
125 1         5 my %args = @_;
126              
127 1         5 my $album_url = $self->{'_base_url'}.'current_item.php';
128 1         2 my $title;
129             my $number;
130 0         0 my %album;
131 0         0 my $slimcat;
132 0         0 my $token;
133              
134 1 50       6 if ( length($args{'cat'}) > 3 ) {
135 1         4 $args{'cat'} = uc($args{'cat'});
136 1 50       6 if ( $args{'cat'} !~ /_DM$/ ) {
137 1         2 $args{'cat'} .= '_DM';
138             }
139 1         4 $slimcat = substr($args{'cat'},0,length($args{'cat'})-3);
140 1         3 $album_url .= ('?selection='.$args{'cat'});
141 1         6 $self->{_response} = $self->{_ua}->get( $album_url );
142 1 50       200812 if ( $self->{_response}->is_success ){
143 0         0 $self->{_parser} = HTML::TokeParser->new(
144             \$self->{_response}->content
145             );
146 0         0 while ( $token = $self->{_parser}->get_tag('div') ){
147 0 0 0     0 if ($token->[1]{class} &&
148             $token->[1]{class} eq 'bleep2selectionTitle') {
149 0         0 while ( $token = $self->{_parser}->get_token() ){
150              
151             # Set album artist
152 0 0 0     0 if ( !$album{artist} && $token->[3][0] &&
      0        
153             $token->[3][0] eq 'href' ) {
154 0 0       0 if ( $token->[2]{href} =~ /^search\.php/ ){
155 0         0 $token = $self->{_parser}->get_token();
156 0         0 $album{artist} = $token->[1];
157             }
158             }
159              
160             # Set album title and date if applicable
161 0 0 0     0 if ( !$album{title} &&
162             $token->[1] =~ /^(.+) \($slimcat\)$/ ){
163 0         0 $album{title} = $1;
164 0         0 $self->{_parser}->get_token();
165 0         0 $self->{_parser}->get_token();
166 0         0 $self->{_parser}->get_token();
167 0         0 $self->{_parser}->get_token();
168 0         0 $token = $self->{_parser}->get_token();
169 0         0 $album{label} = $token->[1];
170 0         0 $self->{_parser}->get_token();
171 0         0 $self->{_parser}->get_token();
172 0         0 $token = $self->{_parser}->get_token();
173 0 0 0     0 if ( $token->[0] eq 'T' &&
174             $token->[1] =~ /(\d{1,2}) \/ (\d{1,4})/ ){
175 0         0 $album{date} = "$1/$2";
176             }
177             }
178              
179 0 0 0     0 if ( $token->[0] eq 'S' && $token->[1] eq 'td'&&
      0        
      0        
180             $token->[2]{width} &&
181             $token->[2]{width} eq '24' ){
182              
183 0         0 $token = $self->{_parser}->get_token();
184 0         0 $token->[1] =~ /(\d\d)/;
185 0         0 $number = $1;
186              
187             # There's got to be a better way to skip
188             # ahead tokens!
189 0         0 $self->{_parser}->get_token();
190 0         0 $self->{_parser}->get_token();
191 0         0 $self->{_parser}->get_token();
192 0         0 $token = $self->{_parser}->get_token();
193              
194 0         0 $token->[1] =~ /(.+) \((\d{1,2}:\d\d)\)/;
195 0 0       0 if ( $2 ){
196 0         0 $album{tracks}->{$number}{title} = $1;
197 0         0 $album{tracks}->{$number}{time} = $2;
198            
199             # Is the track buyable?
200 0         0 $album{tracks}->{$number}{valid} = 1;
201             }
202             else{
203 0         0 $album{tracks}->{$number}{valid} = 0;
204             }
205             }
206             }
207             }
208             }
209 0         0 return %album;
210             }
211              
212             # Page could not be loaded!
213             else {
214 1         15 $self->{_error} = $self->{_response}->status_line;
215 1         23 return 0;
216             }
217             }
218             else {
219 0         0 $self->{_error} = qq(Please use a catalog value with four or more characters.);
220 0         0 return 0;
221             }
222             }
223              
224              
225             =head2 artists
226              
227             Returns an array of artists from Bleep.com. An optional argument
228             must be a valid record label name.
229              
230             # Returns all artists (Be careful with this one,
231             # the list is very large!)
232             @artists = $bleep->artists();
233              
234             # Returns only artists on Warp
235             @artists = $bleep->artists( 'Warp' );
236            
237             # Returns null (not a valid record label)
238             @artists = $bleep->artists( 'foo1234' );
239              
240             Due to the size of the artist list, it may take a minute to
241             populate.
242              
243             =cut
244              
245             sub artists($$) {
246 1     1 1 3 my $self = shift;
247 1         6 my %args = @_;
248 1         4 undef $self->{artists};
249              
250 1         5 my $artists_url = $self->{'_base_url'}.'browse_artists.php?label=';
251 1         7 $artists_url .= _cleanurldata( uc($args{'label'}) );
252              
253 1         9 $self->{_response} = $self->{_ua}->get( $artists_url );
254              
255 1 50       37088 if ( $self->{_response}->is_success ){
256 0         0 $self->{_parser} = HTML::TokeParser->new(\$self->{_response}->content);
257            
258 0         0 while( my $token = $self->{_parser}->get_tag("a") ){
259 0   0     0 my $url = $token->[1]{href} || '-';
260 0         0 my $text = $self->{_parser}->get_trimmed_text("/a");
261 0 0       0 unless ( $url eq 'javascript:void(0);' ){
262 0         0 push @{$self->{_artists}}, $text;
  0         0  
263             }
264             }
265 0 0       0 if( $self->{_artists} ){
266 0         0 return @{$self->{_artists}};
  0         0  
267             }
268             else {
269 0         0 $self->{error} = qq(No artists could be found!);
270             }
271             }
272             else {
273 1         13 $self->{error} = qq(No response from url!\nDo you have an active internet connection and is bleep.com up?);
274 1         12 return 0;
275             }
276             }
277              
278              
279             =head2 tracks
280              
281             Gathers the tracks based on the arguments given.
282              
283             Requires one or of the following arguments:
284              
285             artist
286             label
287             album*
288              
289             Returns an array of hashes of track names, track numbers and album
290             catalog numbers. *Currently, 'album' dies off, as it uses a
291             different method that hasn't been configured.
292              
293             =cut
294              
295             sub tracks($$) {
296 1     1 1 2 my $self = shift;
297 1         4 my %args = @_;
298 1         4 my $tracks_url = $self->{'_base_url'}.'browse_results.php?';
299              
300 1         4 undef $self->{_tracks};
301 1         3 undef $self->{hastracks};
302              
303 1 50 33     11 if ($args{artist} || $args{label} || $args{album}) {
      33        
304 1 50       5 if ($args{artist}) {
305 1         5 $tracks_url .= ('artist='._cleanurldata($args{artist}).'&');
306             }
307 1 50       4 if ($args{label}){
308 0         0 $tracks_url .= ('label='._cleanurldata($args{label}).'&');
309             }
310 1 50       65 if ($args{album}) {
311 0         0 die "Option \"album\" not yet supported!";
312             }
313              
314 1         10 $self->{_response} = $self->{_ua}->get( $tracks_url );
315              
316 1 50       37733 if ($self->{_response}->is_success) {
317              
318 0         0 $self->{_parser} = HTML::TokeParser->new(\$self->{_response}->content);
319            
320             # Move the offset so it does not include albums
321 0         0 while (my $token = $self->{_parser}->get_tag('td')) {
322 0 0       0 if ($self->{_parser}->get_trimmed_text('/td') eq 'TRACKS') {
323 0         0 while (my $token = $self->{_parser}->get_tag("a")) {
324 0   0     0 my $url = $token->[1]{href} || '-';
325 0         0 my $text = $self->{_parser}->get_trimmed_text("/a");
326 0 0 0     0 unless ( !$url || $url eq 'javascript:void(0);' ){
327 0         0 $url =~ /\?id=(\w+)-(\d\d)/;
328 0         0 push @{$self->{_tracks}}, {title=>$text,cat=>$1,number=>$2};
  0         0  
329             }
330             }
331 0 0       0 if ($self->{_tracks}) {
332 0         0 return @{$self->{_tracks}};
  0         0  
333             }
334             else {
335 0         0 $self->{error} = qq(Artist exists, but no tracks can be found... Sorry!);
336 0         0 return 0;
337             }
338             }
339             }
340             }
341             else {
342 1         12 $self->{error} = qq(No response from url! Do you have an active internet connection and is bleep.com up?);
343 1         10 return 0;
344             }
345             }
346             else {
347 0         0 $self->{error} = qq(This function requires one or more arguments!);
348 0         0 return 0;
349             }
350 0         0 1;
351             }
352              
353              
354              
355             # Internal routine to fix any obscure characters
356             sub _cleanurldata($) {
357 2 50   2   7 if ($_[0]) {
358 2         29 my @data = split //, shift;
359 2         5 my $val;
360              
361 2         4 foreach my $char (@data) {
362 14         17 $val = ord($char);
363 14 100 33     182 if ($val < 48 || ($val > 57 && $val < 65) || $val > 90) {
      66        
      66        
364 8         21 $val = unpack('H*',chr($val));
365 8         12 $char = ('%'.$val);
366             }
367             }
368              
369 2         6 $val = join '', @data;
370 2         10 return $val;
371             }
372             else {
373 0           return $_[0];
374             }
375             }
376              
377             =head1 SEE ALSO
378              
379             L
380              
381             =head1 AUTHOR
382              
383             Clif Bratcher, Esnevine@cpan.orgE
384              
385             =head1 COPYRIGHT AND LICENSE
386              
387              
388             Copyright (C) 2006 - 2009 by Clif Bratcher
389              
390             This library is free software; you can redistribute it and/or modify
391             it under the same terms as Perl itself, either Perl version 5.8.8 or,
392             at your option, any later version of Perl 5 you may have available.
393              
394              
395             =cut