#!/usr/bin/perl use 5.14.0; use strict; use warnings; use Cwd qw(abs_path); use File::Basename qw(basename); use File::Find qw(find); use File::Path qw(make_path); use File::Copy qw(copy); my $library = "/home/sir/Music/Songbird Music"; #my $library = "$ENV{HOME}/test"; my @lacc = qw(EAC "Exact Audio Copy" "XLD X Lossless Decoder" cdparanoia Rubyripper); my (@log, %files); foreach my $dn (@ARGV) { if (! -d $dn) { say "'$dn' is not a directory!"; exit; } find({ wanted => \&action, no_chdir => 1 }, $dn); sub action { if (-d) { my $dn = abs_path($File::Find::name); undef %files; @log = getfiles($dn); my $fc = keys(%files); if ($fc > 0) { say "Importing '$dn'...\n"; import($fc); } else { say "There are no FLAC files in '$dn'!"; } } } } if (!@ARGV) { my $s = basename($0); say "Usage: $s [directory 1] .. [directory N]"; exit; } sub gettags { my $fn = quotemeta(shift); my %alltags; open(OUTPUT, '-|', qq{metaflac --no-utf8-convert --export-tags-to=- $fn}) || die "can't run metaflac: $!"; chomp(my @mflac = ()); foreach (@mflac) { my (@tag, $tagname); @tag = split('='); $tagname = lc($tag[0]); if ($tag[1]) { $tag[1] =~ tr!/\\<>|:;$*"'!!d; push @{$alltags{$tagname}}, $tag[1]; } } close(OUTPUT) || die "can't close metaflac: $!"; return %alltags; } sub checktags { my $fn = shift; my $artist = $files{$fn}{artist}->[0]; my $album = $files{$fn}{album}->[0]; if (!$artist || !$album ) { say "'$fn' is NOT tagged!"; exit; } } sub getfiles { my $dn = shift; my @log; 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) }; } if (/.log$/ && -f $fn) { open(my $text, $fn) or die "Can't open file '$fn': $!"; my $line1 = <$text>; close $text or die "Can't close file '$fn': $!"; foreach my $req (@lacc) { if ($line1 =~ /$req/) { push(@log, $fn); } } } } closedir $dh or die "Can't close directory '$dn': $!"; return(@log); } sub albumartist { my $fn = shift; my $tracks = keys %files; my $artist = $files{$fn}{artist}->[0]; 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) { $artist = 'Various Artists'; } return $artist; } sub import { my $fc = shift; my $cp = 0; my $cplog = 1; my $total = $fc; my ($artist, $album, $albumartist, $discnumber, $newname, $path); foreach my $sf (sort(keys %files)) { checktags($sf); $artist = $files{$sf}{artist}->[0]; $album = $files{$sf}{album}->[0]; $albumartist = albumartist($sf, $fc); if (exists $files{$sf}{discnumber}->[0]) { $discnumber = $files{$sf}{discnumber}->[0]; } $path = "$library/$albumartist/$album"; if( $cp == 0 && -d $path ) { say "'$path' already exists!"; say "Skipping...\n"; return; } else { make_path($path); } my $track = $files{$sf}{tracknumber}->[0]; my $title = $files{$sf}{title}->[0]; if ($discnumber) { $newname = sprintf('%s-%02s. %s.flac', $discnumber, $track, $title); } else { $newname = sprintf('%02s. %s.flac', $track, $title); } my $tf = "$path/$newname"; say "Copying '$sf'\n\tto '$tf'..."; copy($sf, $tf) or die "Copy failed: $!"; $cp++ } say "Copied $cp / $total files from '$album'.\n"; foreach my $sf (@log) { my $tf; if (scalar(@log) > 1) { $tf = "$path/$cplog-$album.log"; } else { $tf = "$path/$album.log"; } say "Copying '$sf'\n\tto '$tf'...\n"; copy($sf, $tf) or die "Copy failed: $!"; $cplog++ } }