1. Burak Gürsoy
  2. CPAN-MP3-M3U-Parser

Commits

Burak Gursoy  committed 87156f2

refurbish

  • Participants
  • Parent commits e4cc11e
  • Branches default

Comments (0)

Files changed (14)

File Build.PL

View file
-use strict;
-use Module::Build;
-
-Module::Build->new(
-   module_name => 'MP3::M3U::Parser',
-   license     => 'perl',
-   requires    => {
-      'File::Spec'     => 0,
-      'IO::File'       => 0,
-      'Cwd'            => 0,
-      'Text::Template' => 0,
-   },
-   dist_author => 'Burak Gursoy <burak@cpan.org>',
-)->create_build_script;
+use strict;
+use Module::Build;
+
+Module::Build->new(
+   module_name => 'MP3::M3U::Parser',
+   license     => 'perl',
+   requires    => {
+      'File::Spec'     => 0,
+      'IO::File'       => 0,
+      'Cwd'            => 0,
+      'Text::Template' => 0,
+      'Test::More'     => '0.40',
+   },
+   dist_author => 'Burak Gursoy <burak@cpan.org>',
+)->create_build_script;

File Changes

View file
-Revision history for Perl extension MP3::M3U::Parser.
-
-Time zone is GMT+2.
-
-2.20 Sun Jun 18 03:58:36 2006
-	=> SIGNATURE sent to oblivion :p
-	=> Added Build.PL
-	=> Updated Makefile.PL
-	=> Updated tests
-	=> Updated copyright & license
-	=> Private methods (including template() & search())
-	   renamed to include an initial underscore:
-	      search() -> _search()
-	      template -> _template()
-	      etc.
-	   Beware() if you->are('sub-classing');
-
-2.1 Tue Sep 14 20:47:09 2004
-	=> export() now has the ability to dump the generated data
-	   to a scalar instead of exporting to a file. Useful for
-	   web programming and dynamic generation. To enable this,
-	   -toscalar option must be passed with a scalar ref.
-	=> Module now require()s IO::File, File::Spec and Cwd if needed.
-	   Because, we won't need this modules, if the user works with
-	   scalars instead of disk files. This will save some memory 
-	   and incerase speed I believe. If you are parsing scalar
-	   data and do not use export(), then no external modules 
-	   will be loaded.
-	=> Updated HTML and XML templates for -toscalar compatibility.
-	=> Updated Makefile.PL
-	=> Updated pod.
-	=> Added new tests to demonstrate -toscalar option.
-	=> Added a CGI example code to distro.
-	=> Some minor fixes.
-
-2.01 Mon Jul 26 19:54:14 2004
-	=> Minor fix in Pod.
-	=> SONG element was including ".mp3" if it is 
-	   extracted from PATH. Fixed.
-	=> ARTIST element was including path info. Fixed.
-	   But, if the song name or artist includes slash or backslash,
-	   we have a problem
-
-2.0 Fri Jul 23 23:19:48 2004
-	=> BEWARE!!! THIS NEW VERSION BREAKS THE OLD CODE!
-	=> Now the module looks *like* a module. It was
-	   like a program code before. With this version
-	   it does only the parsing job. 
-	=> Removed directory reading codes. 
-	=> Removed some parameters passed to new()
-	=> Removed ignore chars thingies.
-	=> Removed Dump() method.
-	=> parse() now accepts file_paths/filehandles/scalars as 
-	   parameters. You can mix and use these types together; the 
-	   parser is now smart enough to do the right job with the 
-	   related type.
-	=> parse() now returns the object itself. 
-	   Use the brand new result() method to get the parsed tree.
-	   This new behaviour is a result of adding the chaining
-	   ability of public methods.
-	=> Object table structure and m3u key orders are changed.
-	   Object table is now more complex and verbose.
-	=> Returned result set is now an array not hash.
-	=> Module now checks if the file is a valid m3u file, 
-	   before parsing it. Dies if it is invalid.
-	=> The new method reset() resets the parser object. Might
-	   be usefull in loops. For example: you can export each
-	   m3u to it's separate file with reset() without any extra work.
-	=> The module no longer requires Data::Dumper, but it now requires
-	   Cwd and Text::Template.
-	=> Exported HTML file is now fully templated. You can subclass
-	   the module and override template() method to change the HTML 
-	   template. Currently, I'm not planning to add a template for 
-	   XML type.
-	=> Added new namespace MP3::M3U::Parser::Dummy for Text::Template.
-	=> Added tcompile() method for compiling templates.
-	=> drive key returned from info() is now an array.
-	=> Added locate_file() method to get the full path of a file,
-	   if the file is in the current working directory.
-	=> Parser now tries to extract the artist and song infos into 
-	   a separate data field.
-	=> New option '-overwrite' added to new() and export(). It 
-	   controls the behaviour of module in export() globally or 
-	   locally depending on where you set it.
-	=> export() checks if the export file already exists. Dies
-	   if it exists and you didn't select to overwrite it.
-	=> Pod updated.
-	=> Subclassing examples added to Pod.
-	=> You can now set the encoding of exported files 
-	   from new() globally via -encoding option.
-	=> You can now set the format of exported files from 
-	   new() globally via -expformat option.
-	=> You can now set the -drives option for export() from 
-	   new() globally via -expdrives option.
-	=> But parameters passed to export() has the priority.
-	=> XML element names changed.
-
-1.1 Mon Aug 25 21:42:31 2003
-	=> Better XML escaping.
-	=> Added '-ignore_chars' parameter.
-	=> Fixed a bug in seconds()
-	=> Moved module to ./lib/MP3/M3U in the 
-	   distribution package.
-
-1.04 Sun Jun 22 15:54:43 2003
-	=> Removed sort() from export() method (the module was using 
-	   it on the $self->{M3U}{$cd} object table). 
-	   Thanks to Patrick Bourdon.
-
-1.03 Tue Jun 10 22:38:53 2003
-	=> HTML code is valid HTML 4.01 & valid CSS. But you can get 
-	   "non SGML character number" errors if your list(s) have 
-	   some of the undefined chars. I may add a filter to 
-	   escape these in the future.
-	=> Some minor fixes.
-
-1.02 Mon Mar 10 22:33:04 2003
-	=> Fixed the comma bug. Reported by Brandon Lederer.
-	=> Fixed a bug in the formatting seconds part.
-	=> Empty result keys (if a search didn't find any matches 
-	   in a list for example) are ignored when exporting to xml.
-	=> Fixed the html playlist (css code was wrong and only 
-	   IE was displaying it correctly).
-	=> Updated the POD.
-
-1.01 Wed Feb 26 18:14:51 2003
-	=> Fixed definition of Dump() in the POD. 
-	=> Added the definition of the parsed data structure to 
-	   the POD. 
-	=> Added the test 't/use.t' to the distribution. Module's 
-	   code didn't changed.
-
-1.00 Tue Feb 25 19:23:14 2003
-	=> First release.
+Revision history for Perl extension MP3::M3U::Parser.
+
+Time zone is GMT+2.
+
+2.21 Sun Mar 29 08:20:01 2009
+    => Fixed an issue related to network paths.
+       Reported by Paul Miller <jettero@cpan.org>.
+    => Converted the distro files to LF from CRLF.
+    => Updated test suite.
+    => Test::More 0.40 is now required.
+
+2.20 Sun Jun 18 03:58:36 2006
+    => SIGNATURE sent to oblivion :p
+    => Added Build.PL
+    => Updated Makefile.PL
+    => Updated tests
+    => Updated copyright & license
+    => Private methods (including template() & search())
+       renamed to include an initial underscore:
+          search() -> _search()
+          template -> _template()
+          etc.
+       Beware() if you->are('sub-classing');
+
+2.1 Tue Sep 14 20:47:09 2004
+    => export() now has the ability to dump the generated data
+       to a scalar instead of exporting to a file. Useful for
+       web programming and dynamic generation. To enable this,
+       -toscalar option must be passed with a scalar ref.
+    => Module now require()s IO::File, File::Spec and Cwd if needed.
+       Because, we won't need this modules, if the user works with
+       scalars instead of disk files. This will save some memory 
+       and incerase speed I believe. If you are parsing scalar
+       data and do not use export(), then no external modules 
+       will be loaded.
+    => Updated HTML and XML templates for -toscalar compatibility.
+    => Updated Makefile.PL
+    => Updated pod.
+    => Added new tests to demonstrate -toscalar option.
+    => Added a CGI example code to distro.
+    => Some minor fixes.
+
+2.01 Mon Jul 26 19:54:14 2004
+    => Minor fix in Pod.
+    => SONG element was including ".mp3" if it is 
+       extracted from PATH. Fixed.
+    => ARTIST element was including path info. Fixed.
+       But, if the song name or artist includes slash or backslash,
+       we have a problem
+
+2.0 Fri Jul 23 23:19:48 2004
+    => BEWARE!!! THIS NEW VERSION BREAKS THE OLD CODE!
+    => Now the module looks *like* a module. It was
+       like a program code before. With this version
+       it does only the parsing job. 
+    => Removed directory reading codes. 
+    => Removed some parameters passed to new()
+    => Removed ignore chars thingies.
+    => Removed Dump() method.
+    => parse() now accepts file_paths/filehandles/scalars as 
+       parameters. You can mix and use these types together; the 
+       parser is now smart enough to do the right job with the 
+       related type.
+    => parse() now returns the object itself. 
+       Use the brand new result() method to get the parsed tree.
+       This new behaviour is a result of adding the chaining
+       ability of public methods.
+    => Object table structure and m3u key orders are changed.
+       Object table is now more complex and verbose.
+    => Returned result set is now an array not hash.
+    => Module now checks if the file is a valid m3u file, 
+       before parsing it. Dies if it is invalid.
+    => The new method reset() resets the parser object. Might
+       be usefull in loops. For example: you can export each
+       m3u to it's separate file with reset() without any extra work.
+    => The module no longer requires Data::Dumper, but it now requires
+       Cwd and Text::Template.
+    => Exported HTML file is now fully templated. You can subclass
+       the module and override template() method to change the HTML 
+       template. Currently, I'm not planning to add a template for 
+       XML type.
+    => Added new namespace MP3::M3U::Parser::Dummy for Text::Template.
+    => Added tcompile() method for compiling templates.
+    => drive key returned from info() is now an array.
+    => Added locate_file() method to get the full path of a file,
+       if the file is in the current working directory.
+    => Parser now tries to extract the artist and song infos into 
+       a separate data field.
+    => New option '-overwrite' added to new() and export(). It 
+       controls the behaviour of module in export() globally or 
+       locally depending on where you set it.
+    => export() checks if the export file already exists. Dies
+       if it exists and you didn't select to overwrite it.
+    => Pod updated.
+    => Subclassing examples added to Pod.
+    => You can now set the encoding of exported files 
+       from new() globally via -encoding option.
+    => You can now set the format of exported files from 
+       new() globally via -expformat option.
+    => You can now set the -drives option for export() from 
+       new() globally via -expdrives option.
+    => But parameters passed to export() has the priority.
+    => XML element names changed.
+
+1.1 Mon Aug 25 21:42:31 2003
+    => Better XML escaping.
+    => Added '-ignore_chars' parameter.
+    => Fixed a bug in seconds()
+    => Moved module to ./lib/MP3/M3U in the 
+       distribution package.
+
+1.04 Sun Jun 22 15:54:43 2003
+    => Removed sort() from export() method (the module was using 
+       it on the $self->{M3U}{$cd} object table). 
+       Thanks to Patrick Bourdon.
+
+1.03 Tue Jun 10 22:38:53 2003
+    => HTML code is valid HTML 4.01 & valid CSS. But you can get 
+       "non SGML character number" errors if your list(s) have 
+       some of the undefined chars. I may add a filter to 
+       escape these in the future.
+    => Some minor fixes.
+
+1.02 Mon Mar 10 22:33:04 2003
+    => Fixed the comma bug. Reported by Brandon Lederer.
+    => Fixed a bug in the formatting seconds part.
+    => Empty result keys (if a search didn't find any matches 
+       in a list for example) are ignored when exporting to xml.
+    => Fixed the html playlist (css code was wrong and only 
+       IE was displaying it correctly).
+    => Updated the POD.
+
+1.01 Wed Feb 26 18:14:51 2003
+    => Fixed definition of Dump() in the POD. 
+    => Added the definition of the parsed data structure to 
+       the POD. 
+    => Added the test 't/use.t' to the distribution. Module's 
+       code didn't changed.
+
+1.00 Tue Feb 25 19:23:14 2003
+    => First release.

