File indexing completed on 2025-03-16 04:29:30
0001 /* 0002 SPDX-FileCopyrightText: 2009 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 #include "k3bdvdformattingjob.h" 0009 0010 #include "k3bglobals.h" 0011 #include "k3bprocess.h" 0012 #include "k3bdevice.h" 0013 #include "k3bdeviceglobals.h" 0014 #include "k3bdevicehandler.h" 0015 #include "k3bdiskinfo.h" 0016 #include "k3bexternalbinmanager.h" 0017 #include "k3bcore.h" 0018 #include "k3bversion.h" 0019 #include "k3bglobalsettings.h" 0020 #include "k3b_i18n.h" 0021 0022 #include <QDebug> 0023 #include <QRegularExpression> 0024 0025 #include <errno.h> 0026 #include <string.h> 0027 0028 0029 class K3b::DvdFormattingJob::Private 0030 { 0031 public: 0032 Private() 0033 : formattingMode(FormattingComplete), 0034 force(false), 0035 mode(WritingModeAuto), 0036 device(0), 0037 process(0), 0038 dvdFormatBin(0), 0039 lastProgressValue(0), 0040 running(false), 0041 forceNoEject(false) { 0042 } 0043 0044 FormattingMode formattingMode; 0045 bool force; 0046 int mode; 0047 0048 Device::Device* device; 0049 Process* process; 0050 const ExternalBin* dvdFormatBin; 0051 0052 int lastProgressValue; 0053 0054 bool success; 0055 bool canceled; 0056 bool running; 0057 0058 bool forceNoEject; 0059 0060 bool error; 0061 }; 0062 0063 0064 K3b::DvdFormattingJob::DvdFormattingJob( JobHandler* jh, QObject* parent ) 0065 : BurnJob( jh, parent ) 0066 { 0067 d = new Private; 0068 } 0069 0070 0071 K3b::DvdFormattingJob::~DvdFormattingJob() 0072 { 0073 delete d->process; 0074 delete d; 0075 } 0076 0077 0078 K3b::Device::Device* K3b::DvdFormattingJob::writer() const 0079 { 0080 return d->device; 0081 } 0082 0083 0084 void K3b::DvdFormattingJob::setForceNoEject( bool b ) 0085 { 0086 d->forceNoEject = b; 0087 } 0088 0089 0090 QString K3b::DvdFormattingJob::jobDescription() const 0091 { 0092 return i18n("Formatting disc"); 0093 } 0094 0095 0096 QString K3b::DvdFormattingJob::jobDetails() const 0097 { 0098 if( d->formattingMode == FormattingQuick ) 0099 return i18n("Quick Format"); 0100 else 0101 return QString(); 0102 } 0103 0104 0105 void K3b::DvdFormattingJob::start() 0106 { 0107 d->canceled = false; 0108 d->running = true; 0109 d->error = false; 0110 0111 jobStarted(); 0112 0113 if( !d->device ) { 0114 emit infoMessage( i18n("No device set"), MessageError ); 0115 d->running = false; 0116 jobFinished(false); 0117 return; 0118 } 0119 0120 // FIXME: check the return value 0121 if( K3b::isMounted( d->device ) ) { 0122 emit infoMessage( i18n("Unmounting medium"), MessageInfo ); 0123 K3b::unmount( d->device ); 0124 } 0125 0126 // 0127 // first wait for a dvd+rw, dvd-rw, or bd-re 0128 // Be aware that an empty DVD-RW might be reformatted to another writing mode 0129 // so we also wait for empty dvds 0130 // 0131 if( waitForMedium( d->device, 0132 Device::STATE_COMPLETE|Device::STATE_INCOMPLETE|Device::STATE_EMPTY, 0133 Device::MEDIA_REWRITABLE_DVD|Device::MEDIA_BD_RE, 0134 0, 0135 i18n("Please insert a rewritable DVD or Blu-ray medium into drive<p><b>%1 %2 (%3)</b>.", 0136 d->device->vendor(), 0137 d->device->description(), 0138 d->device->blockDeviceName()) ) == Device::MEDIA_UNKNOWN ) { 0139 emit canceled(); 0140 d->running = false; 0141 jobFinished(false); 0142 return; 0143 } 0144 0145 emit infoMessage( i18n("Checking medium"), MessageInfo ); 0146 emit newTask( i18n("Checking medium") ); 0147 0148 connect( Device::sendCommand( Device::DeviceHandler::CommandDiskInfo, d->device ), 0149 SIGNAL(finished(K3b::Device::DeviceHandler*)), 0150 this, 0151 SLOT(slotDeviceHandlerFinished(K3b::Device::DeviceHandler*)) ); 0152 } 0153 0154 0155 void K3b::DvdFormattingJob::start( const Device::DiskInfo& di ) 0156 { 0157 d->canceled = false; 0158 d->running = true; 0159 0160 jobStarted(); 0161 0162 startFormatting( di ); 0163 } 0164 0165 0166 void K3b::DvdFormattingJob::cancel() 0167 { 0168 if( d->running ) { 0169 d->canceled = true; 0170 if( d->process ) 0171 d->process->terminate(); 0172 } 0173 else { 0174 qDebug() << "(K3b::DvdFormattingJob) not running."; 0175 } 0176 } 0177 0178 0179 void K3b::DvdFormattingJob::setDevice( Device::Device* dev ) 0180 { 0181 d->device = dev; 0182 } 0183 0184 0185 void K3b::DvdFormattingJob::setMode( int m ) 0186 { 0187 d->mode = m; 0188 } 0189 0190 0191 void K3b::DvdFormattingJob::setFormattingMode( FormattingMode mode ) 0192 { 0193 d->formattingMode = mode; 0194 } 0195 0196 0197 void K3b::DvdFormattingJob::setForce( bool b ) 0198 { 0199 d->force = b; 0200 } 0201 0202 0203 void K3b::DvdFormattingJob::slotStderrLine( const QString& line ) 0204 { 0205 // * BD/DVD±RW/-RAM format utility by <appro@fy.chalmers.se>, version 7.1. 0206 // * 4.7GB DVD-RW media in Sequential mode detected. 0207 // * blanking 100.0| 0208 0209 // * formatting 100.0| 0210 0211 emit debuggingOutput( "dvd+rw-format", line ); 0212 0213 // parsing for the -gui mode (since dvd+rw-format 4.6) 0214 int pos = line.indexOf( "blanking" ); 0215 if( pos < 0 ) 0216 pos = line.indexOf( "formatting" ); 0217 static const QRegularExpression digitRx("\\d"); 0218 if( pos >= 0 ) { 0219 pos = line.indexOf( digitRx, pos ); 0220 } 0221 // parsing for \b\b... stuff 0222 else if( !line.startsWith('*') ) { 0223 pos = line.indexOf( digitRx ); 0224 } 0225 else if( line.startsWith( ":-(" ) ) { 0226 if( line.startsWith( ":-( unable to proceed with format" ) ) { 0227 d->error = true; 0228 } 0229 } 0230 0231 if( pos >= 0 ) { 0232 static const QRegularExpression endPosRx("[^\\d\\.]"); 0233 int endPos = line.indexOf( endPosRx, pos ) - 1; 0234 bool ok; 0235 int progress = (int)(line.mid( pos, endPos - pos ).toDouble(&ok)); 0236 if( ok ) { 0237 d->lastProgressValue = progress; 0238 emit percent( progress ); 0239 } 0240 else { 0241 qDebug() << "(K3b::DvdFormattingJob) parsing error: '" << line.mid( pos, endPos - pos ) << "'"; 0242 } 0243 } 0244 } 0245 0246 0247 void K3b::DvdFormattingJob::slotProcessFinished( int exitCode, QProcess::ExitStatus exitStatus ) 0248 { 0249 if( d->canceled ) { 0250 emit canceled(); 0251 d->success = false; 0252 } 0253 else if( exitStatus == QProcess::NormalExit ) { 0254 if( !d->error && (exitCode == 0) ) { 0255 emit infoMessage( i18n("Formatting successfully completed"), Job::MessageSuccess ); 0256 0257 if( d->lastProgressValue < 100 ) { 0258 emit infoMessage( i18n("Do not be concerned with the progress stopping before 100%."), MessageInfo ); 0259 emit infoMessage( i18n("The formatting will continue in the background during writing."), MessageInfo ); 0260 } 0261 0262 d->success = true; 0263 } 0264 else { 0265 emit infoMessage( i18n("%1 returned an unknown error (code %2).",d->dvdFormatBin->name(), exitCode), 0266 Job::MessageError ); 0267 emit infoMessage( i18n("Please send me an email with the last output."), Job::MessageError ); 0268 0269 d->success = false; 0270 } 0271 } 0272 else { 0273 emit infoMessage( i18n("%1 did not exit cleanly.",d->dvdFormatBin->name()), 0274 MessageError ); 0275 d->success = false; 0276 } 0277 0278 if( d->forceNoEject || 0279 !k3bcore->globalSettings()->ejectMedia() ) { 0280 d->running = false; 0281 jobFinished(d->success); 0282 } 0283 else { 0284 emit infoMessage( i18n("Ejecting medium..."), MessageInfo ); 0285 connect( Device::eject( d->device ), 0286 SIGNAL(finished(K3b::Device::DeviceHandler*)), 0287 this, 0288 SLOT(slotEjectingFinished(K3b::Device::DeviceHandler*)) ); 0289 } 0290 } 0291 0292 0293 void K3b::DvdFormattingJob::slotEjectingFinished( Device::DeviceHandler* dh ) 0294 { 0295 if( !dh->success() ) 0296 emit infoMessage( i18n("Unable to eject medium."), MessageError ); 0297 0298 d->running = false; 0299 jobFinished(d->success); 0300 } 0301 0302 0303 void K3b::DvdFormattingJob::slotDeviceHandlerFinished( Device::DeviceHandler* dh ) 0304 { 0305 if( d->canceled ) { 0306 emit canceled(); 0307 jobFinished(false); 0308 d->running = false; 0309 } 0310 0311 if( dh->success() ) { 0312 startFormatting( dh->diskInfo() ); 0313 } 0314 else { 0315 emit infoMessage( i18n("Unable to determine media state."), MessageError ); 0316 d->running = false; 0317 jobFinished(false); 0318 } 0319 } 0320 0321 0322 void K3b::DvdFormattingJob::startFormatting( const Device::DiskInfo& diskInfo ) 0323 { 0324 // 0325 // Now check the media type: 0326 // if DVD-RW: use d->mode 0327 // emit warning if formatting is full and stuff 0328 // 0329 // in overwrite mode: emit info that progress might stop before 100% since formatting will continue 0330 // in the background once the media gets rewritten (only DVD+RW/BD-RE?) 0331 // 0332 0333 0334 // emit info about what kind of media has been found 0335 if( diskInfo.mediaType() & (Device::MEDIA_REWRITABLE_DVD | Device::MEDIA_BD_RE) ) { 0336 emit infoMessage( i18n("Found %1 medium.", Device::mediaTypeString(diskInfo.mediaType())), 0337 MessageInfo ); 0338 } 0339 else { 0340 emit infoMessage( i18n("No rewritable DVD or BD medium found. Unable to format."), MessageError ); 0341 d->running = false; 0342 jobFinished(false); 0343 return; 0344 } 0345 0346 bool format = true; // do we need to format 0347 bool blank = false; // blank is for DVD-RW sequential incremental 0348 // DVD-RW restricted overwrite and DVD+RW uses the force option (or no option at all) 0349 0350 // 0351 // DVD+RW/BD-RE is quite easy to handle. There is only one possible mode and it is always recommended to not 0352 // format it more than once but to overwrite it once it is formatted 0353 // Once the initial formatting has been done it's always "appendable" (or "complete"???) 0354 // 0355 0356 if( diskInfo.mediaType() & (Device::MEDIA_DVD_PLUS_ALL | Device::MEDIA_BD_RE) ) { 0357 0358 if( diskInfo.empty() ) { 0359 // 0360 // The DVD+RW/BD is blank and needs to be initially formatted 0361 // 0362 blank = false; 0363 } 0364 else { 0365 emit infoMessage( i18n("No need to format %1 media more than once.", 0366 Device::mediaTypeString(diskInfo.mediaType())), MessageInfo ); 0367 emit infoMessage( i18n("It may simply be overwritten."), MessageInfo ); 0368 0369 if( d->force ) { 0370 emit infoMessage( i18n("Forcing formatting anyway."), MessageInfo ); 0371 emit infoMessage( i18n("It is not recommended to force formatting of %1 media.", 0372 Device::mediaTypeString(diskInfo.mediaType())), MessageInfo ); 0373 emit infoMessage( i18n("After 10-20 reformats the media might become unusable."), MessageInfo ); 0374 blank = false; 0375 } 0376 else { 0377 format = false; 0378 } 0379 } 0380 0381 if( format ) 0382 emit newSubTask( i18n("Formatting %1 medium", Device::mediaTypeString(diskInfo.mediaType())) ); 0383 } 0384 0385 // 0386 // DVD-RW has two modes: incremental sequential (the default which is also needed for DAO writing) 0387 // and restricted overwrite which compares to the DVD+RW mode. 0388 // 0389 0390 else { // MEDIA_DVD_RW 0391 0392 if( diskInfo.currentProfile() != Device::MEDIA_UNKNOWN ) { 0393 emit infoMessage( i18n("Formatted in %1 mode.",Device::mediaTypeString(diskInfo.currentProfile())), MessageInfo ); 0394 0395 0396 // 0397 // Is it possible to have an empty DVD-RW in restricted overwrite mode???? I don't think so. 0398 // 0399 0400 if( diskInfo.empty() && 0401 (d->mode == WritingModeAuto || 0402 (d->mode == WritingModeIncrementalSequential && 0403 diskInfo.currentProfile() == Device::MEDIA_DVD_RW_SEQ) || 0404 (d->mode == WritingModeRestrictedOverwrite && 0405 diskInfo.currentProfile() == Device::MEDIA_DVD_RW_OVWR) ) 0406 ) { 0407 emit infoMessage( i18n("Media is already empty."), MessageInfo ); 0408 if( d->force ) 0409 emit infoMessage( i18n("Forcing formatting anyway."), MessageInfo ); 0410 else 0411 format = false; 0412 } 0413 else if( diskInfo.currentProfile() == Device::MEDIA_DVD_RW_OVWR && 0414 d->mode != WritingModeIncrementalSequential ) { 0415 emit infoMessage( i18n("No need to format %1 media more than once.", 0416 Device::mediaTypeString(diskInfo.currentProfile())), MessageInfo ); 0417 emit infoMessage( i18n("It may simply be overwritten."), MessageInfo ); 0418 0419 if( d->force ) 0420 emit infoMessage( i18n("Forcing formatting anyway."), MessageInfo ); 0421 else 0422 format = false; 0423 } 0424 0425 if( format ) { 0426 if( d->mode == WritingModeAuto ) { 0427 // just format in the same mode as the media is currently formatted 0428 blank = (diskInfo.currentProfile() == Device::MEDIA_DVD_RW_SEQ); 0429 } 0430 else { 0431 blank = (d->mode == WritingModeIncrementalSequential); 0432 } 0433 0434 emit newSubTask( i18n("Formatting" 0435 " DVD-RW in %1 mode.",Device::mediaTypeString( blank ? 0436 Device::MEDIA_DVD_RW_SEQ : 0437 Device::MEDIA_DVD_RW_OVWR )) ); 0438 } 0439 } 0440 else { 0441 emit infoMessage( i18n("Unable to determine the current formatting state of the DVD-RW medium."), MessageError ); 0442 d->running = false; 0443 jobFinished(false); 0444 return; 0445 } 0446 } 0447 0448 0449 if( format ) { 0450 delete d->process; 0451 d->process = new Process(); 0452 connect( d->process, SIGNAL(stderrLine(QString)), this, SLOT(slotStderrLine(QString)) ); 0453 connect( d->process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotProcessFinished(int,QProcess::ExitStatus)) ); 0454 0455 d->dvdFormatBin = k3bcore->externalBinManager()->binObject( "dvd+rw-format" ); 0456 if( !d->dvdFormatBin ) { 0457 emit infoMessage( i18n("Could not find %1 executable.",QString("dvd+rw-format")), MessageError ); 0458 d->running = false; 0459 jobFinished(false); 0460 return; 0461 } 0462 0463 if( !d->dvdFormatBin->copyright().isEmpty() ) 0464 emit infoMessage( i18n("Using %1 %2 – Copyright © %3",d->dvdFormatBin->name(),d->dvdFormatBin->version(),d->dvdFormatBin->copyright()), MessageInfo ); 0465 0466 0467 *d->process << d->dvdFormatBin; 0468 0469 if( d->dvdFormatBin->version() >= Version( 4, 6 ) ) 0470 *d->process << "-gui"; 0471 0472 QString p; 0473 if( blank ) 0474 p = "-blank"; 0475 else 0476 p = "-force"; 0477 if( d->formattingMode == FormattingComplete ) 0478 p += "=full"; 0479 0480 *d->process << p; 0481 0482 *d->process << d->device->blockDeviceName(); 0483 0484 // additional user parameters from config 0485 const QStringList& params = d->dvdFormatBin->userParameters(); 0486 for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it ) 0487 *d->process << *it; 0488 0489 qDebug() << "***** dvd+rw-format parameters:\n"; 0490 QString s = d->process->joinedArgs(); 0491 qDebug() << s << Qt::endl << Qt::flush; 0492 emit debuggingOutput( "dvd+rw-format command:", s ); 0493 0494 if( !d->process->start( KProcess::OnlyStderrChannel ) ) { 0495 // something went wrong when starting the program 0496 // it "should" be the executable 0497 qDebug() << "(K3b::DvdFormattingJob) could not start " << d->dvdFormatBin->path(); 0498 emit infoMessage( i18n("Could not start %1.",d->dvdFormatBin->name()), Job::MessageError ); 0499 d->running = false; 0500 jobFinished(false); 0501 } 0502 else { 0503 emit newTask( i18n("Formatting") ); 0504 } 0505 } 0506 else { 0507 // already formatted :) 0508 d->running = false; 0509 jobFinished(true); 0510 } 0511 } 0512 0513 #include "moc_k3bdvdformattingjob.cpp"