#!/usr/bin/perl #use Socket; #use Sys::Hostname; #use Time::Local; use IPC::Open3; #use File::Glob ':glob'; use Tk; use Tk::Xrm; use Tk qw(exit); use CDDB; $version="Blueberry Armageddon 20100731"; $width=400; ($device)=@ARGV; $query='cdparanoia -d $device -Q 2>&1'; $rip='cdparanoia -ez -d $device $track -w $wavfile'; $encode='oggenc -q 4.9 -a {$metaartist} -t {$metatitle} -l {$metaalbum} $wavfile -o $outfile'; $playrip='cdparanoia -vZd $device $playstatus $pipe'; $play='play -t wav $pipe '; $trackbase="/media/audio/albums"; $suffix=".ogg"; if($#ARGV<0){ print "Usage: Rip /dev/???\n\n"; exit(1); } # build the UI my $toplevel=new MainWindow(-class=>'MontyRip'); my $Xname=$toplevel->Class; $toplevel->optionAdd("$Xname.background", "#3d84ff",20); $toplevel->optionAdd("$Xname*highlightBackground", "#80c0d3",20); $toplevel->optionAdd("$Xname.Panel.background", "#3d84ff",20); $toplevel->optionAdd("$Xname.Panel.foreground", "#d0d0d0",20); $toplevel->optionAdd("$Xname.Panel.font", '-*-helvetica-bold-o-*-*-18-*-*-*-*-*-*-*',20); $toplevel->optionAdd("$Xname*Statuslabel.font", '-*-helvetica-bold-r-*-*-18-*-*-*-*-*-*-*',20); $toplevel->optionAdd("$Xname*Statuslabel.foreground", "#606060"); $toplevel->optionAdd("$Xname*Status.font", '-*-helvetica-bold-r-*-*-18-*-*-*-*-*-*-*',20); $toplevel->optionAdd("$Xname*AlertDetail.font", '-*-helvetica-medium-r-*-*-10-*-*-*-*-*-*-*',20); $toplevel->optionAdd("$Xname*background", "#d0d0d0",20); $toplevel->optionAdd("$Xname*foreground", '#000000',20); $toplevel->optionAdd("$Xname*Button*background", "#f0d0b0",20); $toplevel->optionAdd("$Xname*Button*foreground", '#000000',20); $toplevel->optionAdd("$Xname*Button*borderWidth", '2',20); $toplevel->optionAdd("$Xname*Button*relief", 'groove',20); $toplevel->optionAdd("$Xname*Button*padY", 1,20); $toplevel->optionAdd("$Xname*Checkbutton*background", "#f0d0b0",20); $toplevel->optionAdd("$Xname*Checkbutton*foreground", '#000000',20); $toplevel->optionAdd("$Xname*Checkbutton*borderWidth", '2',20); $toplevel->optionAdd("$Xname*Checkbutton*relief", 'groove',20); $toplevel->optionAdd("$Xname*activeBackground", "#ffffff",20); $toplevel->optionAdd("$Xname*activeForeground", '#0000a0',20); $toplevel->optionAdd("$Xname*borderWidth", 0,20); $toplevel->optionAdd("$Xname*relief", 'flat',20); $toplevel->optionAdd("$Xname*activeBorderWidth", 1,20); $toplevel->optionAdd("$Xname*highlightThickness", 0,20); $toplevel->optionAdd("$Xname*padX", 2,20); $toplevel->optionAdd("$Xname*padY", 2,20); $toplevel->optionAdd("$Xname*font", '-*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*',20); $toplevel->optionAdd("$Xname*Entry.font", '-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*',20); $toplevel->optionAdd("$Xname*Exit.font", '-*-helvetica-bold-r-*-*-10-*-*-*-*-*-*-*',20); $toplevel->optionAdd("$Xname*Exit.relief", 'groove',20); $toplevel->optionAdd("$Xname*Exit.padX", 1,20); $toplevel->optionAdd("$Xname*Exit.padY", 1,20); $toplevel->optionAdd("$Xname*Exit.borderWidth", 2,20); $toplevel->optionAdd("$Xname*Exit*background", "#a0a0a0",20); $toplevel->optionAdd("$Xname*Exit*disabledForeground", "#ffffff",20); $toplevel->optionAdd("$Xname*Entry.background", "#ffffff",20); $toplevel->optionAdd("$Xname*Entry.disabledForeground", "#c0c0c0",20); $toplevel->optionAdd("$Xname*Entry.relief", "sunken",20); $toplevel->optionAdd("$Xname*Entry.borderWidth", 1,20); $toplevel->optionAdd("$Xname*Field.background", "#ffffff",20); $toplevel->optionAdd("$Xname*Field.disabledForeground", "#c0c0c0",20); $toplevel->optionAdd("$Xname*Field.relief", "flat",20); $toplevel->optionAdd("$Xname*Field.borderWidth", 1,20); $toplevel->optionAdd("$Xname*Label.disabledForeground", "#c0c0c0",20); $toplevel->optionAdd("$Xname*Label.borderWidth", 1,20); $toplevel->configure(-background=>$toplevel->optionGet("background","")); #$toplevel->resizable(FALSE,FALSE); $window_shell=$toplevel->Label(Name=>"shell",-borderwidth=>1,-relief=>raised)-> place(-x=>10,-y=>36,-relwidth=>1.0,-relheight=>1.0, -width=>-20,-height=>-46,-anchor=>'nw'); $window_quit=$window_shell->Button(-class=>"Exit",-text=>"quit")-> place(-x=>-1,-y=>-1,-relx=>1.0,-rely=>1.0,-anchor=>'se'); $window_version=$toplevel-> Label(Name=>"logo text",-class=>"Panel",-text=>$version)-> place(-x=>5,-y=>5,-anchor=>'nw'); $y=8; $x=5; $window_statuslabel=$window_shell-> Label(Name=>"statuslabel",-class=>"Statuslabel",-text=>"Status: ")-> place(-x=>$x,-y=>$y,-anchor=>'nw'); $window_status=$window_shell-> Label(Name=>"status",-class=>"Status",-text=>"Starting...",-anchor=>'w')-> place(-x=>0,-y=>0,-relx=>1.,-anchor=>'nw',-in=>$window_statuslabel); $y+=$window_statuslabel->reqheight()*1.3; $window_quit->configure(-command=>[sub{Shutdown();}]); $window_device_label=$window_shell->Label(-text=>"device: ")-> place(-x=>$x,-y=>$y,-anchor=>'nw'); $window_device=$window_shell->Label(-class=>"Field",-text=>"$device")-> place(-relx=>1.,-anchor=>'nw',-bordermode=>outside, -in=>$window_device_label); $window_go=$window_shell->Button(-text=>"go",-state=>'disabled')-> place(-x=>-$x,-y=>$y,-relx=>1.,-anchor=>'ne',-bordermode=>outside); $window_check=$window_shell->Button(-text=>"recheck",-state=>disabled)-> place(-anchor=>'ne',-bordermode=>outside,-in=>$window_go); $window_scan=$window_shell->Button(-text=>"rescan")-> place(-anchor=>'ne',-bordermode=>outside,-in=>$window_check); $y+=$window_scan->reqheight()*1.2+5; $window_artist_label=$window_shell->Label(-text=>"artist: ")-> place(-x=>$x,-y=>$y,-anchor=>'nw'); $window_artist=$window_shell-> Entry(-width=>1024,-textvariable=>\$artist)-> place(-x=>$x+$window_artist_label->reqwidth(),-y=>$y, -relwidth=>1., -width=>-10-$window_artist_label->reqwidth(), -anchor=>'nw'); $y+=$window_artist_label->reqheight(); $window_album_label=$window_shell->Label(-text=>"album: ")-> place(-x=>$x,-y=>$y,-anchor=>'nw'); $window_album=$window_shell-> Entry(-width=>1024,-textvariable=>\$album)-> place(-x=>$x+$window_artist_label->reqwidth(),-y=>$y, -relwidth=>1., -width=>-10-$window_artist_label->reqwidth(), -anchor=>'nw'); $y+=$window_artist_label->reqheight()+5; $infoy=$y; $seekrit=0; $tracks=0; for(my$i=1;$i<=99;$i++){ my$text="track$i: "; $text="track0$i: " if($i<10); $window_track_label[$i]=$window_shell->Label(-text=>$text); $trackname[$i]=""; $window_track[$i]=$window_shell-> Entry(-width=>1024,-textvariable=>\$trackname[$i]); $window_play[$i]=$window_shell-> Button(-text=>"play",-command=>[main::playtrack,$i]); $window_rip[$i]=$window_shell-> Checkbutton(-variable=>\$trackrip[$i],-text=>"rip"); } $totaly=int(46+$window_quit->reqheight()+10+$y); $toplevel->configure(-width=>$width); $toplevel->configure(-height=>$totaly); $window_scan->configure(-command=>[sub{Rescan();}]); $window_check->configure(-command=>[sub{Recheck();}]); $window_go->configure(-command=>[sub{Rip();}]); Status("Ready to Query"); my $ripstatus=0; my $playstatus=0; # main loop Tk::MainLoop(); sub playstop{ if($playstatus>0){ unlink "/tmp/$$.pipe"; $toplevel->fileevent(PLAY_STDERR,'readable' => ''); Status("Stopping playback"); close (PLAY_STDERR); kill 15, ($playpid1); waitpid $playpid1,0; waitpid $playpid2,0; $window_play[$playstatus]->configure(-text=>"play"); Status("Playback stopped"); $playstatus=0; } } sub playtrack{ my($tracknum)=@_; if($playstatus>0 && $playstatus==$tracknum){ playstop(); }else{ playstop(); $playstatus=$tracknum; $window_play[$tracknum]->configure(-text=>"stop"); # creating a process -> process pipe is buggy if we [the Perl # script] are not one of the processes (first one works, # subsequent ones always fail). So, make a named pipe # instead. my$pipe="/tmp/$$.pipe"; system('mknod', $pipe, 'p') && system('mkfifo', $pipe) && die "can't mknod $pipe: $!"; my$glob; eval "\$glob=\"$playrip\""; my$glob2; eval "\$glob2=\"$play\""; $SIG{PIPE} = 'IGNORE'; die "pipe call failed unexpectedly: $!" unless pipe PLAY_STDERR,WRITEH2; $playpid1=open3("STDIN","STDOUT",">&WRITEH2",$glob); $playpid2=open3("STDIN","STDOUT",">&WRITEH2",$glob2); die $! unless close WRITEH2; Status("Playing track $tracknum..."); $toplevel->fileevent(PLAY_STDERR,'readable'=>[sub{PlayStderr();}]); } } sub Cancel{ Status("Cancelling"); if(defined($rippid) && $rippid>=0){ $toplevel->fileevent(RIP_STDERR,'readable' => ''); kill 15, ($rippid); close (RIP_STDERR); unlink $wavfile; unlink $outfile; waitpid $rippid,0; } $window_go->configure(-command=>[sub{Rip();}],-text=>"go"); $window_scan->configure(-command=>[sub{Rescan();}],-text=>"rescan"); $ripstatus=0; $rippid=-1; Status("Ready"); } sub Skip{ Status("Skipping"); if(defined($rippid) && $rippid>=0){ $toplevel->fileevent(RIP_STDERR,'readable' => ''); kill 15, ($rippid); close (RIP_STDERR); unlink $wavfile; unlink $outfile; waitpid $rippid,0; } $rippid=-1; Rip(); } sub Rip{ $ripstatus++; $window_go->configure(-command=>[sub{Cancel();}],-text=>"cancel"); $window_scan->configure(-command=>[sub{Skip();}],-text=>"skip"); print STDERR "\n"; while($ripstatus<=$tracks){ if($trackrip[$ripstatus]){ # rip this track; event driven, set up the callback. my$track=$ripstatus; my$title=$trackname[$track]; $track=0 if($track==$tracks && $seekrit); $wavfile="/tmp/$$.wav"; my$glob; eval "\$glob=\"$rip\""; print "$glob\n"; die "pipe call failed unexpectedly: $!" unless pipe RIP_STDERR,WRITEH; $rippid=open3("STDIN",">&STDOUT",">&WRITEH",$glob); close WRITEH; Status("Starting read..."); $toplevel->fileevent(RIP_STDERR,'readable'=>[sub{ReadStderr();}]); return; } $ripstatus++; } # all done! $window_go->configure(-command=>[sub{Rip();}],-text=>"go"); $window_scan->configure(-command=>[sub{Rescan();}],-text=>"rescan"); Status("Done"); $ripstatus=0; } sub Track_Path{ my($tracknum)=@_; $trackpad=$tracknum; $trackpad=0 if($trackpad==$tracks && $seekrit); $trackpad="0$trackpad" if($trackpad<10); "$trackbase/$artist/$album/$artist - $album - $trackpad - $trackname[$tracknum]$suffix"; } sub Rescan{ Status("Scanning disc"); $toplevel->update(); $window_go->configure(-state=>"disabled"); # get table of contents my$foo; eval "\$foo=\"$query\""; my$output=`$foo`; my$leadout; $tracks=0; $output=~s/[^=]+=+\n(.+)/$1/; my @toc; # CDDB protocol is stupidly designed in many ways, but one of the # biggest is that matching depends on the offset of the first # track... which is neither deterministic from CDROM drive to # CDROM drive, nor a well defined concept in the RedBook spec. # Even worse, it appears that much CDROM software always reports # it as MSF 0 2 0 regardless of what it is, and thus CDDB often # has the offset for the whole disc fucked up. I've seen discs # encoded in CDDB in one of several different ways, so we try the # ones I know about. # 1: Offset drive's track information by 2 sec # 2: Use drive's track information directly # 3: Offset drive's track information to exactly 2 sec # 4: Offset drive's track information to exactly 0 sec # 5: Use drive's track information directly, but set first track to 2 sec foreach (split(/\n/,$output)){ if(m/^\s*([0-9]+)\.\s+(\d+)\s+\S+\s+(\d+)/){ my$f=$3+150; my$s=int($f/75); $f-=$s*75; my$m=int($s/60); $s-=$m*60; $tracks=$1 if ($1>$tracks); $leadout=$2+$3; push @toc,"$1 $m $s $f"; $initial=$3 if($1==1); } } if($tracks>0){ my$f=$leadout+150; my$s=int($f/75); $f-=$s*75; my$m=int($s/60); $s-=$m*60; push @toc, "999 $m $s $f"; } if($tracks==0){ $artist=""; $album=""; AlbumUpdate(); Status("Query failed"); Alert("Could not query cdrom; drive empty?",$output); return; } for(my$i=1;$i<=99;$i++){ $trackrip[$i]=1; } Status("Querying CDDB (format 1)"); $toplevel->update(); # perform cddb query my $cddb = new CDDB( Host => 'freedb.freedb.org', Port => 8880, Submit_Address => 'test-submit@freedb.org', Debug => 0, ) or die $!; my @discs = $cddb->get_discs_by_toc(@toc); my@temp=@{$discs[0]}; if(!defined(@discs)|| $#discs<0 || !defined(@temp[1])){ # 2: Use drive's track information directly @toc = []; foreach (split(/\n/,$output)){ if(m/^\s*([0-9]+)\.\s+(\d+)\s+\S+\s+(\d+)/){ my$f=$3; my$s=int($f/75); $f-=$s*75; my$m=int($s/60); $s-=$m*60; $tracks=$1 if ($1>$tracks); $leadout=$2+$3; push @toc,"$1 $m $s $f"; $initial=$3 if($1==1); } } if($tracks>0){ my$f=$leadout; my$s=int($f/75); $f-=$s*75; my$m=int($s/60); $s-=$m*60; push @toc, "999 $m $s $f"; } # perform cddb query $cddb->DESTROY(); $cddb = new CDDB() or die $!; Status("Querying CDDB (format 2)"); $toplevel->update(); @discs = $cddb->get_discs_by_toc(@toc); @temp=@{$discs[0]}; } if(!defined(@discs)|| $#discs<0 || !defined(@temp[1])){ # 3: Offset drive's track information to exactly 2 sec @toc = []; my $offset; foreach (split(/\n/,$output)){ if(m/^\s*([0-9]+)\.\s+(\d+)\s+\S+\s+(\d+)/){ if(!defined($offset)){ $offset = 150-$3; } my$f=$3+$offset; my$s=int($f/75); $f-=$s*75; my$m=int($s/60); $s-=$m*60; $tracks=$1 if ($1>$tracks); $leadout=$2+$3; push @toc,"$1 $m $s $f"; $initial=$3 if($1==1); } } if($tracks>0){ my$f=$leadout+$offset; my$s=int($f/75); $f-=$s*75; my$m=int($s/60); $s-=$m*60; push @toc, "999 $m $s $f"; } if($offset != 0){ # perform cddb query $cddb->DESTROY(); $cddb = new CDDB() or die $!; Status("Querying CDDB (format 3)"); $toplevel->update(); @discs = $cddb->get_discs_by_toc(@toc); @temp=@{$discs[0]}; } } if(!defined(@discs)|| $#discs<0 || !defined(@temp[1])){ # 4: Offset drive's track information to exactly 0 sec @toc = []; my $offset; foreach (split(/\n/,$output)){ if(m/^\s*([0-9]+)\.\s+(\d+)\s+\S+\s+(\d+)/){ if(!defined($offset)){ $offset = -$3; } my$f=$3+$offset; my$s=int($f/75); $f-=$s*75; my$m=int($s/60); $s-=$m*60; $tracks=$1 if ($1>$tracks); $leadout=$2+$3; push @toc,"$1 $m $s $f"; $initial=$3 if($1==1); } } if($tracks>0){ my$f=$leadout+$offset; my$s=int($f/75); $f-=$s*75; my$m=int($s/60); $s-=$m*60; push @toc, "999 $m $s $f"; } if($offset != 0){ # perform cddb query $cddb->DESTROY(); $cddb = new CDDB() or die $!; Status("Querying CDDB (format 4)"); $toplevel->update(); @discs = $cddb->get_discs_by_toc(@toc); @temp=@{$discs[0]}; } } if(!defined(@discs)|| $#discs<0 || !defined(@temp[1])){ # 5: Use drive's track information directly, but set first track to 2 sec @toc = []; my $offset; foreach (split(/\n/,$output)){ if(m/^\s*([0-9]+)\.\s+(\d+)\s+\S+\s+(\d+)/){ if(!defined($offset)){ $offset = $3; } my$f=$3; my$s=int($f/75); $f-=$s*75; my$m=int($s/60); $s-=$m*60; $tracks=$1 if ($1>$tracks); $leadout=$2+$3; push @toc,"$1 $m $s $f"; $initial=$3 if($1==1); } } if($tracks>0){ my$f=$leadout; my$s=int($f/75); $f-=$s*75; my$m=int($s/60); $s-=$m*60; push @toc, "999 $m $s $f"; } if($offset != 150){ $toc[0]="1 0 2 0"; # perform cddb query $cddb->DESTROY(); $cddb = new CDDB() or die $!; Status("Querying CDDB (format 3)"); $toplevel->update(); @discs = $cddb->get_discs_by_toc(@toc); @temp=@{$discs[0]}; } } if(!defined(@discs)|| $#discs<0 || !defined(@temp[1])){ Status("Album Loaded; no CDDB entry"); for(my$i=1;$i<=$tracks;$i++){ $trackname[$i]=""; } $artist=""; $album=""; $seekrit=0; if($initial>100){ $tracks++; $trackname[$tracks]="Hidden Track Zero"; $seekrit=1; } $cddb->DESTROY(); AlbumUpdate(); $window_go->configure(-state=>"normal"); $window_check->configure(-state=>"normal"); return; } my ($genre, $cddb_id, $title) = @{$discs[0]}; $title=~/(.+)\s\/\s(.+)/; $artist=$1; $album=$2; $artist=~s/\//:/g; $album=~s/\//:/g; my $disc_info = $cddb->get_disc_details($genre, $cddb_id); for(my$i=0;$i<$tracks;$i++){ $trackname[$i+1]=$disc_info->{"ttitles"}[$i]; $trackname[$i+1]=~s/\//:/g; } $seekrit=0; if($initial>100){ $tracks++; $trackname[$tracks]="Hidden Track Zero"; $seekrit=1; } Recheck(); Status("Album Loaded"); $cddb->DESTROY(); AlbumUpdate(); $window_go->configure(-state=>"normal"); $window_check->configure(-state=>"normal"); } sub Recheck{ for(my$i=1;$i<=$tracks;$i++){ $trackrip[$i]=0 if(-e Track_Path($i)); } } sub AlbumUpdate{ my $y=$infoy; # re-place widgets up to number of tracks... for(my$i=1;$i<=$tracks;$i++){ $window_track_label[$i]-> place(-y=>$y,-x=>$x,-anchor=>'nw',-bordermode=>outside); $window_rip[$i]-> place(-y=>$y,-x=>-$x,-relx=>1.,-anchor=>'ne',-bordermode=>outside); $window_play[$i]-> place(-y=>$y,-x=>-$x-$window_rip[$i]->reqwidth(),-relx=>1., -anchor=>'ne',-bordermode=>outside); $window_track[$i]-> place(-y=>$y, -x=>$x+$window_track_label[$i]->reqwidth(), -relwidth=>1., -width=> -15 -$window_rip[$i]->reqwidth() -$window_track_label[$i]->reqwidth() -$window_play[$i]->reqwidth(), -anchor=>'nw',-bordermode=>outside); $y+=$window_rip[$i]->reqheight(); } # unplace widgets past number of tracks for(my$i=$tracks+1;$i<=99;$i++){ $window_track_label[$i]->placeForget(); $window_track[$i]->placeForget(); $window_play[$i]->placeForget(); $window_rip[$i]->placeForget(); } my $totaly=int(46+$window_quit->reqheight()+10+$y); my $geometry=$toplevel->geometry(); $toplevel->minsize(400,$totaly); $geometry=~/^(\d+)/; $toplevel->configure(-width=>$1); $toplevel->configure(-height=>$totaly); } sub Shutdown{ Cancel(); playstop(); Tk::exit(0); } sub Status{ my$text=shift @_; $window_status->configure(-text=>"$text"); #$toplevel->update(); } sub Alert{ my($message,$detail,$window)=@_; $window=$toplevel if(!defined($window)); $modal->destroy() if(defined($modal)); print STDERR "\n\n$message :: $detail\n"; $modal=new MainWindow(-class=>"$Xname"); $modal->configure(-background=>$modal->optionGet("background","")); $modal_shell=$modal->Label(-class=>Alert,Name=>"shell", -borderwidth=>1,-relief=>raised)-> place(-x=>4,-y=>4,-relwidth=>1.0,-relheight=>1.0, -width=>-8,-height=>-8,-anchor=>'nw'); $modal_exit=$modal_shell-> Button(-class=>"Exit",-text=>"X")-> place(-x=>-1,-y=>-1,-relx=>1.0,-rely=>1.0,-anchor=>'se'); $modal_message=$modal_shell-> Label(-text=>$message,-class=>"AlertText")-> place(-x=>5,-y=>10); my$width=$modal_message->reqwidth(); $width=300 if($width<300); $detail="" if(!defined($detail)); $modal_detail=$modal_shell-> Message(-text=>$detail,-class=>"AlertDetail", -width=>($width-$modal_exit->reqwidth()))-> place(-relx=>0,-y=>5,-rely=>1.0,-anchor=>'nw', -in=>$modal_message); $width+=20; my$height=$modal_message->reqheight()+$modal_detail->reqheight()+30; my$xx=$window->rootx(); my$yy=$window->rooty(); my$ww=$window->width(); my$hh=$window->height(); my$x=$xx+$ww/2-$width/2; my$y=$yy+$hh/2-$height/2; $modal->geometry($width."x".$height."+".int($x)."+".int($y)); $modal->resizable(FALSE,FALSE); #$modal->transient($window); $modal_exit->configure(-command=>[sub{$modal->destroy();undef $modal}]); } sub ReadStderr{ my$saveflag=0; $bytes=sysread RIP_STDERR, my$scalar, 4096; if($bytes==0){ $toplevel->fileevent(RIP_STDERR,'readable' => ''); Alert("ripper unexpectedly exited!","Skipping track $ripstatus for now..."); Skip(); } $scalar=~s/\r/\n/; push my@lines, split /[\n|\r]/, $saved_stderr.$scalar; if((chomp $scalar)==0){ $saved_stderr=$lines[$#lines]; }else{ $saved_stderr=""; } foreach my$line (@lines){ if($line=~/Done encoding file/){ $saved_stderr=""; # shut down waitpid $rippid,0; $rippid=-1; $toplevel->fileevent(RIP_STDERR,'readable' => ''); close(RIP_STDERR); # move the file my$trackname="$trackbase"; mkdir $trackname if(!-e $trackname); $trackname="$trackbase/$artist"; mkdir $trackname if(!-e $trackname); $trackname="$trackbase/$artist/$album"; mkdir $trackname if(!-e $trackname); my$trackname=Track_Path($ripstatus); print STDERR "moving $outfile to $trackname\n"; unlink $wavfile; $trackname=~s/([\"\$\`\\])/\\$1/g; die "could not move file!: $!" if `mv "$outfile" "$trackname"`; # unclick the rip button Recheck(); #rip next Rip(); last; } if($line=~/\[finished\]/){ # cdparanoia finished $saved_stderr=""; $toplevel->fileevent(RIP_STDERR,'readable' => ''); waitpid $rippid,0; $rippid=-1; close(RIP_STDERR); my$metatitle=$trackname[$ripstatus]; $metaartist=$artist; $metaalbum=$album; $wavfile="/tmp/$$.wav"; $outfile="/tmp/$$.ogg"; $metatitle=~s/([\"\$\`\\])/\\$1/g; $metaartist=~s/([\"\$\`\\])/\\$1/g; $metaalbum=~s/([\"\$\`\\])/\\$1/g; my$glob; eval "\$glob=\"$encode\""; $glob=~s/[\{\}]/\"/g; print "$glob\n"; die "pipe call failed unexpectedly: $!" unless pipe RIP_STDERR,WRITEH; $rippid=open3("STDIN",">&STDOUT",">&WRITEH",$glob); close WRITEH; Status("Starting encode..."); $toplevel->fileevent(RIP_STDERR,'readable'=>[sub{ReadStderr();}]); last; } if($line=~/Encoding with/){ Status("Encoding track $ripstatus"); next; } if($line=~/\[\s*(\d*\.\d)\%\]/){ Status("Encoding track $ripstatus [$1%]"); next; } if($line=~/to sector\s*(\d+)/){ $endsector=$1; } if($line=~/\[wrote\]\s\@\s(\d+)/){ $left=$endsector-int($1/1176); Status("Reading track $ripstatus [$left]"); } } } sub PlayStderr{ my$saveflag=0; $bytes2=sysread PLAY_STDERR, my$scalar, 4096; print $scalar; if($bytes2==0){ playstop(); return; } push my@lines, split /[\n\r]/, $saved_stderr2.$scalar; if((chomp $scalar)==0){ $saved_stderr2=$lines[$#lines]; }else{ $saved_stderr2=""; } foreach my$line (@lines){ if($line=~/\[finished\]/){ # cdparanoia finished $saved_stderr2=""; playstop(); return; } } }