Updated translator note, though useless in linguist.
[abby.git] / src / mainwnd.cpp
blob771188e484144e35cb20a6b2bf485da8f285040a
1 /*
2 * abby Copyright (C) 2009 Toni Gundogdu.
3 * This file is part of abby.
5 * abby 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 3 of the License, or
8 * (at your option) any later version.
10 * abby is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include <QMainWindow>
19 #include <QSettings>
20 #include <QCloseEvent>
21 #include <QMessageBox>
22 #include <QFileDialog>
23 #include <QIcon>
25 #include "mainwnd.h"
26 #include "prefsdlg.h"
27 #include "passdlg.h"
28 #include "aboutdlg.h"
30 MainWindow::MainWindow():
31 cancelled(false)
35 /* TRANSLATOR
37 The word "English" is not meant to be translated. Instead,
38 enter the name of the target language in native language.
39 For example: Suomi, Deutsch, Svenska, Turkce, Francais, etc.
41 The only purpose of this string is to be able to list the
42 available languages in the Preferences dialog. */
43 const QString lang = tr("English");
45 setupUi(this);
46 setWindowIcon(QIcon(":/rc/abby.png"));
47 readSettings();
49 prefs = new PreferencesDialog(this);
51 connect(&process, SIGNAL(started()),
52 this, SLOT(onProcStarted()));
53 connect(&process, SIGNAL(error(QProcess::ProcessError)),
54 this, SLOT(onProcError(QProcess::ProcessError)));
55 connect(&process, SIGNAL(readyReadStandardError()),
56 this, SLOT(onProcStderrReady()));
57 connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)),
58 this, SLOT(onProcFinished(int,QProcess::ExitStatus)));
61 void
62 MainWindow::closeEvent(QCloseEvent *event) {
63 writeSettings();
64 event->accept();
67 void
68 MainWindow::writeSettings() {
69 QSettings s;
70 s.beginGroup("MainWindow");
71 s.setValue("size",size());
72 s.setValue("pos",pos());
73 s.setValue("formatCombo",formatCombo->currentIndex());
74 s.endGroup();
77 void
78 MainWindow::readSettings() {
79 QSettings s;
80 s.beginGroup("MainWindow");
81 resize(s.value("size",QSize(400,400)).toSize());
82 move(s.value("pos",QPoint(200,200)).toPoint());
83 formatCombo->setCurrentIndex(s.value("formatCombo").toInt());
84 s.endGroup();
87 void
88 MainWindow::updateLog(QString newText) {
89 QString text = logEdit->toPlainText() + newText;
90 logEdit->setPlainText(text);
94 // Slots
96 void
97 MainWindow::onPreferences() {
98 prefs->exec();
101 void
102 MainWindow::onSaveasStateChanged(int state) {
103 saveasEdit->setEnabled(state != 0);
106 void
107 MainWindow::onStreamStateChanged(int state) {
108 streamSpin->setEnabled(state != 0);
111 void
112 MainWindow::onSaveasBrowse() {
113 QString fname = QFileDialog::getSaveFileName(this,tr("Save as"));
114 if (!fname.isEmpty())
115 saveasEdit->setText(fname);
118 void
119 MainWindow::onStart() {
120 // Check video URL
122 QString url = urlEdit->text();
124 if (url.isEmpty()) {
125 statusBar()->showMessage(tr("Enter a video link."));
126 return;
129 url = url.trimmed();
131 if (!url.startsWith("http://",Qt::CaseInsensitive))
132 url.insert(0,"http://");
134 // Check cclive
136 QString cclive = prefs->ccliveEdit->text();
137 if (cclive.isEmpty()) {
138 QMessageBox::information(this,QCoreApplication::applicationName(),
139 tr("Path to cclive command undefined. See preferences."));
140 onPreferences();
141 return;
144 // Check video save directory
146 // NOTE: cclive itself does not support this concept so we
147 // work around it by changing the working directory to the
148 // save directory.
150 QString savedir = prefs->savedirEdit->text();
151 if (savedir.isEmpty()) {
152 QMessageBox::information(this,QCoreApplication::applicationName(),
153 tr("Save directory undefined. See preferences."));
154 onPreferences();
155 return;
157 process.setWorkingDirectory(savedir);
159 // Construct cclive args
161 QStringList args;
162 args << "--print-fname";
164 QString s = prefs->additionalEdit->text();
165 if (!s.isEmpty())
166 args << s;
168 s = prefs->streamEdit->text();
169 if (!s.isEmpty() && streamBox->isChecked()) {
170 args << QString("--stream-exec=%1").arg(s);
171 args << QString("--stream=%1").arg(streamSpin->value());
174 s = prefs->commandEdit->text();
175 if (!s.isEmpty() && commandBox->isChecked()) {
176 if (!s.endsWith(";"))
177 s += ";";
178 args << QString("--exec=%1").arg(s);
181 if (prefs->proxyCombo->currentIndex() == 0)
182 args << "--no-proxy";
183 else {
184 s = prefs->proxyEdit->text();
185 if (!s.isEmpty())
186 args << QString("--proxy=%1").arg(s);
189 if (prefs->limitBox->checkState()) {
190 int n = prefs->limitSpin->value();
191 args << QString("--limit-rate=%1").arg(n);
194 if (prefs->youtubeGroup->isChecked()) {
195 QString user = prefs->ytuserEdit->text();
196 QString pass = prefs->ytpassEdit->text();
197 if (pass.isEmpty()) {
198 PasswordDialog pwd(this);
199 if (pwd.exec())
200 pass = pwd.passEdit->text();
202 if (!user.isEmpty() && !pass.isEmpty()) {
203 args << QString("--youtube-user=%1").arg(user);
204 args << QString("--youtube-pass=%1").arg(pass);
208 s = saveasEdit->text();
209 if (!s.isEmpty())
210 args << QString("--output-video=%1").arg(s);
212 if (continueBox->isChecked())
213 args << "--continue";
215 args << QString("--download=%1").arg(formatCombo->currentText());
216 args << QString("%1").arg(url);
218 // Prepare log
220 logEdit->clear();
221 updateLog("% cclive " +args.join(" ")+ "\n");
223 // And finally start the process
225 cancelled = false;
226 process.start(cclive,args);
229 void
230 MainWindow::onCancel() {
231 cancelled = true;
232 process.kill();
235 void
236 MainWindow::onAbout() {
237 AboutDialog about(this,prefs->ccliveEdit->text());
238 about.exec();
241 void
242 MainWindow::onURLEditingFinished() {
244 // Change format combobox contents dynamically based on the video URL.
246 QString url = urlEdit->text();
247 if (url.isEmpty())
248 return;
250 struct lookup_s {
251 const char *host;
252 const char *formats;
254 static const struct lookup_s lookup[] = {
255 {"youtube.com", "mp4|xflv|3gpp"},
256 {"video.google.", "mp4"},
257 {"dailymotion.com", "spak-mini|vp6-hq|vp6-hd|vp6|h264"},
260 const int c = sizeof(lookup)/sizeof(struct lookup_s);
262 QStringList formats;
263 formats << "flv";
265 for (int i=0; i<c; ++i) {
266 if (url.contains(lookup[i].host)) {
267 QString s = lookup[i].formats;
268 formats << s.split("|");
269 break;
273 formatCombo->clear();
274 formatCombo->addItems(formats);
277 void
278 MainWindow::onFormatStateChanged(int) {
280 // Disable --continue for the specified hosts if format is "flv".
281 // Make changes based on the video URL.
283 QString url = urlEdit->text();
284 if (url.isEmpty())
285 return;
287 struct lookup_s {
288 const char *host;
290 static const struct lookup_s lookup[] = {
291 {"youtube.com"},
292 {"video.google."},
295 const int c = sizeof(lookup)/sizeof(struct lookup_s);
296 bool enable = true;
298 for (int i=0; i<c; ++i) {
299 if (url.contains(lookup[i].host)) {
300 if (formatCombo->currentText() == "flv") {
301 enable = false;
302 break;
307 continueBox->setEnabled(enable);
309 if (continueBox->isChecked() && !enable)
310 continueBox->setCheckState(Qt::Unchecked);
313 void
314 MainWindow::onProcStarted() {
315 statusBar() ->clearMessage();
316 fileLabel ->setText("-");
317 sizeLabel ->setText("-- / --");
318 rateLabel ->setText("--.-");
319 etaLabel ->setText("--:--");
320 progressBar ->setValue(0);
322 startButton ->setEnabled(false);
323 cancelButton->setEnabled(true);
326 void
327 MainWindow::onProcError(QProcess::ProcessError err) {
328 if (err == QProcess::FailedToStart) {
329 QString msg = tr("error: failed to start process");
330 updateLog(msg);
331 statusBar()->showMessage(msg);
335 void
336 MainWindow::onProcStderrReady() {
337 QString newText =
338 QString::fromLocal8Bit(process.readAllStandardError());
340 QString status;
341 if (newText.contains("fetch"))
342 status = tr("Fetching link...");
343 else if (newText.contains("verify"))
344 status = tr("Verifying video link...");
345 else if (newText.contains("error:")) {
346 // Probably better to connect finished etc. signal instead
347 status = tr("error: see log for details");
349 else if (newText.contains("file:")) {
350 status = tr("Extracting video...");
351 QStringList lst = newText.split(" ",QString::SkipEmptyParts);
352 fileLabel->setText(lst[1].remove("\n"));
355 if (!status.isEmpty())
356 statusBar()->showMessage(status.remove("\n"));
358 if (newText.contains("%")) {
359 newText.replace("\n", " ");
361 QStringList lst =
362 newText.split(" ",QString::SkipEmptyParts);
364 QString percent = lst[1].remove(QChar('%'));
365 QString now = lst[2];
366 QString expected= lst[4];
367 QString rate = lst[5];
368 QString eta = lst[6];
370 sizeLabel->setText(now +" / "+ expected);
371 rateLabel->setText(rate);
372 etaLabel->setText(eta);
373 progressBar->setValue(percent.toInt());
374 } else {
375 updateLog(newText);
379 void
380 MainWindow::onProcFinished(int exitCode, QProcess::ExitStatus exitStatus) {
381 QString status;
382 if (exitStatus == QProcess::NormalExit) {
383 if (exitCode != 0)
384 status = tr("Process exited with an error; see log");
385 else
386 status = tr("Process exited normally");
387 } else {
388 status = cancelled
389 ? tr("Process terminated")
390 : tr("Process crashed; see log");
393 if (!statusBar()->currentMessage().contains(tr("error:")))
394 statusBar()->showMessage(status);
396 updateLog(status + ".");
398 startButton ->setEnabled(true);
399 cancelButton->setEnabled(false);