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>
20 #include <QCloseEvent>
21 #include <QMessageBox>
22 #include <QFileDialog>
30 MainWindow::MainWindow():
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");
46 setWindowIcon(QIcon(":/rc/abby.png"));
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
)));
62 MainWindow::closeEvent(QCloseEvent
*event
) {
68 MainWindow::writeSettings() {
70 s
.beginGroup("MainWindow");
71 s
.setValue("size",size());
72 s
.setValue("pos",pos());
73 s
.setValue("formatCombo",formatCombo
->currentIndex());
78 MainWindow::readSettings() {
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());
88 MainWindow::updateLog(QString newText
) {
89 QString text
= logEdit
->toPlainText() + newText
;
90 logEdit
->setPlainText(text
);
97 MainWindow::onPreferences() {
102 MainWindow::onSaveasStateChanged(int state
) {
103 saveasEdit
->setEnabled(state
!= 0);
107 MainWindow::onStreamStateChanged(int state
) {
108 streamSpin
->setEnabled(state
!= 0);
112 MainWindow::onSaveasBrowse() {
113 QString fname
= QFileDialog::getSaveFileName(this,tr("Save as"));
114 if (!fname
.isEmpty())
115 saveasEdit
->setText(fname
);
119 MainWindow::onStart() {
122 QString url
= urlEdit
->text();
125 statusBar()->showMessage(tr("Enter a video link."));
131 if (!url
.startsWith("http://",Qt::CaseInsensitive
))
132 url
.insert(0,"http://");
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."));
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
150 QString savedir
= prefs
->savedirEdit
->text();
151 if (savedir
.isEmpty()) {
152 QMessageBox::information(this,QCoreApplication::applicationName(),
153 tr("Save directory undefined. See preferences."));
157 process
.setWorkingDirectory(savedir
);
159 // Construct cclive args
162 args
<< "--print-fname";
164 QString s
= prefs
->additionalEdit
->text();
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(";"))
178 args
<< QString("--exec=%1").arg(s
);
181 if (prefs
->proxyCombo
->currentIndex() == 0)
182 args
<< "--no-proxy";
184 s
= prefs
->proxyEdit
->text();
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);
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();
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
);
221 updateLog("% cclive " +args
.join(" ")+ "\n");
223 // And finally start the process
226 process
.start(cclive
,args
);
230 MainWindow::onCancel() {
236 MainWindow::onAbout() {
237 AboutDialog
about(this,prefs
->ccliveEdit
->text());
242 MainWindow::onURLEditingFinished() {
244 // Change format combobox contents dynamically based on the video URL.
246 QString url
= urlEdit
->text();
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
);
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("|");
273 formatCombo
->clear();
274 formatCombo
->addItems(formats
);
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();
290 static const struct lookup_s lookup
[] = {
295 const int c
= sizeof(lookup
)/sizeof(struct lookup_s
);
298 for (int i
=0; i
<c
; ++i
) {
299 if (url
.contains(lookup
[i
].host
)) {
300 if (formatCombo
->currentText() == "flv") {
307 continueBox
->setEnabled(enable
);
309 if (continueBox
->isChecked() && !enable
)
310 continueBox
->setCheckState(Qt::Unchecked
);
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);
327 MainWindow::onProcError(QProcess::ProcessError err
) {
328 if (err
== QProcess::FailedToStart
) {
329 QString msg
= tr("error: failed to start process");
331 statusBar()->showMessage(msg
);
336 MainWindow::onProcStderrReady() {
338 QString::fromLocal8Bit(process
.readAllStandardError());
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", " ");
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());
380 MainWindow::onProcFinished(int exitCode
, QProcess::ExitStatus exitStatus
) {
382 if (exitStatus
== QProcess::NormalExit
) {
384 status
= tr("Process exited with an error; see log");
386 status
= tr("Process exited normally");
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);