Hi All! I wrote this today as a quickie fix for an easy problem. I have this directory full of directories for mp3 files. I wanted to be able to browse using a remote media player and play an entire album. The script below goes into each directory and generates an m3u file for that directory. (Will not generate an m3u if no mp3 files are present.) It's quick and dirty, but very useful if you have this same problem.

#!/usr/bin/perl -w use strict; use File::Find::Rule; # Change this next line to reflect your mp3 directory my @dirs_only = File::Find::Rule->directory()->in('/steeler/mp3'); foreach( @dirs_only ) { my $dir = $_; chdir( "$dir" ); chomp( my @files = `ls` ); my $files = `ls`; if (( $files =~ /\.mp3$/ ) or ( $files =~ /\.MP3$/ )) { my $current_dir = `pwd`; my $basename = `basename \"$current_dir\"`; $basename =~ s/\s//g; $basename =~ s/'//g; $basename =~ s/\.//g; my $m3u_file = "$basename".'.m3u'; print "Creating m3u file: $m3u_file\n"; open M3U, ">$m3u_file"; foreach( @files ){ print M3U "$_\n"; } close M3U; } }

This script assumes you are running on a unixy-type system, I'm sure some of those shelled-out commands won't work on windows.

Replies are listed 'Best First'.
Re: Generate m3u files automatically
by Corion (Patriarch) on May 07, 2010 at 07:27 UTC

    You can easily replace `ls` with glob, `basename` with File::Basename and `pwd` with Cwd::getcwd. Then your script would also work elsewhere.

    As a note on your coding style, Perl is not the shell, so there is little need to quote parameters when passing them to functions.

    chdir( "$dir" );

    is better written as

    chdir( $dir );

    You can use the /i switch to make your regular expressions match case insensitively, so /\.mp3$/i will also match .Mp3.

Re: Generate m3u files automatically
by graff (Chancellor) on May 07, 2010 at 08:20 UTC
    This script assumes you are running on a unixy-type system

    It also assumes you are willing to edit the script every time you want to run it on some different directory (other than "/steeler/mp3", which doesn't exist on most "unixy-type" systems ;)

    And it assumes that if the directory contains any mp3 file, then it contains nothing but mp3 files -- all files get listed in your "m3u" file (including the m3u file, if the script has been run previously on that path).

    And it assumes that there will never be a problem with opening "m3u" files for output, or that you don't care when open fails.

    There's no need to make any of these assumptions (and no need for chdir):

    #!/usr/bin/perl use strict; use warnings; use File::Find::Rule; use File::Spec; die "Usage: $0 path/to/search\n" unless ( @ARGV == 1 and -d $ARGV[0] ) +; my @dirs = File::Find::Rule->directory()->in( $ARGV[0] ) or die "Can't find anything in $ARGV[0]\n"; for my $dir ( @dirs ) { my @mp3s = File::Find::Rule->maxdepth(1)->file()->name("*.[Mm][Pp] +3")->in($dir); if ( @mp3s ) { ( my $m3u = ( File::Spec->splitdir( $dir ))[-1] ) =~ s/[\s.']+ +//g; if ( open( M3U, ">", "$dir/$m3u.m3u" )) { print M3U "$_\n" for ( @mp3s ); close M3U; warn sprintf( "%d mp3s listed in %s\n", scalar @mp3s, "$di +r/$m3u.m3u" ); } else { warn "Unable to create $dir/$m3u.m3u : $!\n"; } } }
    Updated second call to File::Find::Rule so that it matches *.MP3 as well as *.mp3 (and *.mP3 and *.Mp3). Also fixed syntax error (added missing quotes in sprintf arg).

      I find it peculiar that the file system is scanned twice. The following avoids that:

      #!/usr/bin/perl use strict; use warnings; use File::Find::Rule qw( ); use Path::Class qw( file ); die "Usage: $0 path/to/search\n" if @ARGV != 1 || !-d $ARGV[0]; my @scan = File::Find::Rule ->name(qr/\.mp3\z/i) #->file ->in( $ARGV[0] ); my $last_dir = ''; my $fh; for (@scan) { my $file = file($_); my $dir = $file->dir; if ($dir ne $last_dir) { $dir = $last_dir; my $m3u = $dir->dir_list(-1); $m3u =~ s/[\s.']+//g; $m3u = $dir->file("$m3u.m3u"); open($fh, '>', $m3u) or warn("Unable to create \"$m3u\": $!\n"); } print($fh "$_\n"); }

      Untested.

      Note that this (and previous solutions) will probably create an m3u named "..m3u" if you pass "." for argument and that directory contains mp3s. This can be solved by changing
      ->in( $ARGV[0] )
      to
      ->in( dir($ARGV[0])->absolute )
      (Don't forget to import dir.)

      (Should have been a reply to the OP. The OP was notified.)

Re: Generate m3u files automatically
by rementis (Beadle) on May 07, 2010 at 19:38 UTC
    Awesome changes on my script. I wrote that thing in 20 minutes knowing full well what I was going to find in each directory. Your update is awesome, and I'll keep a copy on hand!