#!/usr/bin/perl #+------------------------------------------------------------------------+ #| | #| id32m3u -- by greenfly | #| | #| usage: id32m3u [--archos] [...] | #| | #| This script reads the id3 tags of any MP3s it finds in the directories | #| it is passed, and then creates a Playlist directory containing | #| m3u files in an Artist and Album structure. | #| | #| if the script is passed the --archos option it will format a playlist | #| suitable for an archos mp3 player | #| | #+------------------------------------------------------------------------+ use MP3::Tag; use File::Find; use File::Path; use File::Copy; use String::Approx qw(amatch); use Cwd; use strict; @ARGV = ('.') unless @ARGV; my $starting_dir = getcwd(); our %tags; my $artist; my $album; my $track; our $last_path = ""; our $archos; sub process_mp3 { my $mp3; my $filename = $_; next unless($filename =~ /\.mp3$/i); my $full_file_path = $File::Find::name; my ($title, $track, $artist, $album, $comment, $year, $genre) = 0; print "$File::Find::dir\n" if($last_path ne $File::Find::dir); $last_path = $File::Find::dir; $mp3 = MP3::Tag->new($filename) or next; ($title, $track, $artist, $album, $comment, $year, $genre) = $mp3->autoinfo(); $artist =~ s/^The\s*//g; $title =~ s/([A-Za-z0-9']+)/\u\L$1/g; $track =~ s/([A-Za-z0-9']+)/\u\L$1/g; $artist =~ s/([A-Za-z0-9']+)/\u\L$1/g; $album =~ s/([A-Za-z0-9']+)/\u\L$1/g; $genre =~ s/([A-Za-z0-9']+)/\u\L$1/g; $tags{$full_file_path}{'title'} = $title; $tags{$full_file_path}{'track'} = $track; $tags{$full_file_path}{'artist'} = $artist; $tags{$full_file_path}{'album'} = $album; $tags{$full_file_path}{'comment'} = $comment; $tags{$full_file_path}{'year'} = $year; $tags{$full_file_path}{'genre'} = $genre; $tags{$full_file_path}{'filename'} = $filename; # print "$tags{$full_file_path}{'title'} "; # print "$tags{$full_file_path}{'track'} "; # print "$tags{$full_file_path}{'artist'} "; # print "$tags{$full_file_path}{'album'} "; # print "$tags{$full_file_path}{'comment'} "; # print "$tags{$full_file_path}{'year'} "; # print "$tags{$full_file_path}{'genre'} "; # print "$tags{$full_file_path}{'filename'} "; # print "\n"; # print "$artist " if(lc($last_artist) ne lc($artist)); # $last_artist = $artist; } # process all the directories passed as arguments $| = 1; print "Processing: "; foreach(@ARGV) { if($_ eq "--archos"){$archos = "/Music/"; next;} print "$_ "; find(\&process_mp3, $_); } print " Finished Processing\n"; prune_artists(); # recreate the directory structure move("Playlists", "Playlists.bak"); mkdir "Playlists"; chdir "Playlists"; mkdir "Artists"; mkdir "Albums"; foreach $artist (list_artists()) { next if($artist eq ""); mkdir "Artists/$artist"; open ALLARTISTS, ">> Artists/\ ALL.m3u"; open ALLALBUMS, ">> Albums/\ ALL.m3u"; open ALLARTISTSALBUM, ">> Artists/$artist/\ ALL.m3u"; foreach $track (tracks_for_artist($artist)) { next if($track eq ""); print ALLARTISTS "$archos$track\n"; print ALLARTISTSALBUM "$archos$track\n"; } foreach $album (albums_for_artist($artist)) { open ARTISTSALBUM, ">> Artists/$artist/$album.m3u"; open ALBUMS, ">> Albums/$album.m3u"; foreach $track (tracks_for_album($album)) { next if($track eq ""); print ALLALBUMS "$archos$track\n"; print ARTISTSALBUM "$archos$track\n"; print ALBUMS "$archos$track\n"; } close ALBUMS; close ARTISTSALBUM; } close ALLARTISTSALBUM; close ALLALBUMS; close ALLARTIST; } chdir($starting_dir); rmtree("Playlists.bak"); sub prune_artists { my %artists = {}; my $artist; my @matches; my $dist; foreach(sort keys %tags) { $artist = ""; @matches = (); $artist = $tags{$_}{'artist'}; next if($artist =~ /^.?$/); @matches = amatch($artist, ['i 10%'], keys %artists); if(defined $matches[0]){$artist = $matches[0]} $tags{$_}{'artist'} = $artist; $artists{$artist}++; } } sub list_artists { my %artists; my $artist; my $matched; foreach(keys %tags) { $artist = $tags{$_}{'artist'}; next if($artist eq ""); $artists{$artist} = 1; } return sort keys %artists; } sub albums_for_artist { my $artist = shift; my %albums; my $confirmed_artist; foreach(keys %tags) { $confirmed_artist = $tags{$_}{'artist'}; #if(amatch("$artist", $confirmed_artist)) if($artist eq $confirmed_artist) { next if($tags{$_}{'album'} eq ""); $albums{$tags{$_}{'album'}} = 1; } } return sort keys %albums; } sub tracks_for_album { my $album = shift; return if($album eq ""); my @tracks; foreach(keys %tags) { if($tags{$_}{'album'} =~ /^\Q$album\E$/i) { if($tags{$_}{'track'} ne "") { $tracks[$tags{$_}{'track'}] = $_; } else { push(@tracks, $_); } } } return @tracks; } sub tracks_for_artist { my $artist = shift; return if($artist eq ""); my @tracks; my $confirmed_artist; foreach(keys %tags) { $confirmed_artist = $tags{$_}{'artist'}; #if(amatch($artist, $confirmed_artist)) if($artist eq $confirmed_artist) { push(@tracks, $_); } } return sort @tracks; } sub by_artist { $tags{$a}{'artist'} cmp $tags{$b}{'artist'} or $tags{$a}{'year'} <=> $tags{$b}{'year'} or $tags{$a}{'album'} cmp $tags{$b}{'album'} or $tags{$a}{'track'} <=> $tags{$b}{'track'} or $tags{$a}{'title'} cmp $tags{$b}{'title'} or $a cmp $b; }