fant0men

replaygain.pl

May 13th, 2018 (edited)
195
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/perl
  2. # This script is meant to manage my FLAC music library, specifically the tags.
  3.  
  4. # This script will:
  5.  
  6. # * Remove empty tags and duplicate tag fields
  7. # * Remove leading and trailing whitespace from tags
  8. # * Check FLAC version and re-encode (-8) if an older version than:
  9. #   $flac_version[1] (the installed version of 'flac')
  10. # * Remove ID3v2 tags while re-encoding, but keep VorbisComment tags
  11. # * Remove tags (only RATING as of right now)
  12. # * Add DISCNUMBER, ALBUMARTIST, TOTALTRACKS tags
  13. # * Remove leading 0s from TRACKNUMBER tags
  14. # * Remove album art (right now that subroutine is disabled)
  15. # * Add ReplayGain tags for all albums, unless they already exist
  16. # * Sort tag fields alphabetically and uppercase them before writing
  17. # * Rename and re-organize files based on the tags
  18. # * Remove empty sub-directories under the FLAC library directory
  19.  
  20. # This is the directory structure, and file name, used / created by the script:
  21. # ${library}/${albumartist}/${album}/${discnumber}-${tracknumber}. ${title}.flac
  22.  
  23. # I haven't tried running the script in a directory that has FLAC albums as a
  24. # single file. It will probably not work as expected. Though, it's easy to
  25. # separate a single file FLAC album into separate tracks if you have the CUE
  26. # file. I only keep my FLAC albums as separate files per track. I just think
  27. # it's good practice.
  28.  
  29. use v5.30;
  30. use strict;
  31. use warnings;
  32. use File::Copy qw(move);
  33. use File::Basename qw(basename dirname);
  34. use File::Path qw(make_path);
  35. use Cwd qw(abs_path);
  36.  
  37. my $script = basename($0);
  38. my(@flac_version, $library, %t, %files, @files, @dirs, %mflac_if, @mflac_of);
  39.  
  40. # The 'version' subroutine checks the installed version of 'flac'.
  41. sub version {
  42.     my(@lines);
  43.  
  44.     open(my $flac_v, '-|', 'flac', '--version') or die "Can't open 'flac': $!";
  45.     chomp(@lines = (<$flac_v>));
  46.     close($flac_v) or die "Can't close 'flac': $!";
  47.  
  48.     @flac_version = split(' ', $lines[0]);
  49. }
  50.  
  51. version();
  52.  
  53. if (defined($ARGV[0])) {
  54.     if (scalar(@ARGV) != 1 or ! -d $ARGV[0]) {
  55.         usage();
  56.     } else { $library = abs_path($ARGV[0]); }
  57. } else {
  58.     usage();
  59. }
  60.  
  61. # Find all the sub-directories under the FLAC library directory.
  62. getdirs($library);
  63.  
  64. # This is the main loop of the script, it calls most of the subroutines.
  65. foreach my $dn (@dirs) {
  66.     getfiles($dn);
  67.     my $fc = scalar(@files);
  68.     if ($fc > 0) {
  69.         for (my $n = 0; $n < $fc; $n++) {
  70.             my $fn = $files[$n];
  71.             undef(%t);
  72.             foreach my $tag (keys( %{ $files{$fn} } )) {
  73.                 $t{$tag} = $files{$fn}{$tag}->[0];
  74.             }
  75.             existstag($fn, 'artist', 'album', 'title', 'tracknumber');
  76.             vendor($fn);
  77.             rmtag($fn, 'rating');
  78.             discnum($fn);
  79.             albumartist($fn);
  80.             tracknum($fn);
  81. #           rm_albumart($fn);
  82.             writetags($fn);
  83.         }
  84.     }
  85. }
  86.  
  87. # Running this loop after the first loop, because it renames the FLAC files.
  88. foreach my $dn (@dirs) {
  89.     getfiles($dn);
  90.     my $fc = scalar(@files);
  91.     if ($fc > 0) {
  92.         for (my $n = 0; $n < $fc; $n++) {
  93.             my $fn = $files[$n];
  94.             undef(%t);
  95.             foreach my $tag (keys( %{ $files{$fn} } )) {
  96.                 $t{$tag} = $files{$fn}{$tag}->[0];
  97.             }
  98.             tags2fn($fn);
  99.         }
  100.     }
  101. }
  102.  
  103. # Remove all the empty sub-directories in the FLAC library directory, since
  104. # files may have been moved by the 'tags2fn' subroutine.
  105. rm_empty_dirs();
  106.  
  107. # Find all the sub-directories under the FLAC library directory. We're checking
  108. # the sub-directories a second time, because they may have changed due to the
  109. # 'tags2fn' subroutine being run.
  110. getdirs($library);
  111.  
  112. # Adding the TOTALTRACKS tag last, because we need to do that after the files
  113. # have been moved to the proper directories. The same rule applies to the
  114. # ReplayGain tags.
  115. foreach my $dn (@dirs) {
  116.     getfiles($dn);
  117.     my $fc = scalar(@files);
  118.     if ($fc > 0) {
  119.         replaygain($dn);
  120.  
  121.         for (my $n = 0; $n < $fc; $n++) {
  122.             my $fn = $files[$n];
  123.             undef(%t);
  124.             foreach my $tag (keys( %{ $files{$fn} } )) {
  125.                 $t{$tag} = $files{$fn}{$tag}->[0];
  126.             }
  127.             totaltracks($fn);
  128.             writetags($fn);
  129.         }
  130.     }
  131. }
  132.  
  133. # The 'usage' subroutine prints syntax, and then quits.
  134. sub usage {
  135.     say "Usage: $script [FLAC library directory]\n";
  136.     exit;
  137. }
  138.  
  139. # The 'or_warn' subroutine shows a warning message if the previous shell
  140. # command failed. system() calls seems to follow different rules than the other
  141. # calls, hence this subroutine is necessary for those situations.
  142. sub or_warn {
  143.     my $msg = "\n" . shift;
  144.  
  145.     if ($? != 0) { warn $msg; }
  146. }
  147.  
  148. # The 'getdirs' subroutine finds all the sub-directories under the FLAC library
  149. # directory.
  150. sub getdirs {
  151.     my $dn = shift;
  152.  
  153.     undef(@dirs);
  154.  
  155.     open(my $find, '-|', 'find', $dn, '-type', 'd', '-iname', '*') or die "Can't open 'find': $!";
  156.     chomp(@dirs = (<$find>));
  157.     close($find) or die "Can't close 'find': $!";
  158. }
  159.  
  160. # The 'getfiles' subroutine gets the list of FLAC files in the directory passed
  161. # to it.
  162. sub getfiles {
  163.     my $dn = shift;
  164.     my(@lines);
  165.  
  166.     undef(%files);
  167.     undef(@files);
  168.     undef(%mflac_if);
  169.  
  170.     open(my $find, '-|', 'find', $dn, '-mindepth', '1', '-maxdepth', '1', '-type', 'f', '-iname', '*') or die "Can't open 'find': $!";
  171.     chomp(@lines = (<$find>));
  172.     close($find) or die "Can't close 'find': $!";
  173.  
  174.     foreach my $fn (@lines) {
  175.         if ($fn =~ m/.flac$/i) {
  176.             push(@files, $fn);
  177.             $files{$fn} = { gettags($fn) };
  178.         }
  179.     }
  180. }
  181.  
  182. # The 'gettags' subroutine reads the tags from the FLAC file.
  183. sub gettags {
  184.     my $fn = shift;
  185.     my(%alltags, @lines);
  186.  
  187.     open(my $output, '-|', 'metaflac', '--no-utf8-convert', '--show-vendor-tag', '--export-tags-to=-', $fn) or die "Can't open 'metaflac': $!";
  188.     chomp(@lines = (<$output>));
  189.     close($output) or die "Can't close 'metaflac': $!";
  190.  
  191.     foreach (@lines) {
  192.         my(@tag, $tagname);
  193.  
  194.         if (/^reference/) {
  195.             @tag = split(' ');
  196.             $tagname = 'vendor_ref';
  197.  
  198.             if (defined($tag[2])) {
  199.                 $tag[1] = $tag[2];
  200.             } else {
  201.                 undef(@tag);
  202.                 $tag[1] = $_;
  203.             }
  204.         } else {
  205.             push(@{$mflac_if{$fn}}, $_);
  206.             @tag = split('=');
  207.  
  208.             if (defined($tag[0])) {
  209.                 $tagname = lc($tag[0]);
  210.             } else { next; }
  211.  
  212.             if (defined($tag[1])) {
  213.                 $tag[1] =~ s/(^\s*)|(\s*$)//g;
  214.             } else { next; }
  215.         }
  216.  
  217.         if (defined($tag[1])) {
  218.             push(@{$alltags{$tagname}}, $tag[1]);
  219.         }
  220.     }
  221.  
  222.     return(%alltags);
  223. }
  224.  
  225. # The 'existstag' subroutine checks for the existence of the chosen tags passed to
  226. # it. If it doesn't find the tag, it quits.
  227. sub existstag {
  228.     my $fn = shift;
  229.     my $switch = 0;
  230.  
  231.     foreach my $tag (@_) {
  232.         if (! defined($t{$tag})) {
  233.             say "$fn: doesn't have $tag tag";
  234.             $switch = 1;
  235.             last;
  236.         }
  237.     }
  238.  
  239.     if ($switch == 1) { exit; }
  240. }
  241.  
  242. # The 'vendor' subroutine re-encodes the FLAC file, if it was encoded with an old
  243. # version of FLAC. If the FLAC has ID3v2 tags, they will be removed in the
  244. # process of decoding the FLAC to WAV, before re-encoding it.
  245. sub vendor {
  246.     my $fn = shift;
  247.     my($newfn, $newfn_flac, $newfn_wav, $newfn_stderr);
  248.  
  249.     sub sigint {
  250.         say "Interrupted by user!";
  251.         foreach my $fn (@_) {
  252.             if (-f $fn) {
  253.                 unlink($fn) or die "Can't remove '$fn': $!";
  254.             }
  255.         }
  256.         exit;
  257.     }
  258.  
  259.     if (! defined($t{vendor_ref}) or $t{vendor_ref} ne $flac_version[1]) {
  260.         $newfn = $fn;
  261.         $newfn =~ s/.[[:alnum:]]+$//i;
  262.         $newfn = $newfn . '-' . int(rand(10000));
  263.         $newfn_flac = $newfn . '.flac';
  264.         $newfn_wav = $newfn . '.wav';
  265.         $newfn_stderr = $newfn . '.stderr';
  266.  
  267.         print "$fn: old encoder ($t{vendor_ref}), re-encoding... ";
  268.  
  269. # Duplicate STDERR (for restoration later).
  270. # Redirect STDERR to a file ($newfn_stderr).
  271.         open(my $olderr, ">&STDERR") or die "Can't dup STDERR: $!";
  272.         close(STDERR) or die "Can't close STDERR: $!";
  273.         open(STDERR, '>', $newfn_stderr) or die "Can't open '$newfn_stderr': $!";
  274.  
  275.         system('flac', '--silent', '-8', $fn, "--output-name=$newfn_flac");
  276.         or_warn("Can't encode file");
  277.  
  278. # Close the STDERR file ($newfn_stderr).
  279. # Restore STDERR from $olderr.
  280. # Close the $olderr filehandle.
  281.         close(STDERR) or die;
  282.         open(STDERR, ">&", $olderr) or die "Can't dup STDERR: $!";
  283.         close($olderr) or die "Can't close STDERR: $!";
  284.  
  285.         if ($? == 0) {
  286.             move($newfn_flac, $fn) or die "Can't rename '$newfn_flac': $!";
  287.  
  288.             say 'done';
  289.         } elsif ($? == 2) {
  290.             sigint($newfn_flac, $newfn_stderr);
  291.         } else {
  292. # Open a filehandle that reads from the STDERR file ($newfn_stderr).
  293. # Save the content of the file in an array (@stderra).
  294.             open(my $fh_stderrf, '<', $newfn_stderr) or die "Can't open '$newfn_stderr': $!";
  295.             chomp(my @stderra = (<$fh_stderrf>));
  296.             close($fh_stderrf) or die "Can't close '$newfn_stderr': $!";
  297.  
  298.             foreach (@stderra) {
  299.                 if (/has an ID3v2 tag/) {
  300.                     print "$fn: replacing ID3v2 tags with VorbisComment... ";
  301.  
  302. # Decode the FLAC file to WAV (in order to lose the ID3v2 tags).
  303.                     system('flac', '--silent', '--decode', $fn, "--output-name=$newfn_wav");
  304.                     or_warn("Can't decode file");
  305.  
  306.                     if ($? == 2) {
  307.                         sigint($newfn_wav, $newfn_stderr);
  308.                     }
  309. # Encode the WAV file to FLAC.
  310.                     system('flac', '--silent', '-8', $newfn_wav, "--output-name=$newfn_flac");
  311.                     or_warn("Can't encode file");
  312.  
  313.                     unlink($newfn_wav) or die "Can't remove '$newfn_wav': $!";
  314.  
  315.                     if ($? == 0) {
  316.                         move($newfn_flac, $fn) or die "Can't move '$newfn_flac': $!";
  317.  
  318.                         say 'done';
  319.  
  320. # Clearing the %mflac_if hash key representing $fn, to force the 'writetags'
  321. # subroutine to rewrite the tags. They were removed in the decoding process.
  322.                         $mflac_if{$fn} = ();
  323.                     } elsif ($? == 2) {
  324.                         sigint($newfn_wav, $newfn_flac, $newfn_stderr);
  325.                     }
  326.                 }
  327.             }
  328.         }
  329. # Delete the STDERR file.
  330.         unlink($newfn_stderr) or die "Can't remove '$newfn_stderr': $!";
  331.     }
  332. }
  333.  
  334. # The 'rmtag' subroutine removes tags of choice.
  335. sub rmtag {
  336.     my $fn = shift;
  337.  
  338.     foreach my $tag (@_) {
  339.         if (defined($t{$tag})) {
  340.             say "$fn: removing $tag tag";
  341.             delete($t{$tag});
  342.         }
  343.     }
  344. }
  345.  
  346. # The 'discnum' subroutine creates the DISCNUMBER tag, if it doesn't exist
  347. # already. This subroutine needs to be run before 'albumartist', and
  348. # 'totaltracks'.
  349. sub discnum {
  350.     my $fn = shift;
  351.     my($disc_str);
  352.  
  353.     my $dn = dirname($fn);
  354.     my $regex = qr/\s*[[:punct:]]?(cd|disc)\s*[0-9]+(\s*of\s*[0-9]+)?[[:punct:]]?\s*$/pi;
  355.     my $regex2 = qr/\s*of\s*[0-9]+[[:punct:]]?\s*$/pi;
  356.     my $regex3 = qr/[0-9]+/p;
  357.  
  358. # Cleaning up DISCNUMBER, TOTALDISCS and DISCTOTAL tags, if they exist.
  359.     if (defined($t{discnumber})) {
  360.         $t{discnumber} =~ m/$regex3/;
  361.         $t{discnumber} = ${^MATCH};
  362.     }
  363.  
  364.     if (defined($t{totaldiscs})) {
  365.         $t{totaldiscs} =~ m/$regex3/;
  366.         $t{totaldiscs} = ${^MATCH};
  367.     }
  368.  
  369.     if (defined($t{disctotal})) {
  370.         $t{disctotal} =~ m/$regex3/;
  371.         $t{disctotal} = ${^MATCH};
  372.     }
  373.  
  374. # Adding the DISCNUMBER tag.
  375.     if (! defined($t{discnumber})) {
  376.         if ($t{album} =~ m/$regex/) {
  377.             $disc_str = ${^MATCH};
  378.             ${^MATCH} =~ m/$regex3/;
  379.             $t{discnumber} = ${^MATCH};
  380.             $t{album} =~ s/$regex//;
  381.  
  382.             say "$fn: adding discnumber tag";
  383.         }
  384.     }
  385.  
  386.     if (! defined($t{discnumber})) {
  387.         if ($dn =~ m/$regex/) {
  388.             $disc_str = ${^MATCH};
  389.             ${^MATCH} =~ m/$regex3/;
  390.             $t{discnumber} = ${^MATCH};
  391.         } else {
  392.             $t{discnumber} = 1;
  393.         }
  394.  
  395.         say "$fn: adding discnumber tag";
  396.     }
  397.  
  398. # Let's add the TOTALDISCS tag as well, if possible.
  399.     if (! defined($t{totaldiscs})) {
  400.         if (defined($t{disctotal})) {
  401.             $t{totaldiscs} = $t{disctotal};
  402.  
  403.             say "$fn: adding totaldiscs tag";
  404.         }
  405.     }
  406.  
  407.     if (! defined($t{totaldiscs}) && defined($disc_str)) {
  408.         if ($disc_str =~ m/$regex2/) {
  409.             ${^MATCH} =~ m/$regex3/;
  410.             $t{totaldiscs} = ${^MATCH};
  411.  
  412.             say "$fn: adding totaldiscs tag";
  413.         }
  414.     }
  415. }
  416.  
  417. # The 'albumartist' subroutine creates the ALBUMARTIST tag, if it doesn't exist
  418. # already.
  419. sub albumartist {
  420.     my $fn = shift;
  421.  
  422.     my($tracks, %tracks);
  423.  
  424.     if (defined($t{discnumber})) {
  425.         foreach (@files) {
  426.             if (defined($files{$_}{discnumber}->[0])) {
  427.                 ${tracks}{$files{$_}{discnumber}->[0]}++;
  428.             }
  429.         }
  430.  
  431.         $tracks = ${tracks}{$t{discnumber}};
  432.  
  433.         if (! defined($t{albumartist})) {
  434.             my(%artist, $max);
  435.  
  436.             if ($tracks == 1) { $max = $tracks; } else { $max = $tracks / 2; }
  437.  
  438.             foreach my $fn (keys(%files)) {
  439.                 $artist{$files{$fn}{artist}->[0]} = 1;
  440.             }
  441.  
  442.             if (keys(%artist) > $max) {
  443.                 $t{albumartist} = 'Various Artists';
  444.             } else { $t{albumartist} = $t{artist}; }
  445.  
  446.             say "$fn: adding albumartist tag";
  447.         }
  448.     }
  449. }
  450.  
  451. # The 'tracknum' subroutine removes leading 0s from the TRACKNUMBER tag.
  452. sub tracknum {
  453.     my $fn = shift;
  454.  
  455.     my $regex = qr/[0-9]+/p;
  456.     my $regex2 = qr/^0+$/;
  457.     my $regex3 = qr/^0+/;
  458.  
  459.     if (defined($t{tracknumber})) {
  460.         my $old_tag = $t{tracknumber};
  461.  
  462.         $t{tracknumber} =~ m/$regex/;
  463.         $t{tracknumber} = ${^MATCH};
  464.  
  465.         if ($t{tracknumber} =~ m/$regex2/) {
  466.             $t{tracknumber} = 0;
  467.         } elsif ($t{tracknumber} =~ m/$regex3/) {
  468.             $t{tracknumber} =~ s/$regex3//;
  469.         }
  470.  
  471.         if ($t{tracknumber} ne $old_tag) {
  472.             say "$fn: fixing tracknumber tag";
  473.         }
  474.     }
  475. }
  476.  
  477. # The 'rm_albumart' subroutine removes the album art, by removing the PICTURE
  478. # metadata block.
  479. sub rm_albumart {
  480.     my $fn = shift;
  481.  
  482.     system('metaflac', '--remove', ,'--block-type=PICTURE', $fn);
  483.     or_warn("Can't remove album art");
  484. }
  485.  
  486. # The 'replaygain' subroutine adds ReplayGain tags, if they don't exist already.
  487. sub replaygain {
  488.     my $dn = shift;
  489.     my(%replaygain);
  490.  
  491.     foreach my $fn (sort(keys %files)) {
  492.         if (defined($files{$fn}{replaygain_album_gain}->[0])) {
  493.             $replaygain{$files{$fn}{replaygain_album_gain}->[0]}++;
  494.         }
  495.     }
  496.  
  497.     if (keys(%replaygain) != 1) {
  498.         print "$dn: adding ReplayGain... ";
  499.  
  500.         system('metaflac', '--remove-replay-gain', keys(%files));
  501.         or_warn("Can't remove ReplayGain");
  502.         system('metaflac', '--add-replay-gain', keys(%files));
  503.         or_warn("Can't add ReplayGain");
  504.  
  505.         if ($? == 0) {
  506.             say 'done';
  507.  
  508.             getfiles($dn);
  509.         }
  510.     }
  511. }
  512.  
  513. # The 'writetags' subroutine sorts the tags by field name, and uppercases the
  514. # field names. Then it writes the tags to the FLAC file.
  515. sub writetags {
  516.     my $fn = shift;
  517.     my $is_equal = 1;
  518.  
  519.     undef(@mflac_of);
  520.  
  521. # Sort the keys in the hash that contains all the tags.
  522. # Then push the tags to the @mflac_of array.
  523.     foreach my $tag (sort(keys(%t))) {
  524.         unless ($tag eq 'vendor_ref') {
  525.             if (defined($t{$tag})) {
  526.                 push(@mflac_of, uc($tag) . '=' . $t{$tag});
  527.             }
  528.         }
  529.     }
  530.  
  531.     if (scalar(@{$mflac_if{$fn}}) != scalar(@mflac_of)) {
  532.         $is_equal = 0;
  533.     } else {
  534.         for (my $i = 0; $i < scalar(@{$mflac_if{$fn}}); $i++) {
  535.             if ($mflac_if{$fn}->[$i] ne $mflac_of[$i]) {
  536.                 $is_equal = 0;
  537.                 last;
  538.             }
  539.         }
  540.     }
  541.  
  542.     if ($is_equal == 0) {
  543. # Import the tags from the @mflac_of array.
  544.         system('metaflac', '--remove-all-tags', $fn);
  545.         or_warn("Can't remove tags");
  546.         open(my $output, '|-', 'metaflac', '--import-tags-from=-', $fn) or die "Can't import tags: $!";
  547.         foreach my $line (@mflac_of) {
  548.             say $output $line;
  549.         }
  550.         close($output) or die "Can't close 'metaflac': $!";
  551.     }
  552. }
  553.  
  554. # The 'tags2fn' subroutine creates a new path and file name for the input
  555. # files, based on the changes that have been made to the tags.
  556. sub tags2fn {
  557.     my $fn = shift;
  558.     my $dn = dirname($fn);
  559.  
  560.     my $discnum = $t{discnumber};
  561.     my $albumartist = $t{albumartist};
  562.     $albumartist =~ tr/a-zA-Z0-9\.\-_ //dc;
  563.     $albumartist =~ s/ +/ /g;
  564.     $albumartist =~ s/^\.+//g;
  565.     my $album = $t{album};
  566.     $album =~ tr/a-zA-Z0-9\.\-_ //dc;
  567.     $album =~ s/ +/ /g;
  568.     $album =~ s/^\.+//g;
  569.     my $tracknum = sprintf("%02d", $t{tracknumber});
  570.     my $title = $t{title};
  571.     $title =~ tr/a-zA-Z0-9\.\-_ //dc;
  572.     $title =~ s/ +/ /g;
  573.     my $newfn_bn = $discnum . '-' . $tracknum . '. ' . $title . '.flac';
  574.     my $newdn_dn = $library . '/' . $albumartist . '/' . $album;
  575.     my $newfn = $newdn_dn . '/' . $newfn_bn;
  576.  
  577.     if (! -d $newdn_dn) { make_path($newdn_dn); }
  578.  
  579.     if (! -f $newfn) {
  580.         say "$fn: renaming based on tags";
  581.         move($fn, $newfn) or die "Can't rename '$fn': $!";
  582.     }
  583. }
  584.  
  585. # The 'totaltracks' subroutine creates the TOTALTRACKS tag, if it doesn't exist
  586. # already.
  587. sub totaltracks {
  588.     my $fn = shift;
  589.  
  590.     my($tracks, %tracks);
  591.  
  592.     if (defined($t{discnumber})) {
  593.         foreach (@files) {
  594.             if (defined($files{$_}{discnumber}->[0])) {
  595.                 ${tracks}{$files{$_}{discnumber}->[0]}++;
  596.             }
  597.         }
  598.  
  599.         $tracks = ${tracks}{$t{discnumber}};
  600.  
  601.         if (! defined($t{totaltracks}) && ! defined($t{tracktotal})) {
  602.             say "$fn: adding totaltracks tag";
  603.             $t{totaltracks} = $tracks;
  604.         }
  605.     }
  606.  
  607.     if (defined($t{tracktotal}) && ! defined($t{totaltracks})) {
  608.         say "$fn: adding totaltracks tag";
  609.         $t{totaltracks} = $t{tracktotal};
  610.     }
  611. }
  612.  
  613. # The 'rm_empty_dirs' subroutine finds all the empty sub-directories under the
  614. # FLAC library directory, and removes them.
  615. sub rm_empty_dirs {
  616.     my(@lines);
  617.  
  618.     open(my $find, '-|', 'find', $library, ,'-mindepth', '1', '-type', 'd', '-empty') or die "Can't open 'find': $!";
  619.     chomp(@lines = (<$find>));
  620.     close($find) or die "Can't close 'find': $!";
  621.  
  622.     foreach my $fn (@lines) {
  623.         rmdir($fn) or die "Can't remove '$fn': $!";
  624.     }
  625. }
  626.  
RAW Paste Data