Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

M3U Playlist Generator

by xunker (Beadle)
on Dec 17, 2001 at 09:56 UTC ( [id://132478]=sourcecode: print w/replies, xml ) Need Help??
Category: Audio Related Programs
Author/Contact Info xunker <xunker@pyxidis.org>
Description:

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.

To-do's:

  • 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
+001
# <xunker@pyxidis.org>
#
use strict;

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

my $Debug = 0;
my $usage = "listmaker.pl <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;
    $counter++;
}

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 ## http://www.perlmonks.org/?node_id=132478 ## 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 "mk-m3u.pl <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

      Why?

      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

        Why?

        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?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://132478]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (2)
As of 2024-04-25 21:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found