Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW

M3U Playlist Generator

by xunker (Beadle)
on Dec 17, 2001 at 09:56 UTC ( #132478=sourcecode: print w/replies, xml ) Need Help??
Category: Audio Related Programs
Author/Contact Info xunker <>

Short Desciption: Produces an .M3U style playlist from a specified path

More Detail: There have been a few other playlist makers posted, but I've given this one the moniker "better" because it's generates useful WinAMP-style .M3U playlists, not just a list of .mp3 files. It finds all MP3 files below a given path, and stores both the file path and the ID3 info (if availble; if not, it uses the filename) in the output M3U file.


  • get rid of the 'shortname' hash -- it works, it was easy, but it's a tacky hack.
  • check for ID3v2 tags ( didn't bother because this was written for my Mp3 collection that I ripped only with ID3v1 tags).
  • Check for the OS it's running on and use proper line terminator ("\n" or "\r\n") and proper directory separator ('/', '\' or ':') accordingly.

This was written for my in-car MP3 player/computer, so when I download new music to it, one double-click will refresh/rebuilt/sort the default playlist, adding the new music just downloaded. Before I did this I had to open the direcotry in WinAMP, sort it and save it. That's a pain with a screen that's a mere 2.2 inches diagonal.

It's not too optimized, but it can still process an archive of 1,300 files in about 1 minute (Pentium 133, 32 Meg, Win2k Pro).

Requires MP3::Info to be installed.

#!c:\perl\bin\perl.exe -w
# WinAMP M3U Sorted Playlist Generator version .0.1 ("Bob") December 2
# <>
use strict;

use File::Find;
use MP3::Info;

my $Debug = 0;
my $usage = " <source dir> <output file> [<verbose>]\n";

my ($source_path, $output_filename, $verbose) = @ARGV;
die $usage unless (($source_path) && ($output_filename));

die "that path doesn't exist, hombre" unless (-e $source_path);

unlink $output_filename if (-e $output_filename);

my @files; my %shortname;

sub addFile {
    $shortname{$File::Find::name} = $_;
    return unless -f;
    return unless /\.mp3$/;
    push @files, $File::Find::name;
    print '.' if $verbose;
print "\n" if $verbose;

find (\&addFile, $source_path);

@files = sort {uc($a) cmp uc($b)} @files;

open FILE, ">$output_filename"
    or die "could not open $output_filename for writing: $!";
print FILE "#EXTM3U\n";
my $counter = 1;  my $max = scalar (@files);
foreach my $file (@files) {
    my $tag = get_mp3tag($file);
    my $info = get_mp3info($file);
    my $pair;
    if (($tag->{ARTIST}) && ($tag->{TITLE})) {
        $pair = $tag->{ARTIST} . ' - ' . $tag->{TITLE};
    } else {
        $pair = $shortname{$file};
    print FILE "#EXTINF:"
        . int ($info->{SECS})
        . ","
        . $pair
        . "\n";
    $file =~ s/\//\\/g; # this is for WinAMP/MS-DOS;
                            # by default, File::Find returns
                            # filenames with the forward
                            # (proper) slash, but I use the
                            # program in Windows, so...
                            # If you use a real OS, remove.
    print FILE "$file\n";
    print "$counter of $max: $pair\n" if $verbose;

close FILE;
Replies are listed 'Best First'.
Re: M3U Playlist Generator
by PodMaster (Abbot) on Aug 18, 2003 at 06:59 UTC
    This should help :) use File::Spec; # ...
    $file = File::Spec->canonpath( $file ); # this is for all platforms perl runs on ;)

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

Re: M3U Playlist Generator
by Soul Singin' (Initiate) on Jul 01, 2012 at 20:31 UTC

    This thread is over 10 years old, so I'm reviving it.

    Below is my rewritten version which does not depend on File::Find. Comments welcome.

    #!/usr/bin/env perl ## Soul Singin' ## 01 July 2012 ## ## completely rewritten M3U playlist generator ## ## preliminaries use strict ; use warnings ; no warnings qw( uninitialized numeric void ) ; use MP3::Info ; ## arguments ( my $inpath = $ARGV[0] ) =~ s/\/$// ; my $otfile = $ARGV[1] ; ## die if the arguments are empty if ( $inpath eq "" || $otfile eq "" ) { die " <source dir> <output file>\n" ; } ## die if cannot find source path if ( ! -e $inpath ) { die $inpath . " does not exist.\n" ; } ## should we overwrite existing files? if ( -e $otfile ) { print $otfile . " already exists.\n" ; print 'Should I overwrite it? (Type "yes" to overwrite). ' ; my $response = <STDIN> ; if ( $response !~ /yes/i ) { die 'You did not type "yes", so I will not overwrite ' . $otfile . + ".\n" ; } } ## get the MP3 files that we want to add to our playlist my @infiles = sort( grep { -f $_ && ($_ =~ /\.mp3$/i) } glob "$inpath/ +*" ) ; ## prepare our new M3U playlist open( OTFILE, ">$otfile" ) || die "could not overwrite $otfile : $!"; print OTFILE "#EXTM3U\n"; foreach my $infile (@infiles) { ## get info from the files my $mtag = get_mp3tag( $infile) ; my $info = get_mp3info($infile) ; my $artist = $mtag->{ARTIST} ; my $stitle = $mtag->{TITLE} ; ## create title-author pair my $pair ; if ( $stitle ne "" ) { $pair .= $stitle ; } if ( $artist ne "" && $stitle ne "" ) { $pair .= " (by " . $artist . ")" ; } elsif ( $artist ne "" ) { $pair .= $artist ; } if ( $pair !~ /[A-Za-z0-9]/ ) { $pair = $infile ; } ## prepare output line my $otline = "#EXTINF:" . int ($info->{SECS}) . "," . $pair ; ## send output to our new M3U playlist print OTFILE $otline . "\n" ; print OTFILE $infile . "\n" ; } ## close the M3U playlist close OTFILE;

      Below is my rewritten version which does not depend on File::Find


      Comments welcome.

      $inpath can be file ( use -f or -d inestead of -e, since both -f and -d imply -exist )

      $inpath could contain glob metacharacters

      I'm personally not a fan of interactive commands, exit program if file already exists and overwrite not forced ( Getopt::Long ),

      $inpath could contain glob metacharacters

      if you don't want to recurse, you could write

      use File::Find::Rule; my @files = find( 'file', 'name', qr/\.mp3$/i, 'maxdepth', 1, 'in', $i +npath );

      FWIW, if you need to do path manipulations, Path::Class::Rule is better than File::Find::Rule;

      3 argument open is better than two, "or" is better than "||" since it doesn't require extra parens

      open my($fh), '>', $file or die "can't open '>', $file : $! ";

        Below is my rewritten version which does not depend on File::Find


        Because I cannot understand the documentation. I'll take a look at your example and keep playing. Thanks!

        $inpath can be file ( use -f or -d inestead of -e, since both -f and -d imply -exist )

        Good point! -d would be best because the $inpath should be a directory.

        $inpath could contain glob metacharacters

        Doh! I did not even think of that ... because on my system it will not!

        3 argument open is better than two

        Old habits are hard to break.

        Thanks! Your comments have been very helpful.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: sourcecode [id://132478]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (8)
As of 2018-06-19 22:49 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (115 votes). Check out past polls.