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__ |