File indexing completed on 2025-03-16 04:29:29
0001 /* 0002 SPDX-FileCopyrightText: 2011 Michal Malek <michalm@jabster.pl> 0003 SPDX-FileCopyrightText: 1998-2010 Sebastian Trueg <trueg@k3b.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 0009 #include "k3bcdcopyjob.h" 0010 #include "k3baudiosessionreadingjob.h" 0011 0012 #include "k3bexternalbinmanager.h" 0013 #include "k3bdevice.h" 0014 #include "k3bdiskinfo.h" 0015 #include "k3btoc.h" 0016 #include "k3bglobals.h" 0017 #include "k3bdevicehandler.h" 0018 #include "k3breadcdreader.h" 0019 #include "k3bdatatrackreader.h" 0020 #include "k3bcdrecordwriter.h" 0021 #include "k3bcdtext.h" 0022 #include "k3bcore.h" 0023 #include "k3binffilewriter.h" 0024 #include "k3bglobalsettings.h" 0025 #include "k3bcddb.h" 0026 #include "k3b_i18n.h" 0027 0028 #include <KConfig> 0029 #include <KIO/Global> 0030 #include <KIO/DeleteJob> 0031 #include <KIO/Job> 0032 0033 #include <QDebug> 0034 #include <QDir> 0035 #include <QFile> 0036 #include <QFileInfo> 0037 #include <QRegExp> 0038 #include <QStringList> 0039 #include <QTemporaryFile> 0040 #include <QTimer> 0041 #include <QVector> 0042 #include <QApplication> 0043 0044 #include <KCDDB/Client> 0045 #include <KCDDB/CDInfo> 0046 0047 0048 0049 class K3b::CdCopyJob::Private 0050 { 0051 public: 0052 Private() 0053 : canceled(false), 0054 running(false), 0055 readcdReader(0), 0056 dataTrackReader(0), 0057 audioSessionReader(0), 0058 cdrecordWriter(0), 0059 infFileWriter(0), 0060 cddb(0) { 0061 } 0062 0063 bool canceled; 0064 bool error; 0065 bool readingSuccessful; 0066 bool running; 0067 0068 int numSessions; 0069 bool doNotCloseLastSession; 0070 0071 int doneCopies; 0072 int currentReadSession; 0073 int currentWrittenSession; 0074 0075 K3b::Device::Toc toc; 0076 QByteArray cdTextRaw; 0077 0078 K3b::ReadcdReader* readcdReader; 0079 K3b::DataTrackReader* dataTrackReader; 0080 K3b::AudioSessionReadingJob* audioSessionReader; 0081 K3b::CdrecordWriter* cdrecordWriter; 0082 K3b::InfFileWriter* infFileWriter; 0083 0084 bool audioReaderRunning; 0085 bool dataReaderRunning; 0086 bool writerRunning; 0087 0088 // image filenames, one for every track 0089 QStringList imageNames; 0090 0091 // inf-filenames for writing audio tracks 0092 QStringList infNames; 0093 0094 // indicates if we created a dir or not 0095 bool deleteTempDir; 0096 0097 KCDDB::Client* cddb; 0098 KCDDB::CDInfo cddbInfo; 0099 0100 bool haveCddb; 0101 bool haveCdText; 0102 0103 QVector<bool> dataSessionProbablyTAORecorded; 0104 0105 // used to determine progress 0106 QVector<long> sessionSizes; 0107 long overallSize; 0108 }; 0109 0110 0111 K3b::CdCopyJob::CdCopyJob( K3b::JobHandler* hdl, QObject* parent ) 0112 : K3b::BurnJob( hdl, parent ), 0113 m_simulate(false), 0114 m_copies(1), 0115 m_onlyCreateImages(false), 0116 m_onTheFly(true), 0117 m_ignoreDataReadErrors(false), 0118 m_ignoreAudioReadErrors(true), 0119 m_noCorrection(false), 0120 m_dataReadRetries(128), 0121 m_audioReadRetries(5), 0122 m_copyCdText(true), 0123 m_writingMode( K3b::WritingModeAuto ) 0124 { 0125 d = new Private(); 0126 } 0127 0128 0129 K3b::CdCopyJob::~CdCopyJob() 0130 { 0131 delete d->infFileWriter; 0132 delete d; 0133 } 0134 0135 0136 void K3b::CdCopyJob::start() 0137 { 0138 d->running = true; 0139 d->canceled = false; 0140 d->error = false; 0141 d->readingSuccessful = false; 0142 d->audioReaderRunning = d->dataReaderRunning = d->writerRunning = false; 0143 d->sessionSizes.clear(); 0144 d->dataSessionProbablyTAORecorded.clear(); 0145 d->deleteTempDir = false; 0146 d->haveCdText = false; 0147 d->haveCddb = false; 0148 0149 if ( m_onlyCreateImages ) 0150 m_onTheFly = false; 0151 0152 jobStarted(); 0153 0154 emit newTask( i18n("Checking Source Medium") ); 0155 0156 emit burning(false); 0157 emit newSubTask( i18n("Waiting for source medium") ); 0158 0159 // wait for a source disk 0160 if( waitForMedium( m_readerDevice, 0161 K3b::Device::STATE_COMPLETE|K3b::Device::STATE_INCOMPLETE, 0162 K3b::Device::MEDIA_WRITABLE_CD|K3b::Device::MEDIA_CD_ROM ) == Device::MEDIA_UNKNOWN ) { 0163 finishJob( true, false ); 0164 return; 0165 } 0166 0167 emit newSubTask( i18n("Checking source medium") ); 0168 0169 // FIXME: read ISRCs and MCN 0170 0171 connect( K3b::Device::mediaInfo( m_readerDevice ), SIGNAL(finished(K3b::Device::DeviceHandler*)), 0172 this, SLOT(slotDiskInfoReady(K3b::Device::DeviceHandler*)) ); 0173 } 0174 0175 0176 void K3b::CdCopyJob::slotDiskInfoReady( K3b::Device::DeviceHandler* dh ) 0177 { 0178 if( dh->success() ) { 0179 d->toc = dh->toc(); 0180 0181 // 0182 // for now we copy audio, pure data (aka 1 data track), cd-extra (2 session, audio and data), 0183 // and data multisession which one track per session. 0184 // Everything else will be rejected 0185 // 0186 bool canCopy = true; 0187 bool audio = false; 0188 d->numSessions = dh->diskInfo().numSessions(); 0189 d->doNotCloseLastSession = (dh->diskInfo().diskState() == K3b::Device::STATE_INCOMPLETE); 0190 switch( dh->toc().contentType() ) { 0191 case K3b::Device::DATA: 0192 // check if every track is in it's own session 0193 // only then we copy the cd 0194 if( (int)dh->toc().count() != dh->diskInfo().numSessions() ) { 0195 emit infoMessage( i18n("K3b does not copy CDs containing multiple data tracks."), MessageError ); 0196 canCopy = false; 0197 } 0198 else if( dh->diskInfo().numSessions() > 1 ) 0199 emit infoMessage( i18n("Copying Multisession Data CD."), MessageInfo ); 0200 else 0201 emit infoMessage( i18n("Copying Data CD."), MessageInfo ); 0202 break; 0203 0204 case K3b::Device::MIXED: 0205 audio = true; 0206 if( dh->diskInfo().numSessions() != 2 || d->toc[0].type() != K3b::Device::Track::TYPE_AUDIO ) { 0207 emit infoMessage( i18n("K3b can only copy CD-Extra mixed mode CDs."), MessageError ); 0208 canCopy = false; 0209 } 0210 else 0211 emit infoMessage( i18n("Copying Enhanced Audio CD (CD-Extra)."), MessageInfo ); 0212 break; 0213 0214 case K3b::Device::AUDIO: 0215 audio = true; 0216 emit infoMessage( i18n("Copying Audio CD."), MessageInfo ); 0217 break; 0218 0219 case K3b::Device::NONE: 0220 default: 0221 emit infoMessage( i18n("The source disk is empty."), MessageError ); 0222 canCopy = false; 0223 break; 0224 } 0225 0226 // 0227 // A data track recorded in TAO mode has two run-out blocks which cannot be read and contain 0228 // zero data anyway. The problem is that I do not know of a valid method to determine if a track 0229 // was written in TAO (the control nibble does definitely not work, I never saw one which did not 0230 // equal 4). 0231 // So the solution for now is to simply try to read the last sector of a data track. If this is not 0232 // possible we assume it was written in TAO mode and reduce the length by 2 sectors 0233 // 0234 unsigned char buffer[2048]; 0235 int i = 1; 0236 for( K3b::Device::Toc::iterator it = d->toc.begin(); it != d->toc.end(); ++it ) { 0237 if( (*it).type() == K3b::Device::Track::TYPE_DATA ) { 0238 // we try twice just to be sure 0239 if( m_readerDevice->read10( buffer, 2048, (*it).lastSector().lba(), 1 ) || 0240 m_readerDevice->read10( buffer, 2048, (*it).lastSector().lba(), 1 ) ) { 0241 d->dataSessionProbablyTAORecorded.append(false); 0242 qDebug() << "(K3b::CdCopyJob) track " << i << " probably DAO recorded."; 0243 } 0244 else { 0245 d->dataSessionProbablyTAORecorded.append(true); 0246 qDebug() << "(K3b::CdCopyJob) track " << i << " probably TAO recorded."; 0247 } 0248 } 0249 0250 ++i; 0251 } 0252 0253 0254 // 0255 // To copy mode2 data tracks we need cdrecord >= 2.01a12 which introduced the -xa1 and -xamix options 0256 // 0257 const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord"); 0258 if( cdrecordBin && !cdrecordBin->hasFeature( "xamix" ) ) { 0259 for( K3b::Device::Toc::const_iterator it = d->toc.constBegin(); it != d->toc.constEnd(); ++it ) { 0260 if( (*it).type() == K3b::Device::Track::TYPE_DATA && 0261 ( (*it).mode() == K3b::Device::Track::XA_FORM1 || 0262 (*it).mode() == K3b::Device::Track::XA_FORM2 ) ) { 0263 emit infoMessage( i18n("K3b needs cdrecord 2.01a12 or newer to copy Mode2 data tracks."), MessageError ); 0264 finishJob( true, false ); 0265 return; 0266 } 0267 } 0268 } 0269 0270 0271 // 0272 // It is not possible to create multisession cds in raw writing mode 0273 // 0274 if( d->numSessions > 1 && m_writingMode == K3b::WritingModeRaw ) { 0275 if( !questionYesNo( i18n("You will only be able to copy the first session in raw writing mode. " 0276 "Continue anyway?"), 0277 i18n("Multisession CD") ) ) { 0278 finishJob( true, false ); 0279 return; 0280 } 0281 else { 0282 emit infoMessage( i18n("Only copying first session."), MessageWarning ); 0283 // TODO: remove the second session from the progress stuff 0284 } 0285 } 0286 0287 0288 // 0289 // We already create the temp filenames here since we need them to check the free space 0290 // 0291 if( !m_onTheFly || m_onlyCreateImages ) { 0292 if( !prepareImageFiles() ) { 0293 finishJob( false, true ); 0294 return; 0295 } 0296 0297 // 0298 // check free temp space 0299 // 0300 KIO::filesize_t imageSpaceNeeded = 0; 0301 for( K3b::Device::Toc::const_iterator it = d->toc.constBegin(); it != d->toc.constEnd(); ++it ) { 0302 if( (*it).type() == K3b::Device::Track::TYPE_AUDIO ) 0303 imageSpaceNeeded += (*it).length().audioBytes() + 44; 0304 else 0305 imageSpaceNeeded += (*it).length().mode1Bytes(); 0306 } 0307 0308 unsigned long avail, size; 0309 QString pathToTest = m_tempPath.left( m_tempPath.lastIndexOf( '/' ) ); 0310 if( !K3b::kbFreeOnFs( pathToTest, size, avail ) ) { 0311 emit infoMessage( i18n("Unable to determine free space in temporary folder '%1'.",pathToTest), MessageError ); 0312 d->error = true; 0313 canCopy = false; 0314 } 0315 else { 0316 if( avail < imageSpaceNeeded/1024 ) { 0317 emit infoMessage( i18n("Not enough space left in temporary folder."), MessageError ); 0318 d->error = true; 0319 canCopy = false; 0320 } 0321 } 0322 } 0323 0324 if( canCopy ) { 0325 if( K3b::isMounted( m_readerDevice ) ) { 0326 emit infoMessage( i18n("Unmounting source medium"), MessageInfo ); 0327 K3b::unmount( m_readerDevice ); 0328 } 0329 0330 d->overallSize = 0; 0331 0332 // now create some progress helper values 0333 for( K3b::Device::Toc::const_iterator it = d->toc.constBegin(); it != d->toc.constEnd(); ++it ) { 0334 d->overallSize += (*it).length().lba(); 0335 if( d->sessionSizes.isEmpty() || (*it).type() == K3b::Device::Track::TYPE_DATA ) 0336 d->sessionSizes.append( (*it).length().lba() ); 0337 else 0338 d->sessionSizes[0] += (*it).length().lba(); 0339 } 0340 0341 if( audio && !m_onlyCreateImages ) { 0342 if( m_copyCdText ) 0343 searchCdText(); 0344 else 0345 queryCddb(); 0346 } 0347 else 0348 startCopy(); 0349 } 0350 else { 0351 finishJob( false, true ); 0352 } 0353 } 0354 else { 0355 emit infoMessage( i18n("Unable to read Table of contents"), MessageError ); 0356 finishJob( false, true ); 0357 } 0358 } 0359 0360 0361 void K3b::CdCopyJob::searchCdText() 0362 { 0363 emit newSubTask( i18n("Searching CD-Text") ); 0364 0365 connect( K3b::Device::sendCommand( K3b::Device::DeviceHandler::CommandCdTextRaw, m_readerDevice ), 0366 SIGNAL(finished(K3b::Device::DeviceHandler*)), 0367 this, 0368 SLOT(slotCdTextReady(K3b::Device::DeviceHandler*)) ); 0369 } 0370 0371 0372 void K3b::CdCopyJob::slotCdTextReady( K3b::Device::DeviceHandler* dh ) 0373 { 0374 if( dh->success() ) { 0375 if( K3b::Device::CdText::checkCrc( dh->cdTextRaw() ) ) { 0376 K3b::Device::CdText cdt( dh->cdTextRaw() ); 0377 emit infoMessage( i18n("Found CD-Text (%1 - %2).",cdt.performer(),cdt.title()), MessageSuccess ); 0378 d->haveCdText = true; 0379 d->cdTextRaw = dh->cdTextRaw(); 0380 } 0381 else { 0382 emit infoMessage( i18n("Found corrupted CD-Text. Ignoring it."), MessageWarning ); 0383 d->haveCdText = false; 0384 } 0385 } 0386 else { 0387 emit infoMessage( i18n("No CD-Text found."), MessageInfo ); 0388 0389 d->haveCdText = false; 0390 } 0391 0392 queryCddb(); 0393 } 0394 0395 0396 void K3b::CdCopyJob::queryCddb() 0397 { 0398 emit newSubTask( i18n("Querying CDDB") ); 0399 0400 d->haveCddb = false; 0401 0402 if( !d->cddb ) { 0403 d->cddb = new KCDDB::Client(); 0404 d->cddb->setBlockingMode( false ); 0405 connect( d->cddb, SIGNAL(finished(KCDDB::Result)), 0406 this, SLOT(slotCddbQueryFinished(KCDDB::Result)) ); 0407 } 0408 0409 d->cddb->config().load(); 0410 d->cddb->lookup( K3b::CDDB::createTrackOffsetList( d->toc ) ); 0411 } 0412 0413 0414 void K3b::CdCopyJob::slotCddbQueryFinished( KCDDB::Result result ) 0415 { 0416 if( result == KCDDB::Success ) { 0417 d->cddbInfo = d->cddb->lookupResponse().first(); 0418 d->haveCddb = true; 0419 emit infoMessage( i18n("Found CDDB entry (%1 - %2).", 0420 d->cddbInfo.get( KCDDB::Artist ).toString(), 0421 d->cddbInfo.get( KCDDB::Title ).toString() ), 0422 MessageSuccess ); 0423 0424 // save the entry locally 0425 d->cddb->store( d->cddbInfo, K3b::CDDB::createTrackOffsetList( d->toc ) ); 0426 } 0427 else if ( result == KCDDB::MultipleRecordFound ) { 0428 KCDDB::CDInfoList results = d->cddb->lookupResponse(); 0429 int i = K3b::CDDB::MultiEntriesDialog::selectCddbEntry( results, qApp->activeWindow() ); 0430 if ( i >= 0 ) { 0431 d->haveCddb = true; 0432 d->cddbInfo = results[i]; 0433 0434 // save the entry locally 0435 d->cddb->store( d->cddbInfo, K3b::CDDB::createTrackOffsetList( d->toc ) ); 0436 } 0437 else { 0438 d->haveCddb = false; 0439 } 0440 } 0441 else if( result == KCDDB::NoRecordFound ) { 0442 emit infoMessage( i18n("No CDDB entry found."), MessageWarning ); 0443 } 0444 else { 0445 emit infoMessage( i18n("CDDB error (%1).", 0446 KCDDB::resultToString( result ) ), 0447 MessageError ); 0448 } 0449 0450 startCopy(); 0451 } 0452 0453 0454 void K3b::CdCopyJob::startCopy() 0455 { 0456 d->currentWrittenSession = d->currentReadSession = 1; 0457 d->doneCopies = 0; 0458 0459 if ( d->haveCdText && d->haveCddb ) { 0460 K3b::Device::CdText cdt( d->cdTextRaw ); 0461 if ( !questionYesNo( i18n( "Found CD-Text (%1 - %2) and CDDB (%3 - %4) entries. " 0462 "Which one should be used to generate the CD-Text on the new CD?", 0463 cdt.performer(), 0464 cdt.title(), 0465 d->cddbInfo.get( KCDDB::Artist ).toString(), 0466 d->cddbInfo.get( KCDDB::Title ).toString() ), 0467 i18n( "CD-Text" ), 0468 KGuiItem( i18n( "Use CD-Text data" ) ), 0469 KGuiItem( i18n( "Use CDDB entry" ) ) ) ) { 0470 d->haveCdText = false; 0471 } 0472 } 0473 0474 if( m_onTheFly && !m_onlyCreateImages ) { 0475 emit newSubTask( i18n("Preparing write process...") ); 0476 0477 if( writeNextSession() ) 0478 readNextSession(); 0479 else { 0480 finishJob( d->canceled, d->error ); 0481 } 0482 } 0483 else 0484 readNextSession(); 0485 } 0486 0487 0488 void K3b::CdCopyJob::cancel() 0489 { 0490 d->canceled = true; 0491 0492 if( d->writerRunning ) { 0493 // 0494 // we will handle cleanup in slotWriterFinished() 0495 // if we are writing onthefly the reader won't be able to write 0496 // anymore and will finish unsuccessfully, too 0497 // 0498 d->cdrecordWriter->cancel(); 0499 } 0500 else if( d->audioReaderRunning ) 0501 d->audioSessionReader->cancel(); 0502 else if( d->dataReaderRunning ) 0503 // d->readcdReader->cancel(); 0504 d->dataTrackReader->cancel(); 0505 } 0506 0507 0508 bool K3b::CdCopyJob::prepareImageFiles() 0509 { 0510 qDebug() << "(K3b::CdCopyJob) prepareImageFiles()"; 0511 0512 d->imageNames.clear(); 0513 d->infNames.clear(); 0514 d->deleteTempDir = false; 0515 0516 QFileInfo fi( m_tempPath ); 0517 0518 if( d->toc.count() > 1 || d->toc.contentType() == K3b::Device::AUDIO ) { 0519 // create a directory which contains all the images and inf and stuff 0520 // and save it in some cool structure 0521 0522 bool tempDirReady = false; 0523 if( !fi.isDir() ) { 0524 if( QFileInfo( m_tempPath.section( '/', 0, -2 ) ).isDir() ) { 0525 if( !QFile::exists( m_tempPath ) ) { 0526 QDir dir( m_tempPath.section( '/', 0, -2 ) ); 0527 dir.mkdir( m_tempPath.section( '/', -1 ) ); 0528 tempDirReady = true; 0529 } 0530 else 0531 m_tempPath = m_tempPath.section( '/', 0, -2 ); 0532 } 0533 else { 0534 emit infoMessage( i18n("Specified an unusable temporary path. Using default."), MessageWarning ); 0535 m_tempPath = K3b::defaultTempPath(); 0536 } 0537 } 0538 0539 // create temp dir 0540 if( !tempDirReady ) { 0541 QDir dir( m_tempPath ); 0542 m_tempPath = K3b::findUniqueFilePrefix( "k3bCdCopy", m_tempPath ); 0543 qDebug() << "(K3b::CdCopyJob) creating temp dir: " << m_tempPath; 0544 if( !dir.mkdir( m_tempPath ) ) { 0545 emit infoMessage( i18n("Unable to create temporary folder '%1'.",m_tempPath), MessageError ); 0546 return false; 0547 } 0548 d->deleteTempDir = true; 0549 } 0550 0551 m_tempPath = K3b::prepareDir( m_tempPath ); 0552 emit infoMessage( i18n("Using temporary folder %1.",m_tempPath), MessageInfo ); 0553 0554 // create temp filenames 0555 int i = 1; 0556 for( K3b::Device::Toc::const_iterator it = d->toc.constBegin(); it != d->toc.constEnd(); ++it ) { 0557 if( (*it).type() == K3b::Device::Track::TYPE_AUDIO ) { 0558 d->imageNames.append( m_tempPath + QString("Track%1.wav").arg(QString::number(i).rightJustified(2, '0')) ); 0559 d->infNames.append( m_tempPath + QString("Track%1.inf").arg(QString::number(i).rightJustified(2, '0')) ); 0560 } 0561 else 0562 d->imageNames.append( m_tempPath + QString("Track%1.iso").arg(QString::number(i).rightJustified(2, '0')) ); 0563 ++i; 0564 } 0565 0566 qDebug() << "(K3b::CdCopyJob) created image filenames:"; 0567 for( int i = 0; i < d->imageNames.count(); ++i ) 0568 qDebug() << "(K3b::CdCopyJob) " << d->imageNames[i]; 0569 0570 return true; 0571 } 0572 else { 0573 // we only need a single image file 0574 if( !fi.isFile() || 0575 questionYesNo( i18n("Do you want to overwrite %1?",m_tempPath), 0576 i18n("File Exists") ) ) { 0577 if( fi.isDir() ) 0578 m_tempPath = K3b::findTempFile( "iso", m_tempPath ); 0579 else if( !QFileInfo( m_tempPath.section( '/', 0, -2 ) ).isDir() ) { 0580 emit infoMessage( i18n("Specified an unusable temporary path. Using default."), MessageWarning ); 0581 m_tempPath = K3b::findTempFile( "iso" ); 0582 } 0583 // else the user specified a file in an existing dir 0584 0585 emit infoMessage( i18n("Writing image file to %1.",m_tempPath), MessageInfo ); 0586 } 0587 else 0588 return false; 0589 0590 d->imageNames.append( m_tempPath ); 0591 0592 return true; 0593 } 0594 } 0595 0596 0597 void K3b::CdCopyJob::readNextSession() 0598 { 0599 if( !m_onTheFly || m_onlyCreateImages ) { 0600 if( d->numSessions > 1 ) 0601 emit newTask( i18n("Reading Session %1",d->currentReadSession) ); 0602 else 0603 emit newTask( i18n("Reading Source Medium") ); 0604 0605 if( d->currentReadSession == 1 ) 0606 emit newSubTask( i18n("Reading track %1 of %2",QString::number(1),d->toc.count()) ); 0607 } 0608 0609 // there is only one situation where we need the audiosessionreader: 0610 // if the first session is an audio session. That means the first track 0611 // is an audio track 0612 if( d->currentReadSession == 1 && d->toc[0].type() == K3b::Device::Track::TYPE_AUDIO ) { 0613 if( !d->audioSessionReader ) { 0614 d->audioSessionReader = new K3b::AudioSessionReadingJob( this, this ); 0615 connect( d->audioSessionReader, SIGNAL(nextTrack(int,int)), 0616 this, SLOT(slotReadingNextTrack(int,int)) ); 0617 connectSubJob( d->audioSessionReader, 0618 SLOT(slotSessionReaderFinished(bool)), 0619 K3b::Job::DEFAULT_SIGNAL_CONNECTION, 0620 K3b::Job::DEFAULT_SIGNAL_CONNECTION, 0621 SLOT(slotReaderProgress(int)), 0622 SLOT(slotReaderSubProgress(int)) ); 0623 } 0624 0625 d->audioSessionReader->setDevice( m_readerDevice ); 0626 d->audioSessionReader->setToc( d->toc ); 0627 d->audioSessionReader->setParanoiaMode( m_paranoiaMode ); 0628 d->audioSessionReader->setReadRetries( m_audioReadRetries ); 0629 d->audioSessionReader->setNeverSkip( !m_ignoreAudioReadErrors ); 0630 if( m_onTheFly ) 0631 d->audioSessionReader->writeTo( d->cdrecordWriter->ioDevice() ); 0632 else 0633 d->audioSessionReader->setImageNames( d->imageNames ); // the audio tracks are always the first tracks 0634 0635 d->audioReaderRunning = true; 0636 d->audioSessionReader->start(); 0637 } 0638 else { 0639 if( !d->dataTrackReader ) { 0640 d->dataTrackReader = new K3b::DataTrackReader( this, this ); 0641 connect( d->dataTrackReader, SIGNAL(percent(int)), this, SLOT(slotReaderProgress(int)) ); 0642 connect( d->dataTrackReader, SIGNAL(processedSize(int,int)), this, SLOT(slotReaderProcessedSize(int,int)) ); 0643 connect( d->dataTrackReader, SIGNAL(finished(bool)), this, SLOT(slotSessionReaderFinished(bool)) ); 0644 connect( d->dataTrackReader, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) ); 0645 connect( d->dataTrackReader, SIGNAL(debuggingOutput(QString,QString)), 0646 this, SIGNAL(debuggingOutput(QString,QString)) ); 0647 } 0648 0649 d->dataTrackReader->setDevice( m_readerDevice ); 0650 d->dataTrackReader->setIgnoreErrors( m_ignoreDataReadErrors ); 0651 d->dataTrackReader->setNoCorrection( m_noCorrection ); 0652 d->dataTrackReader->setRetries( m_dataReadRetries ); 0653 if( m_onlyCreateImages ) 0654 d->dataTrackReader->setSectorSize( K3b::DataTrackReader::MODE1 ); 0655 else 0656 d->dataTrackReader->setSectorSize( K3b::DataTrackReader::AUTO ); 0657 0658 K3b::Device::Track* track = 0; 0659 int dataTrackIndex = 0; 0660 if( d->toc.contentType() == K3b::Device::MIXED ) { 0661 track = &d->toc[d->toc.count()-1]; 0662 dataTrackIndex = 0; 0663 } 0664 else { 0665 track = &d->toc[d->currentReadSession-1]; // only one track per session 0666 dataTrackIndex = d->currentReadSession-1; 0667 } 0668 0669 // HACK: if the track is TAO recorded cut the two run-out sectors 0670 if( d->dataSessionProbablyTAORecorded.count() > dataTrackIndex && 0671 d->dataSessionProbablyTAORecorded[dataTrackIndex] ) 0672 d->dataTrackReader->setSectorRange( track->firstSector(), track->lastSector() - 2 ); 0673 else 0674 d->dataTrackReader->setSectorRange( track->firstSector(), track->lastSector() ); 0675 0676 int trackNum = d->currentReadSession; 0677 if( d->toc.contentType() == K3b::Device::MIXED ) 0678 trackNum = d->toc.count(); 0679 0680 if( m_onTheFly ) 0681 d->dataTrackReader->writeTo( d->cdrecordWriter->ioDevice() ); 0682 else 0683 d->dataTrackReader->setImagePath( d->imageNames[trackNum-1] ); 0684 0685 d->dataReaderRunning = true; 0686 if( !m_onTheFly || m_onlyCreateImages ) 0687 slotReadingNextTrack( 1, 1 ); 0688 0689 d->dataTrackReader->start(); 0690 } 0691 } 0692 0693 0694 bool K3b::CdCopyJob::writeNextSession() 0695 { 0696 // we emit our own task since the cdrecord task is way too simple 0697 if( d->numSessions > 1 ) { 0698 if( m_simulate ) 0699 emit newTask( i18n("Simulating Session %1",d->currentWrittenSession) ); 0700 else if( m_copies > 1 ) 0701 emit newTask( i18n("Writing Copy %1 (Session %2)",d->doneCopies+1,d->currentWrittenSession) ); 0702 else 0703 emit newTask( i18n("Writing Copy (Session %1)",d->currentWrittenSession) ); 0704 } 0705 else { 0706 if( m_simulate ) 0707 emit newTask( i18n("Simulating") ); 0708 else if( m_copies > 1 ) 0709 emit newTask( i18n("Writing Copy %1",d->doneCopies+1) ); 0710 else 0711 emit newTask( i18n("Writing Copy") ); 0712 } 0713 0714 if ( d->currentWrittenSession == 1 ) { 0715 emit newSubTask( i18n("Waiting for media") ); 0716 0717 if( waitForMedium( m_writerDevice, 0718 K3b::Device::STATE_EMPTY, 0719 K3b::Device::MEDIA_WRITABLE_CD ) == Device::MEDIA_UNKNOWN ) { 0720 finishJob( true, false ); 0721 return false; 0722 } 0723 } 0724 0725 if( !d->cdrecordWriter ) { 0726 d->cdrecordWriter = new K3b::CdrecordWriter( m_writerDevice, this, this ); 0727 connect( d->cdrecordWriter, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) ); 0728 connect( d->cdrecordWriter, SIGNAL(percent(int)), this, SLOT(slotWriterProgress(int)) ); 0729 connect( d->cdrecordWriter, SIGNAL(processedSize(int,int)), this, SIGNAL(processedSize(int,int)) ); 0730 connect( d->cdrecordWriter, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) ); 0731 connect( d->cdrecordWriter, SIGNAL(processedSubSize(int,int)), this, SIGNAL(processedSubSize(int,int)) ); 0732 connect( d->cdrecordWriter, SIGNAL(nextTrack(int,int)), this, SLOT(slotWritingNextTrack(int,int)) ); 0733 connect( d->cdrecordWriter, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) ); 0734 connect( d->cdrecordWriter, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) ); 0735 connect( d->cdrecordWriter, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)), this, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)) ); 0736 connect( d->cdrecordWriter, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) ); 0737 // connect( d->cdrecordWriter, SIGNAL(newTask(QString)), this, SIGNAL(newTask(QString)) ); 0738 connect( d->cdrecordWriter, SIGNAL(newSubTask(QString)), this, SIGNAL(newSubTask(QString)) ); 0739 connect( d->cdrecordWriter, SIGNAL(debuggingOutput(QString,QString)), 0740 this, SIGNAL(debuggingOutput(QString,QString)) ); 0741 } 0742 0743 d->cdrecordWriter->setBurnDevice( m_writerDevice ); 0744 d->cdrecordWriter->clearArguments(); 0745 d->cdrecordWriter->setSimulate( m_simulate ); 0746 d->cdrecordWriter->setBurnSpeed( m_speed ); 0747 0748 0749 // create the cdrecord arguments 0750 if( d->currentWrittenSession == 1 && d->toc[0].type() == K3b::Device::Track::TYPE_AUDIO ) { 0751 // 0752 // Audio session 0753 // 0754 0755 0756 if( !d->infFileWriter ) 0757 d->infFileWriter = new K3b::InfFileWriter(); 0758 0759 // 0760 // create the inf files if not already done 0761 // 0762 if( d->infNames.isEmpty() || !QFile::exists( d->infNames[0] ) ) { 0763 0764 int trackNumber = 1; 0765 0766 for( K3b::Device::Toc::const_iterator it = d->toc.constBegin(); it != d->toc.constEnd(); ++it ) { 0767 const K3b::Device::Track& track = *it; 0768 0769 if( track.type() == K3b::Device::Track::TYPE_DATA ) 0770 break; 0771 0772 d->infFileWriter->setTrack( track ); 0773 d->infFileWriter->setTrackNumber( trackNumber ); 0774 0775 if( d->haveCddb ) { 0776 d->infFileWriter->setTrackTitle( d->cddbInfo.track( trackNumber-1 ).get( KCDDB::Title ).toString() ); 0777 d->infFileWriter->setTrackPerformer( d->cddbInfo.track( trackNumber-1 ).get( KCDDB::Artist ).toString() ); 0778 d->infFileWriter->setTrackMessage( d->cddbInfo.track( trackNumber-1 ).get( KCDDB::Comment ).toString() ); 0779 0780 d->infFileWriter->setAlbumTitle( d->cddbInfo.get( KCDDB::Title ).toString() ); 0781 d->infFileWriter->setAlbumPerformer( d->cddbInfo.get( KCDDB::Artist ).toString() ); 0782 } 0783 0784 if( m_onTheFly ) { 0785 0786 d->infFileWriter->setBigEndian( true ); 0787 0788 // we let KTempFile choose a temp file but delete it on our own 0789 // the same way we delete them when writing with images 0790 // It is important that the files have the ending inf because 0791 // cdrecord only checks this 0792 QTemporaryFile tmp( "XXXXXX.inf" ); 0793 tmp.setAutoRemove( false ); 0794 tmp.open(); 0795 d->infNames.append( tmp.fileName() ); 0796 QTextStream stream( &tmp ); 0797 bool success = d->infFileWriter->save( stream ); 0798 tmp.close(); 0799 if( !success ) 0800 return false; 0801 } 0802 else { 0803 d->infFileWriter->setBigEndian( false ); 0804 0805 if( !d->infFileWriter->save( d->infNames[trackNumber-1] ) ) 0806 return false; 0807 } 0808 0809 ++trackNumber; 0810 } 0811 } 0812 0813 // 0814 // the inf files are ready and named correctly when writing with images 0815 // 0816 K3b::WritingMode usedWritingMode = m_writingMode; 0817 if( usedWritingMode == K3b::WritingModeAuto ) { 0818 // 0819 // there are a lot of writers out there which produce coasters 0820 // in dao mode if the CD contains pregaps of length 0 (or maybe already != 2 secs?) 0821 // 0822 bool zeroPregap = false; 0823 if( d->numSessions == 1 ) { 0824 for( K3b::Device::Toc::const_iterator it = d->toc.constBegin(); it != d->toc.constEnd(); ++it ) { 0825 const K3b::Device::Track& track = *it; 0826 if( track.index0() == 0 ) { 0827 ++it; 0828 if( it != d->toc.constEnd() ) 0829 zeroPregap = true; 0830 --it; 0831 } 0832 } 0833 } 0834 0835 if( zeroPregap && m_writerDevice->supportsRawWriting() ) { 0836 if( d->numSessions == 1 ) 0837 usedWritingMode = K3b::WritingModeRaw; 0838 else 0839 usedWritingMode = K3b::WritingModeTao; 0840 } 0841 else if( m_writerDevice->dao() ) 0842 usedWritingMode = K3b::WritingModeSao; 0843 else if( m_writerDevice->supportsRawWriting() ) 0844 usedWritingMode = K3b::WritingModeRaw; 0845 else 0846 usedWritingMode = K3b::WritingModeTao; 0847 } 0848 d->cdrecordWriter->setWritingMode( usedWritingMode ); 0849 0850 d->cdrecordWriter->setMulti( d->numSessions > 1 ); 0851 0852 if( d->haveCddb || d->haveCdText ) { 0853 if( usedWritingMode == K3b::WritingModeTao ) { 0854 emit infoMessage( i18n("It is not possible to write CD-Text in TAO mode."), MessageWarning ); 0855 } 0856 else if( d->haveCdText ) { 0857 // use the raw CDTEXT data 0858 d->cdrecordWriter->setRawCdText( d->cdTextRaw ); 0859 } 0860 else { 0861 // make sure the writer job does not create raw cdtext 0862 d->cdrecordWriter->setRawCdText( QByteArray() ); 0863 // cdrecord will use the cdtext data in the inf files 0864 d->cdrecordWriter->addArgument( "-text" ); 0865 } 0866 } 0867 0868 d->cdrecordWriter->addArgument( "-useinfo" ); 0869 0870 // 0871 // add all the audio tracks 0872 // 0873 d->cdrecordWriter->addArgument( "-audio" )->addArgument( "-shorttrack" ); 0874 0875 for( int i = 0; i < d->infNames.count(); ++i ) { 0876 if( m_onTheFly ) 0877 d->cdrecordWriter->addArgument( d->infNames[i] ); 0878 else 0879 d->cdrecordWriter->addArgument( d->imageNames[i] ); 0880 } 0881 } 0882 else { 0883 // 0884 // Data Session 0885 // 0886 K3b::Device::Track* track = 0; 0887 int dataTrackIndex = 0; 0888 if( d->toc.contentType() == K3b::Device::MIXED ) { 0889 track = &d->toc[d->toc.count()-1]; 0890 dataTrackIndex = 0; 0891 } 0892 else { 0893 track = &d->toc[d->currentWrittenSession-1]; 0894 dataTrackIndex = d->currentWrittenSession-1; 0895 } 0896 0897 bool multi = d->doNotCloseLastSession || (d->numSessions > 1 && d->currentWrittenSession < d->toc.count()); 0898 K3b::WritingMode usedWritingMode = m_writingMode; 0899 if( usedWritingMode == K3b::WritingModeAuto ) { 0900 // at least the NEC3540a does write 2056 byte sectors only in tao mode. Same for LG4040b 0901 // since writing data tracks in TAO mode is no loss let's default to TAO in the case of 2056 byte 0902 // sectors (which is when writing xa form1 sectors here) 0903 if( m_writerDevice->dao() && 0904 d->toc.count() == 1 && 0905 !multi && 0906 track->mode() == K3b::Device::Track::MODE1 ) 0907 usedWritingMode = K3b::WritingModeSao; 0908 else 0909 usedWritingMode = K3b::WritingModeTao; 0910 } 0911 d->cdrecordWriter->setWritingMode( usedWritingMode ); 0912 0913 // 0914 // all but the last session of a multisession disk are written in multi mode 0915 // and every data track has it's own session which we forced above 0916 // 0917 d->cdrecordWriter->setMulti( multi ); 0918 0919 // just to let the reader init 0920 if( m_onTheFly ) 0921 d->cdrecordWriter->addArgument( "-waiti" ); 0922 0923 if( track->mode() == K3b::Device::Track::MODE1 ) 0924 d->cdrecordWriter->addArgument( "-data" ); 0925 else if( track->mode() == K3b::Device::Track::XA_FORM1 ) 0926 d->cdrecordWriter->addArgument( "-xa1" ); 0927 else 0928 d->cdrecordWriter->addArgument( "-xamix" ); 0929 0930 if( m_onTheFly ) { 0931 // HACK: if the track is TAO recorded cut the two run-out sectors 0932 unsigned long trackLen = track->length().lba(); 0933 if( d->dataSessionProbablyTAORecorded.count() > dataTrackIndex && 0934 d->dataSessionProbablyTAORecorded[dataTrackIndex] ) 0935 trackLen -= 2; 0936 0937 if( track->mode() == K3b::Device::Track::MODE1 ) 0938 trackLen = trackLen * 2048; 0939 else if( track->mode() == K3b::Device::Track::XA_FORM1 ) 0940 trackLen = trackLen * 2056; // see k3bdatatrackreader.h 0941 else 0942 trackLen = trackLen * 2332; // see k3bdatatrackreader.h 0943 d->cdrecordWriter->addArgument( QString("-tsize=%1").arg(trackLen) )->addArgument("-"); 0944 } 0945 else if( d->toc.contentType() == K3b::Device::MIXED ) 0946 d->cdrecordWriter->addArgument( d->imageNames[d->toc.count()-1] ); 0947 else 0948 d->cdrecordWriter->addArgument( d->imageNames[d->currentWrittenSession-1] ); 0949 0950 // clear cd text from previous sessions 0951 d->cdrecordWriter->setRawCdText( QByteArray() ); 0952 } 0953 0954 0955 // 0956 // Finally start the writer 0957 // 0958 emit burning(true); 0959 d->writerRunning = true; 0960 d->cdrecordWriter->start(); 0961 0962 return true; 0963 } 0964 0965 0966 // both the readcdreader and the audiosessionreader are connected to this slot 0967 void K3b::CdCopyJob::slotSessionReaderFinished( bool success ) 0968 { 0969 d->audioReaderRunning = d->dataReaderRunning = false; 0970 0971 if( success ) { 0972 if( d->numSessions > 1 ) 0973 emit infoMessage( i18n("Successfully read session %1.",d->currentReadSession), MessageSuccess ); 0974 else 0975 emit infoMessage( i18n("Successfully read source disk."), MessageSuccess ); 0976 0977 if( !m_onTheFly ) { 0978 if( d->numSessions > d->currentReadSession ) { 0979 d->currentReadSession++; 0980 readNextSession(); 0981 } 0982 else { 0983 d->readingSuccessful = true; 0984 if( !m_onlyCreateImages ) { 0985 if( m_readerDevice == m_writerDevice ) { 0986 // eject the media (we do this blocking to know if it worked 0987 // because if it did not it might happen that k3b overwrites a CD-RW 0988 // source) 0989 if( !K3b::eject( m_readerDevice ) ) { 0990 blockingInformation( i18n("K3b was unable to eject the source disk. Please do so manually.") ); 0991 } 0992 } 0993 0994 if( !writeNextSession() ) { 0995 // nothing is running here... 0996 finishJob( d->canceled, d->error ); 0997 } 0998 } 0999 else { 1000 finishJob( false, false ); 1001 } 1002 } 1003 } 1004 } 1005 else { 1006 if( !d->canceled ) { 1007 emit infoMessage( i18n("Error while reading session %1.",d->currentReadSession), MessageError ); 1008 if( m_onTheFly ) 1009 d->cdrecordWriter->setSourceUnreadable(true); 1010 } 1011 1012 finishJob( d->canceled, !d->canceled ); 1013 } 1014 } 1015 1016 1017 void K3b::CdCopyJob::slotWriterFinished( bool success ) 1018 { 1019 emit burning(false); 1020 1021 d->writerRunning = false; 1022 1023 if( success ) { 1024 // 1025 // if this was the last written session we need to reset d->currentWrittenSession 1026 // and start a new writing if more copies are wanted 1027 // 1028 1029 if( d->currentWrittenSession < d->numSessions ) { 1030 d->currentWrittenSession++; 1031 d->currentReadSession++; 1032 1033 // many drives need to reload the medium to return to a proper state 1034 if ( m_writerDevice->diskInfo().numSessions() < ( int )d->currentWrittenSession ) { 1035 emit infoMessage( i18n( "Need to reload medium to return to proper state." ), MessageInfo ); 1036 emit newSubTask( i18n("Reloading the medium") ); 1037 connect( K3b::Device::reload( m_writerDevice ), SIGNAL(finished(K3b::Device::DeviceHandler*)), 1038 this, SLOT(slotMediaReloadedForNextSession(K3b::Device::DeviceHandler*)) ); 1039 } 1040 1041 if( !writeNextSession() ) { 1042 // nothing is running here... 1043 finishJob( d->canceled, d->error ); 1044 } 1045 else if( m_onTheFly ) 1046 readNextSession(); 1047 } 1048 else { 1049 d->doneCopies++; 1050 1051 if( !m_simulate && d->doneCopies < m_copies ) { 1052 // start next copy 1053 if( !K3b::eject( m_writerDevice ) ) { 1054 blockingInformation( i18n("K3b was unable to eject the written disk. Please do so manually.") ); 1055 } 1056 1057 d->currentWrittenSession = 1; 1058 d->currentReadSession = 1; 1059 if( writeNextSession() ) { 1060 if( m_onTheFly ) 1061 readNextSession(); 1062 } 1063 else { 1064 // nothing running here... 1065 finishJob( d->canceled, d->error ); 1066 } 1067 } 1068 else { 1069 if ( k3bcore->globalSettings()->ejectMedia() ) { 1070 K3b::Device::eject( m_writerDevice ); 1071 } 1072 finishJob( false, false ); 1073 } 1074 } 1075 } 1076 else { 1077 // 1078 // If we are writing on the fly the reader will also stop when it is not able to write anymore 1079 // The error handling will be done only here in that case 1080 // 1081 1082 // the K3b::CdrecordWriter emitted an error message 1083 1084 finishJob( d->canceled, !d->canceled ); 1085 } 1086 } 1087 1088 1089 void K3b::CdCopyJob::slotMediaReloadedForNextSession( K3b::Device::DeviceHandler* dh ) 1090 { 1091 if( !dh->success() ) 1092 blockingInformation( i18n("Please reload the medium and press 'OK'"), 1093 i18n("Failed to reload the medium") ); 1094 1095 if( !writeNextSession() ) { 1096 // nothing is running here... 1097 finishJob( d->canceled, d->error ); 1098 } 1099 else if( m_onTheFly ) 1100 readNextSession(); 1101 } 1102 1103 1104 void K3b::CdCopyJob::cleanup() 1105 { 1106 if( m_onTheFly || !m_keepImage || ((d->canceled || d->error) && !d->readingSuccessful) ) { 1107 emit infoMessage( i18n("Removing temporary files."), MessageInfo ); 1108 for( QStringList::iterator it = d->infNames.begin(); it != d->infNames.end(); ++it ) 1109 QFile::remove( *it ); 1110 } 1111 1112 if( !m_onTheFly && (!m_keepImage || ((d->canceled || d->error) && !d->readingSuccessful)) ) { 1113 emit infoMessage( i18n("Removing image files."), MessageInfo ); 1114 for( QStringList::iterator it = d->imageNames.begin(); it != d->imageNames.end(); ++it ) 1115 QFile::remove( *it ); 1116 1117 // remove the tempdir created in prepareImageFiles() 1118 if( d->deleteTempDir ) { 1119 KIO::del( QUrl::fromLocalFile( m_tempPath ), KIO::HideProgressInfo )->exec(); 1120 d->deleteTempDir = false; 1121 } 1122 } 1123 } 1124 1125 1126 void K3b::CdCopyJob::slotReaderProgress( int p ) 1127 { 1128 if( !m_onTheFly || m_onlyCreateImages ) { 1129 int bigParts = ( m_onlyCreateImages ? 1 : (m_simulate ? 2 : m_copies + 1 ) ); 1130 double done = (double)p * (double)d->sessionSizes[d->currentReadSession-1] / 100.0; 1131 for( int i = 0; i < d->currentReadSession-1; ++i ) 1132 done += (double)d->sessionSizes[i]; 1133 emit percent( (int)(100.0*done/(double)d->overallSize/(double)bigParts) ); 1134 1135 if( d->dataReaderRunning ) 1136 emit subPercent(p); 1137 } 1138 } 1139 1140 1141 void K3b::CdCopyJob::slotReaderSubProgress( int p ) 1142 { 1143 // only if reading an audiosession 1144 if( !m_onTheFly || m_onlyCreateImages ) { 1145 emit subPercent( p ); 1146 } 1147 } 1148 1149 1150 void K3b::CdCopyJob::slotReaderProcessedSize( int p, int pp ) 1151 { 1152 if( !m_onTheFly ) 1153 emit processedSubSize( p, pp ); 1154 } 1155 1156 1157 void K3b::CdCopyJob::slotWriterProgress( int p ) 1158 { 1159 int bigParts = ( m_simulate ? 1 : m_copies ) + ( m_onTheFly ? 0 : 1 ); 1160 long done = ( m_onTheFly ? d->doneCopies : d->doneCopies+1 ) * d->overallSize 1161 + (p * d->sessionSizes[d->currentWrittenSession-1] / 100); 1162 for( int i = 0; i < d->currentWrittenSession-1; ++i ) 1163 done += d->sessionSizes[i]; 1164 emit percent( 100*done/d->overallSize/bigParts ); 1165 } 1166 1167 1168 void K3b::CdCopyJob::slotWritingNextTrack( int t, int tt ) 1169 { 1170 if( d->toc.contentType() == K3b::Device::MIXED ) { 1171 if( d->currentWrittenSession == 1 ) 1172 emit newSubTask( i18n("Writing track %1 of %2",t,d->toc.count()) ); 1173 else 1174 emit newSubTask( i18n("Writing track %1 of %2",d->toc.count(),d->toc.count()) ); 1175 } 1176 else if( d->numSessions > 1 ) 1177 emit newSubTask( i18n("Writing track %1 of %2",d->currentWrittenSession,d->toc.count()) ); 1178 else 1179 emit newSubTask( i18n("Writing track %1 of %2",t,tt) ); 1180 } 1181 1182 1183 void K3b::CdCopyJob::slotReadingNextTrack( int t, int ) 1184 { 1185 if( !m_onTheFly || m_onlyCreateImages ) { 1186 int track = t; 1187 if( d->audioReaderRunning ) 1188 track = t; 1189 else if( d->toc.contentType() == K3b::Device::MIXED ) 1190 track = d->toc.count(); 1191 else 1192 track = d->currentReadSession; 1193 1194 emit newSubTask( i18n("Reading track %1 of %2",track,d->toc.count()) ); 1195 } 1196 } 1197 1198 1199 QString K3b::CdCopyJob::jobDescription() const 1200 { 1201 if( m_onlyCreateImages ) { 1202 return i18n("Creating CD Image"); 1203 } 1204 else if( m_simulate ) { 1205 if( m_onTheFly ) 1206 return i18n("Simulating CD Copy On-The-Fly"); 1207 else 1208 return i18n("Simulating CD Copy"); 1209 } 1210 else { 1211 if( m_onTheFly ) 1212 return i18n("Copying CD On-The-Fly"); 1213 else 1214 return i18n("Copying CD"); 1215 } 1216 } 1217 1218 1219 QString K3b::CdCopyJob::jobDetails() const 1220 { 1221 return i18np("Creating 1 copy", 1222 "Creating %1 copies", 1223 (m_simulate||m_onlyCreateImages) ? 1 : m_copies ); 1224 } 1225 1226 1227 QString K3b::CdCopyJob::jobSource() const 1228 { 1229 if( Device::Device* device = reader() ) 1230 return device->vendor() + ' ' + device->description(); 1231 else 1232 return QString(); 1233 } 1234 1235 1236 QString K3b::CdCopyJob::jobTarget() const 1237 { 1238 if( Device::Device* device = writer() ) 1239 return device->vendor() + ' ' + device->description(); 1240 else 1241 return m_tempPath; 1242 } 1243 1244 1245 void K3b::CdCopyJob::finishJob( bool c, bool e ) 1246 { 1247 if( d->running ) { 1248 if( c ) { 1249 d->canceled = true; 1250 emit canceled(); 1251 } 1252 if( e ) 1253 d->error = true; 1254 1255 cleanup(); 1256 1257 d->running = false; 1258 1259 jobFinished( !(c||e) ); 1260 } 1261 } 1262 1263 #include "moc_k3bcdcopyjob.cpp"