File MANIFEST

View file
-lib/MP3/M3U/Parser.pm
-t/01-pod.t
-t/02-pod-coverage.t
-t/03-basic.t
-t/04-basic_xml.t
-t/05-sub_html.t
-t/06-sub_search.t
-t/07-scalar_xml.t
-t/08-scalar_html.t
-t/skip.test
-eg/m3u.cgi
-test.m3u
-Changes
-Makefile.PL
-Build.PL
-MANIFEST
-README
+lib/MP3/M3U/Parser.pm
+t/01-pod.t
+t/02-pod-coverage.t
+t/03-basic.t
+t/04-basic_xml.t
+t/05-sub_html.t
+t/06-sub_search.t
+t/07-scalar_xml.t
+t/08-scalar_html.t
+eg/m3u.cgi
+test.m3u
+Changes
+Makefile.PL
+Build.PL
+MANIFEST
+README

File Makefile.PL

View file
-use ExtUtils::MakeMaker;
-
-WriteMakefile(
-   NAME         => 'MP3::M3U::Parser',
-   VERSION_FROM => 'lib/MP3/M3U/Parser.pm',
-   PREREQ_PM    => {
-      'File::Spec'     => 0,
-      'IO::File'       => 0,
-      'Cwd'            => 0,
-      'Text::Template' => 0,
-   },
-   ($] >= 5.005 ? (
-   AUTHOR   => 'Burak Gursoy <burak@cpan.org>',
-   ABSTRACT => 'MP3 playlist parser',
-   ) : ()),
-   PL_FILES     => {},
-);
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+   NAME         => 'MP3::M3U::Parser',
+   VERSION_FROM => 'lib/MP3/M3U/Parser.pm',
+   PREREQ_PM    => {
+      'File::Spec'     => 0,
+      'IO::File'       => 0,
+      'Cwd'            => 0,
+      'Text::Template' => 0,
+      'Test::More'     => '0.40',
+   },
+   ($] >= 5.005 ? (
+   AUTHOR   => 'Burak Gursoy <burak@cpan.org>',
+   ABSTRACT => 'MP3 playlist parser',
+   ) : ()),
+   PL_FILES     => {},
+);

File README

View file
-MP3::M3U::Parser
-================
-
-Parses M3U mp3 playlists and if wanted, exports the parsed data to 
-formats like xml and html.
-
-Read the module's POD for documentation.
-
-See the "t" and "eg" directories for examples.
-
-INSTALLATION
-
-To install this module type the following:
-
-   perl Makefile.PL
-   make
-   make test
-   make install
-
-or under Windows:
-
-   perl Makefile.PL
-   nmake
-   nmake test
-   nmake install
-
-DEPENDENCIES
-
-This module requires these other modules:
-
-	File::Spec
-	IO::File
-	Cwd
-	Text::Template
-
-All are available on CPAN
-
-COPYRIGHT AND LICENCE
-
-Copyright (C) 2003-2006 Burak G�rsoy <burak@cpan.org>. All rights reserved.
-
-This library is free software; you can redistribute it and/or modify 
-it under the same terms as Perl itself, either Perl version 5.8.8 or, 
-at your option, any later version of Perl 5 you may have available.
+MP3::M3U::Parser
+================
+
+Parses M3U mp3 playlists and if wanted, exports the parsed data to 
+formats like xml and html.
+
+Read the module's POD for documentation.
+
+See the "t" and "eg" directories for examples.
+
+INSTALLATION
+
+To install this module type the following:
+
+   perl Makefile.PL
+   make
+   make test
+   make install
+
+or under Windows:
+
+   perl Makefile.PL
+   nmake
+   nmake test
+   nmake install
+
+DEPENDENCIES
+
+This module requires these other modules:
+
+   File::Spec
+   IO::File
+   Cwd
+   Text::Template
+   Test::More
+
+All are available on CPAN
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2003-2009 Burak G�rsoy <burak@cpan.org>. All rights reserved.
+
+This library is free software; you can redistribute it and/or modify 
+it under the same terms as Perl itself, either Perl version 5.8.8 or, 
+at your option, any later version of Perl 5 you may have available.

File lib/MP3/M3U/Parser.pm

