(Terug) naar scripts op heeck.nl of heeck.nl

******************************************************************************************
renamemp3

En toen was ik m'n mp3-tjes aan het uitzoeken om een paar cd's te maken voor de vakantie.... Aangezien mijn autoradio ook datacd's met mp3's aankan leek het mij wel praktisch om mijn favoriete muziek te rippen en een paar leuke verzamelaars te bakken. En ja, hoe sorteer je dan het beste...........

Daarnaast was de verzameling oude rips, downloads en andere kadootjes ook al aardig uitgedijd zodat ik door de bomen het bos niet meer zag. Elke set mp3's had zijn eigen soort bestandsnamen.In de ene zat een albumtitel, de andere had het tracknummer er in en de artiest in HOOFDLETTERS maar de songtitel weer niet, en we hadden natuurlijk ook nog track01.mp3, track018667239211123.mp3 en aanverwante ellende.

In de media player van ome Bill en in de windows verkenner kreeg ik headerinformatie van de mp3's te zien die vaak veel meer zei dan de feitelijke bestandsnaam en dat bracht mij op het idee een scriptje te schrijven om deze berg ongeregeld van consistente bestandsnamen te voorzien zonder mijn vingers blauw te typen.

De headerinformatie voor audio- en videobestanden is vastgelegd in deze standaard.

Omdat er voor elk denkbaar klusje wel een Perl module geschreven is ben ik vervolgens op CPAN rond gaan neuzen en vond ik daar de module MP3::Info.

Ik moest wat met het gegeven dat ik liever geen rare tekens en letters met accenten in bestandsnamen heb terwijl die wel in de headers kunnen voorkomen. Sommige shell's, waaronder de door my gebruikte Cygwin Bash doen namelijk wat flauw met unicode en geven daardoor onvoorspelbare resultaten.
Doedumijdusmaar platte ASCII en graag zonder spaties, dus Sinéad O'Connor moest Sinead_OConnor worden enzovoorts. Om de 'vreemde tekens' te vertalen heb ik een aardig stuk code bij Ivan Kurmanov gevonden en daartoe ook de module Unicode::Normalize geïnstalleerd.

mp3 properties

"Nothing Compares 2 U" krijgt op basis van bovenstaande headerinformatie als bestandsnaam:
Sinead_OConnor-I_Do_Not_Want_What_I_Havent_Got-006-Nothing_Compares_2_U.mp3

Scriptje ergens neerzetten en aftrappen vanuit de directory met te converteren mp3's.

Features:
  • Bestanden zonder headerinfo worden overgeslagen.
  • Mp3's zonder artiestennaam in de header worden overgeslagen.
  • Mp3's zonder songtitel in de header worden overgeslagen.
  • Voor het tracknummer en de albumtitel wordt zonodig een default gezet.
  • Er is een controle en uitwijk (volgnummer) om dubbele bestandsnamen en daarmee het overschrijven van bestanden te voorkomen.
  • De bestandsnaam kan op meerdere manieren samengesteld worden voor bijna elke gewenste sortering. Daarvoor hoef je alleen op regel 9 de sortering te kiezen.
  • Bestanden die na controle al de goede naam blijken te hebben worden ook overgeslagen om het tempo er een beetje in te houden.
  • Rapportage van behandelde bestanden met oude en nieuwe naam op STDOUT.
En wat 'ie niet doet/heeft:
  • Controle op rechten in de directory. Zelf effe checke.
  • Een undo-functie. Als je het niet vertrouwt stuur je de output naar een tekstbestand. Dan heb je de oude en de nieuwe bestandsnaam en kun je een renamescriptje bakken.
  • Directories induiken. Heb ik nog niet gemist en lijkt me zelfs een gevaarlijke feature aangezien er wel bestandsnamen aangepast worden.
  • Correcte afhandeling van meerdere runs over directories met duplicaten. Die wisselen bij opvolgende runs van volgnummer. Als je dubbelen hebt die je wilt bewaren kun je het beste na ontdekking de headers iets aanpassen (b.v. tracknummer wijzigen) om toch onderscheid te maken.
  • Garantie. Nee, nope, nopppes. Alles is hier ayor. Opbouwende kritiek is altijd welkom.
Gebouwd, getest en blij in gebruik met: "Perl, v5.8.7 built for cygwin-thread-multi-64int".


