1 /*
2  * Cantata
3  *
4  * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5  *
6  * ----
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  */
22 
23 #include "transcodingjob.h"
24 #include "device.h"
25 #include <QStringList>
26 
TranscodingJob(const Encoders::Encoder & enc,int val,const QString & src,const QString & dest,const DeviceOptions & d,int co,const Song & s)27 TranscodingJob::TranscodingJob(const Encoders::Encoder &enc, int val, const QString &src, const QString &dest, const DeviceOptions &d, int co, const Song &s)
28     : CopyJob(src, dest, d, co, s)
29     , encoder(enc)
30     , value(val)
31     , process(nullptr)
32     , duration(-1)
33 {
34 }
35 
~TranscodingJob()36 TranscodingJob::~TranscodingJob()
37 {
38     delete process;
39 }
40 
run()41 void TranscodingJob::run()
42 {
43     QString src(updateTagsLocal());
44     if (src.isEmpty()) {
45         return;
46     }
47 
48     if (stopRequested) {
49         emit result(Device::Cancelled);
50     } else {
51         QStringList parameters=encoder.params(value, src, destFile);
52         process = new QProcess;
53         process->setProcessChannelMode(QProcess::MergedChannels);
54         process->setReadChannel(QProcess::StandardOutput);
55         connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()));
56         connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
57         QString cmd=parameters.takeFirst();
58         process->start(cmd, parameters);
59     }
60 }
61 
stop()62 void TranscodingJob::stop()
63 {
64     if (process) {
65         process->close();
66         process->deleteLater();
67         process=nullptr;
68         emit result(Device::Cancelled);
69     }
70 }
71 
finished(int exitCode,QProcess::ExitStatus exitStatus)72 void TranscodingJob::finished(int exitCode, QProcess::ExitStatus exitStatus)
73 {
74     Q_UNUSED(exitStatus)
75     if (!process) {
76         return;
77     }
78     if (stopRequested) {
79         emit result(Device::Cancelled);
80         return;
81     }
82     if (0==exitCode) {
83         updateTagsDest();
84         copyCover(srcFile);
85     }
86     emit result(0==exitCode ? Device::Ok : Device::TranscodeFailed);
87 }
88 
processOutput()89 void TranscodingJob::processOutput()
90 {
91     if (stopRequested) {
92         emit result(Device::Cancelled);
93         return;
94     }
95     QString output = process->readAllStandardOutput().data();
96     if(output.simplified().isEmpty()) {
97         return;
98     }
99 
100     if (!data.isEmpty()) {
101         output=data+output;
102     }
103     if (-1==duration) {
104         duration = computeDuration(output);
105     }
106 
107     if (duration>0) {
108         qint64 prog = computeProgress(output);
109         if (prog>-1) {
110             setPercent((prog*100)/duration);
111         }
112     }
113 
114     if (!output.endsWith('\n') && !output.endsWith('\r')) {
115         int last=output.lastIndexOf('\n');
116         if (-1==last) {
117             last=output.lastIndexOf('\r');
118         }
119         if (last>-1) {
120             data=output.mid(last+1);
121         } else {
122             data=output;
123         }
124     }
125 }
126 
computeDuration(const QString & output)127 inline qint64 TranscodingJob::computeDuration(const QString &output)
128 {
129     //We match something like "Duration: 00:04:33.60"
130     QRegExp matchDuration("Duration: (\\d{2,}):(\\d{2}):(\\d{2})\\.(\\d{2})");
131 
132     if(output.contains(matchDuration)) {
133         //duration is in csec
134         return matchDuration.cap(1).toLong() * 60 * 60 * 100 +
135                matchDuration.cap(2).toInt()  * 60 * 100 +
136                matchDuration.cap(3).toInt()  * 100 +
137                matchDuration.cap(4).toInt();
138     } else {
139         return -1;
140     }
141 }
142 
computeProgress(const QString & output)143 inline qint64 TranscodingJob::computeProgress(const QString &output)
144 {
145     //Output is like size=     323kB time=18.10 bitrate= 146.0kbits/s
146     //We're going to use the "time" column, which counts the elapsed time in seconds.
147     QRegExp matchTime("time=(\\d+)\\.(\\d{2})");
148 
149     if(output.contains(matchTime)) {
150         return matchTime.cap(1).toLong() * 100 +
151                matchTime.cap(2).toInt();
152     } else {
153         return -1;
154     }
155 }
156 
157 #include "moc_transcodingjob.cpp"
158