Recently I posted a little program to implement a watched folder/renamer for newly downloaded mp3 files. More recently, eMusic.com went and changed things around a little on their system. Now the mp3 links are ill-named redirects, and they don't play nicely with GetRight anymore, since GetRight wants to create the file before actually checking out the URL and getting the real server-suggested filename.

BUT! One cool thing about eMusic is the RMP link for each album. It allows you to download the full album, provided your player is Zinf.

Or RealPlayer I think, but puh-leeze, yucko.

The thing is, I dig winamp. I don't wanna switch. So I looked at these .rmp files, and saw that they're just XML. A little further googling turned up a groovy java-based utility for RMP files, but that had one problem: It wasn't in perl. So I rolled up my sleeves, found a spec-let for RMP, and wrote a nice little CL-based RMP download utility. It allows you to customize the resulting file names, as well as the option of creating directories for artist/album. I wrote it on windows, but it ought to work in Linux. I'll try that when I can.

I have not done much with XML::* or LWP before, so it was a great learning experience on both fronts. I'm seeking input on the code itself, style, concept, whatever.

the code follows:

#!/usr/bin/perl -w =head1 NAME getrmp.pl =head1 SYNOPSIS Pass it a local filename: getrmp.pl /path/to/somefile.rmp getrmp.pl C:\path\to\somefile.rmp Or a URL: getrmp.pl http://server.com/path/to/somefile.rmp NOTE that URL support is primitive, if the server needs cookies, authentication, or redirects, the program will die. =head1 ABSTRACT RMP is an XML format for playlists, begun initially by RealNetworks I +think. eMusic.com uses it to allow customers to download full albums with one + click. Freeamp/zinf support RMP, but WinAMP does not. And since eMusic.com ch +anged things around recently, it's harder to use dowload managers because th +e mp3 links are given broken names and then redirected. My mouse was getting + tired of clicking each song, so this is quick and dirty an RMP processor tha +t, when passed an RMP file, will download the album and save it somewhere. Be sure to look over the configuration section for customizations. Of course all corporate names used herein are trademarked and copyrigh +ted by their respective owners. And this software is free, with no warranty. +Yeah. =cut use XML::Simple; use LWP::UserAgent; use LWP::Simple; use File::Copy; use strict; ## CONFIGURATION ## Where to put files my $dlpath = "d:\\music\\"; ## Make directories for each artist? my $artistdir = 1; ## Make sub-artist directories for each album? my $albumdir = 0; ## How to name the new music files. my $naming = '[##ARTIST## - ##ALBUM##] ##TRACK## ##TITLE##'; ### Other configs ## User agent to report my $agent = 'Mozilla/8.0'; ### END OF CONFIGURATION my $ua = LWP::UserAgent->new; $ua->agent($agent); my $rmpfilename = $ARGV[0] || die q| Usage: getrmp.pl path/or/URL/to/file.rmp getrmp.pl C:\path\to\somefile.rmp getrmp.pl http://server.com/path/to/somefile.rmp |; my $rmpfile; #### This IF construct may not work if it needs authentication, #### cookie, or redirect support. if ($rmpfilename =~ m|^\w+://|) { # we've got a URL print "Getting rmp file: "; my $res = $ua->request( HTTP::Request->new(GET=>$rmpfilename) ); # Check the outcome of the response if ($res->is_success) { $rmpfile = $res->content; print "OK\n"; } else { die "Can't get remote $rmpfilename"; } } else { #it's a regular /path/to/file on disk $rmpfile = $rmpfilename; } my $xmlref = XMLin($rmpfile) || die "Something bad happened with XML:: +Simple"; my $baseurl = 'http://'. $xmlref->{SERVER}{NETNAME}. $xmlref->{SERVER}{LOCATION}; # got these from http://docs.real.com/docs/rn/realone/RMP_Creation_Gui +de.pdf my %parsecodes = ( fid => 'TRACKID', f => 'FILENAME', # Note, emusic.com does not use these two, and I don't know what they +are. see above URL. lid => 'UNUSED', pid => 'UNUSED', ); my $total = scalar @{$xmlref->{TRACKLIST}{TRACK}}; my $tcount; print "Downloading '$xmlref->{TITLE}' from $xmlref->{SERVER}{NETNAME}\ +n\n"; foreach my $track (@{$xmlref->{TRACKLIST}{TRACK}}) { # Weed out some bad characters for (qw(ARTIST ALBUM TITLE)) { $track->{$_} =~ s([\*\?\/\\])(-)g; } my $TRACKNO = sprintf("%02d",++$tcount); # Here's where we make any new dirs if ($artistdir) { makedir($dlpath.$track->{ARTIST}) or die "Can't make directory $dlpath.$track->{ARTIST}"; if ($albumdir) { makedir($dlpath.$track->{ARTIST}."\\".$track->{ALBUM}) or die "Can't make directory $dlpath.$track->{ARTIST}. +$track->{ALBUM}"; } } # Figure out the new filename (my $newfile = $naming) =~ s/##TRACK##/$TRACKNO/g; for (qw(ARTIST ALBUM TITLE)) { $newfile =~ s/##$_##/$track->{$_}/ge; } my $fullpath = $dlpath . ($artistdir ? "$track->{ARTIST}\\" : '') . ($albumdir ? "$track->{ALBUM}\\" : '') . $newfile; my ($expected_length, $bytes_received); open FH, "> $fullpath.getrmp" or die "Can't open file $fullpath.ge +trmp"; binmode (FH); print "$tcount/$total: $newfile\n"; my $url = $baseurl; # Interpolate the path codes for (qw(fid f lid pid)) { $url =~ s/\%$_/$track->{ $parsecodes{$_} }/g; } my ($extension) = ($track->{FILENAME} =~ /(\.\S*)$/); my $tracktime = time; my $res = $ua->request( HTTP::Request->new(GET => $url), sub { my($chunk, $res) = @_; $bytes_received += length($chunk); unless (defined $expected_length) { $expected_length = $res->content_length || 0; } print FH $chunk; progress($bytes_received, $expected_length); },1024); close FH; my $ttime = time - $tracktime; my $kbps = int ($bytes_received / 1024 / $ttime); print " - $bytes_received bytes OK at $kbps KBps\n"; move("$fullpath.getrmp", $fullpath.$extension) or print " ERROR Moving File. Temp file left.\n"; } #END foreach print "Finished\n"; sub progress { my ($val, $tot) = @_; printf("\b" x 4 . "%3d%%", int(100 * $val / $tot)); } sub makedir { my $dir = shift; if (-e $dir) { return 1; }else{ return mkdir $dir; } }

Updated with <READMORE> tag.

cheers!


In reply to RMP-file music downloader (eMusic.com, mp3.com, etc) by sedhed

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.