[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]> wrote:
: This is basically my 1st perl script. : : It's a podcast download app. I don't know the etiquette for : attaching files, but I have attached a <100 line perl file. : : It seems that it fails if there is only 1 podcast for a feed : with: : : fetching http://www.abc.net.au/science/k2/podcast/drk_rss.xml ... : : Odd number of elements in anonymous hash : at /home/lexhider/bin/GodCast.pl line 65. : fileparse(): need a valid pathname at : /home/lexhider/bin/GodCast.pl line 68 The XML::Simple parser will not assign a single item (<item></item>) to an array reference like it does with many items (unless you specify it to do so). If $data->{channel}->{item} is an array reference, you have many items. If it is a hash reference you have only one item. If the "item" key does not exist, then there are no items. if ( exists $data->{channel}->{item} ) { # 1 or more items. } else { # No items. } We can check the number of items using the perl ref() function. if ( exists $data->{channel}->{item} ) { # 1 or more items. if ( ref $data->{channel}->{item} eq 'HASH' ) { # 1 item } elsif ( ref $data->{channel}->{item} eq 'ARRAY' ) { # More than 1 item. } } else { # No items. } Ideally, we would like XML::Simple to return the following. - No items An array with no items in it. - One item An array with only one item in it. - Many items An array with many items in it. if ( exists $data->{channel}->{item} ) { # 1 or more items. if ( ref $data->{channel}->{item} eq 'HASH' ) { $data->{channel}->{item} = [ $data->{channel}->{item} ]; # 1 item } elsif ( ref $data->{channel}->{item} eq 'ARRAY' ) { # more than 1 item. } } else { # No items. $data->{channel}->{item} = []; } Since the "many items" section doesn't do anything (it's in the form we want), we can eliminate that choice. if ( exists $data->{channel}->{item} ) { if ( ref $data->{channel}->{item} eq 'HASH' ) { # Only 1 item. $data->{channel}->{item} = [ $data->{channel}->{item} ]; } } else { # No items. $data->{channel}->{item} = []; } As it turns out, XML::Simple has an option to force arrays when there is only one item. Now we only need to check on zero items (though that probably shouldn't happen). my $xml = XML::Simple->new(); my $source = get $feed or die qq(Failed to fetch feed: "$feed".) my $data = $xml->XMLin( $source, ForceArray => [ 'item' ] ); # Handle zero items. $data->{channel}->{item} = [] unless exists $data->{channel}->{item}; Now that we always have an item array we can tackle the $latest option. While we can do the testing as you did, it might be easier if we just limit the size of the item array. Then we can step through the array without a test each time. Here's a way to do it using the min() function from List::Util. use List::Util qw( min ); # Get just the array items we want to process. if ( $latest ) { my $item_count = min( $latest, scalar @{ $data->{channel}->{item} } ); @{ $data->{channel}->{item} } = @{ $data->{channel}->{item} }[0 .. $item_count - 1]; } And then the loop. foreach my $item ( @{ $data->{channel}->{item} } ) { my $cast = $item->{enclosure}->{url}; chomp( my $base = basename($cast) ); if ( ! -e "$feed_dir/$base" ) { `wget -c $cast`; move( $base, $feed_dir ); } else { print "On HD: $base.\n"; } } Putting that together we get something like this. (Not tested.) use strict; use warnings; use Getopt::Long; use XML::Simple; # Imported functions use File::Basename qw( basename ); use File::Copy; qw( move ); use List::Util qw( min ); use LWP::Simple qw( get ); my $download_dir = "$ENV{HOME}/Audio/Podcasts"; my $config_dir = "$ENV{HOME}/.GodCast"; # File with url of feeds on each line. my $feeds = "$config_dir/feeds.txt"; # Change tmp if you want semi-downloaded # files to stay after a reboot. my $tmp = "/tmp/Podcasts"; my $latest = 0; my $across; # Not implemented. GetOptions( 'latest|l=s' => \$latest, 'across' => \$across ); foreach my $dir ( $download_dir, $config_dir, $tmp, ) { unless( -d $dir ) { mkdir $dir or die qq(Failed to make dir "$dir": $!); } } die qq(File "$feeds" not readable: $!) unless -r $feeds; open FEEDS, $feeds or die qq(Could not open file: "$feeds": $!); while ( my $feed = <FEEDS> ) { chomp $feed; print "\nfetching $feed ... \n\n"; my $xml = XML::Simple->new(); my $source = get $feed or die qq(Failed to fetch feed: "$feed".); my $data = $xml->XMLin( $source, ForceArray => [ 'item' ] ); # Handle zero items. $data->{channel}->{item} = [] unless exists $data->{channel}->{item}; my $feed_dir = "$download_dir/$data->{channel}->{title}"; my $temp_feed_dir = "$tmp/$data->{channel}->{title}"; foreach my $dir ( $feed_dir, $temp_feed_dir ) { unless (-d $dir) { mkdir $dir or die qq(Failed to make directory "$dir": $!); } } chdir $temp_feed_dir; # Get just the array items we want to process. if ( $latest ) { my $item_count = min( $latest, scalar @{ $data->{channel}->{item} } ); @{ $data->{channel}->{item} } = @{ $data->{channel}->{item} }[0 .. $item_count - 1]; } foreach my $item ( @{ $data->{channel}->{item} } ) { my $cast = $item->{enclosure}->{url}; chomp( my $base = basename($cast) ); if ( ! -e "$feed_dir/$base" ) { `wget -c $cast`; move( $base, $feed_dir ); } else { print "On HD: $base\n"; } } } close FEEDS; __END__ HTH, Charles K. Clarkson -- Mobile Homes Specialist 254 968-8328 -- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] <http://learn.perl.org/> <http://learn.perl.org/first-response>