PDA

View Full Version : Use of uninitialized value...


rider4life4889
06-30-2008, 06:36 PM
I'm trying to run this perl code, but I keep getting some errors and I'm having trouble debugging. When I run this, it seems to work, and then it tells me "use of uninitialized value $total in length at leechm3u.pl line 95, <URLS> line 3. Use of uninitialized value $total in division (/) at leechm3u.pl line 96, <URLS> line 3. [err] Can't save: Invalid argument"

I'm new to perl, but I understand programming concepts, and it looks as if $total is being locally initialized in the function progress_bar. Does anyone have any ideas on why this isn't working, or what I can do to the code to make it work correctly?

Thanks so much.

#!/usr/bin/perl -w
#
# LeechM3U - save mp3s listed in an .m3u file, smartly.
# Part of the Leecharoo suite - for all those hard to leech places.
# http://disobey.com/d/code/ or contact morbus.disobey.com.
#
# This code is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself
#

use strict; $|++;
my $VERSION = "1.0";
use File::Spec::Functions;

# make sure we have the modules we need, else die peacefully.
eval("use LWP 5.6.9;"); die "[err] LWP 5.6.9 or greater required.\n" if $@;
eval("use URI::Escape;"); die "[err] URI::Escape is not installed.\n" if $@;

my $dir = "mp3s"; # save downloads to...?
mkdir $dir; # make sure that dir exists.
my $mp3_data; # final holder of our MP3.
my $total_size; # total size of the MP3.

