pastebin - collaborative debugging tool
kpaste.net RSS


flac2lame perl script threaded
Posted by Anonymous on Tue 11th Sep 2012 03:06
raw | new post

  1. #!/usr/bin/perl
  2.  
  3. use 5.14.0;
  4. use strict;
  5. use warnings;
  6. use Cwd qw(abs_path cwd);
  7. use File::Path qw(make_path);
  8. use File::Basename qw(basename dirname);
  9.  
  10. use threads qw(yield);
  11. use threads::shared;
  12. use Thread::Queue;
  13.  
  14. my $total :shared = 0;
  15. my $disk_size = 1000000000;
  16.  
  17. my @dirs;
  18. my %pcm :shared;
  19. my @opts = ('-q 0', '-V 2', '--silent', '--id3v2-only');
  20. my $q = Thread::Queue->new();
  21.  
  22. if (!$ARGV[0] || ! -d $ARGV[0]) {
  23.         say "Usage: " . basename($0) . " [FLAC directory]";
  24.         exit;
  25. }
  26.  
  27. foreach my $arg (@ARGV) {
  28.         my $src = abs_path($arg);
  29.         if (-d $src) {
  30.                 getdirs($src);
  31.         }
  32. }
  33.  
  34. sub gettags {
  35.         my $fn = quotemeta(shift);
  36.         my (%alltags, @mflac);
  37.         open(OUTPUT, '-|', qq{metaflac --no-utf8-convert --export-tags-to=- $fn}) || die "can't run metaflac: $!";
  38.         chomp(@mflac = (<OUTPUT>));
  39.         foreach (@mflac) {
  40.                 my @tag = split('=');
  41.                 my $tagname = lc($tag[0]) or say $fn;
  42.  
  43.                 if ($alltags{$tagname}) { next; }
  44.                 elsif ($tag[1]) {
  45.                         $alltags{$tagname} = $tag[1];
  46.                 } else {
  47.                         $alltags{$tagname} = 'null';
  48.                 }
  49.         }
  50.         close(OUTPUT) || die "couldn't close metaflac: $!";
  51.         return %alltags;
  52. }
  53.  
  54. sub getdirs {
  55.  
  56.         my $dn = shift;
  57.  
  58.         open(FIND, '-|', qq(find -L "$dn" -name "*" -type d))
  59.         or die "Can't run 'find': $!";
  60.         push(@dirs, (<FIND>));
  61.         chomp(@dirs);
  62.         close(FIND) or die "Can't close 'find': $!";
  63. }
  64.  
  65. sub getfiles {
  66.         my $dn = shift;
  67.         my @files;
  68.         opendir(my $dh, $dn) or die "Can't open directory '$dn': $!";
  69.         foreach (sort(readdir $dh)) {
  70.                 my $fn = "$dn/$_";
  71.                 if (/.flac$/ && -f $fn) {
  72.                         push(@files, $fn);
  73.                 }
  74.         }
  75.         closedir $dh or die "Can't close directory '$dn': $!";
  76.         return @files;
  77. }
  78.  
  79. sub name {
  80.  
  81.         my $fn = shift;
  82.         my %t = gettags($fn);
  83.         my $tref = \%t;
  84.         my $dest;
  85.  
  86.         checktags($fn, $tref);
  87.  
  88.         my @dest = ('home', 'sir', 'lame', $t{albumartist}, $t{album});
  89.         foreach my $n (0 .. (scalar(@dest) - 1)) {
  90.                 $dest[$n] =~ tr!\\/<>|:;$*"'!!d;
  91.         }
  92.         { local $" = '/'; $dest = "/@{dest}/"; }
  93.  
  94.         make_path($dest);
  95.  
  96.         my $newfn = sprintf('%s-%02s. %s.mp3', $t{discnumber}, $t{tracknumber}, $t{title});
  97.         $newfn =~ tr!\\/<>|:;$*"'!!d;
  98.         $newfn = $dest . $newfn;
  99.  
  100.         return($newfn, $tref);
  101. }
  102.  
  103. sub decode {
  104.  
  105.         my $fn = shift;
  106.         local $/;
  107.         open(FLAC, '-|:raw', qq{flac --silent --stdout --decode "$fn"}) || die "can't run 'flac': $!";
  108.         chomp($pcm{$fn} = <FLAC>);
  109.         close(FLAC) || die "couldn't close 'flac': $!";
  110.  
  111.         lock($total);
  112.         $total += length($pcm{$fn});
  113. }
  114.  
  115. sub checktags {
  116.         my $fn = shift;
  117.         my $tref = shift;
  118.         my %t = %{$tref};
  119.         foreach my $tag ('discnumber', 'albumartist', 'album', 'tracknumber', 'title') {
  120.                 if (! $t{$tag}) {
  121.                         say "${fn}: missing '$tag' tag.";
  122.                         exit;
  123.                 }
  124.         }
  125. }
  126.  
  127. #say "Starting threads...";
  128. my @threads;
  129. for my $i (1..4) {
  130.         push(@threads, threads->create(\&lame));
  131. }
  132.  
  133. sub lame {
  134.  
  135.         my $started = 0;
  136.  
  137.         while ($q->pending() == 0 && $started == 0) {
  138.                 yield();
  139.                 next;
  140.         }
  141.  
  142.         $started++;
  143.  
  144.         my $tid = threads->tid();
  145.  
  146.         while (my $fn = $q->dequeue_nb()) {
  147.  
  148.                 my $oldfn = $fn;
  149.                 my ($newfn, $tref) = name($fn);
  150.                 my %t = %{$tref};
  151.  
  152.                 foreach my $tag ('artist', 'title', 'album', 'tracknumber', 'date') {
  153.                         if ($t{$tag}) {
  154.                                 given($tag) {
  155.                                         when ('artist') {
  156.                                                 push(@opts, '--ta');
  157.                                                 push(@opts, "\"$t{artist}\"");
  158.                                         }
  159.                                         when ('title') {
  160.                                                 push(@opts, '--tt');
  161.                                                 push(@opts, "\"$t{title}\"");
  162.                                         }
  163.                                         when ('album') {
  164.                                                 push(@opts, '--tl');
  165.                                                 push(@opts, "\"$t{album}\"");
  166.                                         }
  167.                                         when ('tracknumber') {
  168.                                                 push(@opts, '--tn');
  169.                                                 push(@opts, "\"$t{tracknumber}\"");
  170.                                         }
  171.                                         when ('date') {
  172.                                                 $t{date} =~ /[0-9]{4}/;
  173.                                                 push(@opts, '--ty');
  174.                                                 push(@opts, "\"$t{date}\"");
  175.                                         }
  176.                                 }
  177.                         }
  178.                 }
  179.  
  180.                 say "thread${tid} ${fn}: encoding...";
  181.                 open(LAME, '|-:raw', qq{lame @opts - "$newfn"}) || die "can't run 'lame': $!";
  182.                 print LAME $pcm{$oldfn};
  183.                 close(LAME) || die "couldn't close 'lame': $!";
  184.  
  185.                 lock(%pcm);
  186.                 lock($total);
  187.                 $total -= length($pcm{$oldfn});
  188.                 delete($pcm{$oldfn});
  189.         }
  190. }
  191.  
  192. foreach my $dn (sort(@dirs)) {
  193.  
  194.         my @files = getfiles($dn);
  195.  
  196.         my @stat;
  197.         my $album_size = 0;
  198.  
  199.         if (scalar(@files) == 0) {
  200.                 next;
  201.         }
  202.  
  203.         # Check the size of the album to make more informed
  204.         # decisions on when to yield in the next loop
  205.         foreach my $fn (@files) {
  206.                 @stat = stat($fn);
  207.                 $album_size += $stat[7];
  208.         }
  209.  
  210.         #say "\nDecoding files and storing in RAM...\n";
  211.         foreach my $fn (@files) {
  212.  
  213.                 if ($album_size > 600000000) { goto(DECODE); }
  214.  
  215.                 until ($total < $disk_size) {
  216.                         yield();
  217.                 }
  218.  
  219.                 DECODE: decode($fn);
  220.         }
  221.  
  222.         #say "\nEncoding PCM data to LAME MP3...\n";
  223.  
  224.         #say "Enqueueing files";
  225.  
  226.         foreach my $fn (@files) {
  227.                 $q->enqueue($fn);
  228.         }
  229.  
  230. }
  231.  
  232. foreach my $t (threads->list()) { $t->join(); }
  233. say "All threads joined";

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at