pastebin - collaborative debugging tool
kpaste.net RSS


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