diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:09:31 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:09:31 +0000 |
commit | f2cfda2a54780868dfe0af7bd652fcd4906547da (patch) | |
tree | c6ac23545528f5701818424f2af5f79ce3665e6c /src/convert.cpp | |
download | soundkonverter-f2cfda2a54780868dfe0af7bd652fcd4906547da.tar.gz soundkonverter-f2cfda2a54780868dfe0af7bd652fcd4906547da.zip |
Added KDE3 version of SoundKonverter
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/soundkonverter@1097614 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/convert.cpp')
-rwxr-xr-x | src/convert.cpp | 1615 |
1 files changed, 1615 insertions, 0 deletions
diff --git a/src/convert.cpp b/src/convert.cpp new file mode 100755 index 0000000..ce435ab --- /dev/null +++ b/src/convert.cpp @@ -0,0 +1,1615 @@ + +#include "convert.h" +//#include "conversionoptions.h" +#include "convertpluginloader.h" +#include "replaygainpluginloader.h" +#include "replaygain.h" +#include "ripperpluginloader.h" +#include "config.h" +#include "tagengine.h" +#include "cdmanager.h" +#include "logger.h" +#include "filelist.h" +#include "replaygainscanner.h" + +#include <math.h> + +#include <klocale.h> +#include <kglobal.h> +//#include <kdebug.h> +#include <ktempfile.h> +#include <kio/job.h> +//#include <kprocess.h> +#include <kstandarddirs.h> + +#include <qfile.h> +#include <qtimer.h> + +ConvertItem::ConvertItem() +{ + // create a new item with the file list item pointer set to zero + ConvertItem( (FileListItem*)0 ); +} + +ConvertItem::ConvertItem( FileListItem* item ) +{ + fileListItem = item; + getTime = getCorrectionTime = ripTime = decodeTime = encodeTime = replaygainTime = 0; +} + +ConvertItem::~ConvertItem() +{} + + +Convert::Convert( Config* _config, TagEngine* _tagEngine, CDManager* _cdManager, FileList* _fileList, Logger* _logger ) +{ + config = _config; + tagEngine = _tagEngine; + cdManager = _cdManager; + fileList = _fileList; + connect( fileList, SIGNAL(convertItem(FileListItem*)), + this, SLOT(add(FileListItem*)) + ); + connect( fileList, SIGNAL(stopItem(FileListItem*)), + this, SLOT(stop(FileListItem*)) + ); + connect( this, SIGNAL(finished(FileListItem*,int)), + fileList, SLOT(itemFinished(FileListItem*,int)) + ); + connect( this, SIGNAL(rippingFinished(const QString&)), + fileList, SLOT(rippingFinished(const QString&)) + ); + logger = _logger; + connect( this, SIGNAL(finishedProcess(int,int)), + logger, SLOT(processCompleted(int,int)) + ); + + tUpdateProgressIndicator = new QTimer( this, "tUpdateProgressIndicator" ); + connect( tUpdateProgressIndicator, SIGNAL(timeout()), + this, SLOT(updateProgressIndicator()) + ); +} + +Convert::~Convert() +{} + +void Convert::cleanUp() +{ + // TODO clean up +} + +void Convert::get( ConvertItem* item ) +{ + logger->log( item->logID, i18n("Getting file") ); + item->state = ConvertItem::get; + + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Getting file")+"... 00 %" ); + + KURL source( item->fileListItem->options.filePathName.replace("?","%3f") ); + KURL destination( item->tempInFile->name() ); + + if( source.isLocalFile() && destination.isLocalFile() ) { + item->convertProcess->clearArguments(); + + *(item->convertProcess) << "cp"; + *(item->convertProcess) << source.path(); + *(item->convertProcess) << destination.path(); + + logger->log( item->logID, "cp \"" + source.path() + "\" \"" + destination.path() + "\"" ); + + item->convertProcess->setPriority( config->data.general.priority ); + item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ); + } + else { + item->moveJob = new KIO::FileCopyJob( source, destination, -1, false, true, false, false ); + connect( item->moveJob, SIGNAL(percent(KIO::Job*,unsigned long)), + this, SLOT(moveProgress(KIO::Job*,unsigned long)) + ); + connect( item->moveJob, SIGNAL(result(KIO::Job*)), + this, SLOT(moveFinished(KIO::Job*)) + ); + } +} + +void Convert::getCorrection( ConvertItem* item ) +{ + logger->log( item->logID, i18n("Getting correction file") ); + item->state = ConvertItem::get_correction; + + // calculate the name of the correction input file + QFile file( OutputDirectory::changeExtension(item->fileListItem->options.filePathName,item->correctionInputExtension) ); + if( !file.exists() ) { + logger->log( item->logID, " " + i18n("Aborting, file does not exist") + " (" + file.name() + ")" ); + executeNextStep( item ); + return; + } + + KURL source( file.name() ); + KURL destination( item->correctionInFile ); + + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Getting correction file")+"... 00 %" ); + + if( source.isLocalFile() && destination.isLocalFile() ) { + item->convertProcess->clearArguments(); + + *(item->convertProcess) << "cp"; + *(item->convertProcess) << source.path(); + *(item->convertProcess) << destination.path(); + + logger->log( item->logID, "cp \"" + source.path() + "\" \"" + destination.path() + "\"" ); + + item->convertProcess->setPriority( config->data.general.priority ); + item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ); + } + else { + item->moveJob = new KIO::FileCopyJob( source, destination, -1, false, true, false, false ); + connect( item->moveJob, SIGNAL(percent(KIO::Job*,unsigned long)), + this, SLOT(moveProgress(KIO::Job*,unsigned long)) + ); + connect( item->moveJob, SIGNAL(result(KIO::Job*)), + this, SLOT(moveFinished(KIO::Job*)) + ); + } +} + +void Convert::rip( ConvertItem* item ) +{ + logger->log( item->logID, i18n("Ripping") ); + item->state = ConvertItem::rip; + +/** kaudiocreator + QString wavFile; + QString args = job->device; + if(!args.isEmpty()) + args = QString("?device=%1").arg(args); + args = args+"&fileNameTemplate=Track %{number}"; + if(job->track < 10) + wavFile = QString("audiocd:/Wav/Track 0%1.wav%2").arg(job->track).arg(args); + else + wavFile = QString("audiocd:/Wav/Track %1.wav%2").arg(job->track).arg(args); +*/ + + RipperPlugin* plugin = config->getCurrentRipper(); + + if( plugin == 0 ) { + // NOTE process devices like '/dev/cdrom' - seems to be done + // TODO implement process priority (nice level) + QString src; + if( item->fileListItem->track != 0 ) { + // TODO does it work with cds with less than 10 tracks? + src.sprintf( "audiocd:/Wav/Track %02i.wav?device=" + item->fileListItem->device + "&fileNameTemplate=Track %%{number}", item->fileListItem->track ); + } + else { + // FIXME implement ripping of full cds + src = "audiocd:/Full CD/Full CD.wav?device=" + item->fileListItem->device + "&albumTemplate=Full CD"; + item->tracks = 1; + } + KURL source( src ); + KURL dest( item->tempWavFile->name() ); + + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Ripping")+"... 00 %" ); + item->fileListItem->ripping = true; + + item->moveJob = new KIO::FileCopyJob( source, dest, -1, false, true, false, false ); + connect( item->moveJob, SIGNAL(percent(KIO::Job*,unsigned long)), + this, SLOT(moveProgress(KIO::Job*,unsigned long)) + ); + connect( item->moveJob, SIGNAL(result(KIO::Job*)), + this, SLOT(moveFinished(KIO::Job*)) + ); + } + else { + QStringList params; + QString param, paramSplinter; + + + item->convertProcess->clearArguments(); + + param = QString::null; + if( plugin->rip.param ) param.append( " " + plugin->rip.param ); + if( plugin->rip.device ) param.append( " " + plugin->rip.device ); + if( plugin->rip.overwrite ) param.append( " " + plugin->rip.overwrite ); + + if( item->fileListItem->track != 0 ) { + if( plugin->rip.track ) param.append( " " + plugin->rip.track ); + } + else { + if( plugin->rip.full_disc.param ) param.append( " " + plugin->rip.full_disc.param ); + item->tracks = cdManager->getTrackCount( item->fileListItem->device ); + item->track = 0; + } + +// if( plugin->rip.out_file.find("%p") != -1 ) { +// QString t_str = plugin->rip.out_file; +// t_str.replace( "%p", param ); +// param = plugin->rip.bin + " " + t_str; +// } +// else { +// param = plugin->rip.bin + param + " " + plugin->rip.out_file; +// } + + QString t_str = plugin->rip.out_file; + t_str.replace( "%p", param ); + param = config->binaries[plugin->rip.bin] + " " + t_str; + + param.simplifyWhiteSpace(); + + params = QStringList::split( ' ', param ); + + for( QStringList::Iterator it = params.begin(); it != params.end(); ++it ) + { + paramSplinter = *it; + paramSplinter.replace( "%d", item->fileListItem->device ); + paramSplinter.replace( "%t", QString().sprintf("%i",item->fileListItem->track) ); + paramSplinter.replace( "%n", QString().sprintf("%i",cdManager->getTrackCount(item->fileListItem->device)) ); + paramSplinter.replace( "%o", item->tempWavFile->name() ); + *(item->convertProcess) << paramSplinter; + } + + param.replace( "%d", item->fileListItem->device ); + param.replace( "%t", QString().sprintf("%i",item->fileListItem->track) ); + param.replace( "%n", QString().sprintf("%i",cdManager->getTrackCount(item->fileListItem->device)) ); + param.replace( "%o", "\""+item->tempWavFile->name()+"\"" ); + logger->log( item->logID, param ); + + //kdDebug() << " Executing: `" << param << "'" << endl; + + //item->readOutputTimer.start(); + item->lastOutputTimer.start(); + + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Ripping")+"... 00 %" ); + item->fileListItem->ripping = true; + + item->convertProcess->setPriority( config->data.general.priority ); + item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ); + } +} + +void Convert::decode( ConvertItem* item ) +{ + logger->log( item->logID, i18n("Decoding") ); + item->state = ConvertItem::decode; + + QStringList params; + QString param, paramSplinter; + + item->convertProcess->clearArguments(); + + ConvertPlugin* plugin = config->decoderForFormat( item->fileListItem->mimeType ); + if( plugin == 0 ) { + logger->log( item->logID, " NULL POINTER: Convert::decode( ... ) / plugin" ); + return; + } + + param = ""; + if( !plugin->dec.param.isEmpty() ) param.append( " " + plugin->dec.param ); + if( !plugin->dec.overwrite.isEmpty() ) param.append( " " + plugin->dec.overwrite ); + + QString t_str = plugin->dec.in_out_files; + t_str.replace( "%p", param ); + param = config->binaries[plugin->dec.bin] + " " + t_str; + + param = param.simplifyWhiteSpace(); + + params = QStringList::split( ' ', param ); + + for( QStringList::Iterator it = params.begin(); it != params.end(); ++it ) + { + paramSplinter = *it; + paramSplinter.replace( "%i", item->tempInFile->name() ); + paramSplinter.replace( "%o", item->tempWavFile->name() ); + *(item->convertProcess) << paramSplinter; + } + + param.replace( "%i", "\""+item->tempInFile->name()+"\"" ); + param.replace( "%o", "\""+item->tempWavFile->name()+"\"" ); + //item->log = param; + logger->log( item->logID, param ); + + //kdDebug() << " Executing: `" << param << "'" << endl; + + //item->readOutputTimer.start(); + item->lastOutputTimer.start(); + + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Decoding")+"... 00 %" ); + + item->convertProcess->setPriority( config->data.general.priority ); + item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ); +} + +void Convert::encode( ConvertItem* item ) +{ + // TODO test quality profiles (never done) + + QString sStrength; + QString sBitrate; + QString sQuality; + QString sMinBitrate; + QString sMaxBitrate; + QString sSamplingRate; + + int t_int; + float t_float; + + logger->log( item->logID, i18n("Encoding") ); + item->state = ConvertItem::encode; + + QStringList params; + QString param, paramSplinter; + + item->convertProcess->clearArguments(); + + // NOTE use mimetype + FormatItem* formatItem = config->getFormatItem( item->fileListItem->options.encodingOptions.sFormat ); + if( formatItem == 0 ) { + //kdDebug() << "NULL POINTER: `" << "Convert::encode( ... ) / formatItem" << "'" << endl; + logger->log( 1000, "NULL POINTER: `Convert::encode( ... ) / formatItem'" ); + return; + } + ConvertPlugin* plugin = formatItem->encoder; + if( plugin == 0 ) { + //kdDebug() << "NULL POINTER: `" << "Convert::encode( ... ) / plugin" << "'" << endl; + logger->log( 1000, "NULL POINTER: `Convert::encode( ... ) / plugin'" ); + return; + } + +// item->binary = plugin->enc.bin; + + param = ""; + if( !plugin->enc.param.isEmpty() ) param.append( " " + plugin->enc.param ); + if( !plugin->enc.overwrite.isEmpty() ) param.append( " " + plugin->enc.overwrite ); + + if( plugin->enc.strength.enabled ) { + param.append( " " + plugin->enc.strength.param ); + int compressionLevel = formatItem->compressionLevel; + + if( plugin->enc.strength.profiles.empty() ) { + if( plugin->enc.strength.step < 1 ) { + if( plugin->enc.strength.range_max >= plugin->enc.strength.range_min ) + sStrength = QString::number( compressionLevel * plugin->enc.strength.step ); + else + sStrength = QString::number( plugin->enc.strength.range_min - compressionLevel * plugin->enc.strength.step ); + } + else { + if( plugin->enc.strength.range_max >= plugin->enc.strength.range_min ) + sStrength = QString::number( int(compressionLevel * plugin->enc.strength.step) ); + else + sStrength = QString::number( int(plugin->enc.strength.range_min - compressionLevel * plugin->enc.strength.step) ); + } + if( plugin->enc.strength.separator != '.' ) sStrength.replace( QChar('.'), plugin->enc.strength.separator ); + } + else { + QStringList::Iterator it = plugin->enc.strength.profiles.at( compressionLevel ); + sStrength = *it; + } + } + + if( item->fileListItem->options.encodingOptions.sQualityMode == i18n("Bitrate") ) { + if( item->fileListItem->options.encodingOptions.sBitrateMode == "cbr" && plugin->enc.lossy.bitrate.cbr.enabled ) { + param.append( " " + plugin->enc.lossy.bitrate.cbr.param ); + sBitrate = QString::number( item->fileListItem->options.encodingOptions.iQuality ); + } + else if( item->fileListItem->options.encodingOptions.sBitrateMode == "abr" && plugin->enc.lossy.bitrate.abr.enabled ) { + param.append( " " + plugin->enc.lossy.bitrate.abr.param ); + sBitrate = QString::number( item->fileListItem->options.encodingOptions.iQuality ); + if( item->fileListItem->options.encodingOptions.bBitrateRange && plugin->enc.lossy.bitrate.abr.bitrate_range.enabled ) { + param.append( " " + plugin->enc.lossy.bitrate.abr.bitrate_range.param_min ); + sMinBitrate = QString::number( item->fileListItem->options.encodingOptions.iMinBitrate ); + param.append( " " + plugin->enc.lossy.bitrate.abr.bitrate_range.param_max ); + sMaxBitrate = QString::number( item->fileListItem->options.encodingOptions.iMaxBitrate ); + } + } + } + else if( item->fileListItem->options.encodingOptions.sQualityMode == i18n("Quality") && plugin->enc.lossy.quality.enabled ) { + param.append( " " + plugin->enc.lossy.quality.param ); + if( plugin->enc.lossy.quality.profiles.empty() ) { + if( plugin->enc.lossy.quality.step < 1 ) { + if( plugin->enc.lossy.quality.range_max >= plugin->enc.lossy.quality.range_min) + t_float = ( (float)item->fileListItem->options.encodingOptions.iQuality * ( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100 ) + plugin->enc.lossy.quality.range_min; + else + t_float = ( (100.0f - (float)item->fileListItem->options.encodingOptions.iQuality) * ( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100 ) + plugin->enc.lossy.quality.range_max; + //t_float -= t_float%plugin->enc.quality.step; + //sQuality = QString().sprintf( "%.2f", t_float ); + sQuality = QString::number( t_float ); + } + else { + if( plugin->enc.lossy.quality.range_max >= plugin->enc.lossy.quality.range_min) + t_int = ( item->fileListItem->options.encodingOptions.iQuality * (int)( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100) + (int)plugin->enc.lossy.quality.range_min; + else + t_int = ( (100 - item->fileListItem->options.encodingOptions.iQuality) * (int)( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100) + (int)plugin->enc.lossy.quality.range_max; + //t_int -= t_int%plugin->enc.quality.step; + sQuality = QString::number( t_int ); + } + if( plugin->enc.bin == "oggenc" ) sQuality.replace(QChar('.'),KGlobal::locale()->decimalSymbol()); // HACK make oggenc usable with all langauges + else if( plugin->enc.lossy.quality.separator != '.' ) sQuality.replace(QChar('.'),plugin->enc.lossy.quality.separator); + } + else { + QStringList::Iterator it = plugin->enc.lossy.quality.profiles.at( rint(item->fileListItem->options.encodingOptions.iQuality*plugin->enc.lossy.quality.range_max/100) ); + sQuality = *it; + } + } + else if( item->fileListItem->options.encodingOptions.sQualityMode == i18n("Lossless") && plugin->enc.lossless.enabled ) { + param.append( " " + plugin->enc.lossless.param ); + } + else if( item->fileListItem->options.encodingOptions.sQualityMode == i18n("Hybrid") && plugin->enc.hybrid.enabled ) { + param.append( " " + plugin->enc.hybrid.param ); + sBitrate = QString::number( item->fileListItem->options.encodingOptions.iQuality ); + } + + if( item->fileListItem->options.encodingOptions.samplingRate.bEnabled && plugin->enc.lossy.samplingrate.enabled ) { + param.append( " " + plugin->enc.lossy.samplingrate.param ); + if( plugin->enc.lossy.samplingrate.unit == PluginLoaderBase::Hz ) { + sSamplingRate = QString::number( item->fileListItem->options.encodingOptions.samplingRate.iSamplingRate ); + } + else { + sSamplingRate = QString::number( (float)item->fileListItem->options.encodingOptions.samplingRate.iSamplingRate/1000 ); + } + } + + if( item->fileListItem->options.encodingOptions.channels.bEnabled ) { + if( item->fileListItem->options.encodingOptions.channels.sChannels == i18n("Mono") && plugin->enc.lossy.channels.mono_enabled ) { + param.append( " " + plugin->enc.lossy.channels.mono_param ); + } + else if( item->fileListItem->options.encodingOptions.channels.sChannels == i18n("Stereo") && plugin->enc.lossy.channels.stereo_enabled ) { + param.append( " " + plugin->enc.lossy.channels.stereo_param ); + } + else if( item->fileListItem->options.encodingOptions.channels.sChannels == i18n("Joint-Stereo") && plugin->enc.lossy.channels.joint_stereo_enabled ) { + param.append( " " + plugin->enc.lossy.channels.joint_stereo_param ); + } + else if( item->fileListItem->options.encodingOptions.channels.sChannels == i18n("Forced Joint-Stereo") && plugin->enc.lossy.channels.forced_joint_stereo_enabled ) { + param.append( " " + plugin->enc.lossy.channels.forced_joint_stereo_param ); + } + else if( item->fileListItem->options.encodingOptions.channels.sChannels == i18n("Dual Channels") && plugin->enc.lossy.channels.dual_channels_enabled ) { + param.append( " " + plugin->enc.lossy.channels.dual_channels_param ); + } + } + + if( item->fileListItem->options.encodingOptions.replaygain.bEnabled && plugin->enc.replaygain.enabled && plugin->enc.replaygain.use && formatItem->internalReplayGain ) { + param.append( " " + plugin->enc.replaygain.use ); + } + else if( plugin->enc.replaygain.enabled && plugin->enc.replaygain.avoid ) { + param.append( " " + plugin->enc.replaygain.avoid ); + } + +// if( !tagEngine->canWrite(item->fileListItem->options.encodingOptions.sFormat) && item->fileListItem->tags && plugin->enc.tag.enabled ) { + if( item->fileListItem->tags && plugin->enc.tag.enabled && item->fileListItem->options.encodingOptions.sFormat != "aac" ) { // HACK don't write metadata to aac + if( !plugin->enc.tag.param.isEmpty() ) param.append( " " + plugin->enc.tag.param ); + if( !plugin->enc.tag.artist.isEmpty() && !item->fileListItem->tags->artist.isEmpty() ) param.append( " " + plugin->enc.tag.artist ); + if( !plugin->enc.tag.album.isEmpty() && !item->fileListItem->tags->album.isEmpty() ) param.append( " " + plugin->enc.tag.album ); + if( !plugin->enc.tag.comment.isEmpty() && !item->fileListItem->tags->comment.isEmpty() ) param.append( " " + plugin->enc.tag.comment ); + if( !plugin->enc.tag.disc.isEmpty() && item->fileListItem->tags->disc != 0 ) param.append( " " + plugin->enc.tag.disc ); + if( !plugin->enc.tag.genre.isEmpty() && !item->fileListItem->tags->genre.isEmpty() ) param.append( " " + plugin->enc.tag.genre ); + if( !plugin->enc.tag.track.isEmpty() && item->fileListItem->tags->track != 0 ) param.append( " " + plugin->enc.tag.track ); + if( !plugin->enc.tag.composer.isEmpty() && !item->fileListItem->tags->composer.isEmpty() ) param.append( " " + plugin->enc.tag.composer ); + if( !plugin->enc.tag.title.isEmpty() && !item->fileListItem->tags->title.isEmpty() ) param.append( " " + plugin->enc.tag.title ); + if( !plugin->enc.tag.year.isEmpty() && item->fileListItem->tags->year != 0 ) param.append( " " + plugin->enc.tag.year ); + } + + QString sInOutFiles = item->fileListItem->options.encodingOptions.sInOutFiles; + param = sInOutFiles.replace( "%p", param ); + + // cosmetic surgery + param = param.simplifyWhiteSpace(); + + params = QStringList::split( ' ', param ); + + QString inputFile; + if( item->mode & ConvertItem::decode || item->mode & ConvertItem::rip ) inputFile = item->tempWavFile->name(); + else inputFile = item->tempInFile->name(); + + for( QStringList::Iterator it = params.begin(); it != params.end(); ++it ) + { + paramSplinter = *it; + paramSplinter.replace( "%i", inputFile ); + paramSplinter.replace( "%o", item->tempOutFile->name() ); + paramSplinter.replace( "%c", sStrength ); + paramSplinter.replace( "%b", sBitrate ); + paramSplinter.replace( "%q", sQuality ); + paramSplinter.replace( "%m", sMinBitrate ); + paramSplinter.replace( "%M", sMaxBitrate ); + paramSplinter.replace( "%s", sSamplingRate ); + + if( item->fileListItem->tags ) { + paramSplinter.replace( "%ta", ( item->fileListItem->tags->artist != "" ) ? item->fileListItem->tags->artist : i18n("Unknown") ); + paramSplinter.replace( "%tb", ( item->fileListItem->tags->album != "" ) ? item->fileListItem->tags->album : i18n("Unknown") ); + paramSplinter.replace( "%tc", ( item->fileListItem->tags->comment != "" ) ? item->fileListItem->tags->comment : i18n("Unknown") ); + paramSplinter.replace( "%td", ( QString::number(item->fileListItem->tags->disc) != "" ) ? QString::number(item->fileListItem->tags->disc) : "0" ); + paramSplinter.replace( "%tg", ( item->fileListItem->tags->genre != "" ) ? item->fileListItem->tags->genre : i18n("Unknown") ); + paramSplinter.replace( "%tn", ( QString::number(item->fileListItem->tags->track) != "" ) ? QString::number(item->fileListItem->tags->track) : "0" ); + paramSplinter.replace( "%tp", ( item->fileListItem->tags->composer != "" ) ? item->fileListItem->tags->composer : i18n("Unknown") ); + paramSplinter.replace( "%tt", ( item->fileListItem->tags->title != "" ) ? item->fileListItem->tags->title : i18n("Unknown") ); + paramSplinter.replace( "%ty", ( QString::number(item->fileListItem->tags->year) != "" ) ? QString::number(item->fileListItem->tags->year) : "0" ); + } + + if( paramSplinter != "" && paramSplinter != " " ) *(item->convertProcess) << paramSplinter; // NOTE fixes wavpack encoding + } + + param.replace( "%i", "\""+inputFile+"\"" ); + param.replace( "%o", "\""+item->tempOutFile->name()+"\"" ); + param.replace( "%c", sStrength ); + param.replace( "%b", sBitrate ); + param.replace( "%q", sQuality ); + param.replace( "%m", sMinBitrate ); + param.replace( "%M", sMaxBitrate ); + param.replace( "%s", sSamplingRate ); + + if( item->fileListItem->tags ) { + param.replace( "%ta", "\""+item->fileListItem->tags->artist+"\"" ); + param.replace( "%tb", "\""+item->fileListItem->tags->album+"\"" ); + param.replace( "%tc", "\""+item->fileListItem->tags->comment+"\"" ); + param.replace( "%td", QString::number(item->fileListItem->tags->disc) ); + param.replace( "%tg", "\""+item->fileListItem->tags->genre+"\"" ); + param.replace( "%tn", QString::number(item->fileListItem->tags->track) ); + param.replace( "%tp", "\""+item->fileListItem->tags->composer+"\"" ); + param.replace( "%tt", "\""+item->fileListItem->tags->title+"\"" ); + param.replace( "%ty", QString::number(item->fileListItem->tags->year) ); + } + + logger->log( item->logID, param ); + + //kdDebug() << " Executing: `" << param << "'" << endl; + + //item->readOutputTimer.start(); + item->lastOutputTimer.start(); + + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Encoding")+"... 00 %" ); + + item->convertProcess->setPriority( config->data.general.priority ); + item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ); +} + +void Convert::replaygain( ConvertItem* item ) +{ + logger->log( item->logID, i18n("Applying Replay Gain") ); + item->state = ConvertItem::replaygain; + + FormatItem* formatItem = config->getFormatItem( item->fileListItem->options.encodingOptions.sFormat ); + if( formatItem == 0 ) { + logger->log( item->logID, " NULL POINTER: Convert::replaygain( ... ) / formatItem" ); + return; + } + ConvertPlugin* plugin = formatItem->encoder; + if( plugin == 0 ) { + logger->log( item->logID, " NULL POINTER: Convert::replaygain( ... ) / plugin" ); + return; + } + + if( plugin->enc.replaygain.enabled && formatItem->internalReplayGain ) { + executeNextStep( item ); + return; + } + + item->replayGain = new ReplayGain( config, logger ); + bool ret = item->replayGain->apply( item->tempOutFile->name(), item->fileListItem->options.encodingOptions.sFormat, item->convertProcess, item->logID ); + + if( !ret ) { + executeNextStep( item ); + return; + } + + //item->readOutputTimer.start(); + item->lastOutputTimer.start(); + + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Replay Gain")+"... 00 %" ); +} + +void Convert::writeTags( ConvertItem* item ) +{ + logger->log( item->logID, i18n("Writing tags") ); + item->state = ConvertItem::write_tags; + + if( item->mode & ConvertItem::encode ) { + tagEngine->writeTags( item->tempOutFile->name(), item->fileListItem->tags ); + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Writing tags")+"... 00 %" ); + } + + executeNextStep( item ); +} + +void Convert::put( ConvertItem* item ) +{ + logger->log( item->logID, i18n("Moving file") ); + item->state = ConvertItem::put; + + QString src; + if( item->mode & ConvertItem::encode ) src = item->tempOutFile->name(); + else src = item->tempWavFile->name(); + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Moving file")+"... 00 %" ); + + item->outputFilePathName = OutputDirectory::makePath( OutputDirectory::uniqueFileName(OutputDirectory::calcPath(item->fileListItem,config)) ).replace("%2f","%252f"); + + KURL source( src ); + KURL destination( item->outputFilePathName ); + + if( source.isLocalFile() && destination.isLocalFile() ) { + item->convertProcess->clearArguments(); + + *(item->convertProcess) << "cp"; + *(item->convertProcess) << source.path(); + *(item->convertProcess) << destination.path(); + + logger->log( item->logID, "cp \"" + source.path() + "\" \"" + destination.path() + "\"" ); + + item->convertProcess->setPriority( config->data.general.priority ); + item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ); + } + else { + item->moveJob = new KIO::FileCopyJob( source, destination, -1, false, false, false, false ); + connect( item->moveJob, SIGNAL(percent(KIO::Job*,unsigned long)), + this, SLOT(moveProgress(KIO::Job*,unsigned long)) + ); + connect( item->moveJob, SIGNAL(result(KIO::Job*)), + this, SLOT(moveFinished(KIO::Job*)) + ); + } +} + +void Convert::putCorrection( ConvertItem* item ) +{ + logger->log( item->logID, i18n("Moving correction file") ); + item->state = ConvertItem::put_correction; + + QString src = item->correctionOutFile; + + QString dest = OutputDirectory::makePath( OutputDirectory::calcPath(item->fileListItem,config,item->correctionOutputExtension) ).replace("%2f","%252f"); + + KURL source( src ); +// KURL destination( dest ); + KURL destination; + destination.setPath( dest ); + + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Moving correction file")+"... 00 %" ); + + if( source.isLocalFile() && destination.isLocalFile() ) { + item->convertProcess->clearArguments(); + + *(item->convertProcess) << "cp"; + *(item->convertProcess) << source.path(); + *(item->convertProcess) << destination.path(); + + logger->log( item->logID, "cp \"" + source.path() + "\" \"" + destination.path() + "\"" ); + + item->convertProcess->setPriority( config->data.general.priority ); + item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ); + } + else { + item->moveJob = new KIO::FileCopyJob( source, destination, -1, false, false, false, false ); + connect( item->moveJob, SIGNAL(percent(KIO::Job*,unsigned long)), + this, SLOT(moveProgress(KIO::Job*,unsigned long)) + ); + connect( item->moveJob, SIGNAL(result(KIO::Job*)), + this, SLOT(moveFinished(KIO::Job*)) + ); + } +} + +void Convert::executeUserScript( ConvertItem* item ) +{ + logger->log( item->logID, i18n("Running user script") ); + item->state = ConvertItem::execute_userscript; + + KURL source( item->fileListItem->options.filePathName ); + KURL destination( item->outputFilePathName ); + + item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Running user script")+"... 00 %" ); + + item->convertProcess->clearArguments(); + + QString userscript = locate( "data", "soundkonverter/userscript.sh" ); + if( userscript == "" ) executeNextStep( item ); + + *(item->convertProcess) << userscript; + *(item->convertProcess) << source.path(); + *(item->convertProcess) << destination.path(); + + logger->log( item->logID, userscript + " \"" + source.path() + "\" \"" + destination.path() + "\"" ); + + item->convertProcess->setPriority( config->data.general.priority ); + item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ); +} + +void Convert::executeNextStep( ConvertItem* item ) +{ + logger->log( item->logID, i18n("Executing next step") ); + + item->percent = 0; + item->lastPercent = 0; // used for ripping a whole cd to one file + + switch( item->state ) + { + case ConvertItem::get: + { + if( item->mode & ConvertItem::get_correction ) getCorrection( item ); + else if( item->mode & ConvertItem::rip ) rip( item ); + else if( item->mode & ConvertItem::decode ) decode( item ); + else if( item->mode & ConvertItem::encode ) encode( item ); + else if( item->mode & ConvertItem::replaygain ) replaygain( item ); + else if( item->mode & ConvertItem::write_tags ) writeTags( item ); + else if( item->mode & ConvertItem::put ) put( item ); + else if( item->mode & ConvertItem::put_correction ) putCorrection( item ); + else if( config->data.general.executeUserScript ) executeUserScript( item ); + else remove( item ); + break; + } + case ConvertItem::get_correction: + { + if( item->mode & ConvertItem::rip ) rip( item ); + else if( item->mode & ConvertItem::decode ) decode( item ); + else if( item->mode & ConvertItem::encode ) encode( item ); + else if( item->mode & ConvertItem::replaygain ) replaygain( item ); + else if( item->mode & ConvertItem::write_tags ) writeTags( item ); + else if( item->mode & ConvertItem::put ) put( item ); + else if( item->mode & ConvertItem::put_correction ) putCorrection( item ); + else if( config->data.general.executeUserScript ) executeUserScript( item ); + else remove( item ); + break; + } + case ConvertItem::rip: + { + if( item->mode & ConvertItem::decode ) decode( item ); + else if( item->mode & ConvertItem::encode ) encode( item ); + else if( item->mode & ConvertItem::replaygain ) replaygain( item ); + else if( item->mode & ConvertItem::write_tags ) writeTags( item ); + else if( item->mode & ConvertItem::put ) put( item ); + else if( item->mode & ConvertItem::put_correction ) putCorrection( item ); + else if( config->data.general.executeUserScript ) executeUserScript( item ); + else remove( item ); + break; + } + case ConvertItem::decode: + { + if( item->mode & ConvertItem::encode ) encode( item ); + else if( item->mode & ConvertItem::replaygain ) replaygain( item ); + else if( item->mode & ConvertItem::write_tags ) writeTags( item ); + else if( item->mode & ConvertItem::put ) put( item ); + else if( item->mode & ConvertItem::put_correction ) putCorrection( item ); + else if( config->data.general.executeUserScript ) executeUserScript( item ); + else remove( item ); + break; + } + case ConvertItem::encode: + { + if( item->mode & ConvertItem::replaygain ) replaygain( item ); + else if( item->mode & ConvertItem::write_tags ) writeTags( item ); + else if( item->mode & ConvertItem::put ) put( item ); + else if( item->mode & ConvertItem::put_correction ) putCorrection( item ); + else if( config->data.general.executeUserScript ) executeUserScript( item ); + else remove( item ); + break; + } + case ConvertItem::replaygain: + { + if( item->mode & ConvertItem::write_tags ) writeTags( item ); + else if( item->mode & ConvertItem::put ) put( item ); + else if( item->mode & ConvertItem::put_correction ) putCorrection( item ); + else if( config->data.general.executeUserScript ) executeUserScript( item ); + else remove( item ); + break; + } + case ConvertItem::write_tags: + { + if( item->mode & ConvertItem::put ) put( item ); + else if( item->mode & ConvertItem::put_correction ) putCorrection( item ); + else if( config->data.general.executeUserScript ) executeUserScript( item ); + else remove( item ); + break; + } + case ConvertItem::put: + { + if( item->mode & ConvertItem::put_correction ) putCorrection( item ); + else if( config->data.general.executeUserScript ) executeUserScript( item ); + else remove( item ); + break; + } + case ConvertItem::put_correction: + { + if( config->data.general.executeUserScript ) executeUserScript( item ); + else remove( item ); + break; + } + case ConvertItem::execute_userscript: + { + remove( item ); + break; + } + default: // case (ConvertItem::Mode)0x0000: + { + if( item->mode & ConvertItem::get ) get( item ); + else if( item->mode & ConvertItem::get_correction ) getCorrection( item ); + else if( item->mode & ConvertItem::rip ) rip( item ); + else if( item->mode & ConvertItem::decode ) decode( item ); + else if( item->mode & ConvertItem::encode ) encode( item ); + else if( item->mode & ConvertItem::replaygain ) replaygain( item ); + else if( item->mode & ConvertItem::write_tags ) writeTags( item ); + else if( item->mode & ConvertItem::put ) put( item ); + else if( item->mode & ConvertItem::put_correction ) putCorrection( item ); + else if( config->data.general.executeUserScript ) executeUserScript( item ); + else remove( item ); + break; + } + } +} + +void Convert::moveProgress( KIO::Job* job, unsigned long percent ) +{ + // search the item list for our item + for( QValueList<ConvertItem*>::Iterator item = items.begin(); item != items.end(); item++ ) { + if( (*item)->moveJob == job ) { + (*item)->percent = percent; + } + } +} + +void Convert::moveFinished( KIO::Job* job ) +{ + // search the item list for our item + for( QValueList<ConvertItem*>::Iterator item = items.begin(); item != items.end(); item++ ) { + if( (*item)->moveJob == job ) { + + (*item)->percent = 0; + if( (*item)->state == ConvertItem::get ) { + if( job->error() != 0 ) { + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + + logger->log( (*item)->logID, " " + i18n("Got file") ); + + // if file is remote or the tag reding failed previously, read the tags now + if( (*item)->fileListItem->tags == 0 ) { + (*item)->fileListItem->tags = tagEngine->readTags( (*item)->tempInFile->name() ); + } + + emit countTime( (*item)->getTime ); + } + else if( (*item)->state == ConvertItem::get_correction ) { + if( job->error() != 0 ) { + emit uncountTime( (*item)->getTime ); + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + + logger->log( (*item)->logID, " " + i18n("Got file") ); + emit countTime( (*item)->getCorrectionTime ); + } + else if( (*item)->state == ConvertItem::rip ) { + if( job->error() != 0 ) { + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + + //logger->log( (*item)->logID, " " + i18n("Ripped track") ); + (*item)->fileListItem->ripping = false; + emit countTime( (*item)->ripTime ); + emit rippingFinished( (*item)->fileListItem->device ); + } + else if( (*item)->state == ConvertItem::put ) { + if( job->error() != 0 ) { + logger->log( (*item)->logID, i18n("Could not write to file: `%1'").arg(OutputDirectory::calcPath((*item)->fileListItem,config)) ); + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + emit uncountTime( (*item)->encodeTime ); + emit uncountTime( (*item)->replaygainTime ); + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + + logger->log( (*item)->logID, " " + i18n("File moved") ); + } + else if( (*item)->state == ConvertItem::put_correction ) { + if( job->error() != 0 ) { + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + emit uncountTime( (*item)->encodeTime ); + emit uncountTime( (*item)->replaygainTime ); + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + + logger->log( (*item)->logID, " " + i18n("File moved") ); + } + + executeNextStep( *item ); + return; + } + } +} + +void Convert::processOutput( KProcess* proc, char* data, int ) +{ + int iPercent = 0, iTime = 0, iPos = 0, iNum = 0; + + // search the item list for our item + for( QValueList<ConvertItem*>::Iterator item = items.begin(); item != items.end(); item++ ) + { + if( (*item)->convertProcess == proc ) + { + QString log_data = data; +/* log_data.replace("\n","\\n"); + log_data.replace("\t","\\t"); + log_data.replace("\r","\\r"); + log_data.replace("\b","\\b");*/ + logger->log( (*item)->logID, " " + i18n("Output") + ": " + log_data ); + + //if( (*item)->readOutputTimer.elapsed() < /*config->pauseTime*/ 100 ) return; // TODO use config value + //(*item)->readOutputTimer.start(); + + if( (*item)->state == ConvertItem::decode ) + { + if( (*item)->fileListItem == 0 ) return; + + ConvertPlugin* plugin = config->decoderForFormat( (*item)->fileListItem->mimeType ); + // TODO null pointer check + + QString outputPattern = plugin->dec.output; + //outputPattern.replace( "%i", "%p" ); // for compatibility with old plugins + + if( outputPattern.find("%p") != -1 ) { + outputPattern.replace( "%p", "%i" ); + sscanf( data, outputPattern, &iPercent ); + } + else if( outputPattern.find("%t") != -1 ) { + outputPattern.replace( "%t", "%i" ); + sscanf( data, outputPattern, &iTime ); + iPercent = iTime * 100 / (*item)->fileListItem->time; + } + else if( outputPattern.find("%0") != -1 && outputPattern.find("%1") != -1 ) { + if( outputPattern.find("%0") < outputPattern.find("%1") ) { + outputPattern.replace( "%0", "%i" ); + outputPattern.replace( "%1", "%i" ); + sscanf( data, outputPattern, &iPos, &iNum ); + } + else { + outputPattern.replace( "%0", "%i" ); + outputPattern.replace( "%1", "%i" ); + sscanf( data, outputPattern, &iNum, &iPos ); + } + if( iPos != 0 && iNum != 0 ) iPercent = iPos * 100 / iNum; + } + + if( iPercent > 0 && iPercent <= 100 ) + { + // TODO guess progress, when no signal is received + (*item)->lastOutputTimer.start(); + (*item)->percent = iPercent; + } + } + else if( (*item)->state == ConvertItem::encode ) + { + if( (*item)->fileListItem == 0 ) return; + + // NOTE use mimetype + ConvertPlugin* plugin = config->encoderForFormat( (*item)->fileListItem->options.encodingOptions.sFormat ); + // TODO null pointer check + + QString outputPattern; + if( (*item)->fileListItem->options.encodingOptions.sQualityMode == i18n("Quality") ) outputPattern = plugin->enc.lossy.quality.output; + else if( (*item)->fileListItem->options.encodingOptions.sQualityMode == i18n("Bitrate") && (*item)->fileListItem->options.encodingOptions.sBitrateMode == "cbr" ) outputPattern = plugin->enc.lossy.bitrate.cbr.output; + else if( (*item)->fileListItem->options.encodingOptions.sQualityMode == i18n("Bitrate") && (*item)->fileListItem->options.encodingOptions.sBitrateMode == "abr" ) outputPattern = plugin->enc.lossy.bitrate.abr.output; + + //outputPattern.replace( "%i", "%p" ); // for compatibility with old plugins + + if( outputPattern.find("%p") != -1 ) { + outputPattern.replace( "%p", "%i" ); + sscanf( data, outputPattern, &iPercent ); + } + else if( outputPattern.find("%t") != -1 ) { + outputPattern.replace( "%t", "%i" ); + sscanf( data, outputPattern, &iTime ); + iPercent = iTime * 100 / (*item)->fileListItem->time; + } + else if( outputPattern.find("%0") != -1 && outputPattern.find("%1") != -1 ) { + if( outputPattern.find("%0") < outputPattern.find("%1") ) { + outputPattern.replace( "%0", "%i" ); + outputPattern.replace( "%1", "%i" ); + sscanf( data, outputPattern, &iPos, &iNum ); + } + else { + outputPattern.replace( "%0", "%i" ); + outputPattern.replace( "%1", "%i" ); + sscanf( data, outputPattern, &iNum, &iPos ); + } + if( iPos != 0 && iNum != 0 ) iPercent = iPos * 100 / iNum; + } + + if( iPercent > 0 && iPercent <= 100 ) + { + // TODO guess progress, when no signal is received + (*item)->lastOutputTimer.start(); + (*item)->percent = iPercent; + } + } + else if( (*item)->state == ConvertItem::rip ) // ### soundkonverter 0.4 make the progress dependent on the track length + { + if( (*item)->fileListItem == 0 ) return; + + RipperPlugin* plugin = config->getCurrentRipper(); + // TODO null pointer check + + QString outputPattern; + if( (*item)->fileListItem->track != 0 ) outputPattern = plugin->rip.output; + else outputPattern = plugin->rip.full_disc.output; + //outputPattern.replace( "%i", "%p" ); // for compatibility with old plugins + + if( outputPattern.find("%p") != -1 || outputPattern.find("%a") != -1 ) { + outputPattern.replace( "%p", "%i" ); + outputPattern.replace( "%a", "%i" ); + sscanf( data, outputPattern, &iPercent ); + } + else if( outputPattern.find("%t") != -1 ) { + outputPattern.replace( "%t", "%i" ); + sscanf( data, outputPattern, &iTime ); + iPercent = iTime * 100 / (*item)->fileListItem->time; + } + else if( outputPattern.find("%0") != -1 && outputPattern.find("%1") != -1 ) { + if( outputPattern.find("%0") < outputPattern.find("%1") ) { + outputPattern.replace( "%0", "%i" ); + outputPattern.replace( "%1", "%i" ); + sscanf( data, outputPattern, &iPos, &iNum ); + } + else { + outputPattern.replace( "%0", "%i" ); + outputPattern.replace( "%1", "%i" ); + sscanf( data, outputPattern, &iNum, &iPos ); + } + if( iPos != 0 && iNum != 0 ) iPercent = iPos * 100 / iNum; + } + + if( iPercent > 0 && iPercent <= 100 ) + { + // TODO guess progress, when no signal is received + (*item)->lastOutputTimer.start(); + if( (*item)->fileListItem->track == 0 && plugin->rip.full_disc.output.find("%a") != -1 ) { + if( iPercent < (*item)->lastPercent ) (*item)->track++; + (*item)->lastPercent = iPercent; + (*item)->percent = (*item)->track * 100 / (*item)->tracks + iPercent / (*item)->tracks; + } + else { + (*item)->percent = iPercent; + } + } + } + return; + } + } +} + +void Convert::processExit( KProcess* proc ) +{ + // search the item list for our item + for( QValueList<ConvertItem*>::Iterator item = items.begin(); item != items.end(); item++ ) { +// if( (*item)->convertProcess == proc && (*item)->fileListItem != 0 ) { + if( (*item)->convertProcess == proc ) { + + (*item)->percent = 0; + if( (*item)->state == ConvertItem::rip ) { + if( proc->signalled() ) { // FIXME does only check, whether the process has been killed + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + else if( !proc->normalExit() ) { + remove( *item, -1 ); + updateProgressIndicator(); + return; + } + else { + (*item)->fileListItem->ripping = false; + emit countTime( (*item)->ripTime ); + emit rippingFinished( (*item)->fileListItem->device ); + } + } + if( (*item)->state == ConvertItem::get ) { + if( proc->signalled() ) { + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + else if( !proc->normalExit() ) { + remove( *item, -1 ); + updateProgressIndicator(); + return; + } + else { + logger->log( (*item)->logID, " " + i18n("Got file") ); + + // if file is remote or the tag reding failed previously, read the tags now + if( (*item)->fileListItem->tags == 0 ) { + (*item)->fileListItem->tags = tagEngine->readTags( (*item)->tempInFile->name() ); + } + + emit countTime( (*item)->getTime ); + } + } + if( (*item)->state == ConvertItem::get_correction ) { + if( proc->signalled() ) { + emit uncountTime( (*item)->getTime ); + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + else if( !proc->normalExit() ) { + emit uncountTime( (*item)->getTime ); + remove( *item, -1 ); + updateProgressIndicator(); + return; + } + else { + logger->log( (*item)->logID, " " + i18n("Got file") ); + emit countTime( (*item)->getCorrectionTime ); + } + } + if( (*item)->state == ConvertItem::decode ) { + if( proc->signalled() ) { + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + else if( !proc->normalExit() ) { + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + remove( *item, -1 ); + updateProgressIndicator(); + return; + } + else { + emit countTime( (*item)->decodeTime ); + } + } + if( (*item)->state == ConvertItem::encode ) { + if( proc->signalled() ) { + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + else if( !proc->normalExit() ) { + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + remove( *item, -1 ); + updateProgressIndicator(); + return; + } + else { +/* if( (*item)->binary == "faac" ) { + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + remove( *item, 1 ); + updateProgressIndicator(); + return; + }*/ + emit countTime( (*item)->encodeTime ); + } + } + if( (*item)->state == ConvertItem::put ) { + if( proc->signalled() ) { + logger->log( (*item)->logID, i18n("Could not write to file: `%1'").arg(OutputDirectory::calcPath((*item)->fileListItem,config)) ); + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + emit uncountTime( (*item)->encodeTime ); + emit uncountTime( (*item)->replaygainTime ); + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + if( !proc->normalExit() ) { + logger->log( (*item)->logID, i18n("Could not write to file: `%1'").arg(OutputDirectory::calcPath((*item)->fileListItem,config)) ); + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + emit uncountTime( (*item)->encodeTime ); + emit uncountTime( (*item)->replaygainTime ); + remove( *item, -1 ); + updateProgressIndicator(); + return; + } + else { + logger->log( (*item)->logID, " " + i18n("File moved") ); + } + } + if( (*item)->state == ConvertItem::put_correction ) { + if( proc->signalled() ) { + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + emit uncountTime( (*item)->encodeTime ); + emit uncountTime( (*item)->replaygainTime ); + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + if( !proc->normalExit() ) { + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + emit uncountTime( (*item)->encodeTime ); + emit uncountTime( (*item)->replaygainTime ); + remove( *item, -1 ); + updateProgressIndicator(); + return; + } + else { + logger->log( (*item)->logID, " " + i18n("File moved") ); + } + } + if( (*item)->state == ConvertItem::replaygain ) { + if( proc->signalled() ) { + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + emit uncountTime( (*item)->encodeTime ); + remove( *item, 1 ); + updateProgressIndicator(); + return; + } + else if( !proc->normalExit() ) { + emit uncountTime( (*item)->getTime ); + emit uncountTime( (*item)->getCorrectionTime ); + emit uncountTime( (*item)->ripTime ); + emit uncountTime( (*item)->decodeTime ); + emit uncountTime( (*item)->encodeTime ); + remove( *item, -1 ); + updateProgressIndicator(); + return; + } + else { + emit countTime( (*item)->replaygainTime ); + } + } + // TODO did we get errors? (complete) + executeNextStep( *item ); + return; + } + } +} + +void Convert::add( FileListItem* item ) +{ + logger->log( 1000, i18n("Adding new item to conversion list: `%1'").arg(item->options.filePathName) ); + + // append the item to the item list and store the iterator + QValueList<ConvertItem*>::Iterator newItem = items.append( new ConvertItem( item ) ); + + // register at the logger + (*newItem)->logID = logger->registerProcess( item->options.filePathName ); + logger->log( 1000, " " + i18n("Got log ID: %1").arg((*newItem)->logID) ); + + logger->log( (*newItem)->logID, "Mime Type: " + (*newItem)->fileListItem->mimeType ); + if( (*newItem)->fileListItem->tags ) logger->log( (*newItem)->logID, i18n("Tags successfully read") ); + else logger->log( (*newItem)->logID, i18n("Reading tags failed") ); + + // set some variables to default values + (*newItem)->mode = (ConvertItem::Mode)0x0000; + (*newItem)->state = (ConvertItem::Mode)0x0000; + (*newItem)->convertProcess = 0; + (*newItem)->moveJob = 0; + (*newItem)->replayGain = 0; + + /* seems to be unnecessary + (*newItem)->correctionInFile = QString::null(); + (*newItem)->correctionOutFile = QString::null(); + (*newItem)->correctionInputExtension = QString::null(); + (*newItem)->correctionOutputExtension = QString::null();*/ + + // connect convertProcess of our new item with the slots of Convert + (*newItem)->convertProcess = new KProcess(); + connect( (*newItem)->convertProcess, SIGNAL(receivedStdout(KProcess*,char*,int)), + this, SLOT(processOutput(KProcess*,char*,int)) + ); + connect( (*newItem)->convertProcess, SIGNAL(receivedStderr(KProcess*,char*,int)), + this, SLOT(processOutput(KProcess*,char*,int)) + ); + connect( (*newItem)->convertProcess, SIGNAL(processExited(KProcess*)), + this, SLOT(processExit(KProcess*)) + ); + + // NOTE the tempInFile is also created if the file is a audio cd track + + // set up the names of our temp files + (*newItem)->tempInFile = new KTempFile( QString::null, "." + item->fileFormat ); + (*newItem)->tempInFile->setAutoDelete( true ); + (*newItem)->tempInFile->close(); + + (*newItem)->tempWavFile = new KTempFile( QString::null, ".wav" ); + (*newItem)->tempWavFile->setAutoDelete( true ); + (*newItem)->tempWavFile->close(); + + (*newItem)->tempOutFile = new KTempFile( QString::null, "." + item->options.encodingOptions.sFormat ); + (*newItem)->tempOutFile->setAutoDelete( true ); + (*newItem)->tempOutFile->close(); + + if( item->track >= 0 ) // it's an audio cd track + { + (*newItem)->mode = ConvertItem::Mode( ConvertItem::rip ); + } + else // it's a file + { + (*newItem)->mode = ConvertItem::Mode( ConvertItem::get ); + if( item->fileFormat != "wav" ) + { + (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::decode ); + } + } + + if( item->options.encodingOptions.sFormat != "wav" ) + { + (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::encode ); + } + if( item->options.encodingOptions.replaygain.bEnabled ) + { + (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::replaygain ); + } + + QString extension; + + extension = config->getCorrectionExtension( item->mimeType ); + if( !extension.isEmpty() ) { + (*newItem)->correctionInputExtension = extension; + (*newItem)->correctionInFile = OutputDirectory::changeExtension( (*newItem)->tempInFile->name(), extension ); + (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::get_correction ); + logger->log( (*newItem)->logID, " correctionInFile: `" + (*newItem)->correctionInFile + "'" ); + } + + extension = config->getCorrectionExtension( item->options.encodingOptions.sFormat ); + if( !extension.isEmpty() && item->options.encodingOptions.sQualityMode == i18n("Hybrid") ) { + (*newItem)->correctionOutputExtension = extension; + (*newItem)->correctionOutFile = OutputDirectory::changeExtension( (*newItem)->tempOutFile->name(), extension ); + (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::put_correction ); + logger->log( (*newItem)->logID, " correctionOutFile: `" + (*newItem)->correctionOutFile + "'" ); + } + + (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::write_tags | ConvertItem::put ); + + // TODO use the values from the format info files !!! + + if( (*newItem)->mode & ConvertItem::get ) { + if( !item->local ) { + (*newItem)->getTime = 0.8; // TODO use the file size from the format info files + } + } + if( (*newItem)->mode & ConvertItem::get_correction ) { + if( !item->local ) { + (*newItem)->getCorrectionTime = 2.0; // TODO use the file size from the format info files + } + } + if( (*newItem)->mode & ConvertItem::rip ) { + (*newItem)->ripTime = 1.0; + } + if( (*newItem)->mode & ConvertItem::decode ) { + (*newItem)->decodeTime = 0.4; + } + if( (*newItem)->mode & ConvertItem::encode ) { + (*newItem)->encodeTime = 1.0; + } + if( (*newItem)->mode & ConvertItem::replaygain ) { + (*newItem)->replaygainTime = 0.2; + } + + float sum = ( (*newItem)->getTime + (*newItem)->getCorrectionTime + (*newItem)->ripTime + (*newItem)->decodeTime + (*newItem)->encodeTime + (*newItem)->replaygainTime ) / item->time; + + (*newItem)->getTime /= sum; + (*newItem)->getCorrectionTime /= sum; + (*newItem)->ripTime /= sum; + (*newItem)->decodeTime /= sum; + (*newItem)->encodeTime /= sum; + (*newItem)->replaygainTime /= sum; + + // visual feedback + item->converting = true; + + if( !tUpdateProgressIndicator->isActive() ) { + tUpdateProgressIndicator->start( config->data.general.updateDelay ); + } + + // and start + executeNextStep( *newItem ); +} + +void Convert::stop( FileListItem* item ) +{ + // search the item list for our item to stop + for( QValueList<ConvertItem*>::Iterator stopItem = items.begin(); stopItem != items.end(); stopItem++ ) { + // is fileListItem pointing at the same address, as item + if( (*stopItem)->fileListItem == item ) { + + if( (*stopItem)->convertProcess->isRunning() ) { + bool ret = (*stopItem)->convertProcess->kill( SIGKILL ); + //kdDebug() << "Killing process... (" << ret << ")" << endl; + if( ret ) { + logger->log( (*stopItem)->logID, i18n("Killing process ...") ); + } + else { + logger->log( (*stopItem)->logID, i18n("Killing process failed. Stopping after files are completed ...") ); + } + } + else if( (*stopItem)->moveJob != 0 ) { + //kdDebug() << "Killing file copy..." << endl; + // FIXME crashes sometimes - should be fixed by SIGKILL + // FIXME crash if file_copy was stealthed + logger->log( (*stopItem)->logID, i18n("Killing process ...") ); + (*stopItem)->moveJob->kill( false ); + } + + return; + } + } +} + +void Convert::remove( ConvertItem* item, int state ) +{ + // TODO "remove" (re-add) the times to the progress indicator + //emit uncountTime( item->getTime + item->getCorrectionTime + item->ripTime + + // item->decodeTime + item->encodeTime + item->replaygainTime ); + + logger->log( item->logID, i18n("Removing file from conversion list. Exit code %1").arg(state) ); + + if( item->fileListItem->notify != "" ) { + QString command = item->fileListItem->notify; + command.replace( "%u", item->fileListItem->url ); + command.replace( "%i", item->fileListItem->options.filePathName.replace(" ","%20") ); + command.replace( "%o", item->outputFilePathName.replace(" ","%20") ); + logger->log( item->logID, " "+i18n("Executing command: \"%1\"").arg(command) ); + notify.clearArguments(); + QString paramSplinter; + // FIXME split correct (strings with spaces are splited by mistake) + // FIXME only one command can be executed at once!? + QStringList params = QStringList::split( ' ', item->fileListItem->notify ); + for( QStringList::Iterator it = params.begin(); it != params.end(); ++it ) + { + paramSplinter = *it; + paramSplinter.replace( "%u", item->fileListItem->url ); + paramSplinter.replace( "%i", item->fileListItem->options.filePathName ); + paramSplinter.replace( "%o", item->outputFilePathName ); + notify << paramSplinter; + } + notify.start( KProcess::DontCare ); + } + + item->fileListItem->converting = false; + emit finished( item->fileListItem, state ); // send signal to FileList + emit finishedProcess( item->logID, state ); // send signal to Logger + + item->fileListItem = 0; + if( item->convertProcess != 0 ) delete item->convertProcess; + item->convertProcess = 0; + //if( item->moveJob != 0 ) delete item->moveJob; // NOTE makes soundkonverter crash + //item->moveJob = 0; + if( item->replayGain != 0 ) delete item->replayGain; + item->replayGain = 0; + + if( item->tempInFile != 0 ) delete item->tempInFile; + item->tempInFile = 0; + if( item->tempWavFile != 0 ) delete item->tempWavFile; + item->tempWavFile = 0; + if( item->tempOutFile != 0 ) delete item->tempOutFile; + item->tempOutFile = 0; + QFile file; + file.setName( item->correctionInFile ); + if( file.exists() ) file.remove(); + file.setName( item->correctionOutFile ); + if( file.exists() ) file.remove(); + + for( QValueList<ConvertItem*>::Iterator it = items.begin(); it != items.end(); it++ ) { + if( (*it) == item ) { + items.remove( it ); + break; + } + } + + delete item; + item = 0; + + if( items.count() == 0 ) { + tUpdateProgressIndicator->stop(); + } +} + +void Convert::updateProgressIndicator() +{ + float time = 0; + + for( QValueList<ConvertItem*>::Iterator it = items.begin(); it != items.end(); it++ ) { + if( (*it)->state == ConvertItem::get ) { + time += (*it)->getTime * (*it)->percent / 100; + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Getting file")+"... "+QString().sprintf("%02i %%",(*it)->percent) ); + } + else if( (*it)->state == ConvertItem::get_correction ) { + time += (*it)->getCorrectionTime * (*it)->percent / 100; + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Getting correction file")+"... "+QString().sprintf("%02i %%",(*it)->percent) ); + } + else if( (*it)->state == ConvertItem::rip ) { + RipperPlugin* plugin = config->getCurrentRipper(); + if( plugin != 0 && plugin->rip.output.isEmpty() ) { + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Ripping")+"... "+i18n("State")+": "+i18n("Unknown") ); + } + else { + time += (*it)->ripTime * (*it)->percent / 100; + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Ripping")+"... "+QString().sprintf("%02i %%",(*it)->percent) ); + } + } + else if( (*it)->state == ConvertItem::decode ) { + ConvertPlugin* plugin = config->decoderForFormat( (*it)->fileListItem->mimeType ); + if( plugin == 0 || plugin->dec.output.isEmpty() ) { + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Decoding")+"... "+i18n("State")+": "+i18n("Unknown") ); + } + else { + time += (*it)->decodeTime * (*it)->percent / 100; + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Decoding")+"... "+QString().sprintf("%02i %%",(*it)->percent) ); + } + } + else if( (*it)->state == ConvertItem::encode ) { + ConvertPlugin* plugin = config->encoderForFormat( (*it)->fileListItem->options.encodingOptions.sFormat ); + QString outputPattern; + if( plugin != 0 && (*it)->fileListItem->options.encodingOptions.sQualityMode == i18n("Quality") ) outputPattern = plugin->enc.lossy.quality.output; + else if( plugin != 0 && (*it)->fileListItem->options.encodingOptions.sQualityMode == i18n("Bitrate") && (*it)->fileListItem->options.encodingOptions.sBitrateMode == "cbr" ) outputPattern = plugin->enc.lossy.bitrate.cbr.output; + else if( plugin != 0 && (*it)->fileListItem->options.encodingOptions.sQualityMode == i18n("Bitrate") && (*it)->fileListItem->options.encodingOptions.sBitrateMode == "abr" ) outputPattern = plugin->enc.lossy.bitrate.abr.output; + if( outputPattern.isEmpty() ) { + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Encoding")+"... "+i18n("State")+": "+i18n("Unknown") ); + } + else { + time += (*it)->encodeTime * (*it)->percent / 100; + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Encoding")+"... "+QString().sprintf("%02i %%",(*it)->percent) ); + } + } + else if( (*it)->state == ConvertItem::replaygain ) { + time += (*it)->replaygainTime * (*it)->percent / 100; + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Replay Gain")+"... "+QString().sprintf("%02i %%",(*it)->percent) ); + } + else if( (*it)->state == ConvertItem::put ) { + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Moving file")+"... "+QString().sprintf("%02i %%",(*it)->percent) ); + } + else if( (*it)->state == ConvertItem::put_correction ) { + time += (*it)->getCorrectionTime * (*it)->percent / 100; + (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Moving correction file")+"... "+QString().sprintf("%02i %%",(*it)->percent) ); + } + } + emit update( time ); +} + +// void Convert::priorityChanged( int priority ) +// { // FIXME setting a higher priority does not work +// KProcess pChangePriority; +// +// for( QValueList<ConvertItem*>::Iterator it = items.begin(); it != items.end(); it++ ) { +// if( (*it)->convertProcess->isRunning() ) { +// //(*it)->convertProcess->setPriority( priority ); +// pChangePriority.clearArguments(); +// pChangePriority << "renice"; +// QString prio; +// prio.sprintf( "%i", priority ); +// pChangePriority << prio; +// QString pid; +// pid.sprintf( "%i", (*it)->convertProcess->pid() ); +// pChangePriority << pid; +// //QString cmd; +// //cmd.sprintf( "renice %i %i", ); +// pChangePriority.start( KProcess::Block ); +// } +// } +// } + + |