View file
-package MP3::M3U::Parser;
-use strict;
-use vars qw[$VERSION $AUTOLOAD];
-
-# Data table key map
-use constant PATH    => 0;
-use constant ID3     => 1;
-use constant LEN     => 2;
-use constant ARTIST  => 3;
-use constant SONG    => 4;
-
-use constant MAXDATA => 4; # Maximum index number of the data table
-
-$VERSION = '2.20';
-
-sub new {
-   # -parse_path -seconds -search -overwrite
-   my $class = shift;
-   my %o     = scalar(@_) % 2 ? () : (@_); # options
-   my $self  = {
-                _M3U_         => [], # for parse()
-                TOTAL_FILES   =>  0, # Counter
-                TOTAL_TIME    =>  0, # In seconds
-                TOTAL_SONGS   =>  0, # Counter
-                AVERAGE_TIME  =>  0, # Counter
-                ACOUNTER      =>  0, # Counter
-                ANON          =>  0, # Counter for SCALAR & GLOB M3U
-                INDEX         =>  0, # index counter for _M3U_
-                EXPORTF       =>  0, # Export file name counter for anonymous exports
-                seconds       => $o{'-seconds'}    || '', # format or get seconds.
-                search_string => $o{'-search'}     || '', # search_string
-                parse_path    => $o{'-parse_path'} || '', # mixed list?
-                overwrite     => $o{'-overwrite'}  ||  0, # overwrite export file if exists?
-                encoding      => $o{'-encoding'}   || '', # leave it to export() if no param
-                expformat     => $o{'-expformat'}  || '', # leave it to export() if no param
-                expdrives     => $o{'-expdrives'}  || '', # leave it to export() if no param
-   };
-   if ($self->{search_string} and length($self->{search_string}) < 3) {
-      die "A search string must be at least three characters long!";
-   }
-   bless  $self, $class;
-   return $self;
-}
-
-sub parse {
-   my $self  = shift;
-   my @files = @_;
-   my $file;
-   foreach $file (@files) {
-      unless(ref $file) {
-         $file = $self->_locate_file($file);
-         -e $file or die "'$file' does not exist!";
-      }
-      $self->_parse_file($file);
-   }
-
-   # Average time of all the parsed songs:
-   $self->{AVERAGE_TIME} = ($self->{ACOUNTER} and $self->{TOTAL_TIME}) 
-                           ? $self->_seconds($self->{TOTAL_TIME}/$self->{ACOUNTER})
-                           : 0;
-   return $self if defined wantarray;
-}
-
-sub _parse_file {
-   # supports disk files, scalar variables and filehandles (typeglobs)
-   my $self = shift;
-   my $file = shift;
-   my $ref  = ref($file) || '';
-   if($ref and $ref !~ m[^(?:GLOB|SCALAR)$]) {
-      die "Unknown parameter of type '$ref' passed to parse()!";
-   }
-   my $cd;
-   unless ($ref) {
-      $cd = (split /[\\\/]/, $file)[-1];
-      $cd =~ s,\.m3u,,i;
-   }
-   my $i = $self->{INDEX};
-   my $this_file;
-   if ($ref) {
-      $this_file = 'ANON'.$self->{ANON}++;
-   } else {
-      $this_file = $self->_locate_file($file);
-   }
-   $self->{'_M3U_'}[$i] = {file  => $this_file,
-                           list  => $ref ? $this_file : ($cd || ''),
-                           drive => 'CDROM:',
-                           data  => [],
-                           total => 0,
-                           };
-   $self->{TOTAL_FILES} += 1; # Total lists counter
-   my $index             = 0; # Index number of the list array
-   # These three variables are used when there is a '-search' parameter.
-   # long: total_time, total_songs, total_average_time
-   my($ttime,$tsong,$taver) = (0,0,0);
-   # while loop variables. j: junk data.
-   my($m3u,$j,@song,$j2,$sec,$temp_sec);
-
-   my($fh, @fh);
-   if($ref eq 'GLOB') {
-      $fh = $file;
-   } elsif ($ref eq 'SCALAR') {
-      @fh = split /\n/, $$file;
-   } else {
-      # Open the file to parse:
-      require IO::File;
-      $fh = IO::File->new;
-      $fh->open("< $file") or die "I could't open '$file': $!" unless $ref;
-   }
-
-PREPROCESS:
-   while ($m3u = ($ref eq 'SCALAR') ? (shift @fh) : <$fh>) {
-      # First line is just a comment. But we need it to validate
-      # the file as a m3u playlist file.
-      chomp $m3u;
-      if($m3u !~ m[^#EXTM3U]) {
-         die $ref ? "The '$ref' parameter you have passed does not contain valid m3u data!"
-                  : "'$file' is not a valid m3u file!";
-      }
-      last PREPROCESS;
-   }
-
-   my $dkey   =  $self->{'_M3U_'}[$i]{data};  # data key
-   my $device = \$self->{'_M3U_'}[$i]{drive}; # device letter
-
-RECORD: 
-   while ($m3u = ($ref eq 'SCALAR') ? (shift @fh) : <$fh>) {
-      chomp $m3u;
-      next unless $m3u; # Record may be blank if it is not a disk file.
-      $#{$dkey->[$index]} = MAXDATA; # For the absence of EXTINF line.
-      # If the extra information exists, parse it:
-      if($m3u =~ m!#EXTINF!i) {
-         ($j ,@song) = split(/\,/,$m3u);
-         ($j ,$sec)  = split(/:/,$j);
-         $ttime     += $sec;
-         $temp_sec   = $sec;
-         $dkey->[$index][ID3] = join(",", @song);
-         $dkey->[$index][LEN] = $self->_seconds($sec || 0);
-         $taver++;
-         next RECORD; # jump to path info
-      }
-
-      # Get the drive and path info.      Possible cases are:
-      if($m3u =~ m{^\w:[\\/](.+?)$}x or # C:\mp3\Singer - Song.mp3
-         $m3u =~ m{^   [\\/](.+?)$}x or # \mp3\Singer - Song.mp3
-         $m3u =~ m{^        (.+?)$}x    # Singer - Song.mp3
-         ) {
-         $dkey->[$index][PATH] = $self->{parse_path} eq 'asis' ? $m3u : $1;
-         $$device = $1 if $$device eq 'CDROM:' and $m3u =~ m[^(\w:)];
-         $tsong++;
-      }
-
-      # Try to extract artist and song info 
-      # and remove leading and trailing spaces
-      # Some artist names can also have a "-" in it. 
-      # For this reason; require that the data has " - " in it. 
-      # ... but the spaces can be one or more.
-      # So, things like "artist-song" does not work...
-      my($artist, @xsong) = split /\s{1,}-\s{1,}/, $dkey->[$index][ID3] || $dkey->[$index][PATH];
-      if ($artist) {
-         $artist =~ s[^\s+][];
-         $artist =~ s[\s+$][];
-         $artist =~ s[.*[\\/]][]; # remove path junk
-         $dkey->[$index][ARTIST] = $artist;
-      }
-      if (@xsong) {
-         my $song = join '-', @xsong;
-         $song =~ s[^\s+][];
-         $song =~ s[\s+$][];
-         $song =~ s[\.[a-zA-Z0-9]+$][]; # remove extension if exists
-         $dkey->[$index][SONG] = $song;
-      }
-
-      # convert undefs to empty strings
-      foreach my $CHECK (0..MAXDATA) {
-         $dkey->[$index][$CHECK] = '' unless defined $dkey->[$index][$CHECK];
-      }
-
-      # If we are searching something:
-      if($self->{search_string}) {
-         if($self->_search($dkey->[$index][PATH], $dkey->[$index][ID3])) {
-            $index++; # If we got a match, increase the index
-         } else { # If we didnt matched anything, resize these counters ...
-            $tsong--;
-            $taver--;
-            $ttime -= $temp_sec;
-            delete $dkey->[$index]; # ... and delete the empty index
-         }
-      } else {
-         $index++; # If we are not searching, just increase the index
-      }
-   }
-   # Close the file
-   $fh->close unless $ref;
-
-   # Calculate the total songs in the list:
-   $self->{'_M3U_'}[$i]{total} = $#{$self->{'_M3U_'}[$i]{data}} + 1;
-
-   # Adjust the global counters:
-   $self->{TOTAL_FILES}-- if($self->{search_string} and $#{ $self->{'_M3U_'}[$i]{data} } < 0);
-   $self->{TOTAL_TIME}  += $ttime;
-   $self->{TOTAL_SONGS} += $tsong;
-   $self->{ACOUNTER}    += $taver;
-   $self->{INDEX}++;
-   # Return the parse object.
-   return $self;
-}
-
-sub reset {
-   # reset the object
-   my $self = shift;
-      $self->{'_M3U_'}      = [];
-      $self->{TOTAL_FILES}  = 0;
-      $self->{TOTAL_TIME}   = 0;
-      $self->{TOTAL_SONGS}  = 0;
-      $self->{AVERAGE_TIME} = 0;
-      $self->{ACOUNTER}     = 0;
-      $self->{INDEX}        = 0;
-   return $self if defined wantarray;
-}
-
-sub result {
-   my $self = shift;
-   return(wantarray ? @{$self->{'_M3U_'}} : $self->{'_M3U_'});
-}
-
-sub _locate_file {
-   require File::Spec;
-
-   my $self = shift;
-   my $file = shift;
-   if ($file !~ m{[\\/]}) {
-      # if $file does not have a slash in it then it is in the cwd.
-      # don't know if this code is valid in some other filesystems.
-      require Cwd;
-      $file = Cwd::getcwd()."/".$file;
-   }
-   return File::Spec->canonpath($file);
-}
-
-sub _search {
-   my $self = shift;
-   my $path = shift;
-   my $id3  = shift;
-   return(0) unless( $id3 or $path);
-   my $search = quotemeta($self->{search_string});
-   # Try a basic case-insensitive match:
-   return(1) if($id3 =~ /$search/i or $path =~ /$search/i);
-   return(0);
-}
-
-sub _escape {
-   my $self = shift;
-   my $text = shift || return '';
-   #$bad .= chr $_ for (1..8,11,12,14..31,127..144,147..159);$text =~ s/[$bad]//gs;
-   my %escape = (
-              '&' => '&amp;',
-              '"' => '&quot;',
-              '<' => '&lt;',
-              '>' => '&gt;',
-   );
-   $text =~ s,\Q$_,$escape{$_},gs foreach keys %escape;
-   return $text;
-}
-
-sub info {
-   # Instead of direct accessing to object tables, use this method.
-   my $self = shift;
-   my @drive;
-   for (my $i = 0; $i <= $#{ $self->{'_M3U_'} }; $i++) {
-      push @drive, $self->{'_M3U_'}[$i]{drive};
-   }
-   return(
-          songs   => $self->{TOTAL_SONGS},
-          files   => $self->{TOTAL_FILES},
-          ttime   => $self->{TOTAL_TIME}    ? $self->_seconds($self->{TOTAL_TIME}) 
-                                            : 0,
-          average => $self->{AVERAGE_TIME} || 0,
-          drive   => [@drive],
-   );
-}
-
-sub _seconds {
-   # Format seconds if wanted.
-   my $self = shift;
-   my $all  = shift;
-   return $all    unless( $self->{seconds} eq 'format' and $all !~ /:/);
-   return '00:00' unless $all;
-      $all  = $all/60;
-   my $min  = int($all);
-   my $sec  = sprintf("%02d",int(($all - $min)*60));
-   my $hr;
-   if($min > 60) {
-      $all = $min/60;
-      $hr  = int $all;
-      $min = int(($all - $hr)*60);
-   }
-   $min = sprintf("%02d",$min);
-   return $hr ? "$hr:$min:$sec" : "$min:$sec";
-}
-
-sub export {
-   my $self      = shift;
-   my %opt       = scalar(@_) % 2 ? () : (@_);
-   my $format    = $opt{'-format'}    || $self->{'expformat'}    || 'html';
-   my $file      = $opt{'-file'}      || sprintf('mp3_m3u%s.%s', $self->{EXPORTF}, $format);
-   my $encoding  = $opt{'-encoding'}  || $self->{'encoding'}     || 'ISO-8859-1';
-   my $drives    = $opt{'-drives'}    || $self->{'expdrives'}    || 'on';
-   my $overwrite = $opt{'-overwrite'} || $self->{'overwrite'}    ||  0; # global overwrite || local overwrite || don't overwrite
-   my $to_scalar = $opt{'-toscalar'} || $self->{'exptoscalar'} ||  0;
-      $file      = $self->_locate_file($file) unless $to_scalar;
-   my $fh;
-   if ($to_scalar) {
-      die "-toscalar must be a SCALAR ref!" unless ref $to_scalar and ref $to_scalar eq 'SCALAR';   
-   }
-   unless ($to_scalar) {
-      if (-e $file and not $overwrite) {
-         die "The export file '$file' exists on disk and you didn't select to overwrite it!";
-      }
-      require IO::File;
-      $fh = IO::File->new;
-      $fh->open("> $file") or die "I can't open export file '$file' for writing: $!"; 
-   }
-   my($cd,$m3u);
-   my $OUTPUT = '';
-   if ($format eq 'xml') {
-      $self->{TOTAL_TIME} = $self->_seconds($self->{TOTAL_TIME}) if $self->{TOTAL_TIME} > 0;
-      $OUTPUT .= sprintf qq~<?xml version="1.0" encoding="%s" ?>\n~, $encoding;
-      $OUTPUT .= sprintf qq~<m3u lists="%s" songs="%s" time="%s" average="%s">\n~, $self->{TOTAL_FILES}, $self->{TOTAL_SONGS}, $self->{TOTAL_TIME}, $self->{AVERAGE_TIME};
-      my $sc = 0;
-      foreach $cd (@{ $self->{'_M3U_'} }) {
-         $sc = $#{$cd->{data}}+1;
-         next unless $sc;
-         $OUTPUT .= sprintf qq~<list name="%s" drive="%s" songs="%s">\n~, $cd->{list}, $cd->{drive}, $sc;
-         foreach $m3u (@{ $cd->{data} }) { 
-            $OUTPUT .= sprintf qq~<song id3="%s" time="%s">%s</song>\n~, $self->_escape($m3u->[ID3]) || '',$m3u->[LEN] || '',$self->_escape($m3u->[PATH]);
-         }
-         $OUTPUT .= "</list>\n";
-         $sc = 0;
-      }
-      $OUTPUT .= "</m3u>\n";
-   } else {
-      require Text::Template;
-      # I don't think that weird numbers in the html mean anything 
-      # to anyone. So, if you didn't want to format seconds in your 
-      # code, I'm overriding it here (only for export(); Outside 
-      # export(), you'll get the old value):
-      local $self->{seconds} = 'format';
-      my %t;
-      ($t{up},$t{cd},$t{data},$t{down}) = split /<!-- MP3DATASPLIT -->/, $self->_template;
-      foreach (keys %t) {
-         $t{$_} =~ s,^\s+,,gs;
-         $t{$_} =~ s,\s+$,,gs;
-      }
-      my $tmptime = $self->{TOTAL_TIME} ? $self->_seconds($self->{TOTAL_TIME}) : undef;
-      my @tmptime;
-      if ($tmptime) {
-         @tmptime = split /:/,$tmptime;
-         unshift @tmptime, 'Z' unless $#tmptime > 1;
-      }
-      my $HTML = {
-              ENCODING    => $encoding,
-              SONGS       => $self->{TOTAL_SONGS},
-              TOTAL       => $self->{TOTAL_FILES},
-              AVERTIME    => $self->{AVERAGE_TIME} ? $self->_seconds($self->{AVERAGE_TIME}) : '<i>Unknown</i>',
-              FILE        => $to_scalar ? '' : $self->_locate_file($file),
-              TOTAL_FILES => $self->{TOTAL_FILES},
-              TOTAL_TIME  => @tmptime ? [@tmptime] : '',
-      };
-
-      $OUTPUT .= $self->_tcompile(template => $t{up}, params=> {HTML => $HTML});
-      my($song,$cdrom, $dlen);
-      foreach $cd (@{ $self->{'_M3U_'} }) {
-         next if($#{$cd->{data}} < 0);
-         $cdrom .= "$cd->{drive}\\" unless($drives eq 'off');
-         $cdrom .= $cd->{list};
-         $OUTPUT .= sprintf $t{cd}."\n", $cdrom;
-         foreach $m3u (@{ $cd->{data} }) {
-            $song = $m3u->[ID3];
-            unless($song) {
-               $song = (split /\\/, $m3u->[PATH])[-1] || $m3u->[PATH];
-               $song = (split /\./, $song       )[ 0] || $song;
-            }
-            $dlen = $m3u->[LEN] ? $self->_seconds($m3u->[LEN]) : '&nbsp;';
-            $song = $song       ? $self->_escape($song)        : '&nbsp;';
-            $OUTPUT .= sprintf "%s\n", $self->_tcompile(template => $t{data}, params=> {data => {len => $dlen, song => $song}});
-         }
-         $cdrom = '';
-      }
-      $OUTPUT .= $t{down};
-   }
-   if($to_scalar) {
-      $$to_scalar = $OUTPUT;
-   } else {
-      print $fh $OUTPUT;
-      $fh->close;
-   }
-   $self->{EXPORTF}++;
-   return $self if defined wantarray;
-}
-
-# compile template
-sub _tcompile {
-   my $self     = shift;
-   my $class    = ref $self;
-   die "Invalid number of parameters!" if scalar @_ % 2;
-   my %opt      = @_;
-   my $template = Text::Template->new(TYPE       => 'STRING', 
-                                      SOURCE     => $opt{template},
-                                      DELIMITERS => ['<%', '%>'],
-                                      ) or die "Couldn't construct the HTML template: $Text::Template::ERROR";
-   my(@globals, $prefix, $ref);
-   foreach (keys %{ $opt{params} }) {
-      if ($ref = ref $opt{params}->{$_}) {
-            if ($ref eq 'HASH')  {$prefix = '%'}
-         elsif ($ref eq 'ARRAY') {$prefix = '@'}
-         else                    {$prefix = '$'}
-      }
-      else {$prefix = '$'}
-      push @globals, $prefix . $_;
-   }
-
-   my $text = $template->fill_in(PACKAGE => $class . '::Dummy',
-                                 PREPEND => sprintf('use strict;use vars qw[%s];', join(" ", @globals)),
-                                 HASH    => $opt{params},
-              ) or die "Couldn't fill in template: $Text::Template::ERROR";
-   return $text;
-}
-
-# HTML template code
-sub _template {
-   return <<'MP3M3UParserTemplate';
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-   "http://www.w3.org/TR/html4/loose.dtd">
-<html>
- <head>
-   <title>MP3::M3U::Parser Generated PlayList</title>
-   <meta name="Generator" content="MP3::M3U::Parser">
-   <meta http-equiv="content-type" content="text/html; charset=<%$HTML{ENCODING}%>">
-
-   <style type="text/css">
-   <!--
-      body   { background   : #000040;
-               font-family  : font1, Arial, serif;
-               color        : #FFFFFF;
-               font-size    : 10pt;    }
-      td     { background   : none;
-               font-family  : Arial, serif;
-               color        : #FFFFFF;
-               font-size    : 13px;    }
-      hr     { background   : none;
-               color        : #FFBF00; }
-      .para1 { margin-top   : -42px;
-               margin-left  : 350px;
-               margin-right : 10px;
-               font-family  : font2, Arial, serif;
-               font-size    : 30px; 
-               line-height  : 35px;
-               background   : none;
-               color        : #E1E1E1;
-               text-align   : left;    }
-      .para2 { margin-top   : 15px;
-               margin-left  : 15px;
-               margin-right : 50px;
-               font-family  : font1, Arial Black, serif;
-               font-size    : 50px;
-               line-height  : 40px;
-               background   : none;
-               color        : #004080;
-               text-align   : left;    }
-      .t     { font-family  : Arial, serif;
-               background   : none;
-               color        : #FFBF00;
-               font-size    : 13px;    }
-      .ts    { font-family  : Arial, serif;
-               color        : #FFBF00;
-               background   : none;
-               font-size    : 10px;    }
-      .s     { font-family  : Arial, serif;
-               background   : none;
-               color        : #FFFFFF;
-               font-size    : 13px;    }
-      .info  { font-family  : Arial, serif;
-               background   : none;
-               color        : #409FFF;
-               font-size    : 10px;    }
-      .infob { font-family  : Arial, serif;
-               background   : none;
-               color        : #FFBF00;
-               font-size    : 15px;    }
-    -->
-   </style>
-
- </head>
-
-<body>
-
- <div align="center">
-  <div class="para2" align="center"><p>MP3::M3U::Parser</p></div>
-  <div class="para1" align="center"><p>playlist</p></div>
- </div>
-
-<hr align="left" width="90%" noshade="noshade" size="1">
- <div align="left">
-
-  <table border="0" cellspacing="0" cellpadding="0" width="98%">
-   <tr><td>
-    <span class="ts"><%$HTML{SONGS}%></span> <span class="info"> tracks and 
-    <span class="ts"><%$HTML{TOTAL}%></span> Lists in playlist, 
-      average track length: </span> 
-      <span class="ts"><%$HTML{AVERTIME}%></span><span class="info">.</span>
-     <br>
-    <span class="info">Playlist length: </span><%
-   my $time;
-   if ($HTML{TOTAL_TIME}) {
-      my @time = @{$HTML{TOTAL_TIME}};
-      $time = qq~<span class="ts"  > $time[0] </span>
-                 <span class="info"> hours    </span>~ if $time[0] ne 'Z';
-      $time .= qq~
-            <span class="ts"  > $time[1] </span>
-            <span class="info"> minutes  </span>
-            <span class="ts"  > $time[2] </span>
-            <span class="info"> seconds. </span>~;
-   } else {
-      $time = qq~<span class="ts"><i>Unknown</i></span><span class="info">.</span>~;
-   }
-   $time;
-
-     %><br>
-    <% 
-      qq~<span class="info">Right-click <a href="file://$HTML{FILE}">here</a>
-      to save this HTML file.</span>~ if $HTML{FILE}
-    %>
-    </td>
-   </tr>
- </table>
-
-</div>
-<blockquote>
-<p><span class="infob"><big><% 
-$HTML{TOTAL_FILES} > 1 ? "Playlists and Files" : "Playlist files"; 
-%>:</big></span></p>
-
-<table border="0" cellspacing="1" cellpadding="2">
-
-<!-- MP3DATASPLIT -->
-<tr><td colspan="2"><b>%s</b></td></tr>
-<!-- MP3DATASPLIT -->
-<tr><td><span class="t"><%$data{len}%></span></td><td><%$data{song}%></td></tr>
-<!-- MP3DATASPLIT -->
-
-  </table>
-</blockquote>
-<hr align="left" width="90%" noshade size="1">
-<span class="s">This HTML File is based on 
-<a href="http://www.winamp.com">WinAmp</a>`s HTML List.</span>
-</body>
-</html>
-MP3M3UParserTemplate
-}
-
-sub AUTOLOAD {
-   my $self = shift;
-   my $name = $AUTOLOAD;
-      $name =~ s/.*://;
-   my $class = ref($self) || __PACKAGE__;
-   die $class . " has no method called '$name'!";
-}
-
-sub DESTROY {}
-
-package MP3::M3U::Parser::Dummy;
-
-1;
-
-__END__;
-
-=head1 NAME
-
-MP3::M3U::Parser - MP3 playlist parser.
-
-=head1 SYNOPSIS
-
-   use MP3::M3U::Parser;
-   my $parser = MP3::M3U::Parser->new(%options);
-
-   $parser->parse(\*FILEHANDLE, \$scalar, "/path/to/playlist.m3u");
-   my $result = $parser->result;
-   my %info   = $parser->info;
-
-   $parser->export(-format   => 'xml',
-                   -file     => "/path/mp3.xml",
-                   -encoding => 'ISO-8859-9');
-
-   $parser->export(-format   => 'html',
-                   -file     => "/path/mp3.html",
-                   -drives   => 'off');
-
-   # convert all m3u files to individual html files.
-   foreach (<*.m3u>) {
-      $parser->parse($_)->export->reset;
-   }
-
-   # convert all m3u files to one big html file.
-   foreach (<*.m3u>) {
-      $parser->parse($_);
-   }
-   $parser->export;
-
-=head1 DESCRIPTION
-
-B<MP3::M3U::Parser> is a parser for M3U mp3 playlist files. 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 B<xml> or B<html>.
-
-=head2 Methods
-
-=head3 B<new>
-
-The object constructor. Takes several arguments like:
-
-=over 4
-
-=item C<-seconds>
-
-Format the seconds returned from parsed file? if you set this to the value 
-C<format>, it will convert the seconds to a format like C<MM:SS> or C<H:MM:SS>.
-Else: you get the time in seconds like; I<256> (if formatted: I<04:15>).
-
-=item C<-search>
-
-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 subclass this module and write your own C<_search> method
-(notice the underscore in the method name). See the tests for a subclassing
-example.
-
-=item C<-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 and removes that drive letter 
-(if there is a drive letter) from the real file path. If there is no drive 
-letter (eg: under linux there is no such thing, or you saved m3u file into 
-the same volume as your mp3s), 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 'C<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; C<export> does take the drive letters from the drive tables. 
-So, you can not use the drive area in the exported xml (for example).
-
-=item C<-overwrite>
-
-Same as the C<-overwrite> option in L<export|/export> but C<new> sets this 
-C<export> option globally.
-
-=item C<-encoding>
-
-Same as the C<-encoding> option in L<export|/export> but C<new> sets this 
-C<export> option globally.
-
-=item C<-expformat>
-
-Same as the C<-format> option in L<export|/export> but C<new> sets this 
-C<export> option globally.
-
-=item C<-expdrives>
-
-Same as the C<-drives> option in L<export|/export> but C<new> sets this 
-C<export> option globally.
-
-=back
-
-=head3 B<parse>
-
-It takes a list of arguments. The list can include file paths, 
-scalar references or filehandle references. You can mix these 
-types. Module interface can handle them correctly.
-
-   open FILEHANDLE, ...
-   $parser->parse(\*FILEHANDLE);
-
-or with new versions of perl:
-
-   open my $fh, ...
-   $parser->parse($fh);
-
-   my $scalar = "#EXTM3U\nFoo - bar.mp3";
-   $parser->parse(\$scalar);
-
-or
-
-   $parser->parse("/path/to/some/playlist.m3u");
-
-or
-
-   $parser->parse("/path/to/some/playlist.m3u",\*FILEHANDLE,\$scalar);
-
-Note that globs and scalars are passed as references.
-
-Returns the object itself.
-
-=head3 B<result>
-
-Must be called after C<parse>. Returns the result set created from
-the parsed data(s). Returns the data as an array or arrayref.
-
-   $result = $parser->result;
-   @result = $parser->result;
-
-Data structure is like this:
-
-   $VAR1 = [
-             {
-               'drive' => 'G:',
-               'file' => '/path/to/mylist.m3u',
-               'data' => [
-                           [
-                             'mp3\Singer - Song.mp3',
-                             'Singer - Song',
-                             232,
-                             'Singer',
-                             'Song'
-                           ],
-                           # other songs in the list
-                         ],
-               'total' => '3',
-               'list' => 'mylist'
-             },
-             # other m3u list
-           ];
-
-Each playlist is added as a hashref:
-
-   $pls = {
-           drive => "Drive letter if available",
-           file  => "Path to the parsed m3u file or generic name if GLOB/SCALAR",
-           data  => "Songs in the playlist",
-           total => "Total number of songs in the playlist",
-           list  => "name of the list",
-   }
-
-And the C<data> key is an AoA:
-
-   data => [
-            ["MP3 PATH INFO", "ID3 INFO","TIME","ARTIST","SONG"],
-            # other entries...
-            ]
-
-You can use the Data::Dumper module to see the structure yourself:
-
-   use Data::Dumper;
-   print Dumper $result;
-
-=head3 B<info>
-
-You must call this after calling L<parse|/parse>. It returns an info hash 
-about the parsed data.
-
-   my %info = $parser->info;
-
-The keys of the C<%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 an arrayref, while others are strings. 
-
-   printf "Drive letter for first list is %s\n", $info{drive}->[0];
-
-But, maybe you do not want to use the C<$info{drive}> table; see C<-parse_path> 
-option in L<new|/new>.
-
-=head3 B<export>
-
-Exports the parsed data to a format. The format can be C<xml> or C<html>. 
-The HTML File' s style is based on the popular mp3 player B<WinAmp>' s 
-HTML List file. Takes several arguments:
-
-=over 4
-
-=item C<-file>
-
-The full path to the file you want to write the resulting data. 
-If you do not set this parameter, a generic name will be used.
-
-=item C<-format>
-
-Can be C<xml> or C<html>. Default is C<html>.
-
-=item C<-encoding>
-
-The exported C<xml> file's encoding. Default is B<ISO-8859-1>. 
-See L<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.
-
-=item C<-drives>
-
-Only required for the html format. If set to C<off>, you will not 
-see the drive information in the resulting html file. Default is 
-C<on>. Also see C<-parse_path> option in L<new|/new>.
-
-=item C<-overwrite>
-
-If the file to export exists on the disk and you didn't set this 
-parameter to a true value, C<export> will die with an error.
-
-If you set this parameter to a true value, the named file will be 
-overwritten if already exists. Use carefully.
-
-Has no effect if you use C<-toscalar> option.
-
-=item C<-toscalar>
-
-With the default configuration, C<export> method will dump the 
-exported data to a disk file, but you can alter this behaviour 
-if you pass this parameter with a reference to a scalar.
-
-   $parser->export(-toscalar => \$dumpvar);
-   # then do something with $dumpvar
-
-=back
-
-Returns the object itself.
-
-=head3 B<reset>
-
-Resets the parser object and returns the object itself. Can be usefull 
-when exporting to html.
-
-   $parser->parse($fh       )->export->reset;
-   $parser->parse(\$scalar  )->export->reset;
-   $parser->parse("file.m3u")->export->reset;
-
-Will create individual files while this code
-
-   $parser->parse($fh       )->export;
-   $parser->parse(\$scalar  )->export;
-   $parser->parse("file.m3u")->export;
-
-creates also individual files but, file2 content will include 
-C<$fh> + C<$scalar> data and file3 will include 
-C<$fh> + C<$scalar> + C<file.m3u> data.
-
-=head2 Subclassing
-
-You may want to subclass the module to implement a more advanced
-search or to change the HTML template.
-
-To override the default search method create a C<_search> method 
-in your class and to override the default template create a C<_template> 
-method in your class.
-
-See the tests in the distribution for examples.
-
-=head2 Error handling
-
-Note that, if there is an error, the module will die with that error. So, 
-using C<eval> for all method calls can be helpful if you don't want to die:
-
-
-   eval {$parser->parse(@list)}
-   die "Parser error: $@" if $@;
-
-As you can see, if there is an error, you can catch this with C<eval> and 
-access the error message with the special Perl variable C<$@>.
-
-=head1 EXAMPLES
-
-See the tests in the distribution for example codes. If you don't have 
-the distro, you can download it from CPAN.
-
-=head2 TIPS
-
-=over 4
-
-=item B<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 "B<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.
-
-=item B<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.
-
-=back
-
-=head1 CAVEATS
-
-v2 is B<not> compatible with v1x. v2 of this module breaks
-old code. See the I<Changes> file for details.
-
-=head1 BUGS
-
-=over 4
-
-=item * 
-
-HTML and XML escaping is limited to these characters: 
-E<amp> E<quot> E<lt> E<gt>.
-
-=back 
-
-Contact the author if you find any other bugs.
-
-=head1 SEE ALSO
-
-L<MP3::M3U>.
-
-=head1 AUTHOR
-
-Burak G�rsoy, E<lt>burakE<64>cpan.orgE<gt>
-
-=head1 COPYRIGHT
-
-Copyright 2003-2006 Burak G�rsoy. All rights reserved.
-
-=head1 LICENSE
-
-This library is free software; you can redistribute it and/or modify 
-it under the same terms as Perl itself, either Perl version 5.8.8 or, 
-at your option, any later version of Perl 5 you may have available.
-
-=cut
+package MP3::M3U::Parser;
+use strict;
+use vars qw[$VERSION $AUTOLOAD];
+
+# Data table key map
+use constant PATH    => 0;
+use constant ID3     => 1;
+use constant LEN     => 2;
+use constant ARTIST  => 3;
+use constant SONG    => 4;
+
+use constant MAXDATA => 4; # Maximum index number of the data table
+
+$VERSION = '2.21';
+
+sub new {
+   # -parse_path -seconds -search -overwrite
+   my $class = shift;
+   my %o     = scalar(@_) % 2 ? () : (@_); # options
+   my $self  = {
+                _M3U_         => [], # for parse()
+                TOTAL_FILES   =>  0, # Counter
+                TOTAL_TIME    =>  0, # In seconds
+                TOTAL_SONGS   =>  0, # Counter
+                AVERAGE_TIME  =>  0, # Counter
+                ACOUNTER      =>  0, # Counter
+                ANON          =>  0, # Counter for SCALAR & GLOB M3U
+                INDEX         =>  0, # index counter for _M3U_
+                EXPORTF       =>  0, # Export file name counter for anonymous exports
+                seconds       => $o{'-seconds'}    || '', # format or get seconds.
+                search_string => $o{'-search'}     || '', # search_string
+                parse_path    => $o{'-parse_path'} || '', # mixed list?
+                overwrite     => $o{'-overwrite'}  ||  0, # overwrite export file if exists?
+                encoding      => $o{'-encoding'}   || '', # leave it to export() if no param
+                expformat     => $o{'-expformat'}  || '', # leave it to export() if no param
+                expdrives     => $o{'-expdrives'}  || '', # leave it to export() if no param
+   };
+   if ($self->{search_string} and length($self->{search_string}) < 3) {
+      die "A search string must be at least three characters long!";
+   }
+   bless  $self, $class;
+   return $self;
+}
+
+sub parse {
+   my $self  = shift;
+   my @files = @_;
+   my $file;
+   foreach $file (@files) {
+      unless(ref $file) {
+         $file = $self->_locate_file($file);
+         -e $file or die "'$file' does not exist!";
+      }
+      $self->_parse_file($file);
+   }
+
+   # Average time of all the parsed songs:
+   $self->{AVERAGE_TIME} = ($self->{ACOUNTER} and $self->{TOTAL_TIME}) 
+                           ? $self->_seconds($self->{TOTAL_TIME}/$self->{ACOUNTER})
+                           : 0;
+   return $self if defined wantarray;
+}
+
+sub _parse_file {
+   # supports disk files, scalar variables and filehandles (typeglobs)
+   my $self = shift;
+   my $file = shift;
+   my $ref  = ref($file) || '';
+   if($ref and $ref !~ m[^(?:GLOB|SCALAR)$]) {
+      die "Unknown parameter of type '$ref' passed to parse()!";
+   }
+   my $cd;
+   unless ($ref) {
+      $cd = (split /[\\\/]/, $file)[-1];
+      $cd =~ s,\.m3u,,i;
+   }
+   my $i = $self->{INDEX};
+   my $this_file;
+   if ($ref) {
+      $this_file = 'ANON'.$self->{ANON}++;
+   } else {
+      $this_file = $self->_locate_file($file);
+   }
+   $self->{'_M3U_'}[$i] = {file  => $this_file,
+                           list  => $ref ? $this_file : ($cd || ''),
+                           drive => 'CDROM:',
+                           data  => [],
+                           total => 0,
+                           };
+   $self->{TOTAL_FILES} += 1; # Total lists counter
+   my $index             = 0; # Index number of the list array
+   # These three variables are used when there is a '-search' parameter.
+   # long: total_time, total_songs, total_average_time
+   my($ttime,$tsong,$taver) = (0,0,0);
+   # while loop variables. j: junk data.
+   my($m3u,$j,@song,$j2,$sec,$temp_sec);
+
+   my($fh, @fh);
+   if($ref eq 'GLOB') {
+      $fh = $file;
+   } elsif ($ref eq 'SCALAR') {
+      @fh = split /\n/, $$file;
+   } else {
+      # Open the file to parse:
+      require IO::File;
+      $fh = IO::File->new;
+      $fh->open("< $file") or die "I could't open '$file': $!" unless $ref;
+   }
+
+PREPROCESS:
+   while ($m3u = ($ref eq 'SCALAR') ? (shift @fh) : <$fh>) {
+      # First line is just a comment. But we need it to validate
+      # the file as a m3u playlist file.
+      chomp $m3u;
+      if($m3u !~ m[^#EXTM3U]) {
+         die $ref ? "The '$ref' parameter you have passed does not contain valid m3u data!"
+                  : "'$file' is not a valid m3u file!";
+      }
+      last PREPROCESS;
+   }
+
+   my $dkey   =  $self->{'_M3U_'}[$i]{data};  # data key
+   my $device = \$self->{'_M3U_'}[$i]{drive}; # device letter
+
+RECORD: 
+   while ($m3u = ($ref eq 'SCALAR') ? (shift @fh) : <$fh>) {
+      chomp $m3u;
+      next unless $m3u; # Record may be blank if it is not a disk file.
+      $#{$dkey->[$index]} = MAXDATA; # For the absence of EXTINF line.
+      # If the extra information exists, parse it:
+      if($m3u =~ m!#EXTINF!i) {
+         ($j ,@song) = split(/\,/,$m3u);
+         ($j ,$sec)  = split(/:/,$j);
+         $ttime     += $sec;
+         $temp_sec   = $sec;
+         $dkey->[$index][ID3] = join(",", @song);
+         $dkey->[$index][LEN] = $self->_seconds($sec || 0);
+         $taver++;
+         next RECORD; # jump to path info
+      }
+
+      # Get the drive and path info.      Possible cases are:
+      if($m3u =~ m{^\w:[\\/](.+?)$}x or # C:\mp3\Singer - Song.mp3
+         $m3u =~ m{^   [\\/]([^\\/].+?)$}x or # \mp3\Singer - Song.mp3
+         $m3u =~ m{^        (.+?)$}x    # Singer - Song.mp3
+         ) {
+         $dkey->[$index][PATH] = $self->{parse_path} eq 'asis' ? $m3u : $1;
+         $$device = $1 if $$device eq 'CDROM:' and $m3u =~ m[^(\w:)];
+         $tsong++;
+      }
+
+      # Try to extract artist and song info 
+      # and remove leading and trailing spaces
+      # Some artist names can also have a "-" in it. 
+      # For this reason; require that the data has " - " in it. 
+      # ... but the spaces can be one or more.
+      # So, things like "artist-song" does not work...
+      my($artist, @xsong) = split /\s{1,}-\s{1,}/, $dkey->[$index][ID3] || $dkey->[$index][PATH];
+      if ($artist) {
+         $artist =~ s[^\s+][];
+         $artist =~ s[\s+$][];
+         $artist =~ s[.*[\\/]][]; # remove path junk
+         $dkey->[$index][ARTIST] = $artist;
+      }
+      if (@xsong) {
+         my $song = join '-', @xsong;
+         $song =~ s[^\s+][];
+         $song =~ s[\s+$][];
+         $song =~ s[\.[a-zA-Z0-9]+$][]; # remove extension if exists
+         $dkey->[$index][SONG] = $song;
+      }
+
+      # convert undefs to empty strings
+      foreach my $CHECK (0..MAXDATA) {
+         $dkey->[$index][$CHECK] = '' unless defined $dkey->[$index][$CHECK];
+      }
+
+      # If we are searching something:
+      if($self->{search_string}) {
+         if($self->_search($dkey->[$index][PATH], $dkey->[$index][ID3])) {
+            $index++; # If we got a match, increase the index
+         } else { # If we didnt matched anything, resize these counters ...
+            $tsong--;
+            $taver--;
+            $ttime -= $temp_sec;
+            delete $dkey->[$index]; # ... and delete the empty index
+         }
+      } else {
+         $index++; # If we are not searching, just increase the index
+      }
+   }
+   # Close the file
+   $fh->close unless $ref;
+
+   # Calculate the total songs in the list:
+   $self->{'_M3U_'}[$i]{total} = $#{$self->{'_M3U_'}[$i]{data}} + 1;
+
+   # Adjust the global counters:
+   $self->{TOTAL_FILES}-- if($self->{search_string} and $#{ $self->{'_M3U_'}[$i]{data} } < 0);
+   $self->{TOTAL_TIME}  += $ttime;
+   $self->{TOTAL_SONGS} += $tsong;
+   $self->{ACOUNTER}    += $taver;
+   $self->{INDEX}++;
+   # Return the parse object.
+   return $self;
+}
+
+sub reset {
+   # reset the object
+   my $self = shift;
+      $self->{'_M3U_'}      = [];
+      $self->{TOTAL_FILES}  = 0;
+      $self->{TOTAL_TIME}   = 0;
+      $self->{TOTAL_SONGS}  = 0;
+      $self->{AVERAGE_TIME} = 0;
+      $self->{ACOUNTER}     = 0;
+      $self->{INDEX}        = 0;
+   return $self if defined wantarray;
+}
+
+sub result {
+   my $self = shift;
+   return(wantarray ? @{$self->{'_M3U_'}} : $self->{'_M3U_'});
+}
+
+sub _locate_file {
+   require File::Spec;
+
+   my $self = shift;
+   my $file = shift;
+   if ($file !~ m{[\\/]}) {
+      # if $file does not have a slash in it then it is in the cwd.
+      # don't know if this code is valid in some other filesystems.
+      require Cwd;
+      $file = Cwd::getcwd()."/".$file;
+   }
+   return File::Spec->canonpath($file);
+}
+
+sub _search {
+   my $self = shift;
+   my $path = shift;
+   my $id3  = shift;
+   return(0) unless( $id3 or $path);
+   my $search = quotemeta($self->{search_string});
+   # Try a basic case-insensitive match:
+   return(1) if($id3 =~ /$search/i or $path =~ /$search/i);
+   return(0);
+}
+
+sub _escape {
+   my $self = shift;
+   my $text = shift || return '';
+   #$bad .= chr $_ for (1..8,11,12,14..31,127..144,147..159);$text =~ s/[$bad]//gs;
+   my %escape = (
+              '&' => '&amp;',
+              '"' => '&quot;',
+              '<' => '&lt;',
+              '>' => '&gt;',
+   );
+   $text =~ s,\Q$_,$escape{$_},gs foreach keys %escape;
+   return $text;
+}
+
+sub info {
+   # Instead of direct accessing to object tables, use this method.
+   my $self = shift;
+   my @drive;
+   for (my $i = 0; $i <= $#{ $self->{'_M3U_'} }; $i++) {
+      push @drive, $self->{'_M3U_'}[$i]{drive};
+   }
+   return(
+          songs   => $self->{TOTAL_SONGS},
+          files   => $self->{TOTAL_FILES},
+          ttime   => $self->{TOTAL_TIME}    ? $self->_seconds($self->{TOTAL_TIME}) 
+                                            : 0,
+          average => $self->{AVERAGE_TIME} || 0,
+          drive   => [@drive],
+   );
+}
+
+sub _seconds {
+   # Format seconds if wanted.
+   my $self = shift;
+   my $all  = shift;
+   return $all    unless( $self->{seconds} eq 'format' and $all !~ /:/);
+   return '00:00' unless $all;
+      $all  = $all/60;
+   my $min  = int($all);
+   my $sec  = sprintf("%02d",int(($all - $min)*60));
+   my $hr;
+   if($min > 60) {
+      $all = $min/60;
+      $hr  = int $all;
+      $min = int(($all - $hr)*60);
+   }
+   $min = sprintf("%02d",$min);
+   return $hr ? "$hr:$min:$sec" : "$min:$sec";
+}
+
+sub export {
+   my $self      = shift;
+   my %opt       = scalar(@_) % 2 ? () : (@_);
+   my $format    = $opt{'-format'}    || $self->{'expformat'}    || 'html';
+   my $file      = $opt{'-file'}      || sprintf('mp3_m3u%s.%s', $self->{EXPORTF}, $format);
+   my $encoding  = $opt{'-encoding'}  || $self->{'encoding'}     || 'ISO-8859-1';
+   my $drives    = $opt{'-drives'}    || $self->{'expdrives'}    || 'on';
+   my $overwrite = $opt{'-overwrite'} || $self->{'overwrite'}    ||  0; # global overwrite || local overwrite || don't overwrite
+   my $to_scalar = $opt{'-toscalar'} || $self->{'exptoscalar'} ||  0;
+      $file      = $self->_locate_file($file) unless $to_scalar;
+   my $fh;
+   if ($to_scalar) {
+      die "-toscalar must be a SCALAR ref!" unless ref $to_scalar and ref $to_scalar eq 'SCALAR';   
+   }
+   unless ($to_scalar) {
+      if (-e $file and not $overwrite) {
+         die "The export file '$file' exists on disk and you didn't select to overwrite it!";
+      }
+      require IO::File;
+      $fh = IO::File->new;
+      $fh->open("> $file") or die "I can't open export file '$file' for writing: $!"; 
+   }
+   my($cd,$m3u);
+   my $OUTPUT = '';
+   if ($format eq 'xml') {
+      $self->{TOTAL_TIME} = $self->_seconds($self->{TOTAL_TIME}) if $self->{TOTAL_TIME} > 0;
+      $OUTPUT .= sprintf qq~<?xml version="1.0" encoding="%s" ?>\n~, $encoding;
+      $OUTPUT .= sprintf qq~<m3u lists="%s" songs="%s" time="%s" average="%s">\n~, $self->{TOTAL_FILES}, $self->{TOTAL_SONGS}, $self->{TOTAL_TIME}, $self->{AVERAGE_TIME};
+      my $sc = 0;
+      foreach $cd (@{ $self->{'_M3U_'} }) {
+         $sc = $#{$cd->{data}}+1;
+         next unless $sc;
+         $OUTPUT .= sprintf qq~<list name="%s" drive="%s" songs="%s">\n~, $cd->{list}, $cd->{drive}, $sc;
+         foreach $m3u (@{ $cd->{data} }) { 
+            $OUTPUT .= sprintf qq~<song id3="%s" time="%s">%s</song>\n~, $self->_escape($m3u->[ID3]) || '',$m3u->[LEN] || '',$self->_escape($m3u->[PATH]);
+         }
+         $OUTPUT .= "</list>\n";
+         $sc = 0;
+      }
+      $OUTPUT .= "</m3u>\n";
+   } else {
+      require Text::Template;
+      # I don't think that weird numbers in the html mean anything 
+      # to anyone. So, if you didn't want to format seconds in your 
+      # code, I'm overriding it here (only for export(); Outside 
+      # export(), you'll get the old value):
+      local $self->{seconds} = 'format';
+      my %t;
+      ($t{up},$t{cd},$t{data},$t{down}) = split /<!-- MP3DATASPLIT -->/, $self->_template;
+      foreach (keys %t) {
+         $t{$_} =~ s,^\s+,,gs;
+         $t{$_} =~ s,\s+$,,gs;
+      }
+      my $tmptime = $self->{TOTAL_TIME} ? $self->_seconds($self->{TOTAL_TIME}) : undef;
+      my @tmptime;
+      if ($tmptime) {
+         @tmptime = split /:/,$tmptime;
+         unshift @tmptime, 'Z' unless $#tmptime > 1;
+      }
+      my $HTML = {
+              ENCODING    => $encoding,
+              SONGS       => $self->{TOTAL_SONGS},
+              TOTAL       => $self->{TOTAL_FILES},
+              AVERTIME    => $self->{AVERAGE_TIME} ? $self->_seconds($self->{AVERAGE_TIME}) : '<i>Unknown</i>',
+              FILE        => $to_scalar ? '' : $self->_locate_file($file),
+              TOTAL_FILES => $self->{TOTAL_FILES},
+              TOTAL_TIME  => @tmptime ? [@tmptime] : '',
+      };
+
+      $OUTPUT .= $self->_tcompile(template => $t{up}, params=> {HTML => $HTML});
+      my($song,$cdrom, $dlen);
+      foreach $cd (@{ $self->{'_M3U_'} }) {
+         next if($#{$cd->{data}} < 0);
+         $cdrom .= "$cd->{drive}\\" unless($drives eq 'off');
+         $cdrom .= $cd->{list};
+         $OUTPUT .= sprintf $t{cd}."\n", $cdrom;
+         foreach $m3u (@{ $cd->{data} }) {
+            $song = $m3u->[ID3];
+            unless($song) {
+               $song = (split /\\/, $m3u->[PATH])[-1] || $m3u->[PATH];
+               $song = (split /\./, $song       )[ 0] || $song;
+            }
+            $dlen = $m3u->[LEN] ? $self->_seconds($m3u->[LEN]) : '&nbsp;';
+            $song = $song       ? $self->_escape($song)        : '&nbsp;';
+            $OUTPUT .= sprintf "%s\n", $self->_tcompile(template => $t{data}, params=> {data => {len => $dlen, song => $song}});
+         }
+         $cdrom = '';
+      }
+      $OUTPUT .= $t{down};
+   }
+   if($to_scalar) {
+      $$to_scalar = $OUTPUT;
+   } else {
+      print $fh $OUTPUT;
+      $fh->close;
+   }
+   $self->{EXPORTF}++;
+   return $self if defined wantarray;
+}
+
+# compile template
+sub _tcompile {
+   my $self     = shift;
+   my $class    = ref $self;
+   die "Invalid number of parameters!" if scalar @_ % 2;
+   my %opt      = @_;
+   my $template = Text::Template->new(TYPE       => 'STRING', 
+                                      SOURCE     => $opt{template},
+                                      DELIMITERS => ['<%', '%>'],
+                                      ) or die "Couldn't construct the HTML template: $Text::Template::ERROR";
+   my(@globals, $prefix, $ref);
+   foreach (keys %{ $opt{params} }) {
+      if ($ref = ref $opt{params}->{$_}) {
+            if ($ref eq 'HASH')  {$prefix = '%'}
+         elsif ($ref eq 'ARRAY') {$prefix = '@'}
+         else                    {$prefix = '$'}
+      }
+      else {$prefix = '$'}
+      push @globals, $prefix . $_;
+   }
+
+   my $text = $template->fill_in(PACKAGE => $class . '::Dummy',
+                                 PREPEND => sprintf('use strict;use vars qw[%s];', join(" ", @globals)),
+                                 HASH    => $opt{params},
+              ) or die "Couldn't fill in template: $Text::Template::ERROR";
+   return $text;
+}
+
+# HTML template code
+sub _template {
+   return <<'MP3M3UParserTemplate';
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+   <title>MP3::M3U::Parser Generated PlayList</title>
+   <meta name="Generator" content="MP3::M3U::Parser">
+   <meta http-equiv="content-type" content="text/html; charset=<%$HTML{ENCODING}%>">
+
+   <style type="text/css">
+   <!--
+      body   { background   : #000040;
+               font-family  : font1, Arial, serif;
+               color        : #FFFFFF;
+               font-size    : 10pt;    }
+      td     { background   : none;
+               font-family  : Arial, serif;
+               color        : #FFFFFF;
+               font-size    : 13px;    }
+      hr     { background   : none;
+               color        : #FFBF00; }
+      .para1 { margin-top   : -42px;
+               margin-left  : 350px;
+               margin-right : 10px;
+               font-family  : font2, Arial, serif;
+               font-size    : 30px; 
+               line-height  : 35px;
+               background   : none;
+               color        : #E1E1E1;
+               text-align   : left;    }
+      .para2 { margin-top   : 15px;
+               margin-left  : 15px;
+               margin-right : 50px;
+               font-family  : font1, Arial Black, serif;
+               font-size    : 50px;
+               line-height  : 40px;
+               background   : none;
+               color        : #004080;
+               text-align   : left;    }
+      .t     { font-family  : Arial, serif;
+               background   : none;
+               color        : #FFBF00;
+               font-size    : 13px;    }
+      .ts    { font-family  : Arial, serif;
+               color        : #FFBF00;
+               background   : none;
+               font-size    : 10px;    }
+      .s     { font-family  : Arial, serif;
+               background   : none;
+               color        : #FFFFFF;
+               font-size    : 13px;    }
+      .info  { font-family  : Arial, serif;
+               background   : none;
+               color        : #409FFF;
+               font-size    : 10px;    }
+      .infob { font-family  : Arial, serif;
+               background   : none;
+               color        : #FFBF00;
+               font-size    : 15px;    }
+    -->
+   </style>
+
+ </head>
+
+<body>
+
+ <div align="center">
+  <div class="para2" align="center"><p>MP3::M3U::Parser</p></div>
+  <div class="para1" align="center"><p>playlist</p></div>
+ </div>
+
+<hr align="left" width="90%" noshade="noshade" size="1">
+ <div align="left">
+
+  <table border="0" cellspacing="0" cellpadding="0" width="98%">
+   <tr><td>
+    <span class="ts"><%$HTML{SONGS}%></span> <span class="info"> tracks and 
+    <span class="ts"><%$HTML{TOTAL}%></span> Lists in playlist, 
+      average track length: </span> 
+      <span class="ts"><%$HTML{AVERTIME}%></span><span class="info">.</span>
+     <br>
+    <span class="info">Playlist length: </span><%
+   my $time;
+   if ($HTML{TOTAL_TIME}) {
+      my @time = @{$HTML{TOTAL_TIME}};
+      $time = qq~<span class="ts"  > $time[0] </span>
+                 <span class="info"> hours    </span>~ if $time[0] ne 'Z';
+      $time .= qq~
+            <span class="ts"  > $time[1] </span>
+            <span class="info"> minutes  </span>
+            <span class="ts"  > $time[2] </span>
+            <span class="info"> seconds. </span>~;
+   } else {
+      $time = qq~<span class="ts"><i>Unknown</i></span><span class="info">.</span>~;
+   }
+   $time;
+
+     %><br>
+    <% 
+      qq~<span class="info">Right-click <a href="file://$HTML{FILE}">here</a>
+      to save this HTML file.</span>~ if $HTML{FILE}
+    %>
+    </td>
+   </tr>
+ </table>
+
+</div>
+<blockquote>
+<p><span class="infob"><big><% 
+$HTML{TOTAL_FILES} > 1 ? "Playlists and Files" : "Playlist files"; 
+%>:</big></span></p>
+
+<table border="0" cellspacing="1" cellpadding="2">
+
+<!-- MP3DATASPLIT -->
+<tr><td colspan="2"><b>%s</b></td></tr>
+<!-- MP3DATASPLIT -->
+<tr><td><span class="t"><%$data{len}%></span></td><td><%$data{song}%></td></tr>
+<!-- MP3DATASPLIT -->
+
+  </table>
+</blockquote>
+<hr align="left" width="90%" noshade size="1">
+<span class="s">This HTML File is based on 
+<a href="http://www.winamp.com">WinAmp</a>`s HTML List.</span>
+</body>
+</html>
+MP3M3UParserTemplate
+}
+
+sub AUTOLOAD {
+   my $self = shift;
+   my $name = $AUTOLOAD;
+      $name =~ s/.*://;
+   my $class = ref($self) || __PACKAGE__;
+   die $class . " has no method called '$name'!";
+}
+
+sub DESTROY {}
+
+package MP3::M3U::Parser::Dummy;
+
+1;
+
+__END__;
+
+=head1 NAME
+
+MP3::M3U::Parser - MP3 playlist parser.
+
+=head1 SYNOPSIS
+
+   use MP3::M3U::Parser;
+   my $parser = MP3::M3U::Parser->new(%options);
+
+   $parser->parse(\*FILEHANDLE, \$scalar, "/path/to/playlist.m3u");
+   my $result = $parser->result;
+   my %info   = $parser->info;
+
+   $parser->export(-format   => 'xml',
+                   -file     => "/path/mp3.xml",
+                   -encoding => 'ISO-8859-9');
+
+   $parser->export(-format   => 'html',
+                   -file     => "/path/mp3.html",
+                   -drives   => 'off');
+
+   # convert all m3u files to individual html files.
+   foreach (<*.m3u>) {
+      $parser->parse($_)->export->reset;
+   }
+
+   # convert all m3u files to one big html file.
+   foreach (<*.m3u>) {
+      $parser->parse($_);
+   }
+   $parser->export;
+
+=head1 DESCRIPTION
+
+B<MP3::M3U::Parser> is a parser for M3U mp3 playlist files. 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 B<xml> or B<html>.
+
+=head2 Methods
+
+=head3 B<new>
+
+The object constructor. Takes several arguments like:
+
+=over 4
+
+=item C<-seconds>
+
+Format the seconds returned from parsed file? if you set this to the value 
+C<format>, it will convert the seconds to a format like C<MM:SS> or C<H:MM:SS>.
+Else: you get the time in seconds like; I<256> (if formatted: I<04:15>).
+
+=item C<-search>
+
+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 subclass this module and write your own C<_search> method
+(notice the underscore in the method name). See the tests for a subclassing
+example.
+
+=item C<-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 and removes that drive letter 
+(if there is a drive letter) from the real file path. If there is no drive 
+letter (eg: under linux there is no such thing, or you saved m3u file into 
+the same volume as your mp3s), 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 'C<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; C<export> does take the drive letters from the drive tables. 
+So, you can not use the drive area in the exported xml (for example).
+
+=item C<-overwrite>
+
+Same as the C<-overwrite> option in L<export|/export> but C<new> sets this 
+C<export> option globally.
+
+=item C<-encoding>
+
+Same as the C<-encoding> option in L<export|/export> but C<new> sets this 
+C<export> option globally.
+
+=item C<-expformat>
+
+Same as the C<-format> option in L<export|/export> but C<new> sets this 
+C<export> option globally.
+
+=item C<-expdrives>
+
+Same as the C<-drives> option in L<export|/export> but C<new> sets this 
+C<export> option globally.
+
+=back
+
+=head3 B<parse>
+
+It takes a list of arguments. The list can include file paths, 
+scalar references or filehandle references. You can mix these 
+types. Module interface can handle them correctly.
+
+   open FILEHANDLE, ...
+   $parser->parse(\*FILEHANDLE);
+
+or with new versions of perl:
+
+   open my $fh, ...
+   $parser->parse($fh);
+
+   my $scalar = "#EXTM3U\nFoo - bar.mp3";
+   $parser->parse(\$scalar);
+
+or
+
+   $parser->parse("/path/to/some/playlist.m3u");
+
+or
+
+   $parser->parse("/path/to/some/playlist.m3u",\*FILEHANDLE,\$scalar);
+
+Note that globs and scalars are passed as references.
+
+Returns the object itself.
+
+=head3 B<result>
+
+Must be called after C<parse>. Returns the result set created from
+the parsed data(s). Returns the data as an array or arrayref.
+
+   $result = $parser->result;
+   @result = $parser->result;
+
+Data structure is like this:
+
+   $VAR1 = [
+             {
+               'drive' => 'G:',
+               'file' => '/path/to/mylist.m3u',
+               'data' => [
+                           [
+                             'mp3\Singer - Song.mp3',
+                             'Singer - Song',
+                             232,
+                             'Singer',
+                             'Song'
+                           ],
+                           # other songs in the list
+                         ],
+               'total' => '3',
+               'list' => 'mylist'
+             },
+             # other m3u list
+           ];
+
+Each playlist is added as a hashref:
+
+   $pls = {
+           drive => "Drive letter if available",
+           file  => "Path to the parsed m3u file or generic name if GLOB/SCALAR",
+           data  => "Songs in the playlist",
+           total => "Total number of songs in the playlist",