The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

MP3::M3U::Parser - Perl extension for parsing mp3 lists.

SYNOPSIS

   use MP3::M3U::Parser;
   my $parser = MP3::M3U::Parser->new(-type    => 'dir',
                                      -path    => '/directory/containing/m3u_files',
                                      -seconds => 'format');

   my %results = $parser->parse;
   my %info    = $parser->info;
   my @lists   = keys %results;

   $parser->export(-format   => 'xml',
                   -file     => "/path/mp3.xml",
                   -encoding => 'ISO-8859-9');

   $parser->export(-format   => 'html',
                   -file     => "/path/mp3.html",
                   -drives   => 'off');

DESCRIPTION

MP3::M3U::Parser is a parser for M3U mp3 song lists. It also parses the EXTINF lines (which contains id3 song name and time) if possible. You can get a parsed object or specify a format and export the parsed data to it. The format can be xml or html.

Methods

new()

The object constructor. Takes several arguments like:

-type

Sets the parse type. It's value can be dir or file. If you select file, only the file specified will be parsed. If you set it to dir, Then, you must set path to a directory that contains m3u files.

If you don't set this parameter, it will take the default value file.

-path

Set this to the directory that holds one or more m3u files. Can NOT be blank. You must set this to a existing directory. Use "/" as the directory seperator. Also do NOT add a trailing slash:

   -path => "/my/path/", # WRONG
   -path => "/my/path",  # TRUE

Note that, *ONLY* the path you've specified will be searched. Any sub directories it may contain will not be searched.

-file

If you set type to file, then you must set this to an existing file. Don't set it to a full path. Only use the name of the file, because you specify the path with path:

   -file => "/my/path/mp3.m3u", # WRONG
   -file => "mp3.m3u",          # TRUE
-seconds

Format the seconds returned from parsed file? if you set this to the value format, it will convert the seconds to a format like MM:SS or H:MM:SS. Else: you get the time in seconds like; 256 (if formatted: 04:15).

If you don't want to get a list of every song in the m3u list, but want to get a specific group's/singer's songs from the list, set this to the string you want to search. Think this "search" as a parser filter.

Note that, the module will do a *very* basic case-insensitive search. It does dot accept multiple words (if you pass a string like "michael beat it", it will not search every word seperated by space, it will search the string "michael beat it" and probably does not return any results -- it will not match "michael jackson - beat it"), it does not have a boolean search support, etc. If you want to do something more complex, get the parsed tree and use it in your own search function, or just re-define the private method search like the private method html.

-parse_path

The module assumes that all of the songs in your M3U lists are (or were: the module does not check the existence of them) on the same drive. And it builds a seperate data table for drive names like:

   DRIVE => {
             FIRST_LIST => 'G:',
             # so on ...
             }

And removes that drive letter (if there is a drive letter) from the real file path. If there is no drive letter, then the drive value is 'CDROM:'.

So, if you have a mixed list like:

   G:\a.mp3
   F:\b.mp3
   Z:\xyz.mp3

set this parameter to 'asis' to not to remove the drive letter from the real path. Also, you "must" ignore the DRIVE table contents which will still contain a possibly wrong value; export() does take the drive letters from the DRIVE table. So, you can not use the drive area in the exported xml (for example).

parse()

Normally it does not take any arguments. Just call it like:

   $parser->parse;

