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"