pastebin - collaborative debugging tool
kpaste.net RSS


flac2lame perl script threaded
Posted by Anonymous on Tue 11th Sep 2012 01:58
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 = 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 = abs_path($_);
  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.         make_path($dest);
  99.  
  100.         my $newfn = sprintf('%s-%02s. %s.mp3', $clean[2], $clean[3], $clean[4]);
  101.         $newfn = $dest . $newfn;
  102.  
  103.         return($newfn, $tref);
  104. }
  105.  
  106. sub decode {
  107.  
  108.         my $fn = shift;
  109.         local $/;
  110.         open(FLAC, '-|:raw', qq{flac --silent --stdout --decode "$fn"}) || die "can't run 'flac': $!";
  111.         chomp($pcm{$fn} = <FLAC>);
  112.         close(FLAC) || die "couldn't close 'flac': $!";
  113.  
  114.         lock($total);
  115.         $total += length($pcm{$fn});
  116. }
  117.  
  118. sub checktags {
  119.         my $fn = shift;
  120.         my $tref = shift;
  121.         my %t = %{$tref};
  122.         foreach my $tag ('discnumber', 'albumartist', 'album', 'tracknumber', 'title') {
  123.                 if (! $t{$tag}) {
  124.                         say "${fn}: missing '$tag' tag.";
  125.                         exit;
  126.                 }
  127.         }
  128. }
  129.  
  130. #say "Starting threads...";
  131. my @threads;
  132. for my $i (1..4) {
  133.         push(@threads, threads->create(\&lame));
  134. }
  135.  
  136. sub lame {
  137.  
  138.         my $started = 0;
  139.  
  140.         while ($q->pending() == 0 && $started == 0) {
  141.                 yield();
  142.                 next;
  143.         }
  144.  
  145.         $started++;
  146.  
  147.         my $tid = threads->tid();
  148.  
  149.         while (my $fn = $q->dequeue_nb()) {
  150.  
  151.                 my $oldfn = $fn;
  152.                 my ($newfn, $tref) = name($fn);
  153.                 my %t = %{$tref};
  154.  
  155.                 foreach my $tag ('artist', 'title', 'album', 'tracknumber', 'date') {
  156.                         if ($t{$tag}) {
  157.                                 given($tag) {
  158.                                         when ('artist') {
  159.                                                 push(@opts, '--ta');
  160.                                                 push(@opts, "\"$t{artist}\"");
  161.                                         }
  162.                                         when ('title') {
  163.                                                 push(@opts, '--tt');
  164.                                                 push(@opts, "\"$t{title}\"");
  165.                                         }
  166.                                         when ('album') {
  167.                                                 push(@opts, '--tl');
  168.                                                 push(@opts, "\"$t{album}\"");
  169.                                         }
  170.                                         when ('tracknumber') {
  171.                                                 push(@opts, '--tn');
  172.                                                 push(@opts, "\"$t{tracknumber}\"");
  173.                                         }
  174.                                         when ('date') {
  175.                                                 $t{date} =~ /[0-9]{4}/;
  176.                                                 push(@opts, '--ty');
  177.                                                 push(@opts, "\"$t{date}\"");
  178.                                         }
  179.                                 }
  180.                         }
  181.                 }
  182.  
  183.                 say "thread${tid} ${fn}: encoding...";
  184.                 open(LAME, '|-:raw', qq{lame @opts - "$newfn"}) || die "can't run 'lame': $!";
  185.                 print LAME $pcm{$oldfn};
  186.                 close(LAME) || die "couldn't close 'lame': $!";
  187.  
  188.                 lock(%pcm);
  189.                 lock($total);
  190.                 $total -= length($pcm{$oldfn});
  191.                 delete($pcm{$oldfn});
  192.         }
  193. }
  194.  
  195. foreach my $dn (sort(@dirs)) {
  196.  
  197.         my @files = getfiles($dn);
  198.  
  199.         my @stat;
  200.         my $album_size = 0;
  201.  
  202.         if (scalar(@files) == 0) {
  203.                 next;
  204.         }
  205.  
  206.         # Check the size of the album to make more informed
  207.         # decisions on when to yield in the next loop
  208.         #foreach my $fn (@files) {
  209.         #       @stat = stat($fn);
  210.         #       $album_size += $stat[7];
  211.         #}
  212.  
  213.         #say "\nDecoding files and storing in RAM...\n";
  214.         foreach my $fn (@files) {
  215.  
  216.                 if ($album_size > 600000000) { goto(DECODE); }
  217.  
  218.                 #until ($total < $disk_size) {
  219.                 #       yield();
  220.                 #}
  221.  
  222.                 DECODE: decode($fn);
  223.         }
  224.  
  225.         #say "\nEncoding PCM data to LAME MP3...\n";
  226.  
  227.         #say "Enqueueing files";
  228.  
  229.         foreach my $fn (@files) {
  230.                 $q->enqueue($fn);
  231.         }
  232.  
  233. }
  234.  
  235. foreach my $t (threads->list()) { $t->join(); }
  236. 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