But, if you want to just parse a file or list of files (means you don't need an object) and export the parsed data to a xml or html file, you must call this method with a true value.

   MP3::M3U::Parser->new(ARGS)->parser(1)->export(ARGS);

See the related example below in the EXAMPLES section.

If you call this method without any arguments, it will return the parsed data as a hash or hash reference:

   my $hash = $parser->parse;

or

   my %hash = $parser->parse;

The data returned from parse() method is like this (lets say, we are parsing a list named 'mp3_16.m3u'):

   # Parse the list(s) and get results:
   $results = $parser->parse;

   # $results is like this:
   $results = {
     'mp3_16' => [ # The parsed list's name is the main key.
                   # and it's value is an arrayref.
                   [ # inside, the values are also arrayrefs.
                    '2 Unlimited - No Limit',          # 1st: ID3 Name
                    '03:29',                           # 2nd: Time
                    'G:\EN\2 Unlimited - No limit.mp3' # 3rd: File path.
                   ],
                   # If there are other songs in the list, they'll be added here.
                 ],# End of 'mp3_16' key.
    # if we've parsed other lists, their content is here.        
   };

You can also use

   my $results = $parser->parse;
      $parser->Dump("/path/to/mydump.txt",[$results]);

to see the structure yourself.

info()

You must call this after calling parse. It returns an info hash about the parsed data.

   my %info = $parser->info;

The keys of the %info hash are:

   songs   => Total number of songs
   files   => Total number of lists parsed
   ttime   => Total time of the songs 
   average => Average time of the songs
   drive   => Drive names for parsed lists

Note that the 'drive' key is a hash ref, while others are strings. If you have parsed a M3U list named "mp3_01.m3u" and want to learn the drive letter of it, call it like this:

   printf "Drive is %s\n", $info{drive}->{'mp3_01'};

But, maybe you do not want to use the $info{drive} table; see parse_path option in new.

export()

Exports the parsed data to a format. The format can be xml or html. The HTML File' s style is based on the popular mp3 player WinAmp' s HTML List file. XML formatting (well... escaping) is experimental. Takes some arguments:

-file

The full path to the file you want to write the resulting data. Can NOT be blank.

-format

Can be xml or html. Default is html.

-encoding

The exported xml file's encoding. Default is ISO-8859-1. See http://www.iana.org/assignments/character-sets for a list. If you don't define the correct encoding for xml, you can get "not well-formed" errors from the xml parsers. This value is also used in the meta tag section of the html file.

-drives

Only required for the html format. If set to off, you will not see the drive information in the resulting html file. Default is on. Also see parse_path option in new.

Dump()

Dumps the data structure to a text file. Call this with a data structure and full path of the text file you want to save:

   $parser->Dump("/my/dump/file.txt",[%results]);

Second argument must be an arrayref. Uses the standard Data::Dumper module.

Error handling

Note that, if there is an error, the module will die with that error. So, using eval for all method calls can be helpful if you don't want to die:

   sub my_sub {
      # do some initial stuff ...
      my %results;
      eval { %results = $parser->parse };
      if($@) {
         warn "There was an error: $@";
         return;
      } else {
         # do something with %results ...
      }
      # do something or not ...
   }

As you can see, if there is an error, you can catch this with eval and access the error message with the special Perl variable $@.

EXAMPLES

How can I parse an M3U List?

Solution:

   #!/usr/bin/perl -w
   use strict;
   use MP3::M3U::Parser;
   my $parser = MP3::M3U::Parser->new(-type    => 'file',
                                      -path    => '/my/path',
                                      -file    => 'mp3.m3u',
                                      -seconds => 'format');
   my %results  = $parser->parse;
How can I parse a list of files?

Solution: Put all your M3U lists into a directory and show this directory to the module with the "-path" parameter:

   #!/usr/bin/perl -w
   use strict;
   use MP3::M3U::Parser;
   my $parser = MP3::M3U::Parser->new(-type    => 'dir',
                                      -path    => '/my/path',
                                      -seconds => 'format');
   my %results  = $parser->parse;
   my @lists    = keys %results;
   my %info     = $parser->info;

   printf "I have parsed these lists: %s\n",join(", ",@lists);

   printf "There are %s songs in %s lists. Total time of the songs is %s. Average time is %s.",
          $info{songs},
          $info{files},
          $info{ttime},
          $info{average};
How can I get the names of the parsed lists?

Solution: As you can see in the above examples:

      my @lists = keys %results;

Note that the .m3u extension and path information are removed from the file names when parsing.

How can I "just" export the parsed tree to a format?

Solution (convert to xml and save):

   #!/usr/bin/perl -w
   use MP3::M3U::Parser;
   MP3::M3U::Parser->new(-type    => 'dir',
                         -path    => '/my/path',
                         -seconds => 'format',
                         )
                   ->parse(1)
                   ->export(-format   => 'xml',
                            -file     => "/my/export/path/mp3.xml",
                            -encoding => 'ISO-8859-9');
   print "Done!\n";

If you dont need any objects, just call new() followed by parse() followed by export(). But DON'T FORGET to pass a true value to parse:

        parse(1)

If you don't do this, parse() method will not pass an object to export() and you'll get a fatal error. If you want to get the parsed results, you must call parse() without any arguments like in the examples above.

How can I do a basic case-insensitive search?

Solution: Just pass the search parameter with the string you want to search:

   my $parser = MP3::M3U::Parser->new(-type    => 'dir',
                                      -path    => '/my/path',
                                      -seconds => 'format',
                                      -search  => 'KLF', # Search KLF songs
                                      );
How can I change the HTML List layout?

Solution: Just re-define the sub 'MP3::M3U::Parser::html' in your program. To do this, first copy the sub html

   sub html {
   # subroutine contents
   }

from this module into your program, but do NOT forget to rename it to:

   sub MP3::M3U::Parser::html {
   # subroutine contents
   }

if you don't name it like this, the module will not see your changed sub, because it belongs to your name space not this module's. Then change what you want. I don't plan to add a template option, because I find it unnecessary. But if you want to do such a thing, this is the solution.

Note: re-definitions generate warnings.

NOTES

This module is based on a code I'm using for my own mp3 archive. There is another M3U parser in CPAN, but it does not have the functionality I want and it skips the EXTINFO lines. So, I wrote this module. This module does not check the existence of the mp3 files, it just parses the playlist entries.

TIPS

WinAmp

(For v2.80. I don't use Winamp v3 series.) If you don't see any EXTINF lines in your saved M3U lists, open preferences, go to "Options", set "Read titles on" to "Display", add songs to your playlist and scroll down/up in the playlist window until you see all songs' time infos. If you don't do this, you'll get only the file names or only the time infos for the songs you have played. Because, to get the time info, winamp must read/scan the file first.

Naming M3U Files

Give your M3U files unique names and put them into the same directory. This way, you can have an easy maintained archive.

TODO

  • Better xml transformation.

  • Split big exported(HTML) into separate files (maybe).

  • Multiple file parameters (maybe).

BUGS

I've tested this module a lot, but if you find any bugs or missed parts, you can contact me.

SEE ALSO

MP3::M3U.

AUTHOR

Burak Gürsoy, <burak@cpan.org>

COPYRIGHT AND LICENSE

Copyright 2003 Burak Gürsoy. All rights reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 1014:

Non-ASCII character seen before =encoding in 'Gürsoy,'. Assuming CP1252