#!/opt/local/bin/perl use 5.010; use strict; use warnings; use File::Find qw(find); my $library = "F:/Music"; #$library = "F:/FLAC/Downloaded/New"; my %files; find({ wanted => \&action, no_chdir => 1 }, $library); sub action { if (-d) { my $dn = $File::Find::name; %files = getfiles($dn); my $fc = keys %files; # say $fc; unless ($fc == 0) { replaygain($dn); foreach my $fn (sort(keys %files)) { rmtag($fn, 'discnumber', 'disctotal', 'rating', 'comment'); albumartist($fn, $fc); track($fn); totaltracks($fn, $fc); # empty($fn); # duplicate($fn); } } undef %files; } } # Make this subroutine and the whole script so that each file name is stored in a hash and that each member contains an anonymous hash with the tags (the alltags hash). # This means it won't have to look up the tags again for the albumartist subroutine. sub gettags { my $fn = shift; my %alltags; open(OUTPUT, '-|', qq{metaflac --no-utf8-convert --export-tags-to=- "$fn"}) || die "can\'t run metaflac: $!"; local $_; while () { chomp; my @tag = split('='); my $tagname = lc($tag[0]); if ($tag[1]) { push @{$alltags{$tagname}}, $tag[1]; } else { push @{$alltags{$tagname}}, 'null'; } } close(OUTPUT) || die "couldn\'t close metaflac: $!"; return %alltags; } sub getfiles { my $dn = shift; my %files; opendir(my $dh, $dn) or die "Can\'t open directory '$dn': $!"; foreach (readdir $dh) { my $fn = "$dn/$_"; if (/.flac$/ && -f $fn) { $files{$fn} = { gettags($fn) }; } } closedir $dh or die "Can\'t close directory '$dn': $!"; return %files; } sub replaygain { my $dn = shift; my %replaygain; foreach my $fn (sort(keys %files)) { if ($files{$fn}{replaygain_album_gain}) { $replaygain{$files{$fn}{replaygain_album_gain}->[0]}++; } } if (keys(%replaygain) != 1) { print "$dn: adding ReplayGain..."; system('metaflac', '--add-replay-gain', keys(%files)); say " done"; } } sub albumartist { my $fn = shift; my $tracks = shift; if (!$files{$fn}{albumartist}) { my $albumartist = 'Various Artists'; my %artist; my $max; if ($tracks == 1) { $max = $tracks; } else { $max = $tracks / 2; } foreach my $fn (keys %files) { $artist{$files{$fn}{artist}->[0]}++ } if (keys(%artist) <= $max) { my $ac = 0; foreach my $a (keys %artist) { if ($artist{$a} > $ac) { $ac = $artist{$a}; $albumartist = $a; } } } say "$fn: adding albumartist tag"; system('metaflac', "--set-tag=ALBUMARTIST=$albumartist", $fn); } } sub empty { my $fn = shift; foreach my $k (keys %{$files{$fn}}) { foreach (@{$files{$fn}{$k}}) { if ($_ eq 'null') { say "$fn: WARNING: empty $k tag"; } } } } sub duplicate { my $fn = shift; my %exists; foreach my $k (keys %{$files{$fn}}) { foreach (@{$files{$fn}{$k}}) { my $tag = quotemeta($k . '=' . $_); if ($exists{$tag}) { say "$fn: WARNING: duplicate $k tag"; } else { $exists{$tag} = 1; } } } } sub track { my $fn = shift; my $track = $files{$fn}{tracknumber}->[0]; if ($track =~ m/^0/ && $track != 0) { say "$fn: fixing tracknumber tag"; $track =~ s/^0+//; system('metaflac', '--remove-tag=TRACKNUMBER', "--set-tag=TRACKNUMBER=$track", $fn); } } sub totaltracks { my $fn = shift; my $tracks = shift; if ( !$files{$fn}{totaltracks} && !$files{$fn}{tracktotal} ) { say "$fn: adding totaltracks tag"; system('metaflac', "--set-tag=TOTALTRACKS=$tracks", $fn); } elsif ( $files{$fn}{tracktotal} && !$files{$fn}{totaltracks} ) { say "$fn: replacing tracktotal tag with totaltracks"; system('metaflac', '--remove-tag=TRACKTOTAL', "--set-tag=TOTALTRACKS=$files{$fn}{tracktotal}->[0]", $fn); } } sub rmtag { my $fn = shift; foreach (@_) { if ($files{$fn}{$_}) { say "$fn: removing $_ tag"; system('metaflac', "--remove-tag=$_", $fn); } } }