Recognizes if input is ogg or not.
[xiph.git] / BlueberryArmageddon / blueberry_armageddon
blobbdd094c9297661521fb21d1b6e5940cfa961eb34
1 #!/usr/bin/perl
3 #use Socket;
4 #use Sys::Hostname;
5 #use Time::Local;
6 use IPC::Open3;
7 #use File::Glob ':glob';
8 use Tk;
9 use Tk::Xrm;
10 use Tk qw(exit);
11 use CDDB;
13 $version="Blueberry Armageddon 20041015";
14 $width=400;
15 ($device)=@ARGV;
17 $query='cdparanoia -d $device -Q 2>&1';
18 $rip='cdparanoia -ez -d $device $track -w $wavfile';
19 $encode='oggenc -q 4.9 -a {$metaartist} -t {$metatitle} -l {$metaalbum} $wavfile -o $outfile';
20 $playrip='cdparanoia -vZd $device $playstatus $pipe';
21 $play='play -t wav $pipe ';
23 $trackbase="/media/audio/albums";
24 $suffix=".ogg";
26 if($#ARGV<0){
27 print "Usage: Rip /dev/???\n\n";
28 exit(1);
32 # build the UI
33 my $toplevel=new MainWindow(-class=>'MontyRip');
34 my $Xname=$toplevel->Class;
36 $toplevel->optionAdd("$Xname.background", "#3d84ff",20);
37 $toplevel->optionAdd("$Xname*highlightBackground", "#80c0d3",20);
38 $toplevel->optionAdd("$Xname.Panel.background", "#3d84ff",20);
39 $toplevel->optionAdd("$Xname.Panel.foreground", "#d0d0d0",20);
40 $toplevel->optionAdd("$Xname.Panel.font",
41 '-*-helvetica-bold-o-*-*-18-*-*-*-*-*-*-*',20);
42 $toplevel->optionAdd("$Xname*Statuslabel.font",
43 '-*-helvetica-bold-r-*-*-18-*-*-*-*-*-*-*',20);
44 $toplevel->optionAdd("$Xname*Statuslabel.foreground", "#606060");
45 $toplevel->optionAdd("$Xname*Status.font",
46 '-*-helvetica-bold-r-*-*-18-*-*-*-*-*-*-*',20);
48 $toplevel->optionAdd("$Xname*AlertDetail.font",
49 '-*-helvetica-medium-r-*-*-10-*-*-*-*-*-*-*',20);
52 $toplevel->optionAdd("$Xname*background", "#d0d0d0",20);
53 $toplevel->optionAdd("$Xname*foreground", '#000000',20);
55 $toplevel->optionAdd("$Xname*Button*background", "#f0d0b0",20);
56 $toplevel->optionAdd("$Xname*Button*foreground", '#000000',20);
57 $toplevel->optionAdd("$Xname*Button*borderWidth", '2',20);
58 $toplevel->optionAdd("$Xname*Button*relief", 'groove',20);
59 $toplevel->optionAdd("$Xname*Button*padY", 1,20);
61 $toplevel->optionAdd("$Xname*Checkbutton*background", "#f0d0b0",20);
62 $toplevel->optionAdd("$Xname*Checkbutton*foreground", '#000000',20);
63 $toplevel->optionAdd("$Xname*Checkbutton*borderWidth", '2',20);
64 $toplevel->optionAdd("$Xname*Checkbutton*relief", 'groove',20);
66 $toplevel->optionAdd("$Xname*activeBackground", "#ffffff",20);
67 $toplevel->optionAdd("$Xname*activeForeground", '#0000a0',20);
68 $toplevel->optionAdd("$Xname*borderWidth", 0,20);
69 $toplevel->optionAdd("$Xname*relief", 'flat',20);
70 $toplevel->optionAdd("$Xname*activeBorderWidth", 1,20);
71 $toplevel->optionAdd("$Xname*highlightThickness", 0,20);
72 $toplevel->optionAdd("$Xname*padX", 2,20);
73 $toplevel->optionAdd("$Xname*padY", 2,20);
74 $toplevel->optionAdd("$Xname*font",
75 '-*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*',20);
76 $toplevel->optionAdd("$Xname*Entry.font",
77 '-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*',20);
79 $toplevel->optionAdd("$Xname*Exit.font",
80 '-*-helvetica-bold-r-*-*-10-*-*-*-*-*-*-*',20);
81 $toplevel->optionAdd("$Xname*Exit.relief", 'groove',20);
82 $toplevel->optionAdd("$Xname*Exit.padX", 1,20);
83 $toplevel->optionAdd("$Xname*Exit.padY", 1,20);
84 $toplevel->optionAdd("$Xname*Exit.borderWidth", 2,20);
85 $toplevel->optionAdd("$Xname*Exit*background", "#a0a0a0",20);
86 $toplevel->optionAdd("$Xname*Exit*disabledForeground", "#ffffff",20);
88 $toplevel->optionAdd("$Xname*Entry.background", "#ffffff",20);
89 $toplevel->optionAdd("$Xname*Entry.disabledForeground", "#c0c0c0",20);
90 $toplevel->optionAdd("$Xname*Entry.relief", "sunken",20);
91 $toplevel->optionAdd("$Xname*Entry.borderWidth", 1,20);
93 $toplevel->optionAdd("$Xname*Field.background", "#ffffff",20);
94 $toplevel->optionAdd("$Xname*Field.disabledForeground", "#c0c0c0",20);
95 $toplevel->optionAdd("$Xname*Field.relief", "flat",20);
96 $toplevel->optionAdd("$Xname*Field.borderWidth", 1,20);
98 $toplevel->optionAdd("$Xname*Label.disabledForeground", "#c0c0c0",20);
99 $toplevel->optionAdd("$Xname*Label.borderWidth", 1,20);
101 $toplevel->configure(-background=>$toplevel->optionGet("background",""));
103 #$toplevel->resizable(FALSE,FALSE);
105 $window_shell=$toplevel->Label(Name=>"shell",-borderwidth=>1,-relief=>raised)->
106 place(-x=>10,-y=>36,-relwidth=>1.0,-relheight=>1.0,
107 -width=>-20,-height=>-46,-anchor=>'nw');
109 $window_quit=$window_shell->Button(-class=>"Exit",-text=>"quit")->
110 place(-x=>-1,-y=>-1,-relx=>1.0,-rely=>1.0,-anchor=>'se');
112 $window_version=$toplevel->
113 Label(Name=>"logo text",-class=>"Panel",-text=>$version)->
114 place(-x=>5,-y=>5,-anchor=>'nw');
116 $y=8;
117 $x=5;
119 $window_statuslabel=$window_shell->
120 Label(Name=>"statuslabel",-class=>"Statuslabel",-text=>"Status: ")->
121 place(-x=>$x,-y=>$y,-anchor=>'nw');
123 $window_status=$window_shell->
124 Label(Name=>"status",-class=>"Status",-text=>"Starting...",-anchor=>'w')->
125 place(-x=>0,-y=>0,-relx=>1.,-anchor=>'nw',-in=>$window_statuslabel);
127 $y+=$window_statuslabel->reqheight()*1.3;
129 $window_quit->configure(-command=>[sub{Shutdown();}]);
131 $window_device_label=$window_shell->Label(-text=>"device: ")->
132 place(-x=>$x,-y=>$y,-anchor=>'nw');
133 $window_device=$window_shell->Label(-class=>"Field",-text=>"$device")->
134 place(-relx=>1.,-anchor=>'nw',-bordermode=>outside,
135 -in=>$window_device_label);
137 $window_go=$window_shell->Button(-text=>"go",-state=>'disabled')->
138 place(-x=>-$x,-y=>$y,-relx=>1.,-anchor=>'ne',-bordermode=>outside);
140 $window_check=$window_shell->Button(-text=>"recheck",-state=>disabled)->
141 place(-anchor=>'ne',-bordermode=>outside,-in=>$window_go);
143 $window_scan=$window_shell->Button(-text=>"rescan")->
144 place(-anchor=>'ne',-bordermode=>outside,-in=>$window_check);
146 $y+=$window_scan->reqheight()*1.2+5;
148 $window_artist_label=$window_shell->Label(-text=>"artist: ")->
149 place(-x=>$x,-y=>$y,-anchor=>'nw');
151 $window_artist=$window_shell->
152 Entry(-width=>1024,-textvariable=>\$artist)->
153 place(-x=>$x+$window_artist_label->reqwidth(),-y=>$y,
154 -relwidth=>1.,
155 -width=>-10-$window_artist_label->reqwidth(),
156 -anchor=>'nw');
158 $y+=$window_artist_label->reqheight();
160 $window_album_label=$window_shell->Label(-text=>"album: ")->
161 place(-x=>$x,-y=>$y,-anchor=>'nw');
163 $window_album=$window_shell->
164 Entry(-width=>1024,-textvariable=>\$album)->
165 place(-x=>$x+$window_artist_label->reqwidth(),-y=>$y,
166 -relwidth=>1.,
167 -width=>-10-$window_artist_label->reqwidth(),
168 -anchor=>'nw');
170 $y+=$window_artist_label->reqheight()+5;
171 $infoy=$y;
173 $seekrit=0;
174 $tracks=0;
175 for(my$i=1;$i<=99;$i++){
176 my$text="track$i: ";
177 $text="track0$i: " if($i<10);
179 $window_track_label[$i]=$window_shell->Label(-text=>$text);
180 $trackname[$i]="";
181 $window_track[$i]=$window_shell->
182 Entry(-width=>1024,-textvariable=>\$trackname[$i]);
183 $window_play[$i]=$window_shell->
184 Button(-text=>"play",-command=>[main::playtrack,$i]);
185 $window_rip[$i]=$window_shell->
186 Checkbutton(-variable=>\$trackrip[$i],-text=>"rip");
189 $totaly=int(46+$window_quit->reqheight()+10+$y);
191 $toplevel->configure(-width=>$width);
192 $toplevel->configure(-height=>$totaly);
194 $window_scan->configure(-command=>[sub{Rescan();}]);
195 $window_check->configure(-command=>[sub{Recheck();}]);
196 $window_go->configure(-command=>[sub{Rip();}]);
198 Status("Ready to Query");
199 my $ripstatus=0;
200 my $playstatus=0;
202 # main loop
203 Tk::MainLoop();
205 sub playstop{
206 if($playstatus>0){
208 unlink "/tmp/$$.pipe";
209 $toplevel->fileevent(PLAY_STDERR,'readable' => '');
210 Status("Stopping playback");
211 close (PLAY_STDERR);
213 kill 15, ($playpid1);
214 waitpid $playpid1,0;
215 waitpid $playpid2,0;
217 $window_play[$playstatus]->configure(-text=>"play");
218 Status("Playback stopped");
219 $playstatus=0;
223 sub playtrack{
224 my($tracknum)=@_;
226 if($playstatus>0 && $playstatus==$tracknum){
227 playstop();
228 }else{
229 playstop();
230 $playstatus=$tracknum;
231 $window_play[$tracknum]->configure(-text=>"stop");
233 # creating a process -> process pipe is buggy if we [the Perl
234 # script] are not one of the processes (first one works,
235 # subsequent ones always fail). So, make a named pipe
236 # instead.
238 my$pipe="/tmp/$$.pipe";
239 system('mknod', $pipe, 'p')
240 && system('mkfifo', $pipe)
241 && die "can't mknod $pipe: $!";
243 my$glob;
244 eval "\$glob=\"$playrip\"";
245 my$glob2;
246 eval "\$glob2=\"$play\"";
247 $SIG{PIPE} = 'IGNORE';
249 die "pipe call failed unexpectedly: $!" unless
250 pipe PLAY_STDERR,WRITEH2;
252 $playpid1=open3("STDIN","STDOUT",">&WRITEH2",$glob);
253 $playpid2=open3("STDIN","STDOUT",">&WRITEH2",$glob2);
255 die $! unless close WRITEH2;
256 Status("Playing track $tracknum...");
257 $toplevel->fileevent(PLAY_STDERR,'readable'=>[sub{PlayStderr();}]);
262 sub Cancel{
263 Status("Cancelling");
264 if(defined($rippid) && $rippid>=0){
265 $toplevel->fileevent(RIP_STDERR,'readable' => '');
266 kill 15, ($rippid);
267 close (RIP_STDERR);
268 unlink $wavfile;
269 unlink $outfile;
270 waitpid $rippid,0;
272 $window_go->configure(-command=>[sub{Rip();}],-text=>"go");
273 $window_scan->configure(-command=>[sub{Rescan();}],-text=>"rescan");
274 $ripstatus=0;
275 $rippid=-1;
276 Status("Ready");
279 sub Skip{
280 Status("Skipping");
281 if(defined($rippid) && $rippid>=0){
282 $toplevel->fileevent(RIP_STDERR,'readable' => '');
283 kill 15, ($rippid);
284 close (RIP_STDERR);
285 unlink $wavfile;
286 unlink $outfile;
287 waitpid $rippid,0;
289 $rippid=-1;
290 Rip();
293 sub Rip{
294 $ripstatus++;
296 $window_go->configure(-command=>[sub{Cancel();}],-text=>"cancel");
297 $window_scan->configure(-command=>[sub{Skip();}],-text=>"skip");
298 print STDERR "\n";
300 while($ripstatus<=$tracks){
301 if($trackrip[$ripstatus]){
302 # rip this track; event driven, set up the callback.
304 my$track=$ripstatus;
305 my$title=$trackname[$track];
307 $track=0 if($track==$tracks && $seekrit);
309 $wavfile="/tmp/$$.wav";
310 my$glob;
311 eval "\$glob=\"$rip\"";
312 print "$glob\n";
314 die "pipe call failed unexpectedly: $!" unless
315 pipe RIP_STDERR,WRITEH;
316 $rippid=open3("STDIN",">&STDOUT",">&WRITEH",$glob);
318 close WRITEH;
319 Status("Starting read...");
320 $toplevel->fileevent(RIP_STDERR,'readable'=>[sub{ReadStderr();}]);
322 return;
324 $ripstatus++;
326 # all done!
327 $window_go->configure(-command=>[sub{Rip();}],-text=>"go");
328 $window_scan->configure(-command=>[sub{Rescan();}],-text=>"rescan");
329 Status("Done");
330 $ripstatus=0;
333 sub Track_Path{
334 my($tracknum)=@_;
335 $trackpad=$tracknum;
336 $trackpad=0 if($trackpad==$tracks && $seekrit);
337 $trackpad="0$trackpad" if($trackpad<10);
338 "$trackbase/$artist/$album/$artist - $album - $trackpad - $trackname[$tracknum]$suffix";
341 sub Rescan{
342 Status("Scanning disc");
343 $toplevel->update();
345 $window_go->configure(-state=>"disabled");
347 # get table of contents
348 my$foo;
349 eval "\$foo=\"$query\"";
350 my$output=`$foo`;
351 my$leadout;
352 $tracks=0;
353 $output=~s/[^=]+=+\n(.+)/$1/;
354 my @toc;
355 foreach (split(/\n/,$output)){
356 if(m/^\s*([0-9]+)\.\s+(\d+)\s+\S+\s+(\d+)/){
357 my$f=$3+150;
358 my$s=int($f/75);
359 $f-=$s*75;
360 my$m=int($s/60);
361 $s-=$m*60;
362 $tracks=$1 if ($1>$tracks);
363 $leadout=$2+$3+150;
364 push @toc,"$1 $m $s $f";
365 $initial=$3 if($1==1);
369 if($tracks>0){
370 my$f=$leadout;
371 my$s=int($f/75);
372 $f-=$s*75;
373 my$m=int($s/60);
374 $s-=$m*60;
375 push @toc, "999 $m $s $f";
378 if($tracks==0){
379 $artist="";
380 $album="";
381 AlbumUpdate();
382 Status("Query failed");
383 Alert("Could not query cdrom; drive empty?",$output);
384 }else{
385 for(my$i=1;$i<=99;$i++){
386 $trackrip[$i]=1;
389 Status("Querying database");
390 $toplevel->update();
392 # perform cddb query
393 my $cddb = new CDDB() or die $!;
394 my @discs = $cddb->get_discs_by_toc(@toc);
396 my@temp=@{$discs[0]};
398 if(!defined(@discs)|| $#discs<0 || !defined(@temp[1])){
400 # CDDB protocol is stupidly designed in many ways, but one
401 # of the biggest is that matching depends on the offset of
402 # the first track... which is neither deterministic from
403 # CDROM drive to CDROM drive, nor a well defined concept
404 # in the RedBook spec. Even worse, it appears that much
405 # CDROM software always reports it as MSF 0 2 0 regardless
406 # of what it is, and thus CDDB often has the offset for
407 # the whole disc fucked up.
409 # retry with 1 0 2 0
411 $toc[0]="1 0 2 0";
413 # perform cddb query
414 $cddb->DESTROY();
415 $cddb = new CDDB() or die $!;
416 Status("Requerying database");
417 $toplevel->update();
418 @discs = $cddb->get_discs_by_toc(@toc);
420 @temp=@{$discs[0]};
422 if(!defined(@discs)|| $#discs<0 || !defined(@temp[1])){
425 Status("Album Loaded; no CDDB entry");
426 for(my$i=1;$i<=$tracks;$i++){
427 $trackname[$i]="";
429 $artist="";
430 $album="";
431 $seekrit=0;
432 if($initial>100){
433 $tracks++;
434 $trackname[$tracks]="Hidden Track Zero";
435 $seekrit=1;
438 $cddb->DESTROY();
440 AlbumUpdate();
441 $window_go->configure(-state=>"normal");
442 $window_check->configure(-state=>"normal");
443 return;
447 my ($genre, $cddb_id, $title) = @{$discs[0]};
448 $title=~/(.+)\s\/\s(.+)/;
449 $artist=$1;
450 $album=$2;
451 $artist=~s/\//:/g;
452 $album=~s/\//:/g;
453 my $disc_info = $cddb->get_disc_details($genre, $cddb_id);
455 for(my$i=0;$i<$tracks;$i++){
456 $trackname[$i+1]=$disc_info->{"ttitles"}[$i];
457 $trackname[$i+1]=~s/\//:/g;
460 $seekrit=0;
461 if($initial>100){
462 $tracks++;
463 $trackname[$tracks]="Hidden Track Zero";
464 $seekrit=1;
467 Recheck();
468 Status("Album Loaded");
469 $cddb->DESTROY();
471 AlbumUpdate();
472 $window_go->configure(-state=>"normal");
473 $window_check->configure(-state=>"normal");
477 sub Recheck{
478 for(my$i=1;$i<=$tracks;$i++){
479 $trackrip[$i]=0 if(-e Track_Path($i));
483 sub AlbumUpdate{
484 my $y=$infoy;
486 # re-place widgets up to number of tracks...
487 for(my$i=1;$i<=$tracks;$i++){
488 $window_track_label[$i]->
489 place(-y=>$y,-x=>$x,-anchor=>'nw',-bordermode=>outside);
490 $window_rip[$i]->
491 place(-y=>$y,-x=>-$x,-relx=>1.,-anchor=>'ne',-bordermode=>outside);
492 $window_play[$i]->
493 place(-y=>$y,-x=>-$x-$window_rip[$i]->reqwidth(),-relx=>1.,
494 -anchor=>'ne',-bordermode=>outside);
495 $window_track[$i]->
496 place(-y=>$y,
497 -x=>$x+$window_track_label[$i]->reqwidth(),
498 -relwidth=>1.,
499 -width=> -15
500 -$window_rip[$i]->reqwidth()
501 -$window_track_label[$i]->reqwidth()
502 -$window_play[$i]->reqwidth(),
503 -anchor=>'nw',-bordermode=>outside);
506 $y+=$window_rip[$i]->reqheight();
508 # unplace widgets past number of tracks
509 for(my$i=$tracks+1;$i<=99;$i++){
510 $window_track_label[$i]->placeForget();
511 $window_track[$i]->placeForget();
512 $window_play[$i]->placeForget();
513 $window_rip[$i]->placeForget();
516 my $totaly=int(46+$window_quit->reqheight()+10+$y);
517 my $geometry=$toplevel->geometry();
518 $toplevel->minsize(400,$totaly);
519 $geometry=~/^(\d+)/;
520 $toplevel->configure(-width=>$1);
521 $toplevel->configure(-height=>$totaly);
524 sub Shutdown{
525 Cancel();
526 playstop();
527 Tk::exit(0);
531 sub Status{
532 my$text=shift @_;
533 $window_status->configure(-text=>"$text");
534 #$toplevel->update();
537 sub Alert{
538 my($message,$detail,$window)=@_;
540 $window=$toplevel if(!defined($window));
541 $modal->destroy() if(defined($modal));
543 print STDERR "\n\n$message :: $detail\n";
545 $modal=new MainWindow(-class=>"$Xname");
546 $modal->configure(-background=>$modal->optionGet("background",""));
548 $modal_shell=$modal->Label(-class=>Alert,Name=>"shell",
549 Borderwidth=>1,Relief=>raised)->
550 place(-x=>4,-y=>4,-relwidth=>1.0,-relheight=>1.0,
551 -width=>-8,-height=>-8,-anchor=>'nw');
553 $modal_exit=$modal_shell->
554 Button(-class=>"Exit",-text=>"X")->
555 place(-x=>-1,-y=>-1,-relx=>1.0,-rely=>1.0,-anchor=>'se');
557 $modal_message=$modal_shell->
558 Label(-text=>$message,-class=>"AlertText")->
559 place(-x=>5,-y=>10);
561 my$width=$modal_message->reqwidth();
562 $width=300 if($width<300);
564 $detail="" if(!defined($detail));
565 $modal_detail=$modal_shell->
566 Message(-text=>$detail,-class=>"AlertDetail",
567 -width=>($width-$modal_exit->reqwidth()))->
568 place(-relx=>0,-y=>5,-rely=>1.0,-anchor=>'nw',
569 -in=>$modal_message);
571 $width+=20;
572 my$height=$modal_message->reqheight()+$modal_detail->reqheight()+30;
574 my$xx=$window->rootx();
575 my$yy=$window->rooty();
576 my$ww=$window->width();
577 my$hh=$window->height();
579 my$x=$xx+$ww/2-$width/2;
580 my$y=$yy+$hh/2-$height/2;
582 $modal->geometry($width."x".$height."+".int($x)."+".int($y));
584 $modal->resizable(FALSE,FALSE);
585 $modal->transient($window);
586 $modal_exit->configure(-command=>[sub{$modal->destroy();undef $modal}]);
589 sub ReadStderr{
590 my$saveflag=0;
592 $bytes=sysread RIP_STDERR, my$scalar, 4096;
593 if($bytes==0){
594 $toplevel->fileevent(RIP_STDERR,'readable' => '');
595 Alert("ripper unexpectedly exited!","Skipping track $ripstatus for now...");
596 Skip();
599 $scalar=~s/\r/\n/;
600 push my@lines, split /[\n|\r]/, $saved_stderr.$scalar;
601 if((chomp $scalar)==0){
602 $saved_stderr=$lines[$#lines];
603 }else{
604 $saved_stderr="";
607 foreach my$line (@lines){
609 if($line=~/Done encoding file/){
610 $saved_stderr="";
612 # shut down
613 waitpid $rippid,0;
614 $rippid=-1;
615 $toplevel->fileevent(RIP_STDERR,'readable' => '');
616 close(RIP_STDERR);
618 # move the file
619 my$trackname="$trackbase";
620 mkdir $trackname if(!-e $trackname);
622 $trackname="$trackbase/$artist";
623 mkdir $trackname if(!-e $trackname);
625 $trackname="$trackbase/$artist/$album";
626 mkdir $trackname if(!-e $trackname);
628 my$trackname=Track_Path($ripstatus);
629 print STDERR "moving $outfile to $trackname\n";
630 unlink $wavfile;
632 $trackname=~s/([\"\$\`\\])/\\$1/g;
633 die "could not move file!: $!" if `mv "$outfile" "$trackname"`;
635 # unclick the rip button
636 Recheck();
638 #rip next
639 Rip();
640 last;
643 if($line=~/\[finished\]/){
644 # cdparanoia finished
645 $saved_stderr="";
646 $toplevel->fileevent(RIP_STDERR,'readable' => '');
648 waitpid $rippid,0;
649 $rippid=-1;
650 close(RIP_STDERR);
652 my$metatitle=$trackname[$ripstatus];
653 $metaartist=$artist;
654 $metaalbum=$album;
655 $wavfile="/tmp/$$.wav";
656 $outfile="/tmp/$$.ogg";
658 $metatitle=~s/([\"\$\`\\])/\\$1/g;
659 $metaartist=~s/([\"\$\`\\])/\\$1/g;
660 $metaalbum=~s/([\"\$\`\\])/\\$1/g;
662 my$glob;
663 eval "\$glob=\"$encode\"";
664 $glob=~s/[\{\}]/\"/g;
666 print "$glob\n";
668 die "pipe call failed unexpectedly: $!" unless
669 pipe RIP_STDERR,WRITEH;
670 $rippid=open3("STDIN",">&STDOUT",">&WRITEH",$glob);
672 close WRITEH;
673 Status("Starting encode...");
674 $toplevel->fileevent(RIP_STDERR,'readable'=>[sub{ReadStderr();}]);
675 last;
678 if($line=~/Encoding with/){
679 Status("Encoding track $ripstatus");
680 next;
682 if($line=~/\[\s*(\d*\.\d)\%\]/){
683 Status("Encoding track $ripstatus [$1%]");
684 next;
687 if($line=~/to sector\s*(\d+)/){
688 $endsector=$1;
691 if($line=~/\[wrote\]\s\@\s(\d+)/){
692 $left=$endsector-int($1/1176);
693 Status("Reading track $ripstatus [$left]");
701 sub PlayStderr{
702 my$saveflag=0;
704 $bytes2=sysread PLAY_STDERR, my$scalar, 4096;
706 print $scalar;
708 if($bytes2==0){
709 playstop();
710 return;
713 push my@lines, split /[\n\r]/, $saved_stderr2.$scalar;
714 if((chomp $scalar)==0){
715 $saved_stderr2=$lines[$#lines];
716 }else{
717 $saved_stderr2="";
720 foreach my$line (@lines){
722 if($line=~/\[finished\]/){
723 # cdparanoia finished
724 $saved_stderr2="";
725 playstop();
726 return;