#!/usr/bin/perl # Purpose: # read high quality $ORIG_FORMAT files from one directory tree # and output low quality ogg to a mirror copy tree # # It will suffer intteruption and resume where it left off, which # is handy if you have a slower box and need to do it over the course # of several days # # The idea here is that I want to listen to my music on # my Sharp Zaurus which plays oggs, but to get my whole # collection on it I need to encode at a pretty low bitrate # # This script may be a bit sloppy/crappy/old, but it gets the job done. # I strongly suggest reading it to make sure it will work for your # system before running it. # # To convert from/to other formats is not too hard, just modify what needs modifying # # It does assume a directory structure of /artist/album/title_track#_etc.$ORIG_FORMAT # If your setup isn't like that, you can run it anyway or change the regexp in # the convertogg sub. # # You may feel free to use it for personal use only under the terms of the GNU GPL # a copy of which can be found at http://gnu.org/copyleft/gpl.html # # Copyright 2005 Matthew Steven http://www.matts.org/ # # Update Mar 2008, added average concurrency for taking advantage of multi core cpus use strict; my $ENCODER='which oggenc'; my $WAVMAKER=`which flac`; my $INFO=`which ogginfo`; chomp $INFO; chomp $WAVMAKER; chomp $ENCODER; my $TMPDIR='/tmp'; my $PATH_TO_HQ='/home/matt/tunes/All'; my $PATH_TO_LQ='/home/lq2_tunes'; my $OUT_QUALITY=2; # oggenc encoding quality. my $AVG_CONCURRENCY=3; # set this to number of cpu cores + 1 my $ORIG_FORMAT='flac'; $SIG{CHLD}='IGNORE'; main($PATH_TO_HQ); sub scanit($){ my $rootdir = shift; my($fileinquestion) = $rootdir =~ m/\/([^\/]+)$/; my($lqdir) = $rootdir =~ m/$PATH_TO_HQ\/(.*)/; if(-d "$rootdir"){ $lqdir =~ s/[^\w\s\d\/\-\.]+/_/g; mkdir("$PATH_TO_LQ/$lqdir") unless (-d "$PATH_TO_LQ/$lqdir"); main($rootdir); }elsif($fileinquestion =~ /$ORIG_FORMAT$/o){ my($outf) = "$PATH_TO_LQ/$lqdir" =~ m:^(.+)\.$ORIG_FORMAT:; $outf="$outf.ogg"; # vfat chokes on all sorts of things in file names so let's keep it clean $outf =~ s/[^\w\s\d\/\-\.]+/_/g; my($disp) = $rootdir =~ m:^.*/([^/]+)$:; print ">>>O) Converting $disp ...\n"; print "=" x 80; print "\n"; if(!-f $outf){ convertogg("$rootdir", $outf); }else{ print "Exists, skipping $disp"; } print "\n"; print "=" x 80; print "\n"; }elsif($fileinquestion =~ /m3u$/o){ print ">>>M) Linking $rootdir to $PATH_TO_LQ/$lqdir\n"; link($rootdir,"$PATH_TO_LQ/$lqdir"); }else{ print ">>>?) '$fileinquestion' skipping\n"; } } sub main($){ # Cant find a pure perl way so unix it is... my $qd=quotemeta($_[0]); foreach(`ls $qd`){ chomp; next unless($_ !~ /^(\.|\.\.)$/o); scanit("$_[0]/$_"); } } sub convertogg($$){ my($info) = $_[0] =~ m:$PATH_TO_HQ/(.+)$:; my($artist,$album,$title) = $info =~ m:^([^/]+)/([^/]+)/([^/]+).$ORIG_FORMAT$:; #die "Artist: $artist Album: $album Title: $title \n $info"; my $mqp=quotemeta($_[0]); my $mqc=quotemeta($_[1]); my $pid=-1; # Should we fork or should we wait? if(`pgrep oggenc|wc -l` < $AVG_CONCURRENCY){ $pid = fork(); return if($pid > 0); # if pid > 0 we forked and should jump to the next one }else{ sleep(4); return convertogg(@_); # we only want forked encoders, no waiting that way. # Recurse until it takes, shouldn't get very deep and will return once it forks off a pid. } $artist =~ s/[^\w\d]+/ /g; $title =~ s/[^\w\d]+/ /g; $album =~ s/[^\w\d]+/ /g; $album =~ s/^$artist[_\s]+(.*)$/$1/g; # strip artist from album name $title =~ s/^.+\D(\d\d\D.+)$/$1/g; # strip off the artist / album info from the song title, this is format-specific- my song files are 01_songname.ogg $artist=quotemeta($artist); $title=quotemeta($title); $album=quotemeta($album); # reason for the double-tagging is so that it will have uppercase tags for some players that are case-challenged my $progstr = "$WAVMAKER -d -c $mqp 2>/dev/null | $ENCODER -o $mqc -q $OUT_QUALITY -c ARTIST=$artist -a $artist -c TITLE=$title -t $title -c ALBUM=$album -l $album -"; `$progstr`; # id3tag them if you like, some programs don't read ogg metadata #`/usr/bin/id3tag -a $artist -A $album -s $title $mqc`; exit if($pid == 0) #this would be a child finished up }