#!/usr/bin/perl #$Id: riofill,v 1.2 1999/09/29 08:30:34 root Exp $ # Fill a Diamond Rio MP3 player with a randomly selected # list of MP3 files. # Based largely on Ron Forrester's (rjf@zero-ping.com) riolist code. # Hacked upon mercilessly by Howard Owen (hbo@egbok.com) # because that's what I do for fun. 8) # use Getopt::Long; my $VERSION = 1.1; die "Error in argument parsing" if( !GetOptions( "d=s" => \$opt_d, "f=s" => \$opt_f, "r=s" => \$opt_r, "b=i" => \$opt_b, "x:i" => \$opt_x, "o" => \$opt_o, "v" => \$opt_v, ) ); $MP3DIR=(defined $opt_d)?$opt_d:"."; $RIOCMD=(defined $opt_r)?$opt_r:"/usr/local/bin/rio"; # We'll test if riocmd is executable after we decide if we want to upload .. # We'll check the MP3 directory now, however. die "MP3 directory '$MP3DIR' does not exist or is not a directory" if (! -d $MP3DIR); # # Truth table for $opt_o and $opt_f options: # # $opt_o $opt_f Action # ======================================== # True eq "-" Output playlist to stdout. Do not upload to Rio # True undef (-f not given) Output playlist to stdout. Do not upload to Rio # True string other than "-" Output playlist to $opt_f file. Do not upload to Rio # False Ignored Output playlist to tmp file. Upload MP3's to Rio # $opt_f= ">".$opt_f if (defined $opt_f); # note that "-f -" results in ">-" = stdout $opt_f = ">-" if (defined $opt_o && ! defined $opt_f); # implied stdout $opt_f=">/tmp/playlist_int.$$" if (! defined $opt_o); # ignore $opt_f # Now we know if we'll be uploading. Test riocmd die "$RIOCMD not found or not executable" if (!(defined $opt_o || -x $RIOCMD)); open PL,"$opt_f" || die "Can't open $opt_f $!"; $STDOUT=select PL; print STDERR "Finding MP3 files in $MP3DIR\n" if (defined $opt_v); @lines = `/usr/bin/find $MP3DIR -follow -name \\*.mp3 -print`; # If none found, say so and get out... # die "No MP3 files found in $MP3DIR" if ($#lines <0); # # Calculate basic ($opt_b || default) and external ($opt_x value || 32MB || 0) memory sizes # The default when neither $opt_b or $opt_x are given is for a PMP300 if (defined $opt_b){ $realb=$opt_b-.5; # actual max size doesn't work $maxBasicSize = $realb * 1024 * 1024; } else { $maxBasicSize = 31.5 * 1024 * 1024; } $opt_x=32 if (defined $opt_x && !$opt_x); if (defined $opt_x){ $realx=$opt_x-.5; # actual max size doesn't work $maxExternalSize = $realx * 1024 * 1024; # actual max size doesn't work } else { $maxExternalSize = 0 # Don't assume external memory } $maxTotalSize = $maxBasicSize + $maxExternalSize;# needed if we are not uploading srand( time() ^ ($$ + ($$ << 15)) ); # keep track of total size of playlist # $totalSize = 0; # until we are finished... # $doExt=0; for (;;) { last if (! defined ($line= randomSelect(\@lines))); chop $line; $size=size($line); # If adding this one to the list would keep the total size # within our limit, then do so. # # toggle between basic and external memory based on $doExt flag. if ($opt_o){ # we'll honor the -b and -x flags when we are not uploading .. $targetSize=$maxTotalSize; # .. but you'll end up with all MP3s in one file. } else { # we are uploading $targetSize=$doExt?$maxExternalSize:$maxBasicSize; # toggle size based on $doExt } if ($totalSize + $size < $targetSize) { $totalSize += $size; print "$line\n"; # if we filled up basic memory and have external memory ... } elsif (defined $opt_x && ! $doExt){# we exceeded basic size. do we have external RAM? if (! defined $opt_o){ # switch temp files if we are uploading. close PL; open PL,">/tmp/playlist_ext.$$" || die "Can't open /tmp/playlist_ext.$$ $!"; select PL; } $doExt=1; # flag it. $totalSize=0; # reset total size } } close PL; # done with playlist select $STDOUT; # reset output to stdout if (! defined $opt_o){ # We're going to upload to the Rio $opt_f=~s/^>//; # Strip output redirection character from base playlist; doRio("$RIOCMD","-za"); # erase rio memory doRio("$RIOCMD","-f $opt_f"); # upload playlist to rio base memory unlink $opt_f; # Ax base temp file. if (defined $opt_x){ doRio("$RIOCMD","-x -f /tmp/playlist_ext.$$") ; # upload playlist to rio ext memory unlink "/tmp/playlist_ext.$$"; } } print "\nriofill done\n"; sub doRio { my($cmd,$args)=@_; if (defined $opt_v){ $args = "-v ".$args; # prepend verbose flag print "$cmd $args\n";# echo cmd print `$cmd $args`; # echo cmd output } else { `$cmd $args`; # do it silently } } sub randomSelect { # Select and remove a random element from the array # referenced by parameter my ($arRef)=shift; if ($#$arRef>=0){ # only if the array is not empty return splice @$arRef,int ((rand) * $#$arRef),1; } else { return undef; } } sub size { my($f)=shift; my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size) = stat $f; return ($size); } =head1 NAME riofill - make a random playlist from a directory of MP3 files that will fit into a Rio's memory. Optionally upload it to a Rio using SBA's B utility. =head1 SYNOPSIS B S<[ B<-o -v> ] [ B<-f>I< filename> ] [ B<-d>I< MP3dir> ] [ B<-r>I< riopath> ] [ B<-x> [ I ] ] > =head1 DESCRIPTION The riofill utility randomly selects a list of MP3 files from a given directory. The utility ensures that the files selected will all fit into the memory of a Diamond Rio portable MP3 player. Switches allow different sized Rio memory to be filled. By default, C uploads the playlist produced to a Rio player connected to the computer's parallel port using the Snowblind Alliance (SBA) B utility. Optionally, the generated playlist may be printed to stdout or saved in a file instead of being uploaded to the Rio. =head2 Switches This script uses C for argument parsing. This means that switches that take an option must have a space or an equals sign between the switch and its value. For example, B<-F=bar> or B<-F bar> would work while B<-Fbar> would not. The following switches are supported: =over 5 =item B<-b> I indicates that B should attempt to load I megabytes worth of MP3 files into the RIO's base memory If this switch is given it must be followed by an integer number which B will use as the number of megabytes (MB) of base memory. If this switch is not given, B will assume the Rio has 32MB of base memory. The actual value used by B is the given or default value minus 0.5. This is because exactly 32MB will not fit in a PMP300's 32MB base memory. See B<-o> switch below for how the B<-b> and B<-x> switches are treated when you are not uploading to a Rio. =item B<-d>I< MP3dir> specifies the directory in which to look for MP3 files. If B<-d> is not given B looks for MP3 files in the current directory. B searches this directory and any subdirectories. =item B<-f>I< filename> specifies a file to which the playlist should be saved. This switch is ignored if B<-o> is not given. If B<-o> is given and B<-f> is either not given or equal to "-" B prints the playlist to the standard output. =item B<-o> specifies that B should output the playlist rather than uploading it to the Rio. if no B<-f> switch (see below) is given, the playlist is printed to the standard output. If B<-o> is not given, B attempts to upload the generated playlist to a Diamond Rio connected to the computer's parallel port. If the B<-b> and/or B<-x> switches are given in combination with the B<-o> switch, B adds their given or default values together to come up with the total size of the playlist. See the EXAMPLES section for a case in point. =item B<-r>I< riopath> specifies where the Snowblind Alliance's B utility is located. If B<-r> is not given, B looks for this utility in /usr/local/bin. If B is asked to upload a playlist to the Rio, this path is checked to ensure the rio utility is present and executable. If it isn't, B exits with an error message. =item B<-v> tells B to be verbose in its actions. If this switch is given, B will say what it is doing and print the output of the B utility to standard output. =item B<-x> [ I ] indicates that B should attempt to load MP3 files into extended memory in the Rio. Rio memory in add-on flash RAM cards is considered extended memory. If this switch is given with no argument, B will assume the Rio has 32MB of extended memory. If an argument is given, B will use it as the number of megabytes (MB) of extended memory. The actual value used by B is the given value minus 0.5. This is because exactly 32MB will not fit in a PMP300's 32MB base memory. The author assumes this restriction applies to extended memory as well. =back =head1 EXAMPLES Search the current directory for MP3 files. Randomly select 32MB worth of these files. Upload them to the Rio: S> Search F for MP3 files. Randomly select 64MB worth of these files. Upload 32MB to the Rio's base memory and 32MB to the Rio's extended memory using the B utility in F: S> Search F for MP3 files. Randomly select 32MB worth of these files. Print the names of these files on the screen: S> Same as above: S> Search F for MP3 files. Randomly select 32MB worth of these files. Print the names of these files to the file F: S> Same as above: S/home/mp3/playlists/mylist>> Generate a playlist of MP3s whose total size does not exceed 88MB: S> =head1 LICENSE B is GPL. B is either GPL or Artistic. =head1 AUTHOR Based on Ron Forrester's Erjf@zero-ping.comE B. His code was hacked upon mercilessly by Howard (that's what I do for fun) Owen Ehbo@egbok.comE to produce B =head1 SEE ALSO B source, rio(1) =head1 PREREQUISITES This script uses the B utility to upload data to the Rio. You can get this utility from the Snowblind Alliance at http://www.world.co.uk/sba/ This script also makes use of the C module which is included in the Perl distribution. =head1 SCRIPT CATEGORIES Audio/MP3 =head1 README Randomly selects a playlist of MP3 files from a given directory and either uploads them to a connected Rio MP3 player or prints the playlist. =cut