SWMBO needed to build an .asx playlist (controller) on w32. Her existing tools were a grand PITA. So, this oneoff, which is probably non-novel. Nonetheless FWIW, in the hope that someone will find it useful.. and in even higher hopes that the wise Brethern will apply a CLUESTICK to any gaffs

UPDATE:Based on comments from jdporter, I've discovered that this will NOT find .mp3's with their system attribute set (why would they have that set? I dunno!) but does find them once they're modified attrib -s *.mp3.
And thanks will be given for further comments, corrections, problems!

#!C:/perl/bin -w # buildasx.pl # Build a (Windows) .asx playlist of .mp3s (Caveat: PRESUMED encoded a +t 128Kbps bitrate) # .mp3 ONLY: see TODO # subdir selected must not be more than 4 deep # ww 2007-05-07 # TODO ############################################################### +##################### # timing calculation is imprecise (seconds are approx) , but so far, a +ppears unimportant # Add support for .wav etc. # add UI to let user select dir and asx filename, defaulting to CWD an +d CWD's name (replacing hardcoded name at 20) # and maybe, replace regex for ident of dir with something better # END TODO as of 2007-05-07 ###################################################################### +##################### ##### Get the output of W32's dir cmd to @list $input = qx{dir} || print STDERR "Couldn't do qx{dir} \/n: $!"; @list = split (/\n/, $input); ##### Open output file (title.asx) and print headers open (ASX, ">title.asx") || die "Can't open title.asx for write: $!"; print ASX '<Asx Version = "3.0" >' . "\n" . '<Param Name = "Name" Valu +e = "title" />' . "\n" . '<Param Name = "AllowShuffle" Value = "yes" +/>' . "\n\n"; #### Now, use info from the dir at line 15 and capture those elements +needed for .asx for $list(@list) { if ($list =~ / ([A-X]: # BEGIN ($dir) CAPTURE: Drive l +etter \\[\s,A-Z,a-z,-,_,0-9]* # first child \\[\s,A-Z,a-z,-,_,0-9]* # second child \\[\s,A-Z,a-z,-,_,0-9]* # third child \\[\s,A-Z,a-z,-,_,0-9]*) # fourth child, END CAPTURE, EN +D of conditional test /x ) { # extended, END of conditional +test $dir = $1; next; } if ($list =~ / (\d{2}\/\d{2}\/\d{4} # CAPTURE_1 (create_date) \s+ # space(s) \d{2}:\d{2}[ap]) # create_time END_CAPTURE \s+ # space(s) (\d+,\d{3},\d{3}) # CAPTURE_2 (file_size) END_CAPTURE \s+ # space(s) ([A-Z,a-z,0-9] # CAPTURE_3: (file_name) first char of na +me [^.]* # anything not a literal period \. # literal (name-type separator) mp3) # title END_CAPTURE /xi ) { # extended, CaseInsens, END of conditiona +l test $file_name = $3; $file_size = $2; $create_date = $1; #### Convert filesize to (rough) duration in seconds and thence to min +utes:seconds:(kluded)decimals : if ($file_size) { $file_size =~ s/,//g; # remove commas, al +tho using "dir /-C" would save us this cleanup $sec_dec = $file_size/16000; # in seconds though + something 16,042..16,080 is more nearly precise $min_dec = ($sec_dec/60); # minutes and decim +al minutes $mins = int ( $min_dec); # drop the decimal +fraction if (length($mins) == 1) { # and prepend a zer +o if mins < 10 $mins = "0" . $mins; } $secs = $sec_dec % 60; # seconds NOT preci +se if (length($secs) == 1) { # and prepend a zer +o if secs < 10 $secs = "0" . $secs; } $duration = "00:$mins" . ":" . "$secs" . ".999"; # not e +xpecting any selection to exceed 59 min 59.999 sec # the " +.999" is a kludge to offset possible modulus truncation } else { $duration = "xx:xx:xx.000"; } #### now print specific entry: print ASX "\n<Entry> \n"; print ASX "<Title > $file_name </Title>\n"; print ASX '<Duration value = "' . "$duration" . '" />' . "\n\n" +. '<Param Name = "Name" Value = "' . "$file_name" . '" />' . "\n\n +" . '<Param Name = "MediaType" Value = "audio" />' . "\n\n" . '<Param Name = "MediaAttribute" Value = "0" />' . "\n\n" . '<Param Name = "Bitrate" Value = "128768" />' . "\n\n" . '<Param Name = "DigitallySecure" Value = "0" />' . "\n\n" . '<Param Name = "PlayCount" Value = "1" />' . "\n\n" . '<Param Name = "SourceURL" Value = "' . "$dir\\$file_name" . '" +/>' . "\n\n" . '<Param Name = "CreationDate" Value = "' . "$create_date" . '" / +>' . "\n\n" . '<Ref href = "' . "$dir\\$file_name" . '" />' . "\n" . '</Entry>' . "\n\n"; } # END OF conditional_a +ction } # END OF for #### Print close tag for .asx and close file print ASX '</Asx>'; close ASX; exit;
Cheers... and bash on!

