File indexing completed on 2024-04-14 04:43:17

0001 /* AUDEX CDDA EXTRACTOR
0002  * SPDX-FileCopyrightText: Copyright (C) 2007 Marco Nelles
0003  * <https://userbase.kde.org/Audex>
0004  *
0005  * SPDX-License-Identifier: GPL-3.0-or-later
0006  */
0007 
0008 #include "encoderwrapper.h"
0009 
0010 #include <QDebug>
0011 
0012 EncoderWrapper::EncoderWrapper(QObject *parent, const QString &commandScheme, const QString &encoderName, const bool deleteFractionFiles)
0013     : QObject(parent)
0014 {
0015     command_scheme = commandScheme;
0016     encoder_name = encoderName;
0017     delete_fraction_files = deleteFractionFiles;
0018 
0019     connect(&proc, SIGNAL(readyReadStandardError()), this, SLOT(parseOutput()));
0020     connect(&proc, SIGNAL(readyReadStandardOutput()), this, SLOT(parseOutput()));
0021     connect(&proc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus)));
0022     connect(&proc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
0023 
0024     proc.setOutputChannelMode(KProcess::SeparateChannels);
0025     proc.setReadChannel(KProcess::StandardError);
0026 
0027     termination = false;
0028     processing = 0;
0029 
0030     not_found_counter = 0;
0031 }
0032 
0033 EncoderWrapper::~EncoderWrapper()
0034 {
0035 }
0036 
0037 bool EncoderWrapper::encode(int n,
0038                             int cdno,
0039                             int trackoffset,
0040                             int nooftracks,
0041                             const QString &artist,
0042                             const QString &album,
0043                             const QString &tartist,
0044                             const QString &ttitle,
0045                             const QString &genre,
0046                             const QString &date,
0047                             const QString &suffix,
0048                             const QString &isrc,
0049                             const QImage &cover,
0050                             const QString &tmppath,
0051                             const QString &input,
0052                             const QString &output)
0053 {
0054     if (!processing)
0055         processing = 1;
0056     else
0057         return false;
0058     termination = false;
0059 
0060     if (command_scheme.isEmpty()) {
0061         Q_EMIT error(i18n("Command scheme is empty."));
0062         return false;
0063     }
0064 
0065     SchemeParser schemeparser;
0066     QString command = schemeparser.parsePerTrackCommandScheme(command_scheme,
0067                                                               input,
0068                                                               output,
0069                                                               n,
0070                                                               cdno,
0071                                                               trackoffset,
0072                                                               nooftracks,
0073                                                               artist,
0074                                                               album,
0075                                                               tartist,
0076                                                               ttitle,
0077                                                               date,
0078                                                               genre,
0079                                                               isrc,
0080                                                               suffix,
0081                                                               cover,
0082                                                               tmppath,
0083                                                               encoder_name);
0084 
0085     qDebug() << "executing command " << command;
0086     proc.setShellCommand(command);
0087     proc.start();
0088     proc.waitForStarted();
0089 
0090     processing_filename = output;
0091 
0092     Q_EMIT info(i18n("Encoding track %1...", n));
0093 
0094     return true;
0095 }
0096 
0097 void EncoderWrapper::cancel()
0098 {
0099     if (!processing)
0100         return;
0101 
0102     // we need to suppress normal error messages, because with a cancel the user known what he does
0103     termination = true;
0104     proc.terminate();
0105 
0106     if (delete_fraction_files) {
0107         QFile file(processing_filename);
0108         if (file.exists()) {
0109             file.remove();
0110             Q_EMIT warning(i18n("Deleted partial file \"%1\".", processing_filename.mid(processing_filename.lastIndexOf("/") + 1)));
0111             qDebug() << "deleted partial file" << processing_filename;
0112         }
0113     }
0114 
0115     Q_EMIT error(i18n("User canceled encoding."));
0116     qDebug() << "Interrupt encoding.";
0117 }
0118 
0119 bool EncoderWrapper::isProcessing()
0120 {
0121     return (processing > 0);
0122 }
0123 
0124 const QStringList &EncoderWrapper::log()
0125 {
0126     return p_log;
0127 }
0128 
0129 void EncoderWrapper::parseOutput()
0130 {
0131     QByteArray rawoutput = proc.readAllStandardError();
0132     if (rawoutput.size() == 0)
0133         rawoutput = proc.readAllStandardOutput();
0134     bool found = false;
0135     if (rawoutput.size() > 0) {
0136         QString output(rawoutput);
0137         QStringList list = output.trimmed().split('\n');
0138         p_log << list;
0139         for (int i = 0; i < list.count(); ++i) {
0140             if (list.at(i).contains('%')) {
0141                 QString line = list.at(i);
0142                 static const QRegularExpression regex("\\d+[,.]?\\d*\\%");
0143                 int startPos = line.indexOf(regex);
0144                 if (startPos == -1)
0145                     continue;
0146                 QString p = line.mid(startPos);
0147                 p = p.left(p.indexOf('%'));
0148                 bool conversionSuccessful = false;
0149                 double percent = p.toDouble(&conversionSuccessful);
0150                 if ((conversionSuccessful) && (percent >= 0) && (percent <= 100)) {
0151                     Q_EMIT progress((int)percent);
0152                     found = true;
0153                     not_found_counter = 0;
0154                 }
0155             }
0156         }
0157     }
0158     if (!found) {
0159         if (not_found_counter > 5)
0160             Q_EMIT progress(-1);
0161         else
0162             ++not_found_counter;
0163     }
0164 }
0165 
0166 void EncoderWrapper::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
0167 {
0168     processing = 0;
0169     if (termination) {
0170         Q_EMIT finished();
0171         return;
0172     }
0173     if ((exitStatus == QProcess::NormalExit) && (exitCode == 0)) {
0174         Q_EMIT info(i18n("Encoding OK (\"%1\").", processing_filename));
0175     } else {
0176         Q_EMIT error(i18n("An error occurred while encoding file \"%1\".", processing_filename), i18n("Please check your profile."));
0177     }
0178     Q_EMIT finished();
0179     qDebug() << "encoding finished.";
0180 }
0181 
0182 void EncoderWrapper::processError(QProcess::ProcessError err)
0183 {
0184     if (termination)
0185         return;
0186     switch (err) {
0187     case QProcess::FailedToStart:
0188         Q_EMIT error(i18n("%1 failed to start.", encoder), i18n("Either it is missing, or you may have insufficient permissions to invoke the program."));
0189         break;
0190     case QProcess::Crashed:
0191         Q_EMIT error(i18n("%1 crashed some time after starting successfully.", encoder), i18n("Please check your profile."));
0192         break;
0193     case QProcess::Timedout:
0194         Q_EMIT error(i18n("%1 timed out. This should not happen.", encoder), i18n("Please check your profile."));
0195         break;
0196     case QProcess::WriteError:
0197         Q_EMIT error(i18n("An error occurred when attempting to write to %1.", encoder),
0198                      i18n("For example, the process may not be running, or it may have closed its input channel."));
0199         break;
0200     case QProcess::ReadError:
0201         Q_EMIT error(i18n("An error occurred when attempting to read from %1.", encoder), i18n("For example, the process may not be running."));
0202         break;
0203     case QProcess::UnknownError:
0204         Q_EMIT error(i18n("An unknown error occurred to %1. This should not happen.", encoder), i18n("Please check your profile."));
0205         break;
0206     }
0207     Q_EMIT finished();
0208     qDebug() << "encoding finished.";
0209 }