|
|
|
/*
|
|
|
|
*
|
|
|
|
* $Id: k3bcdrecordwriter.cpp 690529 2007-07-21 10:51:47Z trueg $
|
|
|
|
* Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This file is part of the K3b project.
|
|
|
|
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
* See the file "COPYING" for the exact licensing terms.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include "k3bcdrecordwriter.h"
|
|
|
|
|
|
|
|
#include <k3bcore.h>
|
|
|
|
#include <k3bexternalbinmanager.h>
|
|
|
|
#include <k3bprocess.h>
|
|
|
|
#include <k3bdevice.h>
|
|
|
|
#include <k3bdevicemanager.h>
|
|
|
|
#include <k3bdevicehandler.h>
|
|
|
|
#include <k3bglobals.h>
|
|
|
|
#include <k3bthroughputestimator.h>
|
|
|
|
#include <k3bglobalsettings.h>
|
|
|
|
#include <k3btempfile.h>
|
|
|
|
|
|
|
|
#include <tqstring.h>
|
|
|
|
#include <tqstringlist.h>
|
|
|
|
#include <tqurl.h>
|
|
|
|
#include <tqvaluelist.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
|
|
|
|
#include <tdelocale.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <tdeglobal.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class K3bCdrecordWriter::Private
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Private()
|
|
|
|
: cdTextFile(0) {
|
|
|
|
}
|
|
|
|
|
|
|
|
K3bThroughputEstimator* speedEst;
|
|
|
|
bool canceled;
|
|
|
|
bool usingBurnfree;
|
|
|
|
int usedSpeed;
|
|
|
|
|
|
|
|
struct Track {
|
|
|
|
int size;
|
|
|
|
bool audio;
|
|
|
|
};
|
|
|
|
|
|
|
|
TQValueList<Track> tracks;
|
|
|
|
|
|
|
|
KTempFile* cdTextFile;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
K3bCdrecordWriter::K3bCdrecordWriter( K3bDevice::Device* dev, K3bJobHandler* hdl,
|
|
|
|
TQObject* parent, const char* name )
|
|
|
|
: K3bAbstractWriter( dev, hdl, parent, name ),
|
|
|
|
m_clone(false),
|
|
|
|
m_cue(false),
|
|
|
|
m_forceNoEject(false)
|
|
|
|
{
|
|
|
|
d = new Private();
|
|
|
|
d->speedEst = new K3bThroughputEstimator( this );
|
|
|
|
connect( d->speedEst, TQT_SIGNAL(throughput(int)),
|
|
|
|
this, TQT_SLOT(slotThroughput(int)) );
|
|
|
|
|
|
|
|
m_process = 0;
|
|
|
|
m_writingMode = K3b::TAO;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bCdrecordWriter::~K3bCdrecordWriter()
|
|
|
|
{
|
|
|
|
delete d->cdTextFile;
|
|
|
|
delete d;
|
|
|
|
delete m_process;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bCdrecordWriter::active() const
|
|
|
|
{
|
|
|
|
return ( m_process && m_process->isRunning() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int K3bCdrecordWriter::fd() const
|
|
|
|
{
|
|
|
|
if( m_process )
|
|
|
|
return m_process->stdinFd();
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::setDao( bool b )
|
|
|
|
{
|
|
|
|
m_writingMode = ( b ? K3b::DAO : K3b::TAO );
|
|
|
|
}
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::setCueFile( const TQString& s)
|
|
|
|
{
|
|
|
|
m_cue = true;
|
|
|
|
m_cueFile = s;
|
|
|
|
|
|
|
|
// cuefile only works in DAO mode
|
|
|
|
setWritingMode( K3b::DAO );
|
|
|
|
}
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::setClone( bool b )
|
|
|
|
{
|
|
|
|
m_clone = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::setWritingMode( int mode )
|
|
|
|
{
|
|
|
|
if( mode == K3b::DAO ||
|
|
|
|
mode == K3b::TAO ||
|
|
|
|
mode == K3b::RAW )
|
|
|
|
m_writingMode = mode;
|
|
|
|
else
|
|
|
|
kdError() << "(K3bCdrecordWriter) wrong writing mode: " << mode << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::prepareProcess()
|
|
|
|
{
|
|
|
|
if( m_process ) delete m_process; // tdelibs want this!
|
|
|
|
m_process = new K3bProcess();
|
|
|
|
m_process->setRunPrivileged(true);
|
|
|
|
// m_process->setPriority( TDEProcess::PrioHighest );
|
|
|
|
m_process->setSplitStdout(true);
|
|
|
|
m_process->setSuppressEmptyLines(true);
|
|
|
|
m_process->setRawStdin(true); // we only use stdin when writing on-the-fly
|
|
|
|
connect( m_process, TQT_SIGNAL(stdoutLine(const TQString&)), this, TQT_SLOT(slotStdLine(const TQString&)) );
|
|
|
|
connect( m_process, TQT_SIGNAL(stderrLine(const TQString&)), this, TQT_SLOT(slotStdLine(const TQString&)) );
|
|
|
|
connect( m_process, TQT_SIGNAL(processExited(TDEProcess*)), this, TQT_SLOT(slotProcessExited(TDEProcess*)) );
|
|
|
|
|
|
|
|
m_cdrecordBinObject = k3bcore->externalBinManager()->binObject("cdrecord");
|
|
|
|
|
|
|
|
if( !m_cdrecordBinObject )
|
|
|
|
return;
|
|
|
|
|
|
|
|
*m_process << m_cdrecordBinObject;
|
|
|
|
|
|
|
|
// display progress
|
|
|
|
*m_process << "-v";
|
|
|
|
|
|
|
|
if( m_cdrecordBinObject->hasFeature( "gracetime") )
|
|
|
|
*m_process << "gracetime=2"; // 2 is the lowest allowed value (Joerg, why do you do this to us?)
|
|
|
|
|
|
|
|
// Again we assume the device to be set!
|
|
|
|
*m_process << TQString("dev=%1").arg(K3b::externalBinDeviceParameter(burnDevice(), m_cdrecordBinObject));
|
|
|
|
|
|
|
|
d->usedSpeed = burnSpeed();
|
|
|
|
if( d->usedSpeed == 0 ) {
|
|
|
|
// try to determine the writeSpeed
|
|
|
|
// if it fails determineMaximalWriteSpeed() will return 0 and
|
|
|
|
// the choice is left to cdrecord
|
|
|
|
d->usedSpeed = burnDevice()->determineMaximalWriteSpeed();
|
|
|
|
}
|
|
|
|
d->usedSpeed /= 175;
|
|
|
|
if( d->usedSpeed != 0 )
|
|
|
|
*m_process << TQString("speed=%1").arg(d->usedSpeed);
|
|
|
|
|
|
|
|
if( m_writingMode == K3b::DAO || m_cue ) {
|
|
|
|
if( burnDevice()->dao() )
|
|
|
|
*m_process << "-dao";
|
|
|
|
else {
|
|
|
|
if( m_cdrecordBinObject->hasFeature( "tao" ) )
|
|
|
|
*m_process << "-tao";
|
|
|
|
emit infoMessage( i18n("Writer does not support disk at once (DAO) recording"), WARNING );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( m_writingMode == K3b::RAW ) {
|
|
|
|
if( burnDevice()->supportsWritingMode( K3bDevice::RAW_R96R ) )
|
|
|
|
*m_process << "-raw96r";
|
|
|
|
else if( burnDevice()->supportsWritingMode( K3bDevice::RAW_R16 ) )
|
|
|
|
*m_process << "-raw16";
|
|
|
|
else if( burnDevice()->supportsWritingMode( K3bDevice::RAW_R96P ) )
|
|
|
|
*m_process << "-raw96p";
|
|
|
|
else {
|
|
|
|
emit infoMessage( i18n("Writer does not support raw writing."), WARNING );
|
|
|
|
if( m_cdrecordBinObject->hasFeature( "tao" ) )
|
|
|
|
*m_process << "-tao";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( m_cdrecordBinObject->hasFeature( "tao" ) )
|
|
|
|
*m_process << "-tao";
|
|
|
|
|
|
|
|
if( simulate() )
|
|
|
|
*m_process << "-dummy";
|
|
|
|
|
|
|
|
d->usingBurnfree = false;
|
|
|
|
if( k3bcore->globalSettings()->burnfree() ) {
|
|
|
|
if( burnDevice()->burnproof() ) {
|
|
|
|
|
|
|
|
d->usingBurnfree = true;
|
|
|
|
|
|
|
|
// with cdrecord 1.11a02 burnproof was renamed to burnfree
|
|
|
|
if( m_cdrecordBinObject->hasFeature( "burnproof" ) )
|
|
|
|
*m_process << "driveropts=burnproof";
|
|
|
|
else
|
|
|
|
*m_process << "driveropts=burnfree";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
emit infoMessage( i18n("Writer does not support buffer underrun free recording (Burnfree)"), WARNING );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( k3bcore->globalSettings()->force() ) {
|
|
|
|
*m_process << "-force";
|
|
|
|
emit infoMessage( i18n("'Force unsafe operations' enabled."), WARNING );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( m_cue ) {
|
|
|
|
m_process->setWorkingDirectory(TQUrl(m_cueFile).dirPath());
|
|
|
|
*m_process << TQString("cuefile=%1").arg( m_cueFile );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( m_clone )
|
|
|
|
*m_process << "-clone";
|
|
|
|
|
|
|
|
if( m_rawCdText.size() > 0 ) {
|
|
|
|
delete d->cdTextFile;
|
|
|
|
d->cdTextFile = new K3bTempFile( TQString(), ".dat" );
|
|
|
|
d->cdTextFile->setAutoDelete(true);
|
|
|
|
d->cdTextFile->file()->writeBlock( m_rawCdText );
|
|
|
|
d->cdTextFile->close();
|
|
|
|
|
|
|
|
*m_process << "textfile=" + d->cdTextFile->name();
|
|
|
|
}
|
|
|
|
|
|
|
|
if( k3bcore->globalSettings()->ejectMedia() &&
|
|
|
|
!m_forceNoEject )
|
|
|
|
*m_process << "-eject";
|
|
|
|
|
|
|
|
bool manualBufferSize = k3bcore->globalSettings()->useManualBufferSize();
|
|
|
|
if( manualBufferSize ) {
|
|
|
|
*m_process << TQString("fs=%1m").arg( k3bcore->globalSettings()->bufferSize() );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool overburn = k3bcore->globalSettings()->overburn();
|
|
|
|
if( overburn )
|
|
|
|
if( m_cdrecordBinObject->hasFeature("overburn") )
|
|
|
|
*m_process << "-overburn";
|
|
|
|
else
|
|
|
|
emit infoMessage( i18n("Cdrecord %1 does not support overburning.").arg(m_cdrecordBinObject->version), WARNING );
|
|
|
|
|
|
|
|
// additional user parameters from config
|
|
|
|
const TQStringList& params = m_cdrecordBinObject->userParameters();
|
|
|
|
for( TQStringList::const_iterator it = params.begin(); it != params.end(); ++it )
|
|
|
|
*m_process << *it;
|
|
|
|
|
|
|
|
// add the user parameters
|
|
|
|
for( TQStringList::const_iterator it = m_arguments.begin(); it != m_arguments.end(); ++it )
|
|
|
|
*m_process << *it;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bCdrecordWriter* K3bCdrecordWriter::addArgument( const TQString& arg )
|
|
|
|
{
|
|
|
|
m_arguments.append( arg );
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::clearArguments()
|
|
|
|
{
|
|
|
|
m_arguments.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::start()
|
|
|
|
{
|
|
|
|
jobStarted();
|
|
|
|
|
|
|
|
d->canceled = false;
|
|
|
|
d->speedEst->reset();
|
|
|
|
|
|
|
|
prepareProcess();
|
|
|
|
|
|
|
|
if( !m_cdrecordBinObject ) {
|
|
|
|
emit infoMessage( i18n("Could not find %1 executable.").arg("cdrecord"), ERROR );
|
|
|
|
jobFinished(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit debuggingOutput( "Used versions", "cdrecord: " + m_cdrecordBinObject->version );
|
|
|
|
|
|
|
|
if( !m_cdrecordBinObject->copyright.isEmpty() )
|
|
|
|
emit infoMessage( i18n("Using %1 %2 - Copyright (C) %3")
|
|
|
|
.arg(m_cdrecordBinObject->hasFeature( "wodim" ) ? "Wodim" : "Cdrecord" )
|
|
|
|
.arg(m_cdrecordBinObject->version)
|
|
|
|
.arg(m_cdrecordBinObject->copyright), INFO );
|
|
|
|
|
|
|
|
|
|
|
|
kdDebug() << "***** " << m_cdrecordBinObject->name() << " parameters:\n";
|
|
|
|
const TQValueList<TQCString>& args = m_process->args();
|
|
|
|
TQString s;
|
|
|
|
for( TQValueList<TQCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
|
|
|
|
s += *it + " ";
|
|
|
|
}
|
|
|
|
kdDebug() << s << flush << endl;
|
|
|
|
emit debuggingOutput( m_cdrecordBinObject->name() + " command:", s);
|
|
|
|
|
|
|
|
m_currentTrack = 0;
|
|
|
|
m_cdrecordError = UNKNOWN;
|
|
|
|
m_totalTracksParsed = false;
|
|
|
|
m_alreadyWritten = 0;
|
|
|
|
d->tracks.clear();
|
|
|
|
m_totalSize = 0;
|
|
|
|
|
|
|
|
emit newSubTask( i18n("Preparing write process...") );
|
|
|
|
|
|
|
|
// FIXME: check the return value
|
|
|
|
if( K3b::isMounted( burnDevice() ) ) {
|
|
|
|
emit infoMessage( i18n("Unmounting medium"), INFO );
|
|
|
|
K3b::unmount( burnDevice() );
|
|
|
|
}
|
|
|
|
|
|
|
|
// block the device (including certain checks)
|
|
|
|
k3bcore->blockDevice( burnDevice() );
|
|
|
|
|
|
|
|
// lock the device for good in this process since it will
|
|
|
|
// be opened in the growisofs process
|
|
|
|
burnDevice()->close();
|
|
|
|
burnDevice()->usageLock();
|
|
|
|
|
|
|
|
if( !m_process->start( TDEProcess::NotifyOnExit, TDEProcess::All ) ) {
|
|
|
|
// something went wrong when starting the program
|
|
|
|
// it "should" be the executable
|
|
|
|
kdDebug() << "(K3bCdrecordWriter) could not start " << m_cdrecordBinObject->name() << endl;
|
|
|
|
emit infoMessage( i18n("Could not start %1.").arg(m_cdrecordBinObject->name()), K3bJob::ERROR );
|
|
|
|
jobFinished(false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if( simulate() ) {
|
|
|
|
emit newTask( i18n("Simulating") );
|
|
|
|
emit infoMessage( i18n("Starting %1 simulation at %2x speed...")
|
|
|
|
.arg(K3b::writingModeString(m_writingMode))
|
|
|
|
.arg(d->usedSpeed),
|
|
|
|
K3bJob::INFO );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
emit newTask( i18n("Writing") );
|
|
|
|
emit infoMessage( i18n("Starting %1 writing at %2x speed...")
|
|
|
|
.arg(K3b::writingModeString(m_writingMode))
|
|
|
|
.arg(d->usedSpeed),
|
|
|
|
K3bJob::INFO );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::cancel()
|
|
|
|
{
|
|
|
|
if( active() ) {
|
|
|
|
d->canceled = true;
|
|
|
|
if( m_process && m_process->isRunning() )
|
|
|
|
m_process->kill();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::slotStdLine( const TQString& line )
|
|
|
|
{
|
|
|
|
static TQRegExp s_burnfreeCounterRx( "^BURN\\-Free\\swas\\s(\\d+)\\stimes\\sused" );
|
|
|
|
static TQRegExp s_burnfreeCounterRxPredict( "^Total\\sof\\s(\\d+)\\s\\spossible\\sbuffer\\sunderruns\\spredicted" );
|
|
|
|
|
|
|
|
// tracknumber: cap(1)
|
|
|
|
// done: cap(2)
|
|
|
|
// complete: cap(3)
|
|
|
|
// fifo: cap(4) (it seems as if some patched cdrecord versions do not emit the fifo info but only the buf... :(
|
|
|
|
// buffer: cap(5)
|
|
|
|
static TQRegExp s_progressRx( "Track\\s(\\d\\d)\\:\\s*(\\d*)\\sof\\s*(\\d*)\\sMB\\swritten\\s(?:\\(fifo\\s*(\\d*)\\%\\)\\s*)?(?:\\[buf\\s*(\\d*)\\%\\])?.*" );
|
|
|
|
|
|
|
|
emit debuggingOutput( m_cdrecordBinObject->name(), line );
|
|
|
|
|
|
|
|
//
|
|
|
|
// Progress and toc parsing
|
|
|
|
//
|
|
|
|
|
|
|
|
if( line.startsWith( "Track " ) ) {
|
|
|
|
if( !m_totalTracksParsed ) {
|
|
|
|
// this is not the progress display but the list of tracks that will get written
|
|
|
|
// we always extract the tracknumber to get the highest at last
|
|
|
|
bool ok;
|
|
|
|
int tt = line.mid( 6, 2 ).toInt(&ok);
|
|
|
|
|
|
|
|
if( ok ) {
|
|
|
|
struct Private::Track track;
|
|
|
|
track.audio = ( line.mid( 10, 5 ) == "audio" );
|
|
|
|
|
|
|
|
m_totalTracks = tt;
|
|
|
|
|
|
|
|
int sizeStart = line.find( TQRegExp("\\d"), 10 );
|
|
|
|
int sizeEnd = line.find( "MB", sizeStart );
|
|
|
|
track.size = line.mid( sizeStart, sizeEnd-sizeStart ).toInt(&ok);
|
|
|
|
|
|
|
|
if( ok ) {
|
|
|
|
d->tracks.append(track);
|
|
|
|
m_totalSize += track.size;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
kdDebug() << "(K3bCdrecordWriter) track number parse error: "
|
|
|
|
<< line.mid( sizeStart, sizeEnd-sizeStart ) << endl;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
kdDebug() << "(K3bCdrecordWriter) track number parse error: "
|
|
|
|
<< line.mid( 6, 2 ) << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( s_progressRx.exactMatch( line ) ) {
|
|
|
|
// int num = s_progressRx.cap(1).toInt();
|
|
|
|
int made = s_progressRx.cap(2).toInt();
|
|
|
|
int size = s_progressRx.cap(3).toInt();
|
|
|
|
int fifo = s_progressRx.cap(4).toInt();
|
|
|
|
|
|
|
|
emit buffer( fifo );
|
|
|
|
m_lastFifoValue = fifo;
|
|
|
|
|
|
|
|
if( s_progressRx.numCaptures() > 4 )
|
|
|
|
emit deviceBuffer( s_progressRx.cap(5).toInt() );
|
|
|
|
|
|
|
|
//
|
|
|
|
// cdrecord's output sucks a bit.
|
|
|
|
// we get track sizes that differ from the sizes in the progress
|
|
|
|
// info since these are dependant on the writing mode.
|
|
|
|
// so we just use the track sizes and do a bit of math...
|
|
|
|
//
|
|
|
|
|
|
|
|
if( d->tracks.count() > m_currentTrack-1 && size > 0 ) {
|
|
|
|
double convV = (double)d->tracks[m_currentTrack-1].size/(double)size;
|
|
|
|
made = (int)((double)made * convV);
|
|
|
|
size = d->tracks[m_currentTrack-1].size;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
kdError() << "(K3bCdrecordWriter) Did not parse all tracks sizes!" << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( size > 0 ) {
|
|
|
|
emit processedSubSize( made, size );
|
|
|
|
emit subPercent( 100*made/size );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( m_totalSize > 0 ) {
|
|
|
|
emit processedSize( m_alreadyWritten+made, m_totalSize );
|
|
|
|
emit percent( 100*(m_alreadyWritten+made)/m_totalSize );
|
|
|
|
}
|
|
|
|
|
|
|
|
d->speedEst->dataWritten( (m_alreadyWritten+made)*1024 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Cdrecord starts all error and warning messages with it's path
|
|
|
|
// With Debian's script it starts with cdrecord (or /usr/bin/cdrecord or whatever! I hate this script!)
|
|
|
|
//
|
|
|
|
|
|
|
|
else if( line.startsWith( "cdrecord" ) ||
|
|
|
|
line.startsWith( m_cdrecordBinObject->path ) ||
|
|
|
|
line.startsWith( m_cdrecordBinObject->path.left(m_cdrecordBinObject->path.length()-5) ) ) {
|
|
|
|
// get rid of the path and the following colon and space
|
|
|
|
TQString errStr = line.mid( line.find(':') + 2 );
|
|
|
|
|
|
|
|
if( errStr.startsWith( "Drive does not support SAO" ) ) {
|
|
|
|
emit infoMessage( i18n("DAO (Disk At Once) recording not supported with this writer"), K3bJob::ERROR );
|
|
|
|
emit infoMessage( i18n("Please choose TAO (Track At Once) and try again"), K3bJob::ERROR );
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith( "Drive does not support RAW" ) ) {
|
|
|
|
emit infoMessage( i18n("RAW recording not supported with this writer"), K3bJob::ERROR );
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith("Input/output error.") ) {
|
|
|
|
emit infoMessage( i18n("Input/output error. Not necessarily serious."), WARNING );
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith("shmget failed") ) {
|
|
|
|
m_cdrecordError = SHMGET_FAILED;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith("OPC failed") ) {
|
|
|
|
m_cdrecordError = OPC_FAILED;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith( "Drive needs to reload the media" ) ) {
|
|
|
|
emit infoMessage( i18n("Reloading of medium required"), K3bJob::INFO );
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith( "The current problem looks like a buffer underrun" ) ) {
|
|
|
|
if( m_cdrecordError == UNKNOWN ) // it is almost never a buffer underrun these days.
|
|
|
|
m_cdrecordError = BUFFER_UNDERRUN;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith("WARNING: Data may not fit") ) {
|
|
|
|
bool overburn = k3bcore->globalSettings()->overburn();
|
|
|
|
if( overburn && m_cdrecordBinObject->hasFeature("overburn") )
|
|
|
|
emit infoMessage( i18n("Trying to write more than the official disk capacity"), K3bJob::WARNING );
|
|
|
|
m_cdrecordError = OVERSIZE;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith("Bad Option") ) {
|
|
|
|
m_cdrecordError = BAD_OPTION;
|
|
|
|
// parse option
|
|
|
|
int pos = line.find( "Bad Option" ) + 13;
|
|
|
|
int len = line.length() - pos - 1;
|
|
|
|
emit infoMessage( i18n("No valid %1 option: %2").arg(m_cdrecordBinObject->name()).arg(line.mid(pos, len)),
|
|
|
|
ERROR );
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith("Cannot set speed/dummy") ) {
|
|
|
|
m_cdrecordError = CANNOT_SET_SPEED;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith("Cannot open new session") ) {
|
|
|
|
m_cdrecordError = CANNOT_OPEN_NEW_SESSION;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith("Cannot send CUE sheet") ) {
|
|
|
|
m_cdrecordError = CANNOT_SEND_CUE_SHEET;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith( "Trying to use ultra high speed" ) ||
|
|
|
|
errStr.startsWith( "Trying to use high speed" ) ||
|
|
|
|
errStr.startsWith( "Probably trying to use ultra high speed" ) ||
|
|
|
|
errStr.startsWith( "You did use a high speed medium on an improper writer" ) ||
|
|
|
|
errStr.startsWith( "You did use a ultra high speed medium on an improper writer" ) ) {
|
|
|
|
m_cdrecordError = HIGH_SPEED_MEDIUM;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith( "You may have used an ultra low speed medium" ) ) {
|
|
|
|
m_cdrecordError = LOW_SPEED_MEDIUM;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith( "Permission denied. Cannot open" ) ||
|
|
|
|
errStr.startsWith( "Operation not permitted." ) ) {
|
|
|
|
m_cdrecordError = PERMISSION_DENIED;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith( "Can only copy session # 1") ) {
|
|
|
|
emit infoMessage( i18n("Only session 1 will be cloned."), WARNING );
|
|
|
|
}
|
|
|
|
else if( errStr == "Cannot fixate disk." ) {
|
|
|
|
emit infoMessage( i18n("Unable to fixate the disk."), ERROR );
|
|
|
|
if( m_cdrecordError == UNKNOWN )
|
|
|
|
m_cdrecordError = CANNOT_FIXATE_DISK;
|
|
|
|
}
|
|
|
|
else if( errStr == "A write error occurred." ) {
|
|
|
|
m_cdrecordError = WRITE_ERROR;
|
|
|
|
}
|
|
|
|
else if( errStr.startsWith( "Try again with cdrecord blank=all." ) ) {
|
|
|
|
m_cdrecordError = BLANK_FAILED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// All other messages
|
|
|
|
//
|
|
|
|
|
|
|
|
else if( line.contains( "at speed" ) ) {
|
|
|
|
// parse the speed and inform the user if cdrdao switched it down
|
|
|
|
int pos = line.find( "at speed" );
|
|
|
|
int pos2 = line.find( "in", pos+9 );
|
|
|
|
int speed = static_cast<int>( line.mid( pos+9, pos2-pos-10 ).toDouble() ); // cdrecord-dvd >= 2.01a25 uses 8.0 and stuff
|
|
|
|
if( speed != d->usedSpeed ) {
|
|
|
|
emit infoMessage( i18n("Medium or burner do not support writing at %1x speed").arg(d->usedSpeed), K3bJob::WARNING );
|
|
|
|
if( speed > d->usedSpeed )
|
|
|
|
emit infoMessage( i18n("Switching burn speed up to %1x").arg(speed), K3bJob::WARNING );
|
|
|
|
else
|
|
|
|
emit infoMessage( i18n("Switching burn speed down to %1x").arg(speed), K3bJob::WARNING );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Starting new" ) ) {
|
|
|
|
m_totalTracksParsed = true;
|
|
|
|
if( m_currentTrack > 0 ) {// nothing has been written at the start of track 1
|
|
|
|
if( d->tracks.count() > m_currentTrack-1 )
|
|
|
|
m_alreadyWritten += d->tracks[m_currentTrack-1].size;
|
|
|
|
else
|
|
|
|
kdError() << "(K3bCdrecordWriter) Did not parse all tracks sizes!" << endl;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
emit infoMessage( i18n("Starting disc write"), INFO );
|
|
|
|
|
|
|
|
m_currentTrack++;
|
|
|
|
|
|
|
|
if( m_currentTrack > d->tracks.count() ) {
|
|
|
|
kdDebug() << "(K3bCdrecordWriter) need to add dummy track struct." << endl;
|
|
|
|
struct Private::Track t;
|
|
|
|
t.size = 1;
|
|
|
|
t.audio = false;
|
|
|
|
d->tracks.append(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
kdDebug() << "(K3bCdrecordWriter) writing track " << m_currentTrack << " of " << m_totalTracks << " tracks." << endl;
|
|
|
|
emit nextTrack( m_currentTrack, m_totalTracks );
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Fixating" ) ) {
|
|
|
|
emit newSubTask( i18n("Closing Session") );
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Writing lead-in" ) ) {
|
|
|
|
m_totalTracksParsed = true;
|
|
|
|
emit newSubTask( i18n("Writing Leadin") );
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Writing Leadout") ) {
|
|
|
|
emit newSubTask( i18n("Writing Leadout") );
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Writing pregap" ) ) {
|
|
|
|
emit newSubTask( i18n("Writing pregap") );
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Performing OPC" ) ) {
|
|
|
|
emit infoMessage( i18n("Performing Optimum Power Calibration"), K3bJob::INFO );
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Sending" ) ) {
|
|
|
|
emit infoMessage( i18n("Sending CUE sheet"), K3bJob::INFO );
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Turning BURN-Free on" ) || line.startsWith( "BURN-Free is ON") ) {
|
|
|
|
emit infoMessage( i18n("Enabled Burnfree"), K3bJob::INFO );
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Turning BURN-Free off" ) ) {
|
|
|
|
emit infoMessage( i18n("Disabled Burnfree"), K3bJob::WARNING );
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Re-load disk and hit" ) ) {
|
|
|
|
// this happens on some notebooks where cdrecord is not able to close the
|
|
|
|
// tray itself, so we need to ask the user to do so
|
|
|
|
blockingInformation( i18n("Please reload the medium and press 'ok'"),
|
|
|
|
i18n("Unable to close the tray") );
|
|
|
|
|
|
|
|
// now send a <CR> to cdrecord
|
|
|
|
// hopefully this will do it since I have no possibility to test it!
|
|
|
|
::write( fd(), "\n", 1 );
|
|
|
|
}
|
|
|
|
else if( s_burnfreeCounterRx.search( line ) ) {
|
|
|
|
bool ok;
|
|
|
|
int num = s_burnfreeCounterRx.cap(1).toInt(&ok);
|
|
|
|
if( ok )
|
|
|
|
emit infoMessage( i18n("Burnfree was used 1 time.", "Burnfree was used %n times.", num), INFO );
|
|
|
|
}
|
|
|
|
else if( s_burnfreeCounterRxPredict.search( line ) ) {
|
|
|
|
bool ok;
|
|
|
|
int num = s_burnfreeCounterRxPredict.cap(1).toInt(&ok);
|
|
|
|
if( ok )
|
|
|
|
emit infoMessage( i18n("Buffer was low 1 time.", "Buffer was low %n times.", num), INFO );
|
|
|
|
}
|
|
|
|
else if( line.contains("Medium Error") ) {
|
|
|
|
m_cdrecordError = MEDIUM_ERROR;
|
|
|
|
}
|
|
|
|
else if( line.startsWith( "Error trying to open" ) && line.contains( "(Device or resource busy)" ) ) {
|
|
|
|
m_cdrecordError = DEVICE_BUSY;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// debugging
|
|
|
|
kdDebug() << "(" << m_cdrecordBinObject->name() << ") " << line << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::slotProcessExited( TDEProcess* p )
|
|
|
|
{
|
|
|
|
// remove temporary cdtext file
|
|
|
|
delete d->cdTextFile;
|
|
|
|
d->cdTextFile = 0;
|
|
|
|
|
|
|
|
// release the device within this process
|
|
|
|
burnDevice()->usageUnlock();
|
|
|
|
|
|
|
|
// unblock the device
|
|
|
|
k3bcore->unblockDevice( burnDevice() );
|
|
|
|
|
|
|
|
if( d->canceled ) {
|
|
|
|
// this will unblock and eject the drive and emit the finished/canceled signals
|
|
|
|
K3bAbstractWriter::cancel();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if( p->normalExit() ) {
|
|
|
|
switch( p->exitStatus() ) {
|
|
|
|
case 0:
|
|
|
|
{
|
|
|
|
if( simulate() )
|
|
|
|
emit infoMessage( i18n("Simulation successfully completed"), K3bJob::SUCCESS );
|
|
|
|
else
|
|
|
|
emit infoMessage( i18n("Writing successfully completed"), K3bJob::SUCCESS );
|
|
|
|
|
|
|
|
int s = d->speedEst->average();
|
|
|
|
emit infoMessage( i18n("Average overall write speed: %1 KB/s (%2x)").arg(s).arg(TDEGlobal::locale()->formatNumber((double)s/150.0), 2), INFO );
|
|
|
|
|
|
|
|
jobFinished( true );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
kdDebug() << "(K3bCdrecordWriter) error: " << p->exitStatus() << endl;
|
|
|
|
|
|
|
|
if( m_cdrecordError == UNKNOWN && m_lastFifoValue <= 3 )
|
|
|
|
m_cdrecordError = BUFFER_UNDERRUN;
|
|
|
|
|
|
|
|
switch( m_cdrecordError ) {
|
|
|
|
case OVERSIZE:
|
|
|
|
if( k3bcore->globalSettings()->overburn() &&
|
|
|
|
m_cdrecordBinObject->hasFeature("overburn") )
|
|
|
|
emit infoMessage( i18n("Data did not fit on disk."), ERROR );
|
|
|
|
else {
|
|
|
|
emit infoMessage( i18n("Data does not fit on disk."), ERROR );
|
|
|
|
if( m_cdrecordBinObject->hasFeature("overburn") )
|
|
|
|
emit infoMessage( i18n("Enable overburning in the advanced K3b settings to burn anyway."), INFO );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BAD_OPTION:
|
|
|
|
// error message has already been emited earlier since we needed the actual line
|
|
|
|
break;
|
|
|
|
case SHMGET_FAILED:
|
|
|
|
emit infoMessage( i18n("%1 could not reserve shared memory segment of requested size.").arg(m_cdrecordBinObject->name()), ERROR );
|
|
|
|
emit infoMessage( i18n("Probably you chose a too large buffer size."), ERROR );
|
|
|
|
break;
|
|
|
|
case OPC_FAILED:
|
|
|
|
emit infoMessage( i18n("OPC failed. Probably the writer does not like the medium."), ERROR );
|
|
|
|
break;
|
|
|
|
case CANNOT_SET_SPEED:
|
|
|
|
emit infoMessage( i18n("Unable to set write speed to %1.").arg(d->usedSpeed), ERROR );
|
|
|
|
emit infoMessage( i18n("Probably this is lower than your writer's lowest writing speed."), ERROR );
|
|
|
|
break;
|
|
|
|
case CANNOT_SEND_CUE_SHEET:
|
|
|
|
emit infoMessage( i18n("Unable to send CUE sheet."), ERROR );
|
|
|
|
if( m_writingMode == K3b::DAO )
|
|
|
|
emit infoMessage( i18n("Sometimes using TAO writing mode solves this issue."), ERROR );
|
|
|
|
break;
|
|
|
|
case CANNOT_OPEN_NEW_SESSION:
|
|
|
|
emit infoMessage( i18n("Unable to open new session."), ERROR );
|
|
|
|
emit infoMessage( i18n("Probably a problem with the medium."), ERROR );
|
|
|
|
break;
|
|
|
|
case CANNOT_FIXATE_DISK:
|
|
|
|
emit infoMessage( i18n("The disk might still be readable."), ERROR );
|
|
|
|
if( m_writingMode == K3b::TAO && burnDevice()->dao() )
|
|
|
|
emit infoMessage( i18n("Try DAO writing mode."), ERROR );
|
|
|
|
break;
|
|
|
|
case PERMISSION_DENIED:
|
|
|
|
emit infoMessage( i18n("%1 has no permission to open the device.").arg("Cdrecord"), ERROR );
|
|
|
|
#ifdef HAVE_K3BSETUP
|
|
|
|
emit infoMessage( i18n("You may use K3bsetup2 to solve this problem."), ERROR );
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case BUFFER_UNDERRUN:
|
|
|
|
emit infoMessage( i18n("Probably a buffer underrun occurred."), ERROR );
|
|
|
|
if( !d->usingBurnfree && burnDevice()->burnproof() )
|
|
|
|
emit infoMessage( i18n("Please enable Burnfree or choose a lower burning speed."), ERROR );
|
|
|
|
else
|
|
|
|
emit infoMessage( i18n("Please choose a lower burning speed."), ERROR );
|
|
|
|
break;
|
|
|
|
case HIGH_SPEED_MEDIUM:
|
|
|
|
emit infoMessage( i18n("Found a high-speed medium not suitable for the writer being used."), ERROR );
|
|
|
|
emit infoMessage( i18n("Use the 'force unsafe operations' option to ignore this."), ERROR );
|
|
|
|
break;
|
|
|
|
case LOW_SPEED_MEDIUM:
|
|
|
|
emit infoMessage( i18n("Found a low-speed medium not suitable for the writer being used."), ERROR );
|
|
|
|
emit infoMessage( i18n("Use the 'force unsafe operations' option to ignore this."), ERROR );
|
|
|
|
break;
|
|
|
|
case MEDIUM_ERROR:
|
|
|
|
emit infoMessage( i18n("Most likely the burning failed due to low-quality media."), ERROR );
|
|
|
|
break;
|
|
|
|
case DEVICE_BUSY:
|
|
|
|
emit infoMessage( i18n("Another application is blocking the device (most likely automounting)."), ERROR );
|
|
|
|
break;
|
|
|
|
case WRITE_ERROR:
|
|
|
|
emit infoMessage( i18n("A write error occurred."), ERROR );
|
|
|
|
if( m_writingMode == K3b::DAO )
|
|
|
|
emit infoMessage( i18n("Sometimes using TAO writing mode solves this issue."), ERROR );
|
|
|
|
break;
|
|
|
|
case BLANK_FAILED:
|
|
|
|
emit infoMessage( i18n("Some drives do not support all erase types."), ERROR );
|
|
|
|
emit infoMessage( i18n("Try again using 'Complete' erasing."), ERROR );
|
|
|
|
break;
|
|
|
|
case UNKNOWN:
|
|
|
|
if( p->exitStatus() == 12 && K3b::kernelVersion() >= K3bVersion( 2, 6, 8 ) && m_cdrecordBinObject->hasFeature( "suidroot" ) ) {
|
|
|
|
emit infoMessage( i18n("Since kernel version 2.6.8 cdrecord cannot use SCSI transport when running suid root anymore."), ERROR );
|
|
|
|
emit infoMessage( i18n("You may use K3bSetup to solve this problem or remove the suid bit manually."), ERROR );
|
|
|
|
}
|
|
|
|
else if( !wasSourceUnreadable() ) {
|
|
|
|
emit infoMessage( i18n("%1 returned an unknown error (code %2).")
|
|
|
|
.arg(m_cdrecordBinObject->name()).arg(p->exitStatus()),
|
|
|
|
K3bJob::ERROR );
|
|
|
|
|
|
|
|
if( p->exitStatus() >= 254 && m_writingMode == K3b::DAO ) {
|
|
|
|
emit infoMessage( i18n("Sometimes using TAO writing mode solves this issue."), ERROR );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
emit infoMessage( i18n("If you are running an unpatched cdrecord version..."), ERROR );
|
|
|
|
emit infoMessage( i18n("...and this error also occurs with high quality media..."), ERROR );
|
|
|
|
emit infoMessage( i18n("...and the K3b FAQ does not help you..."), ERROR );
|
|
|
|
emit infoMessage( i18n("...please include the debugging output in your problem report."), ERROR );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
jobFinished( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
emit infoMessage( i18n("%1 did not exit cleanly.").arg(m_cdrecordBinObject->name()),
|
|
|
|
ERROR );
|
|
|
|
jobFinished( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bCdrecordWriter::slotThroughput( int t )
|
|
|
|
{
|
|
|
|
emit writeSpeed( t, d->tracks[m_currentTrack-1].audio ? 175 : 150 );
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "k3bcdrecordwriter.moc"
|