File Coverage

blib/lib/LyricFinder.pm
Criterion Covered Total %
statement 30 141 21.2
branch 0 48 0.0
condition 0 48 0.0
subroutine 10 15 66.6
pod 4 4 100.0
total 44 256 17.1


line stmt bran cond sub pod time code
1             package LyricFinder;
2              
3             require 5.001;
4              
5 1     1   77465 use strict;
  1         3  
  1         34  
6 1     1   6 use warnings;
  1         2  
  1         32  
7 1     1   4 use Carp;
  1         3  
  1         61  
8 1     1   519 use parent 'LyricFinder::_Class';
  1         370  
  1         6  
9              
10             # LyricFinder - A Derived work, by (c) 2020 Jim Turner of:
11             #
12             # Lyrics Fetcher
13             #
14             # Copyright (C) 2007-2020 David Precious (CPAN: BIGPRESH)
15             #
16             # Originally authored by and copyright (C) 2003 Sir Reflog
17             # who kindly passed maintainership on to David Precious in Feb 2007
18             #
19             # Original idea:
20             # Copyright (C) 2003 Zachary P. Landau
21             # All rights reserved.
22             #
23             # This program is free software; you can redistribute it and/or modify
24             # it under the terms of the GNU General Public License as published by
25             # the Free Software Foundation; either version 2 of the License, or
26             # (at your option) any later version.
27             #
28             # This program is distributed in the hope that it will be useful,
29             # but WITHOUT ANY WARRANTY; without even the implied warranty of
30             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31             # GNU General Public License for more details.
32             #
33             # You should have received a copy of the GNU General Public License
34             # along with this program; if not, write to the Free Software
35             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
36              
37             our $VERSION = '1.21';
38             our $DEBUG = 0; # If you want debug messages, set debug to a true value
39              
40             my @supported_mods = (qw(Cache ApiLyricsOvh AZLyrics Genius Letras Musixmatch));
41              
42             my %haveit;
43              
44             foreach my $module (@supported_mods)
45             {
46             $haveit{$module} = 0;
47 1     1   857 eval "use LyricFinder::$module; \$haveit{$module} = 1; 1";
  1     1   3  
  1     1   45  
  1     1   556  
  1     1   4  
  1     1   44  
  1         543  
  1         4  
  1         32  
  1         513  
  1         4  
  1         30  
  1         537  
  1         3  
  1         32  
  1         545  
  1         15  
  1         33  
48             }
49              
50             sub new
51             {
52 0     0 1   my $class = shift;
53              
54             #EXTRACT ANY MAIN-SPECIFIC ARGUMENTS (NOT TO BE PASSED TO SUBMODULES):
55 0           my @args = ();
56 0           while (@_) {
57 0           my $arg = shift(@_);
58 0 0         if ($arg =~ /^\-omit$/o) { #ALLOW USER TO OMIT SPECIFIC INSTALLED SUBMODULE(S):
59 0           my $omit = shift(@_);
60 0 0         my @omitModules = ref($omit) ? @{$omit} : split(/\,\s*/, $omit);
  0            
61 0           foreach my $omit (@omitModules)
62             {
63 0 0 0       $haveit{$omit} = 0 if (defined($haveit{$omit}) && $haveit{$omit});
64             }
65             } else {
66 0           push @args, $arg;
67             }
68             }
69              
70 0           my $self = $class->SUPER::new('', @args);
71             # @{$self->{'_fetchers'}} = @FETCHERS;
72 0           @{$self->{'_fetchers'}} = ();
  0            
73 0           @{$self->{'_FETCHERS'}} = ();
  0            
74             #NOTE: UPPER CASE _FETCHERS USED FOR "random" AND "all", & *NEVER* INCLUDES CACHE (1ST SUBMODULE TRIES CACHE)!:
75             #LOWER CASE _fetchers INCLUDES CACHE FIRST IF CACHE DIRECTORY AND IT'S NOT WRITEONLY, AS THIS IS FOR ORDER/TRIED LIST!
76 0           foreach my $module (@supported_mods)
77             {
78 0 0 0       next unless ($haveit{$module} && $module ne 'Cache');
79 0           push @{$self->{'_FETCHERS'}}, $module;
  0            
80 0           push @{$self->{'_fetchers'}}, $module;
  0            
81             }
82            
83 0           unshift(@{$self->{'_fetchers'}}, 'Cache') if ($haveit{'Cache'}
84 0 0 0       && $self->{'-cache'} && $self->{'-cache'} !~ /^\>/);
      0        
85              
86 0           bless $self, $class; #BLESS IT!
87              
88 0           return $self;
89             }
90              
91             sub order {
92 0     0 1   my $self = shift;
93 0 0         return wantarray ? split(/\,/, $self->{'Order'}) : $self->{'Order'};
94             }
95              
96             sub tried {
97 0     0 1   my $self = shift;
98 0 0         return wantarray ? split(/\,/, $self->{'Tried'}) : $self->{'Tried'};
99             }
100              
101             sub _fetch {
102 0     0     my ($self, $artist, $title, $fetchers) = @_;
103              
104 0           $self->_debug("LyricFinder::_fetch($artist, $title, $fetchers)!");
105 0 0 0       if (!$artist || !$title || ref $artist || ref $title) {
      0        
      0        
106 0           carp("e:_fetch() called without artist and title.");
107 0           return;
108             }
109              
110 0 0 0       if (!$fetchers || ref $fetchers ne 'ARRAY') {
111 0           carp("e:_fetch not given arrayref of fetchers to try");
112 0           return;
113             }
114              
115 0           for my $fetcher (@$fetchers) {
116 0           $self->{'Url'} = '';
117 0           $self->_debug("..Trying fetcher $fetcher for artist:$artist title:$title");
118              
119 0           my $fetcherpkg = __PACKAGE__ . "::$fetcher";
120 0           my $finderModule = 0;
121 0           eval "\$finderModule = new ${fetcherpkg}(\%{\$self});";
122 0 0 0       if ($@ || !$finderModule) {
123 0           carp("w:Failed to load sub-module $fetcherpkg ($@)");
124 0           next;
125             }
126            
127             # OK, we require()d this fetcher, try using it:
128 0           $self->{'Error'} = 'Ok';
129 0           $self->_debug("..Source module $fetcher loaded OK");
130 0           $self->{'Tried'} .= "$fetcher,";
131 0 0         if (!$finderModule->can('fetch')) {
132 0           $self->_debug("e:Source LyricFinder::$fetcher can't ->fetch($finderModule->{'Error'})");
133 0           next;
134             }
135            
136 0           $self->_debug("..Trying to fetch with $fetcher");
137 0           my $lyrics = $finderModule->fetch($artist, $title);
138 0           $self->{'Error'} = $finderModule->message();
139 0           $self->{'Url'} = $finderModule->url();
140 0 0         if ($self->{'Error'} eq 'Ok') {
141 0           $self->_debug("..Source: $fetcher returned lyrics");
142 0 0 0       if (defined($lyrics) && $lyrics =~ /\S/o) {
143 0           $self->{'Source'} = $finderModule->source();;
144 0           $self->{'Site'} = $finderModule->site();
145 0           $self->{'image_url'} = $finderModule->image_url();
146 0           @{$self->{'Credits'}} = $finderModule->credits();
  0            
147 0           $self->{'Tried'} =~ s/\,$//;
148 0           $self->_debug("i:Lyrics fetched from: ".$self->{'Source'});
149              
150 0           return $lyrics;
151             }
152             }
153             }
154              
155             # if we get here, we tried all sites we were asked to try, and none
156             # of them worked.
157 0 0         $self->{'Error'} = 'e:All sites failed to fetch lyrics!' if ($#{$fetchers} > 0);
  0            
158 0           $self->{'Tried'} =~ s/\,$//;
159            
160 0           return undef;
161             }
162              
163             sub fetch {
164 0     0 1   my ($self, $artist, $title, $fetcherspec, $limit) = @_;
165              
166 0           my @tryfetchers = ();
167              
168 0           $self->_debug("LyricFinder::fetch($artist, $title, $fetcherspec)!");
169 0           $self->{'Tried'} = '';
170              
171 0 0 0       $fetcherspec = 'random' unless (defined($fetcherspec) && $fetcherspec);
172 0           $self->{'Source'} = 'none';
173 0 0 0       if ( $fetcherspec && !ref $fetcherspec && $fetcherspec !~ m'^auto$'i) {
    0 0        
174             # we've been given a specific fetcher to use:
175 0 0 0       if (grep /$fetcherspec/, @{$self->{'_FETCHERS'}}) {
  0 0          
    0          
    0          
176 0           push @tryfetchers, $fetcherspec;
177             } elsif ($fetcherspec =~ m'^random$'i) {
178 0           my $random_fetcher;
179 0           my %usedSources = ();
180 0           my $usedcnt = 0;
181 0           while ($usedcnt <= $#{$self->{'_FETCHERS'}}) {
  0            
182 0           $random_fetcher = int(rand(scalar @{$self->{'_FETCHERS'}}));
  0            
183 0 0         unless ($usedSources{${$self->{'_FETCHERS'}}[$random_fetcher]}) {
  0            
184 0           push @tryfetchers, ${$self->{'_FETCHERS'}}[$random_fetcher];
  0            
185 0           $usedSources{${$self->{'_FETCHERS'}}[$random_fetcher]} = 1;
  0            
186 0           $usedcnt++;
187             }
188             }
189             } elsif ($fetcherspec =~ m'^Cache$'i && $haveit{'Cache'}) {
190 0           @tryfetchers = ('Cache');
191             } elsif ($fetcherspec =~ m'^All$'i) {
192 0           push @tryfetchers, @{$self->{'_FETCHERS'}};
  0            
193             } else {
194 0           carp($self->{'Error'} = "s:Source (module) $fetcherspec isn't installed or is invalid!");
195 0           return;
196             }
197             } elsif (ref $fetcherspec eq 'ARRAY') {
198             # we've got an arrayref of fetchers to use:
199 0           for my $fetcher (@$fetcherspec) {
200 0 0         if (grep /$fetcher/, @{$self->{'_FETCHERS'}}) {
  0            
201 0           push @tryfetchers, $fetcher;
202             } else {
203 0           carp("e:$fetcher isn't a valid fetcher, ignoring");
204             }
205             }
206             } else { #really shouldn't end up here (since default=random now), but leaving in for now.
207             # OK, try all available fetchers.
208 0           push @tryfetchers, @{$self->{'_FETCHERS'}};
  0            
209             }
210 0           $self->{'Order'} = join(',', @tryfetchers);
211 0 0 0       $#tryfetchers = $limit - 1 if (defined($limit) && $limit > 0 && $limit < scalar(@tryfetchers));
      0        
212              
213 0           return $self->_fetch($artist, $title, \@tryfetchers);
214             } # end of sub fetch.
215              
216             1
217              
218             __END__