Unit cdrip; {$mode objfpc}{$H+} Interface uses Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ComCtrls, StdCtrls, Buttons, ExtCtrls, cddb, dos, Grids, DBCtrls, process, debug; resourcestring rsEncodeToMp3 = 'Encode to mp3'; rsQuerryCDDB = 'Query CDDB'; rsLoad = 'Load'; rsEject = 'Eject'; rsStart = 'Start'; rsBack = 'Back'; rsSetID3Tag = 'Write ID3-Tags'; rsCrSubfolders = 'Create artist subfolders'; rsOutfileNamin = 'Outfile naming scheme'; Type { Tcdrip } Tcdrip = Class(TForm) bitratebox: TComboBox; FileNameType: TComboBox; LNameScheme: TLabel; paranoia: TCheckBox; startbtn: TButton; backbtn: TButton; querrybtn: TButton; ejectbtn: TButton; loadbtn: TButton; browsedirectorybtn: TButton; encodecheck: TCheckBox; writetagscheck: TCheckBox; artistedit: TEdit; albumedit: TEdit; subfoldercheck: TCheckBox; drivebox: TComboBox; outputfolderbox: TComboBox; Label1: TLabel; LArtist: TLabel; LAlbum: TLabel; SelectDirectoryDialog1: TSelectDirectoryDialog; Trackgrid: TStringGrid; Panel1: TPanel; Timer1: TTimer; Procedure Button1Click(Sender: TObject); Procedure Button2Click(Sender: TObject); Procedure Button4Click(Sender: TObject); Procedure Button5Click(Sender: TObject); procedure browsedirectorybtnClick(Sender: TObject); Procedure CheckBox1Change(Sender: TObject); Procedure FormClose(Sender: TObject; Var CloseAction: TCloseAction); Procedure FormCreate(Sender: TObject); Procedure Timer1Timer(Sender: TObject); Procedure TrackgridHeaderClick(Sender: TObject; IsColumn: Boolean; index: Integer); Procedure TrackgridMouseDown(Sender: TOBject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Procedure TrackgridSelectCell(Sender: TObject; Col, Row: Integer; Var CanSelect: Boolean); Procedure encodecheckChange(Sender: TObject); Procedure startButClick(Sender: TObject); Private { private declarations } Outputstring: TStringlist; Outputstream: TMemoryStream; RipProcess, EncodeProcess: TProcess; Public { public declarations } ToRemove, ToRip, ToEncode: array[1..100] Of boolean; OutFileNames: array[1..100] Of string; RipTrack, EncodeTrack: byte; ripping, encoding: boolean; outfolder: string; CDDBcon: TCddbObject; End; Var cdripwin: Tcdrip; Implementation Uses mainform, mediacol, translations, functions, config; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { Tcdrip } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.Button1Click(Sender: TObject); Begin Button5Click(Nil); If CDDBcon=Nil Then CDDBcon := TCddbObject.create Else Begin Timer1.Enabled := false; CDDBcon.destroy; CDDBcon := TCddbObject.create; End; If CDDBcon.ReadTOC(drivebox.Text) Then Begin CDDBcon.query(drivebox.Text, 'freedb.org', 8880); Timer1.Enabled := true; End; End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.Button2Click(Sender: TObject); Begin close; End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.Button4Click(Sender: TObject); Begin {$ifdef linux} Exec('/usr/bin/eject',CDDBcon.CDromDrives[drivebox.ItemIndex+1]); {$endif linux} {$ifdef win32} Exec('eject.exe',CDDBcon.CDromDrives[drivebox.ItemIndex+1]); {$endif win32} DebugOutLn('ATTENTION!! DIRTY! ejecting cdrom drive... ',0); Trackgrid.Clean([gzNormal, gzFixedCols]); End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.Button5Click(Sender: TObject); Var b, z: byte; tmps, t1, t2: string; ti1, ti2: integer; Begin Trackgrid.Clean([gzNormal, gzFixedCols]); If CDDBcon.ReadTOC(drivebox.Text) Then Begin artistedit.Text := 'Unknown'; albumedit.Text := 'Unknown'; Trackgrid.RowCount := 1; Trackgrid.ColWidths[0] := 20; For b := 1 To CDDBcon.NrTracks Do Begin str(b, tmps); Trackgrid.RowCount := Trackgrid.RowCount+1; Trackgrid.Cells[0,b] := tmps; Trackgrid.Cells[1,b] := 'Track '+tmps; z := b; ti1 := (CDDBcon.TOCEntries[z+1].min)-(CDDBcon.TOCEntries[z].min); ti2 := (CDDBcon.TOCEntries[z+1].sec)-(CDDBcon.TOCEntries[z].sec); If ti2<0 Then dec(ti1); ti2 := abs(ti2); str(ti1, t1); str(ti2, t2); If ti2<10 Then t2 := '0'+t2; Trackgrid.Cells[3,b] := t1+':'+t2; End; End; Timer1.Enabled := true; End; procedure Tcdrip.browsedirectorybtnClick(Sender: TObject); begin if DirectoryExists(outputfolderbox.Text) then SelectDirectoryDialog1.FileName := outputfolderbox.Text; if SelectDirectoryDialog1.Execute Then begin if outputfolderbox.Items.IndexOf(SelectDirectoryDialog1.FileName) < 0 then outputfolderbox.Items.Insert(0, SelectDirectoryDialog1.FileName); outputfolderbox.ItemIndex := outputfolderbox.Items.IndexOf(SelectDirectoryDialog1.FileName); end; end; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.CheckBox1Change(Sender: TObject); Begin End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.FormClose(Sender: TObject; Var CloseAction: TCloseAction); Begin Timer1.Enabled := false; RipProcess.free; EncodeProcess.free; Outputstream.free; Outputstring.free; Timer1.free; CDDBcon.destroy; main.Enabled := true; End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.FormCreate(Sender: TObject); Var b, z: byte; tmps, t1, t2: string; ti1, ti2, i: integer; Begin TranslateUnitResourceStrings('cdrip', cactusconfig.DataPrefix+'languages/cactus.%s.po', CactusConfig.language, copy(CactusConfig.language, 0, 2)); encodecheck.Caption := rsEncodeToMp3; LArtist.Caption := rsArtist; LAlbum.Caption := rsAlbum; querrybtn.Caption := rsQuerryCDDB; loadbtn.Caption := rsLoad; ejectbtn.Caption := rsEject; startbtn.Caption := rsStart; backbtn.Caption := rsBack; writetagscheck.Caption := rsSetID3Tag; subfoldercheck.Caption := rsCrSubfolders; LNameScheme.Caption := rsOutfileNamin; ripping := false; RipProcess := TProcess.Create(Nil); EncodeProcess := TProcess.Create(Nil); Outputstring := TStringList.Create; Outputstream := TMemoryStream.Create; For i:= 0 To MediaCollection.DirList.Count-1 Do // Begin outputfolderbox.AddItem(MediaCollection.DirList[i], Nil); End; outputfolderbox.ItemIndex := 0; CDDBcon := TCddbObject.create; If (CDDBcon.DriveCount>0) Then Begin For b:=1 To CDDBcon.DriveCount Do drivebox.AddItem(CDDBcon.CDromDrives[b], Nil); drivebox.ItemIndex := 0; If CDDBcon.ReadTOC(CDDBcon.CDromDrives[drivebox.ItemIndex+1]) Then Begin artistedit.Text := 'Unknown'; albumedit.Text := 'Unknown'; Trackgrid.Clean([gzNormal, gzFixedCols]); Trackgrid.Cells[1,0] := 'Title'; Trackgrid.Cells[2,0] := 'Rip'; Trackgrid.Cells[3,0] := 'Length'; Trackgrid.RowCount := 1; Trackgrid.ColWidths[0] := 20; For b := 1 To CDDBcon.NrTracks Do Begin str(b, tmps); Trackgrid.RowCount := Trackgrid.RowCount+1; Trackgrid.Cells[0,b] := tmps; Trackgrid.Cells[1,b] := 'Track '+tmps; z := b; ti1 := (CDDBcon.TOCEntries[z+1].min)-(CDDBcon.TOCEntries[z].min); ti2 := (CDDBcon.TOCEntries[z+1].sec)-(CDDBcon.TOCEntries[z].sec); If ti2<0 Then dec(ti1); ti2 := abs(ti2); str(ti1, t1); str(ti2, t2); If ti2<10 Then t2 := '0'+t2; Trackgrid.Cells[3,b] := t1+':'+t2; End; End; Timer1.Enabled := true; End Else Begin ShowMessage('No CDROM-Drive found on this computer'); End; End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ {This timerevent is too confusing, big and buggy. split in 2 timer objects -> one for IP communication and one for rip/encode status } Procedure Tcdrip.Timer1Timer(Sender: TObject); Var b: byte; tmps: string; ti1, ti2: integer; t1,t2, EncPercent: string; i: integer; Begin If (ripping=false) And (encoding=false) Then Begin CDDBcon.callevents; If CDDBcon.data_ready Then Begin artistedit.Text := CDDBcon.artist; albumedit.Text := CDDBcon.album; Trackgrid.Clean([gzNormal, gzFixedCols]); Trackgrid.RowCount := 1; Trackgrid.ColWidths[0] := 20; For b:= 1 To CDDBcon.NrTracks Do Begin str(b, tmps); Trackgrid.RowCount := Trackgrid.RowCount+1; Trackgrid.Cells[0,b] := tmps; Trackgrid.Cells[1,b] := CDDBcon.title[b]; ti1 := (CDDBcon.TOCEntries[b+1].min)-(CDDBcon.TOCEntries[b].min); ti2 := (CDDBcon.TOCEntries[b+1].sec)-(CDDBcon.TOCEntries[b].sec); If ti2<0 Then dec(ti1); ti2 := abs(ti2); str(ti1, t1); str(ti2, t2); If ti2<10 Then t2 := '0'+t2; Trackgrid.Cells[3,b] := t1+':'+t2; End; CDDBcon.data_ready := false; End; End; If ripping And RipProcess.Running Then Begin Outputstream.Clear; Outputstream.SetSize(2048); i := (RipProcess.OutPut.Read(Outputstream.Memory^, 2048)); Outputstream.SetSize(i); If i>5 Then Begin Outputstring.clear; Outputstring.LoadFromStream(Outputstream); for i:=0 to Outputstring.Count-1 do begin tmps := copy(Outputstring.Strings[i], pos('%', Outputstring.Strings[i])-3, 3); if tmps<>'' then EncPercent:=tmps; end; Trackgrid.Cells[2,RipTrack] := EncPercent+'%'; End; End; If (ripping) And (RipProcess.Running=false) Then Begin If ToEncode[RipTrack] Then Begin encoding := true; EncodeTrack := RipTrack; str(EncodeTrack, tmps); If EncodeTrack<10 Then tmps := '0'+tmps; If FileNameType.ItemIndex=0 Then OutFileNames[EncodeTrack] := tmps+' - '+ artistedit.Text+' - '+ Trackgrid.Cells[1, EncodeTrack]+'.mp3'; If FileNameType.ItemIndex=1 Then OutFileNames[EncodeTrack] := artistedit. Text+' - '+Trackgrid.Cells[1 , EncodeTrack]+'.mp3'; If FileNameType.ItemIndex=2 Then OutFileNames[EncodeTrack] := artistedit. Text+' - '+inttostr( EncodeTrack)+' - '+Trackgrid .Cells[1, EncodeTrack]+ '.mp3'; OutFileNames[EncodeTrack] := StringReplace(OutFileNames[EncodeTrack], '/', '_', [rfReplaceAll]); OutFileNames[EncodeTrack] := StringReplace(OutFileNames[EncodeTrack], '\', '_', [rfReplaceAll]); OutFileNames[EncodeTrack] := IncludeTrailingPathDelimiter(outfolder)+OutFileNames[EncodeTrack]; OutFileNames[EncodeTrack] := StringReplace(OutFileNames[EncodeTrack], #39, '', [ rfReplaceAll]); DebugOutLn(OutFileNames[EncodeTrack],0); EncodeProcess.CommandLine := '/usr/bin/lame -h -b'+bitratebox.Items[bitratebox.ItemIndex]+ ' --tt "'+UTF8toLatin1(Trackgrid.Cells[1, EncodeTrack])+ '" --ta "'+UTF8toLatin1(artistedit.Text)+'" --tl "'+ UTF8toLatin1(albumedit.Text)+'" --tn '+tmps+' "'+outfolder+ '/Track'+tmps+'.wav"'+' "'+OutFileNames[EncodeTrack]+'"'; DebugOutLn(EncodeProcess.CommandLine,0); Caption := 'Encoding Track '+inttostr(EncodeTrack)+' ...'; EncodeProcess.Options := [poUsePipes, poStderrToOutPut]; EncodeProcess.Execute; encoding := true; ripping := false; Timer1.Enabled := true; End Else Begin encoding := false; i := 0; str(Riptrack, tmps); If RipTrack<10 Then tmps := '0'+tmps; If ToRemove[RipTrack] Then Begin DeleteFile(outfolder+'/Track'+tmps+'.wav'); DeleteFile(outfolder+'/Track'+tmps+'.inf'); DebugOutLn('delete '+outfolder+'/Track'+tmps+'.wav',0); End; Repeat inc(i) Until (ToRip[i]=true) Or (i>CDDBcon.NrTracks); If i<=CDDBcon.NrTracks Then Begin Trackgrid.Cells[2,i] := '0%'; ToRip[i] := false; ToRemove[RipTrack] := false; Trackgrid.Cells[2,RipTrack] := '100%'; RipTrack := i; str(i, tmps); If i<10 Then tmps := '0'+tmps; If paranoia.Checked Then RipProcess.CommandLine := '/usr/bin/cdda2wav -paranoia -D'+CDDBcon.Device+' -t '+ tmps+' '''+outfolder+'/Track'+tmps+'.wav''' Else RipProcess.CommandLine := '/usr/bin/cdda2wav -D'+CDDBcon.Device+' -t '+tmps+' '''+ outfolder+'/Track'+tmps+'.wav'''; RipProcess.Options := [poUsePipes,poStderrToOutPut, poDefaultErrorMode]; Caption := 'Ripping Track '+tmps+' ...'; DebugOutLn('Ripping Track '+tmps,0); RipProcess.Execute; Timer1.Enabled := true; End Else Begin DebugOutLn('Finished all tracks', 0); Trackgrid.Cells[2,RipTrack] := '100%'; Caption := 'CD Rip... < Finished >'; Trackgrid.Options:= Trackgrid.Options + [goEditing]; Timer1.Enabled := false; main.update_artist_view; update_title_view; ripping := false; encoding := false; ShowMessage('Ripping and encoding finished'); MediaCollection.SaveToFile; End; End; End; If encoding And EncodeProcess.Running And ToEncode[RipTrack] Then Begin Outputstream.Clear; Outputstream.SetSize(1024); i := (EncodeProcess.OutPut.Read(Outputstream.Memory^, 1024)); Outputstream.SetSize(i); If i>0 Then Begin Outputstring.clear; Outputstring.LoadFromStream(Outputstream); for i:=0 to Outputstring.Count-1 do begin tmps := copy(Outputstring.Strings[i], pos('%', Outputstring.Strings[i])-2, 2); if tmps<>'' then EncPercent:=tmps; end; DebugOutLn(EncPercent,0); Trackgrid.Cells[2,EncodeTrack] := EncPercent+'%'; Application.ProcessMessages; End; End; If encoding And (EncodeProcess.Running=false) {and (ToEncode[RipTrack]=false)} Then Begin ripping := true; encoding := false; ToEncode[RipTrack] := false; DebugOutLn('adding file',0); MediaCollection.add(OutFileNames[EncodeTrack]); End; End; Procedure Tcdrip.TrackgridHeaderClick(Sender: TObject; IsColumn: Boolean; index: Integer); Var row: integer; Begin If index = 2 Then Begin For row:=1 To Trackgrid.RowCount-1 Do If Trackgrid.Cells[2, row]='' Then Trackgrid.Cells[2, row] := 'x' Else Trackgrid.Cells[2, row] := ''; End; End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.TrackgridMouseDown(Sender: TOBject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Begin End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.TrackgridSelectCell(Sender: TObject; Col, Row: Integer; Var CanSelect: Boolean); Begin If col=2 Then If Trackgrid.Cells[2, row]='' Then Trackgrid.Cells[2, row] := 'x' Else Trackgrid.Cells[2, row] := ''; End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.encodecheckChange(Sender: TObject); Begin If EncodeCheck.Checked Then Begin writetagscheck.Enabled := true; subfoldercheck.Enabled := true; bitratebox.enabled := true; End Else Begin writetagscheck.Enabled := false; subfoldercheck.Enabled := false; bitratebox.enabled := false; End; End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Procedure Tcdrip.startButClick(Sender: TObject); Var row, i: integer; tmps : string; Begin outfolder := outputfolderbox.Items[outputfolderbox.ItemIndex]; If subfoldercheck.Checked Then outfolder := outfolder+DirectorySeparator+artistedit.Text; If DirectoryExists(outfolder)=false Then mkdir(outfolder); For i:= 1 To CDDBcon.NrTracks Do Begin If Trackgrid.Cells[2,i]='x' Then ToRip[i] := true; End; i := 0; If encodecheck.Checked Then begin ToEncode := ToRip; ToRemove := ToRip; end; Repeat inc(i) Until (ToRip[i]=true) Or (i>CDDBcon.NrTracks); If i<=CDDBcon.NrTracks Then Begin If FileExists('/usr/bin/cdda2wav') Then Begin {NOT PORTABLE!!!} Trackgrid.Options:= Trackgrid.Options - [goEditing]; // Trackgrid.Enabled := false; Trackgrid.Cells[2,i] := '0%'; ToRip[i] := false; RipTrack := i; str(i, tmps); If i<10 Then tmps := '0'+tmps; If paranoia.Checked Then RipProcess.CommandLine := '/usr/bin/cdda2wav -paranoia -D'+CDDBcon.Device+' -t '+tmps+ ' "'+outfolder+'/Track'+tmps+'.wav"' Else RipProcess.CommandLine := '/usr/bin/cdda2wav -D'+CDDBcon.Device+' -t '+tmps+' "'+ outfolder+'/Track'+tmps+'.wav"'; RipProcess.Options := [poUsePipes, poStderrToOutPut, poDefaultErrorMode]; DebugOutLn('Ripping Track '+tmps, 0); Caption := 'Ripping Track '+tmps+' ...'; RipProcess.Execute; Timer1.Enabled := true; ripping := true; End Else ShowMessage( 'ERROR: cdda2wav executable not found. Please install cdda2wav package first...' ); End Else If MessageDlg('No tracks selected. Rip complete disc?', mtWarning, mbOKCancel, 0)=mrOK Then Begin For row:=1 To Trackgrid.RowCount-1 Do If Trackgrid.Cells[2, row]='' Then Trackgrid.Cells[2, row] := 'x' Else Trackgrid.Cells[2, row] := ''; startButClick(Nil); End; End; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ initialization {$I cdrip.lrs} End.