# loop through each M3U file.
foreach my $file (@ARGV) {

# open the passed M3U file or move onto the next.
open(URLS, "<$file") or print "[err] Could not open $file: $!\n";

# for each line.
while (<URLS>){
next if /^#/; # skip if it's a comment/
chomp; # remove trailing newline.
my $url = $_; # more semantic, yes?

# split the URL into parts, defined by the "/" delimeter
# in the URL. we'll use this to determine the name of
# the file, as well as its parent directory. in most
# cases, the parent directory is the album name.
my @parts = split(/\//, $url);

# properly encoded URLs are decimal encoded, with %20
# representing a space, etc. without conversion, our
# files would be named like that. we clean these up.
foreach (@parts) { $_ = uri_unescape($_); }

# take the second-to-last part, which is the parent
# directory of our file. we're assuming an album name.
my $album_dir = $parts[$#parts-1];

#create an OS-specific path to our album and file.
my $album_path = catdir($dir, $album_dir);
my $file_name = $parts[$#parts]; # prettier.
my $file_path = catfile($album_path, $file_name);
mkdir $album_path; # to prepare for dumping.

# get the size of the MP3 for our progress bar.
# some sites block Perl User-Agents, so we fakir.
print "Downloading \"$file_path\"...\n";
my $ua = LWP::UserAgent->new(agent => 'Mozilla/4.76 [en] (Win98; U)');
$total_size = $ua->head($url)->headers->content_length;

# only download the file if it hasn't been before.
if (-e $file_path and (stat($file_path))[7] == $total_size) {
print " Skipping - this file has already been downloaded.\n";
next;
}

# download the file with a callback for progress.
$ua->get($url, ':content_cb' => \&callback);

# with the data downloaded into $mp3_data with our
# callback, save that information to our $file_path.
# (note: bad grammer so word wrapping won't happen)
open (MP3, ">$file_path") or die "[err] Can't save: $!\n";
print MP3 $mp3_data; close(MP3); $mp3_data = undef;
}

# next file!
close(URLS);
}

# per chunk.
sub callback {
my ($data, $response, $protocol) = @_;
$mp3_data .= $data; # append to existing data.
print progress_bar( length($mp3_data), $total_size, 25, '=' );
}

# wget-style. routine by tachyon
# at http://tachyon.perlmonk.org/
sub progress_bar {
my ( $got, $total, $width, $char ) = @_;
$width ||= 25; $char ||= '=';
my $num_width = length $total;
sprintf "|%-${width}s| Got %${num_width}s bytes of %s (%.2f%%)\r",
$char x (($width-1)*$got/$total). '>',
$got, $total, 100*$got/+$total;
}

bazz
06-30-2008, 07:21 PM
In sub callback, you call sub progress_bar but you don't add any values.
You say:

print progress_bar ( length(mp3_data), $total_size, 25, '=' );
Where is $total having its value initialised?

In the progress_bar sub, make a second line to see what values the sub is getting.

print qq(
g=$got<br />
t=$total<br />
w=$width<br />
c=$char
);

I think you'll find that the others would throw an error too but, you have defined them in the progress_bar sub, second line anyway.

bazz

rider4life4889
06-30-2008, 08:06 PM
my $num_width = length $total;
Hmm, $total isn't being defined right there? If not, how would I initialize it?

In sub callback, you call sub progress_bar but you don't add any values.
You say:


print progress_bar ( length($mp3_data), $total_size, 25, '=' );
I thought those were the values being called into the sub progress_bar method, because they are determined at run time.

When I tried adding in those lines, all I got was another error that said useless use of sprintf in void context.

rider4life4889
06-30-2008, 09:04 PM
So I just changed
$total_size = $ua->head($url)->headers->content_length;
to
$total_size = $ua->head($url)->headers->content_length || 0;

but I still get the [err] Can't save: invalid argument error. Any ideas?

Thanks again.

bazz
06-30-2008, 09:35 PM
I am curious why you send values to the sub and then immediately change them before using them even once.



sub progress_bar {
my ( $got, $total, $width, $char ) = @_; #you have presumably got the values here
$width ||= 25; $char ||= '='; # yet you change them here.
my $num_width = length $total;
sprintf "|%-${width}s| Got %${num_width}s bytes of %s (%.2f%%)\r",
$char x (($width-1)*$got/$total). '>',
$got, $total, 100*$got/+$total;
}



bazz

rider4life4889
06-30-2008, 09:48 PM
Is that irrelevant now, though? Now that I changed that one line I'm not getting the error with that part. I'm getting the error Can't save: Invalid Argument in the line
open (MP3, ">$file_path") or die "[err] Can't save: $!\n";
I don't get why, though.

eeijlar
06-30-2008, 10:12 PM
You are passing $total_size into sub progress_bar. This might not have a value. You should initialise it to $total_size=0; just after you declare it to cover that case.

FishMonger
06-30-2008, 10:12 PM
That script has a number of problems that I'll point out latter. For now, you need to add 2 lines, one just before that open statement which will output the value of $file_path, the other loads the Data::Dumper module

use Data::Dumper # add this at the top, with the other use statements.

print Dumper $file_path; # this goes just before or just after the open statement that generates the error.

rider4life4889
06-30-2008, 10:30 PM
Thank you all. I tried adding in those lines, FishMonger, where you said to, but I am still getting the same Can't save: Invalid Argument error. Here is the updated code I am working off of right now...

#!/usr/bin/perl -w
#
# LeechM3U - save mp3s listed in an .m3u file, smartly.
# Part of the Leecharoo suite - for all those hard to leech places.
# http://disobey.com/d/code/ or contact morbus.disobey.com.
#
# This code is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself
#

use strict; $|++;
my $VERSION = "1.0";
use File::Spec::Functions;
use Data::Dumper;

# make sure we have the modules we need, else die peacefully.
eval("use LWP 5.6.9;"); die "[err] LWP 5.6.9 or greater required.\n" if $@;
eval("use URI::Escape;"); die "[err] URI::Escape is not installed.\n" if $@;

my $dir = "mp3s"; # save downloads to...?
mkdir $dir; # make sure that dir exists.
my $mp3_data; # final holder of our MP3.
my $total_size; # total size of the MP3.

# loop through each M3U file.
foreach my $file (@ARGV) {

# open the passed M3U file or move onto the next.
open(URLS, "<$file") or print "[err] Could not open $file: $!\n";

# for each line.
while (<URLS>){
next if /^#/; # skip if it's a comment/
chomp; # remove trailing newline.
my $url = $_; # more semantic, yes?

# split the URL into parts, defined by the "/" delimeter
# in the URL. we'll use this to determine the name of
# the file, as well as its parent directory. in most
# cases, the parent directory is the album name.
my @parts = split(/\//, $url);

# properly encoded URLs are decimal encoded, with %20
# representing a space, etc. without conversion, our
# files would be named like that. we clean these up.
foreach (@parts) { $_ =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; }

# take the second-to-last part, which is the parent
# directory of our file. we're assuming an album name.
my $album_dir = $parts[$#parts-1];

#create an OS-specific path to our album and file.
my $album_path = catdir($dir, $album_dir);
my $file_name = $parts[$#parts]; # prettier.
my $file_path = catfile($album_path, $file_name);
mkdir $album_path; # to prepare for dumping.

# get the size of the MP3 for our progress bar.
# some sites block Perl User-Agents, so we fakir.
print "Downloading \"$file_path\"...\n";
my $ua = LWP::UserAgent->new(agent => 'Mozilla/4.76 [en] (Win98; U)');
$total_size = $ua->head($url)->headers->content_length || 0;

# only download the file if it hasn't been before.
if (-e $file_path and (stat($file_path))[7] == $total_size) {
print " Skipping - this file has already been downloaded.\n";
next;
}

# download the file with a callback for progress.
$ua->get($url, ':content_cb' => \&callback);

# with the data downloaded into $mp3_data with our
# callback, save that information to our $file_path.
# (note: bad grammer so word wrapping won't happen)
open (MP3, ">$file_path") or die "[err] Can't save: $!\n";
print Dumper $file_path;
print MP3 $mp3_data; close(MP3); $mp3_data = undef;
}

# next file!
close(URLS);
}

# per chunk.
sub callback {
my ($data, $response, $protocol) = @_;
$mp3_data .= $data; # append to existing data.
print progress_bar( length($mp3_data), $total_size, 25, '=' );
}

# wget-style. routine by tachyon
# at http://tachyon.perlmonk.org/
sub progress_bar {
my ( $got, $total, $width, $char ) = @_;
$width ||= 25; $char ||= '=';
my $num_width = length $total;
sprintf "|%-${width}s| Got %${num_width}s bytes of %s (%.2f%%)\r",
$char x (($width-1)*$got/$total). '>',
$got, $total, 100*$got/+$total;
}



Any help on this error would be greatly appreciated. Thanks again everyone.

FishMonger
06-30-2008, 11:13 PM
The lines I had you add were not supposed to fix the problem, but they should help narrow down the problem.

Please post the exact and complete error message and the value of $file_path when the open call fails.

KevinADC
07-01-2008, 05:29 AM
this does not change values:

$width ||= 25; $char ||= '='; # yet you change them here.

it assigns a value to the scalar if it does not have one. Its the same as:

$width = $width || 25;

The "invalid argument" error is very strange. He has used the correct arguments for the open() function.

open (MP3, ">$file_path") or die "[err] Can't save: $!\n";

It will help to see what $file_path is, if it contains some type of control character it might be causing a problem.

rider4life4889
07-01-2008, 02:54 PM
I have attached a screenshot of what happens when I try running it.

KevinADC
07-01-2008, 03:54 PM
The "invalid argument" appears to be coming from Windows and not perl.

Some of the characters in the filename might be illegal to use with Windows. You may need to add a routine to strip out some of the characters before using them as a filename, such as ? = ().

KevinADC
07-02-2008, 10:04 AM
I have attached a screenshot of what happens when I try running it.

Did you find out what the problem is?

nkrgupta
07-02-2008, 12:10 PM
I have attached a screenshot of what happens when I try running it.

In the screenshot, I can see the value of $file being out as mp3s\\Supernatural\\01 (De Le) Yaleo.mp3?stream=1

As Kevin suggested above, you need to strip out the invalid character from this fielname, which in this case is ?. () and = are valid filename characters on Win XP. But you'll be better off stripping everything that comes after .mp3

HTH
Naveen