File Coverage

blib/lib/LyricFinder/_Class.pm
Criterion Covered Total %
statement 15 149 10.0
branch 0 36 0.0
condition 0 15 0.0
subroutine 5 24 20.8
pod 0 13 0.0
total 20 237 8.4


line stmt bran cond sub pod time code
1             package LyricFinder::_Class;
2              
3 1     1   438 use strict;
  1         1  
  1         34  
4 1     1   3 use warnings;
  1         1  
  1         33  
5 1     1   839 use LWP::UserAgent;
  1         95708  
  1         56  
6 1     1   17 use HTTP::Request;
  1         2  
  1         32  
7 1     1   6 use Carp;
  1         2  
  1         2768  
8              
9             our $AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:112.0) Gecko/20100101 Firefox/112.0";
10             our $DEBUG = 0; # If you want debug messages, set debug to a true value, and
11             # messages will be output with warn.
12              
13             sub new
14             {
15 0     0 0   my $class = shift;
16 0           my $source = shift;
17              
18 0           my $self = {};
19              
20 0           $self->{'-debug'} = $DEBUG;
21 0           $self->{'-agent'} = $AGENT;
22 0           $self->{'-cache'} = '';
23 0           $self->{'-synced'} = 'No'; #USER CHOICES ARE: YES(synced|plain), NO(plain), ONLY(synced), or OK|''(plain|synced)!
24 0           $self->{'Error'} = 'Ok';
25 0           $self->{'Source'} = $source;
26 0           $self->{'Site'} = '';
27 0           $self->{'Order'} = '';
28 0           $self->{'Tried'} = '';
29 0           $self->{'Url'} = '';
30 0           $self->{'image_url'} = '';
31 0           $self->{'Credits'} = [];
32              
33             #EXTRACT ANY ARGUMENTS:
34 0           while (@_) {
35 0 0         if ($_[0] =~ /^\-/o) {
36 0           my $key = shift;
37 0 0 0       $self->{$key} = (!defined($_[0]) || $_[0] =~/^\-/) ? 1 : shift;
38 0           next;
39             }
40 0           shift;
41             }
42              
43             #NOW EXTRACT ANY SUBMODULE-SPECIFIC HASH ARGUMENTS (ie. "-Submodule => {args}"):
44 0 0 0       if (defined($self->{"-$source"}) && ref($self->{"-$source"}) =~ /HASH/) {
45 0           my @subarglist = %{$self->{"-$source"}};
  0            
46 0           while (@subarglist) {
47 0 0         if ($subarglist[0] =~ /^\-/o) {
48 0           my $key = shift @subarglist;
49 0 0 0       $self->{$key} = (!defined($subarglist[0]) || $subarglist[0] =~/^\-/) ? 1 : shift(@subarglist);
50 0           next;
51             }
52 0           shift @subarglist;
53             }
54             }
55              
56 0 0 0       $self->{'-debug'} = $DEBUG unless (defined($self->{'-debug'}) && $self->{'-debug'} =~ /^\d$/);
57 0           bless $self, $class; #BLESS IT!
58              
59 0           return $self;
60             }
61              
62             sub _debug {
63 0     0     my $self = shift;
64 0           my $msg = shift;
65            
66 0 0         warn $msg if $self->{'-debug'};
67             }
68              
69             sub sources {
70 0     0 0   my $self = shift;
71 0 0         return wantarray ? @{$self->{'_fetchers'}} : \@{$self->{'_fetchers'}};
  0            
  0            
72             }
73              
74             sub source {
75 0     0 0   my $self = shift;
76 0           return $self->{'Source'};
77             }
78              
79             sub url {
80 0     0 0   my $self = shift;
81 0           return $self->{'Url'};
82             }
83              
84             sub order {
85 0     0 0   my $self = shift;
86 0 0         return wantarray ? ($self->{'Source'}) : $self->{'Source'};
87             }
88              
89             sub tried {
90 0     0 0   return order (@_);
91             }
92              
93             sub credits {
94 0     0 0   my $self = shift;
95 0 0         return wantarray ? @{$self->{'Credits'}} : join(', ', @{$self->{'Credits'}});
  0            
  0            
96             }
97              
98             sub message {
99 0     0 0   my $self = shift;
100 0           return $self->{'Error'};
101             }
102              
103             sub site {
104 0     0 0   my $self = shift;
105 0           return $self->{'Site'};
106             }
107              
108             # Allow user to specify a different user-agent:
109             sub agent {
110 0     0 0   my $self = shift;
111 0 0         if (defined $_[0]) {
112 0           $self->{'-agent'} = $_[0];
113             } else {
114 0           return $self->{'-agent'};
115             }
116             }
117              
118             sub cache {
119 0     0 0   my $self = shift;
120 0 0         if (defined $_[0]) {
121 0           $self->{'-cache'} = $_[0];
122             } else {
123 0           return $self->{'-cache'};
124             }
125             }
126              
127             sub image_url {
128 0     0 0   return shift->{'image_url'};
129             }
130              
131             sub fetch_synced_lyrics {
132 0     0 0   my $self = shift;
133 0 0         if (defined $_[0]) {
134 0           $self->{'-synced'} = $_[0];
135             } else {
136 0           return $self->{'-synced'};
137             }
138             }
139              
140             sub _check_inputs {
141 0     0     my $self = shift;
142              
143 0           my $Source = $self->{'Source'};
144             # reset the error var, change it if an error occurs.
145 0           $self->{'Error'} = 'Ok';
146 0           $self->{'Url'} = '';
147              
148 0 0 0       unless ($_[0] && $_[1]) {
149 0           carp($self->{'Error'} = "e:$Source.fetch() called without artist and song!");
150 0           return 0;
151             }
152 0           return 1;
153             }
154              
155             sub _web_fetch {
156 0     0     my $self = shift;
157              
158 0           $self->_debug($self->{'Source'}.":_web_fetch($_[0], $_[1]): URL=".$self->{'Url'}."=");
159 0           my $ua = LWP::UserAgent->new(
160             ssl_opts => { verify_hostname => 0, },
161             );
162 0           $ua->timeout(10);
163 0           $ua->agent($self->{'-agent'});
164 0           $ua->protocols_allowed(['https', 'http']);
165 0           $ua->cookie_jar( {} );
166 0           push @{ $ua->requests_redirectable }, 'GET';
  0            
167 0           (my $referer = $self->{'Url'}) =~ s{^(\w+)\:\/\/}{};
168 0           my $protocol = $1;
169 0           $referer =~ s{\/.+$}{\/};
170 0           my $host = $referer;
171 0           $host =~ s{\/$}{};
172 0           $referer = $protocol . '://' . $referer;
173 0           my $req = new HTTP::Request 'GET' => $self->{'Url'};
174 0           $req->header(
175             'Accept' =>
176             'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
177             'Accept-Language' => 'en-US,en;q=0.5',
178             'Accept-Encoding' => 'gzip, deflate',
179             'Connection' => 'keep-alive',
180             'Upgrade-insecure-requests' => 1,
181             'Host' => $host,
182             );
183              
184 0           my $res = $ua->request($req);
185              
186 0 0         if ($res->is_success) {
187 0           my $lyrics = $self->_parse($res->decoded_content, @_);
188 0           return $lyrics;
189             } else {
190 0           my $Source = $self->{'Source'};
191 0 0         if ($res->status_line =~ /^404/) {
192 0           $self->{'Error'} = "..$Source - Lyrics not found.";
193             } else {
194 0           carp($self->{'Error'} = "e:$Source - Failed to retrieve ".$self->{'Url'}
195             .' ('.$res->status_line.').');
196             }
197 0           return '';
198             }
199             }
200              
201             sub _remove_accents {
202 0     0     my $self = shift;
203 0           my $str = shift;
204              
205 0           $str =~ tr/\xc4\xc2\xc0\xc1\xc3\xe4\xe2\xe0\xe1\xe3/aaaaaaaaaa/;
206 0           $str =~ tr/\xcb\xca\xc8\xc9\xeb\xea\xe8\xe9/eeeeeeee/;
207 0           $str =~ tr/\xcf\xcc\xef\xec/iiii/;
208 0           $str =~ tr/\xd6\xd4\xd2\xd3\xd5\xf6\xf4\xf2\xf3\xf5/oooooooooo/;
209 0           $str =~ tr/\xdc\x{0016}\xd9\xda\xfc\x{0016}\xf9\xfa/uuuuuuuu/;
210 0           $str =~ tr/\x{0178}\xdd\xff\xfd/yyyy/;
211 0           $str =~ tr/\xd1\xf1/nn/;
212 0           $str =~ tr/\xc7\xe7/cc/;
213 0           $str =~ s/\xdf/ss/g;
214              
215 0           return $str;
216             }
217              
218             # nasty way to strip out HTML
219             sub _html2text {
220 0     0     my $self = shift;
221 0           my $str = shift;
222              
223 0           $str =~ s#\<(?:br|\/?p).*?\>#\n#gio;
224 0           $str =~ s#\>\;#\>#go;
225 0           $str =~ s#\<\;#\<#go;
226 0           $str =~ s#\&\;#\&#go;
227 0           $str =~ s#\"\;#\"#go;
228 0           $str =~ s#\<.*?\>##go;
229              
230 0           return $str;
231             }
232              
233             sub _normalize_lyric_text {
234 0     0     my $self = shift;
235 0           my $str = shift;
236              
237             # normalize Windowsey \r\n sequences:
238 0           $str =~ s/\r+//gs;
239             # strip off pre & post padding with spaces:
240 0           $str =~ s/^ +//mg;
241 0           $str =~ s/ +$//mg;
242             # clear up repeated blank lines:
243 0           $str =~ s/(\R){2,}/\n\n/gs;
244             # and remove any blank top and bottom lines:
245 0           $str =~ s/^\R+//s;
246 0           $str =~ s/\R\R+$/\n/s;
247             # add a linefeed to end of lyrics if ther's not one already:
248 0 0         $str .= "\n" unless ($str =~ /\n$/s);
249             # now fix up for either Windows or Linux/Unix:
250 0 0         $str =~ s/\R/\r\n/gs if ($^O =~ /Win/);
251              
252 0           return $str;
253             }
254              
255             1
256              
257             __END__