001 #!/usr/bin/perl -w
002 require Encode;
003 use strict;
004 use MP3::Info;
005 use Unicode::Normalize;
006
007 my $defaultalbum =  "Unknown_album";  # default for album title section of filename
008 my $defaulttracknum = 0;              # default for track number section of filename
009 my $order = "artist";        # or album or track or title, artist is the default
010                              # see sub new_filename for filename construction options
011
012
013
014 MP3FILE:foreach my $file(@ARGV){
015   my $tag = get_mp3tag($file) or next MP3FILE;
016 # get and validate artist
017   my $artist = $tag->{ARTIST};
018 # skip file if artist field is not filled with anything recognizable
019   if ( $artist !~ /[a-z]/i ){
020      next MP3FILE;
021 # I only want the first artist if there are more in this field
022   } elsif ( $artist =~ /\// ){
023      ( $artist, undef ) = split(/\//, $artist);
024   }
025   $artist = clean_string( $artist );
026 # get and validate title
027   my $title = $tag->{TITLE};
028 # skip file if title field is not filled with anything recognizable
029   if ( $title !~ /[a-z]/i and $title !~ /[0-9]/ ){
030      next MP3FILE;
031   }
032   $title = clean_string( $title );
033 # get and validate albumtitle, set default if none present
034   my $album = $tag->{ALBUM};
035   if ( $album !~ /[a-z]/i and $album !~ /[0-9]/ ){
036      $album = $defaultalbum;
037   }
038   $album = clean_string( $album );
039 # get and validate track number, set default if none present
040   my $tracknum = $tag->{TRACKNUM};
041 # tracnumber can be 1/10 as in track/tracks, I only want the first number
042   if ( $tracknum =~ /\// ){
043      ( $tracknum, undef ) = split(/\//, $tracknum);
044   }
045   $tracknum = clean_string( $tracknum );
046   $tracknum =~ s/[a-z]//g;
047   $tracknum =~ s/[A-Z]//g;
048   if ( $tracknum !~ /[0-9]/i ){
049      $tracknum = $defaulttracknum;
050   }
051   $tracknum = sprintf("%03d", $tracknum);
052 # create new filename if neccesary
053   my $newfile = new_filename($artist, $title, $album, $tracknum, $order);
054   if ( $file ne $newfile ){
055      if ( -f $newfile ){
056         $newfile = unique_filename($newfile);
057      }
058      rename ( $file, $newfile);
059      print "$file\t\t$newfile\n"
060   }
061 }
062
063
064 # get rid of special chars, spaces etc...
065 sub clean_string{
066   my ($string) = (@_);
067 # the next code translates accented characters to their harmless counterparts :-)
068 # I prefer ascii-only filenames to keep everything platform independent
069 #
070 # Thank you very much, Mr. Ivan Kurmanov!!!!!
071 #########################################################################
072 # begin code found on http://ahinea.com/en/tech/accented-translate.html #
073 #########################################################################
074   for ( $string ) {  # the variable we work on
075
076      ##  convert to Unicode first
077      ##  if your data comes in Latin-1, then uncomment:
078      #$_ = Encode::decode( 'iso-8859-1', $_ );
079
080      s/\xe4/ae/g;  ##  treat characters ä ñ ö ü ÿ
081      s/\xf1/ny/g;  ##  this was wrong in previous version of this doc
082      s/\xf6/oe/g;
083      s/\xfc/ue/g;
084      s/\xff/yu/g;
085
086      $_ = NFD( $_ );   ##  decompose (Unicode Normalization Form D)
087      s/\pM//g;         ##  strip combining characters
088
089      # additional normalizations:
090
091      s/\x{00df}/ss/g;  ##  German beta “ß” -> “ss”
092      s/\x{00c6}/AE/g;  ##  Æ
093      s/\x{00e6}/ae/g;  ##  æ
094      s/\x{0132}/IJ/g;  ##  ?
095      s/\x{0133}/ij/g;  ##  ?
096      s/\x{0152}/Oe/g;  ##  Œ
097      s/\x{0153}/oe/g;  ##  œ
098
099      tr/\x{00d0}\x{0110}\x{00f0}\x{0111}\x{0126}\x{0127}/DDddHh/; # ÐÐðdHh
100      tr/\x{0131}\x{0138}\x{013f}\x{0141}\x{0140}\x{0142}/ikLLll/; # i??L?l
101      tr/\x{014a}\x{0149}\x{014b}\x{00d8}\x{00f8}\x{017f}/NnnOos/; # ???Øø?
102      tr/\x{00de}\x{0166}\x{00fe}\x{0167}/TTtt/;                   # ÞTþt
103
104      s/[^\0-\x80]//g;  ##  clear everything else; optional
105   }
106 #########################################################################
107 #  end code found on http://ahinea.com/en/tech/accented-translate.html  #
108 #########################################################################
109
110   $string =~ s/^\s+//;
111   $string =~ s/\s+$//;
112   $string =~ s/&/and/g;
113   $string =~ s/[^\w ]//g;
114   $string =~ /([\w ]+)/;
115   $string =~ s/[\s ]/_/g;
116   return $string;
117 }
118
119
120
121 # create new filename
122 sub new_filename{
123   my ($artist, $title, $album, $tracknum, $order) = @_;
124   my $newfile = "${artist}-${album}-${tracknum}-${title}.mp3";
125   my %sortkey = (
126   "album"     =>    "${album}-${tracknum}-${artist}-${title}.mp3",
127   "tracknum"  =>    "${tracknum}-${artist}-${album}-${title}.mp3",
128   "title"     =>    "${title}-${artist}-${album}-${tracknum}.mp3",
129   );
130   if ( defined $sortkey{$order} ){
131      $newfile = "$sortkey{$order}";
132   }
133   return $newfile;
134 }
135
136 sub unique_filename{
137   my ($newfile) = @_;
138   my ($filename, undef) = split(/\./, $newfile);
139 #check for existing files with proposed filename, rename to avoid overwriting
140   if ( -f $newfile ){
141      my $copycount=1;
142      $newfile = "${filename}-(${copycount}).mp3";
143 # find 1st available sequencenumber to avoid overwriting files
144      while ( -f $newfile ){
145         $copycount++;
146         $newfile = "${filename}-(${copycount}).mp3";
147      }
148   }
149   return $newfile;
150 }




* Begin **********************************************************************************