1 /*****************************************************************************
2 ** QNapi
3 ** Copyright (C) 2008-2017 Piotr Krzemiński <pio.krzeminski@gmail.com>
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
11 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
12 **
13 *****************************************************************************/
14
15 #include "frmprogress.h"
16 #include <QCloseEvent>
17 #include <QDesktopWidget>
18 #include <QDir>
19 #include <QFile>
20 #include <QFileInfo>
21 #include <QMessageBox>
22 #include <QSystemTrayIcon>
23 #include "libqnapi.h"
24 #include "qnapiapp.h"
25
frmProgress(QWidget * parent,Qt::WindowFlags f)26 frmProgress::frmProgress(QWidget *parent, Qt::WindowFlags f)
27 : QWidget(parent, f) {
28 qRegisterMetaType<SubtitleInfoList>("QNapiSubtitleInfoList");
29
30 ui.setupUi(this);
31
32 setAttribute(Qt::WA_DeleteOnClose, false);
33 setAttribute(Qt::WA_QuitOnClose, false);
34
35 setBatchMode(false);
36
37 connect(&getThread, SIGNAL(fileNameChange(const QString &)), ui.lbFileName,
38 SLOT(setText(const QString &)));
39 connect(&getThread, SIGNAL(actionChange(const QString &)), ui.lbAction,
40 SLOT(setText(const QString &)));
41 connect(&getThread, SIGNAL(progressChange(int, int, float)), this,
42 SLOT(updateProgress(int, int, float)));
43 connect(&getThread, SIGNAL(selectSubtitles(QString, SubtitleInfoList)), this,
44 SLOT(selectSubtitles(QString, SubtitleInfoList)));
45 connect(this, SIGNAL(subtitlesSelected(int)), &getThread,
46 SLOT(subtitlesSelected(int)));
47 connect(&getThread, SIGNAL(finished()), this, SLOT(downloadFinished()));
48
49 QRect position = frameGeometry();
50 position.moveCenter(QDesktopWidget().availableGeometry().center());
51 move(position.topLeft());
52 }
53
receiveRequest(const QString & request)54 void frmProgress::receiveRequest(const QString &request) {
55 enqueueFile(request);
56 if (!getThread.isRunning()) download();
57 raise();
58 activateWindow();
59 }
60
enqueueFile(const QString & filePath)61 void frmProgress::enqueueFile(const QString &filePath) {
62 static QMutex locker;
63 locker.lock();
64 if (QFile::exists(filePath)) {
65 getThread.queue << filePath;
66 updateProgress(-1, getThread.queue.size(), -1);
67 }
68 locker.unlock();
69 }
70
enqueueFiles(const QStringList & fileList)71 void frmProgress::enqueueFiles(const QStringList &fileList) {
72 for (int i = 0; i < fileList.size(); i++) {
73 enqueueFile(fileList.at(i));
74 }
75 }
76
download()77 bool frmProgress::download() {
78 if (summary.isVisible()) {
79 summary.close();
80 }
81
82 auto config = LibQNapi::loadConfig();
83 auto config1 = config;
84 if (!targetFormatOverride.isEmpty()) {
85 config1 = config1.setPostProcessingConfig(
86 config1.postProcessingConfig().setSubFormat(targetFormatOverride));
87 }
88 auto config2 = config1;
89 if (!targetExtOverride.isEmpty()) {
90 config2 = config2.setPostProcessingConfig(
91 config2.postProcessingConfig().setSubExtension(targetExtOverride));
92 }
93
94 QNapi napi(config2);
95
96 // TODO: perform these checks earlier
97 if (!napi.checkP7ZipPath()) {
98 QMessageBox::warning(0, tr("Can not find p7zip!"),
99 tr("The path to the program p7zip is incorrect!"));
100 return false;
101 }
102
103 if (!napi.checkTmpPath()) {
104 QMessageBox::warning(
105 0, tr("Invalid temporary directory!"),
106 tr("Unable to write to the temporary directory! Check your settings."));
107 return false;
108 }
109
110 if (getThread.queue.isEmpty()) {
111 QMessageBox::warning(
112 0, tr("No files!"),
113 tr("Can't download subtitles as no movie files specified!"));
114 return false;
115 }
116
117 if (!isVisible()) {
118 QRect position = frameGeometry();
119 position.moveCenter(QDesktopWidget().availableGeometry().center());
120 move(position.topLeft());
121 show();
122 }
123
124 showSummary = true;
125 closeRequested = false;
126 ui.pbCancel->setEnabled(true);
127
128 getThread.setConfig(config2);
129 getThread.start();
130 return true;
131 }
132
updateProgress(int current,int all,float stageProgress)133 void frmProgress::updateProgress(int current, int all, float stageProgress) {
134 static int lastCurrent, lastAll;
135 static float lastStageProgress;
136
137 static QMutex m;
138 m.lock();
139
140 if (current >= 0) lastCurrent = current;
141 if (all >= 0) lastAll = all;
142 if (stageProgress >= 0) lastStageProgress = stageProgress;
143
144 QString windowTitle =
145 (lastAll > 1) ? QString(tr("QNapi - downloading subtitles (%1/%2)"))
146 .arg(lastCurrent + 1)
147 .arg(lastAll)
148 : QString(tr("QNapi - downloading subtitles..."));
149 setWindowTitle(windowTitle);
150
151 ui.pbProgress->setMaximum(lastAll * 100);
152 ui.pbProgress->setValue(lastCurrent * 100 + (int)(lastStageProgress * 100));
153
154 m.unlock();
155 }
156
selectSubtitles(QString fileName,SubtitleInfoList subtitles)157 void frmProgress::selectSubtitles(QString fileName,
158 SubtitleInfoList subtitles) {
159 frmSelect.setFileName(fileName);
160 frmSelect.setSubtitlesList(subtitles);
161
162 int selIdx;
163
164 if (frmSelect.exec() == QDialog::Accepted) {
165 selIdx = frmSelect.getSelectedIndex();
166 } else {
167 selIdx = -1;
168 }
169
170 emit subtitlesSelected(selIdx);
171 }
172
downloadFinished()173 void frmProgress::downloadFinished() {
174 hide();
175
176 QStringList queue = getThread.queue;
177 getThread.queue.clear();
178
179 mutex.lock();
180 if (showSummary) {
181 if (!getThread.criticalMessage.isEmpty()) {
182 QMessageBox::critical(0, tr("Critical error!"),
183 getThread.criticalMessage);
184 } else if (queue.size() > 0 && !getThread.subStatusList.isEmpty()) {
185 summary.setSummaryList(getThread.subStatusList);
186 summary.exec();
187 }
188 }
189
190 if (closeRequested) close();
191
192 mutex.unlock();
193 if (batchMode) qApp->quit();
194 }
195
closeEvent(QCloseEvent * event)196 void frmProgress::closeEvent(QCloseEvent *event) {
197 if (getThread.isRunning()) {
198 if (QMessageBox::question(
199 this, tr("QNapi"),
200 tr("Do you want to cancel subtitles downloading?"),
201 QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
202 mutex.lock();
203 showSummary = false;
204 getThread.requestAbort();
205 ui.lbAction->setText(tr("Finishing the tasks..."));
206 ui.lbFileName->setText("");
207 ui.pbCancel->setEnabled(false);
208 qApp->processEvents();
209 closeRequested = true;
210 mutex.unlock();
211 event->ignore();
212 } else {
213 event->ignore();
214 }
215 } else {
216 event->accept();
217 }
218 }
219
dragEnterEvent(QDragEnterEvent * event)220 void frmProgress::dragEnterEvent(QDragEnterEvent *event) {
221 QUrl url(event->mimeData()->urls().at(0));
222 QFileInfo fi(url.toLocalFile());
223 if (fi.exists() && fi.isFile()) event->acceptProposedAction();
224 }
225
dropEvent(QDropEvent * event)226 void frmProgress::dropEvent(QDropEvent *event) {
227 QList<QUrl> urlList;
228 QFileInfo info;
229
230 urlList = event->mimeData()->urls();
231
232 foreach (QUrl url, urlList) {
233 info.setFile(url.toLocalFile());
234 if (!info.exists() || !info.isFile()) continue;
235 enqueueFile(url.toLocalFile());
236 }
237 }
238
GetThread()239 GetThread::GetThread()
240 : langBackupPassed(false), config(LibQNapi::loadConfig()) {
241 connect(this, SIGNAL(criticalError(const QString &)), this,
242 SLOT(setCriticalMessage(const QString &)));
243 }
244
subtitlesSelected(int idx)245 void GetThread::subtitlesSelected(int idx) {
246 selIdx = idx;
247 waitForDlg.unlock();
248 }
249
250 #define ABORT_POINT \
251 { \
252 if (abort) { \
253 return; \
254 } \
255 }
256
run()257 void GetThread::run() {
258 abort = false;
259 criticalMessage.clear();
260 if (queue.size() <= 0) return;
261
262 napiSuccess = napiFail = 0;
263 subStatusList.clear();
264
265 QNapi napi(config, specificEngine);
266
267 emit progressChange(0, queue.size(), 0.0f);
268
269 QString language = !lang.isEmpty() ? lang : config.generalConfig().language();
270 QString languageBackup =
271 langBackupPassed ? langBackup : config.generalConfig().backupLanguage();
272
273 for (int i = 0; i < queue.size(); i++) {
274 ABORT_POINT
275
276 QFileInfo fi(queue[i]);
277 emit fileNameChange(fi.fileName());
278
279 napi.setMoviePath(queue[i]);
280
281 emit progressChange(i, queue.size(), 0.1f);
282 emit actionChange(tr("Checking permissions of the video directory..."));
283
284 if (!napi.checkWritePermissions()) {
285 emit criticalError(tr("No permission to write to the directory '%1'!")
286 .arg(QFileInfo(queue[i]).path()));
287 return;
288 }
289
290 napi.clearSubtitlesList();
291
292 emit progressChange(i, queue.size(), 0.3f);
293 emit actionChange(tr("Calculating checksum of the file..."));
294
295 napi.checksum();
296
297 ABORT_POINT
298
299 bool found = false;
300 SearchPolicy sp = config.generalConfig().searchPolicy();
301
302 if (sp == SP_SEARCH_ALL_WITH_BACKUP_LANG) {
303 foreach (QString e, napi.listLoadedEngines()) {
304 emit progressChange(i, queue.size(), 0.4f);
305 emit actionChange(
306 tr("Searching for subtitles [%1] (%2)...").arg(language, e));
307 found = napi.lookForSubtitles(language, e) || found;
308
309 ABORT_POINT
310
311 emit actionChange(
312 tr("Searching for subtitles in alternative language [%1] (%2)...")
313 .arg(languageBackup, e));
314 found = napi.lookForSubtitles(languageBackup, e) || found;
315
316 ABORT_POINT
317 }
318 } else {
319 foreach (QString e, napi.listLoadedEngines()) {
320 emit progressChange(i, queue.size(), 0.4f);
321 emit actionChange(
322 tr("Searching for subtitles [%1] (%2)...").arg(language, e));
323 found = napi.lookForSubtitles(language, e) || found;
324
325 if (sp == SP_BREAK_IF_FOUND && found) {
326 break;
327 }
328
329 ABORT_POINT
330 }
331
332 if (!found && !languageBackup.isEmpty()) {
333 foreach (QString e, napi.listLoadedEngines()) {
334 emit progressChange(i, queue.size(), 0.45f);
335 emit actionChange(
336 tr("Searching for subtitles in alternative language [%1] (%2)...")
337 .arg(languageBackup, e));
338 found = napi.lookForSubtitles(languageBackup, e) || found;
339
340 if (sp == SP_BREAK_IF_FOUND && found) break;
341
342 ABORT_POINT
343 }
344 }
345 }
346
347 if (!found) {
348 ++napiFail;
349 subStatusList << SubtitleInfo::fromFailed(queue[i]);
350 continue;
351 }
352
353 // jesli mozna i potrzeba, listujemy dostepne napisy
354 if (napi.needToShowList()) {
355 emit selectSubtitles(QFileInfo(queue[i]).fileName(),
356 napi.listSubtitles());
357
358 waitForDlg.lock();
359 waitForDlg.lock();
360 waitForDlg.unlock();
361 } else {
362 selIdx = napi.bestIdx();
363 }
364
365 ABORT_POINT
366
367 if (selIdx == -1) {
368 ++napiFail;
369 subStatusList << SubtitleInfo::fromFailed(queue[i]);
370 continue;
371 }
372
373 emit progressChange(i, queue.size(), 0.5);
374 emit actionChange(tr("Downloading subtitles file..."));
375
376 if (!napi.download(selIdx)) {
377 ABORT_POINT
378
379 ++napiFail;
380 subStatusList << SubtitleInfo::fromFailed(queue[i]);
381 continue;
382 }
383
384 ABORT_POINT
385
386 emit progressChange(i, queue.size(), 0.65f);
387 emit actionChange(tr("Unpacking subtitles file..."));
388
389 if (!napi.unpack(selIdx)) {
390 ++napiFail;
391 subStatusList << SubtitleInfo::fromFailed(queue[i]);
392 continue;
393 }
394
395 if (napi.ppEnabled()) {
396 emit progressChange(i, queue.size(), 0.8f);
397 emit actionChange(tr("Post-processing subtitles..."));
398 napi.postProcessSubtitles();
399 }
400
401 emit progressChange(i, queue.size(), 0.9f);
402 emit actionChange(tr("Adjusting subtitles..."));
403
404 if (!napi.matchSubtitles()) {
405 ABORT_POINT
406
407 ++napiFail;
408 subStatusList << SubtitleInfo::fromFailed(queue[i]);
409
410 emit criticalError(tr("Could not adjust subtitles!"));
411 return;
412 }
413
414 ++napiSuccess;
415 subStatusList << napi.listSubtitles().at(selIdx);
416
417 napi.cleanup();
418
419 emit progressChange(i, queue.size(), 1.0f);
420 }
421
422 emit progressChange(queue.size() - 1, queue.size(), 1);
423 }
424