Replies are listed 'Best First'.
Re: buildasx.pl (W32 Windows playlist builder)
by jdporter (Paladin) on May 07, 2007 at 21:26 UTC

    I don't know what I'm doing wrong, but your code doesn't work, as far as I can tell. It never finds any files. Is it expecting a commandline argument?

    Anyway, I re-wrote it using MP3::Info.

    use File::Find; use MP3::Info; use strict; use warnings; my $dir = shift() or die "Usage: $0 absoluteDir\n"; my $entries=''; find( \&process, $dir ); sub process { /mp3$/i or return; my $ctime = localtime( (stat $File::Find::name)[10] ); my $mp3 = get_mp3info ($File::Find::name); my $duration = sprintf "%02d:%02d.%03d", $mp3->{'SECS'} ? @{$mp3}{qw( MM SS MS )} : (0,0,0); my $bitrate = $mp3->{'BITRATE'} * 1024; ( my $url = $File::Find::name ) =~ s#/#\\#g; $entries .= <<EOF; <Entry> <Title > $_ </Title> <Duration value = "$duration" /> <Param Name = "Name" Value = "$_" /> <Param Name = "MediaType" Value = "audio" /> <Param Name = "MediaAttribute" Value = "0" /> <Param Name = "Bitrate" Value = "$bitrate" /> <Param Name = "DigitallySecure" Value = "0" /> <Param Name = "PlayCount" Value = "1" /> <Param Name = "SourceURL" Value = "$url" /> <Param Name = "CreationDate" Value = "$ctime" /> <Ref href = "$url" /> </Entry> EOF } $entries or die "No files found.\n"; print <<EOF; <Asx Version = "3.0" > <Param Name = "Name" Value = "title" /> <Param Name = "AllowShuffle" Value = "yes" /> $entries </Asx> EOF

    It also seems to me that you're writing some non-standard ASX fields, and even ones that are standard, you aren't putting valid data into (e.g. the ref href should probably be a valid URL, I'm guessing).

    But I'd say the main lessons you should take away from this are:

    1. Use modules, esp. when they're standard (e.g. File::Find). The way you're trying to parse `dir` output, and fudge a duration from filesize is particularly gruesome.
    2. alternating between string quotes is ugly, and virtually never necessary.
      '<Duration value = "' . "$duration" . '" />' . "\n"
      can be written as
      "<Duration value = \"$duration\" />\n"
      and, even better, as
      qq(<Duration value = "$duration" />\n)
      as well as
      <<EOF <Duration value = "$duration" /> EOF

    A word spoken in Mind will reach its own level, in the objective world, by its own weight
Re: buildasx.pl (W32 Windows playlist builder)
by blazar (Canon) on May 07, 2007 at 20:24 UTC
    SWMBO needed to build an .asx playlist (controller) on w32. Her existing tools were a grand PITA.

    I occasionally use the following very poor man's script which I wrote quite a lot of time ago, but which also served me fine since.

    #!/usr/bin/perl -lpi.bak use strict; use warnings; BEGIN { @ARGV=map glob, @ARGV } print '<ASX version = "3.0">' if $. == 1; $_ = <<"XXX"; <Entry> <Ref href = "$_" /> </Entry> XXX ($_ .= <<"XXX"), close ARGV if eof; </ASX> XXX chomp; __END__
Re: buildasx.pl (W32 Windows playlist builder)
by ww (Archbishop) on May 07, 2007 at 22:32 UTC
    Thank you, both!

    jdporter No, no CLI arguements expected; that's a todo, yet, but it does expect to run in the directory with the mp3s as it now stands. Could my oversight in failing to mention that be the reason it "doesn't work" for you? It's been fine on all three recent w32 boxes here.

    BUT, thank you especially for pointing out problems AND improvements. Clearly, I should NOT have missed MP3::Info (though it is NOT FOUND in ActiveState's ppm repository)... which may explain, if not excuse, my reverse-engineering of the two working .asx samples I found.

    If you would, please, elaborate on "gruesome" -- I think I understand re parsing the output from dir, but don't see why the code to obtain a (close) approximation of runtimes earned that epithet (which is definitely NOT to say it doesn't deserve it; I just don't understand why, yet).

    blazar -- The brevity and elegance of your so-called "poor man's" script amazes me... but it also prompts a (hasty) question (I'll likely regret): as I read it, your script seems to imply that most of the junk I'm so awkwardly and verbosely handling is "unnecessary." True? Wikipedia was less than helpful; PC Mag's was even less so.
    Update: Thanks also for quick reply below, even as I was fixing 2 omissions here.

      blazar -- The brevity and elegance of your so-called "poor man's" script amazes me... but it also prompts a (hasty) question (I'll likely regret): as I read it, your script seems to imply that most of the junk I'm so awkwardly and verbosely handling is "unnecessary." True?

      Well, mine just takes plain lists of files, as generated say by ls -1 or dir /b and makes them into minimal (but proper? I can only say they appear to work!) asx files. Just so that I can click on them under Windows and have them open in the appropriate player, in the order I want.

        "Minimal" works! (just for the record).
Re: buildasx.pl (W32 Windows playlist builder)
by girarde (Hermit) on May 29, 2007 at 16:52 UTC
    I agree that attrib +s foo.mp3 is an awfully strange thing to do.