File indexing completed on 2024-04-28 04:50:22
0001 /* 0002 SPDX-FileCopyrightText: 2003 Christian Kvasny <chris@k3b.org> 0003 SPDX-FileCopyrightText: 1998-2007 Sebastian Trueg <trueg@k3b.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "k3bvideocdrip.h" 0009 #include "k3bcore.h" 0010 #include "k3bexternalbinmanager.h" 0011 #include "k3bglobals.h" 0012 0013 #include <KConfig> 0014 #include <KProcess> 0015 #include <KLocalizedString> 0016 #include <KIO/Global> 0017 0018 #include <QDateTime> 0019 #include <QDebug> 0020 #include <QFile> 0021 #include <QList> 0022 #include <QRegExp> 0023 #include <QString> 0024 #include <QTimer> 0025 #include <QUrl> 0026 #include <QDomDocument> 0027 #include <QDomElement> 0028 #include <QDomNode> 0029 0030 0031 K3b::VideoCdRip::VideoCdRip( K3b::JobHandler* hdl, K3b::VideoCdRippingOptions* options, QObject* parent ) 0032 : K3b::Job( hdl, parent ), 0033 m_ripsourceType( 0 ), 0034 m_subPosition ( 0 ), 0035 m_videooptions( options ), 0036 m_canceled( false ), 0037 m_process( 0 ) 0038 {} 0039 0040 0041 K3b::VideoCdRip::~VideoCdRip() 0042 { 0043 if ( m_process ) 0044 delete m_process; 0045 } 0046 0047 0048 void K3b::VideoCdRip::cancel() 0049 { 0050 cancelAll(); 0051 0052 emit infoMessage( i18n( "Job canceled by user." ), K3b::Job::MessageError ); 0053 emit canceled(); 0054 jobFinished( false ); 0055 } 0056 0057 0058 void K3b::VideoCdRip::cancelAll() 0059 { 0060 m_canceled = true; 0061 0062 if ( m_process->state() != QProcess::NotRunning ) { 0063 m_process->disconnect( this ); 0064 m_process->kill(); 0065 } 0066 } 0067 0068 0069 void K3b::VideoCdRip::start() 0070 { 0071 qDebug() << "(K3b::VideoCdRip) starting job"; 0072 0073 jobStarted(); 0074 m_canceled = false; 0075 0076 vcdxRip(); 0077 } 0078 0079 void K3b::VideoCdRip::vcdxRip() 0080 { 0081 emit newTask( i18n( "Check files" ) ); 0082 0083 m_stage = stageUnknown; 0084 delete m_process; 0085 m_process = new KProcess(this); 0086 0087 const K3b::ExternalBin* bin = k3bcore ->externalBinManager() ->binObject( "vcdxrip" ); 0088 0089 if ( !bin ) { 0090 qDebug() << "(K3b::VideoCdRip) could not find vcdxrip executable"; 0091 emit infoMessage( i18n( "Could not find %1 executable." , QString("vcdxrip") ), K3b::Job::MessageError ); 0092 emit infoMessage( i18n( "To rip Video CDs you have to install VcdImager Version %1." , QString(">= 0.7.12") ), K3b::Job::MessageInfo ); 0093 emit infoMessage( i18n( "You can find this on your distribution’s software repository or download it from https://www.gnu.org/software/vcdimager/" ), K3b::Job::MessageInfo ); 0094 cancelAll(); 0095 jobFinished( false ); 0096 return ; 0097 } 0098 0099 if( bin->version() < K3b::Version("0.7.12") ) { 0100 qDebug() << "(K3b::VideoCdRip) vcdxrip executable too old!"; 0101 emit infoMessage( i18n( "%1 executable too old: need version %2 or greater." , QString("Vcdxrip") , QString("0.7.12") ), K3b::Job::MessageError ); 0102 emit infoMessage( i18n( "You can find this on your distribution disks or download it from http://www.vcdimager.org" ), K3b::Job::MessageInfo ); 0103 cancelAll(); 0104 jobFinished( false ); 0105 return ; 0106 } 0107 0108 if ( !bin->copyright().isEmpty() ) 0109 emit infoMessage( i18n( "Using %1 %2 – Copyright © %3" , bin->name() , bin->version() , bin->copyright() ), MessageInfo ); 0110 0111 0112 *m_process << k3bcore ->externalBinManager() ->binPath( "vcdxrip" ); 0113 0114 // additional user parameters from config 0115 const QStringList& params = k3bcore->externalBinManager() ->program( "vcdxrip" ) ->userParameters(); 0116 for ( QStringList::const_iterator it = params.constBegin(); it != params.constEnd(); ++it ) 0117 *m_process << *it; 0118 0119 *m_process << "--gui" << "--progress"; 0120 0121 if ( !m_videooptions ->getVideoCdRipFiles() ) 0122 *m_process << "--nofiles"; 0123 0124 if ( !m_videooptions ->getVideoCdRipSegments() ) 0125 *m_process << "--nosegments"; 0126 0127 if ( !m_videooptions ->getVideoCdRipSequences() ) 0128 *m_process << "--nosequences"; 0129 0130 if ( m_videooptions ->getVideoCdIgnoreExt() ) 0131 *m_process << "--no-ext-psd"; 0132 0133 if ( m_videooptions ->getVideoCdSector2336() ) 0134 *m_process << "--sector-2336"; 0135 0136 *m_process << "-i" << QFile::encodeName( m_videooptions ->getVideoCdSource() ); 0137 0138 if ( m_videooptions ->getVideoCdExtractXml() ) 0139 *m_process << "-o" << QFile::encodeName( m_videooptions ->getVideoCdDescription() + ".xml" ); 0140 else 0141 *m_process << "-o" << "/dev/null"; 0142 0143 0144 m_process->setOutputChannelMode( KProcess::MergedChannels ); 0145 connect( m_process, SIGNAL(readyReadStandardOutput()), 0146 this, SLOT(slotParseVcdXRipOutput()) ); 0147 connect( m_process, SIGNAL(finished(int,QProcess::ExitStatus)), 0148 this, SLOT(slotVcdXRipFinished(int,QProcess::ExitStatus)) ); 0149 0150 m_process->setWorkingDirectory( m_videooptions ->getVideoCdDestination() ); 0151 0152 // vcdxrip commandline parameters 0153 qDebug() << "***** vcdxrip parameters:"; 0154 QStringList args = m_process->program(); 0155 args.removeFirst(); 0156 QString s = args.join(" "); 0157 qDebug() << s << Qt::flush; 0158 emit debuggingOutput( "vcdxrip command:", s ); 0159 0160 emit newTask( i18n( "Extracting" ) ); 0161 emit infoMessage( i18n( "Start extracting." ), K3b::Job::MessageInfo ); 0162 emit infoMessage( i18n( "Extract files from %1 to %2." , m_videooptions ->getVideoCdSource() , m_videooptions ->getVideoCdDestination() ), K3b::Job::MessageInfo ); 0163 0164 m_process->start(); 0165 if ( !m_process->waitForStarted() ) { 0166 qDebug() << "(K3b::VideoCdRip) could not start vcdxrip"; 0167 emit infoMessage( i18n( "Could not start %1." , QString("vcdxrip") ), K3b::Job::MessageError ); 0168 cancelAll(); 0169 jobFinished( false ); 0170 } 0171 } 0172 0173 void K3b::VideoCdRip::slotParseVcdXRipOutput() 0174 { 0175 QString buffer = QString::fromLocal8Bit( m_process->readAllStandardOutput() ); 0176 0177 // split to lines 0178 QStringList lines = buffer.split( '\n' ); 0179 0180 QDomDocument xml_doc; 0181 QDomElement xml_root; 0182 0183 // do every line 0184 QStringList::iterator end( lines.end()); 0185 for ( QStringList::iterator str = lines.begin(); str != end; ++str ) { 0186 *str = ( *str ).trimmed(); 0187 0188 emit debuggingOutput( "vcdxrip", *str ); 0189 0190 xml_doc.setContent( QString( "<?xml version='1.0'?><vcdxrip>" ) + *str + "</vcdxrip>" ); 0191 0192 xml_root = xml_doc.documentElement(); 0193 0194 for ( QDomNode node = xml_root.firstChild(); !node.isNull(); node = node.nextSibling() ) { 0195 QDomElement el = node.toElement(); 0196 if ( el.isNull() ) 0197 continue; 0198 0199 const QString tagName = el.tagName().toLower(); 0200 0201 if ( tagName == "progress" ) { 0202 const QString oper = el.attribute( "operation" ).toLower(); 0203 const unsigned long long overallPos = el.attribute( "position" ).toLong(); 0204 const unsigned long long pos = overallPos - m_subPosition; 0205 const unsigned long long size = el.attribute( "size" ).toLong() - m_subPosition; 0206 0207 if ( oper == "extract" ) { 0208 emit subPercent( ( int ) ( 100.0 * ( double ) pos / ( double ) size ) ); 0209 emit processedSubSize( ( pos * 2352 ) / 1024 / 1024 , ( size * 2352 ) / 1024 / 1024 ); 0210 0211 m_bytesFinished = pos; 0212 0213 qDebug() << "(slotParseVcdXRipOutput) overall: " << ((long)overallPos * 2352) 0214 << ", videocdsize: " << m_videooptions->getVideoCdSize() << Qt::endl; 0215 double relOverallWritten = ( ( double ) overallPos * 2352 ) / ( double ) m_videooptions ->getVideoCdSize() ; 0216 int newpercent = ( int ) ( 100 * relOverallWritten ); 0217 if ( newpercent > m_oldpercent ) { 0218 emit percent( newpercent ); 0219 m_oldpercent = newpercent; 0220 } 0221 0222 } else { 0223 return ; 0224 } 0225 0226 } else if ( tagName == "log" ) { 0227 QDomText tel = el.firstChild().toText(); 0228 const QString level = el.attribute( "level" ).toLower(); 0229 if ( tel.isText() ) { 0230 const QString text = tel.data(); 0231 if ( level == "information" ) { 0232 qDebug() << QString( "(K3b::VideoCdRip) vcdxrip information, %1" ).arg( text ); 0233 parseInformation( text ); 0234 } else { 0235 if ( level != "error" ) { 0236 qDebug() << QString( "(K3b::VideoCdRip) vcdxrip warning, %1" ).arg( text ); 0237 emit debuggingOutput( "vcdxrip", text ); 0238 parseInformation( text ); 0239 } else { 0240 qDebug() << QString( "(K3b::VideoCdRip) vcdxrip error, %1" ).arg( text ); 0241 emit infoMessage( text, K3b::Job::MessageError ); 0242 } 0243 } 0244 } 0245 } 0246 } 0247 } 0248 } 0249 0250 0251 void K3b::VideoCdRip::slotVcdXRipFinished( int exitCode, QProcess::ExitStatus exitStatus ) 0252 { 0253 if ( exitStatus == QProcess::NormalExit ) { 0254 // TODO: check the process' exitStatus() 0255 switch ( exitCode ) { 0256 case 0: 0257 emit infoMessage( i18n( "Files successfully extracted." ), K3b::Job::MessageSuccess ); 0258 break; 0259 default: 0260 emit infoMessage( i18n( "%1 returned an unknown error (code %2)." , QString("vcdxrip" ), exitCode ), K3b::Job::MessageError ); 0261 emit infoMessage( i18n( "Please send me an email with the last output..." ), K3b::Job::MessageError ); 0262 cancelAll(); 0263 jobFinished( false ); 0264 return ; 0265 } 0266 } else { 0267 emit infoMessage( i18n( "%1 did not exit cleanly." , QString("Vcdxrip") ), K3b::Job::MessageError ); 0268 cancelAll(); 0269 jobFinished( false ); 0270 return ; 0271 } 0272 0273 jobFinished( true ); 0274 } 0275 0276 void K3b::VideoCdRip::parseInformation( QString text ) 0277 { 0278 // parse warning 0279 if ( text.contains( "encountered non-form2 sector" ) ) { 0280 // I think this is an error not a warning. Finish ripping with invalid mpegs. 0281 emit infoMessage( i18n( "%1 encountered non-form2 sector" ,QString("Vcdxrip")), K3b::Job::MessageError ); 0282 emit infoMessage( i18n( "leaving loop" ), K3b::Job::MessageError ); 0283 cancelAll(); 0284 jobFinished( false ); 0285 return; 0286 } 0287 0288 // parse extra info 0289 else if ( text.contains( "detected extended VCD2.0 PBC files" ) ) 0290 emit infoMessage( i18n( "detected extended VCD2.0 PBC files" ), K3b::Job::MessageInfo ); 0291 0292 // parse startposition and extracting sequence info 0293 // extracting avseq05.mpg... (start lsn 32603 (+28514)) 0294 else if ( text.startsWith( "extracting" ) ) { 0295 if ( text.contains( "(start lsn" ) ) { 0296 int index = text.indexOf( "(start lsn" ); 0297 int end = text.indexOf( " (+" ); 0298 if ( end > 0) { 0299 m_subPosition = text.mid( index + 11, end - index - 11 ).trimmed().toLong(); 0300 } 0301 else { 0302 // found segment here we can get only the start lsn :) 0303 // extracting item0001.mpg... (start lsn 225, 1 segments) 0304 int end = text.indexOf( ',', index ); 0305 int overallPos = text.mid( index + 11, end - index - 11 ).trimmed().toLong(); 0306 double relOverallWritten = ( ( double ) overallPos * 2352 ) / ( double ) m_videooptions ->getVideoCdSize() ; 0307 int newpercent = ( int ) ( 100 * relOverallWritten ); 0308 if ( newpercent > m_oldpercent ) { 0309 emit percent( newpercent ); 0310 m_oldpercent = newpercent; 0311 } 0312 } 0313 0314 0315 index = 11; 0316 end = text.indexOf( "(start lsn" ); 0317 emit newSubTask( i18n( "Extracting %1" , text.mid( index, end - index ).trimmed() ) ); 0318 } 0319 // parse extracting files info 0320 // extracting CDI/CDI_IMAG.RTF to _cdi_cdi_imag.rtf (lsn 258, size 1315168, raw 1) 0321 else if ( text.contains( "(lsn" ) && text.contains( "size" ) ) { 0322 int index = 11; 0323 int end = text.indexOf( "to" ); 0324 QString extractFileName = text.mid( index, end - index ).trimmed(); 0325 index = text.indexOf( " to " ); 0326 end = text.indexOf( " (lsn" ); 0327 QString toFileName = text.mid( index + 4, end - index - 4 ).trimmed(); 0328 emit newSubTask( i18n( "Extracting %1 to %2" , extractFileName ,toFileName ) ); 0329 } 0330 } 0331 } 0332 0333 QString K3b::VideoCdRip::jobDescription() const 0334 { 0335 return i18n( "Extracting %1" , m_videooptions ->getVideoCdDescription() ); 0336 } 0337 0338 QString K3b::VideoCdRip::jobDetails() const 0339 { 0340 return QString( "(%1)" ).arg ( KIO::convertSize( m_videooptions ->getVideoCdSize() ) ); 0341 } 0342 0343 #include "moc_k3bvideocdrip.cpp"