/**
* This file is part of the KAudioCreator package
* Copyright ( C ) 2003 Benjamin C Meyer ( ben + kaudiocreator at meyerhome dot net )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public License
* along with this library ; see the file COPYING . LIB . If not , write to
* the Free Software Foundation , Inc . , 51 Franklin Street , Fifth Floor ,
* Boston , MA 02110 - 1301 , USA .
*/
# include "encoder.h"
# include "prefs.h"
# include "encoder_prefs.h"
# include "encoderoutput.h"
# include <tqregexp.h>
# include <tqdir.h>
# include <kstandarddirs.h>
# include <kmessagebox.h>
# include <kurl.h>
# include <kdebug.h>
# include <knotifyclient.h>
# include <tqtextedit.h>
# include <kinputdialog.h>
/**
* Constructor , load settings .
*/
Encoder : : Encoder ( TQObject * parent , const char * name ) : TQObject ( parent , name ) , reportCount ( 0 ) {
loadSettings ( ) ;
}
/**
* Load the settings for this class .
*/
void Encoder : : loadSettings ( ) {
loadEncoder ( Prefs : : currentEncoder ( ) ) ;
// If the cpu count change then try
for ( uint i = 0 ; i < ( uint ) Prefs : : numberOfCpus ( ) ; i + + )
tendToNewJobs ( ) ;
}
EncoderPrefs * Encoder : : loadEncoder ( int encoder ) {
EncoderPrefs * prefs ;
TQString currentEncoderGroup = TQString ( " Encoder_%1 " ) . arg ( encoder ) ;
prefs = EncoderPrefs : : prefs ( currentEncoderGroup ) ;
if ( ! EncoderPrefs : : hasPrefs ( currentEncoderGroup ) ) {
KMessageBox : : sorry ( 0 , i18n ( " No encoder has been selected. \n Please select an encoder in the configuration. " ) , i18n ( " No Encoder Selected " ) ) ;
prefs - > setCommandLine ( TQString : : null ) ;
}
return prefs ;
}
/**
* Deconstructor , remove pending jobs , remove current jobs .
*/
Encoder : : ~ Encoder ( ) {
pendingJobs . clear ( ) ;
TQMap < KShellProcess * , Job * > : : Iterator pit ;
for ( pit = jobs . begin ( ) ; pit ! = jobs . end ( ) ; + + pit ) {
Job * job = jobs [ pit . key ( ) ] ;
KShellProcess * process = pit . key ( ) ;
threads . remove ( process ) ;
process - > kill ( ) ;
TQFile : : remove ( job - > newLocation ) ;
delete job ;
delete process ;
}
jobs . clear ( ) ;
}
/**
* @ return The number of active jobs
*/
int Encoder : : activeJobCount ( ) {
return jobs . count ( ) ;
}
/**
* @ return The number of pending jobs
*/
int Encoder : : pendingJobCount ( ) {
return pendingJobs . count ( ) ;
}
/**
* Stop this job with the matching id .
* @ param id the id number of the job to stop .
*/
void Encoder : : removeJob ( int id ) {
TQMap < KShellProcess * , Job * > : : Iterator it ;
for ( it = jobs . begin ( ) ; it ! = jobs . end ( ) ; + + it ) {
if ( it . data ( ) - > id = = id ) {
KShellProcess * process = it . key ( ) ;
Job * job = jobs [ it . key ( ) ] ;
threads . remove ( process ) ;
process - > kill ( ) ;
jobs . remove ( process ) ;
delete job ;
delete process ;
break ;
}
}
Job * job = pendingJobs . first ( ) ;
while ( job ) {
if ( job - > id = = id )
break ;
job = pendingJobs . next ( ) ;
}
if ( job ) {
pendingJobs . remove ( job ) ;
delete job ;
}
tendToNewJobs ( ) ;
}
/**
* Adds job to the que of jobs to encode .
* @ param job the job to encode .
*/
void Encoder : : encodeWav ( Job * job ) {
emit ( addJob ( job , i18n ( " Encoding (%1): %2 - %3 " ) . arg ( loadEncoder ( job - > encoder ) - > extension ( ) )
. arg ( job - > track_artist ) . arg ( job - > track_title ) ) ) ;
pendingJobs . append ( job ) ;
tendToNewJobs ( ) ;
}
/**
* See if there are are new jobs to attend too . If we are all loaded up
* then just loop back in a few seconds and check agian .
*/
void Encoder : : tendToNewJobs ( ) {
if ( pendingJobs . count ( ) = = 0 ) {
emit jobsChanged ( ) ;
return ;
}
// If we are currently ripping the max try again in a little bit.
if ( ( int ) threads . count ( ) > = Prefs : : numberOfCpus ( ) ) {
emit jobsChanged ( ) ;
return ;
}
Job * job = pendingJobs . first ( ) ;
pendingJobs . remove ( job ) ;
EncoderPrefs * prefs = loadEncoder ( job - > encoder ) ;
TQString desiredFile = Prefs : : fileFormat ( ) ;
desiredFile . replace ( TQRegExp ( " ~ " ) , TQDir : : homeDirPath ( ) ) ;
{
TQMap < TQString , TQString > map ;
map . insert ( " extension " , prefs - > extension ( ) ) ;
Job jobx = * job ;
jobx . fix ( Prefs : : replaceInput ( ) , Prefs : : replaceOutput ( ) ) ;
jobx . fix ( " / " , " %2f " ) ;
// If the user wants anything regexp replaced do it now...
desiredFile = jobx . replaceSpecialChars ( desiredFile , false , map ) ;
}
while ( TQFile : : exists ( desiredFile ) ) {
bool ok ;
TQString text = KInputDialog : : getText (
i18n ( " File Already Exists " ) , i18n ( " Sorry, file already exists. Please pick a new name: " ) ,
desiredFile , & ok ) ;
if ( ok & & ! text . isEmpty ( ) )
desiredFile = text ;
else {
emit jobsChanged ( ) ;
updateProgress ( job - > id , - 1 ) ;
return ;
}
}
int lastSlash = desiredFile . findRev ( ' / ' , - 1 ) ;
if ( lastSlash = = - 1 | |
! ( KStandardDirs : : makeDir ( desiredFile . mid ( 0 , lastSlash ) , 0775 ) ) ) {
KMessageBox : : sorry ( 0 , i18n ( " Cannot place file, unable to make directories. " ) , i18n ( " Encoding Failed " ) ) ;
emit jobsChanged ( ) ;
updateProgress ( job - > id , - 1 ) ;
return ;
}
job - > newLocation = desiredFile ;
reportCount = 0 ;
TQString command = prefs - > commandLine ( ) ; {
TQMap < TQString , TQString > map ;
map . insert ( " extension " , prefs - > extension ( ) ) ;
map . insert ( " f " , job - > location ) ;
map . insert ( " o " , desiredFile ) ;
command = job - > replaceSpecialChars ( command , true , map ) ;
}
updateProgress ( job - > id , 1 ) ;
job - > errorString = command ;
KShellProcess * proc = new KShellProcess ( ) ;
proc - > setPriority ( Prefs : : niceLevel ( ) ) ;
* proc < < TQFile : : encodeName ( command ) ;
connect ( proc , TQT_SIGNAL ( receivedStdout ( KProcess * , char * , int ) ) ,
this , TQT_SLOT ( receivedThreadOutput ( KProcess * , char * , int ) ) ) ;
connect ( proc , TQT_SIGNAL ( receivedStderr ( KProcess * , char * , int ) ) ,
this , TQT_SLOT ( receivedThreadOutput ( KProcess * , char * , int ) ) ) ;
connect ( proc , TQT_SIGNAL ( processExited ( KProcess * ) ) , this , TQT_SLOT ( jobDone ( KProcess * ) ) ) ;
jobs . insert ( proc , job ) ;
threads . append ( proc ) ;
proc - > start ( KShellProcess : : NotifyOnExit , KShellProcess : : AllOutput ) ;
emit jobsChanged ( ) ;
}
/**
* We have received some output from a thread . See if it contains % .
* @ param proc the process that has new output .
* @ param buffer the output from the process
* @ param buflen the length of the buffer .
*/
void Encoder : : receivedThreadOutput ( KProcess * process , char * buffer , int length ) {
if ( Prefs : : fullDecoderDebug ( ) & & buffer )
kdDebug ( 60002 ) < < buffer < < endl ;
// Make sure we have a job to send an update too.
if ( jobs . find ( ( KShellProcess * ) process ) = = jobs . end ( ) ) {
kdDebug ( 60002 ) < < " Encoder::receivedThreadOutput Job doesn't exists. Line: " < < __LINE__ < < endl ;
return ;
}
Job * job = jobs [ ( KShellProcess * ) process ] ;
// Keep the output in the event it fails.
job - > output + = TQString ( buffer ) . mid ( 0 , length ) ;
// Make sure the output string has a % symble in it.
TQString output = TQString ( buffer ) . mid ( 0 , length ) ;
if ( output . find ( ' % ' ) = = - 1 & & reportCount < 5 ) {
kdDebug ( 60002 ) < < " No \' %% \' in output. Report as bug w/encoder options if progressbar doesn't fill. " < < endl ;
reportCount + + ;
return ;
}
//qDebug(TQString("Pre cropped: %1").arg(output).latin1());
output = output . mid ( output . find ( ' % ' ) - loadEncoder ( job - > encoder ) - > percentLength ( ) , 2 ) ;
//qDebug(TQString("Post cropped: %1").arg(output).latin1());
bool conversionSuccessfull = false ;
int percent = output . toInt ( & conversionSuccessfull ) ;
//qDebug(TQString("number: %1").arg(percent).latin1());
if ( percent > = 0 & & percent < 100 & & conversionSuccessfull ) {
emit ( updateProgress ( job - > id , percent ) ) ;
}
// If it was just some random output that couldn't be converted then don't report the error.
else
if ( conversionSuccessfull )
kdWarning ( " Percent done: \" %d \" is not >= 0 && < 100. " , percent ) ;
}
/**
* When the process is done encoding a file this function is called .
* @ param job the job that just finished .
*/
void Encoder : : jobDone ( KProcess * process ) {
// Normal error checking here.
if ( ! process )
return ;
//qDebug("Process exited with status: %d", process->exitStatus());
Job * job = jobs [ ( KShellProcess * ) process ] ;
threads . remove ( ( KShellProcess * ) process ) ;
jobs . remove ( ( KShellProcess * ) process ) ;
bool showDebugBox = false ;
if ( process - > exitStatus ( ) = = 127 ) {
KMessageBox : : sorry ( 0 , i18n ( " The selected encoder was not found. \n The wav file has been removed. Command was: %1 " ) . arg ( job - > errorString ) , i18n ( " Encoding Failed " ) ) ;
emit ( updateProgress ( job - > id , - 1 ) ) ;
}
else if ( TQFile : : exists ( job - > newLocation ) ) {
// fyi segfaults return 136
if ( process - > exitStatus ( ) ! = 0 ) {
if ( KMessageBox : : questionYesNo ( 0 , i18n ( " The encoder exited with a error. Please check that the file was created. \n Do you want to see the full encoder output? " ) , i18n ( " Encoding Failed " ) , i18n ( " Show Output " ) , i18n ( " Skip Output " ) ) = = KMessageBox : : Yes )
{
showDebugBox = true ;
}
}
else {
//qDebug("Must be done: %d", (process->exitStatus()));
emit ( updateProgress ( job - > id , 100 ) ) ;
KNotifyClient : : event ( " track encoded " ) ;
if ( job - > lastSongInAlbum )
KNotifyClient : : event ( " cd encoded " ) ;
}
}
else
{
if ( KMessageBox : : questionYesNo ( 0 , i18n ( " The encoded file was not created. \n Please check the encoder options. \n The wav file has been removed. \n Do you want to see the full encoder output? " ) , i18n ( " Encoding Failed " ) , i18n ( " Show Output " ) , i18n ( " Skip Output " ) ) = = KMessageBox : : Yes )
{
showDebugBox = true ;
}
emit ( updateProgress ( job - > id , - 1 ) ) ;
}
if ( job - > removeTempFile )
TQFile : : remove ( job - > location ) ;
if ( showDebugBox ) {
EncoderOutput dlg ( 0 , " Encoder Output " ) ;
job - > output = job - > errorString + " \n \n \n " + job - > output ;
dlg . output - > setText ( job - > output ) ;
dlg . exec ( ) ;
}
delete job ;
delete process ;
tendToNewJobs ( ) ;
}
# include "encoder.moc"